diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..d114f87b4 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,37 @@ +name: tests + +on: + push: + branches: [master] + pull_request: + schedule: + # Run on the first of the month + - cron: "0 0 1 * *" + workflow_dispatch: + +jobs: + tests: + name: Tests (${{ matrix.os }}, Python ${{ matrix.python-version }}) + runs-on: ${{ matrix.os }} + strategy: + matrix: + # don't run on macos-latest; it can't install pytables + os: [ubuntu-latest, windows-latest] + python-version: ["3.10"] + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements-dev.txt + + - name: Run tests + run: | + make tests diff --git a/.gitignore b/.gitignore index b3102ac4b..43b2257af 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.idea + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..d2bdb316c --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +PROJECT_NAME = ModSimPy +PYTHON_VERSION = 3.10 +PYTHON_INTERPRETER = python + + +create_environment: + conda create -y --name $(PROJECT_NAME) python=$(PYTHON_VERSION) + @echo ">>> conda env created. Activate with:\nconda activate $(PROJECT_NAME)" + + +requirements: + $(PYTHON_INTERPRETER) -m pip install -U pip setuptools wheel + $(PYTHON_INTERPRETER) -m pip install -r requirements-dev.txt + + +lint: + flake8 pacs + black --check --config pyproject.toml pacs + + +format: + black --config pyproject.toml pacs + + +## Delete all compiled Python files +clean: + find . -type f -name "*.py[co]" -delete + find . -type d -name "__pycache__" -delete + + +tests: + cd chapters; pytest --nbmake chap*.ipynb + # many of the examples don't pass tests without the solutions + # cd examples; pytest --nbmake *.ipynb diff --git a/ModSimPyNotebooks.zip b/ModSimPyNotebooks.zip new file mode 100644 index 000000000..2d5f007dc Binary files /dev/null and b/ModSimPyNotebooks.zip differ diff --git a/README.md b/README.md index 4b4c260f1..845d86c47 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,14 @@ -# ModSimPython -Text and supporting code for Modeling and Simulation in Python +# Modeling and Simulation in Python -You can run the code from the repository in a browser by pressing the Binder button below. - -[![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/AllenDowney/ModSimPy/master) +Order the book from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) or [Amazon](https://amzn.to/3y9UxNb). +[Read the book and run the code](https://allendowney.github.io/ModSimPy/). *Modeling and Simulation in Python* is an introduction to physical modeling using a computational approach. It is organized in three parts: * The first part presents discrete models, including a bikeshare system and world population growth. -* The second part introduces first-order systems, including models of infectious disease, thermal systems, and chemical kinetics. +* The second part introduces first-order systems, including models of infectious disease, thermal systems, and pharmacokinetics. * The third part is about second-order systems, including mechanical systems like projectiles, celestial mechanics, and rotating rigid bodies. @@ -20,166 +18,5 @@ Python is an ideal programming language for this material. It is a good first l *Modeling and Simulation in Python* is a Free Book. It is available under the [Creative Commons Attribution-NonCommercial 4.0 Unported License](https://creativecommons.org/licenses/by-nc/4.0/), which means that you are free to copy, distribute, and modify it, as long as you attribute the work and don’t use it for commercial purposes. -[This](http://greenteapress.com/wp/modsimpy/) and other Free Books by Allen Downey are available from [Green Tea Press](http://greenteapress.com/wp). - - -Getting started ---------------- - -To run the examples and work on the exercises in this book, you have to: - -1. Install Python on your computer, along with the libraries we will - use. - -2. Copy my files onto your computer. - -3. Run Jupyter, which is a tool for running and writing programs, and - load a **notebook**, which is a file that contains code and - text. - -The next three sections provide details for these steps. I wish there -were an easier way to get started; it’s regrettable that you have to do -so much work before you write your first program. Be persistent! - -Installing Python ------------------ - -You might already have Python installed on your computer, but you might -not have the latest version. To use the code in this book, you need -Python 3.6 or later. Even if you have the latest version, you probably -don’t have all of the libraries we need. - -You could update Python and install these libraries, but I strongly -recommend that you don’t go down that road. I think you will find it -easier to use **Anaconda**, which is a free Python distribution that -includes all the libraries you need for this book (and more). - -Anaconda is available for Linux, macOS, and Windows. By default, it puts -all files in your home directory, so you don’t need administrator (root) -permission to install it, and if you have a version of Python already, -Anaconda will not remove or modify it. - -Start at . Download the installer for -your system and run it. You don’t need administrative privileges to -install Anaconda, so I recommend you run the installer as a normal user, -not as administrator or root. - -I suggest you accept the recommended options. On Windows you have the -option to install Visual Studio Code, which is an interactive -environment for writing programs. You won’t need it for this book, but -you might want it for other projects. - -By default, Anaconda installs most of the packages you need, but there -are a few more you might have to add. Once the installation is complete, -open a command window. On macOS or Linux, you can use Terminal. On -Windows, open Git Bash. - -Run the following command (copy and paste it if you can, to avoid -typos): - -``` -conda install jupyterlab pandas seaborn sympy beautifulsoup4 lxml -``` - -Some of these packages might already be installed. Then run this -command: - -``` -conda install -c unidata pint -``` - -That should be everything you need. - -Copying my files ----------------- - -The code for this book is available from -, which is a **Git -repository**. Git is a software tool that helps you keep track of the -programs and other files that make up a project. A collection of files -under Git’s control is called a repository (the cool kids call it a -“repo"). GitHub is a hosting service that provides storage for Git -repositories and a convenient web interface. - -Before you download these files, I suggest you copy my repository on -GitHub, which is called **forking**. If you don’t already have a -GitHub account, you’ll need to create one. - -Use a browser to view the homepage of my repository at -. You should see a gray button -in the upper right that says **Fork**. If you press it, GitHub will -create a copy of my repository that belongs to you. - -Now, the best way to download the files is to use a **Git client**, -which is a program that manages git repositories. You can get -installation instructions for Windows, macOS, and Linux at -. - -In Windows, I suggest you accept the options recommended by the -installer, with two exceptions: - -- As the default editor, choose instead of . - -- For “Configuring line ending conversions", select “Check out as is, - commit as is". - -For macOS and Linux, I suggest you accept the recommended options. - -Once the installation is complete, open a command window. On Windows, -open Git Bash, which should be in your Start menu. On macOS or Linux, -you can use Terminal. - -To find out what directory you are in, type , which stands for “print -working directory". On Windows, most likely you are in . On MacOS or -Linux, you are probably in your home directory, . - -The next step is to copy files from your repository on GitHub to your -computer; in Git vocabulary, this process is called **cloning**. Run -this command: - -``` -git clone https://github.com/YourGitHubUserName/ModSimPy -``` - -Of course, you should replace with your GitHub user name. After cloning, -you should have a new directory called . - -If you don’t want to use Git, you can download my files in a Zip archive -from . You will need a program like WinZip or -gzip to unpack the Zip file. Make a note of the location of the files -you download. - -Running Jupyter ---------------- - -The code for each chapter, and starter code for the exercises, is in -Jupyter notebooks. If you have not used Jupyter before, you can read -about it at . - -To start Jupyter on macOS or Linux, open a Terminal; on Windows, open -Git Bash. Use to “change directory" into the code directory in the -repository: - -``` -cd ModSimPy/code -``` - -Then launch the Jupyter notebook server: - -``` -jupyter notebook -``` - -Jupyter should open a window in a browser, and you should see the list -of notebooks in my repository. Click on the first notebook, and follow -the instructions to run the first few “cells". The first time you run a -notebook, it might take several seconds to start, while some Python -files get initialized. After that, it should run faster. - -Feel free to read through the notebook, but it might not make sense -until you read Chapter 1. +This and other Free Books by Allen Downey are available from [Green Tea Press](http://greenteapress.com/wp). -You can also launch Jupyter from the Start menu on Windows, the Dock on -macOS, or the Anaconda Navigator on any system. If you do that, Jupyter -might start in your home directory or somewhere else in your file -system, so you might have to navigate to find the directory. diff --git a/README.rst b/README.rst new file mode 100644 index 000000000..46fe4ed49 --- /dev/null +++ b/README.rst @@ -0,0 +1,214 @@ +ModSimPython +============ + +Supporting code for Modeling and Simulation in Python. `This `__ and other Free Books by +Allen Downey are available from `Green Tea +Press `__. + +You can run the code from the repository in a browser by pressing the +Binder button below. + +|Binder| + +*Modeling and Simulation in Python* is an introduction to physical +modeling using a computational approach. It is organized in three parts: + +- The first part presents discrete models, including a bikeshare system + and world population growth. + +- The second part introduces first-order systems, including models of + infectious disease, thermal systems, and pharmacokinetics. + +- The third part is about second-order systems, including mechanical + systems like projectiles, celestial mechanics, and rotating rigid + bodies. + +Taking a computational approach makes it possible to work with more +realistic models than what you typically see in a first-year physics +class, with the option to include features like friction and drag. + +Python is an ideal programming language for this material. It is a good +first language for people who have not programmed before, and it +provides high-level data structures that are well-suited to express +solutions to the problems we are interested in. + +*Modeling and Simulation in Python* is a Free Book. It is available +under the `Creative Commons Attribution-NonCommercial 4.0 Unported +License `__, which +means that you are free to copy, distribute, and modify it, as long as +you attribute the work and don't use it for commercial purposes. + + + +Getting started +--------------- + +To run the examples and work on the exercises in this book, you have to: + +1. Install Python on your computer, along with the libraries we will + use. + +2. Copy my files onto your computer. + +3. Run Jupyter, which is a tool for running and writing programs, and + load a **notebook**, which is a file that contains code and text. + +The next three sections provide details for these steps. I wish there +were an easier way to get started; it's regrettable that you have to do +so much work before you write your first program. Be persistent! + +Installing Python +----------------- + +You might already have Python installed on your computer, but you might +not have the latest version. To use the code in this book, you need +Python 3.6 or later. Even if you have the latest version, you probably +don't have all of the libraries we need. + +You could update Python and install these libraries, but I strongly +recommend that you don't go down that road. I think you will find it +easier to use **Anaconda**, which is a free Python distribution that +includes all the libraries you need for this book (and more). + +Anaconda is available for Linux, macOS, and Windows. By default, it puts +all files in your home directory, so you don't need administrator (root) +permission to install it, and if you have a version of Python already, +Anaconda will not remove or modify it. + +Start at `the Anaconda download +page `__. +Download the installer for your system and run it. You don't need +administrative privileges to install Anaconda, so I recommend you run +the installer as a normal user, not as administrator or root. + +I suggest you accept the recommended options. On Windows you have the +option to install Visual Studio Code, which is an interactive +environment for writing programs. You won't need it for this book, but +you might want it for other projects. + +By default, Anaconda installs most of the packages you need, but there +are a few more you have to add. Once the installation is complete, open +a command window. On macOS or Linux, you can use Terminal. On Windows, +open the Anaconda Prompt that should be in your Start menu. + +Run the following command (copy and paste it if you can, to avoid +typos): + +:: + + conda install jupyterlab pandas seaborn sympy beautifulsoup4 lxml html5lib pytables + +To install Pint, run this command: + +:: + + conda install -c unidata pint + +And to install the ModSim library, run this command: + +:: + + pip install modsim + +That should be everything you need. + +Copying my files +---------------- + +The simplest way to get the files for this book is to download a `Zip +archive from +GitHub `__. +You will need a program like WinZip or gzip to unpack the Zip file. Make +a note of the location of the files you download. + +If you download the Zip file, you can skip the rest of this section, +which explains how to use Git. + +The code for this book is available from +https://github.com/AllenDowney/ModSimPy, which is a **Git repository**. +Git is a software tool that helps you keep track of the programs and +other files that make up a project. A collection of files under Git's +control is called a repository (the cool kids call it a "repo"). GitHub +is a hosting service that provides storage for Git repositories and a +convenient web interface. + +Before you download these files, I suggest you copy my repository on +GitHub, which is called **forking**. If you don't already have a GitHub +account, you'll need to create one. + +Use a browser to view the homepage of my repository at +https://github.com/AllenDowney/ModSimPy. You should see a gray button in +the upper right that says Fork. If you press it, GitHub will create a +copy of my repository that belongs to you. + +Now, the best way to download the files is to use a **Git client**, +which is a program that manages git repositories. You can get +installation instructions for Windows, macOS, and Linux at +http://modsimpy.com/getgit. + +In Windows, I suggest you accept the options recommended by the +installer, with two exceptions: + +- As the default editor, choose instead of . + +- For "Configuring line ending conversions", select "Check out as is, + commit as is". + +For macOS and Linux, I suggest you accept the recommended options. + +Once the installation is complete, open a command window. On Windows, +open Git Bash, which should be in your Start menu. On macOS or Linux, +you can use Terminal. + +To find out what directory you are in, type "pwd", which stands for "print +working directory". On Windows, most likely you are in . On MacOS or +Linux, you are probably in your home directory, . + +The next step is to copy files from your repository on GitHub to your +computer; in Git vocabulary, this process is called **cloning**. Run +this command: + +:: + + git clone https://github.com/YourGitHubUserName/ModSimPy + +Of course, you should replace with your GitHub user name. After cloning, +you should have a new directory called . + +Running Jupyter +--------------- + +The code for each chapter, and starter code for the exercises, is in +Jupyter notebooks. If you have not used Jupyter before, you can read +about it at https://jupyter.org. + +To start Jupyter on macOS or Linux, open a Terminal; on Windows, open +Git Bash. Use "cd" to change directory into the code directory in the +repository: + +:: + + cd ModSimPy/code + +Then launch the Jupyter notebook server: + +:: + + jupyter notebook + +Jupyter should open a window in a browser, and you should see the list +of notebooks in my repository. Click on the first notebook, and follow +the instructions to run the first few "cells". The first time you run a +notebook, it might take several seconds to start, while some Python +files get initialized. After that, it should run faster. + +Feel free to read through the notebook, but it might not make sense +until you read Chapter 1. + +You can also launch Jupyter from the Start menu on Windows, the Dock on +macOS, or the Anaconda Navigator on any system. If you do that, Jupyter +might start in your home directory or somewhere else in your file +system, so you might have to navigate to find the directory. + +.. |Binder| image:: https://mybinder.org/badge.svg + :target: https://mybinder.org/v2/gh/AllenDowney/ModSimPy/master?filepath=notebooks diff --git a/apt.txt b/apt.txt new file mode 100644 index 000000000..a29c57dba --- /dev/null +++ b/apt.txt @@ -0,0 +1,9 @@ +texlive-latex-base +texlive-latex-recommended +texlive-generic-recommended +texlive-science +texlive-latex-extra +texlive-fonts-recommended +texlive-xetex +dvipng +ghostscript diff --git a/book/Makefile b/book/Makefile deleted file mode 100644 index d0910f452..000000000 --- a/book/Makefile +++ /dev/null @@ -1,80 +0,0 @@ -LATEX = latex - -DVIPS = dvips - -PDFFLAGS = -dCompatibilityLevel=1.4 -dPDFSETTINGS=/prepress \ - -dCompressPages=true -dUseFlateCompression=true \ - -dEmbedAllFonts=true -dSubsetFonts=true -dMaxSubsetPct=100 - - -%.dvi: %.tex - $(LATEX) $< - -%.ps: %.dvi - $(DVIPS) -o $@ $< - -%.pdf: %.ps - ps2pdf $(PDFFLAGS) $< - -all: book.tex - pdflatex $(PDFFLAGS) --shell-escape book - makeindex book.idx - evince book.pdf - -hevea: - #sed 's/\(figs\/[^.]*\).\(pdf\|png\)/\1.eps/' - cp book.tex ModSimPy.tex - rm -rf html - mkdir html - hevea -O -e latexonly htmlonly ModSimPy -# the following greps are a kludge to prevent imagen from seeing -# the definitions in latexonly, and to avoid headers on the images - grep -v latexonly ModSimPy.image.tex > a; mv a ModSimPy.image.tex - grep -v fancyhdr ModSimPy.image.tex > a; mv a ModSimPy.image.tex - imagen -png ModSimPy - hacha ModSimPy.html - cp up.png next.png back.png html - mv index.html ModSimPy.css ModSimPy*.html html - mv ModSimPy*.png *motif.gif html - -DEST = /home/downey/public_html/greent/ModSimPy/ -DIST_FILES = ModSimPy.pdf - -distrib: - cp book.pdf ModSimPy.pdf - rsync -a $(DIST_FILES) $(DEST) - #chmod -R o+r $(DEST)/* - cd /home/downey/public_html/greent; sh back - -plastex: - # Before running plastex, we need the current directory in PYTHONPATH - # export PYTHONPATH=$PYTHONPATH:. - # rm -rf /home/downey/ModSimPy/book/.svn - plastex --renderer=DocBook --theme=book --image-resolution=300 --filename=book.xml book.tex - # rm -rf /home/downey/ModSimPy/trunk/book/.svn - -xxe: - xmlcopyeditor ~/ModSimPy/book/book/book.xml & - #~/Downloads/xxe-perso-4_8_0/bin/xxe book/book.xml - -lint: - xmllint -noout book/book.xml - -OREILLY = atlas - -oreilly: - rsync -a book/ $(OREILLY) - rsync -a figs/* $(OREILLY)/figs/ - cd $(OREILLY); git add . - cd $(OREILLY); git commit -m "Automated check in." - cd $(OREILLY); git push - -clean: - rm -f *~ *.aux *.log *.dvi *.idx *.ilg *.ind *.toc - -split: - cp book.pdf ModSimPy.pdf - rm -f ModSimPyChapters/* - ~/Downloads/sejda-console-3.2.51/bin/sejda-console splitbybookmarks -f ModSimPy.pdf -l 1 -o ModSimPyChapters - - diff --git a/book/back.png b/book/back.png deleted file mode 100644 index 9c8847276..000000000 Binary files a/book/back.png and /dev/null differ diff --git a/book/book.tex b/book/book.tex deleted file mode 100644 index c92cfc695..000000000 --- a/book/book.tex +++ /dev/null @@ -1,7937 +0,0 @@ -% LaTeX source for ``Modeling and Simulation in Python'' -% Copyright 2017 Allen B. Downey. - -% License: Creative Commons Attribution-NonCommercial 4.0 Unported License. -% https://creativecommons.org/licenses/by-nc/4.0/ -% - -\documentclass[12pt]{book} - -\title{Modeling and Simulation in Python} -\author{Allen B. Downey} - -\newcommand{\thetitle}{Modeling and Simulation in Python} -\newcommand{\thesubtitle}{} -\newcommand{\theauthors}{Allen B. Downey} -\newcommand{\theversion}{2.3} - - -%%%% Both LATEX and PLASTEX - -\usepackage{graphicx} -\usepackage{hevea} -\usepackage{makeidx} -\usepackage{setspace} -\usepackage{xcolor} -\usepackage{upquote} -\usepackage[listings]{tcolorbox} - - -% to get siunitx -% sudo apt-get install texlive-science -\usepackage{siunitx} -\sisetup{per-mode=symbol} - -\definecolor{light-gray}{gray}{0.95} - -\newtcblisting{python}{ - skin=standard, - boxrule=0.4pt, - colback=light-gray, - listing only, - top=0pt, - bottom=0pt, - left=0pt, - right=0pt, - boxsep=2pt, - listing options={ - basicstyle=\ttfamily, - language=python, - showstringspaces=false, - }, -} - -\newtcblisting{result}{ - skin=standard, - boxrule=0.0pt, - colback=white, - listing only, - top=0pt, - bottom=0pt, - left=0pt, - right=0pt, - boxsep=2pt, - listing options={ - basicstyle=\ttfamily, - language=python, - showstringspaces=false, - }, -} - -\makeindex - -% automatically index glossary terms -\newcommand{\term}[1]{% -\item[#1:]\index{#1}} - -\usepackage{amsmath} -\usepackage{amsthm} - -% format end of chapter excercises -\newtheoremstyle{exercise} - {12pt} % space above - {12pt} % space below - {} % body font - {} % indent amount - {\bfseries} % head font - {} % punctuation - {12pt} % head space - {} % custom head -\theoremstyle{exercise} -\newtheorem{exercise}{Exercise}[chapter] - -\usepackage{afterpage} - -\newcommand\blankpage{% - \null - \thispagestyle{empty}% - \addtocounter{page}{-1}% - \newpage} - -\newif\ifplastex -\plastexfalse - -%%%% PLASTEX ONLY -\ifplastex - -\usepackage{localdef} - -\usepackage{url} - -\newcount\anchorcnt -\newcommand*{\Anchor}[1]{% - \@bsphack% - \Hy@GlobalStepCount\anchorcnt% - \edef\@currentHref{anchor.\the\anchorcnt}% - \Hy@raisedlink{\hyper@anchorstart{\@currentHref}\hyper@anchorend}% - \M@gettitle{}\label{#1}% - \@esphack% -} - -% code listing environments: -% we don't need these for plastex because they get replaced -% by preprocess.py -%\newenvironment{code}{\begin{code}}{\end{code}} -%\newenvironment{stdout}{\begin{code}}{\end{code}} - -% inline syntax formatting -\newcommand{\py}{\verb}%} - -%%%% LATEX ONLY -\else - -\input{latexonly} - -\fi - -%%%% END OF PREAMBLE -\begin{document} - -\frontmatter - -%%%% PLASTEX ONLY -\ifplastex - -\maketitle - -%%%% LATEX ONLY -\else - -\begin{latexonly} - -%-half title-------------------------------------------------- -%\thispagestyle{empty} -% -%\begin{flushright} -%\vspace*{2.0in} -% -%\begin{spacing}{3} -%{\huge \thetitle} -%\end{spacing} -% -%\vspace{0.25in} -% -%Version \theversion -% -%\vfill -% -%\end{flushright} - -%--verso------------------------------------------------------ - -%\afterpage{\blankpage} - -%\newpage -%\newpage -%\clearemptydoublepage -%\pagebreak -%\thispagestyle{empty} -%\vspace*{6in} - -%--title page-------------------------------------------------- -\pagebreak -\thispagestyle{empty} - -\begin{flushright} -\vspace*{2.0in} - -\begin{spacing}{3} -{\huge \thetitle} -\end{spacing} - -\vspace{0.25in} - -Version \theversion - -\vspace{1in} - - -{\Large -\theauthors \\ -} - - -\vspace{0.5in} - -{\Large Green Tea Press} - -{\small Needham, Massachusetts} - -%\includegraphics[width=1in]{figs/logo1.eps} -\vfill - -\end{flushright} - - - -%--copyright-------------------------------------------------- -\pagebreak -\thispagestyle{empty} - -Copyright \copyright ~2017 \theauthors. - - - -\vspace{0.2in} - -\begin{flushleft} -Green Tea Press \\ -9 Washburn Ave \\ -Needham MA 02492 -\end{flushleft} - -Permission is granted to copy, distribute, transmit and adapt this work under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License: \url{http://modsimpy.com/license}. - -% TODO: get the shortened URLs working with https - -If you are interested in distributing a commercial version of this -work, please contact the author. - -The \LaTeX\ source and code for this book is available from - -\begin{code} -https://github.com/AllenDowney/ModSimPy -\end{code} - -%--table of contents------------------------------------------ - -\cleardoublepage -\setcounter{tocdepth}{1} -\tableofcontents - -\end{latexonly} - - -% HTML title page------------------------------------------ - -\begin{htmlonly} - -\vspace{1em} - -{\Large \thetitle} - -{\large \theauthors} - -Version \theversion - -\vspace{1em} - -Copyright \copyright ~2017 \theauthors. - -Permission is granted to copy, distribute, and/or modify this work -under the terms of the Creative Commons -Attribution-NonCommercial-ShareAlike 4.0 International License, which is -available at \url{http://modsimpy.com/license}. - -\vspace{1em} - -\setcounter{chapter}{-1} - -\end{htmlonly} - -% END OF THE PART WE SKIP FOR PLASTEX -\fi - -\chapter{Preface} -\label{preface} - - -\section{Can modeling be taught?} - -The essential skills of modeling --- abstraction, analysis, simulation, and validation --- are central in engineering, natural sciences, social sciences, medicine, and many other fields. Some students learn these skills implicitly, but in most schools they are not taught explicitly, and students get little practice. That's the problem this book is meant to address. - -At Olin College, we use this book in a class called Modeling and Simulation, which all students take in their first semester. My colleagues, John Geddes and Mark Somerville, and I developed this class and taught it for the first time in 2009. - -It is based on our belief that modeling should be taught explicitly, early, and throughout the curriculum. It is also based on our conviction that computation is an essential part of this process. - -If students are limited to the mathematical analysis they can do by hand, they are restricted to a small number of simple physical systems, like a projectile moving in a vacuum or a block on a frictionless plane. - -And they will only work with bad models; that is, models that are too simple for their intended purpose. In nearly every mechanical system, air resistance and friction are essential features; if we ignore them, our predictions will be wrong and our designs won't work. - -In most freshman physics classes, students don't make modeling decisions; sometimes they are not even aware of the decisions that have been made for them. Our goal is to teach the entire modeling process and give students a chance to practice it. - - -\section{How much programming do I need?} - -If you have never programmed before, you should be able to read this book, understand it, and do the exercises. I will do my best to explain everything you need to know; in particular, I have chosen carefully the vocabulary I introduce, and I try to define each term the first time it is used. If you find that I have used a term without defining it, let me know. - -If you have programmed before, you will have an easier time getting started, but you might be uncomfortable in some places. I take an approach to programming you have probably not seen before. - -Most programming classes\footnote{Including many I have taught.} have two big problems: - -\begin{enumerate} - -\item They go ``bottom up", starting with basic language features and gradually adding more powerful tools. As a result, it takes a long time before students can do anything more interesting than convert Fahrenheit to Celsius. - -\index{bottom up} - -\item They have no context. Students learn to program with no particular goal in mind, so the exercises span an incoherent collection of topics, and the exercises tend to be unmotivated. - -\end{enumerate} - -In this book, you learn to program with an immediate goal in mind: writing simulations of physical systems. And we proceed ``top down", by which I mean we use professional-strength data structures and language features right away. In particular, we use the following Python {\bf libraries}: - -\index{top down} - -\begin{itemize} - -\item NumPy for basic numerical computation (see \url{https://www.numpy.org/}). - -\index{NumPy} - -\item SciPy for scientific computation (see \url{https://www.scipy.org/}). - -\index{SciPy} - -\item Matplotlib for visualization (see \url{https://matplotlib.org/}). - -\index{Matplotlib} - -\item Pandas for working with data (see \url{https://pandas.pydata.org/}). - -\index{Pandas} - -\item SymPy for symbolic computation, (see \url{https://www.sympy.org}). - -\index{SymPy} - -\item Pint for units like kilograms and meters (see \url{https://pint.readthedocs.io}). - -\index{Pint} - -\item Jupyter for reading, running, and developing code (see \url{https://jupyter.org}). - -\index{Jupyter} - -\end{itemize} - -These tools let you work on more interesting programs sooner, but there are some drawbacks: they can be hard to use, and it can be challenging to keep track of which library does what and how they interact. - -I have tried to mitigate these problems by providing a library, called \py{modsim}, that makes it easier to get started with these tools, and provides some additional capabilities. - -\index{modsim library} - -Some features in the \py{modsim} library are like training wheels; at some point you will probably stop using them and start working with the underlying libraries directly. Other features you might find useful the whole time you are working through the book, and later. - -I encourage you to read the the \py{modsim} library code. Most of it is not complicated, and I tried to make it readable. Particularly if you have some programming experience, you might learn something by reverse engineering my design decisions. - - -\section{How much math and science do I need?} - -I assume that you know what derivatives and integrals are, but that's about all. In particular, you don't need to know (or remember) much about finding derivatives or integrals of functions analytically. If you know the derivative of $x^2$ and you can integrate $2x~dx$, that will do it\footnote{And if you noticed that those two questions answer each other, even better.}. More importantly, you should understand what those concepts {\em mean}; but if you don't, this book might help you figure it out. - -\index{calculus} - -You don't have to know anything about differential equations. - -As for science, we will cover topics from a variety of fields, including demography, epidemiology, medicine, thermodynamics, and mechanics. For the most part, I don't assume you know anything about these topics. In fact, one of the skills you need to do modeling is the ability to learn enough about new fields to develop models and simulations. - -When we get to mechanics, I assume you understand the relationship between position, velocity, and acceleration, and that you are familiar with Newton's laws of motion, especially the second law, which is often expressed as $F = ma$ (force equals mass times acceleration). - -\index{science} -\index{mechanics} - -I think that's everything you need, but if you find that I left something out, please let me know. - - -\section{Getting started} -\label{code} - -To run the examples and work on the exercises in this book, you have to: - -\begin{enumerate} - -\item Install Python on your computer, along with the libraries we will use. - -\item Copy my files onto your computer. - -\item Run Jupyter, which is a tool for running and writing programs, and load a {\bf notebook}, which is a file that contains code and text. - -\end{enumerate} - -The next three sections provide details for these steps. I wish there were an easier way to get started; it's regrettable that you have to do so much work before you write your first program. Be persistent! - - -\section{Installing Python} - -You might already have Python installed on your computer, but you might not have the latest version. To use the code in this book, you need Python 3.6 or later. Even if you have the latest version, you probably don't have all of the libraries we need. - -\index{installing Python} - -You could update Python and install these libraries, but I strongly recommend that you don't go down that road. I think you will find it easier to use {\bf Anaconda}, which is a free Python distribution that includes all the libraries you need for this book (and more). - -\index{Anaconda} - -Anaconda is available for Linux, macOS, and Windows. By default, it puts all files in your home directory, so you don't need administrator (root) permission to install it, and if you have a version of Python already, Anaconda will not remove or modify it. - -Start at \url{https://www.anaconda.com/download}. Download the installer for your system and run it. You don't need administrative privileges to install Anaconda, so I recommend you run the installer as a normal user, not as administrator or root. - -I suggest you accept the recommended options. -On Windows you have the option to install Visual Studio Code, which is an interactive environment for writing programs. You won't need it for this book, but you might want it for other projects. - -By default, Anaconda installs most of the packages you need, but there are a few more you might have to add. Once the installation is complete, open a command window. On macOS or Linux, you can use Terminal. On Windows, open Git Bash. - -Run the following command (copy and paste it if you can, to avoid typos): - -\begin{code} -conda install jupyterlab pandas seaborn sympy beautifulsoup4 lxml -\end{code} - -Some of these packages might already be installed. Then run this command: - -\begin{code} -conda install -c unidata pint -\end{code} - -That should be everything you need. - - -\section{Copying my files} - -The code for this book is available from -\url{https://github.com/AllenDowney/ModSimPy}, which is a {\bf Git repository}. Git is a software tool that helps you keep track of the programs and other files that make up a project. A collection of files under Git's control is called a repository (the cool kids call it a ``repo"). GitHub is a hosting service that provides storage for Git repositories and a convenient web interface. - -\index{repository} -\index{Git} -\index{GitHub} - -Before you download these files, I suggest you copy my repository on GitHub, which is called {\bf forking}. If you don't already have a GitHub account, you'll need to create one. - -Use a browser to view the homepage of my repository at \url{https://github.com/AllenDowney/ModSimPy}. You should see a gray button in the upper right that says {\sf Fork}. If you press it, GitHub will create a copy of my repository that belongs to you. - -Now, the best way to download the files is to use a {\bf Git client}, which is a program that manages git repositories. You can get installation instructions for Windows, macOS, and Linux at \url{http://modsimpy.com/getgit}. - -In Windows, I suggest you accept the options recommended by the installer, with two exceptions: - -\begin{itemize} - -\item As the default editor, choose \py{nano} instead of \py{vim}. - -\item For ``Configuring line ending conversions", select ``Check out as is, commit as is". - -\end{itemize} - -For macOS and Linux, I suggest you accept the recommended options. - -Once the installation is complete, open a command window. On Windows, open Git Bash, which should be in your Start menu. On macOS or Linux, you can use Terminal. - -To find out what directory you are in, type \py{pwd}, which stands for ``print working directory". On Windows, most likely you are in \py{Users\\yourusername}. On MacOS or Linux, you are probably in your home directory, \py{/home/yourusername}. - -The next step is to copy files from your repository on GitHub to your computer; in Git vocabulary, this process is called {\bf cloning}. Run this command: - -\begin{python} -git clone https://github.com/YourGitHubUserName/ModSimPy -\end{python} - -Of course, you should replace \py{YourGitHubUserName} with your GitHub user name. After cloning, you should have a new directory called \py{ModSimPy}. - -If you don't want to use Git, you can download my files -in a Zip archive from \url{http://modsimpy.com/zip}. You will need a program like WinZip or gzip to unpack the Zip file. Make a note of the location of the files you download. - - -\section{Running Jupyter} - -The code for each chapter, and starter code for the exercises, is in -Jupyter notebooks. If you have not used Jupyter before, you can read -about it at \url{https://jupyter.org}. - -\index{Jupyter} - -To start Jupyter on macOS or Linux, open a Terminal; on Windows, open Git Bash. Use \py{cd} to ``change directory" into the code directory in the repository: - -\begin{code} -cd ModSimPy/code -\end{code} - -Then launch the Jupyter notebook server: - -\begin{code} -jupyter notebook -\end{code} - -Jupyter should open a window in a browser, and you should see the list of notebooks in my repository. Click on the first notebook, \py{chap01.ipynb} and follow the instructions to run the first few ``cells". The first time you run a notebook, it might take several seconds to start, while some Python files get initialized. After that, it should run faster. - -Feel free to read through the notebook, but it might not make sense until you read Chapter~\ref{chap01}. - -You can also launch Jupyter from the Start menu on Windows, the Dock on macOS, or the Anaconda Navigator on any system. If you do that, Jupyter might start in your home directory or somewhere else in your file system, so you might have to navigate to find the \py{ModSimPy} directory. - - -\section*{Contributor List} - -If you have a suggestion or correction, send it to -{\tt downey@allendowney.com}. Or if you are a Git user, send me a pull request! - -If I make a change based on your feedback, I will add you to the contributor list, unless you ask to be omitted. -\index{contributors} - -If you include at least part of the sentence the error appears in, that makes it easy for me to search. Page and section numbers are fine, too, but not as easy to work with. Thanks! - -\begin{itemize} - -\item I am grateful to John Geddes and Mark Somerville for their early collaboration with me to create Modeling and Simulation, the class at Olin College this book is based on. - -\item My early work on this book benefited from conversations with -my amazing colleagues at Olin College, including John Geddes, Alison -Wood, Chris Lee, and Jason Woodard. - -\item I am grateful to Lisa Downey and Jason Woodard for their thoughtful and careful copy editing. - -\item Thanks to Alessandra Ferzoco, Erhardt Graeff, Emily Tow, -Kelsey Houston-Edwards, Linda Vanasupa, Matt Neal, Joanne Pratt, and Steve Matsumoto for their helpful suggestions. - -% ENDCONTRIB - -\end{itemize} - - - -\normalsize - -\cleardoublepage - -% TABLE OF CONTENTS -\begin{latexonly} - -% \tableofcontents - -\cleardoublepage - -\end{latexonly} - -% START THE BOOK -\mainmatter - - -\chapter{Modeling} -\label{chap01} - -This book is about modeling and simulation of physical systems. -The following diagram shows what I mean by ``modeling": - -\index{modeling} - -\vspace{0.2in} -\centerline{\includegraphics[height=3in]{figs/modeling_framework.pdf}} - -Starting in the lower left, the {\bf system} is something in the real world we are interested in. Often, it is something complicated, so we have to decide which details can be left out; removing details is called {\bf abstraction}. - -\index{system} - -The result of abstraction is a {\bf model}, which is a description of the system that includes only the features we think are essential. A model can be represented in the form of diagrams and equations, which can be used for mathematical {\bf analysis}. It can also be implemented in the form of a computer program, which can run {\bf simulations}. - -\index{model} -\index{abstraction} -\index{analysis} - -The result of analysis and simulation might be a {\bf prediction} about what the system will do, an {\bf explanation} of why it behaves the way it does, or a {\bf design} intended to achieve a purpose. - -\index{prediction} -\index{explanation} -\index{design} - -We can {\bf validate} predictions and test designs by taking {\bf measurements} from the real world and comparing the {\bf data} we get with the results from analysis and simulation. - -\index{validation} -\index{data} - -For any physical system, there are many possible models, each one including and excluding different features, or including different levels of detail. The goal of the modeling process is to find the model best suited to its purpose (prediction, explanation, or design). - -\index{iterative modeling} - -Sometimes the best model is the most detailed. If we include more features, the model is more realistic, and we expect its predictions to be more accurate. - -\index{realism} - -But often a simpler model is better. If we include only the essential features and leave out the rest, we get models that are easier to work with, and the explanations they provide can be clearer and more compelling. - -\index{simplicity} - -As an example, suppose someone asked you why the orbit of the Earth is nearly elliptical. If you model the Earth and Sun as point masses (ignoring their actual size), compute the gravitational force between them using Newton's law of universal gravitation, and compute the resulting orbit using Newton's laws of motion, you can show that the result is an ellipse. - -\index{orbit} -\index{ellipse} - -Of course, the actual orbit of Earth is not a perfect ellipse, because of the gravitational forces of the Moon, Jupiter, and other objects in the solar system, and because Newton's laws of motion are only approximately true (they don't take into account relativistic effects). - -\index{Newton} -\index{relativity} - -But adding these features to the model would not improve the explanation; more detail would only be a distraction from the fundamental cause. However, if the goal is to predict the position of the Earth with great precision, including more details might be necessary. - -Choosing the best model depends on what the model is for. It is usually a good idea to start with a simple model, even if it is likely to be too simple, and test whether it is good enough for its purpose. Then you can add features gradually, starting with the ones you expect to be most essential. This process is called {\bf iterative modeling}. - -Comparing results of successive models provides a form of {\bf internal validation}, so you can catch conceptual, mathematical, and software errors. And by adding and removing features, you can tell which ones have the biggest effect on the results, and which can be ignored. - -\index{internal validation} -\index{validation!internal} -\index{external validation} -\index{validation!external} - -Comparing results to data from the real world provides {\bf external validation}, which is generally the strongest test. - - -\section{The falling penny myth} -\label{penny} - -Let's see an example of how models are used. You might have heard that a penny dropped from the top of the Empire State Building would be going so fast when it hit the pavement that it would be embedded in the concrete; or if it hit a person, it would break their skull. - -\index{Empire State Building} -\index{penny} -\index{myth} - -We can test this myth by making and analyzing a model. To get started, we'll assume that the effect of air resistance is small. This will turn out to be a bad assumption, but bear with me. - -If air resistance is negligible, the primary force acting on the penny is gravity, which causes the penny to accelerate downward. -\index{air resistance} - -If the initial velocity is 0, the velocity after $t$ seconds is $a t$, and the distance the penny has dropped is -% -\[ h = a t^2 / 2 \] -% -Using algebra, we can solve for $t$: -% -\[ t = \sqrt{ 2 h / a} \] -% -Plugging in the acceleration of gravity, $a = \SI{9.8}{\meter\per\second\squared}$, and the height of the Empire State Building, $h=\SI{381}{\meter}$, we get $t = \SI{8.8}{\second}$. Then computing $v = a t$ we get a velocity on impact of $\SI{86}{\meter\per\second}$, which is about 190 miles per hour. That sounds like it could hurt. - -Of course, these results are not exact because the model is based on simplifications. For example, we assume that gravity is constant. In fact, the force of gravity is different on different parts of the globe, and gets weaker as you move away from the surface. But these differences are small, so ignoring them is probably a good choice for this scenario. -\index{gravity} - -On the other hand, ignoring air resistance is not a good choice. Once the penny gets to about \SI{18}{\meter\per\second}, the upward force of air resistance equals the downward force of gravity, so the penny stops accelerating. After that, it doesn't matter how far the penny falls; it hits the sidewalk (or your head) at about \SI{18}{\meter\per\second}, much less than \SI{86}{\meter\per\second}, as the simple model predicts. - -The statistician George Box famously said ``All models are wrong, but some are useful." He was talking about statistical models, but his wise words apply to all kinds of models. Our first model, which ignores air resistance, is very wrong, and probably not useful. In the notebook for this chapter, you will see another model, which assumes that acceleration is constant until the penny reaches terminal velocity. This model is also wrong, but it's better, and it's good enough to refute the myth. - -\index{Box, George} - -The television show {\it Mythbusters} has tested the myth of the falling penny more carefully; you can view the results at \url{http://modsimpy.com/myth}. Their work is based on a mathematical model of motion, measurements to determine the force of air resistance on a penny, and a physical model of a human head. - -\index{Mythbusters} - - -\section{Computation} -\label{computation} - -There are (at least) two ways to work with mathematical models, {\bf analysis} and {\bf simulation}. Analysis often involves algebra and other kinds of symbolic manipulation. Simulation often involves computers. -\index{analysis} -\index{simulation} - -In this book we do some analysis and a lot of simulation; along the way, I discuss the pros and cons of each. The primary tools we use for simulation are the Python programming language and Jupyter, which is an environment for writing and running programs. - -As a first example, I'll show you how I computed the results from the previous section using Python. - -First I create a {\bf variable} to represent acceleration. - -\index{variable} -\index{value} - -\begin{python} -a = 9.8 * meter / second**2 -\end{python} - -A variable is a name that corresponds to a value. In this example, the name is \py{a} and the value is the number \py{9.8} multiplied by the units \py{meter / second**2}. This example demonstrates some of the symbols Python uses to perform mathematical operations: -\index{operator!mathematical} - -\begin{tabular}{l|c} -{\bf Operation} & {\bf Symbol} \\ -\hline -Addition & \py{+} \\ -Subtraction & \py{-} \\ -Multiplication & \py{*} \\ -Division & \py{/} \\ -Exponentiation & \py{**} \\ -\end{tabular} - -Next, we can compute the time it takes for the penny to drop \SI{381}{\meter}, the height of the Empire State Building. - -\begin{python} -h = 381 * meter -t = sqrt(2 * h / a) -\end{python} - -These lines create two more variables: \py{h} gets the height of the building in meters; \py{t} gets the time, in seconds, for the penny to fall to the sidewalk. \py{sqrt} is a {\bf function} that computes square roots. Python keeps track of units, so the result, \py{t}, has the correct units, seconds. -\index{unit} -\index{function} -\index{sqrt} - -Finally, we can compute the velocity of the penny after $t$ seconds: - -\begin{python} -v = a * t -\end{python} - -The result is about \SI{86}{\meter\per\second}, again with the correct units. - -This example demonstrates analysis and computation using Python. Next we'll see an example of simulation. - -Before you go on, you might want to read the notebook for this chapter, \py{chap01.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - -\chapter{Bike share} -\label{chap02} - -This chapter presents a simple model of a bike share system and demonstrates the features of Python we'll use to develop simulations of real-world systems. - -Along the way, we'll make decisions about how to model the system. In the next chapter we'll review these decisions and gradually improve the model. - - -\section{Bikeshare} -\label{modeling} - -Imagine a bike share system for students traveling between Olin College and Wellesley College, which are about 3 miles apart in eastern Massachusetts. - -\index{Wellesley College} -\index{Olin College} - -Suppose the system contains 12 bikes and two bike racks, one at Olin and one at Wellesley, each with the capacity to hold 12 bikes. - -\index{bike share system} - -As students arrive, check out a bike, and ride to the other campus, the number of bikes in each location changes. In the simulation, we'll need to keep track of where the bikes are. To do that, I'll create a \py{State} object, which is defined in the \py{modsim} library. - -\index{State object} - -Before we can use the library, we have to \py{import} it: - -\begin{python} -from modsim import * -\end{python} - -This line of code is an {\bf import statement} that tells Python -to read the file {\tt modsim.py} and make the functions it defines available. - -\index{import statement} - -Functions in the \py{modsim.py} library include \py{sqrt}, which we used in the previous section, and \py{State}, which we are using now. \py{State} creates a \py{State} object, which is a collection of {\bf state variables}. - -\index{state variable} - -\begin{python} -bikeshare = State(olin=10, wellesley=2) -\end{python} - -The state variables, \py{olin} and \py{wellesley}, represent the number of bikes at each location. The initial values are 10 and 2, indicating that there are 10 bikes at Olin and 2 at Wellesley. The \py{State} object created by \py{State} is assigned to a new variable named \py{bikeshare}. - -\index{dot operator} -\index{operator!dot} - -We can read the variables inside a \py{State} object using the {\bf dot operator}, like this: - -\begin{python} -bikeshare.olin -\end{python} - -The result is the value 10. Similarly, for: - -\begin{python} -bikeshare.wellesley -\end{python} - -The result is 2. If you forget what variables a state object has, you can just type the name: - -\begin{python} -bikeshare -\end{python} - -The result looks like a table with the variable names and their values: - -\begin{tabular}{lr} - & {\bf \sf value} \\ -\hline -{\bf \sf olin} & 10 \\ -{\bf \sf wellesley} & 2 \\ -\end{tabular} - -The state variables and their values make up the {\bf state} of the system. We can update the state by assigning new values to the variables. For example, if a student moves a bike from Olin to Wellesley, we can figure out the new values and assign them: - -\index{state} - -\begin{python} -bikeshare.olin = 9 -bikeshare.wellesley = 3 -\end{python} - -Or we can use {\bf update operators}, \py{-=} and \py{+=}, to subtract 1 from \py{olin} and add 1 to \py{wellesley}: - -\index{update operator} -\index{operator!update} - -\begin{python} -bikeshare.olin -= 1 -bikeshare.wellesley += 1 -\end{python} - -The result is the same either way, but the second version is more versatile. - - -\section{Defining functions} - -So far we have used functions defined in \py{modsim} and other libraries. Now we're going to define our own functions. - -\index{function} -\index{defining functions} - -When you are developing code in Jupyter, it is often efficient to -write a few lines of code, test them to confirm they do what -you intend, and then use them to define a new function. For -example, these lines move a bike from Olin to Wellesley: - -\begin{python} -bikeshare.olin -= 1 -bikeshare.wellesley += 1 -\end{python} - -Rather than repeat them every time a bike moves, we can define a -new function: - -\begin{python} -def bike_to_wellesley(): - bikeshare.olin -= 1 - bikeshare.wellesley += 1 -\end{python} - -\py{def} is a special word in Python that indicates we are defining a new -function. The name of the function is \py{bike_to_wellesley}. The empty parentheses indicate that this function requires no additional information when it runs. The colon indicates the beginning of an indented -{\bf code block}. -\index{def} -\index{code block} -\index{body} -\index{indentation} - -The next two lines are the {\bf body} of the function. They have -to be indented; by convention, the indentation is 4 spaces. - -When you define a function, it has no immediate effect. The body -of the function doesn't run until you {\bf call} the function. -Here's how to call this function: -\index{call} - -\begin{python} -bike_to_wellesley() -\end{python} - -When you call the function, it runs the statements in the body, which update the variables of the {\tt bikeshare} object; you can check by displaying -or plotting the new state. - -When you call a function, you have to include the parentheses. If you leave them out, like this: -\index{argument} -\index{parentheses} - -\begin{python} -bike_to_wellesley -\end{python} - -Python looks up the name of the function and displays: - -\begin{python} - -\end{python} - -This result indicates that \py{bike_to_wellesley} is a function. You don't have to know what \py{__main__} means, but if you see something like this, it probably means that you looked up a function but you didn't -actually call it. So don't forget the parentheses. - -Just like \py{bike_to_wellesley}, we can define a function that moves a bike from Wellesley to Olin: - -\begin{python} -def bike_to_olin(): - bikeshare.wellesley -= 1 - bikeshare.olin += 1 -\end{python} - -And call it like this: - -\begin{python} -bike_to_olin() -\end{python} - -One benefit of defining functions is that you avoid repeating chunks -of code, which makes programs smaller. Another benefit is that the -name you give the function documents what it does, which makes programs -more readable. - - -\section{Print statements} - -As you write more complicated programs, it is easy to lose track of what is going on. One of the most useful tools for debugging is the {\bf print statement}, which displays text in the Jupyter notebook. -\index{print statement} -\index{statement!print} - -Normally when Jupyter runs the code in a cell, it displays the value of the last line of code. For example, if you run: - -\begin{python} -bikeshare.olin -bikeshare.wellesley -\end{python} - -Jupyter runs both lines of code, but it only displays the value of the second line. If you want to display more than one value, you can use print statements: - -\begin{python} -print(bikeshare.olin) -print(bikeshare.wellesley) -\end{python} - -\py{print} is a function, so it takes an argument in parentheses. It can also take a sequence of arguments separated by commas, like this: - -\begin{python} -print(bikeshare.olin, bikeshare.wellesley) -\end{python} - -In this example, the two values appear on the same line, separated by a space. - -Print statements are also useful for debugging functions. For example, we can add a print statement to \py{move_bike}, like this: - -\begin{python} -def bike_to_wellesley(): - print('Moving a bike to Wellesley') - bikeshare.olin -= 1 - bikeshare.wellesley += 1 -\end{python} - -Each time we call this version of the function, it displays a message, which can help us keep track of what the program is doing. - -\index{string} - -The argument of this \py{print} is a {\bf string}, which is a sequence of letters and other symbols in quotes. - - - -\section{If statements} - -The \py{modsim} library provides a function called \py{flip} that takes -as an argument a probability between 0 and 1: - -\begin{python} -flip(0.7) -\end{python} - -The result is one of two values: \py{True} with probability 0.7 or \py{False} with probability 0.3. If you run this function 100 times, you should get \py{True} about 70 times and \py{False} about 30 times. But the results are random, so they might differ from these expectations. -\index{flip} -\index{True} -\index{False} - -\py{True} and \py{False} are special values defined by Python. Note -that they are not strings. There is a difference between \py{True}, -which is a special value, and \py{'True'}, which is a string. -\index{string} -\index{boolean} - -\py{True} and \py{False} are called {\bf boolean} values because -they are related to Boolean algebra (\url{http://modsimpy.com/boolean}). - -We can use boolean values to control the behavior of the program, using -an {\bf if statement}: -\index{if statement} -\index{statement!if} - -\begin{python} -if flip(0.5): - print('heads') -\end{python} - -If the result from \py{flip} is \py{True}, the program displays the string \py{'heads'}. Otherwise it does nothing. - -The punctuation for if statements is similar to the punctuation for function definitions: the first line has to end with a colon, and the lines inside the if statement have to be indented. -\index{indentation} -\index{else clause} - -Optionally, you can add an {\bf else clause} to indicate what should happen if the result is \py{False}: - -\begin{python} -if flip(0.5): - print('heads') -else: - print('tails') -\end{python} - -Now we can use \py{flip} to simulate the arrival of students who want to borrow a bike. Suppose students arrive at the Olin station every 2 minutes, on average. In that case, the chance of an arrival during any one-minute period is 50\%, and we can simulate it like this: - -\begin{python} -if flip(0.5): - bike_to_wellesley() -\end{python} - -If students arrive at the Wellesley station every 3 minutes, on average, the chance of an arrival during any one-minute period is 33\%, and we can simulate it like this: - -\begin{python} -if flip(0.33): - bike_to_olin() -\end{python} - -We can combine these snippets into a function that simulates a {\bf time step}, which is an interval of time, in this case one minute: - -\index{time step} - -\begin{python} -def step(): - if flip(0.5): - bike_to_wellesley() - - if flip(0.33): - bike_to_olin() -\end{python} - -Then we can simulate a time step like this: - -\begin{python} -step() -\end{python} - - - -\section{Parameters} - -The previous version of \py{step} is fine if the arrival probabilities never change, but in reality, these probabilities vary over time. - -So instead of putting the constant values 0.5 and 0.33 in \py{step} we can replace them with {\bf parameters}. Parameters are variables whose values are set when a function is called. - -Here's a version of \py{step} that takes two parameters, \py{p1} and \py{p2}: - -\index{probability} - -\begin{python} -def step(p1, p2): - if flip(p1): - bike_to_wellesley() - - if flip(p2): - bike_to_olin() -\end{python} - -The values of \py{p1} and \py{p2} are not set inside this function; instead, they are provided when the function is called, like this: - -\begin{python} -step(0.5, 0.33) -\end{python} - -The values you provide when you call the function are called {\bf arguments}. -The arguments, \py{0.5} and \py{0.33}, get assigned to the parameters, \py{p1} and \py{p2}, in order. So running this function has the same effect as: - -\begin{python} -p1 = 0.5 -p2 = 0.33 - -if flip(p1): - bike_to_wellesley() - -if flip(p2): - bike_to_olin() -\end{python} - -The advantage of using parameters is that you can call the same function many times, providing different arguments each time. Adding parameters to a function is called {\bf generalization}, because it makes the function more general, that is, less specific. - -\index{generalization} - - -\section{For loops} -\label{forloop} - -At some point you will get sick of running cells over and over. Fortunately, there is an easy way to repeat a chunk of code, the {\bf for loop}. Here's an example: -\index{for loop} -\index{loop} - -\begin{python} -for i in range(4): - bike_to_wellesley() -\end{python} - -The punctuation here should look familiar; the first line ends with a colon, and the lines inside the for loop are indented. The other elements of the for loop are: -\index{range} - -\begin{itemize} - -\item The words \py{for} and \py{in} are special words we have to use in a for loop. - -\item \py{range} is a Python function we're using here to control the number of times the loop runs. -\index{range} - -\item \py{i} is a {\bf loop variable} that gets created when the for loop runs. -\index{loop variable} - -\end{itemize} - -In this example we don't actually use \py{i}; we will see examples later where we use the loop variable inside the loop. - -When this loop runs, it runs the statements inside the loop four times, -which moves one bike at a time from Olin to Wellesley. - - -\section{TimeSeries} -\label{timeseries} - -When we run a simulation, we usually want to save the results for later analysis. The \py{modsim} library provides a \py{TimeSeries} object for this purpose. A \py{TimeSeries} contains a sequence of time stamps and a corresponding sequence of values. In this example, the time stamps are integers representing minutes, and the values are the number of bikes at one location. - -%TODO: index modsim library functions -\index{modsim library} -\index{TimeSeries} - -We can create a new, empty \py{TimeSeries} like this: - -\begin{python} -results = TimeSeries() -\end{python} - -And we can add a value to a \py{TimeSeries} like this: - -\begin{python} -results[0] = bikeshare.olin -\end{python} - -The number in brackets is the time stamp, also called a {\bf label}. -\index{label} - -We can use a \py{TimeSeries} inside a for loop to store the results of the simulation: - -\begin{python} -for i in range(10): - step(0.3, 0.2) - results[i] = bikeshare.olin -\end{python} - -Each time through the loop, we call \py{step}, which updates \py{bikeshare}. Then we store the number of bikes at Olin in \py{results}. We use the loop variable, \py{i}, as the time stamp. - -\index{loop} -\index{loop variable} -\index{time stamp} - -When the loop exits, \py{results} contains 10 time stamps, from 0 through 9, and the number of bikes at Olin at the end of each time step. -\index{loop variable} - -\py{TimeSeries} is a specialized version of \py{Series}, which is defined by Pandas, one of the libraries we'll be using extensively. The \py{Series} object provides many functions; one example is \py{mean}, which we can call like this: - -\begin{python} -results.mean() -\end{python} - -You can read the documentation of \py{Series} at \url{http://modsimpy.com/series}. - -\index{Pandas} -\index{Series} -\index{TimeSeries} -\index{mean} - - -\section{Plotting} -\label{plotting} - -The \py{modsim} library provides a function called \py{plot} we can use to plot \py{results}: - -\begin{python} -plot(results) -\end{python} - -\py{plot} can take an additional argument that gives the line a label; this label will appear in the legend of the plot, if we create one. - -\begin{python} -plot(results, label='Olin') -\end{python} - -\py{label} is an example of a {\bf keyword argument}, so called because we provide a ``keyword'', which is \py{label} in this case, along with its value. Arguments without keywords are called {\bf positional arguments} because they are assigned to parameters according to their position. It is good to know these terms because they appear in Python error messages. - -\index{keyword argument} -\index{positional argument} -\index{argument} - -Whenever you make a figure, you should label the axes. The \py{modsim} library provides \py{decorate}, which labels the axes and gives the figure a title and legend: - -\begin{python} -decorate(title='Olin-Wellesley Bikeshare', - xlabel='Time step (min)', - ylabel='Number of bikes') -\end{python} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap01-fig01.pdf}} -\caption{Simulation of a bikeshare system showing number of bikes at Olin over time.} -\label{chap01-fig01} -\end{figure} - -Figure~\ref{chap01-fig01} shows the result. - -\py{plot} and \py{decorate} are based on Pyplot, which is a Python library for generating figures. You can read more about \py{plot} and the arguments it takes at \url{http://modsimpy.com/plot}. - -\index{Pyplot} -\index{plot} -\index{decorate} - -Before you go on, you might want to read the notebook for this chapter, \py{chap02.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - - -\chapter{Iterative modeling} -\label{chap03} - -To paraphrase two Georges, ``All models are wrong, but some models are more wrong than others." In this chapter, I demonstrate the process we use to make models less wrong. - -\index{Box, George} -\index{Orwell, George} - -As an example, we'll review the bikeshare model from the previous chapter, consider its strengths and weaknesses, and gradually improve it. We'll also see ways to use the model to understand the behavior of the system and evaluate designs intended to make it work better. - -\index{bikeshare} - - -\section{Iterative modeling} - -The model we have so far is simple, but it is based on unrealistic assumptions. Before you go on, take a minute to review the model from the previous chapters. What assumptions is it based on? Make a list of ways this model might be unrealistic; that is, what are the differences between the model and the real world? - -Here are some of the differences on my list: - -\begin{itemize} - -\item In the model, a student is equally likely to arrive during any one-minute period. In reality, this probability varies depending on time of day, day of the week, etc. - -\index{probability} - -\item The model does not account for travel time from one bike station to another. - -\item The model does not check whether a bike is available, so it's possible for the number of bikes to be negative (as you might have noticed in some of your simulations). - -\end{itemize} - -Some of these modeling decisions are better than others. For example, the first assumption might be reasonable if we simulate the system for a short period of time, like one hour. - -The second assumption is not very realistic, but it might not affect the results very much, depending on what we use the model for. - -\index{realism} - -On the other hand, the third assumption seems problematic, and it is relatively easy to fix. In Section~\ref{negativebikes}, we will. - -This process, starting with a simple model, identifying the most important problems, and making gradual improvements, is called {\bf iterative modeling}. - -\index{iterative modeling} - -For any physical system, there are many possible models, based on different assumptions and simplifications. It often takes several iterations to develop a model that is good enough for the intended purpose, but no more complicated than necessary. - - -\section{More than one State object} - -Before we go on, I want to make a few changes to the code from the previous chapter. First I'll generalize the functions we wrote so they take a \py{State} object as a parameter. Then, I'll make the code more readable by adding documentation. - -\index{parameter} - -Here is one of the functions from the previous chapter, \py{bike_to_wellesley}: - -\begin{python} -def bike_to_wellesley(): - bikeshare.olin -= 1 - bikeshare.wellesley += 1 -\end{python} - -When this function is called, it modifies \py{bikeshare}. As long as there is only one \py{State} object, that's fine, but what if there is more than one bike share system in the world? Or what if we want to run more than one simulation? - -This function would be more flexible if it took a \py{State} object as a parameter. Here's what that looks like: - -\index{State object} - -\begin{python} -def bike_to_wellesley(state): - state.olin -= 1 - state.wellesley += 1 -\end{python} - -The name of the parameter is \py{state} rather than \py{bikeshare} as a reminder that the value of \py{state} could be any \py{State} object, not just \py{bikeshare}. - -This version of \py{bike_to_wellesley} requires a \py{State} object as a parameter, so we have to provide one when we call it: - -\begin{python} -bike_to_wellesley(bikeshare) -\end{python} - -Again, the argument we provide gets assigned to the parameter, so this function call has the same effect as: - -\begin{code} -state = bikeshare -state.olin -= 1 -state.wellesley += 1 -\end{code} - -Now we can create as many \py{State} objects as we want: - -\begin{python} -bikeshare1 = State(olin=10, wellesley=2) -bikeshare2 = State(olin=2, wellesley=10) -\end{python} - -And update them independently: - -\begin{python} -bike_to_wellesley(bikeshare1) -bike_to_wellesley(bikeshare2) -\end{python} - -Changes in \py{bikeshare1} do not affect \py{bikeshare2}, and vice versa. So we can simulate different bike share systems, or run multiple simulations of the same system. - - -\section{Documentation} -\label{documentation} - -Another problem with the code we have so far is that it contains no {\bf documentation}. Documentation is text we add to a program to help other programmers read and understand it. It has no effect on the program when it runs. - -\index{documentation} -\index{docstring} -\index{comment} - -There are two forms of documentation, {\bf docstrings} and {\bf comments}. - A docstring is a string in triple-quotes that appears at the beginning of a function, like this: - -\begin{python} -def run_simulation(state, p1, p2, num_steps): - """Simulate the given number of time steps. - - state: State object - p1: probability of an Olin->Wellesley customer arrival - p2: probability of a Wellesley->Olin customer arrival - num_steps: number of time steps - """ - results = TimeSeries() - for i in range(num_steps): - step(state, p1, p2) - results[i] = state.olin - - plot(results, label='Olin') -\end{python} - -Docstrings follow a conventional format: - -\begin{itemize} - -\item The first line is a single sentence that describes what the function does. - -\item The following lines explain what each of the parameters are. - -\end{itemize} - -A function's docstring should include the information someone needs to know to {\em use} the function; it should not include details about how the function works. That's what comments are for. - -A comment is a line of text that begins with a hash symbol, \py{#}. It usually appears inside a function to explain something that would not be obvious to someone reading the program. - -\index{comment} -\index{hash symbol} - -For example, here is a version of \py{bike_to_olin} with a docstring and a comment. - -\begin{python} -def bike_to_olin(state): - """Move one bike from Wellesley to Olin. - - state: State object - """ - # We decrease one state variable and increase the - # other, so the total number of bikes is unchanged. - state.wellesley -= 1 - state.olin += 1 -\end{python} - -At this point we have more documentation than code, which is not unusual for short functions. - - -\section{Negative bikes} -\label{negativebikes} - -The changes we've made so far improve the quality of the code, but we haven't done anything to improve the quality of the model yet. Let's do that now. - -\index{code quality} - -Currently the simulation does not check whether a bike is available when a customer arrives, so the number of bikes at a location can be negative. That's not very realistic. Here's an updated version of \py{bike_to_olin} that fixes the problem: - -\begin{python} -def bike_to_olin(state): - if state.wellesley == 0: - return - state.wellesley -= 1 - state.olin += 1 -\end{python} - -The first line checks whether the number of bikes at Wellesley is zero. If so, it uses a a {\bf return statement}, which causes the function to end immediately, without running the rest of the statements. So if there are no bikes at Wellesley, we ``return" from \py{bike_to_olin} without changing the state. - -\index{return statement} -\index{statement!return} - -We can update \py{bike_to_wellesley} the same way. - - -\section{Comparison operators} - -The version of \py{bike_to_olin} in the previous section uses the equals operator, \py{==}, which compares two values and returns \py{True} if they are equal and \py{False} otherwise. - -It is easy to confuse the equals operators with the assignment operator, \py{=}, which assigns a value to a variable. For example, the following statement creates a variable, \py{x}, if it doesn't already exist, and gives it the value \py{5}. - -\index{equality} -\index{assignment operator} -\index{operator!assignment} - -\begin{python} -x = 5 -\end{python} - -On the other hand, the following statement checks whether \py{x} is \py{5} and returns \py{True} or \py{False}. It does not create \py{x} or change its value. - -\begin{python} -x == 5 -\end{python} - -You can use the equals operator in an \py{if} statement, like this: - -\index{if statement} -\index{statement!if} - -\begin{python} -if x == 5: - print('yes, x is 5') -\end{python} - -If you make a mistake and use \py{=} in an \py{if} statement, like this: - -\begin{python} -if x = 5: - print('yes, x is 5') -\end{python} - -That's a {\bf syntax error}, which means that the structure of the program is invalid. Python will print an error message and the program won't run. - -\index{syntax error} -\index{error!syntax} - -The equals operator is one of the {\bf comparison operators}. The others are: - -\index{comparison operator} -\index{operator!comparison} - -\begin{tabular}{l|c} -{\bf Operation} & {\bf Symbol} \\ -\hline -Less than & \py{<} \\ -Greater than & \py{>} \\ -Less than or equal & \py{<=} \\ -Greater than or equal & \py{>=} \\ -Equal & \py{==} \\ -Not equal & \py{!=} \\ -\end{tabular} - - -\section{Metrics} -\label{metrics} - -Getting back to the bike share system, at this point we have the ability to simulate the behavior of the system. Since the arrival of customers is random, the state of the system is different each time we run a simulation. Models like this are called random or {\bf stochastic}; models that do the same thing every time they run are {\bf deterministic}. - -\index{stochastic} -\index{deterministic} - -Suppose we want to use our model to predict how well the bike share system will work, or to design a system that works better. First, we have to decide what we mean by ``how well" and ``better". - -From the customer's point of view, we might like to know the probability of finding an available bike. From the system-owner's point of view, we might want to minimize the number of customers who don't get a bike when they want one, or maximize the number of bikes in use. Statistics like these that quantify how well the system works are called {\bf metrics}. - -\index{metric} - -As a simple example, let's measure the number of unhappy customers. Here's a version of \py{bike_to_olin} that keeps track of the number of customers who arrive at a station with no bikes: - -\begin{python} -def bike_to_olin(state): - if state.wellesley == 0: - state.wellesley_empty += 1 - return - state.wellesley -= 1 - state.olin += 1 -\end{python} - -If a customer arrives at the Wellesley station and finds no bike available, \py{bike_to_olin} updates \py{wellesley_empty} which counts the number of unhappy customers. - -This function only works if we initialize \py{wellesley_empty} when we create the \py{State} object, like this: - -\begin{python} -bikeshare = State(olin=10, wellesley=2, - olin_empty=0, wellesley_empty=0) -\end{python} - -Assuming we update \py{move_to_wellesley} the same way, we can run the simulation like this (see Section~\ref{documentation}): - -\begin{python} -run_simulation(bikeshare, 0.4, 0.2, 60) -\end{python} - -Then we can check the metrics: - -\begin{python} -print(bikeshare.olin_empty, bikeshare.wellesley_empty) -\end{python} - -Because the simulation is stochastic, the results are different each time it runs. - -Before you go on, you might want to read the notebook for this chapter, \py{chap03.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - -\chapter{Sweeping parameters} -\label{chap04} - -In the previous chapter we defined metrics that quantify the performance of bike sharing this system. In this chapter we see how those metrics depend on the parameters of the system, like the arrival rate of customers at bike stations. - -We also discuss a program development strategy, called incremental development, that might help you write programs faster and spend less time debugging. - - -\section{Functions that return values} - -We have seen several functions that return values; for example, when you run \py{sqrt}, it returns a number you can assign to a variable. - -\index{return value} - -\begin{python} -t = sqrt(2 * h / a) -\end{python} - -When you run \py{State}, it returns a new \py{State} object: - -\begin{python} -bikeshare = State(olin=10, wellesley=2) -\end{python} - -Not all functions have return values. For example, when you run \py{step}, it updates a \py{State} object, but it doesn't return a value. - -To write functions that return values, we can use a second form of the \py{return} statement, like this: - -\index{return statement} -\index{statement!return} - -\begin{python} -def add_five(x): - return x + 5 -\end{python} - -\py{add_five} takes a parameter, \py{x}, which could be any number. It computes \py{x + 5} and returns the result. So if we run it like this, the result is \py{8}: - -\begin{python} -add_five(3) -\end{python} - -As a more useful example, here's a version of \py{run_simulation} that creates a \py{State} object, runs a simulation, and then returns the \py{State} object as a result: - -\begin{python} -def run_simulation(): - p1 = 0.4 - p2 = 0.2 - num_steps = 60 - - state = State(olin=10, wellesley=2, - olin_empty=0, wellesley_empty=0) - - for i in range(num_steps): - step(state, p1, p2) - - return state -\end{python} - -If we call \py{run_simulation} like this: - -\begin{python} -state = run_simulation() -\end{python} - -It assigns the \py{State} object from \py{run_simulation} to \py{state}, which contains the metrics we are interested in: - -\begin{python} -print(state.olin_empty, state.wellesley_empty) -\end{python} - - -\section{Two kinds of parameters} - -This version of \py{run_simulation} always starts with the same initial condition, 10 bikes at Olin and 2 bikes at Wellesley, and the same values of \py{p1}, \py{p2}, and \py{num_steps}. Taken together, these five values are the {\bf parameters of the model}, which are values that determine the behavior of the system. - -\index{parameter!of a model} -\index{parameter!of a function} - -It is easy to get the parameters of a model confused with the parameters of a function. They are closely related ideas; in fact, it is common for the parameters of the model to appear as parameters in functions. For example, we can write a more general version of \py{run_simulation} that takes \py{p1} and \py{p2} as function parameters: - -\begin{python} -def run_simulation(p1, p2, num_steps): - state = State(olin=10, wellesley=2, - olin_empty=0, wellesley_empty=0) - - for i in range(num_steps): - step(state, p1, p2) - - return state -\end{python} - -Now we can run it with different arrival rates, like this: - -\begin{python} -state = run_simulation(0.6, 0.3, 60) -\end{python} - -In this example, \py{0.6} gets assigned to \py{p1}, \py{0.3} gets assigned to \py{p2}, and \py{60} gets assigned to \py{num_steps}. - -Now we can call \py{run_simulation} with different parameters and see how the metrics, like the number of unhappy customers, depend on the parameters. But before we do that, we need a new version of a for loop. - -\index{metric} - - -\section{Loops and arrays} -\label{array} - -In Section~\ref{forloop}, we saw a loop like this: - -\begin{python} -for i in range(4): - bike_to_wellesley() -\end{python} - -\py{range(4)} creates a sequence of numbers from 0 to 3. Each time through the loop, the next number in the sequence gets assigned to the loop variable, \py{i}. - -\index{loop} -\index{loop variable} -\index{variable!loop} - -\py{range} only works with integers; to get a sequence of non-integer values, we can use \py{linspace}, which is defined in the \py{modsim} library: - -\begin{python} -p1_array = linspace(0, 1, 5) -\end{python} - -The arguments indicate where the sequence should start and stop, and how many elements it should contain. In this example, the sequence contains \py{5} equally-spaced numbers, starting at \py{0} and ending at \py{1}. - -\index{linspace} -\index{NumPy} -\index{array} - -The result is a NumPy {\bf array}, which is a new kind of object we have not seen before. An array is a container for a sequence of numbers. - -We can use an array in a \py{for} loop like this: - -\begin{python} -for p1 in p1_array: - print(p1) -\end{python} - -When this loop runs, it - -\begin{enumerate} - -\item Gets the first value from the array and assigns it to \py{p1}. - -\item Runs the body of the loop, which prints \py{p1}. - -\item Gets the next value from the array and assigns it to \py{p1}. - -\item Runs the body of the loop, which prints \py{p1}. - -\end{enumerate} - -And so on, until it gets to the end of the array. The result is: - -\begin{result} -0.0 -0.25 -0.5 -0.75 -1.0 -\end{result} - -This will come in handy in the next section. - - -\section{Sweeping parameters} - -If we know the actual values of parameters like \py{p1} and \py{p2}, we can use them to make specific predictions, like how many bikes will be at Olin after one hour. - -\index{prediction} -\index{explanation} - -But prediction is not the only goal; models like this are also used to explain why systems behave as they do and to evaluate alternative designs. For example, if we observe the system and notice that we often run out of bikes at a particular time, we could use the model to figure out why that happens. And if we are considering adding more bikes, or another station, we could evaluate the effect of various ``what if" scenarios. -\index{what if scenario} - -As an example, suppose we have enough data to estimate that \py{p2} is about \py{0.2}, but we don't have any information about \py{p1}. We could run simulations with a range of values for \py{p1} and see how the results vary. This process is called {\bf sweeping} a parameter, in the sense that the value of the parameter ``sweeps" through a range of possible values. - -\index{sweep} -\index{parameter sweep} - -Now that we know about loops and arrays, we can use them like this: - -\begin{python} -p1_array = linspace(0, 1, 11) -p2 = 0.2 -num_steps = 60 - -for p1 in p1_array: - state = run_simulation(p1, p2, num_steps) - print(p1, state.olin_empty) -\end{python} - -Each time through the loop, we run a simulation with a different value of \py{p1} and the same value of \py{p2}, \py{0.2}. Then we print \py{p1} and the number of unhappy customers at Olin. - -To save and plot the results, we can use a \py{SweepSeries} object, which is similar to a \py{TimeSeries}; the difference is that the labels in a \py{SweepSeries} are parameter values rather than time values. - -We can create an empty \py{SweepSeries} like this: - -\begin{code} -sweep = SweepSeries() -\end{code} - -And add values like this: - -\begin{python} -for p1 in p1_array: - state = run_simulation(p1, p2, num_steps) - sweep[p1] = state.olin_empty -\end{python} - -The result is a \py{SweepSeries} that maps from each value of \py{p1} to the resulting number of unhappy customers. Then we can plot the results: - -\begin{code} -plot(sweep, label='Olin') -\end{code} - - - - -\section{Incremental development} - -When you start writing programs that are more than a few lines, you -might find yourself spending more and more time debugging. The more -code you write before you start debugging, the harder it is to find -the problem. - -\index{debugging} -\index{incremental development} - -{\bf Incremental development} is a way of programming that tries -to minimize the pain of debugging. The fundamental steps are: - -\begin{enumerate} - -\item Always start with a working program. If you have an -example from a book, or a program you wrote that is similar to -what you are working on, start with that. Otherwise, start with -something you {\em know} is correct, like {\tt x=5}. Run the program -and confirm that it does what you expect. - -\item Make one small, testable change at a time. A ``testable'' -change is one that displays something or has some -other effect you can check. Ideally, you should know what -the correct answer is, or be able to check it by performing another -computation. - -\index{testable change} - -\item Run the program and see if the change worked. If so, go back -to Step 2. If not, you will have to do some debugging, but if the -change you made was small, it shouldn't take long to find the problem. - -\end{enumerate} - -When this process works, your changes usually work the first time, or if they don't, the problem is obvious. In practice, there are two problems with incremental development: - -\begin{itemize} - -\item Sometimes you have to write extra code to generate visible output that you can check. This extra code is called {\bf scaffolding} because you use it to build the program and then remove it when you are done. That might seem like a waste, but time you spend on scaffolding is almost always time you save on debugging. - -\index{scaffolding} - -\item When you are getting started, it might not be obvious how to -choose the steps that get from {\tt x=5} to the program you are trying -to write. You will see more examples of this process as we go along, and you will get better with experience. - -\end{itemize} - -If you find yourself writing more than a few lines of code before you start testing, and you are spending a lot of time debugging, try incremental development. - -Before you go on, you might want to read the notebook for this chapter, \py{chap04.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - -%\part{Modeling population growth} - -\chapter{World population} -\label{chap05} - -In 1968 Paul Erlich published {\it The Population Bomb}, in which he predicted that world population would grow quickly during the 1970s, that agricultural production could not keep up, and that mass starvation in the next two decades was inevitable (see \url{http://modsimpy.com/popbomb}). As someone who grew up during those decades, I am happy to report that those predictions were wrong. - -\index{Erlich, Paul} -\index{Population Bomb} - -But world population growth is still a topic of concern, and it is an open question how many people the earth can sustain while maintaining and improving our quality of life. - -\index{world population} -\index{population} - -In this chapter and the next, we use tools from the previous chapters to explain world population growth since 1950 and generate predictions for the next 50--100 years. - -\index{prediction} - -For background on world population growth, watch this video from the American Museum of Natural History \url{http://modsimpy.com/human}. - -\index{American Museum of Natural History} - - -\section{World Population Data} -\label{worldpopdata} - -The Wikipedia article on world population contains tables with estimates of world population from prehistory to the present, and projections for the future (\url{http://modsimpy.com/worldpop}). - -\index{Wikipedia} -\index{Pandas} - -To read this data, we will use Pandas, which provides functions for working with data. The function we'll use is \py{read_html}, which can read a web page and extract data from any tables it contains. Before we can use it, we have to import it. You have already seen this import statement: - -\index{\py{read_html}} -\index{import statement} -\index{statement!import} - -\begin{python} -from modsim import * -\end{python} - -which imports all functions from the \py{modsim} library. To import \py{read_html}, the statement we need is: - -\begin{python} -from pandas import read_html -\end{python} - -Now we can use it like this: - -\begin{python} -filename = 'data/World_population_estimates.html' -tables = read_html(filename, - header=0, - index_col=0, - decimal='M') -\end{python} - -The arguments are: -\index{argument} - -\begin{itemize} - -\item \py{filename}: The name of the file (including the directory it's in) as a string. This argument can also be a URL starting with \py{http}. - -\item \py{header}: Indicates which row of each table should be considered the header, that is, the set of labels that identify the columns. In this case it is the first row (numbered 0). - -\item \py{index_col}: Indicates which column of each table should be considered the {\bf index}, that is, the set of labels that identify the rows. In this case it is the first column, which contains the years. - -\item \py{decimal}: Normally this argument is used to indicate which character should be considered a decimal point, because some conventions use a period and some use a comma. In this case I am abusing the feature by treating \py{M} as a decimal point, which allows some of the estimates, which are expressed in millions, to be read as numbers. - -\end{itemize} - -The result, which is assigned to \py{tables}, is a sequence that contains one \py{DataFrame} for each table. A \py{DataFrame} is an object, defined by Pandas, that represents tabular data. - -\index{DataFrame} -\index{sequence} - -To select a \py{DataFrame} from \py{tables}, we can use the bracket operator like this: - -\begin{python} -table2 = tables[2] -\end{python} - -This line selects the third table (numbered 2), which contains population estimates from 1950 to 2016. - -\index{bracket operator} -\index{operator!bracket} - -We can display the first few lines like this: - -\begin{python} -table2.head() -\end{python} - -The column labels are long strings, which makes them hard to work with. We can replace them with shorter strings like this: - -\index{string} -\index{columns} - -\begin{python} -table2.columns = ['census', 'prb', 'un', 'maddison', - 'hyde', 'tanton', 'biraben', 'mj', - 'thomlinson', 'durand', 'clark'] -\end{python} - -Now we can select a column from the \py{DataFrame} using the dot operator, like selecting a state variable from a \py{State} object: - -\index{dot operator} -\index{operator!dot} - -\begin{python} -census = table2.census / 1e9 -un = table2.un / 1e9 -\end{python} - -These lines select the estimates generated by the United Nations Department of Economic and Social Affairs (UN DESA) and the United States Census Bureau. - -\index{United Nations} -\index{United States Census Bureau} - -Each result is a Pandas \py{Series}, which is like a \py{DataFrame} with just one column. - -\index{Series} - -The number \py{1e9} is a shorter, less error-prone way to write \py{1000000000} or one billion. When we divide a \py{Series} by a number, it divides all of the elements of the \py{Series}. From here on, we'll express population estimates in terms of billions. - - -\section{Plotting} - -Now we can plot the estimates like this: - -\index{plot} - -\begin{python} -plot(census, ':', label='US Census') -plot(un, '--', label='UN DESA') -\end{python} - - -The next two lines plot the \py{Series} objects. The {\bf format strings} \py{':'} and \py{'--'} indicate dotted and dashed lines. For more about format strings in Pyplot, see \url{http://modsimpy.com/plot}. - -\index{format string} -\index{Pyplot} - -The \py{label} argument provides the string that appears in the legend. - -\index{label} -\index{legend} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap03-fig01.pdf}} -\caption{Estimates of world population, 1950--2016.} -\label{chap03-fig01} -\end{figure} - -Figure~\ref{chap03-fig01} shows the result. The lines overlap almost completely; for most dates the difference between the two estimates is less than 1\%. - - -\section{Constant growth model} - -Suppose we want to predict world population growth over the next 50 or 100 years. We can do that by developing a model that describes how populations grow, fitting the model to the data we have so far, and then using the model to generate predictions. - -\index{constant growth} - -In the next few sections I demonstrate this process starting with simple models and gradually improving them. - -\index{iterative modeling} - -Although there is some curvature in the plotted estimates, it looks like world population growth has been close to linear since 1960 or so. So we'll start with a model that has constant growth. - -To fit the model to the data, we'll compute the average annual growth from 1950 to 2016. Since the UN and Census data are so close, we'll use the Census data. - -We can select a value from a \py{Series} using the bracket operator: -\index{bracket operator} -\index{operator!bracket} - -\begin{python} -census[1950] -\end{python} - -So we can get the total growth during the interval like this: - -\begin{python} -total_growth = census[2016] - census[1950] -\end{python} - -The numbers in brackets are called {\bf labels}, because they label the rows of the \py{Series} (not to be confused with the labels we saw in the previous section, which label lines in a graph). - -\index{label} - -In this example, the labels 2016 and 1950 are part of the data, so it would be better not to make them part of the program. Putting values like these in the program is called {\bf hard coding}; it is considered bad practice because if the data change in the future, we have to modify the program (see \url{http://modsimpy.com/hardcode}). - -\index{hard coding} - -It would be better to get the first and last labels from the \py{Series} like this: - -\begin{python} -t_0 = get_first_label(census) -t_end = get_last_label(census) -elapsed_time = t_end - t_0 -\end{python} - -\py{get_first_label} and \py{get_last_label} are defined in \py{modsim.py}; as you might have guessed, they select the first and last labels from \py{census}. -The difference between them is the elapsed time. - -The \py{modsim} library also defines \py{get_first_value} and \py{get_last_value}, which we can use to compute \py{total_growth}: - -\begin{python} -p_0 = get_first_value(census) -p_end = get_last_value(census) -total_growth = p_end - p_0 -\end{python} - -Finally, we can compute average annual growth. - -\begin{python} -annual_growth = total_growth / elapsed_time -\end{python} - -The next step is to use this estimate to simulate population growth since 1950. - - -\section{Simulation} - -Our simulation will start with the observed population in 1950, \py{p0}, and add \py{annual_growth} each year. To store the results, we'll use a \py{TimeSeries} object: - -\index{TimeSeries} - -\begin{python} -results = TimeSeries() -\end{python} - -We can set the first value in the new \py{TimeSeries} by copying the first value from \py{census}: - -\begin{python} -results[t_0] = census[p_0] -\end{python} - -Then we set the rest of the values by simulating annual growth: - -\begin{python} -for t in linrange(t_0, t_end): - results[t+1] = results[t] + annual_growth -\end{python} - -\py{linrange} is defined in the \py{modsim} library. In this example it returns a NumPy array of integers from \py{t_0} to \py{t_end}, including the first but not the last. - -\index{linrange} -\index{NumPy} -\index{array} - -Each time through the loop, the loop variable \py{t} gets the next value from the array. Inside the loop, we compute the population for each year by adding the population for the previous year and \py{annual_growth}. The last time through the loop, the value of \py{t} is 2015, so the last label in \py{results} is 2016, which is what we want. - -\index{loop} -\index{loop variable} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap03-fig02.pdf}} -\caption{Estimates of world population, 1950--2016, and a constant growth model.} -\label{chap03-fig02} -\end{figure} - -Figure~\ref{chap03-fig02} shows the result. The model does not fit the data particularly well from 1950 to 1990, but after that, it's pretty good. Nevertheless, there are problems: - -\begin{itemize} - -\item There is no obvious mechanism that could cause population growth to be constant from year to year. Changes in population are determined by the fraction of people who die and the fraction of people who give birth, so we expect them to depend on the current population. - -\item According to this model, we would expect the population to keep growing at the same rate forever, and that does not seem reasonable. - -\end{itemize} - -We'll try out some different models in the next few sections, but first let's clean up the code. - -Before you go on, you might want to read the notebook for this chapter, \py{chap05.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - -\chapter{Modeling growth} -\label{chap06} -In the previous chapter we simulated a model of world population with constant growth. In this chapter we see if we can make a better model with growth proportional to the population. - -But first, we can improve the code from the previous chapter by encapsulating it in a function and using \py{System} objects. - -\section{System objects} -\label{nowwithsystem} - -Like a \py{State} object, a \py{System} object contains variables and their values. The difference is: - -\begin{itemize} - -\item \py{State} objects contain state variables, which represent the state of the system, which get updated in the course of a simulation. - -\item \py{System} objects contain {\bf system variables}, which represent parameters of the system, which usually don't get updated over the course of a simulation. - -\end{itemize} - -For example, in the bike share model, state variables include the number of bikes at each location, which get updated whenever a customer moves a bike. System variables include the number of locations, total number of bikes, and arrival rates at each location. - -In the population model, the only state variable is the population. System variables include the annual growth rate, the initial time and population, and the end time. - -Suppose we have the following variables, as computed in the previous chapter (assuming that \py{census} is a \py{Series} object): - -\begin{python} -t_0 = get_first_label(census) -t_end = get_last_label(census) -elapsed_time = t_end - t_0 - -p_0 = get_first_value(census) -p_end = get_last_value(census) -total_growth = p_end - p_0 - -annual_growth = total_growth / elapsed_time -\end{python} - -Some of these are parameters we need to simulate the system; others are temporary values we can discard. We can put the parameters we need into a \py{System} object like this: - -\index{System object} - -\begin{python} -system = System(t_0=t_0, - t_end=t_end, - p_0=p_0, - annual_growth=annual_growth) -\end{python} - -\py{t0} and \py{t_end} are the first and last years; \py{p_0} is the initial population, and \py{annual_growth} is the estimated annual growth. - -Next we'll wrap the code from the previous chapter in a function: - -\begin{python} -def run_simulation1(system): - results = TimeSeries() - results[system.t_0] = system.p_0 - - for t in linrange(system.t_0, system.t_end): - results[t+1] = results[t] + system.annual_growth - - return results -\end{python} - -When \py{run_simulation1} runs, it stores the results in a \py{TimeSeries} and returns it. - -\index{TimeSeries object} - -The following function plots the results along with the estimates \py{census} and \py{un}: - -\begin{python} -def plot_results(census, un, timeseries, title): - plot(census, ':', label='US Census') - plot(un, '--', label='UN DESA') - plot(timeseries, color='gray', label='model') - - decorate(xlabel='Year', - ylabel='World population (billion)', - title=title) -\end{python} - -\index{plot} -\index{decorate} - -The \py{color} argument specifies the color of the line. For details on color specification in Pyplot, see \url{http://modsimpy.com/color}. - -\index{Pyplot} -\index{color} - -Finally, we can run the simulation like this. - -\begin{python} -results = run_simulation1(system) -plot_results(census, un, results, 'Constant growth model') -\end{python} - -The results are the same as Figure~\ref{chap03-fig02}. - -It might not be obvious that using functions and \py{System} objects is a big improvement, and for a simple model that we run only once, maybe it's not. But as we work with more complex models, and when we run many simulations with different parameters, we'll see that the organization of the code makes a big difference. - -Now let's see if we can improve the model. - - -\section{Proportional growth model} - -The biggest problem with the constant growth model is that it doesn't make any sense. It is hard to imagine how people all over the world could conspire to keep population growth constant from year to year. - -\index{proportional growth} - -On the other hand, if some fraction of the population dies each year, and some fraction gives birth, we can compute the net change in the population like this: - -\begin{python} -def run_simulation2(system): - results = TimeSeries() - results[system.t_0] = system.p_0 - - for t in linrange(system.t_0, system.t_end): - births = system.birth_rate * results[t] - deaths = system.death_rate * results[t] - results[t+1] = results[t] + births - deaths - - return results -\end{python} - -Now we can choose the values of \py{birth_rate} and \py{death_rate} that best fit the data. Without trying too hard, I chose: - -\begin{python} -system.death_rate = 0.01 -system.birth_rate = 0.027 -\end{python} - -Then I ran the simulation and and plotted the results: - -\begin{python} -results = run_simulation2(system) -plot_results(census, un, results, 'Proportional model') -\end{python} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap03-fig03.pdf}} -\caption{Estimates of world population, 1950--2016, and a proportional model.} -\label{chap03-fig03} -\end{figure} - -Figure~\ref{chap03-fig03} shows the results. The proportional model fits the data well from 1950 to 1965, but not so well after that. Overall, the {\bf quality of fit} is not as good as the constant growth model, which is surprising, because it seems like the proportional model is more realistic. - -In the next chapter we'll try one more time to find a model that makes sense and fits the data. But first, I want to make a few more improvements to the code. - - -\section{Factoring out the update function} - -\py{run_simulation1} and \py{run_simulation2} are nearly identical except for the body of the \py{for} loop, where we compute the population for the next year. - -\index{update function} -\index{function!update} - -Rather than repeat identical code, we can separate the things that change from the things that don't. First, I'll pull out the update code from \py{run_simulation2} and make it a function: - -\begin{python} -def update_func1(pop, t, system): - births = system.birth_rate * pop - deaths = system.death_rate * pop - return pop + births - deaths -\end{python} - -This function takes as arguments the current population, current year, and a \py{System} object; it returns the computed population for the next year. - -This update function does not use \py{t}, so we could leave it out. But we will see other functions that need it, and it is convenient if they all take the same parameters, used or not. - -Now we can write a function that runs any model: - -\begin{python} -def run_simulation(system, update_func): - results = TimeSeries() - results[system.t_0] = system.p_0 - - for t in linrange(system.t_0, system.t_end): - results[t+1] = update_func(results[t], t, system) - - return results -\end{python} - -This function demonstrates a feature we have not seen before: it takes a function as a parameter! When we call \py{run_simulation}, the second parameter is a function, like \py{update_func1}, that computes the population for the next year. - -\index{function!as parameter} - -Here's how we call it: - -\begin{python} -results = run_simulation(system, update_func1) -\end{python} - -Passing a function as an argument is the same as passing any other value. The argument, which is \py{update_func1} in this example, gets assigned to the parameter, which is called \py{update_func}. Inside \py{run_simulation}, we can run \py{update_func} just like any other function. - -The loop in \py{run_simulation} calls \py{update_func1} once for each year between \py{t_0} and \py{t_end-1}. The result is the same as Figure~\ref{chap03-fig03}. - - -\section{Combining birth and death} - -While we are at it, we can also simplify the code by combining births and deaths to compute the net growth rate. Instead of two parameters, \py{birth_rate} and \py{death_rate}, we can write the update function in terms of a single parameter that represents the difference: - -\begin{python} -system.alpha = system.birth_rate - system.death_rate -\end{python} - -The name of this parameter, \py{alpha}, is the conventional name for a proportional growth rate. - -Here's the modified version of \py{update_func1}: - -\begin{python} -def update_func2(pop, t, system): - net_growth = system.alpha * pop - return pop + net_growth -\end{python} - -And here's how we run it: - -\begin{python} -results = run_simulation(system, update_func2) -\end{python} - -Again, the result is the same as Figure~\ref{chap03-fig03}. - -Before you go on, you might want to read the notebook for this chapter, \py{chap06.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - - -\chapter{Quadratic growth} -\label{chap07} - -In the previous chapter we developed a population model where net growth during each time step is proportional to the current population. This model seems more realistic than the constant growth model, but it does not fit the data as well. - -There are a few things we could try to improve the model: - -\begin{itemize} - -\item Maybe the net growth rate varies over time. - -\item Maybe net growth depends on the current population, but the relationship is quadratic, not linear. - -\end{itemize} - -In the notebook for this chapter, you will have a chance to try the first option. In this chapter, we explore the second. - - -\section{Quadratic growth} -\label{quadratic} - -It makes sense that net growth should depend on the current population, but maybe it's not a linear relationship, like this: - -\begin{python} - net_growth = system.alpha * pop -\end{python} - -Maybe it's a quadratic relationship, like this: - -\index{quadratic growth} - -\begin{python} - net_growth = system.alpha * pop + system.beta * pop**2 -\end{python} - -We can test that conjecture with a new update function: - -\begin{python} -def update_func_quad(pop, t, system): - net_growth = system.alpha * pop + system.beta * pop**2 - return pop + net_growth -\end{python} - -Now we need two parameters. I chose the following values by trial and error; we will see better ways to do it later. - -\index{parameter} - -\begin{python} -system.alpha = 0.025 -system.beta = -0.0018 -\end{python} - -And here's how we run it: - -\begin{python} -results = run_simulation(system, update_func_quad) -\end{python} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap03-fig04.pdf}} -\caption{Estimates of world population, 1950--2016, and a quadratic model.} -\label{chap03-fig04} -\end{figure} - -Figure~\ref{chap03-fig04} shows the result. The model fits the data well over the whole range, with just a bit of space between them in the 1960s. - -Of course, we should expect the quadratic model to fit better than the constant and proportional models because it has two parameters we can choose, where the other models have only one. In general, the more parameters you have to play with, the better you should expect the model to fit. - -\index{quality of fit} -\index{data} -\index{fitting data} - -But fitting the data is not the only reason to think the quadratic model might be a good choice. It also makes sense; that is, there is a legitimate reason to expect the relationship between growth and population to have this form. - -To understand it, let's look at net growth as a function of population. Here's how we compute it: - -\begin{python} -pop_array = linspace(0, 15, 100) -net_growth_array = (system.alpha * pop_array + - system.beta * pop_array**2) -\end{python} - -\py{pop_array} contains 100 equally spaced values from 0 to 15. \py{net_growth_array} contains the corresponding 100 values of net growth. We can plot the results like this: - -\begin{python} -plot(pop_array, net_growth_array) -\end{python} - -Previously we have used \py{plot} with \py{Series} objects. In this example, we use two NumPy arrays, corresponding to the x and y axes. - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap03-fig05.pdf}} -\caption{Net growth as a function of population.} -\label{chap03-fig05} -\end{figure} - -Figure~\ref{chap03-fig05} shows the result. Note that the x-axis is not time, as in the previous figures, but population. We can divide this curve into four regimes of behavior: -\index{regime} - -\begin{itemize} - -\item When the population is less than 3-4 billion, net growth is proportional to population, as in the proportional model. In this regime, the population grows slowly because the population is small. - -\item Between 4 billion and 10 billion, the population grows quickly because there are a lot of people. - -\item Above 10 billion, population grows more slowly; this behavior models the effect of resource limitations that lower birth rates or increase death rates. - -\item Above 14 billion, resources are so limited that the death rate exceeds the birth rate and net growth becomes negative. - -\end{itemize} - -Just below 14 billion, there is a point where net growth is 0, which means that the population does not change. At this point, the birth and death rates are equal, so the population is in {\bf equilibrium}. - -\index{equilibrium} - - -\section{Equilibrium} -\label{equilibrium} - -To find the equilibrium point, we can find the roots, or zeros, of this equation: -% -\[ \Delta p = \alpha p + \beta p^2 \] -% -where $\Delta p$ is net population growth, $p$ is current population, and $\alpha$ and $\beta$ are the parameters of the model. We can rewrite the right hand side like this: -% -\[ \Delta p = p (\alpha + \beta p) \] -% -which is $0$ when $p=0$ or $p=-\alpha/\beta$. In this example, $\alpha = 0.025$ and $\beta = -0.0018$, so $-\alpha/\beta = 13.9$. - -In the context of population modeling, the quadratic model is more conventionally written like this: -% -\[ \Delta p = r p (1 - p / K) \] -% -This is the same model; it's just a different way to {\bf parameterize} it. Given $\alpha$ and $\beta$, we can compute $r=\alpha$ and $K=-\alpha/\beta$. - -\index{parameterize} - -In this version, it is easier to interpret the parameters: $r$ is the maximum growth rate, observed when $p$ is small, and $K$ is the equilibrium point. $K$ is also called the {\bf carrying capacity}, since it indicates the maximum population the environment can sustain. - -\index{carrying capacity} - -In the next chapter we use the models we have developed to generate predictions. - -\section{Dysfunctions} - -When people learn about functions, there are a few things they often find confusing. In this section I present and explain some common problems. - -As an example, suppose you want a function that takes as a parameter \py{System} object with variables \py{alpha} and \py{beta}, and computes the carrying capacity, \py{-alpha/beta}. Here's a good solution: - -\begin{python} -def carrying_capacity(system): - K = -system.alpha / system.beta - return K - -sys1 = System(alpha=0.025, beta=-0.0018) -pop = carrying_capacity(sys1) -print(pop) -\end{python} - -Now let's see all the ways that can go wrong. - -Dysfunction \#1: Not using parameters. In the following version, the function doesn't take any parameters; when \py{sys1} appears inside the function, it refers to the object we create outside the function. - -\begin{python} -def carrying_capacity(): - K = -sys1.alpha / sys1.beta - return K - -sys1 = System(alpha=0.025, beta=-0.0018) -pop = carrying_capacity() -print(pop) -\end{python} - -This version actually works, but it is not as versatile as it could be. If there are several \py{System} objects, this function can only work with one of them, and only if it is named \py{sys1}. - -Dysfunction \#2: Clobbering the parameters. When people first learn about parameters, they often write functions like this: - -\begin{python} -# WRONG -def carrying_capacity(system): - system = System(alpha=0.025, beta=-0.0018) - K = -system.alpha / system.beta - return K - -sys1 = System(alpha=0.03, beta=-0.002) -pop = carrying_capacity(sys1) -print(pop) -\end{python} - -In this example, we have a \py{System} object named \py{sys1} that gets passed as an argument to \py{carrying_capacity}. But when the function runs, it ignores the argument and immediately replaces it with a new \py{System} object. As a result, this function always returns the same value, no matter what argument is passed. - -When you write a function, you generally don't know what the values of the parameters will be. Your job is to write a function that works for any valid values. If you assign your own values to the parameters, you defeat the whole purpose of functions. - - -Dysfunction \#3: No return value. Here's a version that computes the value of \py{K} but doesn't return it. - -\begin{python} -# WRONG -def carrying_capacity(system): - K = -system.alpha / system.beta - -sys1 = System(alpha=0.025, beta=-0.0018) -pop = carrying_capacity(sys1) -print(pop) -\end{python} - -A function that doesn't have a return statement always returns a special value called \py{None}, so in this example the value of \py{pop} is \py{None}. If you are debugging a program and find that the value of a variable is \py{None} when it shouldn't be, a function without a return statement is a likely cause. -\index{None} - -Dysfunction \#4: Ignoring the return value. Finally, here's a version where the function is correct, but the way it's used is not. - -\begin{python} -# WRONG -def carrying_capacity(system): - K = -system.alpha / system.beta - return K - -sys1 = System(alpha=0.025, beta=-0.0018) -carrying_capacity(sys1) -print(K) -\end{python} - -In this example, \py{carrying_capacity} runs and returns \py{K}, but the return value is dropped. - -When you call a function that returns a value, you should do something with the result. Often you assign it to a variable, as in the previous examples, but you can also use it as part of an expression. For example, you could eliminate the temporary variable \py{pop} like this: - -\begin{python} -print(carrying_capacity(sys1)) -\end{python} - -Or if you had more than one system, you could compute the total carrying capacity like this: - -\begin{python} -total = carrying_capacity(sys1) + carrying_capacity(sys2) -\end{python} - -Before you go on, you might want to read the notebook for this chapter, \py{chap07.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - - - -\chapter{Prediction} -\label{chap08} - -In the previous chapter we developed a quadratic model of world population growth from 1950 to 2016. It is a simple model, but it fits the data well and the mechanisms it's based on are plausible. - -In this chapter we'll use the quadratic model to generate projections of future growth, and compare our results to projections from actual demographers. Also, we'll represent the models from the previous chapters as differential equations and solve them analytically. - -\index{prediction} -\index{projection} - - -\section{Generating projections} - -We'll start with the quadratic model from Section~\ref{quadratic}, which is based on this update function: -\index{quadratic growth} - -\begin{python} -def update_func_quad(pop, t, system): - net_growth = system.alpha * pop + system.beta * pop**2 - return pop + net_growth -\end{python} - -As we saw in the previous chapter, we can get the start date, end date, and initial population from \py{census}, which is a series that contains world population estimates generated by the U.S. Census: - -\begin{python} -t_0 = get_first_label(census) -t_end = get_last_label(census) -p_0 = census[t_0] -\end{python} - -Now we can create a \py{System} object: -\index{System object} - -\begin{python} -system = System(t_0=t_0, - t_end=t_end, - p_0=p_0, - alpha=0.025, - beta=-0.0018) -\end{python} - -And run the model: - -\begin{python} -results = run_simulation(system, update_func_quad) -\end{python} - -We have already seen the results in Figure~\ref{chap03-fig04}. Now, to generate a projection, the only thing we have to change is \py{t_end}: - -\begin{python} -system.t_end = 2250 -results = run_simulation(system, update_func_quad) -\end{python} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap04-fig01.pdf}} -\caption{Quadratic model of world population growth, with projection from 2016 to 2250.} -\label{chap04-fig01} -\end{figure} - -Figure~\ref{chap04-fig01} shows the result, with a projection until 2250. According to this model, population growth will continue almost linearly for the next 50--100 years, then slow over the following 100 years, approaching 13.9 billion by 2250. - -I am using the word ``projection" deliberately, rather than ``prediction", with the following distinction: ``prediction" implies something like ``this is what we should reasonably expect to happen, at least approximately"; ``projection" implies something like ``if this model is actually a good description of what is happening in this system, and if nothing in the future causes the parameters of the model to change, this is what would happen." - -Using ``projection" leaves open the possibility that there are important things in the real world that are not captured in the model. It also suggests that, even if the model is good, the parameters we estimate based on the past might be different in the future. - -The quadratic model we've been working with is based on the assumption that population growth is limited by the availability of resources; in that scenario, as the population approaches carrying capacity, birth rates fall and death rates rise because resources become scarce. - -\index{carrying capacity} - -If that assumption is valid, we might be able to use actual population growth to estimate carrying capacity, especially if we observe the transition into the regime where the growth rate starts to fall. - -But in the case of world population growth, those conditions don't apply. Over the last 50 years, the net growth rate has leveled off, but not yet started to fall, so we don't have enough data to make a credible estimate of carrying capacity. And resource limitations are probably {\em not} the primary reason growth has slowed. As evidence, consider: - -\begin{itemize} - -\item First, the death rate is not increasing; rather, it has declined from 1.9\% in 1950 to 0.8\% now (see \url{http://modsimpy.com/mortality}). So the decrease in net growth is due entirely to declining birth rates. - -\index{mortality rate} - -\item Second, the relationship between resources and birth rate is the opposite of what the model assumes; as nations develop and people become more wealthy, birth rates tend to fall. - -\index{birth rate} - -\end{itemize} - -We should not take too seriously the idea that this model can estimate carrying capacity. But the predictions of a model can be credible even if the assumptions of the model are not strictly true. For example, population growth might behave {\em as if} it is resource limited, even if the actual mechanism is something else. - -In fact, demographers who study population growth often use models similar to ours. In the next section, we'll compare our projections to theirs. - - -\section{Comparing projections} - -Table 3 from \url{http://modsimpy.com/worldpop} contains projections from the U.S. Census and the United Nations DESA: - -\begin{python} -table3 = tables[3] -\end{python} - -For some years, one agency or the other has not published a projection, so some elements of \py{table3} contain the special value \py{NaN}, which stands for ``not a number". \py{NaN} is often used to indicate missing data. - -\index{not a number} -\index{NaN} -\index{missing data} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap04-fig02.pdf}} -\caption{Projections of world population generated by the U.S. Census Bureau, the United Nations, and our quadratic model.} -\label{chap04-fig02} -\end{figure} - -Pandas provides functions that deal with missing data, including \py{dropna}, which removes any elements in a series that contain \py{NaN}. Using \py{dropna}, we can plot the projections like this: - -\index{Pandas} -\index{dropna} - -\begin{python} -def plot_projections(table): - census_proj = table.census / 1e9 - un_proj = table.un / 1e9 - - plot(census_proj.dropna(), 'b:', label='US Census') - plot(un_proj.dropna(), 'g--', label='UN DESA') -\end{python} - -The format string \py{'b:'} indicates a blue dotted line; \py{g--} indicates a green dashed line. - -\index{format string} -\index{plot} - -We can run our model over the same interval: - -\begin{python} -system.t_end = 2100 -results = run_simulation(system, update_func_quad) -\end{python} - -And compare our projections to theirs. Figure~\ref{chap04-fig02} shows the results. Real demographers expect world population to grow more slowly than our model projects, probably because their models are broken down by region and country, where conditions are different, and they take into account expected economic development. - -\index{demography} - -Nevertheless, their projections are qualitatively similar to ours, and theirs differ from each other almost as much as they differ from ours. So the results from this model, simple as it is, are not entirely crazy. - -Before you go on, you might want to read the notebook for this chapter, \py{chap08.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - -\chapter{Analysis} -\label{chap09} - -In this chapter we express the models from previous chapters as difference equations and differential equations, solve the equations, and derive the functional forms of the solutions. We also discuss the complementary roles of mathematical analysis and simulation. - - -\section{Recurrence relations} - -The population models in the previous chapter and this one are simple enough that we didn't really need to run simulations. We could have solved them mathematically. For example, we wrote the constant growth model like this: - -\begin{python} -model[t+1] = model[t] + annual_growth -\end{python} - -In mathematical notation, we would write the same model like this: -% -\[ x_{n+1} = x_n + c \] -% -where $x_n$ is the population during year $n$, $x_0$ is a given initial population, and $c$ is constant annual growth. This way of representing the model is a {\bf recurrence relation}; see \url{http://modsimpy.com/recur}. - -\index{recurrence relation} - -Sometimes it is possible to solve a recurrence relation by writing an equation that computes $x_n$, for a given value of $n$, directly; that is, without computing the intervening values from $x_1$ through $x_{n-1}$. - -In the case of constant growth we can see that $x_1 = x_0 + c$, and $x_2 = x_1 + c$. Combining these, we get $x_2 = x_0 + 2c$, then $x_3 = x_0 + 3c$, and it is not hard to conclude that in general -% -\[ x_n = x_0 + nc \] -% -So if we want to know $x_{100}$ and we don't care about the other values, we can compute it with one multiplication and one addition. - -We can also write the proportional model as a recurrence relation: -% -\[ x_{n+1} = x_n + \alpha x_n \] -% -Or more conventionally as: -% -\[ x_{n+1} = x_n (1 + \alpha) \] -% -Now we can see that $x_1 = x_0 (1 + \alpha)$, and $x_2 = x_0 (1 + \alpha)^2$, and in general -% -\[ x_n = x_0 (1 + \alpha)^n \] -% -This result is a {\bf geometric progression}; see \url{http://modsimpy.com/geom}. When $\alpha$ is positive, the factor $1+\alpha$ is greater than 1, so the elements of the sequence grow without bound. - -\index{geometric progression} -\index{quadratic growth} - -Finally, we can write the quadratic model like this: -% -\[ x_{n+1} = x_n + \alpha x_n + \beta x_n^2 \] -% -or with the more conventional parameterization like this: -% -\[ x_{n+1} = x_n + r x_n (1 - x_n / K) \] -% -There is no analytic solution to this equation, but we can approximate it with a differential equation and solve that, which is what we'll do in the next section. - - -\section{Differential equations} -\label{diffeq} - -Starting again with the constant growth model -% -\[ x_{n+1} = x_n + c \] -% -If we define $\Delta x$ to be the change in $x$ from one time step to the next, we can write: -% -\[ \Delta x = x_{n+1} - x_n = c \] -% -If we define $\Delta t$ to be the time step, which is one year in the example, we can write the rate of change per unit of time like this: -% -\[ \frac{\Delta x}{\Delta t} = c \] -% -This model is {\bf discrete}, which means it is only defined at integer values of $n$ and not in between. But in reality, people are born and die all the time, not once a year, so a {\bf continuous} model might be more realistic. - -\index{discrete} -\index{continuous} -\index{time step} - -We can make this model continuous by writing the rate of change in the form of a derivative: -% -\[ \frac{dx}{dt} = c \] -% -This way of representing the model is a {\bf differential equation}; see \url{http://modsimpy.com/diffeq}. - -\index{differential equation} - -We can solve this differential equation if we multiply both sides by $dt$: -% -\[ dx = c dt \] -% -And then integrate both sides: -% -\[ x(t) = c t + x_0 \] -% -Similarly, we can write the proportional growth model like this: -% -\[ \frac{\Delta x}{\Delta t} = \alpha x \] -% -And as a differential equation like this: -% -\[ \frac{dx}{dt} = \alpha x \] -% -If we multiply both sides by $dt$ and divide by $x$, we get -% -\[ \frac{1}{x}~dx = \alpha~dt \] -% -Now we integrate both sides, yielding: -% -\[ \ln x = \alpha t + K \] -% -where $\ln$ is the natural logarithm and $K$ is the constant of integration. Exponentiating both sides\footnote{The exponential function can be written $\exp(x)$ or $e^x$. In this book I use the first form because it resembles the Python code. }, we have -% -\[ \exp(\ln(x)) = \exp(\alpha t + K) \] -% -which we can rewrite -% -\[ x = \exp(\alpha t) \exp(K) \] -% -Since $K$ is an arbitrary constant, $\exp(K)$ is also an arbitrary constant, so we can write -% -\[ x = C \exp(\alpha t) \] -% -where $C = \exp(K)$. There are many solutions to this differential equation, with different values of $C$. The particular solution we want is the one that has the value $x_0$ when $t=0$. - -When $t=0$, $x(t) = C$, so $C = x_0$ and the solution we want is -% -\[ x(t) = x_0 \exp(\alpha t) \] -% -If you would like to see this derivation done more carefully, you might like this video: \url{http://modsimpy.com/khan1}. - -\index{logarithm} -\index{exponentiation} -\index{integration} -\index{constant of integration} - - -\section{Analysis and simulation} - -Once you have designed a model, there are generally two ways to proceed: simulation and analysis. Simulation often comes in the form of a computer program that models changes in a system over time, like births and deaths, or bikes moving from place to place. Analysis often comes in the form of algebra; that is, symbolic manipulation using mathematical notation. - -\index{analysis} -\index{algebra} -\index{symbolic manipulation} - -Analysis and simulation have different capabilities and limitations. Simulation is generally more versatile; it is easy to add and remove parts of a program and test many versions of a model, as we have done in the previous examples. - -But there are several things we can do with analysis that are harder or impossible with simulations: - -\begin{itemize} - -\item With analysis we can sometimes compute, exactly and efficiently, a value that we could only approximate, less efficiently, with simulation. For example, in Figure~\ref{chap03-fig05}, we can see that net growth goes to zero near 14 billion, and we could estimate carrying capacity using a numerical search algorithm (more about that later). But with the analysis in Section~\ref{equilibrium}, we get the general result that $K=-\alpha/\beta$. - -\item Analysis often provides ``computational shortcuts", that is, the ability to jump forward in time to compute the state of a system many time steps in the future without computing the intervening states. - -\index{time step} - -\item We can use analysis to state and prove generalizations about models; for example, we might prove that certain results will always or never occur. With simulations, we can show examples and sometimes find counterexamples, but it is hard to write proofs. - -\index{proof} - -\item Analysis can provide insight into models and the systems they describe; for example, sometimes we can identify regimes of qualitatively different behavior and key parameters that control those behaviors. - -\index{regime} - -\end{itemize} - -When people see what analysis can do, they sometimes get drunk with power, and imagine that it gives them a special ability to see past the veil of the material world and discern the laws of mathematics that govern the universe. When they analyze a model of a physical system, they talk about ``the math behind it" as if our world is the mere shadow of a world of ideal mathematical entities\footnote{I am not making this up; see \url{http://modsimpy.com/plato}.}. - -\index{Plato} - -This is, of course, nonsense. Mathematical notation is a language designed by humans for a purpose, specifically to facilitate symbolic manipulations like algebra. Similarly, programming languages are designed for a purpose, specifically to represent computational ideas and run programs. - -\index{math notation} -\index{programming languages} - -Each of these languages is good for the purposes it was designed for and less good for other purposes. But they are often complementary, and one of the goals of this book is to show how they can be used together. - - -\section{Analysis with WolframAlpha} - -Until recently, most analysis was done by rubbing graphite on wood pulp\footnote{Or ``rubbing the white rock on the black rock", a line I got from Woodie Flowers, who got it from Stephen Jacobsen.}, a process that is laborious and error-prone. A useful alternative is symbolic computation. If you have used services like WolframAlpha, you have used symbolic computation. - -\index{symbolic computation} -\index{WolframAlpha} - -For example, if you go to \url{https://www.wolframalpha.com/} and type - -\begin{python} -df(t) / dt = alpha f(t) -\end{python} - -WolframAlpha infers that \py{f(t)} is a function of \py{t} and \py{alpha} is a parameter; it classifies the query as a ``first-order linear ordinary differential equation", and reports the general solution: -% -\[ f(t) = c_1 \exp(\alpha t) \] -% -If you add a second equation to specify the initial condition: - -\begin{python} -df(t) / dt = alpha f(t), f(0) = p_0 -\end{python} - -WolframAlpha reports the particular solution: - -\[ f(t) = p_0 \exp(\alpha t) \] - -WolframAlpha is based on Mathematica, a powerful programming language designed specifically for symbolic computation. - -\index{Mathematica} - - -\section{Analysis with SymPy} - -Python has a library called SymPy that provides symbolic computation tools similar to Mathematica. They are not as easy to use as WolframAlpha, but they have some other advantages. - -\index{SymPy} - -Before we can use SymPy, we have to import it: - -\index{import statement} -\index{statement!import} - -\begin{python} -from sympy import * -\end{python} - -SymPy defines many functions, and some of them conflict with functions defined in \py{modsim} and the other libraries we're using. To avoid these conflicts, I suggest that you do symbolic computation with SymPy in a separate notebook. - -SymPy defines a \py{Symbol} object that represents symbolic variable names, functions, and other mathematical entities. - -\index{Symbol object} - -The \py{symbols} function takes a string and returns \py{Symbol} objects. So if we run this assignment: - -\begin{python} -t = symbols('t') -\end{python} - -Python understands that \py{t} is a symbol, not a numerical value. If we now run - -\begin{python} -expr = t + 1 -\end{python} - -Python doesn't try to perform numerical addition; rather, it creates a new \py{Symbol} that represents the sum of \py{t} and \py{1}. We can evaluate the sum using \py{subs}, which substitutes a value for a symbol. This example substitutes 2 for \py{t}: - -\begin{python} -expr.subs(t, 2) -\end{python} - -The result is 3. - -Functions in SymPy are represented by a special kind of \py{Symbol}: - -\begin{python} -f = Function('f') -\end{python} - -Now if we write \py{f(t)}, we get an object that represents the evaluation of a function, $f$, at a value, $t$. But again SymPy doesn't actually try to evaluate it. - - -\section{Differential equations in SymPy} - -SymPy provides a function, \py{diff}, that can differentiate a function. We can apply it to \py{f(t)} like this: - -\index{differential equation} -\index{SymPy} - -\begin{python} -dfdt = diff(f(t), t) -\end{python} - -The result is a \py{Symbol} that represents the derivative of \py{f} with respect to \py{t}. But again, SymPy doesn't try to compute the derivative yet. - -\index{Symbol object} - -To represent a differential equation, we use \py{Eq}: - -\begin{python} -alpha = symbols('alpha') -eq1 = Eq(dfdt, alpha*f(t)) -\end{python} - -The result is an object that represents an equation, which is displayed like this: -% -\[ \frac{d}{d t} f{\left (t \right )} = \alpha f{\left (t \right )} \] -% -Now we can use \py{dsolve} to solve this differential equation: - -\begin{python} -solution_eq = dsolve(eq1) -\end{python} - -The result is the equation -% -\[ f{\left (t \right )} = C_{1} \exp(\alpha t) \] -% -This is the {\bf general solution}, which still contains an unspecified constant, $C_1$. To get the {\bf particular solution} where $f(0) = p_0$, we substitute \py{p0} for \py{C1}. First, we have to create two more symbols: - -\index{general solution} -\index{particular solution} - -\begin{python} -C1, p_0 = symbols('C1 p_0') -\end{python} - -Now we can perform the substitution: - -\begin{python} -particular = solution_eq.subs(C1, p_0) -\end{python} - -The result is -% -\[ f{\left (t \right )} = p_{0} \exp(\alpha t) \] -% -This function is called the {\bf exponential growth curve}; see \url{http://modsimpy.com/expo}. - -\index{exponential growth} - - -\section{Solving the quadratic growth model} - -In the notebook for this chapter, you will see how to use the same tools to solve the quadratic growth model with parameters $r$ and $K$. The general solution is -% -\[ f{\left (t \right )} = \frac{K \exp(C_{1} K + r t)}{\exp(C_{1} K + r t) - 1} \] -% -To get the particular solution where $f(0) = p_0$, we evaluate the general solution at $t=0$, which yields: -% -\[ f(0) = \frac{K \exp(C_{1} K)}{\exp(C_{1} K) - 1} \] -% -Then we set this expression equal to $p_0$ and solve for $C_1$. The result is: -% -\[ C_1 = \frac{1}{K} \ln{\left (- \frac{p_{0}}{K - p_{0}} \right )} \] -% -Finally, we substitute this value of $C_1$ into the general solution, which yields: -% -\[ f(t) = \frac{K p_{0} \exp(r t)}{K + p_{0} \exp(r t) - p_{0}} \] -% -This function is called the {\bf logistic growth curve}; see \url{http://modsimpy.com/logistic}. In the context of growth models, the logistic function is often written, equivalently, -% -\[ f(t) = \frac{K}{1 + A \exp(-rt)} \] -% -where $A = (K - p_0) / p_0$. - -If you would like to see this differential equation solved by hand, you might like this video: \url{http://modsimpy.com/khan2} -\index{quadratic growth} -\index{logistic function} - - -\section{Summary} - -The following tables summarize the results so far: - -\begin{tabular}{l|l} -\hline -Growth type & Discrete (difference equation) \\ -\hline -Constant & linear: $x_n = p_0 + \alpha n$ \\ - -Proportional & geometric: $x_n = p_0(1+\alpha)^n$ \\ - -\end{tabular} - -\begin{tabular}{l|l} -\hline - & Continuous (differential equation) \\ -\hline -Constant & linear: $x(t) = p_0 + \alpha t$ \\ - -Proportional & exponential: $x(t) = p_0 \exp(\alpha t)$ \\ - -Quadratic & logistic: $x(t) = K / (1 + A\exp(-rt))$ \\ -\end{tabular} - -What I've been calling the constant growth model is more commonly called ``linear growth" because the solution is a line. Similarly, what I've called proportional is commonly called ``exponential", and what I've called quadratic is commonly called ``logistic". I avoided these terms until now because they are based on results we had not derived yet. - -\index{linear growth} -\index{exponential growth} -\index{logistic growth} - -Before you go on, you might want to read the notebook for this chapter, \py{chap09sympy.ipynb}. For instructions on downloading and running the code, see Section~\ref{code}. - - -\chapter{Case studies} -\label{chap10} - -This chapter reviews the computational patterns we have seen so far and presents exercises where you can apply them. - -\section{Computational tools} - -In Chapter~\ref{chap01} we used Pint to define units and perform calculations with units: - -\begin{code} -meter = UNITS.meter -second = UNITS.second -a = 9.8 * meter / second**2 -\end{code} - -In Chapter~\ref{chap02} we defined a \py{State} object that contains variables that represent the state of a system, usually changing over time: - -\begin{code} -bikeshare = State(olin=10, wellesley=2) -\end{code} - -We used update operators like \py{+=} and \py{-=} to change state variables. We used \py{print} statements to display the values of variables. - -We used the \py{flip} function to simulate random arrivals, and used \py{if} statements to check the results. - -We learned to define new functions that take parameters: - -\begin{code} -def step(p1, p2): - if flip(p1): - bike_to_wellesley() - - if flip(p2): - bike_to_olin() -\end{code} - -We used a \py{for} loop with the \py{range} function to execute the body of the loop a specified number of times. - -\begin{code} -for i in range(4): - step(p1, p2) -\end{code} - -We learned to create a \py{TimeSeries} object and use it to store the value of a state variable as it changes over time: - -\begin{code} -results = TimeSeries() - -for i in range(10): - step(0.3, 0.2) - results[i] = bikeshare.olin -\end{code} - -We used \py{plot} to plot the results, \py{decorate} to label the axes, and \py{savefig} to save the figure. - -\begin{code} -plot(results, label='Olin') -decorate(xlabel='Time step (min)', - ylabel='Number of bikes') -savefig('chap01-fig01.pdf) -\end{code} - -In Chapter~\ref{chap03} we used comparison operators to check for certain conditions and the \py{return} statement to end the execution of a function. - -\begin{code} -def bike_to_olin(state): - if state.wellesley == 0: - state.wellesley_empty += 1 - return - state.wellesley -= 1 - state.olin += 1 -\end{code} - -In Chapter~\ref{chap04} we wrote a version of \py{run_simulation} that uses a \py{return} statement to return a value: - -\begin{code} -def run_simulation(p1, p2, num_steps): - state = State(olin=10, wellesley=2, - olin_empty=0, wellesley_empty=0) - - for i in range(num_steps): - step(state, p1, p2) - - return state -\end{code} - -This version of \py{run_simulation} returns the final value of \py{state}, which contains metrics we can use to measure the performance of the system. - -We used \py{linspace} to create a NumPy array of equally spaced values, and a \py{for} loop to loop through the array. We used a \py{SweepSeries} to store results from a series of simulations, mapping from the value of a parameter to the value of a resulting metric. - -\begin{code} -p1_array = linspace(0, 1, 11) -sweep = SweepSeries() - -for p1 in p1_array: - state = run_simulation(p1, p2, num_steps) - sweep[p1] = state.olin_empty -\end{code} - -In Chapter~\ref{chap05} we used Pandas to read data from a web page and store the results in a \py{DataFrame}. We selected a column from the \py{DataFrame} to get a \py{Series}. - -In Chapter~\ref{chap06} we created a \py{System} object to contain the parameters of the model, and defined another version of \py{run_simulation}: - -\begin{code} -def run_simulation(system, update_func): - results = TimeSeries() - results[system.t_0] = system.p_0 - - for t in linrange(system.t_0, system.t_end): - results[t+1] = update_func(results[t], t, system) - - return results -\end{code} - -This version takes a \py{System} object as a parameter, and an update function. Instead of returning the final state of the system, it returns a \py{TimeSeries} that contains the state as it changes over time. - -The update function takes the current state of the system, the time, and the \py{System} object as parameters, and returns the new state. For example, here's the update function for the quadratic growth model. - -\begin{code} -def update_func_quad(pop, t, system): - net_growth = system.alpha * pop + system.beta * pop**2 - return pop + net_growth -\end{code} - -In this example, the state of the system is a single number, \py{pop}. Later we'll see examples where state is represented by a \py{State} object with more than one variable. - -Chapter~\ref{chap07} introduces the quadratic growth model and -Chapter~\ref{chap08} uses the model to generate predictions, but neither chapter introduces new computational tools. - -Chapter~\ref{chap09} introduces SymPy, which we can use to create \py{Symbol} objects: - -\begin{code} -t, alpha = symbols('t alpha') -f = Function('f') -\end{code} - -Write differential equations: - -\begin{code} -dfdt = diff(f(t), t) -eq1 = Eq(dfdt, alpha*f(t)) -\end{code} - -And solve them: - -\begin{code} -solution_eq = dsolve(eq1) -\end{code} - -That's a brief summary of the computational tools we have seen so far. - - -\section{Under the hood} -\label{dataframe} - -So far we've been using \py{DataFrame} and \py{Series} objects without really understanding how they work. In this section we'll review what we know so far and get into a little more detail. - -Each \py{DataFrame} contains three objects: \py{index} is a sequence of labels for the rows, \py{columns} is a sequence of labels for the columns, and \py{values} is a NumPy array that contains the data. - -In the \py{DataFrame} objects in this chapter, \py{index} contains years from 1950 to 2016, \py{columns} contains names of agencies and people that produce population estimates, and \py{values} is an array of estimates. - -\begin{figure} -\centerline{\includegraphics[height=2.5in]{figs/dataframe.pdf}} -\caption{The elements that make up a \py{DataFrame} and a \py{Series}.} -\label{fig-dataframe} -\end{figure} - -A \py{Series} is like a \py{DataFrame} with one column: it contains a string \py{name} that is like a column label, an index, and an array of values. - -Figure~\ref{fig-dataframe} shows the elements of a \py{DataFrame} and \py{Series} graphically. - -\index{type function} - -To determine the types of these elements, we can use the Python function \py{type}: - -\begin{code} -type(table2) -type(table2.index) -type(table2.columns) -type(table2.values) -\end{code} - -The type of \py{table2} is \py{DataFrame}. The type of \py{table2.index} is \py{Int64Index}, which is similar to a \py{Series}. - -The type of \py{table2.columns} is \py{Index}, which might seem strange, because ``the" index is the sequence of row labels. But the sequence of column labels is also a kind of index. - -The type of \py{table2.values} is \py{ndarray}, which is the primary array type provided by NumPy; the name indicates that the array is ``n-dimensional"; that is, it can have an arbitrary number of dimensions. - -In \py{census} or \py{un}, the index is an \py{Int64Index} object and the values are stored in an \py{ndarray}. - -In the \py{modsim} library, the functions \py{get_first_label} and \py{get_last_label} provide a simple way to access the index of a \py{DataFrame} or \py{Series}: - -\begin{code} -def get_first_label(series): - return series.index[0] - -def get_last_label(series): - return series.index[-1] -\end{code} - -In brackets, the number \py{0} selects the first label; the number \py{-1} selects the last label. - -Several of the objects defined in \py{modsim} are modified versions of \py{Series} objects. \py{State} and \py{System} objects are \py{Series} where the labels are variables names. A \py{TimeSeries} is a \py{Series} where the labels are times, and a \py{SweepSeries} is a \py{Series} where the labels are parameter values. - -Defining these objects wasn't necessary; we could do all the same things using \py{Series} objects. But giving them different names makes the code easier to read and understand, and helps avoid certain kinds of errors (like getting two \py{Series} objects mixed up). - -If you write simulations in Python in the future, you can continue using the objects in \py{modsim}, if you find them useful, or you can use Pandas objects directly. - -\section{One queue or two?} - -This chapter presents two cases studies that let you practice what you have learned so far. The first case study is related to {\bf queueing theory}, which is the study of systems that involve waiting in lines, also known as ``queues". - -Suppose you are designing the checkout area for a new store. There is enough room in the store for two checkout counters and a waiting area for customers. You can make two lines, one for each counter, or one line that feeds both counters. - -In theory, you might expect a single line to be better, but it has some practical drawbacks: in order to maintain a single line, you might have to install barriers, and customers might be put off by what seems to be a longer line, even if it moves faster. - -So you'd like to check whether the single line is really better and by how much. Simulation can help answer this question. - -\begin{figure} -\centerline{\includegraphics[width=4.5in]{figs/queue.pdf}} -\caption{One queue, one server (left), one queue, two servers (middle), two queues, two servers (right).} -\label{fig-queue} -\end{figure} - -Figure~\ref{fig-queue} shows the three scenarios we'll consider. As we did in the bike share model, we'll assume that a customer is equally likely to arrive during any time step. I'll denote this probability using the Greek letter lambda, $\lambda$, or the variable name \py{lam}. The value of $\lambda$ probably varies from day to day, so we'll have to consider a range of possibilities. - -Based on data from other stores, you know that it takes 5 minutes for a customer to check out, on average. But checkout times are variable: most customers take less than 5 minutes, but some take substantially more. A simple way to model this variability is to assume that when a customer is checking out, always have the same probability of finishing during the next time step, regardless of how long they have been checking out. I'll denote this probability using the Greek letter mu, $\mu$, or the variable name \py{mu}. - -If we choose $\mu=1/5$ per minute, the average time for each checkout will be 5 minutes, which is consistent with the data. Most people takes less than 5 minutes, but a few take substantially longer, which is probably not a bad model of the distribution in real stores. - -Now we're ready to get started. In the repository for this book, you'll find a notebook called \py{queue.ipynb} that contains some code to get you started and instructions. - -As always, you should practice incremental development: write no more than one or two lines of code a time, and test as you go! - - - - -\section{Predicting salmon populations} - -Each year the U.S. Atlantic Salmon Assessment Committee reports estimates of salmon populations in oceans and rivers in the northeastern United States. The reports are useful for monitoring changes in these populations, but they generally do not include predictions. - -The goal of this case study is to model year-to-year changes in population, evaluate how predictable these changes are, and estimate the probability that a particular population will increase or decrease in the next 10 years. - -As an example, I use data from page 18 of the 2017 report, which provides population estimates for the Narraguagus and Sheepscot Rivers in Maine. - -In the repository for this book, you'll find a notebook called \py{salmon.ipynb} that contains some code to get you started and instructions. - -You should take my instructions as suggestions; if you want to try something different, please do! - - -\section{Tree growth} - -This case study is based on ``Height-Age Curves for Planted Stands of Douglas Fir, with Adjustments for Density", a working paper by Flewelling, Collier, Gonyea, Marshall, and Turnblom, available from \url{http://modsimpy.com/trees}. - -It provides ``site index curves", which are curves that show the expected height of the tallest tree in a stand of Douglas firs as a function of age, for a stand where the trees are the same age. - -Depending on the quality of the site, the trees might grow more quickly or slowing. So each curve is identified by a ``site index" that indicates the quality of the site. - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/trees-fig01.pdf}} -\caption{Site index curves for tree growth.} -\label{trees-fig01} -\end{figure} - -Figure~\ref{trees-fig01} shows site curves for three different site indices. -The goal of this case study is to explain the shape of these curves; that is, why trees grow the way they do. - -As a starting place, let's assume that the ability of the tree to gain mass is limited by the area it exposes to sunlight, and that the growth rate (in mass) is proportional to that area. In that case we can write: -% -$ m_{n+1} = m_n + \alpha A$ -% -where $m_n$ is the mass of the at time step $n$, $A$ is the area exposed to sunlight, and $\alpha$ is an unknown growth parameter. - -To get from $m$ to $A$, I'll make the additional assumption that mass is proportional to height raised to an unknown power: -% -$ m = \beta h^D $ -% -where $h$ is height, $\beta$ is an unknown constant of proportionality, and $D$ is the dimension that relates height and mass. I start by assuming $D=3$, but then revisit that assumption. - -Finally, we'll assume that area is proportional to height squared: - -$ A = \gamma h^2$ - -I specify height in feet, and choose units for mass and area so that $\beta=1$ and $\gamma=1$. Putting all that together, we can write a difference equation for height: - -$ h_{n+1}^D = h_n^D + \alpha h_n^2 $ - -The solution to this equation turns out to be close to a straight line, which is not a bad model for the growth curves. But the model that trees can grow forever, and we know that's not true. As trees get taller, it gets harder for them to move water and nutrients against the force of gravity, and their growth slows. - -We can model this effect by adding a factor to the model similar to what we saw in the logistic model of population growth. Instead of assuming: - -$ m_{n+1} = m_n + \alpha A $ - -Let's assume - -$ m_{n+1} = m_n + \alpha A (1 - h / K) $ - -where $K$ is similar to the carrying capacity of the logistic model. As $h$ approaches $K$, the factor $(1 - h/K)$ goes to 0, causing growth to level off. - -In the repository for this book, you'll find a notebook called \py{trees.ipynb} that implements both models and uses them to fit the data. There are no exercises in this case study; it is mostly meant as an example of what you can do with the tools we have so far, and a preview of what we will be able to do with the tools in the next few chapters. - - - -\chapter{Epidemiology} -\label{chap11} - -In this chapter, we develop a model of an epidemic as it spreads in a susceptible population, and use it to evaluate the effectiveness of possible interventions. - -\index{epidemic} - -My presentation of the SIR model in the next few chapters is based on an excellent article by David Smith and Lang Moore\footnote{Smith and Moore, ``The SIR Model for Spread of Disease," Journal of Online Mathematics and its Applications, December 2001, at \url{http://modsimpy.com/sir}.}. - -\index{SIR model} - - -\section{The Freshman Plague} - -Every year at Olin College, about 90 new students come to campus from around the country and the world. Most of them arrive healthy and happy, but usually at least one brings with them some kind of infectious disease. A few weeks later, predictably, some fraction of the incoming class comes down with what we call ``The Freshman Plague". - -\index{Olin College} -\index{Freshman Plague} -\index{Kermack-McKendrick} - -In this chapter we introduce a well-known model of infectious disease, the Kermack-McKendrick model, and use it to explain the progression of the disease over the course of the semester, predict the effect of possible interventions (like immunization) and design the most effective intervention campaign. - -\index{disease} -\index{infection} -\index{design} - -So far we have done our own modeling; that is, we've chosen physical systems, identified factors that seem important, and made decisions about how to represent them. In this chapter we start with an existing model and reverse-engineer it. Along the way, we consider the modeling decisions that went into it and identify its capabilities and limitations. - - -\section{The SIR model} - -The Kermack-McKendrick model is a simple version of an {\bf SIR model}, so-named because it considers three categories of people: - -\begin{itemize} - -\item {\bf S}: People who are ``susceptible", that is, capable of contracting the disease if they come into contact with someone who is infected. - -\item {\bf I}: People who are ``infectious", that is, capable of passing along the disease if they come into contact with someone susceptible. - -\item {\bf R}: People who are ``recovered". - -\end{itemize} - -In the basic version of the model, people who have recovered are considered to be immune to reinfection. That is a reasonable model for some diseases, but not for others, so it should be on the list of assumptions to reconsider later. - -Let's think about how the number of people in each category changes over time. Suppose we know that people with the disease are infectious for a period of 4 days, on average. If 100 people are infectious at a particular point in time, and we ignore the particular time each one became infected, we expect about 1 out of 4 to recover on any particular day. - -Putting that a different way, if the time between recoveries is 4 days, the recovery rate is about 0.25 recoveries per day, which we'll denote with the Greek letter gamma, $\gamma$. If the total number of people in the population is $N$, and the fraction currently infectious is $i$, the total number of recoveries we expect per day is $\gamma i N$. - -\index{recovery rate} - -Now let's think about the number of new infections. Suppose we know that each susceptible person comes into contact with 1 person every 3 days, on average, in a way that would cause them to become infected if the other person is infected. We'll denote this contact rate with the Greek letter beta, $\beta$. - -\index{infection rate} - -It's probably not reasonable to assume that we know $\beta$ ahead of time, but later we'll see how to estimate it based on data from previous outbreaks. - -If $s$ is the fraction of the population that's susceptible, $s N$ is the number of susceptible people, $\beta s N$ is the number of contacts per day, and $\beta s i N$ is the number of those contacts where the other person is infectious. - -\index{susceptible} - -In summary: - -\begin{itemize} - -\item The number of recoveries we expect per day is $\gamma i N$; dividing by $N$ yields the fraction of the population that recovers in a day, which is $\gamma i$. - -\item The number of new infections we expect per day is $\beta s i N$; dividing by $N$ yields the fraction of the population that gets infected in a day, which is $\beta s i$. - -\end{itemize} - -This model assumes that the population is closed; that is, no one arrives or departs, so the size of the population, $N$, is constant. - - -\section{The SIR equations} -\label{sireqn} - -If we treat time as a continuous quantity, we can write differential equations that describe the rates of change for $s$, $i$, and $r$ (where $r$ is the fraction of the population that has recovered): -% -\begin{align*} -\frac{ds}{dt} &= -\beta s i \\ -\frac{di}{dt} &= \beta s i - \gamma i\\ -\frac{dr}{dt} &= \gamma i -\end{align*} -% -To avoid cluttering the equations, I leave it implied that $s$ is a function of time, $s(t)$, and likewise for $i$ and $r$. -\index{differential equation} - -SIR models are examples of {\bf compartment models}, so-called because they divide the world into discrete categories, or compartments, and describe transitions from one compartment to another. Compartments are also called {\bf stocks} and transitions between them are called {\bf flows}. - -\index{compartment model} -\index{stock} -\index{flow} -\index{stock and flow diagram} - -In this example, there are three stocks --- susceptible, infectious, and recovered --- and two flows --- new infections and recoveries. Compartment models are often represented visually using stock-and-flow diagrams (see \url{http://modsimpy.com/stock}. -Figure~\ref{stock_flow1} shows the stock and flow diagram for an SIR model. - -\begin{figure} -% first version was on YUML -% https://yuml.me/edit/3de9c163 -% wget https://yuml.me/2389d485.pdf; mv 2389d485.pdf figs/stock_flow1.pdf -% current version uses lodraw -\centerline{\includegraphics[width=4in]{figs/stock_flow1.pdf}} -\caption{Stock and flow diagram for an SIR model.} -\label{stock_flow1} -\end{figure} - -Stocks are represented by rectangles, flows by arrows. The widget in the middle of the arrows represents a valve that controls the rate of flow; the diagram shows the parameters that control the valves. - - -\section{Implementation} - -For a given physical system, there are many possible models, and for a given model, there are many ways to represent it. For example, we can represent an SIR model as a stock-and-flow diagram, as a set of differential equations, or as a Python program. The process of representing a model in these forms is called {\bf implementation}. In this section, we implement the SIR model in Python. - -\index{implementation} - -I'll represent the initial state of the system using a \py{State} object with state variables \py{S}, \py{I}, and \py{R}; they represent the fraction of the population in each compartment. - -\index{System object} -\index{State object} -\index{state variable} - -We can initialize the \py{State} object with the {\em number} of people in each compartment, assuming there is one infected student in a class of 90: - -\begin{python} -init = State(S=89, I=1, R=0) -\end{python} - -And then convert the numbers to fractions by dividing by the total: - -\begin{python} -init /= sum(init) -\end{python} - -For now, let's assume we know the time between contacts and time between recoveries: - -\begin{python} -tc = 3 # time between contacts in days -tr = 4 # recovery time in days -\end{python} - -We can use them to compute the parameters of the model: - -\begin{python} -beta = 1 / tc # contact rate in per day -gamma = 1 / tr # recovery rate in per day -\end{python} - -Now we need a \py{System} object to store the parameters and initial conditions. The following function takes the system parameters as function parameters and returns a new \py{System} object: - -\index{\py{make_system}} - -\begin{python} -def make_system(beta, gamma): - init = State(S=89, I=1, R=0) - init /= sum(init) - - t0 = 0 - t_end = 7 * 14 - - return System(init=init, t0=t0, t_end=t_end, - beta=beta, gamma=gamma) -\end{python} - -The default value for \py{t_end} is 14 weeks, about the length of a semester. - - -\section{The update function} - -At any point in time, the state of the system is represented by a \py{State} object with three variables, \py{S}, \py{I} and \py{R}. So I'll define an update function that takes as parameters a \py{State} object, the current time, and a \py{System} object: - -\index{update function} -\index{function!update} -\index{time step} - -\begin{python} -def update_func(state, t, system): - s, i, r = state - - infected = system.beta * i * s - recovered = system.gamma * i - - s -= infected - i += infected - recovered - r += recovered - - return State(S=s, I=i, R=r) -\end{python} - -The first line uses a feature we have not seen before, {\bf multiple assignment}. The value on the right side is a \py{State} object that contains three values. The left side is a sequence of three variable names. The assignment does just what we want: it assigns the three values from the \py{State} object to the three variables, in order. - -The local variables, \py{s}, \py{i} and \py{r}, are lowercase to distinguish them from the state variables, \py{S}, \py{I} and \py{R}. - -\index{State object} -\index{state variable} -\index{local variable} - -The update function computes \py{infected} and \py{recovered} as a fraction of the population, then updates \py{s}, \py{i} and \py{r}. The return value is a \py{State} that contains the updated values. - -\index{return value} - -When we call \py{update_func} like this: - -\begin{python} -state = update_func(init, 0, system) -\end{python} - -The result is a \py{State} object with these values: - -\begin{tabular}{lr} - & {\bf \sf value} \\ -\hline -{\bf \sf S} & 0.985388 \\ -{\bf \sf I} & 0.011865 \\ -{\bf \sf R} & 0.002747 \\ -\end{tabular} - -You might notice that this version of \py{update_func} does not use one of its parameters, \py{t}. I include it anyway because update functions sometimes depend on time, and it is convenient if they all take the same parameters, whether they need them or not. - -%TODO: figure out when to talk about integers and floats (or never) - - -\section{Running the simulation} - -Now we can simulate the model over a sequence of time steps: - -\index{time step} - -\begin{python} -def run_simulation(system, update_func): - state = system.init - - for t in linrange(system.t0, system.t_end): - state = update_func(state, t, system) - - return state -\end{python} - -The parameters of \py{run_simulation} are the \py{System} object and the update function. The \py{System} object contains the parameters, initial conditions, and values of \py{t0} and \py{t_end}. - -\index{\py{run_simulation}} - -The outline of this function should look familiar; it is similar to the function we used for the population model in Section~\ref{nowwithsystem}. - -We can call \py{run_simulation} like this: - -\begin{python} -system = make_system(beta, gamma) -final_state = run_simulation(system, update_func) -\end{python} - -The result is the final state of the system: - -\begin{tabular}{lr} - & {\bf \sf value} \\ -\hline -{\bf \sf S} & 0.520819 \\ -{\bf \sf I} & 0.000676 \\ -{\bf \sf R} & 0.478505 \\ -\end{tabular} - -This result indicates that after 14 weeks (98 days), about 52\% of the population is still susceptible, which means they were never infected, less than 1\% are actively infected, and 48\% have recovered, which means they were infected at some point. - - -\section{Collecting the results} - -The previous version of \py{run_simulation} only returns the final state, but we might want to see how the state changes over time. We'll consider two ways to do that: first, using three \py{TimeSeries} objects, then using a new object called a \py{TimeFrame}. - -\index{TimeFrame object} -\index{TimeSeries object} - -Here's the first version: - -\begin{python} -def run_simulation(system, update_func): - S = TimeSeries() - I = TimeSeries() - R = TimeSeries() - - state = system.init - t0 = system.t0 - S[t0], I[t0], R[t0] = state - - for t in linrange(system.t0, system.t_end): - state = update_func(state, t, system) - S[t+1], I[t+1], R[t+1] = state - - return S, I, R -\end{python} - -First, we create \py{TimeSeries} objects to store the results. Notice that the variables \py{S}, \py{I}, and \py{R} are \py{TimeSeries} objects now. - -Next we initialize \py{state}, \py{t0}, and the first elements of \py{S}, \py{I} and \py{R}. - -Inside the loop, we use \py{update_func} to compute the state of the system at the next time step, then use multiple assignment to unpack the elements of \py{state}, assigning each to the corresponding \py{TimeSeries}. - -\index{time step} - -At the end of the function, we return the values \py{S}, \py{I}, and \py{R}. This is the first example we have seen where a function returns more than one value. - -Now we can run the function like this: - -\begin{python} -system = make_system(beta, gamma) -S, I, R = run_simulation(system, update_func) -\end{python} - -We'll use the following function to plot the results: - -\begin{python} -def plot_results(S, I, R): - plot(S, '--', label='Susceptible') - plot(I, '-', label='Infected') - plot(R, ':', label='Resistant') - decorate(xlabel='Time (days)', - ylabel='Fraction of population') -\end{python} - -\index{plot} -\index{decorate} - -And run it like this: - -\begin{python} -plot_results(S, I, R) -\end{python} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap05-fig01.pdf}} -\caption{Time series for \py{S}, \py{I}, and \py{R} over the course of 98 days.} -\label{chap05-fig01} -\end{figure} - -Figure~\ref{chap05-fig01} shows the result. Notice that it takes about three weeks (21 days) for the outbreak to get going, and about six weeks (42 days) before it peaks. The fraction of the population that's infected is never very high, but it adds up. In total, almost half the population gets sick. - - -\section{Now with a TimeFrame} -\label{timeframe} - -If the number of state variables is small, storing them as separate \py{TimeSeries} objects might not be so bad. But a better alternative is to use a \py{TimeFrame}, which is another object defined in the \py{modsim} library. - -\index{TimeFrame object} -\index{DataFrame object} - -A \py{TimeFrame} is almost identical to a \py{DataFrame}, which we used in Section~\ref{worldpopdata}, with just a few changes I made to adapt it for our purposes. - -Here's a more concise version of \py{run_simulation} using a \py{TimeFrame}: - -\begin{python} -def run_simulation(system, update_func): - frame = TimeFrame(columns=system.init.index) - frame.row[system.t0] = system.init - - for t in linrange(system.t0, system.t_end): - frame.row[t+1] = update_func(frame.row[t], system) - - return frame -\end{python} - -The first line creates an empty \py{TimeFrame} with one column for each state variable. Then, before the loop starts, we store the initial conditions in the \py{TimeFrame} at \py{t0}. Based on the way we've been using \py{TimeSeries} objects, it is tempting to write: - -\begin{python} -frame[system.t0] = system.init -\end{python} - -But when you use the bracket operator with a \py{TimeFrame} or \py{DataFrame}, it selects a column, not a row. For example, to select a column, we could write: - -\index{bracket operator} -\index{operator~bracket} - -\begin{python} -frame['S'] -\end{python} - -To select a row, we have to use \py{row}, like this: - -\index{row} - -\begin{python} -frame.row[system.t0] = system.init -\end{python} - -Since the value on the right side is a \py{State}, the assignment matches up the index of the \py{State} with the columns of the \py{TimeFrame}; that is, it assigns the \py{S} value from \py{system.init} to the \py{S} column of \py{frame}, and likewise with \py{I} and \py{R}. - -\index{assignment} - -We can use the same feature to write the loop more concisely, assigning the \py{State} we get from \py{update_func} directly to the next row of \py{frame}. - -\index{system variable} - -Finally, we return \py{frame}. We can call this version of \py{run_simulation} like this: - -\begin{python} -results = run_simulation(system, update_func) -\end{python} - -And plot the results like this: - -\begin{python} -plot_results(results.S, results.I, results.R) -\end{python} - -As with a \py{DataFrame}, we can use the dot operator to select columns from a \py{TimeFrame}. - -\index{dot operator} -\index{operator!dot} - -Before you go on, you might want to read the notebook for this chapter, \py{chap11.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - -\chapter{Optimization} -\label{chap12} - -In the previous chapter I presented the SIR model of infectious disease and used it to model the Freshman Plague at Olin. In this chapter we'll consider metrics intended to quantify the effect of the disease and interventions that might reduce the negative effects. - - -\section{Metrics} -\label{metrics2} - -When we plot a time series, we get a view of everything that happened when the model ran, but often we want to boil it down to a few numbers that summarize the outcome. These summary statistics are called {\bf metrics}, as we saw in Section~\ref{metrics}. - -\index{metric} - -In the SIR model, we might want to know the time until the peak of the outbreak, the number of people who are sick at the peak, the number of students who will still be sick at the end of the semester, or the total number of students who get sick at any point. - -As an example, I will focus on the last one --- the total number of sick students --- and we will consider interventions intended to minimize it. - -When a person gets infected, they move from \py{S} to \py{I}, so we can get the total number of infections by computing the difference in \py{S} at the beginning and the end: - -\begin{python} -def calc_total_infected(results, system): - return results.S[system.t0] - results.S[system.t_end] -\end{python} - -In the notebook that accompanies this chapter, you will have a chance to write functions that compute other metrics. Two functions you might find useful are \py{max} and \py{idxmax}. - -\index{max} -\index{idxmax} - -If you have a \py{Series} called \py{S}, you can compute the largest value of the series like this: - -\begin{python} -largest_value = S.max() -\end{python} - -And the label of the largest value like this: - -\begin{python} -time_of_largest_value = S.idxmax() -\end{python} - -If the \py{Series} is a \py{TimeSeries}, the label you get from \py{idxmax} is a time or date. You can read more about these functions in the \py{Series} documentation at \url{http://modsimpy.com/series}. - -\index{Series} - - -\section{Immunization} - -Models like this are useful for testing ``what if?" scenarios. As an example, we'll consider the effect of immunization. - -\index{immunization} -\index{vaccine} -\index{Freshman Plague} - -Suppose there is a vaccine that causes a student to become immune to the Freshman Plague without being infected. How might you modify the model to capture this effect? - -One option is to treat immunization as a short cut from susceptible to recovered without going through infectious. We can implement this feature like this: - -\begin{python} -def add_immunization(system, fraction): - system.init.S -= fraction - system.init.R += fraction -\end{python} - -\py{add_immunization} moves the given fraction of the population from \py{S} to \py{R}. If we assume that 10\% of students are vaccinated at the beginning of the semester, and the vaccine is 100\% effective, we can simulate the effect like this: - -\begin{python} -system2 = make_system(beta, gamma) -add_immunization(system2, 0.1) -results2 = run_simulation(system2, update_func) -\end{python} - -For comparison, we can run the same model without immunization and plot the results. Figure~\ref{chap05-fig02} shows \py{S} as a function of time, with and without immunization. - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap05-fig02.pdf}} -\caption{Time series for \py{S}, with and without immunization.} -\label{chap05-fig02} -\end{figure} - -Without immunization, almost 47\% of the population gets infected at some point. With 10\% immunization, only 31\% gets infected. That's pretty good. - -Now let's see what happens if we administer more vaccines. This following function sweeps a range of immunization rates: - -\index{sweep} - -\begin{python} -def sweep_immunity(immunize_array): - sweep = SweepSeries() - - for fraction in immunize_array: - sir = make_system(beta, gamma) - add_immunization(sir, fraction) - results = run_simulation(sir, update_func) - sweep[fraction] = calc_total_infected(results, sir) - - return sweep -\end{python} - -The parameter of \py{sweep_immunity} is an array of immunization rates. The result is a \py{SweepSeries} object that maps from each immunization rate to the resulting fraction of students ever infected. - -\index{SweepSeries object} -\index{parameter sweep} - -Figure~\ref{chap05-fig03} shows a plot of the \py{SweepSeries}. Notice that the x-axis is the immunization rate, not time. - -As the immunization rate increases, the number of infections drops steeply. If 40\% of the students are immunized, fewer than 4\% get sick. That's because immunization has two effects: it protects the people who get immunized (of course) but it also protects the rest of the population. - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap05-fig03.pdf}} -\caption{Fraction of the population infected as a function of immunization rate.} -\label{chap05-fig03} -\end{figure} - -Reducing the number of ``susceptibles" and increasing the number of ``resistants" makes it harder for the disease to spread, because some fraction of contacts are wasted on people who cannot be infected. This phenomenon is called {\bf herd immunity}, and it is an important element of public health (see \url{http://modsimpy.com/herd}). - -\index{herd immunity} - -The steepness of the curve in Figure~\ref{chap05-fig03} is a blessing and a curse. It's a blessing because it means we don't have to immunize everyone, and vaccines can protect the ``herd" even if they are not 100\% effective. - -But it's a curse because a small decrease in immunization can cause a big increase in infections. In this example, if we drop from 80\% immunization to 60\%, that might not be too bad. But if we drop from 40\% to 20\%, that would trigger a major outbreak, affecting more than 15\% of the population. For a serious disease like measles, just to name one, that would be a public health catastrophe. - -\index{measles} - -One use of models like this is to demonstrate phenomena like herd immunity and to predict the effect of interventions like vaccination. Another use is to evaluate alternatives and guide decision making. We'll see an example in the next section. - - - - - -\section{Hand washing} - -Suppose you are the Dean of Student Life, and you have a budget of just \$1200 to combat the Freshman Plague. You have two options for spending this money: - -\begin{enumerate} - -\item You can pay for vaccinations, at a rate of \$100 per dose. - -\item You can spend money on a campaign to remind students to wash hands frequently. - -\end{enumerate} - -We have already seen how we can model the effect of vaccination. Now let's think about the hand-washing campaign. We'll have to answer two questions: - -\begin{enumerate} - -\item How should we incorporate the effect of hand washing in the model? - -\item How should we quantify the effect of the money we spend on a hand-washing campaign? - -\end{enumerate} - -For the sake of simplicity, let's assume that we have data from a similar campaign at another school showing that a well-funded campaign can change student behavior enough to reduce the infection rate by 20\%. - -In terms of the model, hand washing has the effect of reducing \py{beta}. That's not the only way we could incorporate the effect, but it seems reasonable and it's easy to implement. - -Now we have to model the relationship between the money we spend and the effectiveness of the campaign. Again, let's suppose we have data from another school that suggests: - -\begin{itemize} - -\item If we spend \$500 on posters, materials, and staff time, we can change student behavior in a way that decreases the effective value of \py{beta} by 10\%. - -\item If we spend \$1000, the total decrease in \py{beta} is almost 20\%. - -\item Above \$1000, additional spending has little additional benefit. - -\end{itemize} - -In the notebook for this chapter you will see how I used a logistic curve to fit this data. The result is the following function, which takes spending as a parameter and returns \py{factor}, which is the factor by which \py{beta} is reduced: - -\index{logistic curve} - -\begin{python} -def compute_factor(spending): - return logistic(spending, M=500, K=0.2, B=0.01) -\end{python} - -I use \py{compute_factor} to write \py{add_hand_washing}, which takes a \py{System} object and a budget, and modifies \py{system.beta} to model the effect of hand washing: - -\begin{python} -def add_hand_washing(system, spending): - factor = compute_factor(spending) - system.beta *= (1 - factor) -\end{python} - -Now we can sweep a range of values for \py{spending} and use the simulation to compute the effect: - -\begin{python} -def sweep_hand_washing(spending_array): - sweep = SweepSeries() - - for spending in spending_array: - sir = make_system(beta, gamma) - add_hand_washing(sir, spending) - results, run_simulation(sir, update_func) - sweep[spending] = calc_total_infected(results, sir) - - return sweep -\end{python} - -Here's how we run it: - -\begin{python} -spending_array = linspace(0, 1200, 20) -infected_sweep = sweep_hand_washing(spending_array) -\end{python} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap05-fig05.pdf}} -\caption{Fraction of the population infected as a function of hand-washing campaign spending.} -\label{chap05-fig05} -\end{figure} - -Figure~\ref{chap05-fig05} shows the result. Below \$200, the campaign has little effect. At \$800 it has a substantial effect, reducing total infections from 46\% to 20\%. Above \$800, the additional benefit is small. - - -\section{Optimization} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap05-fig06.pdf}} -\caption{Fraction of the population infected as a function of the number of doses.} -\label{chap05-fig06} -\end{figure} - -Let's put it all together. With a fixed budget of \$1200, we have to decide how many doses of vaccine to buy and how much to spend on the hand-washing campaign. - -\index{optimization} - -Here are the parameters: - -\begin{python} -num_students = 90 -budget = 1200 -price_per_dose = 100 -max_doses = int(budget / price_per_dose) -\end{python} - -The fraction \py{budget/price_per_dose} might not be an integer. \py{int} is a built-in function that converts numbers to integers, rounding down. - -We'll sweep the range of possible doses: - -\begin{python} -dose_array = linrange(max_doses, endpoint=True) -\end{python} - -In this example we call \py{linrange} with only one argument; it returns a NumPy array with the integers from 0 to \py{max_doses}. With the argument \py{endpoint=True}, the result includes both endpoints. - -\index{linrange} -\index{NumPy} - \index{array} - -Then we run the simulation for each element of \py{dose_array}: - -\begin{python} -def sweep_doses(dose_array): - sweep = SweepSeries() - - for doses in dose_array: - fraction = doses / num_students - spending = budget - doses * price_per_dose - - sir = make_system(beta, gamma) - add_immunization(sir, fraction) - add_hand_washing(sir, spending) - - run_simulation(sir, update_func) - sweep[doses] = calc_total_infected(sir) - - return sweep -\end{python} - -For each number of doses, we compute the fraction of students we can immunize, \py{fraction} and the remaining budget we can spend on the campaign, \py{spending}. Then we run the simulation with those quantities and store the number of infections. - -Figure~\ref{chap05-fig06} shows the result. If we buy no doses of vaccine and spend the entire budget on the campaign, the fraction infected is around 19\%. At 4 doses, we have \$800 left for the campaign, and this is the optimal point that minimizes the number of students who get sick. - -As we increase the number of doses, we have to cut campaign spending, which turns out to make things worse. But interestingly, when we get above 10 doses, the effect of herd immunity starts to kick in, and the number of sick students goes down again. - -\index{herd immunity} - -Before you go on, you might want to read the notebook for this chapter, \py{chap12.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - - -\chapter{Sweeping two parameters} -\label{chap13} - -In the previous chapters I presented an SIR model of infectious disease, specifically the Kermack-McKendrick model. We extended the model to include vaccination and the effect of a hand-washing campaign, and used the extended model to allocate a limited budget optimally, that is, to minimize the number of infections. - -\index{Kermack-McKendrick model} -\index{SIR model} - -But we assumed that the parameters of the model, contact rate and recovery rate, were known. In this chapter, we explore the behavior of the model as we vary these parameters, use analysis to understand these relationships better, and propose a method for using data to estimate parameters. - - -\section{Unpack} -\label{unpack} - -Before we analyze the SIR model, I want to make a few improvements to the code. In the previous chapter, we used this version of \py{run_simulation}: -\index{\py{run_simulation}} - -\begin{python} -def run_simulation(system, update_func): - frame = DataFrame(columns=system.init.index) - frame.row[system.t0] = system.init - - for t in linrange(system.t0, system.t_end): - frame.row[t+1] = update_func(frame.row[t], t, system) - - system.results = frame -\end{python} - -Because we read so many variables from \py{system}, this code is a bit cluttered. We can clean it up using \py{unpack}, which is defined in the \py{modsim} library. \py{unpack} takes a \py{System} object as a parameter and makes the system variables available without using the dot operator. So we can rewrite \py{run_simulation} like this: - -\index{unpack} -\index{System object} - -\begin{python} -def run_simulation(system, update_func): - unpack(system) - - frame = TimeFrame(columns=init.index) - frame.row[t0] = init - - for t in linrange(t0, t_end): - frame.row[t+1] = update_func(frame.row[t], t, system) - - system.results = frame -\end{python} - -The variables you unpack should be treated as read-only. Modifying them is not an error, but it might not have the behavior you expect. In the notebook for this chapter, you can use \py{unpack} to clean up \py{update1}. - - -\section{Sweeping beta} - -Recall that $\beta$ is the contact rate, which captures both the frequency of interaction between people and the fraction of those interactions that result in a new infection. If $N$ is the size of the population and $s$ is the fraction that's susceptible, $s N$ is the number of susceptibles, $\beta s N$ is the number of contacts per day between susceptibles and other people, and $\beta s i N$ is the number of those contacts where the other person is infectious. -\index{parameter sweep} - -As $\beta$ increases, we expect the total number of infections to increase. To quantify that relationship, I'll create a range of values for $\beta$: - -\begin{python} -beta_array = linspace(0.1, 1.1, 11) -\end{python} - -Then run the simulation for each value and print the results. - -\begin{python} -for beta in beta_array: - sir = make_system(beta, gamma) - run_simulation(sir, update1) - print(sir.beta, calc_total_infected(sir)) -\end{python} - -We can wrap that code in a function and store the results in a \py{SweepSeries} object: -\index{SweepSeries object} - -\begin{python} -def sweep_beta(beta_array, gamma): - sweep = SweepSeries() - for beta in beta_array: - system = make_system(beta, gamma) - run_simulation(system, update1) - sweep[system.beta] = calc_total_infected(system) - return sweep -\end{python} - -Now we can run \py{sweep_beta} like this: - -\begin{python} -infected_sweep = sweep_beta(beta_array, gamma) -\end{python} - -And plot the results: - -\begin{python} -label = 'gamma = ' + str(gamma) -plot(infected_sweep, label=label) -\end{python} - -%TODO: figure out when to introduce strings - -The first line uses string operations to assemble a label for the plotted line: -\index{string} - -\begin{itemize} - -\item When the \py{+} operator is applied to strings, it joins them end-to-end, which is called {\bf concatenation}. - -\index{concatenation} - -\item The function \py{str} converts any type of object to a String representation. In this case, \py{gamma} is a number, so we have to convert it to a string before trying to concatenate it. - -\index{str function} - -\end{itemize} - -If the value of \py{gamma} is \py{0.25}, the value of \py{label} is the string \py{'gamma = 0.25'}. - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap06-fig01.pdf}} -\caption{Total number of infected students as a function of the parameter \py{beta}, with \py{gamma = 0.25}.} -\label{chap06-fig01} -\end{figure} - -Figure~\ref{chap06-fig01} shows the results. Remember that this figure is a parameter sweep, not a time series, so the x-axis is the parameter \py{beta}, not time. - -When \py{beta} is small, the contact rate is low and the outbreak never really takes off; the total number of infected students is near zero. As \py{beta} increases, it reaches a threshold near 0.3 where the fraction of infected students increases quickly. When \py{beta} exceeds 0.5, more than 80\% of the population gets sick. - - -\section{Sweeping gamma} - -Now let's see what that looks like for a few different values of \py{gamma}. Again, we'll use \py{linspace} to make an array of values: - -\index{linspace} - -\begin{python} -gamma_array = linspace(0.1, 0.7, 4) -\end{python} - -And run \py{sweep_beta} for each value of \py{gamma}: - -\begin{python} -for gamma in gamma_array: - infected_sweep = sweep_beta(beta_array, gamma) - label = 'gamma = ' + str(gamma) - plot(infected_sweep, label=label) -\end{python} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap06-fig02.pdf}} -\caption{Total number of infected students as a function of the parameter \py{beta}, for several values of \py{gamma}.} -\label{chap06-fig02} -\end{figure} - -Figure~\ref{chap06-fig02} shows the results. When \py{gamma} is low, the recovery rate is low, which means people are infectious longer. In that case, even a low contact rate (\py{beta}) results in an epidemic. - -When \py{gamma} is high, \py{beta} has to be even higher to get things going. That observation suggests that there might be a relationship between \py{gamma} and \py{beta} that determines the outcome of the model. In fact, there is. In the next two chapters I demonstrate it by running simulations, then derive it by analysis. - -Before you go on, you might want to read the notebook for this chapter, \py{chap13.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - -\chapter{Analysis} -\label{chap14} - -In the previous chapters we used simulation to predict the effect of an infectious disease in a susceptible population and to design interventions that would minimize the effect. - -In this chapter we use analysis to investigate the relationship between the parameters, \py{beta} and \py{gamma}, and the outcome of the simulation. - - -\section{Nondimensionalization} -\label{nondim} - -Before we go on, let's wrap the code from the previous chapter in a function: - -\begin{python} -def sweep_parameters(beta_array, gamma_array): - frame = SweepFrame(columns=gamma_array) - for gamma in gamma_array: - frame[gamma] = sweep_beta(beta_array, gamma) - return frame -\end{python} - -\py{sweep_parameters} takes as parameters two arrays: a range of values for \py{beta} and a range of values for \py{gamma}. - -It creates a \py{SweepFrame} to store the results, with one column for each value of \py{gamma} and one row for each value of \py{beta}. A \py{SweepFrame} is a kind of \py{DataFrame}, defined in the \py{modsim} library. Its purpose is to store results from a two-dimensional parameter sweep. -\index{SweepFrame object} -\index{DataFrame object} - -Each time through the loop, we run \py{sweep_beta}. The result is a \py{SweepSeries} object with one element for each value of \py{gamma}. The assignment - -\begin{python} -frame[gamma] = sweep_beta(beta_array, gamma) -\end{python} - -stores the values from the \py{SweepSeries} object as a new column in the \py{SweepFrame}, corresponding to the current value of \py{gamma}. - -At the end, the \py{SweepFrame} stores the fraction of students infected for each pair of parameters, \py{beta} and \py{gamma}. - -We can run \py{sweep_parameters} like this: - -\begin{python} -frame = sweep_parameters(beta_array, gamma_array) -\end{python} - -Then we can loop through the results like this: - -\begin{python} -for gamma in frame.columns: - series = frame[gamma] - for beta in series.index: - frac_infected = series[beta] - print(beta, gamma, frac_infected) -\end{python} - -This is the first example we've seen with one \py{for} loop inside another: - -\begin{itemize} - -\item Each time the outer loop runs, it selects a value of \py{gamma} from the columns of the \py{DataFrame} and extracts the corresponding column as a \py{Series}. -\index{Series} - -\item Each time the inner loop runs, it selects a value of \py{beta} from the \py{Series} and selects the corresponding element, which is the fraction of student infected. - -\end{itemize} - -In this example, \py{frame} has 4 columns, one for each value of \py{gamma}, and 11 rows, one for each value of \py{beta}. So these loops print 44 lines, one for each pair of parameters. - -Now let's think about possible relationships between \py{beta} and \py{gamma}: - -\begin{itemize} - -\item When \py{beta} exceeds \py{gamma}, that means there are more contacts (that is, potential infections) than recoveries. The difference between \py{beta} and \py{gamma} might be called the ``excess contact rate", in units of contacts per day. - -\item As an alternative, we might consider the ratio \py{beta/gamma}, which is the number of contacts per recovery. Because the numerator and denominator are in the same units, this ratio is {\bf dimensionless}, which means it has no units. -\index{dimensionless} - -\end{itemize} - -Describing physical systems using dimensionless parameters is often a useful move in the modeling and simulation game. It is so useful, in fact, that it has a name: {\bf nondimensionalization} (see \url{http://modsimpy.com/nondim}). - -\index{nondimensionalization} - -So we'll try the second option first. In the notebook for this chapter, you can explore the first option as an exercise. - -The following function wraps the previous loops and plots the fraction infected as a function of the ratio \py{beta/gamma}: - -\begin{python} -def plot_sweep_frame(frame): - for gamma in frame.columns: - series = frame[gamma] - for beta in series.index: - frac_infected = series[beta] - plot(beta/gamma, frac_infected, 'ro') -\end{python} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap06-fig03.pdf}} -\caption{Total fraction infected as a function of contact number.} -\label{chap06-fig03} -\end{figure} - -Figure~\ref{chap06-fig03} shows that the results fall neatly on a single curve, at least approximately. That means that we can predict the fraction of students who will be infected based on a single parameter, the ratio \py{beta/gamma}. We don't need to know the values of \py{beta} and \py{gamma} separately. - - -\section{Contact number} -\label{contact} - -Recall that the number of new infections in a given day is $\beta s i N$, and the number of recoveries is $\gamma i N$. If we divide these quantities, the result is $\beta s / \gamma$, which is the number of new infections per recovery (as a fraction of the population). - -\index{contact number} -\index{basic reproduction number} - -When a new disease is introduced to a susceptible population, $s$ is approximately 1, so the number of people infected by each sick person is $\beta / \gamma$. This ratio is called the ``contact number" or ``basic reproduction number" (see \url{http://modsimpy.com/contact}). By convention it is usually denoted $R_0$, but in the context of an SIR model, this notation is confusing, so we'll use $c$ instead. - -The results in the previous section suggest that there is a relationship between $c$ and the total number of infections. We can derive this relationship by analyzing the differential equations from Section~\ref{sireqn}: -% -\begin{align*} -\frac{ds}{dt} &= -\beta s i \\ -\frac{di}{dt} &= \beta s i - \gamma i\\ -\frac{dr}{dt} &= \gamma i -\end{align*} -% -In the same way we divided the contact rate by the infection rate to get the dimensionless quantity $c$, now we'll divide $di/dt$ by $ds/dt$ to get a ratio of rates: -% -\[ \frac{di}{ds} = -1 + \frac{1}{cs} \] -% -Dividing one differential equation by another is not an obvious move, but in this case it is useful because it gives us a relationship between $i$, $s$ and $c$ that does not depend on time. From that relationship, we can derive an equation that relates $c$ to the final value of $S$. In theory, this equation makes it possible to infer $c$ by observing the course of an epidemic. - -Here's how the derivation goes. We multiply both sides of the previous equation by $ds$: -% -\[ di = \left( -1 + \frac{1}{cs} \right) ds \] -% -And then integrate both sides: -% -\[ i = -s + \frac{1}{c} \log s + q \] -% -where $q$ is a constant of integration. Rearranging terms yields: -% -\[ q = i + s - \frac{1}{c} \log s \] -% -Now let's see if we can figure out what $q$ is. At the beginning of an epidemic, if the fraction infected is small and nearly everyone is susceptible, we can use the approximations $i(0) = 0$ and $s(0) = 1$ to compute $q$: -% -\[ q = 0 + 1 + \frac{1}{c} \log 1 \] -% -Since $\log 1 = 0$, we get $q = 1$. -\index{integration} -\index{constant of integration} - -\newcommand{\sinf}{s_{\infty}} - -Now, at the end of the epidemic, let's assume that $i(\infty) = 0$, and $s(\infty)$ is an unknown quantity, $\sinf$. Now we have: -% -\[ q = 1 = 0 + \sinf - \frac{1}{c} \log \sinf \] -% -Solving for $c$, we get -% -\[ c = \frac{\log \sinf}{\sinf - 1} \] -% -By relating $c$ and $\sinf$, this equation makes it possible to estimate $c$ based on data, and possibly predict the behavior of future epidemics. - -\section{Analysis and simulation} - -Let's compare this analytic result to the results from simulation. -I'll create an array of values for $\sinf$ -\index{linspace} - -\begin{python} -s_inf_array = linspace(0.0001, 0.9999, 31) -\end{python} - -And compute the corresponding values of $c$: - -\begin{python} -c_array = log(s_inf_array) / (s_inf_array - 1) -\end{python} - -To get the total infected, we compute the difference between $s(0)$ and $s(\infty)$, then store the results in a \py{Series}: -\index{array} -\index{series} - -\begin{python} -frac_infected = 1 - s_inf_array -frac_infected_series = Series(frac_infected, index=c_array) -\end{python} - -Recall from Section~\ref{dataframe} that a \py{Series} object contains an index and a corresponding sequence of values. In this case, the index is \py{c_array} and the values are from \py{frac_infected}. - -Now we can plot the results: - -\begin{python} -plot(frac_infected_series) -\end{python} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap06-fig04.pdf}} -\caption{Total fraction infected as a function of contact number, showing results from simulation and analysis.} -\label{chap06-fig04} -\end{figure} - -Figure~\ref{chap06-fig04} compares the analytic results from this section with the simulation results from Section~\ref{nondim}. Over most of the range they are consistent with each other, with one discrepancy: when the contact number is less than 1, analysis indicates there should be no infections; but in the simulations a small part of the population is affected even when $c<1$. -\index{analysis} - -The reason for the discrepancy is that the simulation divides time into a discrete series of days, whereas the analysis treats time as a continuous quantity. In other words, the two methods are actually based on different models. So which model is better? - -Probably neither. When the contact number is small, the early progress of the epidemic depends on details of the scenario. If we are lucky, the original infected person, ``patient zero", infects no one and there is no epidemic. If we are unlucky, patient zero might have a large number of close friends, or might work in the dining hall (and fail to observe safe food handling procedures). -\index{patient zero} - -For contact numbers near or less than 1, we might need a more detailed model. But for higher contact numbers the SIR model might be good enough. - -Figure~\ref{chap06-fig04} shows that if we know the contact number, we can compute the fraction infected. But we can also read the figure the other way; that is, at the end of an epidemic, if we can estimate the fraction of the population that was ever infected, we can use it to estimate the contact number. - -Well, at least in theory. In practice, it might not work very well, because of the shape of the curve. When the contact number is near 2, the curve is quite steep, which means that small changes in $c$ yield big changes in the number of infections. If we observe that the total fraction infected is anywhere from 20\% to 80\%, we would conclude that $c$ is near 2. - -On the other hand, for larger contact numbers, nearly the entire population is infected, so the curve is quite flat. In that case we would not be able to estimate $c$ precisely, because any value greater than 3 would yield effectively the same results. Fortunately, this is unlikely to happen in the real world; very few epidemics affect anything like 90\% of the population. - -So the SIR model has limitations; nevertheless, it provides insight into the behavior of infectious disease, especially the phenomenon of herd resistance. As we saw in the previous chapter, if we know the parameters of the model, we can use it to evaluate possible interventions. And as we saw in this chapter, we might be able to use data from earlier outbreaks to estimate the parameters. - -Before you go on, you might want to read the notebook for this chapter, \py{chap14.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - - -%\part{Modeling thermal systems} - - - -\chapter{Heat} -\label{chap15} - -So far the systems we have studied have been physical in the sense that they exist in the world, but they have not been physics, in the sense of what physics classes are usually about. In the next few chapters, we'll do some physics, starting with {\bf thermal systems}, that is, systems where the temperature of objects changes as heat transfers from one to another. - -\index{thermal system} - -\section{The coffee cooling problem} - -The coffee cooling problem was discussed by Jearl Walker in {\it Scientific American} in 1977\footnote{Walker, ``The Amateur Scientist", {\it Scientific American}, Volume 237, Issue 5, November 1977.}; since then it has become a standard example of modeling and simulation. - -\index{coffee cooling problem} -\index{Walker, Jearl} - -Here is my version of the problem: - -\begin{quote} -Suppose I stop on the way to work to pick up a cup of coffee, which I take with milk. Assuming that I want the coffee to be as hot as possible when I arrive at work, should I add the milk at the coffee shop, wait until I get to work, or add the milk at some point in between? -\end{quote} - -To help answer this question, I made a trial run with the milk and coffee in separate containers and took some measurements\footnote{This is fiction. I usually drink tea and bike to work.}: - -\begin{itemize} - -\item When served, the temperature of the coffee is \SI{90}{\celsius}. The volume is \SI{300}{mL}. - -\item The milk is at an initial temperature of \SI{5}{\celsius}, and I take about \SI{50}{mL}. - -\item The ambient temperature in my car is \SI{22}{\celsius}. - -\item The coffee is served in a well insulated cup. When I arrive at work after 30 minutes, the temperature of the coffee has fallen to \SI{70}{\celsius}. - -\item The milk container is not as well insulated. After 15 minutes, it warms up to \SI{20}{\celsius}, nearly the ambient temperature. - -\end{itemize} - -To use this data and answer the question, we have to know something about temperature and heat, and we have to make some modeling decisions. - - -\section{Temperature and heat} - -To understand how coffee cools (and milk warms), we need a model of temperature and heat. {\bf Temperature} is a property of an object or a system; in SI units it is measured in degrees Celsius (\si{\celsius}). Temperature quantifies how hot or cold the object is, which is related to the average velocity of the particles that make up the object. - -\index{temperature} - -When particles in a hot object contact particles in a cold object, the hot object gets cooler and the cold object gets warmer as energy is transferred from one to the other. The transferred energy is called {\bf heat}; in SI units it is measured in joules (\si{\joule}). - -\index{heat} - -Heat is related to temperature by the following equation (see \url{http://modsimpy.com/thermass}): -% -\[ Q = C \Delta T \] -% -where $Q$ is the amount of heat transferred to an object, $\Delta T$ is resulting change in temperature, and $C$ is the {\bf thermal mass} of the object, which quantifies how much energy it takes to heat or cool it. In SI units, thermal mass is measured in joules per degree Celsius (\si{\joule\per\celsius}). - -\index{thermal mass} - -For objects made primarily from one material, thermal mass can be computed like this: -% -\[ C = m c_p \] -% -where $m$ is the mass of the object and $c_p$ is the {\bf specific heat capacity} of the material (see \url{http://modsimpy.com/specheat}). - -\index{specific heat capacity} - -We can use these equations to estimate the thermal mass of a cup of coffee. The specific heat capacity of coffee is probably close to that of water, which is \SI{4.2}{\joule\per\gram\per\celsius}. Assuming that the density of coffee is close to that of water, which is \SI{1}{\gram\per\milli\liter}, the mass of \SI{300}{\milli\liter} of coffee is \SI{300}{\gram}, and the thermal mass is \SI{1260}{\joule\per\celsius}. - -\index{density} - -So when a cup of coffee cools from \SI{90}{\celsius} to \SI{70}{\celsius}, the change in temperature, $\Delta T$ is \SI{20}{\celsius}, which means that \SI{25200}{\joule} of heat energy was transferred from the coffee to the surrounding environment (the cup holder and air in my car). - -To give you a sense of how much energy that is, if you were able to harness all of that heat to do work (which you cannot\footnote{See \url{http://modsimpy.com/thermo}.}), you could use it to lift a cup of coffee from sea level to \SI{8571}{\meter}, just shy of the height of Mount Everest, \SI{8848}{\meter}. - -\index{Mount Everest} - -Assuming that the cup has less mass than the coffee, and is made from a material with lower specific heat, we can ignore the thermal mass of the cup. -For a cup with substantial thermal mass, we might consider a model that computes the temperature of coffee and cup separately. - - -\section{Heat transfer} - -In a situation like the coffee cooling problem, there are three ways heat transfers from one object to another (see \url{http://modsimpy.com/transfer}): - -\index{heat transfer} -\index{conduction} -\index{convection} -\index{radiation} - -\begin{itemize} - -\item Conduction: When objects at different temperatures come into contact, the faster-moving particles of the higher-temperature object transfer kinetic energy to the slower-moving particles of the lower-temperature object. - -\item Convection: When particles in a gas or liquid flow from place to place, they carry heat energy with them. Fluid flows can be caused by external action, like stirring, or by internal differences in temperature. For example, you might have heard that hot air rises, which is a form of ``natural convection". - -\index{fluid flow} - -\item Radiation: As the particles in an object move due to thermal energy, they emit electromagnetic radiation. The energy carried by this radiation depends on the object's temperature and surface properties (see \url{http://modsimpy.com/thermrad}). - -\end{itemize} - -For objects like coffee in a car, the effect of radiation is much smaller than -the effects of conduction and convection, so we will ignore it. - -Convection can be a complex topic, since it often depends on details of fluid flow in three dimensions. But for this problem we will be able to get away with a simple model called ``Newton's law of cooling". - -\index{Newton's law of cooling} - -\section{Newton's law of cooling} - -Newton's law of cooling asserts that the temperature rate of change for an object is proportional to the difference in temperature between the object and the surrounding environment: -% -\[ \frac{dT}{dt} = -r (T - T_{env}) \] -% -where $T$, the temperature of the object, is a function of time, $t$; $T_{env}$ is the temperature of the environment, and $r$ is a constant that characterizes how quickly heat is transferred between the system and the environment. - -Newton's so-called ``law" is really a model in the sense that it is approximately true in some conditions, only roughly true in others, and not at all true in others. - -For example, if the primary mechanism of heat transfer is conduction, Newton's law is ``true", which is to say that $r$ is constant over a wide range of temperatures. And sometimes we can estimate $r$ based on the material properties and shape of the object. - -When convection contributes a non-negligible fraction of heat transfer, $r$ depends on temperature, but Newton's law is often accurate enough, at least over a narrow range of temperatures. In this case $r$ usually has to be estimated experimentally, since it depends on details of surface shape, air flow, evaporation, etc. - -When radiation makes up a substantial part of heat transfer, Newton's law is not a good model at all. This is the case for objects in space or in a vacuum, and for objects at high temperatures (more than a few hundred degrees Celsius, say). - -\index{radiation} - -However, for a situation like the coffee cooling problem, we expect Newton's model to be quite good. - - -\section{Implementation} -\label{coffee_impl} - -To get started, let's forget about the milk temporarily and focus on the coffee. I'll create a \py{State} object to represent the initial temperature: - -\begin{python} -init = State(T=90) -\end{python} - -And a \py{System} object to contain the parameters of the system: - -\index{State object} -\index{System object} - -\begin{python} -coffee = System(init=init, - volume=300, - r=0.01, - T_env=22, - t_0=0, - t_end=30, - dt=1) -\end{python} - -The values of \py{volume}, \py{T_env}, and \py{t_end} come from the statement of the problem. I chose the value of \py{r} arbitrarily for now; we will figure out how to estimate it soon. - -\index{time step} - -\py{dt} is the time step we use to simulate the cooling process. -Strictly speaking, Newton's law is a differential equation, but over a short period of time we can approximate it with a difference equation: -% -\[ \Delta T = -r (T - T_{env}) dt \] -% -where $dt$ is a small time step and $\Delta T$ is the change in temperature during that time step. - -Note: I use $\Delta T$ to denote a change in temperature over time, but in the context of heat transfer, you might also see $\Delta T$ used to denote the difference in temperature between an object and its environment, $T - T_{env}$. To minimize confusion, I avoid this second use. - -Now we can write an update function: - -\index{unpack} - -\begin{python} -def update_func(state, t, system): - unpack(system) - - T = state.T - T += -r * (T - T_env) * dt - - return State(T=T) -\end{python} - -Like previous update functions, this one takes a \py{State} object, a time, and a \py{System} object. - -Now if we run - -\begin{python} -update_func(init, 0, coffee) -\end{python} - -we see that the temperature after one minute is \SI{89.3}{\celsius}, so the temperature drops by about \SI{0.7}{\celsius\per\minute}, at least for this value of \py{r}. - -Here's a version of \py{run_simulation} that simulates a series of time steps from \py{t_0} to \py{t_end}: - -\index{time step} -\index{\py{run_simulation}} -\index{unpack} - -\begin{python} -def run_simulation(system, update_func): - unpack(system) - - frame = TimeFrame(columns=init.index) - frame.row[t_0] = init - ts = linrange(t_0, t_end, dt) - - for t in ts: - frame.row[t+dt] = update_func(frame.row[t], t, system) - - # store the final temperature in T_final - system.T_final = get_last_value(frame.T) - - return frame -\end{python} - -This function is similar to previous versions of \py{run_simulation}. - -One difference is that it uses \py{linrange} to make an array of values from \py{t_0} to \py{t_end} with time step \py{dt}. The result does not include \py{t_end}, so the last value in the array is \py{t_end-dt}. - -\index{linrange} -\index{NumPy} -\index{array} - -Also, it stores the final temperature as a system variable, \py{T_final}. - -We can run it like this: - -\begin{python} -results = run_simulation(coffee, update_func) -\end{python} - -The result is a \py{TimeFrame} object with one row per time step and just one column, \py{T}. The temperature after 30 minutes is \SI{72.3}{\celsius}, which is a little higher than stated in the problem, \SI{70}{\celsius}. We can adjust \py{r} and find the right value by trial and error, but we'll see a better way in the next chapter. - -\index{time step} -\index{TimeFrame object} - -First I want to wrap what we have so far in a function: - -\begin{python} -def make_system(T_init=90, r=0.01, volume=300, t_end=30): - init = State(T=T_init) - - return System(init=init, - T_final=T_init, - volume=volume, - r=r, - T_env=22, - t_0=0, - t_end=t_end, - dt=1) -\end{python} - -\py{make_system} takes the system parameters and packs them into a \py{System} object. Now we can simulate the system like this: - -\index{\py{make_system}} - -\begin{python} -coffee = make_system(T_init=90, r=0.01, - volume=300, t_end=30) -results = run_simulation(coffee, update_func) -\end{python} - -Before you go on, you might want to read the notebook for this chapter, \py{chap15.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - -\chapter{Mixing} -\label{chap16} - -In the previous chapter we wrote a simulation of a cooling cup of coffee. Given the initial temperature of the coffee, the temperature of the atmosphere, and the rate parameter, \py{r}, we can predict how the temperature of the coffee will change over time. - -In general, we don't know the value of \py{r}, but we can use measurements to estimate it. Given an initial temperature, a final temperature, and the time in between, we can find \py{r} by trial and error. - -In this chapter, we'll see a better way to find \py{r}, using a SciPy function called \py{fsolve}. - -And the we'll get back to solving the coffee cooling problem. - -\section{Using fsolve} -\label{fsolve} - -SciPy provides a method called \py{fsolve} that finds the roots of non-linear equations. As a simple example, suppose you want to find the roots of the polynomial -% -\[ f(x) = (x - 1)(x - 2)(x - 3) \] -% -where {\bf root} means a value of $x$ that makes $f(x)=0$. Because of the way I wrote the polynomial, we can see that if $x=1$, the first factor is 0; if $x=2$, the second factor is 0; and if $x=3$, the third factor is 0, so those are the roots. - -\index{\py{fsolve}} -\index{root} - -But usually it's not that easy. In that case \py{fsolve} can help. First, we have to write a function that evaluates $f$: - -\begin{python} -def func(x): - return (x-1) * (x-2) * (x-3) -\end{python} - -Now we call \py{fsolve} like this: - -\begin{python} -fsolve(func, x0=0) -\end{python} - -The first argument is the function whose roots we want. The second argument, \py{x0}, is an initial guess about where a root might be. Generally, the closer the initial guess is to an actual root, the faster \py{fsolve} runs. In this case, with the initial guess \py{x0=0}, the result is 1. - -Often \py{fsolve} finds the root that's closest to the initial guess. In this example, when \py{x0=1.9}, \py{fsolve} returns 2, and when \py{x0=2.9}, \py{fsolve} returns 3. But this behavior can be unpredictable; with \py{x0=1.5}, \py{fsolve} returns 3. - -So how can we use \py{fsolve} to estimate \py{r}? - -What we want is the value of \py{r} that yields a final temperature of \SI{70}{\celsius}. To work with \py{fsolve}, we need a function that takes \py{r} as a parameter and returns the difference between the final temperature and the goal: - -\begin{python} -def error_func1(r): - system = make_system(r=r) - results = run_simulation(system, update) - return system.T_final - 70 -\end{python} - -I call a function like this an ``error function" because it returns the difference between what we got and what we wanted, that is, the error. When we find the right value of \py{r}, this error will be 0. - -\index{error function} -\index{function!error} - -We can test \py{error_func1} like this, using our initial guess for \py{r}: - -\begin{python} -error_func1(r=0.01) -\end{python} - -The result is an error of \SI{2.3}{\celsius}, because the final temperature with this value of \py{r} is too high. - -Now we can call \py{fsolve} like this: - -\begin{python} -solution = fsolve(error_func1, 0.01) -r_coffee = solution[0] -\end{python} - -The return value from \py{fsolve} is an array with a single element, which is the root \py{fsolve} found. In this example, \py{r_coffee} turns out to be about \py{0.012}, in units of \si{\per\minute}. - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap07-fig01.pdf}} -\caption{Temperature of the coffee and milk over time.} -\label{chap07-fig01} -\end{figure} - -As one of the exercises for this chapter, you will use the same process to estimate \py{r_milk}. - -With the correct values of \py{r_coffee} and \py{r_milk}, the simulation results should look like Figure~\ref{chap07-fig01}, which shows the temperature of the coffee and milk over time. - - -\section{Mixing liquids} - -When we mix two liquids, the temperature of the mixture depends on the temperatures of the ingredients, but it might not be obvious how to compute it. - -\index{mixing} - -Assuming there are no chemical reactions that either produce or consume heat, the total thermal energy of the system is the same before and after mixing; in other words, thermal energy is {\bf conserved}. - -\index{conservation of energy} - -If the temperature of the first liquid is $T_1$, the temperature of the second liquid is $T_2$, and the final temperature of the mixture is $T$, the heat transfer into the first liquid is $C_1 (T - T_1)$ and the heat transfer into the second liquid is $C_2 (T - T_2)$, where $C_1$ and $C_2$ are the thermal masses of the liquids. - -In order to conserve energy, these heat transfers must add up to 0: -% -\[ C_1 (T - T_1) + C_2 (T - T_2) = 0 \] -% -We can solve this equation for T: -% -\[ T = \frac{C_1 T_1 + C_2 T_2}{C_1 + C_2} \] -% -For the coffee cooling problem, we have the volume of each liquid; if we also know the density, $\rho$, and the specific heat capacity, $c_p$, we can compute thermal mass: -% -\[ C = \rho V c_p \] -% -If we assume that the density and specific heat of the milk and coffee are equal, they drop out of the equation, and we can write: -% -\[ T = \frac{V_1 T_1 + V_2 T_2}{V_1 + V_2} \] -% -where $V_1$ and $V_2$ are the volumes of the liquids. As an exercise, you can look up the density and specific heat of milk to see how good this approximation is. - -\index{volume} -\index{density} -\index{specific heat} - -The following function takes two \py{System} objects that represent the coffee and milk, and creates a new \py{System} to represent the mixture: - -\begin{python} -def mix(s1, s2): - assert s1.t_end == s2.t_end - - V_mix = s1.volume + s2.volume - - T_mix = (s1.volume * s1.T_final + - s2.volume * s2.T_final) / V_mix - - mixture = make_system(T_init=T_mix, - t_end=0, - r=s1.r, - volume=V_mix) - - return mixture -\end{python} - -The first line is an \py{assert} statement, which is a way of checking for errors. It compares \py{t_end} for the two systems to confirm that they have been cooling for the same time. If not, \py{assert} displays an error message and stops the program. - -\index{assert statement} -\index{statement!assert} - -The next two statements compute the total volume of the mixture and its temperature. Finally, \py{mix} makes a new \py{System} object and returns it. - -This function uses the value of \py{r} from \py{s1} as the value of \py{r} for the mixture. If \py{s1} represents the coffee, and we are adding the milk to the coffee, this is probably a reasonable choice. On the other hand, when we increase the amount of liquid in the coffee cup, that might change \py{r}. So this is an assumption we might want to revisit. - - -\section{Mix first or last?} - -Now we have everything we need to solve the problem. First I'll create objects to represent the coffee and cream, and run for 30 minutes. - -\begin{python} -coffee = make_system(T_init=90, t_end=30, - r=r_coffee, volume=300) -coffee_results = run_simulation(coffee, update_func) - -milk = make_system(T_init=5, t_end=30, - r=r_milk, volume=50) -milk_results = run_simulation(milk, update_func) -\end{python} - -The final temperatures, before mixing, are \SI{70}{\celsius} and \SI{21.8}{\celsius}. Then I'll mix them: - -\begin{python} -mix_last = mix(coffee, milk) -\end{python} - -After mixing, the temperature is \SI{63.1}{\celsius}, which is still warm enough to be enjoyable. Would we do any better if we added the milk first? - -To find out, I'll create new objects for the coffee and milk: - -\begin{python} -coffee = make_system(T_init=90, r=r_coffee, - volume=300, t_end=30) -milk = make_system(T_init=5, r=r_milk, - volume=50, t_end=30) -\end{python} - -Then mix them and simulate 30 minutes: - -\begin{python} -mix_first = mix(coffee, milk) -results = run_simulation(mix_first, update_func) -\end{python} - -The final temperature is only \SI{61.4}{\celsius}. So it looks like adding the milk at the end is better, by about \SI{1.7}{\celsius}. But is that the best we can do? - - -\section{Optimization} - -Adding the milk after 30 minutes is better than adding immediately, but maybe there's something in between that's even better. To find out, I'll use the following function, which takes \py{t_add} as a parameter: - -\index{optimization} - -\begin{python} -def run_and_mix(t_add, t_total=30): - coffee = make_system(T_init=90, t_end=t_add, - r=r_coffee, volume=300) - coffee_results = run_simulation(coffee, update_func) - - milk = make_system(T_init=5, t_end=t_add, - r=r_milk, volume=50) - milk_results = run_simulation(milk, update_func) - - mixture = mix(coffee, milk) - mixture.t_end = t_total - t_add - results = run_simulation(mixture, update_func) - - return mixture.T_final -\end{python} - -When \py{t_add=0}, we add the milk immediately; when \py{t_add=30}, we add it at the end. Now we can sweep the range of values in between: - -\begin{python} -sweep = SweepSeries() -for t_add in linspace(0, 30, 11): - sweep[t_add] = run_and_mix(t_add, 30) -\end{python} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap07-fig02.pdf}} -\caption{Final temperature as a function of the time the milk is added.} -\label{chap07-fig02} -\end{figure} - -Figure~\ref{chap07-fig02} shows the result. Again, note that this is a parameter sweep, not a time series. The x-axis is the time when we add the milk, not the index of a \py{TimeSeries}. - -The final temperature is maximized when \py{t_add=30}, so adding the milk at the end is optimal. - -In the notebook for this chapter you will have a chance to explore this solution and try some variations. For example, suppose the coffee shop won't let me take milk in a separate container, but I keep a bottle of milk in the refrigerator at my office. In that case is it better to add the milk at the coffee shop, or wait until I get to the office? - - -\section{Analysis} - -Simulating Newton's law of cooling is almost silly, because we can solve the differential equation analytically. If -% -\[ \frac{dT}{dt} = -r (T - T_{env}) \] -% -the general solution is -% -\[ T{\left (t \right )} = C_{1} \exp(-r t) + T_{env} \] -% -and the particular solution where $T(0) = T_{init}$ is -% -\[ T_{env} + \left(- T_{env} + T_{init}\right) \exp(-r t) \] -% -You can see how I got this solution using SymPy in \py{chap16sympy.ipynb} in the repository for this book. If you would like to see it done by hand, you can watch this video: \url{http://modsimpy.com/khan3}. - -\index{analysis} -\index{SymPy} - -Now we can use the observed data to estimate the parameter $r$. If we observe $T(t_{end}) = T_{end}$, we can plug $t_{end}$ and $T_{end}$ into the particular solution and solve for $r$. The result is: -% -\[ r = \frac{1}{t_{end}} \log{\left (\frac{T_{init} - T_{env}}{T_{end} - T_{env}} \right )} \] -% -Plugging in $t_{end}=30$ and $T_{end}=70$ (and again with $T_{init}=90$ and $T_{env}=22$), the estimate for $r$ is 0.0116. - -We can use the following function to compute the time series: - -\index{unpack} - -\begin{python} -def run_analysis(system): - unpack(system) - - T_init = init.T - ts = linrange(t_0, t_end, dt, endpoint=True) - - T_array = T_env + (T_init - T_env) * exp(-r * ts) - - results = TimeFrame(T_array, index=ts, columns=['T']) - system.T_final = get_last_value(results.T) - - return results -\end{python} - -This function is similar to \py{run_simulation}; it takes a \py{System} as a parameter and returns a \py{TimeFrame} as a result. - -Because \py{linrange} returns a NumPy array, \py{T_array} is also a NumPy array. To be consistent with \py{run_simulation}, we have to put it into a \py{TimeFrame}. - -We can run it like this: -\index{\py{run_analysis}} - -\begin{python} -r_coffee2 = 0.0116 -coffee2 = make_system(T_init=90, r=r_coffee2, - volume=300, t_end=30) -results = run_analysis(coffee2) -\end{python} - -The final temperature is \SI{70}{\celsius}, as it should be. In fact, the results are identical to what we got by simulation, with a small difference due to round off. - -Before you go on, you might want to read the notebook for this chapter, \py{chap16.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - -%%\part{Pharmacokinetics} - -\chapter{Pharmacokinetics} -\label{chap17} - -{\bf Pharmacokinetics} is the study of how drugs and other substances move around the body, react, and are eliminated. In this chapter, we implement one of the most widely used pharmacokinetic models: the so-called {\bf minimal model} of glucose and insulin in the blood stream. - -\index{pharmacokinetics} - -We will use this model to fit data collected from a patient, and use the parameters of the fitted model to quantify the patient's ability to produce insulin and process glucose. - -\index{glucose} -\index{insulin} - -My presentation in this chapter follows Bergman (2005) ``Minimal Model" (abstract at \url{http://modsimpy.com/bergman}, -PDF at \url{http://modsimpy.com/minmod}). - - - -\section{The glucose-insulin system} - -{\bf Glucose} is a form of sugar that circulates in the blood of animals; it is used as fuel for muscles, the brain, and other organs. The concentration of blood sugar is controlled by the hormone system, and especially by {\bf insulin}, which is produced by the pancreas and has the effect of reducing blood sugar. - -\index{pancreas} - -In people with normal pancreatic function, the hormone system maintains {\bf homeostasis}; that is, it keeps the concentration of blood sugar in a range that is neither too high or too low. - -But if the pancreas does not produce enough insulin, or if the cells that should respond to insulin become insensitive, blood sugar can become elevated, a condition called {\bf hyperglycemia}. Long term, severe hyperglycemia is the defining symptom of {\bf diabetes mellitus}, a serious disease that affects almost 10\% of the population in the U.S. (see \url{http://modsimpy.com/cdc}). - -\index{hyperglycemia} -\index{diabetes} - -One of the most-used tests for hyperglycemia and diabetes is the frequently sampled intravenous glucose tolerance test (FSIGT), in which glucose is injected into the blood stream of a fasting subject (someone who has not eaten recently); then blood samples are collected at intervals of 2--10 minutes for 3 hours. The samples are analyzed to measure the concentrations of glucose and insulin. - -\index{FSIGT} - -By analyzing these measurements, we can estimate several parameters of the subject's response; the most important is a parameter denoted $S_I$, which quantifies the effect of insulin on the rate of reduction in blood sugar. - - -\section{The glucose minimal model} - -The ``minimal model" was proposed by Bergman, Ider, Bowden, and Cobelli\footnote{Bergman RN, Ider YZ, Bowden CR, Cobelli C., ``Quantitative estimation of insulin sensitivity", Am J Physiol. 1979 Jun;236(6):E667-77. Abstract at \url{http://modsimpy.com/insulin}.}. -It consists of two parts: the glucose model and the insulin model. I will present an implementation of the glucose model; as a case study, you will have the chance to implement the insulin model. - -\index{minimal model} - -The original model was developed in the 1970s; since then, many variations and extensions have been proposed. Bergman's comments on the development of the model provide insight into their process: - -\begin{quote} -We applied the principle of Occam's Razor, i.e.~by asking -what was the simplest model based upon known physiology -that could account for the insulin-glucose relationship -revealed in the data. Such a model must be simple -enough to account totally for the measured glucose (given -the insulin input), yet it must be possible, using mathematical -techniques, to estimate all the characteristic parameters -of the model from a single data set (thus avoiding -unverifiable assumptions). -\end{quote} - -The most useful models are the ones that achieve this balance: including enough realism to capture the essential features of the system without too much complexity to be practical. In this case the practical limit is the ability to estimate the parameters of the model using data, and to interpret the parameters meaningfully. - -\index{Occam's Razor} - -Bergman discusses the features he and his colleagues thought were essential: - -\begin{quote} -(1) Glucose, once elevated by injection, returns to basal level due to -two effects: the effect of glucose itself to normalize its own -concentration [...] as well as the catalytic effect of insulin to allow -glucose to self-normalize (2) Also, we discovered -that the effect of insulin on net glucose disappearance -must be sluggish --- that is, that insulin acts slowly because -insulin must first move from plasma to a remote compartment [...] to exert its action on glucose disposal. -\end{quote} - -To paraphrase the second point, the effect of insulin on glucose disposal, as seen in the data, happens more slowly than we would expect if it depended primarily on the the concentration of insulin in the blood. Bergman's group hypothesized that insulin must move, relatively slowly, from the blood to a ``remote compartment" where it has its effect. - -\index{compartment model} - -At the time, the remote compartment was a modeling abstraction that might, or might not, reflect something physical. Later, according to Bergman, it was ``shown to be interstitial fluid", that is, the fluid that surrounds tissue cells. In the history of mathematical modeling, it is common for hypothetical entities, added to models to achieve particular effects, to be found later to correspond to physical entities. - -\index{interstitial fluid} - -The glucose model consists of two differential equations: -% -\[ \frac{dG}{dt} = -k_1 \left[ G(t) - G_b \right] - X(t) G(t) \] -% -\[ \frac{dX}{dt} = k_3 \left[I(t) - I_b \right] - k_2 X(t) \] -% -where - -\begin{itemize} - -\item $G$ is the concentration of blood glucose as a function of time and $dG/dt$ is its rate of change. - -\item $I$ is the concentration of insulin in the blood as a function of time, which is taken as an input into the model, based on measurements. - -\item $G_b$ is the basal concentration of blood glucose and $I_b$ is the basal concentration of blood insulin, that is, the concentrations at equilibrium. Both are constants estimated from measurements at the beginning or end of the test. - -\item $X$ is the concentration of insulin in the tissue fluid as a function of time, and $dX/dt$ is its rate of change. - -\item $k_1$, $k_2$, and $k_3$ are positive-valued parameters that control the rates of appearance and disappearance for glucose and insulin. - -\end{itemize} - -We can interpret the terms in the equations one by one: - -\begin{itemize} - -\item $-k_1 \left[ G(t) - G_b \right]$ is the rate of glucose disappearance due to the effect of glucose itself. When $G(t)$ is above basal level, $G_b$, this term is negative; when $G(t)$ is below basal level this term is positive. So in the absence of insulin, this term tends to restore blood glucose to basal level. - -\item $-X(t) G(t)$ models the interaction of glucose and insulin in tissue fluid, so the rate increases as either $X$ or $G$ increases. This term does not require a rate parameter because the units of $X$ are unspecified; we can consider $X$ to be in whatever units would make the parameter of this term 1. - -\item $k_3 \left[ I(t) - I_b \right]$ is the rate at which insulin diffuses between blood and tissue fluid. When $I(t)$ is above basal level, insulin diffuses from the blood into the tissue fluid. When $I(t)$ is below basal level, insulin diffuses from tissue to the blood. - -\item $-k_2 X(t)$ is the rate of insulin disappearance in tissue fluid as it is consumed or broken down. - -\end{itemize} - -The initial state of the model is $X(0) = I_b$ and $G(0) = G_0$, where $G_0$ is a constant that represents the concentration of blood sugar immediately after the injection. In theory we could estimate $G_0$ based on measurements, but in practice it takes time for the injected glucose to spread through the blood volume. Since $G_0$ is not measurable, it is treated as a {\bf free parameter} of the model, which means that we are free to choose it to fit the data. - -\index{free parameter} - - -\section{Data} - -To develop and test the model, I use data from Pacini and Bergman\footnote{``MINMOD: A computer program to calculate insulin sensitivity and pancreatic responsivity from the frequently sampled intravenous glucose tolerance test", {\em Computer Methods and Programs in Biomedicine} 23: 113-122, 1986.}. The dataset is in a file in the repository for this book, which we can read into a \py{DataFrame}: - -\index{data} -\index{DataFrame object} - -\begin{python} -data = pd.read_csv('data/glucose_insulin.csv', - index_col='time') -\end{python} - -\py{data} has two columns: \py{glucose} is the concentration of blood glucose in \si{\milli\gram/\deci\liter}; \py{insulin} is concentration of insulin in the blood in \si{\micro U\per\milli\liter} (a medical ``unit", denoted \si{U}, is an amount defined by convention in context). The index is time in \si{\minute}. - -\index{concentration} - -\begin{figure} -\centerline{\includegraphics[width=3.5in]{figs/chap08-fig01.pdf}} -\caption{Glucose and insulin concentrations measured by FSIGT.} -\label{chap08-fig01} -\end{figure} - -Figure~\ref{chap08-fig01} shows glucose and insulin concentrations over \SI{182}{\minute} for a subject with normal insulin production and sensitivity. - - -\section{Interpolation} -\label{interpolate} - -Before we are ready to implement the model, there's one problem we have to solve. In the differential equations, $I$ is a function that can be evaluated at any time, $t$. But in the \py{DataFrame}, we only have measurements at discrete times. This is a job for interpolation! - -\index{interpolation} - -The \py{modsim} library provides a function named \py{interpolate}, which is a wrapper for the SciPy function \py{interp1d}. It takes any kind of \py{Series} as a parameter, including \py{TimeSeries} and \py{SweepSeries}, and returns a function. That's right, I said it returns a {\em function}. - -\index{function!as return value} -\index{Series} -\index{interpolate} -\index{interp1d} -\index{SciPy} - -So we can call \py{interpolate} like this: - -\begin{python} -I = interpolate(data.insulin) -\end{python} - -Then we can call the new function, \py{I}, like this: - -\begin{python} -I(18) -\end{python} - -The result is 31.66, which is a linear interpolation between the actual measurements at \py{t=16} and \py{t=19}. We can also pass an array as an argument to \py{I}: - -\begin{python} -ts = linrange(t_0, t_end, endpoint=True) -I(ts) -\end{python} - -The result is an array of interpolated values for equally-spaced values of \py{t}. - -\index{linrange} -\index{NumPy} - -\py{interpolate} can take additional arguments, which it passes along to \py{interp1d}. You can read about these options at \url{http://modsimpy.com/interp}. - - -\section{Implementation} -\label{glucose} - -To get started, we'll assume that the parameters of the model are known. We'll implement the model and use it to generate time series for \py{G} and \py{X}. Then we'll see how to find the parameters that generate the series that best fits the data. - -Taking advantage of estimates from prior work, we'll start with these values: - -\begin{python} -params = Params(G0 = 290, - k1 = 0.03, - k2 = 0.02, - k3 = 1e-05) -\end{python} - -A \py{Params} object is similar to a \py{System} or \py{State} object; it is useful for holding a collection of parameters. - -\index{State object} -\index{System object} - -We can pass \py{params} and \py{data} to \py{make_system}: - -\begin{python} -def make_system(params, data): - G0, k1, k2, k3 = params - - Gb = data.glucose[0] - Ib = data.insulin[0] - - t_0 = get_first_label(data) - t_end = get_last_label(data) - - init = State(G=G0, X=0) - - return System(G0=G0, k1=k1, k2=k2, k3=k3, - init=init, Gb=Gb, Ib=Ib, - t_0=t_0, t_end=t_end, dt=2) -\end{python} - -\py{make_system} uses the measurements at \py{t=0} as the basal levels, \py{Gb} and \py{Ib}. -It gets \py{t_0} and \py{t_end} from the data. -And it uses the parameter \py{G0} as the initial value for \py{G}. -Then it packs everything into a \py{System} object. - -Here's the update function: - -\index{update function} -\index{function!update} - -\begin{python} -def update_func(state, t, system): - G, X = state - unpack(system) - - dGdt = -k1 * (G - Gb) - X*G - dXdt = k3 * (I(t) - Ib) - k2 * X - - G += dGdt * dt - X += dXdt * dt - - return State(G=G, X=X) -\end{python} - -As usual, the update function takes a \py{State} object, a time, and a \py{System} object as parameters. The first line \py{update} uses multiple assignment to extract the current values of \py{G} and \py{X}. The second line uses \py{unpack} so we can read the system variables without using the dot operator. - -\index{unpack} - -Computing the derivatives \py{dGdt} and \py{dXdt} is straightforward; we just translate the equations from math notation to Python. - -\index{derivative} - -Then, to perform the update, we multiply each derivative by the discrete time step \py{dt}, which is \SI{2}{\minute} in this example. The return value is a \py{State} object with the new values of \py{G} and \py{X}. - -\index{time step} - -Before running the simulation, it is a good idea to run the update function with the initial conditions: - -\begin{python} -update_func(system.init, system.t_0, system) -\end{python} - -Now we are ready to run the simulation. We'll use this version of \py{run_simulation}, which is very similar to previous versions: - -\index{\py{run_simulation}} - -\begin{python} -def run_simulation(system, update_func): - unpack(system) - - frame = TimeFrame(columns=init.index) - frame.loc[t0] = init - ts = linrange(t0, t_end, dt) - - for t in ts: - frame.row[t+dt] = update_func(frame.row[t], t, system) - - return frame -\end{python} - -We can run it like this: - -\begin{python} -results = run_simulation(system, update_func) -\end{python} - -\begin{figure} -\centerline{\includegraphics[width=3.5in]{figs/chap08-fig03.pdf}} -\caption{Results from simulation of the glucose minimal model.} -\label{chap08-fig03} -\end{figure} - -The top plot in Figure~\ref{chap08-fig03} shows simulated glucose levels from the model along with the measured data. The bottom plot shows simulated insulin levels in tissue fluid, which is in unspecified units, and not to be confused with measured insulin levels in the blood. - -With the parameters I chose, the model fits the data well, but we can do better. - -In the next chapter, we replace \py{run_simulation} with a better differential equation solver, then search for the parameters that yield the best fit for the data. - -Before you go on, you might want to read the notebook for this chapter, \py{chap17.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - - -\chapter{Numerical methods} -\label{chap18} - -In the previous chapter, we implemented the glucose minimal model using \py{run_simulation}, which solves differential equations using discrete time steps. This method works well enough for many applications, but it is not very accurate. In this chapter we explore a better option: using an {\bf ODE solver}. - -Then we use a search algorithm to find the model parameters that yield the best fit for the data. - - -\section{Solving differential equations} -\label{slopefunc} - -So far we have solved differential equations by rewriting them as difference equations. In the current example, the differential equations are: -% -\[ \frac{dG}{dt} = -k_1 \left[ G(t) - G_b \right] - X(t) G(t) \] -% -\[ \frac{dX}{dt} = k_3 \left[I(t) - I_b \right] - k_2 X(t) \] -% -If we multiply both sides by $dt$, we have: -% -\[ dG = \left[ -k_1 \left[ G(t) - G_b \right] - X(t) G(t) \right] dt \] -% -\[ dX = \left[ k_3 \left[I(t) - I_b \right] - k_2 X(t) \right] dt \] -% -When $dt$ is very small, or more precisely {\bf infinitesimal}, this equation is exact. But in our simulations, $dt$ is \SI{2}{\minute}, which is small but not infinitesimal. In effect, the simulations assume that the derivatives $dG/dt$ and $dX/dt$ are constant during each \SI{2}{\minute} time step. - -\index{time step} - -This method, evaluating derivatives at discrete time steps and assuming that they are constant in between, is called {\bf Euler's method} (see \url{http://modsimpy.com/euler}). - -\index{Euler's method} - -Euler's method is good enough for some simple problems, but there are many better ways to solve differential equations. Rather than implement these methods ourselves, we will use functions from SciPy. The \py{modsim} library provides a function called \py{run_ode_solver}, which uses the SciPy function \py{solve_ivp}. - -\index{SciPy} - -The ``ODE" in \py{run_ode_solver} stands for ``ordinary differential equation integrator". The equations we are solving are ``ordinary'' because all the derivatives are with respect to the same variable; in other words, there are no partial derivatives. - -The ``IVP" in \py{solve_ivp} stands for ``initial value problem", which is the term for problems where you start with an initial value and figure out how the system changes over time, or sometimes space or another quantity. - -\index{ordinary differential equation} -\index{initial value problem} - -\py{solve_ivp} can use one of several ODE solvers; by default it uses a {\bf Runge-Kutta-Fehlberg method}. These methods are {\bf adaptive}; that is, they choose the step size automatically, using small steps when necessary to limit errors, and large steps when possible to be efficient. - -To use \py{run_ode_solver}, we have to provide a ``slope function", like this: - -\index{slope function} -\index{function!slope} -\index{unpack} - -\begin{python} -def slope_func(state, t, system): - G, X = state - unpack(system) - - dGdt = -k1 * (G - Gb) - X*G - dXdt = k3 * (I(t) - Ib) - k2 * X - - return dGdt, dXdt -\end{python} - -\py{slope_func} is similar to \py{update_func}; in fact, it takes the same parameters in the same order. But \py{slope_func} is simpler, because all we have to do is compute the derivatives, that is, the slopes. We don't have to do the updates; \py{run_ode_solver} does them for us. - - -\index{\py{run_ode_solver}} - -Now we can call \py{run_ode_solver} like this: - -\begin{python} -results, details = run_ode_solver(system, slope_func, - t_eval=data.index) -\end{python} - -\py{run_ode_solver} is similar to \py{run_simulation}: it takes a \py{System} object and a slope function as parameters, and returns a \py{TimeFrame} as a result. \py{results} has one row for each time step and one column for each state variable. In this example, the rows are the values from \py{data.index}; the columns are the state variables, \py{G} and \py{X}. - -\index{TimeFrame object} - -\py{run_ode_solver} also returns \py{details}, which is a \py{ModSimSeries} with information about how the solver ran, including a success code, a diagnostic message, and other information. A \py{ModSimSeries} is like a \py{System} or \py{State} object; it contains a set of variables and their values. - -\index{ModSimSeries} - -\py{run_ode_solver} takes an optional argument, \py{t_eval}, which indicates where we want to evaluate the solution. In this case we want the results to have the same time steps as the data, so we can compare them easily. - -The results are similar to what we saw in Figure~\ref{chap08-fig03}. The biggest relative difference is less than 1\%. - - -\section{Least squares} - -So far we have been taking the parameters as given, but in general we don't have that luxury. Normally we are given the data and we have to search for the parameters that yield a time series that best matches the data. - -\index{fitting data} - -We will do that now, in two steps: - -\begin{enumerate} - -\item First we'll define an {\bf error function} that takes a set of parameters, simulates the system with the given parameters, and computes the errors, that is, the differences between the simulation results and the data. - -\index{error function} -\index{function~error} - -\item Then we'll use \py{fit_leastsq}, to search for the parameters that minimize mean squared error (MSE). - -\index{\py{fit_leastsq}} -\index{leastsq} -\index{mean squared error} -\index{MSE} - -\end{enumerate} - -When \py{fit_leastsq} runs, it calls \py{error_func} many times, each time with a different set of parameters, until it converges on the parameters that minimize MSE. - -\index{\py{error_func}} - -Here's the error function: - -\begin{python} -def error_func(params, data): - system = make_system(params, data) - results, details = run_ode_solver(system, slope_func) - errors = results.G - data.glucose - return errors -\end{python} - -\py{error_func} takes as parameters a \py{Params} object and a \py{DataFrame} containing the measurements. It uses \py{make_system} to create a \py{System} object, and calls \py{run_ode_solver} using the same slope function we saw in Section~\ref{slopefunc}. - -Then it computes the difference between the simulation results and the data. Since \py{results.G} and \py{data.glucose} are both \py{Series} objects, \py{errors} is also a \py{Series}. - -\index{Series} -\index{\py{run_ode_solver}} - -Now, to do the actual minimization, we run \py{fit_leastsq}: - -\begin{python} -best_params, details = fit_leastsq(error_func, params, data) -\end{python} - -The first return value is a \py{Params} object with the parameters that yield the best fit for the data. The second return value is a \py{ModSimSeries} object with more information. - - -\section{Interpreting parameters} - -To see the results, we can pass \py{best_params} to \py{make_system} and then run the simulation: - -\begin{python} -system = make_system(best_params, data) -results, details = run_ode_solver(system, slope_func, - t_eval=data.index) -\end{python} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap08-fig04.pdf}} -\caption{Simulation of the glucose minimal model with parameters that minimize MSE.} -\label{chap08-fig04} -\end{figure} - -Figure~\ref{chap08-fig04} shows the results. The simulation matches the measurements well except during the first few minutes after the injection. - -But we don't expect the model to do well in this regime. - -The problem is that the model is {\bf non-spatial}; that is, it does not take into account different concentrations in different parts of the body. Instead, it assumes that the concentrations of glucose and insulin in blood, and insulin in tissue fluid, are the same throughout the body. This way of representing the body is known among experts as the ``bag of blood" model. - -\index{non-spatial model} -\index{bag of blood} - -Immediately after injection, it takes time for the injected glucose to circulate. During that time, we don't expect a non-spatial model to be accurate. For this reason, we should not take the estimated value of \py{G0} too seriously; it is useful for fitting the model, but not meant to correspond to a physical, measurable quantity. - -On the other hand, the other parameters are meaningful; in fact, they are the reason the model is useful. Using the best-fit parameters, we can estimate two quantities of interest: - -\index{glucose effectiveness} -\index{insulin sensitivity} - -\begin{itemize} - -\item ``Glucose effectiveness", $E$, which is the tendency of elevated glucose to cause depletion of glucose. - -\item ``Insulin sensitivity", $S$, which is the ability of elevated blood insulin to enhance glucose effectiveness. - -\end{itemize} - -Glucose effectiveness is defined as the change in $dG/dt$ as we vary $G$: -% -\[ E \equiv - \frac{\delta \dot{G}}{\delta G} \] -% -where $\dot{G}$ is shorthand for $dG/dt$. Taking the derivative of $dG/dt$ with respect to $G$, we get -% -\[ E = k_1 + X \] -% -The {\bf glucose effectiveness index}, $S_G$, is the value of $E$ when blood insulin is near its basal level, $I_b$. In that case, $X$ approaches 0 and $E$ approaches $k_1$. So we can use the best-fit value of $k_1$ as an estimate of $S_G$. - -\index{basal level} - -Insulin sensitivity is defined as the change in $E$ as we vary $I$: -% -\[ S \equiv - \frac{\delta E}{\delta I} \] -% -The {\bf insulin sensitivity index}, $S_I$, is the value of $S$ when $E$ and $I$ are at steady state: -% -\[ S_I \equiv \frac{\delta E_{SS}}{\delta I_{SS}} \] -% -$E$ and $I$ are at steady state when $dG/dt$ and $dX/dt$ are 0, but we don't actually have to solve those equations to find $S_I$. If we set $dX/dt = 0$ and solve for $X$, we find the relation: -% -\[ X_{SS} = \frac{k_3}{k_2} I_{SS} \] -% -And since $E = k_1 + X$, we have: -% -\[ S_I = \frac{\delta E_{SS}}{\delta I_{SS}} = \frac{\delta X_{SS}}{\delta I_{SS}} \] -% -Taking the derivative of $X_{SS}$ with respect to $I_{SS}$, we have: -% -\[ S_I = k_3 / k_2 \] -% -So if we find parameters that make the model fit the data, we can use $k_3 / k_2$ as an estimate of $S_I$. - -For the example data, the estimated values of $S_G$ and $S_I$ are $0.029$ and for $8.9 \times 10^{-4}$. According to Pacini and Bergman, these values are within the normal range. - -Before you go on, you might want to read the notebook for this chapter, \py{chap18.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - -\chapter{Case studies} -\label{chap19} - -This chapter reviews the computational patterns we have seen so far and presents exercises where you can apply them. - -\section{Computational tools} - -In Chapter~\ref{chap11} we saw an update function that uses multiple assignment to unpack a \py{State} object and assign the state variables to local variables. - -\begin{python} -def update_func(state, t, system): - s, i, r = state - - infected = system.beta * i * s - recovered = system.gamma * i - - s -= infected - i += infected - recovered - r += recovered - - return State(S=s, I=i, R=r) -\end{python} - -And in \py{run_simulation} we used multiple assignment again to assign state variables to a row in a \py{TimeFrame}: - -\begin{python} -def run_simulation(system, update_func): - frame = TimeFrame(columns=system.init.index) - frame.row[system.t0] = system.init - - for t in linrange(system.t0, system.t_end): - frame.row[t+1] = update_func(frame.row[t], system) - - return frame -\end{python} - -In Chapter~\ref{chap12} we used the functions \py{max} and \py{idxmax} to compute metrics: - -\begin{python} -largest_value = S.max() -time_of_largest_value = S.idxmax() -\end{python} - -And we saw the logistic function, a general function which is useful for modeling relationships between variables, like the effectiveness of an intervention as a function of expenditure. - -In Chapter~\ref{chap13} we saw the \py{unpack} function, which makes system variables available as if they were local variables. - -\begin{python} -def run_simulation(system, update_func): - unpack(system) - - frame = TimeFrame(columns=init.index) - frame.row[t0] = init - - for t in linrange(t0, t_end): - frame.row[t+1] = update_func(frame.row[t], t, system) - - system.results = frame -\end{python} - -One thing to remember when you use \py{unpack}: if you modify any of the unpacked variables, the change does not affect the \py{System} object. - -In Chapter~\ref{chap14} we used a \py{SweepFrame} object to sweep two parameters. - -\begin{python} -def sweep_parameters(beta_array, gamma_array): - frame = SweepFrame(columns=gamma_array) - for gamma in gamma_array: - frame[gamma] = sweep_beta(beta_array, gamma) - return frame -\end{python} - -In Chapter~\ref{chap15} we used \py{linrange} to create an array of values with a given step size. \py{linrange} is similar to \py{linspace}: the difference is that \py{linrange} lets you specify the space between values, and it computes the number of values; \py{linspace} lets you specify the number of values, and it computes the space between them. - -Here's a version of \py{run_simulation} that uses \py{linrange}: - -\begin{python} -def run_simulation(system, update_func): - unpack(system) - - frame = TimeFrame(columns=init.index) - frame.row[t_0] = init - ts = linrange(t_0, t_end, dt) - - for t in ts: - frame.row[t+dt] = update_func(frame.row[t], t, system) - - # store the final temperature in T_final - system.T_final = get_last_value(frame.T) - - return frame -\end{python} - -In Chapter~\ref{chap16} we used \py{fsolve} to find the value of a parameter that yields a particular result. We defined an error function: - -\index{\py{fsolve}} - -\begin{python} -def error_func1(r): - system = make_system(r=r) - results = run_simulation(system, update) - return system.T_final - 70 -\end{python} - -And passed it to \py{fsolve} with an initial guess, like this: - -\begin{python} -solution = fsolve(error_func1, 0.01) -r_coffee = solution[0] -\end{python} - -In Chapter~\ref{chap17} we used \py{interpolate}, which returns a function: - -\begin{python} -I = interpolate(data.insulin) -\end{python} - -which we can call like any other function, passing as an argument either a single value or a NumPy array: - -\begin{python} -I(18) - -ts = linrange(t_0, t_end) -I(ts) -\end{python} - -We also used a \py{Params} object, which is a collection of parameters. - -\begin{python} -params = Params(G0 = 290, - k1 = 0.03, - k2 = 0.02, - k3 = 1e-05) -\end{python} - -Chapter~\ref{chap18} introduces \py{run_ode_solver} which computes numerical solutions to differential equations. - -\py{run_ode_solver} uses a slope function, which is similar to an update function: - -\begin{python} -def slope_func(state, t, system): - G, X = state - unpack(system) - - dGdt = -k1 * (G - Gb) - X*G - dXdt = k3 * (I(t) - Ib) - k2 * X - - return dGdt, dXdt -\end{python} - -We used \py{run_ode_solver} to write an error function, which takes a \py{Params} object, runs a simulation, and returns the difference between the simulation results and the data: - -\begin{python} -def error_func(params, data): - system = make_system(params, data) - results, details = run_ode_solver(system, slope_func, - t_eval=data.index) - errors = results.G - data.glucose - return errors -\end{python} - -Then we passed the error function to \py{fit_leastsq}, which finds the set of parameters that minimizes the errors: - -\begin{python} -best_params, fit_details = fit_leastsq(error_func, params, data) -\end{python} - -%TODO: Anything to say here? - - -\section{Under the hood} - -\py{unpack} is a function in the \py{modsim} library that copies the variables and values from a \py{System} object into a special Python data structure that stores global variables. To be honest, it is a bit of a hack; that is, it may be expedient, but it might not be an example of software engineering you should emulate. - -\py{fsolve} is based on a Scipy function that is also called \py{fsolve}; you can read more about it at \url{http://modsimpy.com/fsolve}. -It uses a numerical method called Powell's method, and you can read more about that at \url{http://modsimpy.com/powell}. - -\index{\py{fsolve}} - -\py{run_ode_solver} uses the SciPy function \py{solve_ivp}, which you can read about at \url{http://modsimpy.com/ivp}. -By default it uses an algorithm called RK45, or RKF45, which you can read about at \url{http://modsimpy.com/runge}). - -\py{fit_leastsq} is based on the SciPy function \py{leastsq}; you can read about it at \url{http://modsimpy.com/sq}. -By default it uses a version of the Levenberg-Marquardt method, which you can read about at \url{http://modsimpy.com/lm }. - -These SciPy functions don't work with the units provided by Pint, so the \py{modsim} library turns off unit-checking before calling them. This mechanism is a bit of a hack, so you might get some unexpected behavior. If so, I suggest you make a modest effort to fix them problem, but don't spend too much time on it. - -Carrying units through computations like this is not as common as it should be, so the tools are not as polished as they could be. I think it is worth trying, because you can catch a lot of errors by checking units; but if it gets to be too much trouble, you can always remove the units from the code. In that case, you should add comments that document the units for all variables! - -The rest of this chapter presents case studies you can use to practice what you have learned so far. - - -\section{The insulin minimal model} - -Along with the glucose minimal model in Chapter~\ref{chap17}, Berman et al.~developed an insulin minimal model, in which the concentration of insulin, $I$, is governed by this differential equation: -% -\[ \frac{dI}{dt} = -k I(t) + \gamma \left[ G(t) - G_T \right] t \] -% -where - -\begin{itemize} - -\item $k$ is a parameter that controls the rate of insulin disappearance independent of blood glucose. - -\item $G(t)$ is the measured concentration of blood glucose at time $t$. - -\item $G_T$ is the glucose threshold; when blood glucose is above this level, it triggers an increase in blood insulin. - -\item $\gamma$ is a parameter that controls the rate of increase (or decrease) in blood insulin when glucose is above (or below) $G_T$. - -% TODO: explain why t is there - -\end{itemize} - -The initial condition is $I(0) = I_0$. As in the glucose minimal model, we treat the initial condition as a parameter which we'll choose to fit the data. - -\index{insulin minimal model} -\index{differential equation} - -The parameters of this model can be used to estimate, $\phi_1$ and $\phi_2$, which are values that ``describe the sensitivity to glucose of the first and second phase pancreatic responsivity". They are related to the parameters as follows: -% -\[ \phi_1 = \frac{I_{max} - I_b}{k (G_0 - G_b)}\] -% -\[ \phi_2 = \gamma \times 10^4 \] -% -where $I_{max}$ is the maximum measured insulin level, and $I_b$ and $G_b$ are the basal levels of insulin and glucose. - -%TODO: Clarify whether G0 here is the parameter we estimated in the previous -% model, or the maximum observed value of G. - -In the repository for this book, you will find a notebook, \py{insulin.ipynb}, which contains starter code for this case study. Use it to implement the insulin model, find the parameters that best fit the data, and estimate these values. - - -\section{Low-Pass Filter} - -The following circuit diagram\footnote{From \url{http://modsimpy.com/divider}} shows a low-pass filter built with one resistor and one capacitor. - -\centerline{\includegraphics[height=1.3in]{figs/RC_Divider.pdf}} - -A ``filter" is a circuit takes a signal, $V_{in}$, as input and produces a signal, $V_{out}$, as output. In this context, a ``signal" is a voltage that changes over time. - -A filter is ``low-pass" if it allows low-frequency signals to pass from $V_{in}$ to $V_{out}$ unchanged, but it reduces the amplitude of high-frequency signals. - -By applying the laws of circuit analysis, we can derive a differential equation that describes the behavior of this system. By solving the differential equation, we can predict the effect of this circuit on any input signal. - -Suppose we are given $V_{in}$ and $V_{out}$ at a particular instant in time. By Ohm's law, which is a simple model of the behavior of resistors, the instantaneous current through the resistor is: -% -\[ I_R = (V_{in} - V_{out}) / R \] -% -where $R$ is resistance in ohms (\si{\ohm}). - -Assuming that no current flows through the output of the circuit, Kirchhoff's current law implies that the current through the capacitor is: -% -\[ I_C = I_R \] -% -According to a simple model of the behavior of capacitors, current through the capacitor causes a change in the voltage across the capacitor: -% -\[ I_C = C \frac{d V_{out}}{dt} \] -% -where $C$ is capacitance in farads (\si{\farad}). Combining these equations yields a differential equation for $V_{out}$: -% -\[ \frac{d V_{out}}{dt} = \frac{V_{in} - V_{out}}{R C} \] -% -In the repository for this book, you will find a notebook, \py{filter.ipynb}, which contains starter code for this case study. Follow the instructions to simulate the low-pass filter for input signals like this: -% -\[ V_{in}(t) = A \cos (2 \pi f t) \] -% -where $A$ is the amplitude of the input signal, say \SI{5}{\volt}, and $f$ is the frequency of the signal in \si{\hertz}. - -In the repository for this book, you will find a notebook, \py{filter.ipynb}, which contains starter code for this case study. Read the notebook, run the code, and work on the exercises. - - - -\section{Thermal behavior of a wall} - -This case study is based on a paper by Gori, et~al\footnote{Gori, Marincioni, Biddulph, Elwell, ``Inferring the thermal resistance and effective thermal mass distribution of a wall from in situ measurements to characterise heat transfer at both the interior and exterior surfaces", {\it Energy and Buildings}, Volume 135, pages 398-409, \url{http://modsimpy.com/wall2}. - -The authors put their paper under a Creative Commons license, and make their data available at \url{http://modsimpy.com/wall }. I thank them for their commitment to open, reproducible science, which made this case study possible.} that models the thermal behavior of a brick wall, with the goal of understanding the ``performance gap between the expected energy use of buildings and their measured energy use". - -The following figure shows the scenario and their model of the wall: - -\vspace{0.1in} -\centerline{\includegraphics[height=1.3in]{figs/wall_model.pdf}} - -On the interior and exterior surfaces of the wall, they measure temperature and heat flux over a period of three days. They model the wall using two thermal masses connected to the surfaces, and to each other, by thermal resistors. - -The primary methodology of the paper is a Bayesian method for inferring the parameters of the system (two thermal masses and three thermal resistances). - -The primary result is a comparison of two models: the one shown here with two thermal masses, and a simpler model with only one thermal mass. They find that the two-mass model is able to reproduce the measured fluxes substantially better. - -For this case study we will implement their model and run it with the estimated parameters from the paper, and then use \py{fit_leastsq} to see if we can find parameters that yield lower errors. - -In the repository for this book, you will find a notebook, \py{wall.ipynb} with the code and results for this case study. - - -\chapter{Projectiles} -\label{chap20} - -So far the differential equations we've worked with have been {\bf first order}, which means they involve only first derivatives. In this chapter, we turn our attention to second order ODEs, which can involve both first and second derivatives. - -\index{first order ODE} -\index{second order ODE} - -We'll revisit the falling penny example from Chapter~\ref{chap01}, and use \py{odeint} to find the position and velocity of the penny as it falls, with and without air resistance. - - -\section{Newton's second law of motion} - -First order ODEs can be written -% -\[ \frac{dy}{dx} = G(x, y) \] -% -where $G$ is some function of $x$ and $y$ (see \url{http://modsimpy.com/ode}). Second order ODEs can be written -% -\[ \frac{d^2y}{dx^2} = H(x, y, \frac{dy}{dt}) \] -% -where $H$ is a function of $x$, $y$, and $dy/dx$. - -In this chapter, we will work with one of the most famous and useful second order ODE, Newton's second law of motion: -% -\[ F = m a \] -% -where $F$ is a force or the total of a set of forces, $m$ is the mass of a moving object, and $a$ is its acceleration. - -\index{Newton's second law of motion} -\index{differential equation} -\index{acceleration} -\index{velocity} -\index{position} - -Newton's law might not look like a differential equation, until we realize that acceleration, $a$, is the second derivative of position, $y$, with respect to time, $t$. With the substitution -% -\[ a = \frac{d^2y}{dt^2} \] -% -Newton's law can be written -% -\[ \frac{d^2y}{dt^2} = F / m \] -% -And that's definitely a second order ODE. In general, $F$ can be a function of time, position, and velocity. - -Of course, this ``law" is really a model, in the sense that it is a simplification of the real world. Although it is often approximately true: - -\begin{itemize} - -\item It only applies if $m$ is constant. If mass depends on time, position, or velocity, we have to use a more general form of Newton's law (see \url{http://modsimpy.com/varmass}). - -\index{variable mass} - -\item It is not a good model for very small things, which are better described by another model, quantum mechanics. - -\index{quantum mechanics} - -\item And it is not a good model for things moving very fast, which are better described by yet another model, relativistic mechanics. - -\index{relativity} - -\end{itemize} - -However, for medium-sized things with constant mass, moving at medium-sized speeds, Newton's model is phenomenally useful. If we can quantify the forces that act on such an object, we can predict how it will move. - - -\section{Dropping pennies} - -As a first example, let's get back to the penny falling from the Empire State Building, which we considered in Section~\ref{penny}. We will implement two models of this system: first without air resistance, then with. - -\index{falling penny} -\index{air resistance} - -Given that the Empire State Building is \SI{381}{\meter} high, and assuming that the penny is dropped with velocity zero, the initial conditions are: - -\index{State object} - -\begin{python} -init = State(y=381 * m, - v=0 * m/s) -\end{python} - -where \py{y} is height above the sidewalk and \py{v} is velocity. The units \py{m} and \py{s} are from the \py{UNITS} object provided by Pint: - -\index{unit} -\index{Pint} - -\begin{python} -m = UNITS.meter -s = UNITS.second -\end{python} - -The only system parameter is the acceleration of gravity: - -\begin{python} -g = 9.8 * m/s**2 -\end{python} - -In addition, we'll specify the duration of the simulation: - -\begin{python} -t_end = 10 * s -\end{python} - -We need a \py{System} object to contain the system parameters: - -\index{System object} - -\begin{python} -system = System(init=init, g=g, t_end=t_end) -\end{python} - -Now we need a slope function, and here's where things get tricky. As we have seen, \py{run_ode_solver} can solve systems of first order ODEs, but Newton's law is a second order ODE. However, if we recognize that - -\index{slope function} -\index{function!slope} - -\begin{enumerate} - -\item Velocity, $v$, is the derivative of position, $dy/dt$, and - -\item Acceleration, $a$, is the derivative of velocity, $dv/dt$, - -\end{enumerate} - -we can rewrite Newton's law as a system of first order ODEs: -% -\[ \frac{dy}{dt} = v \] -% -\[ \frac{dv}{dt} = a \] -% -And we can translate those equations into a slope function: - -\index{system of equations} -\index{unpack} - -\begin{python} -def slope_func(state, t, system): - y, v = state - unpack(system) - - dydt = v - dvdt = -g - - return dydt, dvdt -\end{python} - -The first parameter, \py{state}, contains the position and velocity of the penny. The last parameter, \py{system}, contains the system parameter \py{g}, which is the magnitude of acceleration due to gravity. - -\index{State object} - -The second parameter, \py{t}, is time. It is not used in this slope function because none of the factors of the model are time dependent (see Section~\ref{glucose}). I include it anyway because this function will be called by \py{run_ode_solver}, which always provides the same arguments, whether they are needed or not. - -\index{time dependent} - -The rest of the function is a straightforward translation of the differential equations, with the substitution $a = -g$, which indicates that acceleration is due to gravity, in the direction of decreasing $y$. \py{slope_func} returns a sequence containing the two derivatives. - -Before calling \py{run_ode_solver}, it is a good idea to test the slope function with the initial conditions: - -\begin{python} -dydt, dvdt = slope_func(init, 0, system) -\end{python} - -The result is \SI{0}{\meter\per\second} for velocity and \SI{9.8}{\meter\per\second\squared} for acceleration. Now we can call \py{run_ode_solver} like this: - -\begin{python} -results, details = run_ode_solver(system, slope_func, - max_step=0.5*s) -\end{python} - -The optional argument \py{max_step} determines the largest step size \py{run_ode_solver} can use. Specifying \py{max_step} makes the simulation take longer, but it makes the results look better when plotted. It has little or no effect on accuracy. - -\py{results} in a \py{TimeFrame} with two columns: \py{y} contains the height of the penny; \py{v} contains its velocity. - -\index{TimeFrame object} -\index{\py{run_ode_solver}} - -We can plot the results like this: - -\begin{python} -def plot_position(results): - plot(results.y) - decorate(xlabel='Time (s)', - ylabel='Position (m)') -\end{python} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap09-fig01.pdf}} -\caption{Height of the penny versus time, with no air resistance.} -\label{chap09-fig01} -\end{figure} - -Figure~\ref{chap09-fig01} shows the result. Since acceleration is constant, velocity increases linearly and position decreases quadratically; as a result, the height curve is a parabola. - -\index{parabola} - -The last value of \py{results.y} is \SI{-109}{\meter}, which means we ran the simulation too long. One way to solve this problem is to use the results to estimate the time when the penny hits the sidewalk. - -The \py{modsim} library provides \py{crossings}, which takes a \py{TimeSeries} and a value, and returns a sequence of times when the series passes through the value. We can find the time when the height of the penny is \py{0} like this: - -\begin{python} -t_crossings = crossings(results.y, 0) -\end{python} - -The result is an array with a single value, \SI{8.818}{s}. Now, we could run the simulation again with \py{t_end = 8.818}, but there's a better way. - -\section{Events} -\label{events} - -As an option, \py{run_ode_solver} can take an {\bf event function}, which detects an ``event", like the penny hitting the sidewalk, and ends the simulation. - -Event functions take the same parameters as slope functions, \py{state}, \py{t}, and \py{system}. They should return a value that passes through \py{0} when the event occurs. Here's an event function that detects the penny hitting the sidewalk: - -\begin{python} -def event_func(state, t, system): - y, v = state - return y -\end{python} - -The return value is the height of the penny, \py{y}, which passes through \py{0} when the penny hits the sidewalk. - -We pass the event function to \py{run_ode_solver} like this: - -\begin{python} -results, details = run_ode_solver(system, slope_func, - events=event_func) -\end{python} - -\py{events} can also be a sequence of event functions, if there is more than one event that might occur. - -\py{run_ode_solver} uses Brent's method to estimate the time of the event precisely (see \url{http://modsimpy.com/brent}). - -Before you go on, you might want to read the notebook for this chapter, \py{chap20.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - - - -\chapter{Air resistance} -\label{chap21} - -In the previous chapter we simulated a penny falling in a vacuum, that is, without air resistance. But the computational framework we used is very general; it is easy to add additional forces, including drag. - -In this chapter, I present a model of drag force and add it to the simulation. - - -\section{Drag force} -\label{drag} - -As an object moves through a fluid, like air, the object applies force to the air and, in accordance with Newton's third law of motion, the air applies an equal and opposite force to the object (see \url{http://modsimpy.com/newton}). - -\index{air resistance} -\index{drag force} -\index{force!drag} -\index{drag equation} - -The direction of this {\bf drag force} is opposite the direction of travel, and its magnitude is given by the drag equation (see \url{http://modsimpy.com/drageq}): -% -\[ F_d = \frac{1}{2}~\rho~v^2~C_d~A \] -% -where - -\begin{itemize} - -\item $F_d$ is force due to drag, in newtons (\si{\newton}). - -\item $\rho$ is the density of the fluid in \si{\kg\per\meter\cubed}. -\index{density} - -\item $v$ is the magnitude of velocity in \si{\meter\per\second}. -\index{velocity} - -\item $A$ is the {\bf reference area} of the object, in \si{\meter\squared}. In this context, the reference area is the projected frontal area, that is, the visible area of the object as seen from a point on its line of travel (and far away). - -\index{reference area} - -\item $C_d$ is the {\bf drag coefficient}, a dimensionless quantity that depends on the shape of the object (including length but not frontal area), its surface properties, and how it interacts with the fluid. - -\index{drag coefficient} - -\end{itemize} - -For objects moving at moderate speeds through air, typical drag coefficients are between 0.1 and 1.0, with blunt objects at the high end of the range and streamlined objects at the low end (see \url{http://modsimpy.com/dragco}). - -For simple geometric objects we can sometimes guess the drag coefficient with reasonable accuracy; for more complex objects we usually have to take measurements and estimate $C_d$ from the data. - -Of course, the drag equation is itself a model, based on the assumption that $C_d$ does not depend on the other terms in the equation: density, velocity, and area. For objects moving in air at moderate speeds (below 45 mph or \SI{20}{\meter\per\second}), this model might be good enough, but we should remember to revisit this assumption. - -For the falling penny, we can use measurements to estimate $C_d$. In particular, we can measure {\bf terminal velocity}, $v_{term}$, which is the speed where drag force equals force due to gravity: -% -\[ \frac{1}{2}~\rho~v_{term}^2~C_d~A = m g \] -% -where $m$ is the mass of the object and $g$ is acceleration due to gravity. Solving this equation for $C_d$ yields: -% -\[ C_d = \frac{2~m g}{\rho~v_{term}^2~A} \] -% -According to {\it Mythbusters}, the terminal velocity of a penny is between 35 and 65 mph (see \url{http://modsimpy.com/mythbust}). Using the low end of their range, 40 mph or about \SI{18}{\meter\per\second}, the estimated value of $C_d$ is 0.44, which is close to the drag coefficient of a smooth sphere. - -\index{Mythbusters} -\index{terminal velocity} - -Now we are ready to add air resistance to the model. - - -\section{Implementation} -\label{penny_drag} - -As the number of system parameters increases, and as we need to do more work to compute them, we will find it useful to define a \py{Params} object to contain the quantities we need to make a \py{System} object. \py{Params} objects are similar to \py{System} and \py{State} objects; in fact, all three have the same capabilities. I have given them different names to document the different roles they play. - -\index{Params object} - -Here's the \py{Params} object for the falling penny: - -\begin{python} -params = Params(height = 381 * m, - v_init = 0 * m / s, - g = 9.8 * m/s**2, - mass = 2.5e-3 * kg, - diameter = 19e-3 * m, - rho = 1.2 * kg/m**3, - v_term = 18 * m / s) -\end{python} - -The mass and diameter are from \url{http://modsimpy.com/penny}. The density of air depends on temperature, barometric pressure (which depends on altitude), humidity, and composition (\url{http://modsimpy.com/density}). I chose a value that might be typical in New York City at \SI{20}{\celsius}. - -\index{System object} -\index{\py{make_system}} - -Here's a version of \py{make_system} that takes a \py{Params} object and returns a \py{System}: - -\index{unpack} - -\begin{python} -def make_system(params): - unpack(params) - - area = np.pi * (diameter/2)**2 - C_d = 2 * mass * g / (rho * area * v_term**2) - init = State(y=height, v=v_init) - t_end = 30 * s - - return System(params, area=area, C_d=C_d, - init=init, t_end=t_end) -\end{python} - -The first argument of \py{System} is \py{params}, so the result contains all of the parameters in \py{params}, plus \py{init}, \py{area}, and \py{C_d}. - -It might not be obvious why we need \py{Params} objects, but they will turn out to be useful soon. - -We can make a \py{System} like this: - -\begin{python} -system = make_system(params) -\end{python} - -Now here's a version of the slope function that includes drag: - -\index{slope function} -\index{function!slope} -\index{unpack} - -\begin{python} -def slope_func(state, t, system): - y, v = state - unpack(system) - - f_drag = rho * v**2 * C_d * area / 2 - a_drag = f_drag / mass - - dydt = v - dvdt = -g + a_drag - - return dydt, dvdt -\end{python} - -\py{f_drag} is force due to drag, based on the drag equation. \py{a_drag} is acceleration due to drag, based on Newton's second law. - -\index{gravity} - -To compute total acceleration, we add accelerations due to gravity and drag. \py{g} is negated because it is in the direction of decreasing \py{y}, and \py{a_drag} is positive because it is in the direction of increasing \py{y}. In the next chapter we will use \py{Vector} objects to keep track of the direction of forces and add them up in a less error-prone way. - -To stop the simulation when the penny hits the sidewalk, we'll use the event function from Section~\ref{events}: - -\begin{python} -def event_func(state, t, system): - y, v = state - return y -\end{python} - -Now we can run the simulation like this: - -\index{\py{run_ode_solver}} - -\begin{python} -results, details = run_ode_solver(system, slope_func, - events=event_func, max_step=0.5*s) -\end{python} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap09-fig02.pdf}} -\caption{Height of the penny versus time, with air resistance.} -\label{chap09-fig02} -\end{figure} - -Figure~\ref{chap09-fig02} shows the result. It only takes a few seconds for the penny to accelerate up to terminal velocity; after that, velocity is constant, so height as a function of time is a straight line. - -\index{terminal velocity} - -In the notebook for this chapter, you'll have a chance to extend this model. - - -\section{Bungee jumping} -\label{bungee} - -Suppose you want to set the world record for the highest ``bungee dunk", which is a stunt in which a bungee jumper dunks a cookie in a cup of tea at the lowest point of a jump. An example is shown in this video: \url{http://modsimpy.com/dunk}. - -Since the record is \SI{70}{\meter}, let's design a jump for \SI{80}{\meter}. We'll start with the following modeling assumptions: - -\begin{itemize} - -\item Initially the bungee cord hangs from a crane with the attachment point \SI{80}{\meter} above a cup of tea. - -\item Until the cord is fully extended, it applies no force to the jumper. It turns out this might not be a good assumption; we will revisit it. - -\item After the cord is fully extended, it obeys Hooke's Law; that is, it applies a force to the jumper proportional to the extension of the cord beyond its resting length. See \url{http://modsimpy.com/hooke}. - -\item The mass of the jumper is \SI{75}{\kilogram}. - -\item The jumper is subject to drag force, as in the previous model, so that their terminal velocity is \SI{60}{\meter \per \second}. - -\end{itemize} - -Our objective is to choose the length of the cord, \py{L}, and its spring constant, \py{k}, so that the jumper falls all the way to the tea cup, but no farther! - -We'll start with the length of the bungee cord, \py{L} at \SI{25}{\meter} and spring constant, \py{k} at \SI{40}{\newton \per \meter}. Here's a \py{Params} object with all of these parameters: - -\begin{python} -params = Params(y_attach = 80 * m, - v_init = 0 * m / s, - g = 9.8 * m/s**2, - mass = 75 * kg, - area = 1 * m**2, - rho = 1.2 * kg/m**3, - v_term = 60 * m / s, - L = 25 * m, - k = 40 * N / m) -\end{python} - -And here's a version of \py{make_system} that uses \py{v_term} to compute \py{C_d}: - -\begin{python} -def make_system(params): - unpack(params) - - C_d = 2 * mass * g / (rho * area * v_term**2) - init = State(y=y_attach, v=v_init) - t_end = 20 * s - - return System(params, C_d=C_d, - init=init, t_end=t_end) -\end{python} - -We can run it like this: - -\begin{python} -system = make_system(params) -\end{python} - -We need a function to compute spring force based on the altitude of the jumper, \py{y}: - -\begin{python} -def spring_force(y, system): - unpack(system) - distance_fallen = y_attach - y - if distance_fallen <= L: - return 0 * N - - extension = distance_fallen - L - f_spring = k * extension - return f_spring -\end{python} - -\py{distance_fallen} is the distance of the jumper from the attachment point. If the cord is not fully extended, the spring force is 0. Otherwise we compute the extension of the cord, \py{extension}, and the spring force, \py{f_spring}. - -I'll also define a function to compute drag force: - -\begin{python} -def drag_force(v, system): - unpack(system) - f_drag = -np.sign(v) * rho * v**2 * C_d * area / 2 - return f_drag -\end{python} - -\py{drag_force} uses the NumPy function \py{sign}, which returns 1 if the argument is positive, -1 if it is negative, and 0 if it is 0. This ensures that the drag force is always in the opposite direction of velocity. - -\index{NumPy} -\index{sign} - -Now here's the slope function: - -\begin{python} -def slope_func(state, t, system): - y, v = state - unpack(system) - - a_drag = drag_force(v, system) / mass - a_spring = spring_force(y, system) / mass - dvdt = -g + a_drag + a_spring - - return v, dvdt -\end{python} - -\py{slope_func} uses \py{spring_force} and \py{drag_force} to compute forces, then divides by \py{mass} to get accelerations. - -We can run the simulation like this: - -\begin{python} -ts = linspace(0, system.t_end, 301) -results, details = run_ode_solver(system, slope_func, - max_step=0.3*s) -\end{python} - -Again, I use \py{max_step} so the results look better when plotted. -Figure~\ref{chap09-fig03} shows the results. - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap09-fig03.pdf}} -\caption{Position and velocity of the bungee jumper.} -\label{chap09-fig03} -\end{figure} - -We can find the altitude of the jumper at the lowest point like this: - -\begin{python} -min(results.y) -\end{python} - -For the parameters we chose, the jumper bottoms out at about \SI{5}{\meter}, so we need to increase \py{L} or decrease \py{k}. In the notebook for this chapter, you'll have to a chance to find the best parameters for the bungee dunk. - -\section{Getting acceleration} - -In fact, there are many combinations of \py{L} or decrease \py{k} that would work. We might want to find the combination that minimizes the peak acceleration of the jumper, providing the maximum time near the sidewalk to make the dunk. - -To do that, we need to know the acceleration of the jumper over time. Although we compute acceleration in the slope function, it is not included in the results. - -It might be tempting to record the acceleration each time the slope function runs, but we should not do that. The ODE solver calls the slope function many times with different values of \py{state} and \py{t}. Because of the way the solver works, not all of the states and times are actually part of the solution. So recording acceleration while the solver is running would not work. - -Instead, we can use the computed velocities to estimate acceleration as a function of time. - -The \py{modsim} library provides \py{gradient}, which uses NumPy to estimate the derivative of a \py{TimeSeries}. Here's how it works: - -\begin{python} -a = gradient(results.v) -\end{python} - -In the notebook for this chapter, \py{chap21.ipynb}, you can finish this problem by finding the combination of \py{L} and \py{k} that allows the jumper to complete the bungee dunk while minimizing the acceleration they experience. For instructions on downloading and running the code, see Section~\ref{code}. - - -\chapter{Projectiles in 2-D} -\label{chap22} - -In the previous chapter we modeled objects moving in one dimension, with and without drag. Now let's move on to two dimensions, and baseball! - -In this chapter we model the flight of a baseball including the effect of air resistance. In the next chapter we use this model to solve an optimization problem. - - -\section{Baseball} -\label{baseball} - -To model the flight of a baseball, we have to make some modeling decisions. To get started, we ignore any spin that might be on the ball, and the resulting Magnus force (see \url{http://modsimpy.com/magnus}). Under this assumption, the ball travels in a vertical plane, so we'll run simulations in two dimensions, rather than three. - -\index{Magnus force} - -Air resistance has a substantial effect on most projectiles in air, so we will include a drag force. - -\index{air resistance} - -To model air resistance, we'll need the mass, frontal area, and drag coefficient of a baseball. Mass and diameter are easy to find (see \url{http://modsimpy.com/baseball}). Drag coefficient is only a little harder; according to {\it The Physics of Baseball}\footnote{Adair, {\it The Physics of Baseball}, Third Edition, Perennial, 2002}, the drag coefficient of a baseball is approximately 0.33 (with no units). - -\index{drag coefficient} - -However, this value {\em does} depend on velocity. At low velocities it might be as high as 0.5, and at high velocities as low as 0.28. Furthermore, the transition between these regimes typically happens exactly in the range of velocities we are interested in, between \SI{20}{\meter\per\second} and \SI{40}{\meter\per\second}. - -Nevertheless, we'll start with a simple model where the drag coefficient does not depend on velocity; as an exercise at the end of this chapter, you will have a chance to implement a more detailed model and see what effect is has on the results. - -But first we need a new computational tool, the \py{Vector} object. - - -\section{Vectors} - -Now that we are working in two dimensions, we will find it useful to work with {\bf vector quantities}, that is, quantities that represent both a magnitude and a direction. We will use vectors to represent positions, velocities, accelerations, and forces in two and three dimensions. - -\index{Vector object} -\index{array} -\index{NumPy} - -The \py{modsim} library provides a \py{Vector} object that represents a vector quantity. A \py{Vector} object is a like a NumPy array; it contains elements that represent the {\bf components} of the vector. For example, in a \py{Vector} that represents a position in space, the components are the $x$ and $y$ coordinates (and a $z$ coordinate in 3-D). A \py{Vector} object can also have units, like the quantities we've seen in previous chapters. - -\index{unit} - -You can create a \py{Vector} by specifying its components. The following \py{Vector} represents a point \SI{3}{\meter} to the right (or east) and \SI{4}{\meter} up (or north) from an implicit origin: - -\index{component} - -\begin{python} -A = Vector(3, 4) * m -\end{python} - -You can access the components of a \py{Vector} by name using the dot operator, for example, \py{A.x} or \py{A.y}. You can also access them by index using brackets, for example, \py{A[0]} or \py{A[1]}. - -Similarly, you can get the magnitude and angle using the dot operator, \py{A.mag} and \py{A.angle}. {\bf Magnitude} is the length of the vector: if the \py{Vector} represents position, magnitude is the distance from the origin; if it represents velocity, magnitude is speed, that is, how fast the object is moving, regardless of direction. - -\index{angle} -\index{magnitude} - -The {\bf angle} of a \py{Vector} is its direction, expressed as the angle in radians from the positive x-axis. In the Cartesian plane, the angle \SI{0}{\radian} is due east, and the angle \SI{\pi}{\radian} is due west. - -\index{radian} - -\py{Vector} objects support most mathematical operations, including addition and subtraction: - -\begin{python} -B = Vector(1, 2) * m -A + B -A - B -\end{python} - -For the definition and graphical interpretation of these operations, see \url{http://modsimpy.com/vecops}. - -\index{vector operation} - -When you add and subtract \py{Vector} objects, the \py{modsim} library uses NumPy and Pint to check that the operands have the same number of dimensions and units. The notebook for this chapter shows examples for working with \py{Vector} objects. - -\index{dimensions} - -One note on working with angles: in mathematics, we almost always represent angle in radians, and most Python functions expect angles in radians. But people often think more naturally in degrees. It can be awkward, and error-prone, to use both units in the same program. Fortunately, Pint makes it possible to represent angles using quantities with units. - -\index{degree} - -As an example, I'll get the \py{degree} unit from \py{UNITS}, and create a quantity that represents 45 degrees: - -\begin{python} -degree = UNITS.degree -angle = 45 * degree -\end{python} - -If we need to convert to radians we can use the \py{to} function -\index{\py{to}} - -\begin{python} -radian = UNITS.radian -rads = angle.to(radian) -\end{python} - -If you are given an angle and velocity, you can make a \py{Vector} using \py{pol2cart}, which converts from polar to Cartesian coordinates. To demonstrate, I'll extract the angle and magnitude of \py{A}: - -\index{pol2cart} - -\begin{python} -mag = A.mag -angle = A.angle -\end{python} - -And then make a new \py{Vector} with the same components: - -\begin{python} -x, y = pol2cart(angle, mag) -Vector(x, y) -\end{python} - -Another way to represent the direction of \py{A} is a {\bf unit vector}, which is a vector with magnitude 1 that points in the same direction as \py{A}. You can compute a unit vector by dividing a vector by its magnitude: - -\index{unit vector} -\index{hat function} - -\begin{python} -A / A.mag -\end{python} - -We can do the same thing using the \py{hat} function, so named because unit vectors are conventionally decorated with a hat, like this: $\hat{A}$. - -\begin{python} -A.hat() -\end{python} - -Now let's get back to the game. - - -\section{Simulating baseball flight} - -Let's simulate the flight of a baseball that is batted from home plate at an angle of \SI{45}{\degree} and initial speed \SI{40}{\meter \per \second}. -Using the center of home plate as the origin, the x-axis is parallel to the ground; the y-axis is vertical. The initial height is about \SI{1}{\meter}. - -As in Section~\ref{penny_drag}, I'll create a \py{Params} object that contains the parameters of the system: - -\index{Params object} - -\begin{python} -params = Params(x = 0 * m, - y = 1 * m, - g = 9.8 * m/s**2, - mass = 145e-3 * kg, - diameter = 73e-3 * m, - rho = 1.2 * kg/m**3, - C_d = 0.3, - angle = 45 * degree, - velocity = 40 * m / s, - duration = 6 * s) -\end{python} - -The mass, diameter, and drag coefficient of the baseball are from the sources in Section~\ref{baseball}. The acceleration of gravity, \py{g}, is a well-known quantity, and the density of air, \py{rho}, is based on a temperature of \SI{20}{\celsius} at sea level (see \url{http://modsimpy.com/tempress}). - I chose the value of \py{duration} to run the simulation long enough for the ball to land on the ground. - -\index{density} - -The following function uses the \py{Params} object to make a \py{System} object. This two-step process makes the code more readable and makes it easier to work with functions like \py{fsolve}. - -\index{System object} -\index{\py{make_system}} - -\begin{python} -def make_system(condition): - unpack(params) - - theta = np.deg2rad(angle) - vx, vy = pol2cart(theta, velocity) - init = State(x=x, y=y, vx=vx, vy=vy) - area = np.pi * (diameter/2)**2 - - return System(params, init=init, area=area) -\end{python} - -\py{make_system} uses \py{np.deg2rad} to convert \py{angle} to radians and \py{pol2cart} to compute the $x$ and $y$ components of the initial velocity. Then it makes the initial \py{State} object, computes \py{area}, and creates the \py{System} object, which contains all of the variables in \py{params} plus \py{init} and \py{area}. - -\index{deg2rad} -\index{State object} - -Next we need a function to compute drag force: - -\begin{python} -def drag_force(v, system): - unpack(system) - mag = rho * v.mag**2 * C_d * area / 2 - direction = -v.hat() - f_drag = direction * mag - return f_drag -\end{python} - -This function differs from the one in Section~\ref{bungee} because it takes \py{v} as a \py{Vector} and returns \py{f_drag} as a \py{Vector}. It uses the drag equation to compute the magnitude of the drag force, and the \py{hat} function to compute the direction. \py{-v.hat()} computes a unit vector pointing in the opposite direction of \py{v}. - -\index{unit vector} -\index{slope function} -\index{function!slope} - -Now we're ready for a slope function: - -\begin{python} -def slope_func(state, t, system): - x, y, vx, vy = state - unpack(system) - - v = Vector(vx, vy) - a_drag = drag_force(v, system) / mass - a_grav = Vector(0, -g) - - a = a_grav + a_drag - - return vx, vy, a.x, a.y -\end{python} - -As usual, the parameters of the slope function are a \py{State} object, time, and a \py{System} object. In this example, we don't use \py{t}, but we can't leave it out because when \py{run_ode_solver} calls the slope function, it always provides the same arguments, whether they are needed or not. - -The \py{State} object contains four state variables: \py{x} and \py{y} are the components of position; \py{vx} and \py{vy} are the components of velocity. - -\index{state variable} - -The return values from the slope function are the derivatives of these components. The derivative of position is velocity, so the first two return values are just \py{vx} and \py{vy}, the values we extracted from the \py{State} object. The derivative of velocity is acceleration, and that's what we have to compute. - -\index{acceleration} -\index{velocity} -\index{position} - -The total acceleration of the baseball is the sum of accelerations due to gravity and drag. These quantities have both magnitude and direction, so they are represented by vectors \py{Vector} objects. - -We already saw how \py{a_drag} is computed. \py{a_grav} is a \py{Vector} with magnitude \py{g} pointed in the negative \py{y} direction. - -Using vectors to represent forces and accelerations makes the code concise, readable, and less error-prone. In particular, when we add \py{a_grav} and \py{a_drag}, the directions are likely to be correct, because they are encoded in the \py{Vector} objects. And the units are certain to be correct, because otherwise Pint would report an error. -\index{Pint} - -As always, we can test the slope function by running it with the initial conditions: - -\begin{python} -slope_func(system.init, 0, system) -\end{python} - -We can use an event function to stop the simulation when the ball hits the ground. - -\begin{python} -def event_func(state, t, system): - x, y, vx, vy = state - return y -\end{python} - -The event function takes the same parameters as the slope function, and returns the y coordinate. When the y coordinate passes through 0, the simulation stops. - -Now we're ready to run the simulation: - -\begin{python} -ts = linspace(0, system.t_end, 101) -results, details = run_ode_solver(system, slope_func, - events=event_func, max_step=0.2*s) -\end{python} - -\py{results} is a \py{TimeFrame} object with one column for each of the state variables, \py{x}, \py{y}, \py{vx}, and \py{vy}. - -\index{TimeFrame object} - -We can get the flight time like this: - -\begin{python} -flight_time = get_last_label(results) * s -\end{python} - -And the final \py{x} coordinate like this: - -\begin{python} -x_dist = get_last_value(results.x) * m -\end{python} - -Notice that the results don't have units; if we want these values to have the correct units, we have to apply them. - - -\section{Trajectories} - -We can plot the $x$ and $y$ components of position like this: - -\begin{python} -plot(results.x, label='x') -plot(results.y, label='y') -\end{python} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap10-fig01.pdf}} -\caption{Simulated baseball flight, $x$ and $y$ components of position as a function of time.} -\label{chap10-fig01} -\end{figure} - -Figure~\ref{chap10-fig01} shows the result. As expected, the $x$ component increases monotonically, with decreasing velocity. The $y$ position climbs initially and then descends, falling slightly below \SI{0}{\meter} after \SI{5.1}{\second}. - -\index{monotonic} - -Another way to view the same data is to plot the $x$ component on the x-axis and the $y$ component on the y-axis, so the plotted line follows the trajectory of the ball through the plane: - -\begin{python} -plot(results.x, results.y, label='trajectory') -\end{python} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap10-fig02.pdf}} -\caption{Simulated baseball flight, trajectory plot.} -\label{chap10-fig02} -\end{figure} - -Figure~\ref{chap10-fig02} shows this way of visualizing the results, which is called a {\bf trajectory plot} (see \url{http://modsimpy.com/trajec}). - -\index{trajectory plot} - -A trajectory plot can be easier to interpret than a time series plot, because it shows what the motion of the projectile would look like (at least from one point of view). Both plots can be useful, but don't get them mixed up! If you are looking at a time series plot and interpreting it as a trajectory, you will be very confused. - -Before you go on, you might want to read the notebook for this chapter, \py{chap22.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - -\chapter{Optimization} -\label{chap23} - -In the previous chapter we developed a model of the flight of a baseball, including gravity and a simple version of drag, but neglecting spin, Magnus force, and the dependence of the coefficient of drag on velocity. - -In this chapter we apply that model to an optimization problem. - -\section{The Manny Ramirez problem} -\label{manny} - -Manny Ramirez is a former member of the Boston Red Sox (an American baseball team) who was notorious for a relaxed attitude and a taste for practical jokes that his managers did not always appreciate. Our objective in this chapter is to solve the following Manny-inspired problem: - -{\it What is the minimum effort required to hit a home run in Fenway Park?} - -Fenway Park is a baseball stadium in Boston, Massachusetts. One of its most famous features is the ``Green Monster", which is a wall in left field that is unusually close to home plate, only 310 feet away. To compensate for the short distance, the wall is unusually high, at 37 feet (see \url{http://modsimpy.com/wally}). - -\index{Ramirez, Manny} -\index{Fenway Park} -\index{baseball} -\index{Green Monster} -\index{velocity} - -We want to find the minimum velocity at which a ball can leave home plate and still go over the Green Monster. We'll proceed in the following steps: - -\begin{enumerate} - -\item For a given velocity, we'll find the optimal {\bf launch angle}, that is, the angle the ball should leave home plate to maximize its height when it reaches the wall. - -\index{launch angle} - -\item Then we'll find the minimal velocity that clears the wall, given that it has the optimal launch angle. - -\end{enumerate} - -We'll use the same model as in the previous chapter, with this \py{Params} object: - -\begin{python} -params = Params(x = 0 * m, - y = 1 * m, - g = 9.8 * m/s**2, - mass = 145e-3 * kg, - diameter = 73e-3 * m, - rho = 1.2 * kg/m**3, - C_d = 0.3, - angle = 45 * degree, - velocity = 40 * m / s, - t_end = 20 * s) -\end{python} - -This version of \py{make_system}: - -\begin{python} -def make_system(condition): - unpack(params) - - theta = np.deg2rad(angle) - vx, vy = pol2cart(theta, velocity) - init = State(x=x, y=y, vx=vx, vy=vy) - area = np.pi * (diameter/2)**2 - - return System(params, init=init, area=area) -\end{python} - -This slope function: - -\begin{python} -def slope_func(state, t, system): - x, y, vx, vy = state - unpack(system) - - V = Vector(vx, vy) - a_drag = drag_force(V, system) / mass - a_grav = Vector(0, -g) - - a = a_grav + a_drag - - return vx, vy, a.x, a.y -\end{python} - -And this event function: - -\begin{python} -def event_func(state, t, system): - x, y, vx, vy = state - return y -\end{python} - - -\section{Finding the range} - -Suppose we want to find the launch angle that maximizes {\bf range}, that is, the distance the ball travels in the air before landing. We'll use a function in the \py{modsim} library, \py{max_bounded}, which takes a function and finds its maximum. - -The function we pass to \py{max_bounded} should take launch angle and a \py{params} object, and return range: - -\begin{python} -def range_func(angle, params): - params = Params(params, angle=angle) - system = make_system(params) - results, details = run_ode_solver(system, slope_func, - events=event_func) - x_dist = get_last_value(results.x) * m - return x_dist -\end{python} - -\py{range_func} makes a new \py{Params} object with the given value of \py{angle}. Then it makes a \py{System} object, calls \py{run_ode_solver}, and returns the final value of \py{x} from the results. - -We can call \py{range_func} directly like this: - -\begin{python} -range_func(45, params) -\end{python} - -And we can sweep a sequence of angles like this: - -\index{parameter sweep} -\index{SweepSeries object} - -\begin{python} -angles = linspace(20, 80, 21) -sweep = SweepSeries() - -for angle in angles: - x_dist = range_func(angle, params) - print(angle, x_dist) - sweep[angle] = x_dist -\end{python} - -\begin{figure} -\centerline{\includegraphics[height=3in]{figs/chap10-fig03.pdf}} -\caption{Distance from home plate as a function of launch angle, with fixed velocity.} -\label{chap10-fig03} -\end{figure} - -Figure~\ref{chap10-fig03} shows the results. It looks like the optimal angle is between \SI{40}{\degree} and \SI{45}{\degree}. - -We can find the optimal angle more precisely and more efficiently using \py{max_bounded}, like this: - -\begin{python} -res = max_bounded(range_func, [0, 90], params) -\end{python} - -The first parameter is the function we want to maximize. The second is the range of values we want to search; in this case it's the range of angles from \SI{0}{\degree} to \SI{90}{\degree}. The third argument can be any object; it gets passed along as an argument when \py{max_bounded} calls \py{range_func}. - -\index{Params object} - -The return value from \py{max_bounded} is an \py{ModSimSeries} that contains the results, including \py{x}, which is the angle that yielded the highest range, and \py{fun}, which is the value of \py{range_func} when it's evaluated at \py{x}, that is, range when the baseball is launched at the optimal angle. - -For these parameters, the optimal angle is \SI{41.1}{\degree}, which yields a range of \SI{103.4}{\meter}. - -\index{ModSimSeries} - - - - -\section{Finishing off the problem} - -In the notebook for this chapter, \py{chap22.ipynb}, you'll have to chance to finish off the Manny Ramirez problem. There are a few things you'll have to do: - -\begin{itemize} - -\item In the previous section the ``optimal" launch angle is the one that maximizes range, but that's not what we want. Rather, we want the angle that maximizes the height of the ball when it gets to the wall (310 feet from home plate). So you'll have to write a height function to compute it, and then use \py{max_bounded} to find the revised optimum. - -\item Once you can find the optimal angle for any velocity, you have to find the minimum velocity that gets the ball over the wall. You'll write a function that takes a velocity as a parameter, computes the optimal angle for that velocity, and returns the height of the ball, at the wall, using the optimal angle. - -\item Finally, you'll use \py{fsolve} to find the velocity that makes the optimal height at the wall just barely 37 feet. - -\index{\py{fsolve}} - -\end{itemize} - -The notebook provides some additional hints, but at this point you should have everything you need. Good luck! - -If you enjoy this exercise, you might be interested in this paper: ``How to hit home runs: Optimum baseball bat swing parameters for maximum range trajectories", by Sawicki, Hubbard, and Stronge, at \url{http://modsimpy.com/runs}. - - -\chapter{Rotation} -\label{chap24} - -In this chapter we model systems that involve rotating objects. In general, rotation is complicated: in three dimensions, objects can rotate around three axes; objects are often easier to spin around some axes than others; and they may be stable when spinning around some axes but not others. - -\index{rotation} - -If the configuration of an object changes over time, it might become easier or harder to spin, which explains the surprising dynamics of gymnasts, divers, ice skaters, etc. - -And when you apply a twisting force to a rotating object, the effect is often contrary to intuition. For an example, see this video on gyroscopic precession \url{http://modsimpy.com/precess}. - -\index{gyroscopic precession} - -In this chapter, we will not take on the physics of rotation in all its glory. Rather, we will focus on simple scenarios where all rotation and all twisting forces are around a single axis. In that case, we can treat some vector quantities as if they were scalars (in the same way that we sometimes treat velocity as a scalar with an implicit direction). - -\index{scalar} - -This approach makes it possible to simulate and analyze many interesting systems, but you will also encounter systems that would be better approached with the more general toolkit. - -The fundamental ideas in this chapter and the next are {\bf angular velocity}, {\bf angular acceleration}, {\bf torque}, and {\bf moment of inertia}. If you are not already familiar with these concepts, I will define them as we go along, and I will point to additional reading. - -At the end of the next chapter, you will use these tools to simulate the behavior of a yo-yo (see \url{http://modsimpy.com/yoyo}). But we'll work our way up to it gradually, starting with toilet paper. - - - -\section{The physics of toilet paper} -\label{paper} - -As a simple example of a system with rotation, we'll simulate the manufacture of a roll of toilet paper. Starting with a cardboard tube at the center, we will roll up \SI{47}{\meter} of paper, the typical length of a roll of toilet paper in the U.S. (see \url{http://modsimpy.com/paper}). - -\index{toilet paper} - -\begin{figure} -\centerline{\includegraphics[height=2.5in]{figs/paper_roll.pdf}} -\caption{Diagram of a roll of toilet paper, showing change in paper length as a result of a small rotation, $d\theta$.} -\label{paper_roll} -\end{figure} - -Figure~\ref{paper_roll} shows a diagram of the system: $r$ represents the radius of the roll at a point in time. Initially, $r$ is the radius of the cardboard core, $R_{min}$. When the roll is complete, $r$ is $R_{max}$. - -I'll use $\theta$ to represent the total rotation of the roll in radians. In the diagram, $d\theta$ represents a small increase in $\theta$, which corresponds to a distance along the circumference of the roll of $r~d\theta$. - -\index{radian} - -Finally, I'll use $y$ to represent the total length of paper that's been rolled. Initially, $\theta=0$ and $y=0$. For each small increase in $\theta$, there is a corresponding increase in $y$: -% -\[ dy = r~d\theta \] -% -If we divide both sides by a small increase in time, $dt$, we get a differential equation for $y$ as a function of time. -% -\[ \frac{dy}{dt} = r \frac{d\theta}{dt} \] -% -As we roll up the paper, $r$ increases, too. Assuming that $r$ increases by a fixed amount per revolution, we can write -% -\[ dr = k~d\theta \] -% -Where $k$ is an unknown constant we'll have to figure out. Again, we can divide both sides by $dt$ to get a differential equation in time: -% -\[ \frac{dr}{dt} = k \frac{d\theta}{dt} \] -% -Finally, let's assume that $\theta$ increases at a constant rate of \SI{10}{\radian\per\second} (about 95 revolutions per minute): -% -\[ \frac{d\theta}{dt} = 10 \] -% -This rate of change is called an {\bf angular velocity}. Now we have a system of three differential equations we can use to simulate the system. - -\index{angular velocity} -\index{differential equation} - - -\section{Implementation} -\label{papersim} - -At this point we have a pretty standard process for writing simulations like this. First, we'll get the units we need from Pint: -\index{Pint} - -\begin{python} -radian = UNITS.radian -m = UNITS.meter -s = UNITS.second -\end{python} - -And create a \py{Params} object with the parameters of the system: - -\index{Params object} - -\begin{python} -params = Params(Rmin = 0.02 * m, - Rmax = 0.055 * m, - L = 47 * m, - t_end = 130 * s) -\end{python} - -\py{Rmin} and \py{Rmax} are the initial and final values for the radius, \py{r}. \py{L} is the total length of the paper, and \py{t_end} is the length of the simulation in time. - -Then we use the \py{Params} object to make a \py{System} object: - -\index{System object} -\index{\py{make_system}} - -\begin{python} -def make_system(params): - unpack(params) - - init = State(theta = 0 * radian, - y = 0 * m, - r = Rmin) - - k = estimate_k(params) - - return System(init=init, k=k, t_end=t_end) -\end{python} - -The initial state contains three variables, \py{theta}, \py{y}, and \py{r}. - -\index{unpack} - -To get started, we'll estimate a reasonable value for \py{k}; then in Section~\ref{paper_analysis} we'll figure it out exactly. Here's how we compute the estimate: - -\begin{python} -def estimate_k(params): - unpack(params) - - Ravg = (Rmax + Rmin) / 2 - Cavg = 2 * pi * Ravg - revs = L / Cavg - rads = 2 * pi * revs - k = (Rmax - Rmin) / rads - return k -\end{python} - -\py{Ravg} is the average radius, half way between \py{Rmin} and \py{Rmax}, so \py{Cavg} is the circumference of the roll when \py{r} is \py{Ravg}. - -\py{revs} is the total number of revolutions it would take to roll up length \py{L} if \py{r} were constant at \py{Ravg}. And \py{rads} is just \py{revs} converted to radians. - -Finally, \py{k} is the change in \py{r} for each radian of revolution. For these parameters, \py{k} is about \py{2.8e-5} \si{\meter\per\radian}. - -Now we can use the differential equations from Section~\ref{paper} to write a slope function: - -\index{slope function} -\index{Function!slope} - -\begin{python} -def slope_func(state, t, system): - theta, y, r = state - unpack(system) - - omega = 10 * radian / s - dydt = r * omega - drdt = k * omega - - return omega, dydt, drdt -\end{python} - -\begin{figure}[t] -\centerline{\includegraphics[height=4.5in]{figs/chap11-fig01.pdf}} -\caption{Results from paper rolling simulation, showing rotation, length, and radius over time.} -\label{chap11-fig01} -\end{figure} - -As usual, the slope function takes a \py{State} object, a time, and a \py{System} object. The \py{State} object contains hypothetical values of \py{theta}, \py{y}, and \py{r} at time \py{t}. The job of the slope function is to compute the time derivatives of these values. The time derivative of \py{theta} is angular velocity, which is often denoted \py{omega}. - -\index{State object} - -We'd like to stop the simulation when the length of paper on the roll is \py{L}. We can do that with an event function that passes through 0 when \py{y} equals \py{L}: - -\begin{python} -def event_func(state, t, system): - theta, y, r = state - unpack(system) - return y - L -\end{python} - -Now we can run the simulation like this: - -\begin{python} -results, details = run_ode_solver(system, slope_func, - events=event_func, max_step=1*s) -\end{python} - - -Figure~\ref{chap11-fig01} shows the results. \py{theta} grows linearly over time, as we should expect. As a result, \py{r} also grows linearly. But since the derivative of \py{y} depends on \py{r}, and \py{r} is increasing, \py{y} grows with increasing slope. - -Because this system is so simple, it is almost silly to simulate it. As we'll see in the next section, it is easy enough to solve the differential equations analytically. But it is often useful to start with a simple simulation as a way of exploring and checking assumptions. - -In order to get the simulation working, we have to get the units right, which can help catch conceptual errors early. And by plugging in realistic parameters, we can detect errors that cause unrealistic results. For example, in this system we can check: - -\begin{itemize} - -\item The total time for the simulation is about 2 minutes, which seems plausible for the time it would take to roll \SI{47}{\meter} of paper. - -\item The final value of \py{theta} is about \SI{1250}{\radian}, which corresponds to about 200 revolutions, which also seems plausible. - -\item The initial and final values for \py{r} are consistent with \py{Rmin} and \py{Rmax}, as we intended when we chose \py{k}. - -\end{itemize} - -But now that we have a working simulation, it is also useful to do some analysis. - - -\section{Analysis} -\label{paper_analysis} - -The differential equations in Section~\ref{paper} are simple enough that we can just solve them. Since angular velocity is constant: -% -\[ \frac{d\theta}{dt} = \omega \] -% -We can find $\theta$ as a function of time by integrating both sides: -% -\[ \theta(t) = \omega t + C_1 \] -% -With the initial condition $\theta(0)=0$, we find $C_1=0$. Similarly, -% -\begin{equation} -\frac{dr}{dt} = k \omega \label{eqn1} -\end{equation} -% -So -% -\[ r(t) = k \omega t + C_2 \] -% -With the initial condition $r(0)=R_{min}$, we find $C_2=R_{min}$. Then we can plug the solution for $r$ into the equation for $y$: -% -\begin{align} -\frac{dy}{dt} & = r \omega \label{eqn2} \\ - & = \left[ k \omega t + R_{min} \right] \omega \nonumber -\end{align} -% -% -Integrating both sides yields: -% -\[ y(t) = \left[ k \omega t^2 / 2 + R_{min} t \right] \omega + C_3\] -% -So $y$ is a parabola, as you might have guessed. With initial condition $y(0)=0$, we find $C_3=0$. - -\index{analysis} -\index{integration} - -We can also use these equations to find the relationship between $y$ and $r$, independent of time, which we can use to compute $k$. Using a move we saw in Section~\ref{contact}, I'll divide Equations~\ref{eqn1} and \ref{eqn2}, yielding -% -\[ \frac{dr}{dy} = \frac{k}{r}\] -% -Separating variables yields -% -\[ r~dr = k~dy\] -% -Integrating both sides yields -% -\[ r^2 / 2 = k y + C \] -% -When $y=0$, $r=R_{min}$, so -% -\[ C = \frac{1}{2} R_{min}^2 \] -% -Solving for $y$, we have -% -\begin{equation} -y = \frac{1}{2k} (r^2 - R_{min}^2) \label{eqn3} -\end{equation} -% -When $y=L$, $r=R_{max}$; substituting in those values yields -% -\[ L = \frac{1}{2k} (R_{max}^2 - R_{min}^2) \] -% -Solving for $k$ yields -% -\begin{equation} -k = \frac{1}{2L} (R_{max}^2 - R_{min}^2) \label{eqn4} -\end{equation} -% -Plugging in the values of the parameters yields \py{2.8e-5} \si{\meter\per\radian}, the same as the ``estimate" we computed in Section~\ref{papersim}. In this case the estimate turns out to be exact. - -In the next chapter, we'll run the simulation the other way, unrolling the paper. - -Before you go on, you might want to read the notebook for this chapter, \py{chap24.ipynb}, and work on the exercises. For instructions on downloading and running the code, see Section~\ref{code}. - - -\chapter{Torque} -\label{chap25} - -In the previous chapter we modeled a scenario with constant angular velocity. In this chapter we make it more complex; we'll model a teapot, on a turntable, revolving with constant angular acceleration and deceleration. - - -\section{Angular acceleration} - -\index{angular acceleration} -\index{torque} - -Just as linear acceleration is the derivative of velocity, {\bf angular acceleration} is the derivative of angular velocity. And just as linear acceleration is caused by force, angular acceleration is caused by the rotational version of force, {\bf torque}. If you are not familiar with torque, you can read about it at \url{http://modsimpy.com/torque}. - -In general, torque is a vector quantity, defined as the {\bf cross product} of $\vec{r}$ and $\vec{F}$, where $\vec{r}$ is the {\bf lever arm}, a vector from the point of rotation to the point where the force is applied, and $\vec{F}$ is the vector that represents the magnitude and direction of the force. - -\index{vector} -\index{lever arm} -\index{cross product} - -However, for the problems in this chapter, we only need the {\em magnitude} of torque; we don't care about the direction. In that case, we can compute -% -\[ \tau = r F \sin \theta \] -% -where $\tau$ is torque, $r$ is the length of the lever arm, $F$ is the magnitude of force, and $\theta$ is the angle between $\vec{r}$ and $\vec{F}$. - -\index{magnitude} - -Since torque is the product of a length and a force, it is expressed in newton meters (\si{\newton\meter}). - - -\section{Moment of inertia} - -In the same way that linear acceleration is related to force by Newton's second law of motion, $F=ma$, angular acceleration is related to torque by another form of Newton's law: -% -\[ \tau = I \alpha \] -% -Where $\alpha$ is angular acceleration and $I$ is {\bf moment of inertia}. Just as mass is what makes it hard to accelerate an object\footnote{That might sound like a dumb way to describe mass, but its actually one of the fundamental definitions.}, moment of inertia is what makes it hard to spin an object. - -\index{mass} -\index{moment of inertia} - -In the most general case, a 3-D object rotating around an arbitrary axis, moment of inertia is a tensor, which is a function that takes a vector as a parameter and returns a vector as a result. - -\index{tensor} - -Fortunately, in a system where all rotation and torque happens around a single axis, we don't have to deal with the most general case. We can treat moment of inertia as a scalar quantity. - -\index{scalar} - -For a small object with mass $m$, rotating around a point at distance $r$, the moment of inertia is $I = m r^2$, in SI units \si{\kilogram\meter\squared}. For more complex objects, we can compute $I$ by dividing the object into small masses, computing moments of inertia for each mass, and adding them up. - -However, for most simple shapes, people have already done the calculations; you can just look up the answers. For example, see \url{http://modsimpy.com/moment}. - - -\section{Teapots and turntables} - -Tables in Chinese restaurants often have a rotating tray or turntable -that makes it easy for customers to share dishes. These turntables are -supported by low-friction bearings that allow them to turn easily and -glide. However, they can be heavy, especially when they are loaded with -food, so they have a high moment of inertia. - -\index{teapot} -\index{turntable} - -Suppose I am sitting at a table with a pot of tea on the turntable -directly in front of me, and the person sitting directly opposite asks -me to pass the tea. I push on the edge of the turntable with \SI{1}{\newton} of force until it has turned \SI{0.5}{\radian}, then let go. The turntable glides until it comes to a stop \SI{1.5}{\radian} from the starting position. How much force should I apply for a second push so the teapot glides to a -stop directly opposite me? - -\index{force} -\index{Newton} -\index{friction} - -We'll answer this question in these steps: - -\begin{enumerate} - -\item - I'll use the results from the first push to estimate the coefficient - of friction for the turntable. - -\item - As an exercise, you'll use that coefficient of friction to estimate the force needed to rotate the turntable through the remaining angle. - -\end{enumerate} - -Our simulation will use the following parameters: - -\begin{enumerate} -\item - The radius of the turntable is \SI{0.5}{\meter}, and its weight is \SI{7}{\kg}. -\item - The teapot weights \SI{0.3}{\kg}, and it sits \SI{0.4}{\meter} from the center of - the turntable. -\end{enumerate} - -\begin{figure} -\centerline{\includegraphics[height=2.5in]{figs/teapot.pdf}} -\caption{Diagram of a turntable with a teapot.} -\label{teapot} -\end{figure} - -Figure~\ref{teapot} shows the scenario, where $F$ is the force I apply to the turntable at the perimeter, perpendicular to the moment arm, $r$, and $\tau$ is the resulting torque. The blue circle near the bottom is the teapot. - -Here's a \py{Params} object with these values - -\begin{python} -params = Params(radius_disk=0.5*m, - mass_disk=7*kg, - radius_pot=0.4*m, - mass_pot=0.3*kg, - force=1*N, - torque_friction=0.2*N*m, - theta_end=0.5*radian) -\end{python} - -\index{Params object} - -\py{make_system} creates the initial state, \py{init}, and -computes the total moment of inertia for the turntable and the teapot. - -\begin{python} -def make_system(params): - unpack(params) - - init = State(theta=0, omega=0) - - I_disk = mass_disk * radius_disk**2 / 2 - I_pot = mass_pot * radius_pot**2 - - return System(params, init=init, t_end=20*s, - I=I_disk+I_pot) -\end{python} - -%\index{make_system} - -\py{theta} represents the initial angle of the table, in \si{\radian}; \py{omega} represents the angular velocity in \si{\radian\per\second}. - -\py{I_disk} is the moment of inertia of the turntable, which is based on the moment of inertia for a horizontal disk revolving around a vertical axis through its center: -% -\[ I_{disk} = m r^2 / 2 \] -% -\py{I_pot} is the moment of inertia of the teapot, which I treat as a point mass with: -% -\[ I_{point} = m r^2 \] -% -Now we can make a \py{System} object: - -\begin{python} -system1 = make_system(params) -\end{python} - -\index{System object} - -Here's a slope that takes the current state, which contains angle and angular velocity, and returns the derivatives, angular velocity and angular acceleration: - -\begin{python} -def slope_func(state, t, system): - theta, omega = state - unpack(system) - - torque = radius_disk * force - torque_friction - alpha = torque / I - - return omega, alpha -\end{python} - -\index{slope function} - -In this scenario, the force I apply to the turntable is always perpendicular to the lever arm, so $\sin \theta = 1$ and the torque due to force is $\tau = r F$. - -In a more detailed model, I might quantify force due to friction and the moment arm of that force, but that's not really necessary; instead, I chose to quantify torque due to friction, leaving out the details. - -\index{friction} - -Now we are ready to run the simulation, but first there's a problem we have to address. - -When I stop pushing on the turntable, the angular acceleration changes -abruptly. We could implement the slope function with an \py{if} -statement that checks the value of \py{theta} and sets -\py{force} accordingly. And for a coarse model like this one, that -might be fine. But we will get more accurate results if we simulate the -system in two phases: - -\begin{enumerate} -\item - During the first phase, force is constant, and we run until - \py{theta} is 0.5 radians. -\item - During the second phase, force is 0, and we run until \py{omega} - is 0. -\end{enumerate} - -Then we can combine the results of the two phases into a single -\py{TimeFrame}. - -\index{two-phase simulation} - -Here's the event function I'll use for Phase 1; it stops the simulation when \py{theta} reaches \py{theta_end}, which is when I stop pushing: - -\begin{python} -def event_func1(state, t, system): - theta, omega = state - unpack(system) - return theta - theta_end -\end{python} - -Now we can run the first phase. - -\begin{python} -results1, details1 = run_ode_solver(system1, slope_func, - events=event_func1, max_step=0.1*s) -\end{python} - -%\index{run_ode_solver} -%\index{max_step} - -Again, I specify \py{max_step} so the results look smoother when I plot them, at the cost of some extra computation. - -\begin{figure} -\centerline{\includegraphics[height=4.0in]{figs/chap25-fig01.pdf}} -\caption{Angle and angular velocity of a turntable with applied force and friction.} -\label{chap25-fig01} -\end{figure} - -Before we run the second phase, we have to extract the final time and -state of the first phase. - -\begin{python} -t_0 = get_last_label(results1) * s -theta, omega = get_last_value(results1) -init2 = State(theta=theta*radian, omega=omega*radian/s) -\end{python} - -Now we can make a \py{System} object for Phase 2, with the initial state from Phase 1, and with \py{force=0}. - -%\index{get_last_label} -%\index{get_last_value} - -\begin{python} -system2 = System(system1, t_0=t_0, init=init2, force=0) -\end{python} - -For the second phase, we need an event function that stops when the turntable stops; that is, when angular velocity is 0. - -\begin{python} -def event_func2(state, t, system): - theta, omega = state - return omega -\end{python} - -Now we can run the second phase. - -\begin{python} -results2, details2 = run_ode_solver(system2, slope_func, - events=event_func2, max_step=0.1*s) -\end{python} - -Pandas provides \py{combine_first}, which combines -\py{results1} and \py{results2}. - -\index{Pandas} - -\begin{python} -results = results1.combine_first(results2) -\end{python} - -Figure~\ref{chap25-fig01} shows the results. Angular velocity, \py{omega}, increases linearly while I am pushing, and decreases linearly after I let go. The angle, \py{theta}, is the integral of angular velocity, so it forms a parabola during each phase. - -In the next section, we'll use this simulation to estimate the torque due to friction. - - -\section{Estimating friction} - -Let's take the code from the previous section and wrap it in a function. - -\index{function} - -\begin{python} -def run_two_phases(force, torque_friction, params): - params = Params(params, force=force, - torque_friction=torque_friction) - - # run phase 1 - system1 = make_system(params) - results1, _ = run_ode_solver(system1, slope_func, - events=event_func1, max_step=0.1*s) - - # get the final state from phase 1 - t_0 = get_last_label(results1) * s - theta, omega = get_last_value(results1) - init2 = State(theta=theta, omega=omega) - - # run phase 2 - system2 = System(system1, t_0=t_0, init=init2, force=0) - results2, _ = run_ode_solver(system2, slope_func, - events=event_func2, max_step=0.1*s) - - # combine and return the results - results = results1.combine_first(results2) - return results -\end{python} - -Now we can use \py{run_two_phases} to write an error function we can use, with \py{fsolve}, to find the torque due to friction that yields the observed results from the first push, a total rotation of \SI{1.5}{\radian}. - -\index{\py{fsolve}} -\index{error function} - -\begin{python} -def error_func1(torque_friction, params): - force = 1 - results = run_two_phases(force, torque_friction, params) - theta_final = get_last_value(results.theta) - print(torque_friction, theta_final) - return theta_final - 1.5 -\end{python} - -But before we call \py{fsolve}, we have to deal with a problem. In order to support computation with units, the \py{modsim} library uses a certain amount of black magic. With most units, the magic works pretty well, but some units are problematic, including Newtons. - -\index{\py{fsolve}} - -However, now that we have a working simulation, and some confidence that it is correct, it is less important to carry units through the computation. - -We can drop them by creating a new \py{Params} object with the same values and no units. The \py{modsim} library provides \py{remove_unit}, which does just that. - -\begin{python} -params_nodim = remove_units(params) -\end{python} - -%\index{remove_units} - -The rest of the code works without modification, in part because I designed it to keep almost all of the units in one place. - -Now we can use \py{fsolve} to estimate torque due to friction. - -\index{torque} -\index{friction} -\index{\py{fsolve}} - -\begin{python} -guess = 0.2 -res = fsolve(error_func1, guess, params_nodim) -\end{python} - -The result is \SI{0.166}{\newton\meter}, a little less than the initial guess. - -Now that we know the torque due to friction, we can compute the force needed to rotate the turntable through the remaining angle, that is, from \SI{1.5}{\radian} to \SI{3.14}{\radian}. - -In the notebook for this chapter, \py{chap25.ipynb}, you will have a chance to finish off the exercise. For instructions on downloading and running the code, see Section~\ref{code}. - - -\chapter{Case studies} -\label{chap26} - -\section{Computational tools} - -In Chapter~\ref{chap20} we rewrote a second order differential equation as a system of first order equations, and solved them using a slope function like this: - -\begin{python} -def slope_func(state, t, system): - y, v = state - unpack(system) - - dydt = v - dvdt = -g - - return dydt, dvdt -\end{python} - -We used the \py{crossings} function to search for zero-crossings in the simulation results. - -Then we used an event function like this: - -\begin{python} -def event_func(state, t, system): - y, v = state - return y -\end{python} - -To stop the simulation when an event occurs. Notice that the event function takes the same parameters as the slope function. - -In Chapter~\ref{chap21} we developed a model of air resistance and used a \py{Params} object, which is a collection of parameters: - -\begin{python} -params = Params(height = 381 * m, - v_init = 0 * m / s, - g = 9.8 * m/s**2, - mass = 2.5e-3 * kg, - diameter = 19e-3 * m, - rho = 1.2 * kg/m**3, - v_term = 18 * m / s) -\end{python} - -And we saw a new way to create a \py{System} object, copying the variables from a \py{Params} object and adding or changing variables: - -\begin{python} - return System(params, area=area, C_d=C_d, - init=init, t_end=t_end) -\end{python} - -We also used the \py{gradient} function to estimate acceleration, given velocity: - -\begin{python} -a = gradient(results.v) -\end{python} - -Chapter~\ref{chap22} introduces \py{Vector} objects, which can represent vector quantities, like position, velocity, force, and acceleration, in 2 or 3 dimensions. - -\begin{python} -A = Vector(3, 4) * m -\end{python} - -It also introduces trajectory plots, which show the path of an object in two dimensions: - -\begin{python} -plot(results.x, results.y, label='trajectory') -\end{python} - -In Chapter~\ref{chap23} we define a range function that computes the distance a baseball flies as a function of launch angle: - -\begin{python} -def range_func(angle, params): - params = Params(params, angle=angle) - system = make_system(params) - results, details = run_ode_solver(system, slope_func, - events=event_func) - x_dist = get_last_value(results.x) * m - return x_dist -\end{python} - -Then we use \py{max_bounded} to find the launch angle that maximizes range: - -\begin{python} -res = max_bounded(range_func, [0, 90], params) -\end{python} - -With that, your toolkit is complete. Chapter~\ref{chap24} and Chapter~\ref{chap25} introduce the physics of rotation, but no new computational tools. - - -\section{Under the hood} - -The \py{crossings} function uses \py{InterpolatedUnivariateSpline} from SciPy, which provides a \py{roots} function that finds zero-crossings. It uses a cubic spline to interpolate between the time steps of the ODE solution. - -\index{crossings} - -The \py{Params} object is identical to the \py{System} object in all but name, and based on a Pandas \py{Series}. I sometimes find it useful to use \py{Params} objects to create \py{System} objects, but it is not necessary. - -\index{Params} - -The \py{Vector} object is a \py{Quantity}, as defined by Pint, so it normally has units. It contains a NumPy array that contains the coordinates. So most vector arithmetic is really array arithmetic. - -\index{Vector} - -\py{max_bounded} uses the SciPy function \py{minimize_scalar}, which uses Brent's method. You can read about it at \url{http://modsimpy.com/minimize}). - -\index{\py{max_bounded}} -\index{SciPy} -\index{\py{minimize_scalar}} -\index{Brent's method} - - -\section{Bungee dunk revisited} - -In Chapter~\ref{chap21}, we simulated a bungee jump with a model that took into account gravity, air resistance, and the spring force of the bungee cord, but we ignored the weight of the cord. - -\index{bungee jump} -\index{bungee cord} - -It is tempting to say that the cord has no effect because it falls along with the jumper, but that intuition is incorrect. As the cord falls, it transfers energy to the jumper. - -At \url{http://modsimpy.com/bungee} you'll find a paper\footnote{Heck, Uylings, and Kędzierska, ``Understanding the physics of bungee jumping", Physics Education, Volume 45, Number 1, 2010.} that explains this phenomenon and derives the acceleration of the jumper, $a$, as a function of position, $y$, and velocity, $v$: -% -\[ a = g + \frac{\mu v^2/2}{\mu(L+y) + 2L} \] -% -where $g$ is acceleration due to gravity, $L$ is the length of the cord, and $\mu$ is the ratio of the mass of the cord, $m$, and the mass of the jumper, $M$. - -If you don't believe that their model is correct, this video might convince you: \url{http://modsimpy.com/drop}. - -Modify the code from Chapter~\ref{chap21} to model this effect. How does the behavior of the system change as we vary the mass of the cord? When the mass of the cord equals the mass of the jumper, what is the net effect on the lowest point in the jump? - - -\section{Spiderman} - -In this case study we'll develop a model of Spider-Man swinging from a -springy cable of webbing attached to the top of the Empire State -Building. Initially, Spider-Man is at the top of a nearby building, as -shown in Figure~\ref{spiderman}. - -\index{Spider-man} -\index{Empire State Building} - -\begin{figure} -\centerline{\includegraphics[height=2.8in]{figs/spiderman.pdf}} -\caption{Diagram of the initial state for the Spider-Man case study.} -\label{spiderman} -\end{figure} - -The origin, \texttt{O}, is at the base of the Empire State Building. The -vector \py{H} represents the position where the webbing is attached -to the building, relative to \py{O}. The vector \py{P} is the -position of Spider-Man relative to \py{O}. And \py{L} is the -vector from the attachment point to Spider-Man. - -\index{vector} - -By following the arrows from \py{O}, along \py{H}, and along -\py{L}, we can see that - -\begin{code} -H + L = P -\end{code} - -So we can compute \py{L} like this: - -\begin{code} -L = P - H -\end{code} - -The goals of this case study are: - -\begin{enumerate} - -\item - Implement a model of this scenario to predict Spider-Man's trajectory. -\index{trajectory} - -\item - Choose the right time for Spider-Man to let go of the webbing in order - to maximize the distance he travels before landing. -\index{range} - -\item - Choose the best angle for Spider-Man to jump off the building, and let - go of the webbing, to maximize range. -\index{optimization} - -\end{enumerate} - -We'll use the following parameters: -\index{parameter} - -\begin{enumerate} - -\item According to the Spider-Man Wiki\footnote{\url{http://modsimpy.com/spider}}, Spider-Man weighs \SI{76}{\kg}. - -\item - Let's assume his terminal velocity is \SI{60}{\meter\per\second}. -\index{terminal velocity} - -\item - The length of the web is \SI{100}{\meter}. - -\item - The initial angle of the web is \SI{45}{\degree} to the left of straight - down. - -\item - The spring constant of the web is \SI{40}{\newton\per\meter} when the cord is stretched, and 0 when it's compressed. - -\end{enumerate} - -In the repository for this book, you will find a notebook, \py{spiderman.ipynb}, which contains starter code. Read through the notebook and run the code. It uses \py{minimize}, which is a SciPy function that can search for an optimal set of parameters (as contrasted with \py{min_bounded}, which can only search along a single axis). - - -\section{Kittens} - -Let's simulate a kitten unrolling toilet paper. As reference material, see this video: \url{http://modsimpy.com/kitten}. - -\index{kitten} - -The interactions of the kitten and the paper roll are complex. To keep things simple, let's assume that the kitten pulls down on the free end of the roll with constant force. Also, we will neglect the friction between the roll and the axle. - -\begin{figure} -\centerline{\includegraphics[height=2.5in]{figs/kitten.pdf}} -\caption{Diagram of a roll of toilet paper, showing a force, lever arm, and the resulting torque.} -\label{kitten} -\end{figure} - -Figure~\ref{kitten} shows the paper roll with $r$, $F$, and $\tau$. As a vector quantity, the direction of $\tau$ is into the page, but we only care about its magnitude for now. - -Here's the \py{Params} object with the parameters we'll need: - -\index{Params object} - -\begin{python} -params = Params(Rmin = 0.02 * m, - Rmax = 0.055 * m, - Mcore = 15e-3 * kg, - Mroll = 215e-3 * kg, - L = 47 * m, - tension = 2e-4 * N, - t_end = 180 * s) -\end{python} - -As before, \py{Rmin} is the minimum radius and \py{Rmax} is the maximum. \py{L} is the length of the paper. \py{Mcore} is the mass of the cardboard tube at the center of the roll; \py{Mroll} is the mass of the paper. \py{tension} is the force applied by the kitten, in \si{\newton}. I chose a value that yields plausible results. - -At \url{http://modsimpy.com/moment} you can find moments of inertia for simple geometric shapes. I'll model the cardboard tube at the center of the roll as a ``thin cylindrical shell", and the paper roll as a ``thick-walled cylindrical tube with open ends". - -\index{cylinder} - -The moment of inertia for a thin shell is just $m r^2$, where $m$ is the mass and $r$ is the radius of the shell. - -For a thick-walled tube the moment of inertia is -% -\[ I = \frac{\pi \rho h}{2} (r_2^4 - r_1^4) \] -% -where $\rho$ is the density of the material, $h$ is the height of the tube, $r_2$ is the outer diameter, and $r_1$ is the inner diameter. - -Since the outer diameter changes as the kitten unrolls the paper, we have to compute the moment of inertia, at each point in time, as a function of the current radius, \py{r}. Here's the function that does it: - -\index{unpack} - -\begin{python} -def moment_of_inertia(r, system): - unpack(system) - Icore = Mcore / 2 * Rmin**2 - Iroll = pi * rho_h / 2 * (r**4 - Rmin**4) - return Icore + Iroll -\end{python} - -\py{rho_h} is the product of density and height, $\rho h$, which is the mass per area. \py{rho_h} is computed in \py{make_system}: - -\index{density} -\index{\py{make_system}} - -\begin{python} -def make_system(params): - unpack(params) - - init = State(theta = 0 * radian, - omega = 0 * radian/s, - y = L) - area = pi * (Rmax**2 - Rmin**2) - rho_h = Mroll / area - k = (Rmax**2 - Rmin**2) / 2 / L / radian - - return System(init=init, k=k, rho_h=rho_h, - Rmin=Rmin, Rmax=Rmax, - Mcore=Mcore, Mroll=Mroll, - t_end=t_end) -\end{python} - -\py{make_system} also computes \py{k} using Equation~\ref{eqn4}. - -In the repository for this book, you will find a notebook, \py{kitten.ipynb}, which contains starter code for this case study. Use it to implement this model and check whether the results seem plausible. - - -\section{Simulating a yo-yo} - -Suppose you are holding a yo-yo with a length of string wound around its axle, and you drop it while holding the end of the string stationary. As gravity accelerates the yo-yo downward, tension in the string exerts a force upward. Since this force acts on a point offset from the center of mass, it exerts a torque that causes the yo-yo to spin. - -\index{yo-yo} -\index{torque} -\index{lever arm} - -\begin{figure} -\centerline{\includegraphics[height=2.5in]{figs/yoyo.pdf}} -\caption{Diagram of a yo-yo showing forces due to gravity and tension in the string, the lever arm of tension, and the resulting torque.} -\label{yoyo} -\end{figure} - -Figure~\ref{yoyo} is a diagram of the forces on the yo-yo and the resulting torque. The outer shaded area shows the body of the yo-yo. The inner shaded area shows the rolled up string, the radius of which changes as the yo-yo unrolls. - -\index{system of equations} - -In this model, we can't figure out the linear and angular acceleration independently; we have to solve a system of equations: -% -\begin{align*} -\sum F &= m a \\ -\sum \tau &= I \alpha -\end{align*} -% -where the summations indicate that we are adding up forces and torques. - -As in the previous examples, linear and angular velocity are related because of the way the string unrolls: -% -\[ \frac{dy}{dt} = -r \frac{d \theta}{dt} \] -% -In this example, the linear and angular accelerations have opposite sign. As the yo-yo rotates counter-clockwise, $\theta$ increases and $y$, which is the length of the rolled part of the string, decreases. - -Taking the derivative of both sides yields a similar relationship between linear and angular acceleration: -% -\[ \frac{d^2 y}{dt^2} = -r \frac{d^2 \theta}{dt^2} \] -% -Which we can write more concisely: -% -\[ a = -r \alpha \] -% -This relationship is not a general law of nature; it is specific to scenarios like this where there is rolling without stretching or slipping. - -\index{rolling} - -Because of the way we've set up the problem, $y$ actually has two meanings: it represents the length of the rolled string and the height of the yo-yo, which decreases as the yo-yo falls. Similarly, $a$ represents acceleration in the length of the rolled string and the height of the yo-yo. - -We can compute the acceleration of the yo-yo by adding up the linear forces: -% -\[ \sum F = T - mg = ma \] -% -Where $T$ is positive because the tension force points up, and $mg$ is negative because gravity points down. - -Because gravity acts on the center of mass, it creates no torque, so the only torque is due to tension: -% -\[ \sum \tau = T r = I \alpha \] -% -Positive (upward) tension yields positive (counter-clockwise) angular acceleration. - -\index{SymPy} - -Now we have three equations in three unknowns, $T$, $a$, and $\alpha$, with $I$, $m$, $g$, and $r$ as known quantities. It is simple enough to solve these equations by hand, but we can also get SymPy to do it for us: - -\begin{python} -T, a, alpha, I, m, g, r = symbols('T a alpha I m g r') -eq1 = Eq(a, -r * alpha) -eq2 = Eq(T - m*g, m * a) -eq3 = Eq(T * r, I * alpha) -soln = solve([eq1, eq2, eq3], [T, a, alpha]) -\end{python} - -The results are -% -\begin{align*} -T &= m g I / I^* \\ -a &= -m g r^2 / I^* \\ -\alpha &= m g r / I^* \\ -\end{align*} -% -where $I^*$ is the augmented moment of inertia, $I + m r^2$. -To simulate the system, we don't really need $T$; we can plug $a$ and $\alpha$ directly into the slope function. - -In the repository for this book, you will find a notebook, \py{yoyo.ipynb}, which contains the derivation of these equations and starter code for this case study. Use it to implement and test this model. - - -%\section{Rigid pendulum} - -%\section{LRC circuit} - -% Pendulum: - -% Springy pendulum - -% Stiff problem as k increases - -% Add drag - -% Rigid pendulum: solve those constraints - -% Generalized coordinates - - -\backmatter -\printindex - -\afterpage{\blankpage} - - -\end{document} - -\end{itemize} - - -\section{Under the hood} - -Throughout this book, we'll use functions defined in the \py{modsim} library. You don't have to know how they work, but you might be curious. So at the end of some chapters I'll provide additional information. If you are an experienced programmer, you might be interested by the design decisions I made. If you are a beginner, and you feel like you have your hands full already, feel free to skip these sections. - -\index{modsim} - -Most of the functions in \py{modsim} are based on other Python libraries; the libraries we have used so far include: - -\begin{itemize} - -\item {\bf Pint}, which provides units like meters and seconds, as we saw in Section~\ref{penny}. - -\item {\bf NumPy}, which provides mathematical operations like \py{sqrt}, which we saw in Section~\ref{computation}. - -\item {\bf Pandas}, which provides the \py{Series} object, which is the basis of the \py{State} object in Section~\ref{modeling}. - -\item {\bf Pyplot}, which provides plotting functions, as we saw in Section~\ref{plotting}. - -\end{itemize} - -You could use these libraries directly, and when you have more experience, you probably will. But the functions in \py{modsim} are meant to be easier to use; they provide some additional capabilities, including error checking; and by hiding details you don't need to know about, they let you focus on more important things. - -However, there are drawbacks. One is that it can be hard to understand the error messages. I'll have more to say about this in later chapters, but for now I have a suggestion. When you are getting started, you should practice making errors. - -\index{debugging} - -For each new function you learn, you should deliberately make as many mistakes as possible so you can see what happens. When you see what the errors messages are, you will understand what they mean. And that should help later, when you make errors accidentally. - - diff --git a/book/figs/RC_Divider.pdf b/book/figs/RC_Divider.pdf deleted file mode 100644 index b875ddfeb..000000000 Binary files a/book/figs/RC_Divider.pdf and /dev/null differ diff --git a/book/figs/RC_Divider.svg b/book/figs/RC_Divider.svg deleted file mode 100644 index 16834bdc9..000000000 --- a/book/figs/RC_Divider.svg +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - R - C - V - in - V - out - - diff --git a/book/figs/api_cheat_sheet.odg b/book/figs/api_cheat_sheet.odg deleted file mode 100644 index 950884703..000000000 Binary files a/book/figs/api_cheat_sheet.odg and /dev/null differ diff --git a/book/figs/chap01-fig01.pdf b/book/figs/chap01-fig01.pdf deleted file mode 100644 index 9849d68a8..000000000 Binary files a/book/figs/chap01-fig01.pdf and /dev/null differ diff --git a/book/figs/chap03-fig01.pdf b/book/figs/chap03-fig01.pdf deleted file mode 100644 index 5e027e1a4..000000000 Binary files a/book/figs/chap03-fig01.pdf and /dev/null differ diff --git a/book/figs/chap03-fig02.pdf b/book/figs/chap03-fig02.pdf deleted file mode 100644 index 308282369..000000000 Binary files a/book/figs/chap03-fig02.pdf and /dev/null differ diff --git a/book/figs/chap03-fig03.pdf b/book/figs/chap03-fig03.pdf deleted file mode 100644 index 7ae613dcb..000000000 Binary files a/book/figs/chap03-fig03.pdf and /dev/null differ diff --git a/book/figs/chap03-fig04.pdf b/book/figs/chap03-fig04.pdf deleted file mode 100644 index 642728bea..000000000 Binary files a/book/figs/chap03-fig04.pdf and /dev/null differ diff --git a/book/figs/chap03-fig05.pdf b/book/figs/chap03-fig05.pdf deleted file mode 100644 index 3f6139ba6..000000000 Binary files a/book/figs/chap03-fig05.pdf and /dev/null differ diff --git a/book/figs/chap04-fig01.pdf b/book/figs/chap04-fig01.pdf deleted file mode 100644 index 48fdf9e36..000000000 Binary files a/book/figs/chap04-fig01.pdf and /dev/null differ diff --git a/book/figs/chap04-fig02.pdf b/book/figs/chap04-fig02.pdf deleted file mode 100644 index 7e29442da..000000000 Binary files a/book/figs/chap04-fig02.pdf and /dev/null differ diff --git a/book/figs/chap05-fig01.pdf b/book/figs/chap05-fig01.pdf deleted file mode 100644 index 58e8cba18..000000000 Binary files a/book/figs/chap05-fig01.pdf and /dev/null differ diff --git a/book/figs/chap05-fig02.pdf b/book/figs/chap05-fig02.pdf deleted file mode 100644 index 261e9fd30..000000000 Binary files a/book/figs/chap05-fig02.pdf and /dev/null differ diff --git a/book/figs/chap05-fig03.pdf b/book/figs/chap05-fig03.pdf deleted file mode 100644 index 71911ce08..000000000 Binary files a/book/figs/chap05-fig03.pdf and /dev/null differ diff --git a/book/figs/chap05-fig04.pdf b/book/figs/chap05-fig04.pdf deleted file mode 100644 index bd6b31455..000000000 Binary files a/book/figs/chap05-fig04.pdf and /dev/null differ diff --git a/book/figs/chap05-fig05.pdf b/book/figs/chap05-fig05.pdf deleted file mode 100644 index 33004088b..000000000 Binary files a/book/figs/chap05-fig05.pdf and /dev/null differ diff --git a/book/figs/chap05-fig06.pdf b/book/figs/chap05-fig06.pdf deleted file mode 100644 index ad6ca5e9f..000000000 Binary files a/book/figs/chap05-fig06.pdf and /dev/null differ diff --git a/book/figs/chap06-fig01.pdf b/book/figs/chap06-fig01.pdf deleted file mode 100644 index 2db69bead..000000000 Binary files a/book/figs/chap06-fig01.pdf and /dev/null differ diff --git a/book/figs/chap06-fig02.pdf b/book/figs/chap06-fig02.pdf deleted file mode 100644 index dcc51bece..000000000 Binary files a/book/figs/chap06-fig02.pdf and /dev/null differ diff --git a/book/figs/chap06-fig03.pdf b/book/figs/chap06-fig03.pdf deleted file mode 100644 index 806f57ea3..000000000 Binary files a/book/figs/chap06-fig03.pdf and /dev/null differ diff --git a/book/figs/chap06-fig04.pdf b/book/figs/chap06-fig04.pdf deleted file mode 100644 index bc2452185..000000000 Binary files a/book/figs/chap06-fig04.pdf and /dev/null differ diff --git a/book/figs/chap07-fig01.pdf b/book/figs/chap07-fig01.pdf deleted file mode 100644 index e8005683c..000000000 Binary files a/book/figs/chap07-fig01.pdf and /dev/null differ diff --git a/book/figs/chap07-fig02.pdf b/book/figs/chap07-fig02.pdf deleted file mode 100644 index 3e70fc2f3..000000000 Binary files a/book/figs/chap07-fig02.pdf and /dev/null differ diff --git a/book/figs/chap08-fig01.pdf b/book/figs/chap08-fig01.pdf deleted file mode 100644 index 40ab0651b..000000000 Binary files a/book/figs/chap08-fig01.pdf and /dev/null differ diff --git a/book/figs/chap08-fig02.pdf b/book/figs/chap08-fig02.pdf deleted file mode 100644 index faa4f4a7d..000000000 Binary files a/book/figs/chap08-fig02.pdf and /dev/null differ diff --git a/book/figs/chap08-fig03.pdf b/book/figs/chap08-fig03.pdf deleted file mode 100644 index a3493b6e4..000000000 Binary files a/book/figs/chap08-fig03.pdf and /dev/null differ diff --git a/book/figs/chap08-fig04.pdf b/book/figs/chap08-fig04.pdf deleted file mode 100644 index 88bc35ba8..000000000 Binary files a/book/figs/chap08-fig04.pdf and /dev/null differ diff --git a/book/figs/chap09-fig01.pdf b/book/figs/chap09-fig01.pdf deleted file mode 100644 index 73c6b4ddc..000000000 Binary files a/book/figs/chap09-fig01.pdf and /dev/null differ diff --git a/book/figs/chap09-fig02.pdf b/book/figs/chap09-fig02.pdf deleted file mode 100644 index 654a0466a..000000000 Binary files a/book/figs/chap09-fig02.pdf and /dev/null differ diff --git a/book/figs/chap09-fig03.pdf b/book/figs/chap09-fig03.pdf deleted file mode 100644 index bc1a97e36..000000000 Binary files a/book/figs/chap09-fig03.pdf and /dev/null differ diff --git a/book/figs/chap10-fig01.pdf b/book/figs/chap10-fig01.pdf deleted file mode 100644 index dc5577614..000000000 Binary files a/book/figs/chap10-fig01.pdf and /dev/null differ diff --git a/book/figs/chap10-fig02.pdf b/book/figs/chap10-fig02.pdf deleted file mode 100644 index 9b9441e26..000000000 Binary files a/book/figs/chap10-fig02.pdf and /dev/null differ diff --git a/book/figs/chap10-fig03.pdf b/book/figs/chap10-fig03.pdf deleted file mode 100644 index 086aaee87..000000000 Binary files a/book/figs/chap10-fig03.pdf and /dev/null differ diff --git a/book/figs/chap11-fig01.pdf b/book/figs/chap11-fig01.pdf deleted file mode 100644 index 36b2a90fc..000000000 Binary files a/book/figs/chap11-fig01.pdf and /dev/null differ diff --git a/book/figs/chap11-fig02.pdf b/book/figs/chap11-fig02.pdf deleted file mode 100644 index 34052ccab..000000000 Binary files a/book/figs/chap11-fig02.pdf and /dev/null differ diff --git a/book/figs/chap25-fig01.pdf b/book/figs/chap25-fig01.pdf deleted file mode 100644 index 4c92ab06e..000000000 Binary files a/book/figs/chap25-fig01.pdf and /dev/null differ diff --git a/book/figs/dataframe.odg b/book/figs/dataframe.odg deleted file mode 100644 index 896fe1165..000000000 Binary files a/book/figs/dataframe.odg and /dev/null differ diff --git a/book/figs/dataframe.pdf b/book/figs/dataframe.pdf deleted file mode 100644 index d7b3d1415..000000000 Binary files a/book/figs/dataframe.pdf and /dev/null differ diff --git a/book/figs/github_work_flow.odg b/book/figs/github_work_flow.odg deleted file mode 100644 index 0189a6fd0..000000000 Binary files a/book/figs/github_work_flow.odg and /dev/null differ diff --git a/book/figs/github_work_flow.pdf b/book/figs/github_work_flow.pdf deleted file mode 100644 index ee058b86b..000000000 Binary files a/book/figs/github_work_flow.pdf and /dev/null differ diff --git a/book/figs/github_work_flow.png b/book/figs/github_work_flow.png deleted file mode 100644 index d073e81f0..000000000 Binary files a/book/figs/github_work_flow.png and /dev/null differ diff --git a/book/figs/jump.png b/book/figs/jump.png deleted file mode 100644 index 82f02c0b5..000000000 Binary files a/book/figs/jump.png and /dev/null differ diff --git a/book/figs/modeling_framework.odg b/book/figs/modeling_framework.odg deleted file mode 100644 index e51a7960a..000000000 Binary files a/book/figs/modeling_framework.odg and /dev/null differ diff --git a/book/figs/modeling_framework.pdf b/book/figs/modeling_framework.pdf deleted file mode 100644 index 7ba39bf66..000000000 Binary files a/book/figs/modeling_framework.pdf and /dev/null differ diff --git a/book/figs/modeling_framework.png b/book/figs/modeling_framework.png deleted file mode 100644 index 340309653..000000000 Binary files a/book/figs/modeling_framework.png and /dev/null differ diff --git a/book/figs/modsim_libraries.odg b/book/figs/modsim_libraries.odg deleted file mode 100644 index e02aca05b..000000000 Binary files a/book/figs/modsim_libraries.odg and /dev/null differ diff --git a/book/figs/modsim_libraries.png b/book/figs/modsim_libraries.png deleted file mode 100644 index d6a4d1c3b..000000000 Binary files a/book/figs/modsim_libraries.png and /dev/null differ diff --git a/book/figs/modsim_logo.odg b/book/figs/modsim_logo.odg deleted file mode 100644 index 517bd7665..000000000 Binary files a/book/figs/modsim_logo.odg and /dev/null differ diff --git a/book/figs/modsim_logo.png b/book/figs/modsim_logo.png deleted file mode 100644 index 0b6b9a0f9..000000000 Binary files a/book/figs/modsim_logo.png and /dev/null differ diff --git a/book/figs/paper_roll.odg b/book/figs/paper_roll.odg deleted file mode 100644 index d59a080c2..000000000 Binary files a/book/figs/paper_roll.odg and /dev/null differ diff --git a/book/figs/paper_roll.pdf b/book/figs/paper_roll.pdf deleted file mode 100644 index 7fcf442de..000000000 Binary files a/book/figs/paper_roll.pdf and /dev/null differ diff --git a/book/figs/paper_roll2.odg b/book/figs/paper_roll2.odg deleted file mode 100644 index e380d13e8..000000000 Binary files a/book/figs/paper_roll2.odg and /dev/null differ diff --git a/book/figs/paper_roll2.pdf b/book/figs/paper_roll2.pdf deleted file mode 100644 index 161a2c2e1..000000000 Binary files a/book/figs/paper_roll2.pdf and /dev/null differ diff --git a/book/figs/queue.odg b/book/figs/queue.odg deleted file mode 100644 index 3c00cfe36..000000000 Binary files a/book/figs/queue.odg and /dev/null differ diff --git a/book/figs/queue.pdf b/book/figs/queue.pdf deleted file mode 100644 index b8ff62222..000000000 Binary files a/book/figs/queue.pdf and /dev/null differ diff --git a/book/figs/stock_flow1.odg b/book/figs/stock_flow1.odg deleted file mode 100644 index 5a5906cf4..000000000 Binary files a/book/figs/stock_flow1.odg and /dev/null differ diff --git a/book/figs/stock_flow1.pdf b/book/figs/stock_flow1.pdf deleted file mode 100644 index 8b53b26c6..000000000 Binary files a/book/figs/stock_flow1.pdf and /dev/null differ diff --git a/book/figs/temp.odg b/book/figs/temp.odg deleted file mode 100644 index f38d5cc75..000000000 Binary files a/book/figs/temp.odg and /dev/null differ diff --git a/book/figs/trees-fig01.pdf b/book/figs/trees-fig01.pdf deleted file mode 100644 index 66ce79e7d..000000000 Binary files a/book/figs/trees-fig01.pdf and /dev/null differ diff --git a/book/figs/wall_model.pdf b/book/figs/wall_model.pdf deleted file mode 100644 index 1031a5885..000000000 Binary files a/book/figs/wall_model.pdf and /dev/null differ diff --git a/book/figs/wall_model.png b/book/figs/wall_model.png deleted file mode 100644 index d326a67f1..000000000 Binary files a/book/figs/wall_model.png and /dev/null differ diff --git a/book/figs/yoyo.odg b/book/figs/yoyo.odg deleted file mode 100644 index 4aaad8de1..000000000 Binary files a/book/figs/yoyo.odg and /dev/null differ diff --git a/book/figs/yoyo.pdf b/book/figs/yoyo.pdf deleted file mode 100644 index 72aeabf60..000000000 Binary files a/book/figs/yoyo.pdf and /dev/null differ diff --git a/book/figs/yoyo.png b/book/figs/yoyo.png deleted file mode 100644 index d922374f3..000000000 Binary files a/book/figs/yoyo.png and /dev/null differ diff --git a/book/footer.html b/book/footer.html deleted file mode 100644 index e1ca5b720..000000000 --- a/book/footer.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - -

-

Are you using one of our books in a class?

We'd like to know -about it. Please consider filling out this short survey. - -

-
- -

-Think Bayes - -

- - -

-Think Python - -

- - -

-Think Stats - -

- - -

-Think Complexity - -

- - - - - - - - diff --git a/book/header.html b/book/header.html deleted file mode 100644 index 09d202823..000000000 --- a/book/header.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - -
- - -

This HTML version of is provided for convenience, but it -is not the best format for the book. In particular, some of the -symbols are not rendered correctly. - -

You might prefer to read -the PDF version. diff --git a/book/hevea.sty b/book/hevea.sty deleted file mode 100644 index 17b4e546b..000000000 --- a/book/hevea.sty +++ /dev/null @@ -1,90 +0,0 @@ -% hevea : hevea.sty -% This is a very basic style file for latex document to be processed -% with hevea. It contains definitions of LaTeX environment which are -% processed in a special way by the translator. -% Mostly : -% - latexonly, not processed by hevea, processed by latex. -% - htmlonly , the reverse. -% - rawhtml, to include raw HTML in hevea output. -% - toimage, to send text to the image file. -% The package also provides hevea logos, html related commands (ahref -% etc.), void cutting and image commands. -\NeedsTeXFormat{LaTeX2e} -\ProvidesPackage{hevea}[2002/01/11] -\RequirePackage{comment} -\newif\ifhevea\heveafalse -\@ifundefined{ifimagen}{\newif\ifimagen\imagenfalse} -\makeatletter% -\newcommand{\heveasmup}[2]{% -\raise #1\hbox{$\m@th$% - \csname S@\f@size\endcsname - \fontsize\sf@size 0% - \math@fontsfalse\selectfont -#2% -}}% -\DeclareRobustCommand{\hevea}{H\kern-.15em\heveasmup{.2ex}{E}\kern-.15emV\kern-.15em\heveasmup{.2ex}{E}\kern-.15emA}% -\DeclareRobustCommand{\hacha}{H\kern-.15em\heveasmup{.2ex}{A}\kern-.15emC\kern-.1em\heveasmup{.2ex}{H}\kern-.15emA}% -\DeclareRobustCommand{\html}{\protect\heveasmup{0.ex}{HTML}} -%%%%%%%%% Hyperlinks hevea style -\newcommand{\ahref}[2]{{#2}} -\newcommand{\ahrefloc}[2]{{#2}} -\newcommand{\aname}[2]{{#2}} -\newcommand{\ahrefurl}[1]{\texttt{#1}} -\newcommand{\footahref}[2]{#2\footnote{\texttt{#1}}} -\newcommand{\mailto}[1]{\texttt{#1}} -\newcommand{\imgsrc}[2][]{} -\newcommand{\home}[1]{\protect\raisebox{-.75ex}{\char126}#1} -\AtBeginDocument -{\@ifundefined{url} -{%url package is not loaded -\let\url\ahref\let\oneurl\ahrefurl\let\footurl\footahref} -{}} -%% Void cutting instructions -\newcounter{cuttingdepth} -\newcommand{\tocnumber}{} -\newcommand{\notocnumber}{} -\newcommand{\cuttingunit}{} -\newcommand{\cutdef}[2][]{} -\newcommand{\cuthere}[2]{} -\newcommand{\cutend}{} -\newcommand{\htmlhead}[1]{} -\newcommand{\htmlfoot}[1]{} -\newcommand{\htmlprefix}[1]{} -\newenvironment{cutflow}[1]{}{} -\newcommand{\cutname}[1]{} -\newcommand{\toplinks}[3]{} -\newcommand{\setlinkstext}[3]{} -\newcommand{\flushdef}[1]{} -\newcommand{\footnoteflush}[1]{} -%%%% Html only -\excludecomment{rawhtml} -\newcommand{\rawhtmlinput}[1]{} -\excludecomment{htmlonly} -%%%% Latex only -\newenvironment{latexonly}{}{} -\newenvironment{verblatex}{}{} -%%%% Image file stuff -\def\toimage{\endgroup} -\def\endtoimage{\begingroup\def\@currenvir{toimage}} -\def\verbimage{\endgroup} -\def\endverbimage{\begingroup\def\@currenvir{verbimage}} -\newcommand{\imageflush}[1][]{} -%%% Bgcolor definition -\newsavebox{\@bgcolorbin} -\newenvironment{bgcolor}[2][] - {\newcommand{\@mycolor}{#2}\begin{lrbox}{\@bgcolorbin}\vbox\bgroup} - {\egroup\end{lrbox}% - \begin{flushleft}% - \colorbox{\@mycolor}{\usebox{\@bgcolorbin}}% - \end{flushleft}} -%%% Style sheets macros, defined as no-ops -\newcommand{\newstyle}[2]{} -\newcommand{\addstyle}[1]{} -\newcommand{\setenvclass}[2]{} -\newcommand{\getenvclass}[1]{} -\newcommand{\loadcssfile}[1]{} -\newenvironment{divstyle}[1]{}{} -\newenvironment{cellstyle}[2]{}{} -\newif\ifexternalcss -%%% Postlude -\makeatother diff --git a/book/htmlonly b/book/htmlonly deleted file mode 100644 index ea5fa884d..000000000 --- a/book/htmlonly +++ /dev/null @@ -1,72 +0,0 @@ -\newcommand{\adjustpage}[1]{} - -\newcommand{\clearemptydoublepage}{} -\newcommand{\blankpage}{} - -\newcommand{\spacing}{} -\newcommand{\endspacing}{} - -\newcommand{\frontmatter}{} -\newcommand{\mainmatter}{} -\newcommand{\backmatter}{} - -\newcommand{\theoremstyle}[1]{} -\newcommand{\newtheoremstyle}[1]{} - -\newcommand{\vfill}{} - -\newcommand{\href}{\ahref} - -\htmlhead{\rawhtmlinput{header.html}} - -\htmlfoot{\rawhtmlinput{footer.html}} - -% these styles get translated into CSS -\newstyle{p+p}{margin-top:1em; margin-bottom:1em} -\newstyle{img}{border: 0px;} - -% change the arrows -\setlinkstext - {\imgsrc[ALT="Previous"]{back.png}} - {\imgsrc[ALT="Up"]{up.png}} - {\imgsrc[ALT="Next"]{next.png}} - -% colors for code listings and output -\usepackage{color} -\definecolor{bgcolor}{rgb}{0.982, 0.982, 0.982} -\definecolor{comment}{rgb}{0.000, 0.488, 0.000} -\definecolor{keyword}{rgb}{0.000, 0.000, 1.000} -\definecolor{strings}{rgb}{0.700, 0.000, 0.000} - -% syntax highlighting in code listings -\usepackage{listings} -\lstset{ - language=java, - basicstyle=\ttfamily, - backgroundcolor=\color{bgcolor}, - commentstyle=\color{comment}, - keywordstyle=\color{keyword}, - stringstyle=\color{strings}, - columns=fullflexible, - emph={label}, % keyword? - keepspaces=true, - showstringspaces=false -} - -% code listing environments -\lstnewenvironment{code} -{} -{} -\lstnewenvironment{stdout} -{\lstset{commentstyle=\relax,keywordstyle=\relax,stringstyle=\relax}} -{} - -% inline syntax formatting -\newcommand{\py}{\lstinline}%} - -% CSS for code listings -\newstyle{.lstframe}{margin: 1em; width: calc(100\% - 1em);} -\newstyle{.lstlisting}{padding: 3pt;} - -% make urls hyperlinks in html -\input{urlhref.hva} diff --git a/book/latexonly b/book/latexonly deleted file mode 100644 index 09b602cf9..000000000 --- a/book/latexonly +++ /dev/null @@ -1,129 +0,0 @@ -\usepackage{geometry} -\geometry{ - width=5.5in, - height=8.5in, - hmarginratio=3:2, - vmarginratio=1:1, - includehead=true, - headheight=15pt -} - -% paragraph spacing -\setlength{\parindent}{0pt} % 17.62482pt -\setlength{\parskip}{12pt plus 4pt minus 4pt} % 0.0pt plus 1.0pt -\linespread{1.05} -\def\arraystretch{1.5} - -% list spacing -\setlength{\topsep}{5pt plus 2pt minus 3pt} % 10.0pt plus 4.0pt minus 6.0pt -\setlength{\partopsep}{-6pt plus 2pt minus 2pt} % 3.0pt plus 2.0pt minus 2.0pt -\setlength{\itemsep}{0pt} % 5.0pt plus 2.5pt minus 1.0pt - -% these are copied from tex/latex/base/book.cls -% all I changed is afterskip -\makeatletter -\renewcommand{\section}{\@startsection{section}{1}{\z@}% - {-3.5ex \@plus -1ex \@minus -.2ex}% - {0.7ex \@plus.2ex}% - {\normalfont\Large\bfseries}} -\renewcommand\subsection{\@startsection{subsection}{2}{\z@}% - {-3.25ex\@plus -1ex \@minus -.2ex}% - {0.3ex \@plus .2ex}% - {\normalfont\large\bfseries}} -\renewcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}% - {-3.25ex\@plus -1ex \@minus -.2ex}% - {0.3ex \@plus .2ex}% - {\normalfont\normalsize\bfseries}} -\makeatother - -% table of contents vertical spacing -\usepackage{tocloft} -\setlength\cftparskip{8pt plus 4pt minus 4pt} - -% balanced index with TOC entry -\usepackage[totoc]{idxlayout} - -% The following line adds a little extra space to the column -% in which the Section numbers appear in the table of contents -\makeatletter -\renewcommand{\l@section}{\@dottedtocline{1}{1.5em}{3.0em}} -\makeatother - -% customize page headers -\usepackage{fancyhdr} -\pagestyle{fancyplain} -\renewcommand{\chaptermark}[1]{\markboth{Chapter \thechapter ~~ #1}{}} -\renewcommand{\sectionmark}[1]{\markright{\thesection ~~ #1}} -\lhead[\fancyplain{}{\bfseries\thepage}]% - {\fancyplain{}{\bfseries\rightmark}} -\rhead[\fancyplain{}{\bfseries\leftmark}]% - {\fancyplain{}{\bfseries\thepage}} -\cfoot{} -%\rfoot{\textcolor{gray}{\tiny ThinkJava Draft \today}} - -%% tweak spacing of figures and captions -%\usepackage{floatrow} -%\usepackage{caption} -%\captionsetup{ -% font=small, -% labelformat=empty, -% justification=centering, -% skip=4pt -%} - -% colors for code listings and output -\usepackage{xcolor} -\definecolor{bgcolor}{HTML}{FAFAFA} -\definecolor{comment}{HTML}{007C00} -\definecolor{keyword}{HTML}{0000FF} -\definecolor{strings}{HTML}{B20000} - -% syntax highlighting in code listings -\usepackage{textcomp} -\usepackage{listings} -\lstset{ - language=python, - basicstyle=\ttfamily, - backgroundcolor=\color{bgcolor}, - commentstyle=\color{comment}, - keywordstyle=\color{keyword}, - stringstyle=\color{strings}, - columns=fullflexible, - emph={label}, % keyword? - keepspaces=true, - showstringspaces=false, - upquote=true, - xleftmargin=15pt, % \parindent - framexleftmargin=3pt, - aboveskip=\parskip, - belowskip=\parskip -} - -% code listing environments -\lstnewenvironment{code} -{\minipage{\linewidth}} -{\endminipage} -\lstnewenvironment{stdout} -{\lstset{commentstyle=,keywordstyle=,stringstyle=}\minipage{\linewidth}} -{\endminipage} - -% inline syntax formatting -\newcommand{\py}[1]{\lstinline{#1}}%{ - -% prevent hyphens in names -\hyphenation{GitHub} - -% pdf hyperlinks, table of contents, and document properties -\usepackage{hyperref} -\hypersetup{% - pdftitle={\thetitle: \thesubtitle}, - pdfauthor={\theauthors}, - pdfsubject={Version \theversion}, - pdfkeywords={}, - bookmarksopen=false, - colorlinks=true, - citecolor=black, - filecolor=black, - linkcolor=black, - urlcolor=blue -} diff --git a/book/links/urls.csv b/book/links/urls.csv deleted file mode 100644 index ae6650fc3..000000000 --- a/book/links/urls.csv +++ /dev/null @@ -1,85 +0,0 @@ -zen,https://zen-of-python.info/namespaces-are-one-honking-great-idea--lets-do-more-of-those.html -precess,https://www.youtube.com/watch?v=ty9QSiVC2g0 -myth,https://www.youtube.com/watch?v=PHxvMLoKRWg -kitten,https://www.youtube.com/watch?v=1Z9YNmCIscs -insulin,https://www.ncbi.nlm.nih.gov/pubmed/443421 -sir,https://www.maa.org/press/periodicals/loci/joma/the-sir-model-for-spread-of-disease -khan2,https://www.khanacademy.org/math/ap-calculus-bc/diff-equations-bc/logistic-diff-eq-bc/v/solving-logistic-differential-equation-part-1 -khan3,https://www.khanacademy.org/math/ap-calculus-ab/diff-equations-ab/exponential-models-ab/v/newtons-law-of-cooling -khan1,https://www.khanacademy.org/math/ap-calculus-ab/diff-equations-ab/exponential-models-ab/v/exponential-solution-to-differential-equation -bergman,https://www.karger.com/Article/Abstract/89312 -nasa,https://www.grc.nasa.gov/www/k-12/airplane/balldrag.html -cdc,https://www.cdc.gov/diabetes/pdfs/data/statistics/national-diabetes-statistics-report.pdf -loc,https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.loc.html -series,https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.html -color,https://matplotlib.org/users/colors.html -plot,https://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.plot -zip,https://github.com/AllenDowney/ModSimPy/archive/master.zip -git,https://git-scm.com/download/linux -yoyo,https://en.wikipedia.org/wiki/Yo-yo -worldpop,https://en.wikipedia.org/wiki/World_population_estimates -trajec,https://en.wikipedia.org/wiki/Trajectory -torque,https://en.wikipedia.org/wiki/Torque -paper,https://en.wikipedia.org/wiki/Toilet_paper -timeser,https://en.wikipedia.org/wiki/Time_series -thermrad,https://en.wikipedia.org/wiki/Thermal_radiation -thermass,https://en.wikipedia.org/wiki/Thermal_mass -popbomb,https://en.wikipedia.org/wiki/The_Population_Bomb -stock,https://en.wikipedia.org/wiki/Stock_and_flow -spline,https://en.wikipedia.org/wiki/Spline_(mathematics) -singval,https://en.wikipedia.org/wiki/Single-valued_function -thermo,https://en.wikipedia.org/wiki/Second_law_of_thermodynamics -recur,https://en.wikipedia.org/wiki/Recurrence_relation -quarter,https://en.wikipedia.org/wiki/Quarter_(United_States_coin) -penny,https://en.wikipedia.org/wiki/Penny_(United_States_coin) -ode,https://en.wikipedia.org/wiki/Ordinary_differential_equation#General_definition -nondim,https://en.wikipedia.org/wiki/Nondimensionalization#First_order_system -varmass,https://en.wikipedia.org/wiki/Newton's_laws_of_motion#Variable-mass_systems -newton,https://en.wikipedia.org/wiki/Newton's_laws_of_motion -mythbust,https://en.wikipedia.org/wiki/MythBusters_(2003_season) -mortality,https://en.wikipedia.org/wiki/Mortality_rate -magnus,https://en.wikipedia.org/wiki/Magnus_effect -minpack,https://en.wikipedia.org/wiki/MINPACK -logistic,https://en.wikipedia.org/wiki/Logistic_function -moment,https://en.wikipedia.org/wiki/List_of_moments_of_inertia -lmm,https://en.wikipedia.org/wiki/Linear_multistep_method -levmarq,https://en.wikipedia.org/wiki/Levenberg-Marquardt_algorithm -inverse,https://en.wikipedia.org/wiki/Inverse_problem -herd,https://en.wikipedia.org/wiki/Herd_immunity -transfer,https://en.wikipedia.org/wiki/Heat_transfer -specheat,https://en.wikipedia.org/wiki/Heat_capacity#Specific_heat_capacity -hardcode,https://en.wikipedia.org/wiki/Hard_coding -wally,https://en.wikipedia.org/wiki/Green_Monster -geom,https://en.wikipedia.org/wiki/Geometric_progression -expo,https://en.wikipedia.org/wiki/Exponential_growth -euler,https://en.wikipedia.org/wiki/Euler_method -vecops,https://en.wikipedia.org/wiki/Euclidean_vector#Addition_and_subtraction -drageq,https://en.wikipedia.org/wiki/Drag_equation -dragco,https://en.wikipedia.org/wiki/Drag_coefficient -diffeq,https://en.wikipedia.org/wiki/Differential_equation -tempress,https://en.wikipedia.org/wiki/Density_of_air#Temperature_and_pressure -density,https://en.wikipedia.org/wiki/Density_of_air -boolean,https://en.wikipedia.org/wiki/Boolean_algebra -contact,https://en.wikipedia.org/wiki/Basic_reproduction_number -baseball,https://en.wikipedia.org/wiki/Baseball_(ball) -plato,https://en.wikipedia.org/wiki/Allegory_of_the_Cave -minimize,https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize_scalar.html -interp,https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html -hindmarsh,http://history.siam.org/oralhistories/hindmarsh.htm -minmod,http://faculty.washington.edu/gennari/teaching/591-BioSimSem/Bergman-minmodel05.pdf -license,http://creativecommons.org/licenses/by-nc-sa/4.0/ -wall ,http://discovery.ucl.ac.uk/1526521 -runs,https://aapt.scitation.org/doi/abs/10.1119/1.1604384 -brent,https://en.wikipedia.org/wiki/Brents_method -divider,https://en.wikipedia.org/wiki/File:RC_Divider.svg -hooke,https://en.wikipedia.org/wiki/Hookes_law -moment,https://en.wikipedia.org/wiki/List_of_moments_of_inertia -runge,https://en.wikipedia.org/wiki/Runge-Kutta-Fehlberg_method -getgit,https://git-scm.com/book/en/v2/Getting-Started-Installing-Git -bungee,https://iopscience.iop.org/article/10.1088/0031-9120/45/1/007 -spider,https://spiderman.wikia.com/wiki/Peter_Parker_(Earth-616) -trees,https://www.cfr.washington.edu/research.smc/working_papers/smc_working_paper_1.pdf -wall2,https://www.sciencedirect.com/science/article/pii/S0378778816313056 -human,https://www.youtube.com/watch?v=PUwmA3Q0_OE -dunk,https://www.youtube.com/watch?v=UBf7WC19lpw -drop,https://www.youtube.com/watch?v=X-QFAB0gEtE diff --git a/book/localdef.py b/book/localdef.py deleted file mode 100644 index 14f7a5bc4..000000000 --- a/book/localdef.py +++ /dev/null @@ -1,81 +0,0 @@ -import plasTeX.Base as Base - -def idgen(): - """ Generate a unique ID """ - i = 1 - while 1: - yield 'a%.10d' % i - i += 1 - -idgen = idgen() - -class Eqn(Base.Command): - args = 'self' - -class Anchor(Base.Command): - args = 'label:str' - def invoke(self, tex): - Base.Command.invoke(self, tex) - self.ownerDocument.context.label(self.attributes['label'], self) - -class exercise(Base.Environment): - counter = 'exercise' - -class index(Base.Command): - args = 'termstring' - - def setEntry(self, s, seetype=0): - # TYPE_NORMAL = 0 - # TYPE_SEE = 1 - # TYPE_SEEALSO = 2 - if type(s) != type(''): - s = s.textContent - if s.count('!'): - priterm, secterm = s.split('!') - if priterm.count('@'): - prisort, primary = priterm.split('@') - else: - prisort, primary = None, priterm - if secterm.count('@'): - secsort, secondary = secterm.split('@') - else: - secsort, secondary = None, secterm - elif s.count('@'): - prisort, primary = s.split('@') - secsort, secondary = None, None - else: - prisort, primary = None, s - secsort, secondary = None, None - -# if secondary: -# self.ownerDocument.userdata.setdefault('index', []).append(\ -# Base.IndexEntry([primary, secondary], self, [prisort, secsort], None, type=seetype)) -# else: -# self.ownerDocument.userdata.setdefault('index', []).append(\ -# Base.IndexEntry([primary], self, [prisort], None, type=seetype)) - return prisort, primary, secsort, secondary - - def invoke(self, tex): - Base.Command.invoke(self, tex) - self.ownerDocument.context.label(idgen.next(), self) - p0,p1,s0,s1 = self.setEntry(self.attributes['termstring']) - if p0: - self.prisort = '%s' % p0 - if p1: - self.primary = '%s' % p1 - if s0: - self.secsort = '%s' % s0 - if s1: - self.secondary = '%s' % s1 - -class scriptN(Base.Command): - unicode = u'\U0001D4A9' - -class uxbar(Base.Command): pass -class uybar(Base.Command): pass -class unhat(Base.Command): pass -class ule(Base.Command): pass -class minus(Base.Command): pass -class lowast(Base.Command): pass -class Erdos(Base.Command): pass - diff --git a/book/next.png b/book/next.png deleted file mode 100644 index e89db2714..000000000 Binary files a/book/next.png and /dev/null differ diff --git a/book/up.png b/book/up.png deleted file mode 100644 index 4e86da1aa..000000000 Binary files a/book/up.png and /dev/null differ diff --git a/chap03.py b/chap03.py new file mode 100644 index 000000000..5b6f16266 --- /dev/null +++ b/chap03.py @@ -0,0 +1,43 @@ +from modsim import * + +def step(state, p1, p2): + """Simulate one time step. + + state: bikeshare State object + p1: probability of an Olin->Wellesley ride + p2: probability of a Wellesley->Olin ride + """ + if flip(p1): + bike_to_wellesley(state) + + if flip(p2): + bike_to_olin(state) + +from modsim import * + +def bike_to_olin(state): + """Move one bike from Wellesley to Olin. + + state: bikeshare State object + """ + if state.wellesley == 0: + state.wellesley_empty += 1 + return + state.wellesley -= 1 + state.olin += 1 + +from modsim import * + +# Solution + +def bike_to_wellesley(state): + """Move one bike from Olin to Wellesley. + + state: bikeshare State object + """ + if state.olin == 0: + state.olin_empty += 1 + return + state.olin -= 1 + state.wellesley += 1 + diff --git a/chap06.py b/chap06.py new file mode 100644 index 000000000..f18bcbe40 --- /dev/null +++ b/chap06.py @@ -0,0 +1,12 @@ +from modsim import * + +def run_simulation(system, growth_func): + results = TimeSeries() + results[system.t_0] = system.p_0 + + for t in range(system.t_0, system.t_end): + growth = growth_func(t, results[t], system) + results[t+1] = results[t] + growth + + return results + diff --git a/chap11.py b/chap11.py new file mode 100644 index 000000000..859f4dc41 --- /dev/null +++ b/chap11.py @@ -0,0 +1,43 @@ +from modsim import * + +def make_system(beta, gamma): + init = State(s=89, i=1, r=0) + init /= init.sum() + + return System(init=init, t_end=7*14, + beta=beta, gamma=gamma) + +from modsim import * + +def update_func(t, state, system): + s, i, r = state.s, state.i, state.r + + infected = system.beta * i * s + recovered = system.gamma * i + + s -= infected + i += infected - recovered + r += recovered + + return State(s=s, i=i, r=r) + +from modsim import * + +def plot_results(S, I, R): + S.plot(style='--', label='Susceptible') + I.plot(style='-', label='Infected') + R.plot(style=':', label='Recovered') + decorate(xlabel='Time (days)', + ylabel='Fraction of population') + +from modsim import * + +def run_simulation(system, update_func): + frame = TimeFrame(columns=system.init.index) + frame.loc[0] = system.init + + for t in range(0, system.t_end): + frame.loc[t+1] = update_func(t, frame.loc[t], system) + + return frame + diff --git a/chap12.py b/chap12.py new file mode 100644 index 000000000..7cfc3909e --- /dev/null +++ b/chap12.py @@ -0,0 +1,7 @@ +from modsim import * + +def calc_total_infected(results, system): + s_0 = results.s[0] + s_end = results.s[system.t_end] + return s_0 - s_end + diff --git a/chap13.py b/chap13.py new file mode 100644 index 000000000..fd5aa0807 --- /dev/null +++ b/chap13.py @@ -0,0 +1,29 @@ +from modsim import * + +# import code from previous notebooks + +from chap11 import make_system +from chap11 import update_func +from chap11 import run_simulation +from chap11 import plot_results + +from chap12 import calc_total_infected + +from modsim import * + +def sweep_beta(beta_array, gamma): + sweep = SweepSeries() + for beta in beta_array: + system = make_system(beta, gamma) + results = run_simulation(system, update_func) + sweep[beta] = calc_total_infected(results, system) + return sweep + +from modsim import * + +def sweep_parameters(beta_array, gamma_array): + frame = SweepFrame(columns=gamma_array) + for gamma in gamma_array: + frame[gamma] = sweep_beta(beta_array, gamma) + return frame + diff --git a/chap15.py b/chap15.py new file mode 100644 index 000000000..f439ced24 --- /dev/null +++ b/chap15.py @@ -0,0 +1,35 @@ +from modsim import * + +def make_system(T_init, volume, r, t_end): + return System(T_init=T_init, + T_final=T_init, + volume=volume, + r=r, + t_end=t_end, + T_env=22, + t_0=0, + dt=1) + +from modsim import * + +def change_func(t, T, system): + r, T_env, dt = system.r, system.T_env, system.dt + return -r * (T - T_env) * dt + +from modsim import * + +def run_simulation(system, change_func): + t_array = linrange(system.t_0, system.t_end, system.dt) + n = len(t_array) + + series = TimeSeries(index=t_array) + series.iloc[0] = system.T_init + + for i in range(n-1): + t = t_array[i] + T = series.iloc[i] + series.iloc[i+1] = T + change_func(t, T, system) + + system.T_final = series.iloc[-1] + return series + diff --git a/chap18.py b/chap18.py new file mode 100644 index 000000000..7e3db625c --- /dev/null +++ b/chap18.py @@ -0,0 +1,30 @@ +from modsim import * + +def make_system(params, data): + G0, k1, k2, k3 = params + + t_0 = data.index[0] + t_end = data.index[-1] + + Gb = data.glucose[t_0] + Ib = data.insulin[t_0] + I = interpolate(data.insulin) + + init = State(G=G0, X=0) + + return System(init=init, params=params, + Gb=Gb, Ib=Ib, I=I, + t_0=t_0, t_end=t_end, dt=2) + +from modsim import * + +def slope_func(t, state, system): + G, X = state + G0, k1, k2, k3 = system.params + I, Ib, Gb = system.I, system.Ib, system.Gb + + dGdt = -k1 * (G - Gb) - X*G + dXdt = k3 * (I(t) - Ib) - k2 * X + + return dGdt, dXdt + diff --git a/chap22.py b/chap22.py new file mode 100644 index 000000000..a5b5f7451 --- /dev/null +++ b/chap22.py @@ -0,0 +1,69 @@ +from modsim import * + +params = Params( + x = 0, # m + y = 1, # m + angle = 45, # degree + speed = 40, # m / s + + mass = 145e-3, # kg + diameter = 73e-3, # m + C_d = 0.33, # dimensionless + + rho = 1.2, # kg/m**3 + g = 9.8, # m/s**2 + t_end = 10, # s +) + +from modsim import * + +from numpy import pi, deg2rad + +def make_system(params): + + # convert angle to radians + theta = deg2rad(params.angle) + + # compute x and y components of velocity + vx, vy = pol2cart(theta, params.speed) + + # make the initial state + init = State(x=params.x, y=params.y, vx=vx, vy=vy) + + # compute the frontal area + area = pi * (params.diameter/2)**2 + + return System(params, + init = init, + area = area) + +from modsim import * + +def drag_force(V, system): + rho, C_d, area = system.rho, system.C_d, system.area + + mag = rho * vector_mag(V)**2 * C_d * area / 2 + direction = -vector_hat(V) + f_drag = mag * direction + return f_drag + +from modsim import * + +def slope_func(t, state, system): + x, y, vx, vy = state + mass, g = system.mass, system.g + + V = Vector(vx, vy) + a_drag = drag_force(V, system) / mass + a_grav = g * Vector(0, -1) + + A = a_grav + a_drag + + return V.x, V.y, A.x, A.y + +from modsim import * + +def event_func(t, state, system): + x, y, vx, vy = state + return y + diff --git a/chapters/build.sh b/chapters/build.sh new file mode 100644 index 000000000..b16f5a3cf --- /dev/null +++ b/chapters/build.sh @@ -0,0 +1,25 @@ +# copy the notebooks and remove the solutions +cp ../ModSimPySolutions/chapters/chap*.ipynb . +python remove_soln.py + +# run nbmake +rm modsim.py chap*.py +pytest --nbmake chap*.ipynb + +# add and commit +git add chap*.ipynb chap*.py +git commit -m "Updating chapters" + +# build the zip file +cd ../..; zip -r ModSimPyNotebooks.zip \ + ModSimPy/chapters/chap*.ipynb \ + ModSimPy/examples/*.ipynb + +# add and commit it +mv ModSimPyNotebooks.zip ModSimPy +cd ModSimPy + +git add ModSimPyNotebooks.zip +git commit -m "Updating the notebook zip file" + +git push diff --git a/chapters/chap01.ipynb b/chapters/chap01.ipynb new file mode 100644 index 000000000..c5c409aed --- /dev/null +++ b/chapters/chap01.ipynb @@ -0,0 +1,1351 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "affecting-malta", + "metadata": {}, + "source": [ + "# Modeling" + ] + }, + { + "cell_type": "markdown", + "id": "pressed-palestinian", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "markdown", + "id": "confirmed-budapest", + "metadata": { + "tags": [] + }, + "source": [ + "## Jupyter\n", + "\n", + "Welcome to *Modeling and Simulation in Python*, and welcome to Jupyter.\n", + "\n", + "This is a Jupyter notebook, which is a development environment where you can write and run Python code. Each notebook is divided into cells. Each cell contains either text (like this cell) or Python code." + ] + }, + { + "cell_type": "markdown", + "id": "danish-scope", + "metadata": { + "tags": [] + }, + "source": [ + "To run a cell, hold down SHIFT and press ENTER. \n", + "\n", + "* If you run a text cell, Jupyter formats the text and displays the result.\n", + "\n", + "* If you run a code cell, Jupyter runs the Python code in the cell and displays the result, if any.\n", + "\n", + "The following cells check whether the libraries we need are installed. If so, the cells produce no output. If not, you'll see updates from the installer." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "passive-dayton", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " from pint import UnitRegistry\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "prompt-committee", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "fifteen-train", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "chronic-moore", + "metadata": {}, + "source": [ + "This chapter introduces the modeling framework we will use throughout the book, and works through our first example, using a simple model of physics to evaluate the claim that a penny falling from the height of the Empire State Building could kill someone if it hit them on the head. Also, you'll see how to do computation in Python with units like meters and seconds." + ] + }, + { + "cell_type": "markdown", + "id": "extensive-proposition", + "metadata": {}, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "hourly-financing", + "metadata": {}, + "source": [ + "## The Modeling Framework\n", + "\n", + "This book is about modeling and simulating physical systems. The\n", + "following diagram shows what I mean by *modeling*:\n", + "\n", + "![Diagram of the modeling framework.](https://github.com/AllenDowney/ModSim/raw/main/figs/modeling_framework.png)" + ] + }, + { + "cell_type": "markdown", + "id": "structured-receiver", + "metadata": {}, + "source": [ + "Starting in the lower left, the *system* is something in the real\n", + "world we are interested in. \n", + "To model the system, we have to decide which elements of the real world to include and which we can leave out.\n", + "This process is called *abstraction*.\n", + "\n", + "The result of abstraction is a *model*, which is a description of the system that includes only the features we think are essential. A model\n", + "can be represented in the form of diagrams and equations, which can be\n", + "used for mathematical *analysis*. It can also be implemented in the\n", + "form of a computer program, which can run *simulations*.\n", + "\n", + "The result of analysis and simulation might be a *prediction* about\n", + "what the system will do, an *explanation* of why it behaves the way it\n", + "does, or a *design* intended to achieve a purpose.\n", + "\n", + "We can *validate* predictions and test designs by taking\n", + "*measurements* from the real world and comparing the *data* we get\n", + "with the results from analysis and simulation." + ] + }, + { + "cell_type": "markdown", + "id": "center-accommodation", + "metadata": {}, + "source": [ + "For any physical system, there are many possible models, each one\n", + "including and excluding different features, or including different\n", + "levels of detail. The goal of the modeling process is to find the model\n", + "best suited to its purpose (prediction, explanation, or design).\n", + "\n", + "Sometimes the best model is the most detailed. If we include more\n", + "features, the model is more realistic, and we expect its predictions to\n", + "be more accurate.\n", + "But often a simpler model is better. If we include only the essential\n", + "features and leave out the rest, we get models that are easier to work\n", + "with, and the explanations they provide can be clearer and more\n", + "compelling." + ] + }, + { + "cell_type": "markdown", + "id": "confirmed-highlight", + "metadata": {}, + "source": [ + "As an example, suppose someone asks you why the orbit of the Earth is\n", + "elliptical. If you model the Earth and Sun as point masses (ignoring\n", + "their actual size), compute the gravitational force between them using\n", + "Newton's law of universal gravitation, and compute the resulting orbit\n", + "using Newton's laws of motion, you can show that the result is an\n", + "ellipse.\n", + "Of course, the actual orbit of Earth is not a perfect ellipse, because\n", + "of the gravitational forces of the Moon, Jupiter, and other objects in\n", + "the solar system, and because Newton's laws of motion are only\n", + "approximately true (they don't take into account relativistic effects).\n", + "But adding these features to the model would not improve the\n", + "explanation; more detail would only be a distraction from the\n", + "fundamental cause. However, if the goal is to predict the position of\n", + "the Earth with great precision, including more details might be\n", + "necessary." + ] + }, + { + "cell_type": "markdown", + "id": "stretch-geneva", + "metadata": {}, + "source": [ + "Choosing the best model depends on what the model is for. It is usually\n", + "a good idea to start with a simple model, even if it is likely to be too\n", + "simple, and test whether it is good enough for its purpose. Then you can\n", + "add features gradually, starting with the ones you expect to be most\n", + "essential. This process is called *iterative modeling*.\n", + "\n", + "Comparing results of successive models provides a form of *internal\n", + "validation*, so you can catch conceptual, mathematical, and software\n", + "errors. And by adding and removing features, you can tell which ones\n", + "have the biggest effect on the results, and which can be ignored.\n", + "\n", + "Comparing results to data from the real world provides *external\n", + "validation*, which is generally the strongest test.\n", + "\n", + "The modeling framework is pretty abstract; the following example might make it clearer." + ] + }, + { + "cell_type": "markdown", + "id": "criminal-lunch", + "metadata": {}, + "source": [ + "## The Falling Penny Myth\n", + "\n", + "You might have heard that a\n", + "penny dropped from the top of the Empire State Building would be going\n", + "so fast when it hit the pavement that it would be embedded in the\n", + "concrete; or if it hit a person, it would break their skull.\n", + "\n", + "We can test this myth by making and analyzing two models. For the first model,\n", + "we'll assume that the effect of air resistance is small. In that case, the primary force acting on the penny is gravity, which causes the penny to accelerate downward.\n", + "\n", + "If the initial velocity is 0 and the acceleration, $a$, is constant, the velocity after $t$ seconds is \n", + "\n", + "$$v = a t$$\n", + "\n", + "and the distance the penny has dropped is \n", + "\n", + "$$x = a t^2 / 2$$ \n", + "\n", + "To find the time until the penny reaches the sidewalk, we can solve for $t$:\n", + "\n", + "$$t = \\sqrt{ 2 x / a}$$ \n", + "\n", + "Plugging in the acceleration of gravity, $a = 9.8$ m/s$^2$, and the height of the Empire State Building, $x = 381$ m, we get $t = 8.8$ s. \n", + "\n", + "Then computing $v = a t$ we get a velocity on impact of $86$ m/s, which is about 190 miles per hour. That sounds like it could hurt." + ] + }, + { + "cell_type": "markdown", + "id": "documentary-diagnosis", + "metadata": {}, + "source": [ + "Of course, these results are not exact because the model is based on simplifications. For example, we assume that gravity is constant. \n", + "In fact, the force of gravity is different on different parts of the globe, and it gets weaker as you move away from the surface. \n", + "But these differences are small, so ignoring them is probably a good choice for this problem.\n", + "\n", + "On the other hand, ignoring air resistance is not a good choice, because in this scenario its effect is substantial.\n", + "Once the penny gets to about 29 m/s, the upward force of air resistance equals the downward force of gravity, so the penny stops accelerating.\n", + "This is the *terminal velocity* of the penny in air.\n", + "\n", + "And that suggests a second model, where the penny accelerates until it reaches terminal velocity; after that, acceleration is 0 and velocity is constant.\n", + "In this model, the penny hits the sidewalk at about 29 m/s.\n", + "That's much less than 86 m/s, which is what the first model predicts.\n", + "Getting hit with a penny at that speed might hurt, but it would be unlikely to cause real harm. And it would not damage concrete." + ] + }, + { + "cell_type": "markdown", + "id": "recent-appliance", + "metadata": {}, + "source": [ + "The statistician George Box famously said \"All models are wrong, but\n", + "some are useful.\" He was talking about statistical models, but his wise words apply to all kinds of models. Our first model, which ignores air resistance, is very wrong, and probably not useful. The second model, which takes air resistance into account, is still wrong, but it's better, and it's good enough to refute the myth.\n", + "\n", + "The television show *Mythbusters* has tested the myth of the falling\n", + "penny more carefully; you can view the results at\n", + ". Their work is based on a mathematical model of motion, measurements to determine the force of air resistance on a penny, and a physical model of a human head." + ] + }, + { + "cell_type": "markdown", + "id": "brief-zoning", + "metadata": {}, + "source": [ + "## Computation In Python\n", + "\n", + "Let me show you how I computed the results from the\n", + "previous section using Python.\n", + "First we'll create a variable to represent acceleration due to gravity in meters per second squared (m/s$^2$)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "eleven-marine", + "metadata": {}, + "outputs": [], + "source": [ + "a = 9.8" + ] + }, + { + "cell_type": "markdown", + "id": "upset-myanmar", + "metadata": {}, + "source": [ + "A *variable* is a name that corresponds to a value. In this example, the name is `a` and the value is the number `9.8`.\n", + "\n", + "Suppose we let the penny drop for $3.4$ seconds (s). I'll create a variable to represent this time:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "following-launch", + "metadata": {}, + "outputs": [], + "source": [ + "t = 3.4" + ] + }, + { + "cell_type": "markdown", + "id": "greek-heritage", + "metadata": {}, + "source": [ + "Now we can compute the velocity of the penny after `t` seconds." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "mature-duration", + "metadata": {}, + "outputs": [], + "source": [ + "v = a * t" + ] + }, + { + "cell_type": "markdown", + "id": "serial-pilot", + "metadata": {}, + "source": [ + "Python uses the symbol `*` for multiplication. The other arithmetic operators are `+` for addition, `-` for subtraction, `/` for division, and `**` for exponentiation." + ] + }, + { + "cell_type": "markdown", + "id": "qualified-diabetes", + "metadata": {}, + "source": [ + "After you assign a value to a variable, you can display the value like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "considered-inclusion", + "metadata": {}, + "outputs": [], + "source": [ + "v" + ] + }, + { + "cell_type": "markdown", + "id": "northern-saturday", + "metadata": {}, + "source": [ + "After $3.4$ s, the velocity of the penny is about $33$ m/s (ignoring air resistance). Now let's see how far it would travel during that time:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "valued-electricity", + "metadata": {}, + "outputs": [], + "source": [ + "x = a * t**2 / 2\n", + "x" + ] + }, + { + "cell_type": "markdown", + "id": "yellow-business", + "metadata": {}, + "source": [ + "It would travel about $56$ m. Now, going in the other direction, let's compute the time it takes to fall 381 m, the height of the Empire State Building." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "closed-month", + "metadata": {}, + "outputs": [], + "source": [ + "h = 381" + ] + }, + { + "cell_type": "markdown", + "id": "fuzzy-lease", + "metadata": {}, + "source": [ + "For this computation, we need the square root function, `sqrt`, which is provided by a library called NumPy.\n", + "Before we can use it, we have to import it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "nuclear-clone", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import sqrt" + ] + }, + { + "cell_type": "markdown", + "id": "unlimited-swiss", + "metadata": {}, + "source": [ + "Now we can use it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "quarterly-nightmare", + "metadata": {}, + "outputs": [], + "source": [ + "t = sqrt(2 * h / a)\n", + "t" + ] + }, + { + "cell_type": "markdown", + "id": "velvet-oklahoma", + "metadata": {}, + "source": [ + "With no air resistance, it would take about $8.8$ s for the penny to reach the sidewalk." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "quality-external", + "metadata": {}, + "outputs": [], + "source": [ + "v = a * t\n", + "v" + ] + }, + { + "cell_type": "markdown", + "id": "active-lobby", + "metadata": {}, + "source": [ + "And its velocity on impact would be about $86$ m/s." + ] + }, + { + "cell_type": "markdown", + "id": "human-phase", + "metadata": {}, + "source": [ + "### False Precision\n", + "\n", + "Python displays results with about 16 digits, which gives the impression that they are very precise, but don't be fooled.\n", + "The numbers we get out are only as good as the numbers we put in.\n", + "\n", + "For example, the \"roof height\" of the Empire State Building is $380$ m (according to Wikipedia: ).\n", + "I chose $h=381$ m for this example on the assumption that the observation deck is on the roof and you drop the penny from a 1 meter railing.\n", + "But that's probably not right, so we should treat this value as an approximation where only the first two digits are likely to be right.\n", + "\n", + "If the precision of the inputs is two digits, the precision of the outputs is two digits, *at most*.\n", + "That's why, if the output is `86.41527642726142`, I report it as \"about 86\"." + ] + }, + { + "cell_type": "markdown", + "id": "clinical-blackjack", + "metadata": {}, + "source": [ + "### Computation With Units\n", + "\n", + "The computations we just did use numbers without units.\n", + "When we set `h=381`, we left out the meters, and when we set `a=9.8`, we left out the meters per second squared.\n", + "And, when we got the result `v=86`, we added back the meters per second.\n", + "\n", + "Leaving units out of computation is a common practice, but it tends to cause errors, including the very expensive failure of the Mars Climate Orbiter in 1999 (see ).\n", + "When possible, it is better to include units in the computation.\n", + "\n", + "To represent units, we'll use a library called Pint ().\n", + "To use it, we have to import a function called `UnitRegistry` and call it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "lovely-declaration", + "metadata": {}, + "outputs": [], + "source": [ + "from pint import UnitRegistry\n", + "\n", + "units = UnitRegistry()" + ] + }, + { + "cell_type": "markdown", + "id": "consecutive-sleeve", + "metadata": {}, + "source": [ + "The result is an object that contains variables representing pretty much every unit you've heard of.\n", + "For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "cardiac-class", + "metadata": {}, + "outputs": [], + "source": [ + "units.league" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "phantom-copper", + "metadata": {}, + "outputs": [], + "source": [ + "units.fortnight" + ] + }, + { + "cell_type": "markdown", + "id": "unlike-opera", + "metadata": {}, + "source": [ + "But leagues and fortnights are not part of the International System of Units\n", + "(see ); in the jargon, they are not \"SI units\".\n", + "Instead, we will use `meter` and `second`." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "russian-popularity", + "metadata": {}, + "outputs": [], + "source": [ + "meter = units.meter\n", + "meter" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "endless-paint", + "metadata": {}, + "outputs": [], + "source": [ + "second = units.second\n", + "second" + ] + }, + { + "cell_type": "markdown", + "id": "spiritual-scenario", + "metadata": { + "tags": [] + }, + "source": [ + "To find out what other units are defined, type `units.` (including the period) in the next cell.\n", + "\n", + "If you are on Colab, a pop-up menu should appear with a list of units.\n", + "In other Jupyter environments, you might have to press `TAB` to get the menu." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "saving-suggestion", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "everyday-location", + "metadata": {}, + "source": [ + "We can use `meter` and `second` to create a variable named `a` and give it the value of acceleration due to gravity. " + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "earlier-bandwidth", + "metadata": {}, + "outputs": [], + "source": [ + "a = 9.8 * meter / second**2\n", + "a" + ] + }, + { + "cell_type": "markdown", + "id": "generic-bowling", + "metadata": {}, + "source": [ + "The result is a *quantity* with two parts, called `magnitude` and `units`, which we can access like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "missing-privilege", + "metadata": {}, + "outputs": [], + "source": [ + "a.magnitude" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "fourth-swedish", + "metadata": {}, + "outputs": [], + "source": [ + "a.units" + ] + }, + { + "cell_type": "markdown", + "id": "realistic-techno", + "metadata": {}, + "source": [ + "We can also create a quantity that represents $3.4$ s." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "apart-france", + "metadata": {}, + "outputs": [], + "source": [ + "t = 3.4 * second\n", + "t" + ] + }, + { + "cell_type": "markdown", + "id": "severe-share", + "metadata": {}, + "source": [ + "And use it to compute the distance a penny would fall after `t` seconds with constant acceleration `a`. " + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "alien-scout", + "metadata": {}, + "outputs": [], + "source": [ + "a * t**2 / 2" + ] + }, + { + "cell_type": "markdown", + "id": "wicked-indianapolis", + "metadata": {}, + "source": [ + "Notice that the units of the result are correct.\n", + "If we create a quantity to represent the height of the Empire State Building:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "confidential-costs", + "metadata": {}, + "outputs": [], + "source": [ + "h = 381 * meter" + ] + }, + { + "cell_type": "markdown", + "id": "hollow-programmer", + "metadata": {}, + "source": [ + "We can use it to compute the time the penny would take to reach the sidewalk." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "studied-opera", + "metadata": {}, + "outputs": [], + "source": [ + "t = sqrt(2 * h / a)\n", + "t" + ] + }, + { + "cell_type": "markdown", + "id": "seasonal-laser", + "metadata": {}, + "source": [ + "And the velocity of the penny on impact:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "exterior-greek", + "metadata": {}, + "outputs": [], + "source": [ + "v = a * t\n", + "v" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "impaired-puzzle", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "assert abs(v.magnitude - 86.41527642726142) < 1e-7" + ] + }, + { + "cell_type": "markdown", + "id": "superior-biography", + "metadata": {}, + "source": [ + "As in the previous section, the result is about $86$, but now it has the correct units, m/s.\n", + "\n", + "With Pint quantities, we can convert from one set of units to another like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "antique-landing", + "metadata": {}, + "outputs": [], + "source": [ + "mile = units.mile\n", + "hour = units.hour" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "included-failure", + "metadata": {}, + "outputs": [], + "source": [ + "v.to(mile/hour)" + ] + }, + { + "cell_type": "markdown", + "id": "progressive-charleston", + "metadata": {}, + "source": [ + "If you are more familiar with miles per hour, this result might be easier to interpret.\n", + "And it might give you a sense that this model is not realistic." + ] + }, + { + "cell_type": "markdown", + "id": "continuing-democrat", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This chapter introduces a modeling framework that consists of three steps:\n", + "\n", + "* Abstraction is the process of defining a model by deciding which elements of the real world to include and which can be left out.\n", + "\n", + "* Analysis and simulation are ways to use a model to generate predictions, explain why things behave as they do, and design things that behave as we want.\n", + "\n", + "* Validation is how we test whether the model is right, often by comparing predictions with measurements from the real world.\n", + "\n", + "As a first example, we modeled a penny dropped from the Empire State building, including gravity but ignoring air resistance.\n", + "In the exercises, you'll have a chance to try a better model, including air resistance.\n", + "\n", + "This chapter also presents Pint, a library for doing computation with units, which is convenient for converting between different units and helpful for avoiding catastrophic errors." + ] + }, + { + "cell_type": "markdown", + "id": "thousand-equation", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "Before you go on, you might want to work on the following exercises." + ] + }, + { + "cell_type": "markdown", + "id": "periodic-objective", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + "In mathematical notation, we can write an equation like $v = a t$ and it's understood that we are multiplying $a$ and $t$.\n", + "But that doesn't work in Python. If you put two variables side-by-side, like this:\n", + "\n", + "```\n", + "v = a t\n", + "```\n", + "\n", + "you'll get a *syntax error*, which means that something is wrong with the structure of the program. Try it out so you see what the error message looks like." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "exempt-delight", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "a = 9.8 * meter / second**2\n", + "t = 3.4 * second\n", + "\n", + "v = a * t\n", + "v" + ] + }, + { + "cell_type": "markdown", + "id": "pressing-sugar", + "metadata": {}, + "source": [ + "### Exercise 2\n", + "\n", + "In this chapter we used the `sqrt` function from the NumPy library. NumPy also provides a variable named `pi` that contains an approximation of the mathematical constant $\\pi$.\n", + "We can import it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "legal-observer", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import pi\n", + "pi" + ] + }, + { + "cell_type": "markdown", + "id": "aggregate-mambo", + "metadata": {}, + "source": [ + "NumPy provides other functions we'll use, including `log`, `exp`, `sin`, and `cos`.\n", + "Import `sin` and `cos` from NumPy and compute\n", + "\n", + "$$sin^2 (\\pi/4) + cos^2 (\\pi/4)$$\n", + "\n", + "Note: A mathematical identity tells us that the answer should be $1$." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "pressing-belgium", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "unlimited-delivery", + "metadata": {}, + "source": [ + "### Exercise 3\n", + "\n", + "Suppose you bring a 10 foot pole to the top of the Empire State Building and use it to drop the penny from `h` plus 10 feet.\n", + "\n", + "Define a variable named `foot` that contains the unit `foot` provided by the `UnitRegistry` we called `units`. Define a variable named `pole_height` and give it the value 10 feet.\n", + "\n", + "What happens if you add `h`, which is in units of meters, to `pole_height`, which is in units of feet? What happens if you write the addition the other way around?" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "inner-equivalent", + "metadata": {}, + "outputs": [], + "source": [ + "h = 381 * meter" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "nuclear-thirty", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "available-steering", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "aggressive-climate", + "metadata": {}, + "source": [ + "### Exercise 4\n", + "\n", + "Why would it be nonsensical to add `a` and `t`? What happens if you try?" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "documentary-doctrine", + "metadata": {}, + "outputs": [], + "source": [ + "a = 9.8 * meter / second**2\n", + "t = 3.4 * second" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "primary-partner", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "legal-audio", + "metadata": {}, + "source": [ + "In this example, you should get a `DimensionalityError`, which indicates that you have violated a rule of dimensional analysis: you cannot add quantities with different dimensions.\n", + "\n", + "The error messages you get from Python are big and scary, but if you read them carefully, they contain a lot of useful information.\n", + "\n", + "The last line usually tells you what type of error happened, and sometimes additional information, so you might want to start from the bottom and read up.\n", + "\n", + "The previous lines are a *traceback* of what was happening when the error occurred. The first section of the traceback shows the code you wrote. The following sections are often from Python libraries." + ] + }, + { + "cell_type": "markdown", + "id": "steady-chancellor", + "metadata": { + "tags": [] + }, + "source": [ + "Before you go on, you might want to delete the erroneous code so the notebook can run without errors." + ] + }, + { + "cell_type": "markdown", + "id": "valid-empire", + "metadata": {}, + "source": [ + "### Exercise 5\n", + "\n", + "Suppose instead of dropping the penny, you throw it downward at its terminal velocity, $29$ m/s. How long would it take to fall $381$ m?" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "greenhouse-reason", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "asian-murray", + "metadata": {}, + "source": [ + "### Exercise 6:\n", + "\n", + "So far we have considered two models of a falling penny:\n", + "\n", + "* If we ignore air resistance, the penny falls with constant acceleration, and we can compute the time to reach the sidewalk and the velocity of the penny when it gets there.\n", + "\n", + "* If we take air resistance into account, and drop the penny at its terminal velocity, it falls with constant velocity. \n", + "\n", + "Now let's consider a third model that includes elements of the first two: let's assume that the acceleration of the penny is `a` until the penny reaches $29$ m/s, and then $0$ m/s$^2$ afterwards. What is the total time for the penny to fall $381$ m?" + ] + }, + { + "cell_type": "markdown", + "id": "detailed-minnesota", + "metadata": {}, + "source": [ + "You can break this question into three parts:\n", + "\n", + "1. How long would the penny take to reach $29$ m/s with constant acceleration `a`.\n", + "2. How far would it fall during that time?\n", + "3. How long would it take to fall the remaining distance with constant velocity $29$ m/s?\n", + "\n", + "Suggestion: Assign each intermediate result to a variable with a meaningful name. And assign units to all quantities!" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "secure-crowd", + "metadata": {}, + "outputs": [], + "source": [ + "a = 9.8 * meter / second**2\n", + "h = 381 * meter" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "thirty-minneapolis", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "premier-seeking", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "brave-laundry", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "adjusted-consultation", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "understanding-consortium", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "composed-tunnel", + "metadata": {}, + "source": [ + "### Exercise 7\n", + "\n", + "When I was in high school, the pitcher on the baseball team claimed that when he threw a fastball he was throwing the ball down; that is, the ball left his hand at a downward angle.\n", + "I was skeptical; watching from the side, I thought the ball left his hand at an upward angle.\n", + "\n", + "Can you think of a simple model you could use to settle the argument? What factors would you include and what could you ignore? What quantities would you have to look up or estimate?\n", + "\n", + "I suggest you convert all quantities to SI units like meters and seconds (see )." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "about-complex", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "unique-owner", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "minus-batman", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "exact-vegetable", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "neutral-lightning", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "jewish-secret", + "metadata": {}, + "source": [ + "### Exercise 8\n", + "\n", + "Suppose I run a 10K race in 44:52 (44 minutes and 52 seconds). What is my average pace in minutes per mile?" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "virgin-cambodia", + "metadata": {}, + "outputs": [], + "source": [ + "mile = units.mile\n", + "kilometer = units.kilometer\n", + "minute = units.minute" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "right-intention", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "mineral-sally", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "coral-camel", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "preceding-cricket", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "effective-rendering", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "printable-reply", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "environmental-wallet", + "metadata": { + "tags": [] + }, + "source": [ + "## More Jupyter\n", + "\n", + "You can add and remove cells from a notebook using the buttons in the toolbar and the items in the menu, both of which you should see at the top of this notebook.\n", + "\n", + "Try the following exercises:\n", + "\n", + "1. From the Insert menu select \"Insert cell below\" to add a cell below this one. By default, you get a code cell, as you can see in the pulldown menu that says \"Code\".\n", + "\n", + "2. In the new cell, add a print statement like `print('Hello')`, and run it.\n", + "\n", + "3. Add another cell, select the new cell, and then click on the pulldown menu that says \"Code\" and select \"Markdown\". This makes the new cell a text cell.\n", + "\n", + "4. In the new cell, type some text, and then run it.\n", + "\n", + "5. Use the arrow buttons in the toolbar to move cells up and down.\n", + "\n", + "6. Use the cut, copy, and paste buttons to delete, add, and move cells.\n", + "\n", + "7. As you make changes, Jupyter saves your notebook automatically, but if you want to make sure, you can press the save button, which looks like a floppy disk from the 1990s.\n", + "\n", + "8. Finally, when you are done with a notebook, select \"Close and Halt\" from the File menu." + ] + }, + { + "cell_type": "markdown", + "id": "successful-kentucky", + "metadata": { + "tags": [] + }, + "source": [ + "When you change the contents of a cell, you have to run it again for those changes to have an effect. If you forget to do that, the results can be confusing, because the code you are looking at is not the code you ran.\n", + "\n", + "If you ever lose track of which cells have run, and in what order, you should go to the Kernel menu and select \"Restart & Run All\". Restarting the kernel means that all of your variables get deleted, and running all the cells means all of your code will run again, in the right order.\n", + "\n", + "Select \"Restart & Run All\" now and confirm that it runs all of the cells." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "weighted-quebec", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap02.ipynb b/chapters/chap02.ipynb new file mode 100644 index 000000000..b02e008c0 --- /dev/null +++ b/chapters/chap02.ipynb @@ -0,0 +1,1238 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "victorian-latitude", + "metadata": {}, + "source": [ + "# Bike Share System" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "electoral-turkey", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "unlimited-antenna", + "metadata": {}, + "source": [ + "This chapter presents a simple model of a bike share system and\n", + "demonstrates the features of Python we'll use to develop simulations of real-world systems.\n", + "\n", + "Along the way, we'll make decisions about how to model the system. In\n", + "the next chapter we'll review these decisions and gradually improve the model." + ] + }, + { + "cell_type": "markdown", + "id": "electronic-radius", + "metadata": {}, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "above-denial", + "metadata": {}, + "source": [ + "## Modeling a Bike Share System\n", + "\n", + "Imagine a bike share system for students traveling between Olin College and Wellesley College, which are about three miles apart in eastern Massachusetts.\n", + "\n", + "Suppose the system contains 12 bikes and two bike racks, one at Olin and one at Wellesley, each with the capacity to hold 12 bikes.\n", + "\n", + "As students arrive, check out a bike, and ride to the other campus, the number of bikes in each location changes. In the simulation, we'll need to keep track of where the bikes are. To do that, we'll use a function called `State`, which is defined in the ModSim library." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "incorrect-comparison", + "metadata": {}, + "outputs": [], + "source": [ + "bikeshare = State(olin=10, wellesley=2)" + ] + }, + { + "cell_type": "markdown", + "id": "living-wayne", + "metadata": {}, + "source": [ + "The equations in parentheses create two variables, `olin` and `wellesley`, and give them the values `10` and `2`.\n", + "The `State` function stores these variables and their values in a `State` object, which gets assigned to a new variable named `bikeshare`.\n", + "\n", + "Variables stored inside a `State` object are called *state variables*.\n", + "In this example, the state variables represent the number of\n", + "bikes at each location. Their values indicate that there are 10 bikes at Olin and 2 at Wellesley. \n", + "\n", + "The `State` object is assigned to a new variable named `bikeshare`.\n", + "We can get the value of a variable in a `State` object using the *dot operator*, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "brief-diversity", + "metadata": {}, + "outputs": [], + "source": [ + "bikeshare.olin" + ] + }, + { + "cell_type": "markdown", + "id": "intermediate-midwest", + "metadata": {}, + "source": [ + "And this:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "designed-brazilian", + "metadata": {}, + "outputs": [], + "source": [ + "bikeshare.wellesley" + ] + }, + { + "cell_type": "markdown", + "id": "phantom-oklahoma", + "metadata": {}, + "source": [ + "Or, to display all of the state variables and their values, you can enter just the name of the object:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "impaired-potter", + "metadata": {}, + "outputs": [], + "source": [ + "bikeshare" + ] + }, + { + "cell_type": "markdown", + "id": "vital-journal", + "metadata": {}, + "source": [ + "These values make up the *state* of the system." + ] + }, + { + "cell_type": "markdown", + "id": "fleet-beijing", + "metadata": { + "tags": [] + }, + "source": [ + "The ModSim library provides a function called `show` that displays a `State` object as a table." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "basic-fabric", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "show(bikeshare)" + ] + }, + { + "cell_type": "markdown", + "id": "specified-definition", + "metadata": { + "tags": [] + }, + "source": [ + "You don't have to use `show`, but I think the results look better." + ] + }, + { + "cell_type": "markdown", + "id": "delayed-ocean", + "metadata": {}, + "source": [ + "We can update the state by assigning new values to the variables. \n", + "For example, if a student moves a bike from Olin to Wellesley, we can figure out the new values and assign them:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "floppy-trainer", + "metadata": {}, + "outputs": [], + "source": [ + "bikeshare.olin = 9\n", + "bikeshare.wellesley = 3" + ] + }, + { + "cell_type": "markdown", + "id": "natural-gossip", + "metadata": {}, + "source": [ + "Or we can use *update operators*, `-=` and `+=`, to subtract 1 from\n", + "`olin` and add 1 to `wellesley`:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "hungarian-bride", + "metadata": {}, + "outputs": [], + "source": [ + "bikeshare.olin -= 1\n", + "bikeshare.wellesley += 1" + ] + }, + { + "cell_type": "markdown", + "id": "radical-mills", + "metadata": {}, + "source": [ + "The result is the same either way." + ] + }, + { + "cell_type": "markdown", + "id": "controversial-opportunity", + "metadata": {}, + "source": [ + "## Defining Functions\n", + "\n", + "So far we have used functions defined in NumPy and the ModSim library. Now we're going to define our own functions.\n", + "\n", + "When you are developing code in Jupyter, it is often efficient to write a few lines of code, test them to confirm they do what you intend, and then use them to define a new function. For example, these lines move a bike from Olin to Wellesley:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "vertical-drawing", + "metadata": {}, + "outputs": [], + "source": [ + "bikeshare.olin -= 1\n", + "bikeshare.wellesley += 1" + ] + }, + { + "cell_type": "markdown", + "id": "approximate-rolling", + "metadata": {}, + "source": [ + "Rather than repeat them every time a bike moves, we can define a new\n", + "function:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "significant-nutrition", + "metadata": {}, + "outputs": [], + "source": [ + "def bike_to_wellesley():\n", + " bikeshare.olin -= 1\n", + " bikeshare.wellesley += 1" + ] + }, + { + "cell_type": "markdown", + "id": "generous-tracker", + "metadata": {}, + "source": [ + "`def` is a special word in Python that indicates we are defining a new\n", + "function. The name of the function is `bike_to_wellesley`. The empty\n", + "parentheses indicate that this function requires no additional\n", + "information when it runs. The colon indicates the beginning of an\n", + "indented *code block*.\n", + "\n", + "The next two lines are the *body* of the function. They have to be\n", + "indented; by convention, the indentation is four spaces.\n", + "\n", + "When you define a function, it has no immediate effect. The body of the\n", + "function doesn't run until you *call* the function. Here's how to call\n", + "this function:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "moving-jurisdiction", + "metadata": {}, + "outputs": [], + "source": [ + "bike_to_wellesley()" + ] + }, + { + "cell_type": "markdown", + "id": "meaningful-christmas", + "metadata": {}, + "source": [ + "When you call the function, it runs the statements in the body, which\n", + "update the variables of the `bikeshare` object; you can check by\n", + "displaying the new state." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "proper-symposium", + "metadata": {}, + "outputs": [], + "source": [ + "show(bikeshare)" + ] + }, + { + "cell_type": "markdown", + "id": "eleven-brook", + "metadata": {}, + "source": [ + "When you call a function, you have to include the parentheses. If you\n", + "leave them out, you get this:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "identical-yacht", + "metadata": {}, + "outputs": [], + "source": [ + "bike_to_wellesley" + ] + }, + { + "cell_type": "markdown", + "id": "premier-youth", + "metadata": {}, + "source": [ + "This result indicates that `bike_to_wellesley` is a function. You don't have to know what `__main__` means, but if you see something like this, it probably means that you named a function but didn't actually call it.\n", + "So don't forget the parentheses." + ] + }, + { + "cell_type": "markdown", + "id": "brazilian-medicare", + "metadata": {}, + "source": [ + "## Print Statements\n", + "\n", + "As you write more complicated programs, it is easy to lose track of what\n", + "is going on. One of the most useful tools for debugging is the *print statement*, which displays text in the Jupyter notebook.\n", + "\n", + "Normally when Jupyter runs the code in a cell, it displays the value of\n", + "the last line of code. For example, if you run:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "heavy-patrol", + "metadata": {}, + "outputs": [], + "source": [ + "bikeshare.olin\n", + "bikeshare.wellesley" + ] + }, + { + "cell_type": "markdown", + "id": "ancient-projection", + "metadata": {}, + "source": [ + "Jupyter runs both lines, but it only displays the value of the\n", + "second. If you want to display more than one value, you can use\n", + "print statements:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "french-preference", + "metadata": {}, + "outputs": [], + "source": [ + "print(bikeshare.olin)\n", + "print(bikeshare.wellesley)" + ] + }, + { + "cell_type": "markdown", + "id": "original-hollywood", + "metadata": {}, + "source": [ + "When you call the `print` function, you can put a variable in\n", + "parentheses, as in the previous example, or you can provide a sequence\n", + "of variables separated by commas, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "alternative-keyboard", + "metadata": {}, + "outputs": [], + "source": [ + "print(bikeshare.olin, bikeshare.wellesley)" + ] + }, + { + "cell_type": "markdown", + "id": "described-produce", + "metadata": {}, + "source": [ + "Python looks up the values of the variables and displays them; in this\n", + "example, it displays two values on the same line, with a space between\n", + "them.\n", + "\n", + "Print statements are useful for debugging functions. For example, we can\n", + "add a print statement to `bike_to_wellesley`, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "robust-holly", + "metadata": {}, + "outputs": [], + "source": [ + "def bike_to_wellesley():\n", + " print('Moving a bike to Wellesley')\n", + " bikeshare.olin -= 1\n", + " bikeshare.wellesley += 1" + ] + }, + { + "cell_type": "markdown", + "id": "vital-lender", + "metadata": {}, + "source": [ + "Each time we call this version of the function, it displays a message,\n", + "which can help us keep track of what the program is doing.\n", + "The message in this example is a *string*, which is a sequence of\n", + "letters and other symbols in quotes.\n", + "\n", + "Just like `bike_to_wellesley`, we can define a function that moves a\n", + "bike from Wellesley to Olin:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "fifteen-atmosphere", + "metadata": {}, + "outputs": [], + "source": [ + "def bike_to_olin():\n", + " print('Moving a bike to Olin')\n", + " bikeshare.wellesley -= 1\n", + " bikeshare.olin += 1" + ] + }, + { + "cell_type": "markdown", + "id": "requested-glasgow", + "metadata": {}, + "source": [ + "And call it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "matched-narrow", + "metadata": {}, + "outputs": [], + "source": [ + "bike_to_olin()" + ] + }, + { + "cell_type": "markdown", + "id": "sitting-semiconductor", + "metadata": {}, + "source": [ + "One benefit of defining functions is that you avoid repeating chunks of\n", + "code, which makes programs smaller. Another benefit is that the name you\n", + "give the function documents what it does, which makes programs more\n", + "readable." + ] + }, + { + "cell_type": "markdown", + "id": "enhanced-maintenance", + "metadata": {}, + "source": [ + "## If Statements\n", + "\n", + "At this point we have functions that simulate moving bikes; now let's think about simulating customers. As a simple model of customer behavior, I will use a random number generator to determine when customers arrive at each station.\n", + "\n", + "The ModSim library provides a function called `flip` that generates random \"coin tosses\".\n", + "When you call it, you provide a probability between 0 and 1, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "29c1f41a", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# this line sets the random number generator so the results in\n", + "# the book are the same every time we run it\n", + "np.random.seed(17)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "illegal-metropolitan", + "metadata": {}, + "outputs": [], + "source": [ + "flip(0.7)" + ] + }, + { + "cell_type": "markdown", + "id": "appropriate-funds", + "metadata": {}, + "source": [ + "The result is one of two values: `True` with probability 0.7 (in this example) or `False`\n", + "with probability 0.3. If you run `flip` like this 100 times, you should\n", + "get `True` about 70 times and `False` about 30 times. But the results\n", + "are random, so they might differ from these expectations.\n", + "\n", + "`True` and `False` are special values defined by Python. \n", + "They are called *boolean* values because they are\n", + "related to Boolean algebra ().\n", + "\n", + "Note that they are not strings. There is a difference between `True`, which is a boolean value, and `'True'`, which is a string.\n", + "\n", + "We can use boolean values to control the behavior of the program, using an *if statement*:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "excessive-murder", + "metadata": {}, + "outputs": [], + "source": [ + "if flip(0.5):\n", + " print('heads')" + ] + }, + { + "cell_type": "markdown", + "id": "seventh-profile", + "metadata": {}, + "source": [ + "If the result from `flip` is `True`, the program displays the string\n", + "`'heads'`. Otherwise it does nothing.\n", + "\n", + "The syntax for `if` statements is similar to the syntax for\n", + "function definitions: the first line has to end with a colon, and the\n", + "lines inside the `if` statement have to be indented.\n", + "\n", + "Optionally, you can add an *else clause* to indicate what should\n", + "happen if the result is `False`:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "fundamental-nursing", + "metadata": {}, + "outputs": [], + "source": [ + "if flip(0.5):\n", + " print('heads')\n", + "else:\n", + " print('tails') " + ] + }, + { + "cell_type": "markdown", + "id": "recovered-chemical", + "metadata": {}, + "source": [ + "If you run the previous cell a few times, it should print `heads` about half the time, and `tails` about half the time.\n", + "\n", + "Now we can use `flip` to simulate the arrival of customers who want to\n", + "borrow a bike. Suppose students arrive at the Olin station every two\n", + "minutes on average.\n", + "In that case, the chance of an arrival during any one-minute period is 50%, and we can simulate it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "twenty-health", + "metadata": {}, + "outputs": [], + "source": [ + "if flip(0.5):\n", + " bike_to_wellesley()" + ] + }, + { + "cell_type": "markdown", + "id": "difficult-construction", + "metadata": {}, + "source": [ + "If students arrive at the Wellesley station every three minutes, on average,\n", + "the chance of an arrival during any one-minute period is 33%, and we can\n", + "simulate it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "played-character", + "metadata": {}, + "outputs": [], + "source": [ + "if flip(0.33):\n", + " bike_to_olin()" + ] + }, + { + "cell_type": "markdown", + "id": "standard-party", + "metadata": {}, + "source": [ + "We can combine these snippets into a function that simulates a *time step*, which is an interval of time, in this case one minute:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "ecological-colon", + "metadata": {}, + "outputs": [], + "source": [ + "def step():\n", + " if flip(0.5):\n", + " bike_to_wellesley()\n", + " \n", + " if flip(0.33):\n", + " bike_to_olin()" + ] + }, + { + "cell_type": "markdown", + "id": "amateur-exposure", + "metadata": {}, + "source": [ + "Then we can simulate a time step like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "mediterranean-german", + "metadata": {}, + "outputs": [], + "source": [ + "step()" + ] + }, + { + "cell_type": "markdown", + "id": "sought-mobile", + "metadata": {}, + "source": [ + "Depending on the results from `flip`, this function might move a bike to Olin, or to Wellesley, or neither, or both." + ] + }, + { + "cell_type": "markdown", + "id": "organic-proportion", + "metadata": {}, + "source": [ + "## Parameters\n", + "\n", + "The previous version of `step` is fine if the arrival probabilities\n", + "never change, but in reality they vary over time.\n", + "\n", + "So instead of putting the constant values 0.5 and 0.33 in `step`, we can replace them with *parameters*.\n", + "Parameters are variables whose values are set when a function is called.\n", + "\n", + "Here's a version of `step` that takes two parameters, `p1` and `p2`:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "hollywood-shopping", + "metadata": {}, + "outputs": [], + "source": [ + "def step(p1, p2):\n", + " if flip(p1):\n", + " bike_to_wellesley()\n", + " \n", + " if flip(p2):\n", + " bike_to_olin()" + ] + }, + { + "cell_type": "markdown", + "id": "encouraging-arkansas", + "metadata": {}, + "source": [ + "The values of `p1` and `p2` are not set inside this function; instead,\n", + "they are provided when the function is called, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "buried-alert", + "metadata": {}, + "outputs": [], + "source": [ + "step(0.5, 0.33)" + ] + }, + { + "cell_type": "markdown", + "id": "aggregate-dynamics", + "metadata": {}, + "source": [ + "The values you provide when you call the function are called\n", + "*arguments*. The arguments, `0.5` and `0.33` in this example, get\n", + "assigned to the parameters, `p1` and `p2`, in order. So running this\n", + "function has the same effect as:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "recognized-denmark", + "metadata": {}, + "outputs": [], + "source": [ + "p1 = 0.5\n", + "p2 = 0.33\n", + "\n", + "if flip(p1):\n", + " bike_to_wellesley()\n", + " \n", + "if flip(p2):\n", + " bike_to_olin()" + ] + }, + { + "cell_type": "markdown", + "id": "raised-museum", + "metadata": {}, + "source": [ + "The advantage of using parameters is that you can call the same function many times, providing different arguments each time.\n", + "\n", + "Adding parameters to a function is called *generalization*, because it makes the function more general; without parameters, the function always does the same thing; with parameters, it can do a range of things." + ] + }, + { + "cell_type": "markdown", + "id": "scenic-african", + "metadata": {}, + "source": [ + "## For Loops\n", + "\n", + "At some point you will get sick of running cells over and over.\n", + "Fortunately, there is an easy way to repeat a chunk of code, the *for loop*. Here's an example:" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "polish-river", + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(3):\n", + " print(i)\n", + " bike_to_wellesley()" + ] + }, + { + "cell_type": "markdown", + "id": "compatible-conspiracy", + "metadata": {}, + "source": [ + "The syntax here should look familiar; the first line ends with a\n", + "colon, and the lines inside the `for` loop are indented. The other\n", + "elements of the loop are:\n", + "\n", + "- The words `for` and `in` are special words we have to use in a for\n", + " loop.\n", + "\n", + "- `range` is a Python function we use to control the number of times the loop runs.\n", + "\n", + "- `i` is a *loop variable* that gets created when the for loop runs.\n", + "\n", + "When this loop runs, it runs the statements inside the loop three times. The first time, the value of `i` is `0`; the second time, it is `1`; the third time, it is `2`.\n", + "\n", + "Each time through the loop, it prints the value of `i` and moves one bike to Wellesley." + ] + }, + { + "cell_type": "markdown", + "id": "breeding-groove", + "metadata": {}, + "source": [ + "## TimeSeries\n", + "\n", + "When we run a simulation, we often want to save the results for later analysis. The ModSim library provides a `TimeSeries` object for this purpose. A `TimeSeries` contains a sequence of timestamps and a\n", + "corresponding sequence of quantities.\n", + "\n", + "In this example, the timestamps are integers representing minutes and the quantities are the number of bikes at one location.\n", + "\n", + "Since we have moved a number of bikes around, let's start again with a new `State` object." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "every-consultation", + "metadata": {}, + "outputs": [], + "source": [ + "bikeshare = State(olin=10, wellesley=2)" + ] + }, + { + "cell_type": "markdown", + "id": "cross-sharp", + "metadata": {}, + "source": [ + "We can create a new, empty `TimeSeries` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "changing-planet", + "metadata": {}, + "outputs": [], + "source": [ + "results = TimeSeries()" + ] + }, + { + "cell_type": "markdown", + "id": "attractive-revision", + "metadata": {}, + "source": [ + "And we can add a quantity like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "aquatic-richardson", + "metadata": {}, + "outputs": [], + "source": [ + "results[0] = bikeshare.olin" + ] + }, + { + "cell_type": "markdown", + "id": "searching-funeral", + "metadata": {}, + "source": [ + "The number in brackets is the timestamp, also called a *label*.\n", + "\n", + "We can use a `TimeSeries` inside a for loop to store the results of the simulation:" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "english-titanium", + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(3):\n", + " print(i)\n", + " step(0.6, 0.6)\n", + " results[i+1] = bikeshare.olin" + ] + }, + { + "cell_type": "markdown", + "id": "prospective-joining", + "metadata": {}, + "source": [ + "Each time through the loop, we print the value of `i` and call `step`, which updates `bikeshare`.\n", + "Then we store the number of bikes at Olin in `results`. \n", + "We use the loop variable, `i`, to compute the timestamp, `i+1`.\n", + "\n", + "The first time through the loop, the value of `i` is `0`, so the timestamp is `1`.\n", + "The last time, the value of `i` is `2`, so the timestamp is `3`.\n", + "\n", + "When the loop exits, `results` contains 4 timestamps, from 0 through\n", + "3, and the number of bikes at Olin at the end of each time step.\n", + "\n", + "We can display the `TimeSeries` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "indonesian-singing", + "metadata": {}, + "outputs": [], + "source": [ + "show(results)" + ] + }, + { + "cell_type": "markdown", + "id": "small-encoding", + "metadata": {}, + "source": [ + "The left column is the timestamps; the right column is the quantities." + ] + }, + { + "cell_type": "markdown", + "id": "following-contrary", + "metadata": {}, + "source": [ + "## Plotting\n", + "\n", + "`results` provides a function called `plot` we can use to plot\n", + "the results, and the ModSim library provides `decorate`, which we can use to label the axes and give the figure a title:" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "saved-hands", + "metadata": {}, + "outputs": [], + "source": [ + "results.plot()\n", + "\n", + "decorate(title='Olin-Wellesley bikeshare',\n", + " xlabel='Time step (min)', \n", + " ylabel='Number of bikes')" + ] + }, + { + "cell_type": "markdown", + "id": "egyptian-korea", + "metadata": {}, + "source": [ + "The result should be a plot with time on the $x$-axis and the number of bikes on the $y$-axis.\n", + "Since we only ran three time steps, it might not be very interesting." + ] + }, + { + "cell_type": "markdown", + "id": "limited-interstate", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This chapter introduces the tools we need to run simulations, record the results, and plot them.\n", + "\n", + "We used a `State` object to represent the state of the system.\n", + "Then we used the `flip` function and an `if` statement to simulate a single time step.\n", + "We used a `for` loop to simulate a series of steps, and a `TimeSeries` to record the results.\n", + "Finally, we used `plot` and `decorate` to plot the results.\n", + "\n", + "In the next chapter, we will extend this simulation to make it a little more realistic." + ] + }, + { + "cell_type": "markdown", + "id": "fallen-surprise", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "Before you go on, you might want to work on the following exercises." + ] + }, + { + "cell_type": "markdown", + "id": "capital-internship", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + "What happens if you spell the name of a state variable wrong? Edit the following cell, change the spelling of `wellesley`, and run it.\n", + "\n", + "The error message uses the word *attribute*, which is another name for what we are calling a state variable. " + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "helpful-zambia", + "metadata": {}, + "outputs": [], + "source": [ + "bikeshare = State(olin=10, wellesley=2)\n", + "\n", + "bikeshare.wellesley" + ] + }, + { + "cell_type": "markdown", + "id": "dirty-multiple", + "metadata": {}, + "source": [ + "### Exercise 2\n", + "\n", + "Make a `State` object with a third state variable, called `downtown`, with initial value 0, and display the state of the system." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "beneficial-mainland", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "christian-madrid", + "metadata": {}, + "source": [ + "### Exercise 3\n", + "\n", + "Wrap the code in the chapter in a function named `run_simulation` that takes three parameters, named `p1`, `p2`, and `num_steps`.\n", + "\n", + "It should:\n", + "\n", + "1. Create a `TimeSeries` object to hold the results.\n", + "\n", + "2. Use a for loop to run `step` the number of times specified by `num_steps`, passing along the specified values of `p1` and `p2`.\n", + "\n", + "3. After each step, it should save the number of bikes at Olin in the `TimeSeries`.\n", + "\n", + "4. After the for loop, it should plot the results and\n", + "\n", + "5. Decorate the axes.\n", + "\n", + "To test your function:\n", + "\n", + "1. Create a `State` object with the initial state of the system.\n", + "\n", + "2. Call `run_simulation` with parameters `p1=0.3`, `p2=0.2`, and `num_steps=60`." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "former-frost", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "spare-honduras", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "instructional-finnish", + "metadata": {}, + "source": [ + "## Under the Hood\n", + "\n", + "This section contains additional information about the functions we've used and pointers to their documentation.\n", + "\n", + "You don't need to know anything in this section, so if you are already feeling overwhelmed, you might want to skip it.\n", + "But if you are curious, read on." + ] + }, + { + "cell_type": "markdown", + "id": "quick-citizen", + "metadata": {}, + "source": [ + "`State` and `TimeSeries` objects are based on the `Series` object defined by the Pandas library.\n", + "The documentation is at .\n", + "\n", + "`Series` objects provide their own `plot` function, which is why we call it like this:\n", + "\n", + "```\n", + "results.plot()\n", + "```\n", + "\n", + "Instead of like this:\n", + "\n", + "```\n", + "plot(results)\n", + "```\n", + "\n", + "You can read the documentation of `Series.plot` at ." + ] + }, + { + "cell_type": "markdown", + "id": "digital-stretch", + "metadata": {}, + "source": [ + "`decorate` is based on Matplotlib, which is a widely used plotting library for Python. Matplotlib provides separate functions for `title`, `xlabel`, and `ylabel`.\n", + "`decorate` makes them a little easier to use.\n", + "For the list of keyword arguments you can pass to `decorate`, see .\n", + "\n", + "The `flip` function uses NumPy's `random` function to generate a random number between 0 and 1, then returns `True` or `False` with the given probability.\n", + "\n", + "You can get the source code for `flip` (or any other function) by running the following cell." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "agricultural-midwest", + "metadata": {}, + "outputs": [], + "source": [ + "source_code(flip)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "junior-lindsay", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap03.ipynb b/chapters/chap03.ipynb new file mode 100644 index 000000000..1716e158e --- /dev/null +++ b/chapters/chap03.ipynb @@ -0,0 +1,864 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "signal-format", + "metadata": {}, + "source": [ + "# Iteration" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "electoral-turkey", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "coral-steering", + "metadata": {}, + "source": [ + "To paraphrase two Georges, \"All models are wrong, but some models are\n", + "more wrong than others.\" This chapter demonstrates the process we\n", + "use to make models less wrong.\n", + "\n", + "As an example, we'll review the bike share model from the previous\n", + "chapter, consider its strengths and weaknesses, and gradually improve\n", + "it. We'll also see ways to use the model to understand the behavior of\n", + "the system and evaluate designs intended to make it work better." + ] + }, + { + "cell_type": "markdown", + "id": "plastic-trigger", + "metadata": {}, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "twelve-length", + "metadata": {}, + "source": [ + "## Iterative Modeling\n", + "\n", + "The model we have so far is simple, but it is based on unrealistic\n", + "assumptions. Before you go on, take a minute to review the model from\n", + "the previous chapter. What assumptions is it based on? Make a list of\n", + "ways this model might be unrealistic; that is, what are the differences between the model and the real world?\n", + "\n", + "Here are some of the differences on my list:\n", + "\n", + "- In the model, a student is equally likely to arrive during any\n", + " one-minute period. In reality, this probability varies depending on time of day, day of the week, etc.\n", + "\n", + "- The model does not account for travel time from one bike station to another.\n", + "\n", + "- The model does not check whether a bike is available, so it's\n", + " possible for the number of bikes to be negative (as you might have\n", + " noticed in some of your simulations)." + ] + }, + { + "cell_type": "markdown", + "id": "sweet-heater", + "metadata": {}, + "source": [ + "Some of these modeling decisions are better than others. For example,\n", + "the first assumption might be reasonable if we simulate the system for a short period of time, like one hour.\n", + "\n", + "The second assumption is not very realistic, but it might not affect the results very much, depending on what we use the model for.\n", + "On the other hand, the third assumption seems more problematic.\n", + "It is relatively easy to fix, though; in this chapter, we'll fix it.\n", + "\n", + "This process, starting with a simple model, identifying the most\n", + "important problems, and making gradual improvements, is called\n", + "*iterative modeling*.\n", + "\n", + "For any physical system, there are many possible models, based on\n", + "different assumptions and simplifications. It often takes several\n", + "iterations to develop a model that is good enough for the intended\n", + "purpose, but no more complicated than necessary." + ] + }, + { + "cell_type": "markdown", + "id": "female-salem", + "metadata": {}, + "source": [ + "## More Than One State Object\n", + "\n", + "Before we go on, I want to make a few changes to the code from the\n", + "previous chapter. First I'll generalize the functions we wrote so they\n", + "take a `State` object as a parameter. Then, I'll make the code more\n", + "readable by adding documentation.\n", + "\n", + "Here is one of the functions from the previous chapter, `bike_to_wellesley`:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "embedded-heavy", + "metadata": {}, + "outputs": [], + "source": [ + "def bike_to_wellesley():\n", + " bikeshare.olin -= 1\n", + " bikeshare.wellesley += 1" + ] + }, + { + "cell_type": "markdown", + "id": "artificial-remains", + "metadata": {}, + "source": [ + "When this function is called, it modifies `bikeshare`. As long as there\n", + "is only one `State` object, that's fine, but what if there is more than\n", + "one bike share system in the world? Or what if we want to run more than\n", + "one simulation?\n", + "\n", + "This function would be more flexible if it took a `State` object as a\n", + "parameter. Here's what that looks like:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "unusual-advancement", + "metadata": {}, + "outputs": [], + "source": [ + "def bike_to_wellesley(state):\n", + " state.olin -= 1\n", + " state.wellesley += 1" + ] + }, + { + "cell_type": "markdown", + "id": "serial-mortality", + "metadata": {}, + "source": [ + "The name of the parameter is `state`, rather than `bikeshare`, as a\n", + "reminder that the value of `state` could be any `State` object, not just the one we called `bikeshare`.\n", + "\n", + "This version of `bike_to_wellesley` requires a `State` object as a\n", + "parameter, so we have to provide one when we call it:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "packed-hungarian", + "metadata": {}, + "outputs": [], + "source": [ + "bikeshare = State(olin=10, wellesley=2)\n", + "bike_to_wellesley(bikeshare)" + ] + }, + { + "cell_type": "markdown", + "id": "fewer-rhythm", + "metadata": {}, + "source": [ + "Again, the argument we provide gets assigned to the parameter, so this\n", + "function call has the same effect as:\n", + "\n", + "```\n", + "state = bikeshare \n", + "state.olin -= 1 \n", + "state.wellesley += 1\n", + "```\n", + "\n", + "Now we can create as many `State` objects as we want:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "right-assessment", + "metadata": {}, + "outputs": [], + "source": [ + "bikeshare1 = State(olin=10, wellesley=2)\n", + "bikeshare2 = State(olin=2, wellesley=10)" + ] + }, + { + "cell_type": "markdown", + "id": "occupied-navigator", + "metadata": {}, + "source": [ + "And update them independently:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "cleared-advocacy", + "metadata": {}, + "outputs": [], + "source": [ + "bike_to_wellesley(bikeshare1)\n", + "bike_to_wellesley(bikeshare2)" + ] + }, + { + "cell_type": "markdown", + "id": "brown-ferry", + "metadata": {}, + "source": [ + "Changes in `bikeshare1` do not affect `bikeshare2`, and vice versa. So\n", + "we can simulate different bike share systems, or run multiple\n", + "simulations of the same system." + ] + }, + { + "cell_type": "markdown", + "id": "magnetic-packing", + "metadata": {}, + "source": [ + "## Documentation\n", + "\n", + "Another problem with the code we have so far is that it contains no\n", + "*documentation*.\n", + "Documentation is text we add to a program to help\n", + "other programmers read and understand it. It has no effect on the\n", + "program when it runs.\n", + "\n", + "There are two kinds of documentation, *docstrings* and *comments*:\n", + "\n", + "* A docstring is a string in triple quotes that appears at the beginning of a function.\n", + "\n", + "* A comment is a line of text that begins with a hash symbol, `#`.\n", + "\n", + "Here's a version of `bike_to_olin` with a docstring and a comment." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "moral-parallel", + "metadata": {}, + "outputs": [], + "source": [ + "def bike_to_olin(state):\n", + " \"\"\"Move one bike from Wellesley to Olin.\n", + " \n", + " state: bikeshare State object\n", + " \"\"\"\n", + " # We decrease one state variable and increase the\n", + " # other so the total number of bikes is unchanged.\n", + " state.wellesley -= 1\n", + " state.olin += 1" + ] + }, + { + "cell_type": "markdown", + "id": "therapeutic-utility", + "metadata": {}, + "source": [ + "Docstrings follow a conventional format:\n", + "\n", + "- The first line is a single sentence that describes what the function does.\n", + "\n", + "- The following lines explain what the parameters are.\n", + "\n", + "A function's docstring should include the information someone needs to\n", + "know to *use* the function; it should not include details about how the function works.\n", + "\n", + "Comments provide details about how the function works, especially if there is something that would not be obvious to someone reading the program." + ] + }, + { + "cell_type": "markdown", + "id": "american-clear", + "metadata": {}, + "source": [ + "## Negative Bikes\n", + "\n", + "The changes we've made so far improve the quality of the code, but we\n", + "haven't done anything to improve the quality of the model. Let's do that now.\n", + "\n", + "Currently the simulation does not check whether a bike is available when a customer arrives, so the number of bikes at a location can be\n", + "negative. That's not very realistic.\n", + "\n", + "Here's a version of `bike_to_olin` that fixes the problem:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "divine-leisure", + "metadata": {}, + "outputs": [], + "source": [ + "def bike_to_olin(state):\n", + " if state.wellesley == 0:\n", + " return\n", + " state.wellesley -= 1\n", + " state.olin += 1" + ] + }, + { + "cell_type": "markdown", + "id": "decimal-denver", + "metadata": {}, + "source": [ + "The first line checks whether the number of bikes at Wellesley is zero. If so, it uses a *return statement*, which causes the function to end immediately, without running the rest of the statements. So if there are no bikes at Wellesley, we return from `bike_to_olin` without changing the state.\n", + "\n", + "We can test it by initializing the state with no bikes at Wellesley and calling `bike_to_olin`." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "choice-cooking", + "metadata": {}, + "outputs": [], + "source": [ + "bikeshare = State(olin=12, wellesley=0)\n", + "bike_to_olin(bikeshare)" + ] + }, + { + "cell_type": "markdown", + "id": "persistent-denmark", + "metadata": {}, + "source": [ + "The state of the system should be unchanged." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "twelve-moderator", + "metadata": {}, + "outputs": [], + "source": [ + "show(bikeshare)" + ] + }, + { + "cell_type": "markdown", + "id": "criminal-general", + "metadata": {}, + "source": [ + "No more negative bikes (at least at Wellesley)." + ] + }, + { + "cell_type": "markdown", + "id": "gorgeous-found", + "metadata": {}, + "source": [ + "## Comparison Operators\n", + "\n", + "The updated version of `bike_to_olin` uses the equals operator, `==`, which compares two values and returns `True` if they are equal, and `False` otherwise.\n", + "\n", + "It is easy to confuse the equals operator with the assignment operator, `=`, which assigns a value to a variable. For example, the following statement creates a variable, `x`, if it doesn't already exist, and gives it the value `5`." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "level-burns", + "metadata": {}, + "outputs": [], + "source": [ + "x = 5" + ] + }, + { + "cell_type": "markdown", + "id": "weighted-monster", + "metadata": {}, + "source": [ + "On the other hand, the following statement checks whether `x` is `5` and\n", + "returns `True` or `False`. It does not create `x` or change its value." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "civic-remains", + "metadata": {}, + "outputs": [], + "source": [ + "x == 5" + ] + }, + { + "cell_type": "markdown", + "id": "forward-perth", + "metadata": {}, + "source": [ + "You can use the equals operator in an `if` statement, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "affecting-naples", + "metadata": {}, + "outputs": [], + "source": [ + "if x == 5:\n", + " print('yes, x is 5')" + ] + }, + { + "cell_type": "markdown", + "id": "consolidated-anatomy", + "metadata": {}, + "source": [ + "If you make a mistake and use `=` in an `if` statement, like this:\n", + "\n", + "```\n", + "if x = 5:\n", + " print('yes, x is 5')\n", + "```\n", + "\n", + "That's a *syntax error*, which means that the structure of the program is invalid. Python will print an error message and the program won't run." + ] + }, + { + "cell_type": "markdown", + "id": "twelve-defensive", + "metadata": {}, + "source": [ + "The equals operator is one of Python's *comparison operators*; the complete list is in the following table.\n", + "\n", + "| Operation | Symbol |\n", + "|-----------------------|--------|\n", + "| Less than | `<` |\n", + "| Greater than | `>` |\n", + "| Less than or equal | `<=` |\n", + "| Greater than or equal | `>=` |\n", + "| Equal | `==` |\n", + "| Not equal | `!=` |" + ] + }, + { + "cell_type": "markdown", + "id": "center-sequence", + "metadata": {}, + "source": [ + "## Metrics\n", + "\n", + "Getting back to the bike share system, at this point we have the ability to simulate the behavior of the system. Since the arrival of customers is random, the state of the system is different each time we run a\n", + "simulation. Models like this are called random or *stochastic*; models\n", + "that do the same thing every time they run are *deterministic*.\n", + "\n", + "Suppose we want to use our model to predict how well the bike share\n", + "system will work, or to design a system that works better. First, we\n", + "have to decide what we mean by \"how well\" and \"better\".\n", + "\n", + "From the customer's point of view, we might like to know the probability of finding an available bike. From the system-owner's point of view, we might want to minimize the number of customers who don't get a bike when they want one, or maximize the number of bikes in use. Statistics like these that quantify how well the system works are called *metrics*.\n", + "\n", + "As an example, let's measure the number of unhappy customers.\n", + "Here's a version of `bike_to_olin` that keeps track of the number of\n", + "customers who arrive at a station with no bikes:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "arbitrary-ferry", + "metadata": {}, + "outputs": [], + "source": [ + "def bike_to_olin(state):\n", + " if state.wellesley == 0:\n", + " state.wellesley_empty += 1\n", + " return\n", + " state.wellesley -= 1\n", + " state.olin += 1" + ] + }, + { + "cell_type": "markdown", + "id": "severe-contact", + "metadata": {}, + "source": [ + "If a customer arrives at the Wellesley station and finds no bike\n", + "available, `bike_to_olin` updates `wellesley_empty`, which counts the\n", + "number of unhappy customers.\n", + "\n", + "This function only works if we initialize `wellesley_empty` when we\n", + "create the `State` object, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "cardiovascular-montgomery", + "metadata": {}, + "outputs": [], + "source": [ + "bikeshare = State(olin=12, wellesley=0, \n", + " wellesley_empty=0)" + ] + }, + { + "cell_type": "markdown", + "id": "computational-prior", + "metadata": {}, + "source": [ + "We can test it by calling `bike_to_olin`:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "cosmetic-above", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "bike_to_olin(bikeshare)" + ] + }, + { + "cell_type": "markdown", + "id": "pleased-gasoline", + "metadata": {}, + "source": [ + "After this update, there should be 12 bikes at Olin, no bikes at Wellesley, and one unhappy customer." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "bulgarian-palestine", + "metadata": {}, + "outputs": [], + "source": [ + "show(bikeshare)" + ] + }, + { + "cell_type": "markdown", + "id": "revised-associate", + "metadata": {}, + "source": [ + "Looks good!" + ] + }, + { + "cell_type": "markdown", + "id": "native-kidney", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "In this chapter, we wrote several versions of `bike_to_olin`:\n", + "\n", + "* We added a parameter, `state`, so we can work with more than one `State` object.\n", + "\n", + "* We added a docstring that explains how to use the function and a comment that explains how it works.\n", + "\n", + "* We used a conditional operator, `==`, to check whether a bike is available, in order to avoid negative bikes.\n", + "\n", + "* We added a state variable, `wellesley_empty`, to count the number of unhappy customers, which is a metric we'll use to quantify how well the system works.\n", + "\n", + "In the exercises, you'll update `bike_to_wellesley` the same way and test it by running a simulation." + ] + }, + { + "cell_type": "markdown", + "id": "impaired-cyprus", + "metadata": {}, + "source": [ + "## Exercises" + ] + }, + { + "cell_type": "markdown", + "id": "careful-hacker", + "metadata": { + "tags": [] + }, + "source": [ + "Here's the code we have so far, with docstrings, all in one place." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "wrong-internet", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def run_simulation(state, p1, p2, num_steps):\n", + " \"\"\"Simulate the given number of time steps.\n", + " \n", + " state: State object\n", + " p1: probability of an Olin->Wellesley customer arrival\n", + " p2: probability of a Wellesley->Olin customer arrival\n", + " num_steps: number of time steps\n", + " \"\"\"\n", + " results = TimeSeries()\n", + " results[0] = state.olin\n", + " \n", + " for i in range(num_steps):\n", + " step(state, p1, p2)\n", + " results[i+1] = state.olin\n", + " \n", + " results.plot(label='Olin')\n", + " decorate(title='Olin-Wellesley Bikeshare',\n", + " xlabel='Time step (min)', \n", + " ylabel='Number of bikes')" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "instrumental-copyright", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def step(state, p1, p2):\n", + " \"\"\"Simulate one time step.\n", + " \n", + " state: bikeshare State object\n", + " p1: probability of an Olin->Wellesley ride\n", + " p2: probability of a Wellesley->Olin ride\n", + " \"\"\"\n", + " if flip(p1):\n", + " bike_to_wellesley(state)\n", + " \n", + " if flip(p2):\n", + " bike_to_olin(state)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "improved-renaissance", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def bike_to_olin(state):\n", + " \"\"\"Move one bike from Wellesley to Olin.\n", + " \n", + " state: bikeshare State object\n", + " \"\"\"\n", + " if state.wellesley == 0:\n", + " state.wellesley_empty += 1\n", + " return\n", + " state.wellesley -= 1\n", + " state.olin += 1" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "unavailable-maker", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def bike_to_wellesley(state):\n", + " \"\"\"Move one bike from Olin to Wellesley.\n", + " \n", + " state: bikeshare State object\n", + " \"\"\"\n", + " state.olin -= 1\n", + " state.wellesley += 1" + ] + }, + { + "cell_type": "markdown", + "id": "bigger-rapid", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + " Modify `bike_to_wellesley` so it checks whether a bike is available at Olin. If not, it should add `1` to `olin_empty`.\n", + "\n", + "To test it, create a `State` that initializes `olin` and `olin_empty` to `0`, run `bike_to_wellesley`, and check the result." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "phantom-carter", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "adopted-contrary", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "comparable-natural", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "attractive-amendment", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "possible-initial", + "metadata": {}, + "source": [ + "### Exercise 2\n", + "\n", + " Now run the simulation with parameters `p1=0.3`, `p2=0.2`, and `num_steps=60`, and confirm that the number of bikes is never negative.\n", + "\n", + "Start with this initial state:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "eleven-constraint", + "metadata": {}, + "outputs": [], + "source": [ + "bikeshare = State(olin=10, wellesley=2,\n", + " olin_empty=0, wellesley_empty=0)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "immune-shock", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap03.py b/chapters/chap03.py new file mode 100644 index 000000000..5b6f16266 --- /dev/null +++ b/chapters/chap03.py @@ -0,0 +1,43 @@ +from modsim import * + +def step(state, p1, p2): + """Simulate one time step. + + state: bikeshare State object + p1: probability of an Olin->Wellesley ride + p2: probability of a Wellesley->Olin ride + """ + if flip(p1): + bike_to_wellesley(state) + + if flip(p2): + bike_to_olin(state) + +from modsim import * + +def bike_to_olin(state): + """Move one bike from Wellesley to Olin. + + state: bikeshare State object + """ + if state.wellesley == 0: + state.wellesley_empty += 1 + return + state.wellesley -= 1 + state.olin += 1 + +from modsim import * + +# Solution + +def bike_to_wellesley(state): + """Move one bike from Olin to Wellesley. + + state: bikeshare State object + """ + if state.olin == 0: + state.olin_empty += 1 + return + state.olin -= 1 + state.wellesley += 1 + diff --git a/chapters/chap04.ipynb b/chapters/chap04.ipynb new file mode 100644 index 000000000..239f8a031 --- /dev/null +++ b/chapters/chap04.ipynb @@ -0,0 +1,896 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "existing-guidance", + "metadata": {}, + "source": [ + "# Sweeping Parameters" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "electoral-turkey", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "caring-gnome", + "metadata": { + "tags": [] + }, + "source": [ + "The following cells download the code from Chapter 3 and import the `step` function we defined." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "ranking-today", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/chap03.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "stylish-raising", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from chap03 import step" + ] + }, + { + "cell_type": "markdown", + "id": "atlantic-collectible", + "metadata": {}, + "source": [ + "In the previous chapter we defined metrics that quantify the performance of a bike sharing system.\n", + "In this chapter we'll see how those metrics depend on the parameters of the system, like the arrival rate of customers at the stations.\n", + "\n", + "And I will present a program development strategy, called incremental\n", + "development, that might help you write programs faster and spend less\n", + "time debugging." + ] + }, + { + "cell_type": "markdown", + "id": "plastic-trigger", + "metadata": {}, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "strategic-newspaper", + "metadata": {}, + "source": [ + "## Functions That Return Values\n", + "\n", + "We have used several functions that return values.\n", + "For example, when you run `sqrt`, it returns a number you can assign to a variable." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "imposed-pregnancy", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import sqrt\n", + "\n", + "root_2 = sqrt(2)\n", + "root_2" + ] + }, + { + "cell_type": "markdown", + "id": "unsigned-recipe", + "metadata": {}, + "source": [ + "And when you run `State`, it returns a new `State` object:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "accessible-wallace", + "metadata": {}, + "outputs": [], + "source": [ + "bikeshare = State(olin=10, wellesley=2)\n", + "bikeshare" + ] + }, + { + "cell_type": "markdown", + "id": "missing-pendant", + "metadata": {}, + "source": [ + "Not all functions have return values. For example, when you run `step`,\n", + "it updates a `State` object, but it doesn't return a value.\n", + "\n", + "To write functions that return values, we can use a `return` statement, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "minimal-supervisor", + "metadata": {}, + "outputs": [], + "source": [ + "def add_five(x):\n", + " return x + 5" + ] + }, + { + "cell_type": "markdown", + "id": "sized-intensity", + "metadata": {}, + "source": [ + "`add_five` takes a parameter, `x`, which could be any number. It\n", + "computes `x + 5` and returns the result. So if we run it like this, the\n", + "result is `8`:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "warming-program", + "metadata": {}, + "outputs": [], + "source": [ + "add_five(3)" + ] + }, + { + "cell_type": "markdown", + "id": "rental-representation", + "metadata": {}, + "source": [ + "As a more useful example, here's a version of `run_simulation` that\n", + "creates a `State` object, runs a simulation, and then returns the\n", + "`State` object:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "sitting-cleveland", + "metadata": {}, + "outputs": [], + "source": [ + "def run_simulation(p1, p2, num_steps):\n", + " state = State(olin=10, wellesley=2,\n", + " olin_empty=0, wellesley_empty=0)\n", + " \n", + " for i in range(num_steps):\n", + " step(state, p1, p2)\n", + " \n", + " return state" + ] + }, + { + "cell_type": "markdown", + "id": "minimal-ability", + "metadata": {}, + "source": [ + "We can call `run_simulation` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "difficult-shepherd", + "metadata": {}, + "outputs": [], + "source": [ + "final_state = run_simulation(0.3, 0.2, 60)" + ] + }, + { + "cell_type": "markdown", + "id": "charming-wheel", + "metadata": {}, + "source": [ + "The result is a `State` object that represents the final state of the system, including the metrics we'll use to evaluate the performance of the system:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "tough-sweet", + "metadata": {}, + "outputs": [], + "source": [ + "print(final_state.olin_empty, \n", + " final_state.wellesley_empty)" + ] + }, + { + "cell_type": "markdown", + "id": "aggregate-lightweight", + "metadata": {}, + "source": [ + "The simulation we just ran starts with `olin=10` and `wellesley=2`, and uses the values `p1=0.3`, `p2=0.2`, and `num_steps=60`. \n", + "These five values are *parameters of the model*, which are quantities that determine the behavior of the system.\n", + "\n", + "It is easy to get the parameters of a model confused with the parameters of a function. \n", + "It is especially easy because the parameters of a model often appear as parameters of a function.\n", + "\n", + "For example, the previous version of `run_simulation` takes `p1`, `p2`, and `num_steps` as parameters.\n", + "So we can call `run_simulation` with different parameters and see how\n", + "the metrics, like the number of unhappy customers, depend on the\n", + "parameters. But before we do that, we need a new version of a `for` loop." + ] + }, + { + "cell_type": "markdown", + "id": "valuable-aircraft", + "metadata": {}, + "source": [ + "## Loops and Arrays\n", + "\n", + "In `run_simulation`, we use this `for` loop:\n", + "\n", + "```\n", + " for i in range(num_steps):\n", + " step(state, p1, p2)\n", + "```\n", + "\n", + "In this example, `range` creates a sequence of numbers from `0` to `num_steps` (including `0` but not `num_steps`). \n", + "Each time through the loop, the next number in the sequence gets assigned to the loop variable, `i`.\n", + "\n", + "But `range` only works with integers; to get a sequence of non-integer\n", + "values, we can use `linspace`, which is provided by NumPy:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "bound-juice", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import linspace\n", + "\n", + "p1_array = linspace(0, 1, 5)\n", + "p1_array" + ] + }, + { + "cell_type": "markdown", + "id": "ordered-colleague", + "metadata": {}, + "source": [ + "The arguments indicate where the sequence should start and stop, and how\n", + "many elements it should contain. In this example, the sequence contains\n", + "`5` equally-spaced numbers, starting at `0` and ending at `1`.\n", + "\n", + "The result is a NumPy *array*, which is a new kind of object we have\n", + "not seen before. An array is a container for a sequence of numbers.\n", + "\n", + "We can use an array in a `for` loop like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "commercial-methodology", + "metadata": {}, + "outputs": [], + "source": [ + "for p1 in p1_array:\n", + " print(p1)" + ] + }, + { + "cell_type": "markdown", + "id": "finnish-budapest", + "metadata": {}, + "source": [ + "When this loop runs, it\n", + "\n", + "1. Gets the first value from the array and assigns it to `p1`.\n", + "\n", + "2. Runs the body of the loop, which prints `p1`.\n", + "\n", + "3. Gets the next value from the array and assigns it to `p1`.\n", + "\n", + "4. Runs the body of the loop, which prints `p1`.\n", + "\n", + "5. ...\n", + "\n", + "And so on, until it gets to the end of the array. This will come in handy in the next section." + ] + }, + { + "cell_type": "markdown", + "id": "crazy-belize", + "metadata": {}, + "source": [ + "## Sweeping Parameters\n", + "\n", + "If we know the actual values of parameters like `p1` and `p2`, we can\n", + "use them to make specific predictions, like how many bikes will be at\n", + "Olin after one hour.\n", + "\n", + "But prediction is not the only goal; models like this are also used to\n", + "explain why systems behave as they do and to evaluate alternative\n", + "designs. For example, if we observe the system and notice that we often run out of bikes at a particular time, we could use the model to figure out why that happens. And if we are considering adding more bikes, or another station, we could evaluate the effect of various \"what if\" scenarios.\n", + "\n", + "As an example, suppose we have enough data to estimate that `p2` is\n", + "about `0.2`, but we don't have any information about `p1`. We could run simulations with a range of values for `p1` and see how the results vary. This process is called *sweeping* a parameter, in the sense that the value of the parameter \"sweeps\" through a range of possible values.\n", + "\n", + "Now that we know about loops and arrays, we can use them like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "working-chair", + "metadata": {}, + "outputs": [], + "source": [ + "p1_array = linspace(0, 0.6, 6)\n", + "p2 = 0.2\n", + "num_steps = 60\n", + "\n", + "for p1 in p1_array:\n", + " final_state = run_simulation(p1, p2, num_steps)\n", + " print(p1, final_state.olin_empty)" + ] + }, + { + "cell_type": "markdown", + "id": "chicken-mainstream", + "metadata": {}, + "source": [ + "Each time through the loop, we run a simulation with a different value\n", + "of `p1` and the same value of `p2`, `0.2`. Then we print `p1` and the\n", + "number of unhappy customers at Olin.\n", + "\n", + "To save and plot the results, we can use a `SweepSeries` object, which\n", + "is similar to a `TimeSeries`; the difference is that the labels in a\n", + "`SweepSeries` are parameter values rather than time values.\n", + "\n", + "We can create an empty `SweepSeries` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "instrumental-session", + "metadata": {}, + "outputs": [], + "source": [ + "sweep = SweepSeries()" + ] + }, + { + "cell_type": "markdown", + "id": "listed-orleans", + "metadata": {}, + "source": [ + "And add values like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "hollywood-technical", + "metadata": {}, + "outputs": [], + "source": [ + "p1_array = linspace(0, 0.6, 31)\n", + "\n", + "for p1 in p1_array:\n", + " final_state = run_simulation(p1, p2, num_steps)\n", + " sweep[p1] = final_state.olin_empty" + ] + }, + { + "cell_type": "markdown", + "id": "healthy-prime", + "metadata": {}, + "source": [ + "The result is a `SweepSeries` that maps from each value of `p1` to the\n", + "resulting number of unhappy customers." + ] + }, + { + "cell_type": "markdown", + "id": "driven-theme", + "metadata": { + "tags": [] + }, + "source": [ + "We can display the results like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "recovered-buffalo", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "show(sweep)" + ] + }, + { + "cell_type": "markdown", + "id": "instructional-showcase", + "metadata": {}, + "source": [ + "We can plot the elements of the `SweepSeries` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "hollywood-spirit", + "metadata": {}, + "outputs": [], + "source": [ + "sweep.plot(label='Olin', color='C1')\n", + "\n", + "decorate(title='Olin-Wellesley bikeshare',\n", + " xlabel='Customer rate at Olin (p1 in customers/min)', \n", + " ylabel='Number of unhappy customers at Olin')" + ] + }, + { + "cell_type": "markdown", + "id": "educated-bloom", + "metadata": {}, + "source": [ + "The keyword argument `color='C1'` specifies the color of the line.\n", + "The `TimeSeries` we have plotted so far use the default color, `C0`, which is blue (see for the other colors defined by Matplotlib).\n", + "I use a different color for `SweepSeries` to remind us that it is not a `TimeSeries`.\n", + "\n", + "When the arrival rate at Olin is low, there are plenty of bikes and no unhappy customers.\n", + "As the arrival rate increases, we are more likely to run out of bikes and the number of unhappy customers increases. The line is jagged because the simulation is based on random numbers. Sometimes we get lucky and there are relatively few unhappy customers; other times we are unlucky and there are more. " + ] + }, + { + "cell_type": "markdown", + "id": "korean-christianity", + "metadata": {}, + "source": [ + "## Incremental Development\n", + "\n", + "When you start writing programs that are more than a few lines, you\n", + "might find yourself spending more time debugging. The more code you write before you start debugging, the harder it is to find the problem.\n", + "\n", + "*Incremental development* is a way of programming that tries to\n", + "minimize the pain of debugging. The fundamental steps are:\n", + "\n", + "1. Always start with a working program. If you have an example from a\n", + " book, or a program you wrote that is similar to what you are working\n", + " on, start with that. Otherwise, start with something you *know* is\n", + " correct, like `x=5`. Run the program and confirm that it does what\n", + " you expect.\n", + "\n", + "2. Make one small, testable change at a time. A \"testable\" change is\n", + " one that displays something or has some other effect you can check.\n", + " Ideally, you should know what the correct answer is, or be able to\n", + " check it by performing another computation.\n", + "\n", + "3. Run the program and see if the change worked. If so, go back to\n", + " Step 2. If not, you have to do some debugging, but if the\n", + " change you made was small, it shouldn't take long to find the\n", + " problem.\n", + "\n", + "When this process works, your changes usually work the first time or, if they don't, the problem is obvious. In practice, there are two problems with incremental development:\n", + "\n", + "- Sometimes you have to write extra code to generate visible output\n", + " that you can check. This extra code is called *scaffolding*\n", + " because you use it to build the program and then remove it when you\n", + " are done. That might seem like a waste, but time you spend on\n", + " scaffolding is almost always time you save on debugging.\n", + "\n", + "- When you are getting started, it might not be obvious how to choose\n", + " the steps that get from `x=5` to the program you are trying to\n", + " write. You will see more examples of this process as we go along,\n", + " and you will get better with experience.\n", + "\n", + "If you find yourself writing more than a few lines of code before you\n", + "start testing, and you are spending a lot of time debugging, try\n", + "incremental development." + ] + }, + { + "cell_type": "markdown", + "id": "nominated-assault", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This chapter introduces functions that return values, which we use to write a version of `run_simulation` that returns a `State` object with the final state of the system.\n", + "\n", + "It also introduces `linspace`, which we use to create a NumPy array, and `SweepSeries`, which we use to store the results of a parameter sweep.\n", + "\n", + "We used a parameter sweep to explore the relationship between one of the parameters, `p1`, and the number of unhappy customers, which is a metric that quantifies how well (or badly) the system works.\n", + "\n", + "In the exercises, you'll have a chance to sweep other parameters and compute other metrics.\n", + "\n", + "In the next chapter, we'll move on to a new problem, modeling and predicting world population growth." + ] + }, + { + "cell_type": "markdown", + "id": "appreciated-preview", + "metadata": {}, + "source": [ + "## Exercises" + ] + }, + { + "cell_type": "markdown", + "id": "primary-quest", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + " Write a function called `make_state` that creates a `State` object with the state variables `olin=10` and `wellesley=2`, and then returns the new `State` object.\n", + "\n", + "Write a line of code that calls `make_state` and assigns the result to a variable named `init`." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "reflected-freedom", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "north-formation", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "robust-blair", + "metadata": {}, + "source": [ + "### Exercise 2\n", + "\n", + "Read the documentation of `linspace` at . Then use it to make an array of 101 equally spaced points between 0 and 1 (including both)." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "collected-butter", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "fleet-debut", + "metadata": {}, + "source": [ + "### Exercise 3\n", + "\n", + " Wrap the code from this chapter in a function named `sweep_p1` that takes an array called `p1_array` as a parameter. It should create a new `SweepSeries` and run a simulation for each value of `p1` in `p1_array`, with `p2=0.2` and `num_steps=60`.\n", + "It should store the results in the `SweepSeries` and return it. \n", + "\n", + "Use your function to generate a `SweepSeries` and then plot the number of unhappy customers at Olin as a function of `p1`. Label the axes." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "authorized-sarah", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "romance-wisdom", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "developmental-broad", + "metadata": {}, + "source": [ + "### Exercise 4\n", + "\n", + " Write a function called `sweep_p2` that runs simulations with `p1=0.5` and a range of values for `p2`. It should store the results in a `SweepSeries` and return the `SweepSeries`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "norman-banana", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "mexican-robert", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "separate-mention", + "metadata": {}, + "source": [ + "## Challenge Exercises\n", + "\n", + "The following two exercises are a little more challenging. If you are comfortable with what you have learned so far, you should give them a try. If you feel like you have your hands full, you might want to skip them for now." + ] + }, + { + "cell_type": "markdown", + "id": "bearing-orbit", + "metadata": {}, + "source": [ + "### Exercise 5\n", + "\n", + " Because our simulations are random, the results vary from one run to another, and the results of a parameter sweep tend to be noisy. We can get a clearer picture of the relationship between a parameter and a metric by running multiple simulations with the same parameter and taking the average of the results.\n", + "\n", + "Write a function called `run_multiple_simulations` that takes as parameters `p1`, `p2`, `num_steps`, and `num_runs`.\n", + "`num_runs` specifies how many times it should call `run_simulation`.\n", + "\n", + "After each run, it should store the total number of unhappy customers (at Olin or Wellesley) in a `TimeSeries`.\n", + "At the end, it should return the `TimeSeries`.\n", + "\n", + "Test your function with parameters\n", + "\n", + "```\n", + "p1 = 0.3\n", + "p2 = 0.3\n", + "num_steps = 60\n", + "num_runs = 10\n", + "```\n", + "\n", + "Display the resulting `TimeSeries` and use the `mean` function from NumPy to compute the average number of unhappy customers." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "accredited-salmon", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "visible-allowance", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "spatial-fundamentals", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "structural-expense", + "metadata": {}, + "source": [ + "### Exercise 6\n", + "\n", + "Continuing the previous exercise, use `run_multiple_simulations` to run simulations with a range of values for `p1` and `p2`.\n", + "\n", + "```\n", + "p2 = 0.3\n", + "num_steps = 60\n", + "num_runs = 20\n", + "```\n", + "\n", + "Store the results in a `SweepSeries`, then plot the average number of unhappy customers as a function of `p1`. Label the axes.\n", + "\n", + "What value of `p1` minimizes the average number of unhappy customers?" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "reverse-emphasis", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "broad-latitude", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "biblical-federal", + "metadata": {}, + "source": [ + "## Under the Hood\n", + "\n", + "The object you get when you call `SweepSeries` is actually a Pandas `Series`, the same as the object you get from `TimeSeries`.\n", + "I give them different names to help us remember that they play different roles.\n", + "\n", + "`Series` provides a number of functions, which you can read about at .\n", + "\n", + "They include `mean`, which computes the average of the values in the `Series`, so if you have a `Series` named `totals`, for example, you can compute the mean like this:\n", + "\n", + "```\n", + " totals.mean()\n", + "```\n", + "\n", + "`Series` provides other statistical functions, like `std`, which computes the standard deviation of the values in the series.\n", + "\n", + "In this chapter I use the keyword argument `color` to specify the color of a line plot.\n", + "You can read about the other available colors at ." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "federal-cemetery", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap05.ipynb b/chapters/chap05.ipynb new file mode 100644 index 000000000..49786750d --- /dev/null +++ b/chapters/chap05.ipynb @@ -0,0 +1,1008 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "wicked-mozambique", + "metadata": {}, + "source": [ + "# World Population" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "electoral-turkey", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "informal-contractor", + "metadata": {}, + "source": [ + "In 1968 Paul Erlich published *The Population Bomb*, in which he\n", + "predicted that world population would grow quickly during the 1970s,\n", + "that agricultural production could not keep up, and that mass starvation in the next two decades was inevitable (see\n", + "). As someone who grew up during those\n", + "decades, I am happy to report that those predictions were wrong.\n", + "\n", + "But world population growth is still a topic of concern, and it is an\n", + "open question how many people Earth can sustain while maintaining\n", + "and improving our quality of life.\n", + "\n", + "In this chapter and the next, we use tools from the previous chapters to model world population growth since 1950 and generate predictions for the next 50-100 years.\n", + "For background on world population growth, watch this video from the\n", + "American Museum of Natural History ." + ] + }, + { + "cell_type": "markdown", + "id": "plastic-trigger", + "metadata": {}, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "dying-browse", + "metadata": {}, + "source": [ + "## World Population Growth\n", + "\n", + "The Wikipedia article on world population contains tables with estimates of world population from prehistory to the present, and projections for the future ()." + ] + }, + { + "cell_type": "markdown", + "id": "continuing-cancellation", + "metadata": { + "tags": [] + }, + "source": [ + "The following cell downloads a copy of https://en.wikipedia.org/wiki/World_population_estimates" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "convenient-stroke", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/data/World_population_estimates.html')" + ] + }, + { + "cell_type": "markdown", + "id": "acknowledged-bracelet", + "metadata": {}, + "source": [ + "To read this data, we will use the Pandas library, which provides functions for\n", + "working with data. The function we'll use is `read_html`, which can read a web page and extract data from any tables it contains. Before we can use it, we have to import it." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "israeli-finding", + "metadata": {}, + "outputs": [], + "source": [ + "from pandas import read_html" + ] + }, + { + "cell_type": "markdown", + "id": "regulation-parade", + "metadata": {}, + "source": [ + "Now we can use it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "instant-beverage", + "metadata": {}, + "outputs": [], + "source": [ + "filename = 'World_population_estimates.html'\n", + "tables = read_html(filename,\n", + " header=0, \n", + " index_col=0,\n", + " decimal='M')" + ] + }, + { + "cell_type": "markdown", + "id": "dried-immunology", + "metadata": {}, + "source": [ + "The arguments are:\n", + "\n", + "- `filename`: The name of the file (including the directory it's in)\n", + " as a string. This argument can also be a URL starting with `http`.\n", + "\n", + "- `header`: Indicates which row of each table should be considered the\n", + " *header*, that is, the set of labels that identify the columns. In\n", + " this case it is the first row (numbered 0).\n", + "\n", + "- `index_col`: Indicates which column of each table should be\n", + " considered the *index*, that is, the set of labels that identify\n", + " the rows. In this case it is the first column, which contains the\n", + " years.\n", + "\n", + "- `decimal`: Normally this argument is used to indicate which\n", + " character should be considered a decimal point, because some\n", + " conventions use a period and some use a comma. In this case I am\n", + " abusing the feature by treating `M` as a decimal point, which allows\n", + " some of the estimates, which are expressed in millions, to be read\n", + " as numbers.\n", + "\n", + "The result, which is assigned to `tables`, is a sequence that contains\n", + "one `DataFrame` for each table. A `DataFrame` is an object, defined by\n", + "Pandas, that represents tabular data.\n", + "\n", + "To select a `DataFrame` from `tables`, we can use the bracket operator\n", + "like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "driving-wrapping", + "metadata": {}, + "outputs": [], + "source": [ + "table2 = tables[2]" + ] + }, + { + "cell_type": "markdown", + "id": "simplified-convert", + "metadata": {}, + "source": [ + "This line selects the third table (numbered 2), which contains\n", + "population estimates from 1950 to 2016.\n", + "\n", + "We can use `head` to display the first few lines of the table." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "running-alcohol", + "metadata": {}, + "outputs": [], + "source": [ + "table2.head()" + ] + }, + { + "cell_type": "markdown", + "id": "valid-editing", + "metadata": {}, + "source": [ + "The first column, which is labeled `Year`, is special. It is the *index* for this `DataFrame`, which means it contains the labels for the rows.\n", + "\n", + "Some of the values use scientific notation; for example, `2.516000e+09` is shorthand for $2.516 \\cdot 10^9$ or 2.516 billion.\n", + "\n", + "`NaN` is a special value that indicates missing data." + ] + }, + { + "cell_type": "markdown", + "id": "plastic-senate", + "metadata": {}, + "source": [ + "The column labels are long strings, which makes them hard to work with.\n", + "We can replace them with shorter strings like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "engaging-regular", + "metadata": {}, + "outputs": [], + "source": [ + "table2.columns = ['census', 'prb', 'un', 'maddison', \n", + " 'hyde', 'tanton', 'biraben', 'mj', \n", + " 'thomlinson', 'durand', 'clark']" + ] + }, + { + "cell_type": "markdown", + "id": "communist-crowd", + "metadata": {}, + "source": [ + "Now we can select a column from the `DataFrame` using the dot operator,\n", + "like selecting a state variable from a `State` object.\n", + "\n", + "Here are the estimates from the United States Census Bureau:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "amended-negative", + "metadata": {}, + "outputs": [], + "source": [ + "census = table2.census / 1e9" + ] + }, + { + "cell_type": "markdown", + "id": "absent-heart", + "metadata": {}, + "source": [ + "The result is a Pandas `Series`, which is similar to the `TimeSeries` and `SweepSeries` objects we've been using.\n", + "\n", + "The number `1e9` is a shorter way to write `1000000000` or one billion.\n", + "When we divide a `Series` by a number, it divides all of the elements of the `Series`.\n", + "From here on, we'll express population estimates in terms of billions.\n", + "\n", + "We can use `tail` to see the last few elements of the `Series`:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "graduate-specialist", + "metadata": {}, + "outputs": [], + "source": [ + "census.tail()" + ] + }, + { + "cell_type": "markdown", + "id": "brilliant-brook", + "metadata": {}, + "source": [ + "The left column is the *index* of the `Series`; in this example it contains the dates.\n", + "The right column contains the *values*, which are population estimates.\n", + "In 2016 the world population was about 7.3 billion.\n", + "\n", + "Here are the estimates from the United Nations\n", + "Department of Economic and Social Affairs (U.N. DESA):" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "linear-admission", + "metadata": {}, + "outputs": [], + "source": [ + "un = table2.un / 1e9\n", + "un.tail()" + ] + }, + { + "cell_type": "markdown", + "id": "mental-sussex", + "metadata": {}, + "source": [ + "The most recent estimate we have from the U.N. is for 2015, so the value for 2016 is `NaN`.\n", + "\n", + "Now we can plot the estimates like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "ordered-garage", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_estimates():\n", + " census.plot(style=':', label='US Census')\n", + " un.plot(style='--', label='UN DESA')\n", + " decorate(xlabel='Year', \n", + " ylabel='World population (billions)') " + ] + }, + { + "cell_type": "markdown", + "id": "invisible-mouse", + "metadata": {}, + "source": [ + "The keyword argument `style=':'` specifies a dotted line; `style='--'` specifies a dashed line.\n", + "The `label` argument provides the string that appears in the legend.\n", + "\n", + "And here's what it looks like." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "periodic-weekend", + "metadata": {}, + "outputs": [], + "source": [ + "plot_estimates()\n", + "decorate(title='World population estimates')" + ] + }, + { + "cell_type": "markdown", + "id": "labeled-magic", + "metadata": {}, + "source": [ + "The lines overlap almost completely, but the most recent estimates diverge slightly.\n", + "In the next section, we'll quantify these differences." + ] + }, + { + "cell_type": "markdown", + "id": "tender-summer", + "metadata": {}, + "source": [ + "## Absolute and Relative Errors\n", + "\n", + "Estimates of world population from the U.S. Census and the U.N. DESA differ slightly.\n", + "One way to characterize this difference is *absolute error*, which is the absolute value of the difference between the estimates.\n", + "\n", + "To compute absolute errors, we can import `abs` from NumPy:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "secondary-player", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import abs" + ] + }, + { + "cell_type": "markdown", + "id": "frozen-scotland", + "metadata": {}, + "source": [ + "And use it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "caring-garlic", + "metadata": {}, + "outputs": [], + "source": [ + "abs_error = abs(un - census)\n", + "abs_error.tail()" + ] + }, + { + "cell_type": "markdown", + "id": "bridal-extraction", + "metadata": {}, + "source": [ + "When you subtract two `Series` objects, the result is a new `Series`.\n", + "Because one of the estimates for 2016 is `NaN`, the result for 2016 is `NaN`.\n", + "\n", + "To summarize the results, we can compute the *mean absolute error*." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "corresponding-emerald", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import mean\n", + "\n", + "mean(abs_error)" + ] + }, + { + "cell_type": "markdown", + "id": "identical-archive", + "metadata": {}, + "source": [ + "On average, the estimates differ by about 0.029 billion.\n", + "But we can also use `max` to compute the maximum absolute error." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "headed-witch", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "from numpy import max\n", + "\n", + "max(abs_error)" + ] + }, + { + "cell_type": "markdown", + "id": "composite-partnership", + "metadata": {}, + "source": [ + "In the worst case, they differ by about 0.1 billion.\n", + "\n", + "Now 0.1 billion is a lot of people, so that might sound like a serious discrepancy.\n", + "But counting everyone is the world is hard, and we should not expect the estimates to be exact.\n", + "\n", + "Another way to quantify the magnitude of the difference is *relative error*, which is the size of the error divided by the estimates themselves." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "advisory-complex", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "rel_error = 100 * abs_error / census\n", + "rel_error.tail()" + ] + }, + { + "cell_type": "markdown", + "id": "greater-register", + "metadata": {}, + "source": [ + "I multiplied by 100 so we can interpret the results as a percentage. In 2015, the difference between the estimates is about 1.4%, and that happens to be the maximum.\n", + "\n", + "Again, we can summarize the results by computing the mean." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "frank-owner", + "metadata": {}, + "outputs": [], + "source": [ + "mean(rel_error)" + ] + }, + { + "cell_type": "markdown", + "id": "peaceful-fleece", + "metadata": {}, + "source": [ + "The mean relative error is about 0.6%.\n", + "So that's not bad.\n", + "\n", + "You might wonder why I divided by `census` rather than `un`.\n", + "In general, if you think one estimate is better than the other, you put the better one in the denominator.\n", + "In this case, I don't know which is better, so I put the smaller one in the denominator, which makes the computed errors a little bigger." + ] + }, + { + "cell_type": "markdown", + "id": "vietnamese-excuse", + "metadata": {}, + "source": [ + "## Modeling Population Growth\n", + "\n", + "Suppose we want to predict world population growth over the next 50 or\n", + "100 years. We can do that by developing a model that describes how\n", + "populations grow, fitting the model to the data we have so far, and then using the model to generate predictions.\n", + "\n", + "In the next few sections I demonstrate this process starting with simple models and gradually improving them.\n", + "\n", + "Although there is some curvature in the plotted estimates, it looks like world population growth has been close to linear since 1960 or so. So we'll start with a model that has constant growth.\n", + "\n", + "To fit the model to the data, we'll compute the average annual growth\n", + "from 1950 to 2016. Since the UN and Census data are so close, we'll use the Census data.\n", + "\n", + "We can select a value from a `Series` using the bracket operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "undefined-sauce", + "metadata": {}, + "outputs": [], + "source": [ + "census[1950]" + ] + }, + { + "cell_type": "markdown", + "id": "precise-correlation", + "metadata": {}, + "source": [ + "So we can get the total growth during the interval like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "postal-debate", + "metadata": {}, + "outputs": [], + "source": [ + "total_growth = census[2016] - census[1950]" + ] + }, + { + "cell_type": "markdown", + "id": "dutch-sample", + "metadata": {}, + "source": [ + "In this example, the labels 2016 and 1950 are part of the data, so it\n", + "would be better not to make them part of the program. \n", + "Putting values like these in the program is called *hard coding*; it is considered bad practice because if the data change in the future, we have to change the program (see ).\n", + "\n", + "It would be better to get the labels from the `Series`.\n", + "We can do that by selecting the index from `census` and then selecting the first element." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "functional-trick", + "metadata": {}, + "outputs": [], + "source": [ + "t_0 = census.index[0]\n", + "t_0" + ] + }, + { + "cell_type": "markdown", + "id": "empty-siemens", + "metadata": {}, + "source": [ + "So `t_0` is the label of the first element, which is 1950.\n", + "We can get the label of the last element like this." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "acoustic-south", + "metadata": {}, + "outputs": [], + "source": [ + "t_end = census.index[-1]\n", + "t_end" + ] + }, + { + "cell_type": "markdown", + "id": "express-metallic", + "metadata": {}, + "source": [ + "The value `-1` indicates the last element; `-2` indicates the second to last element, and so on.\n", + "\n", + "The difference between `t_0` and `t_end` is the elapsed time between them." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "planned-remains", + "metadata": {}, + "outputs": [], + "source": [ + "elapsed_time = t_end - t_0\n", + "elapsed_time" + ] + }, + { + "cell_type": "markdown", + "id": "bridal-royal", + "metadata": {}, + "source": [ + "Now we can use `t_0` and `t_end` to select the population at the beginning and end of the interval." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "quarterly-aggregate", + "metadata": {}, + "outputs": [], + "source": [ + "p_0 = census[t_0]\n", + "p_end = census[t_end]" + ] + }, + { + "cell_type": "markdown", + "id": "driven-castle", + "metadata": {}, + "source": [ + "And compute the total growth during the interval." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "prescription-optics", + "metadata": {}, + "outputs": [], + "source": [ + "total_growth = p_end - p_0\n", + "total_growth" + ] + }, + { + "cell_type": "markdown", + "id": "accepted-auditor", + "metadata": {}, + "source": [ + "Finally, we can compute average annual growth." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "convertible-patch", + "metadata": {}, + "outputs": [], + "source": [ + "annual_growth = total_growth / elapsed_time\n", + "annual_growth" + ] + }, + { + "cell_type": "markdown", + "id": "japanese-merit", + "metadata": {}, + "source": [ + "From 1950 to 2016, world population grew by about 0.07 billion people per year, on average.\n", + "The next step is to use this estimate to simulate population growth." + ] + }, + { + "cell_type": "markdown", + "id": "public-verification", + "metadata": {}, + "source": [ + "## Simulating Population Growth\n", + "\n", + "Our simulation will start with the observed population in 1950, `p_0`,\n", + "and add `annual_growth` each year. To store the results, we'll use a\n", + "`TimeSeries` object:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "duplicate-leave", + "metadata": {}, + "outputs": [], + "source": [ + "results = TimeSeries()" + ] + }, + { + "cell_type": "markdown", + "id": "expired-salmon", + "metadata": {}, + "source": [ + "We can set the first value in the new `TimeSeries` like this." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "convenient-thong", + "metadata": {}, + "outputs": [], + "source": [ + "results[t_0] = p_0" + ] + }, + { + "cell_type": "markdown", + "id": "constant-casino", + "metadata": {}, + "source": [ + "Here's what it looks like so far." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "israeli-surveillance", + "metadata": {}, + "outputs": [], + "source": [ + "show(results)" + ] + }, + { + "cell_type": "markdown", + "id": "stretch-snapshot", + "metadata": {}, + "source": [ + "Now we set the rest of the values by simulating annual growth:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "attended-morris", + "metadata": {}, + "outputs": [], + "source": [ + "for t in range(t_0, t_end):\n", + " results[t+1] = results[t] + annual_growth" + ] + }, + { + "cell_type": "markdown", + "id": "signed-colleague", + "metadata": {}, + "source": [ + "The values of `t` go from `t_0` to `t_end`, including the first but not the last.\n", + "\n", + "Inside the loop, we compute the population for the next year by adding the population for the current year and `annual_growth`. \n", + "\n", + "The last time through the loop, the value of `t` is 2015, so the last label in `results` is 2016.\n", + "\n", + "Here's what the results look like, compared to the estimates." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "wrong-brooks", + "metadata": {}, + "outputs": [], + "source": [ + "results.plot(color='gray', label='model')\n", + "plot_estimates()\n", + "decorate(title='Constant growth model')" + ] + }, + { + "cell_type": "markdown", + "id": "automated-albany", + "metadata": {}, + "source": [ + "From 1950 to 1990, the model does not fit the data particularly well, but after that, it's pretty good." + ] + }, + { + "cell_type": "markdown", + "id": "unnecessary-million", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This chapter is a first step toward modeling changes in world population growth during the last 70 years.\n", + "\n", + "We used Pandas to read data from a web page and store the results in a `DataFrame`.\n", + "From the `DataFrame` we selected two `Series` objects and used them to compute absolute and relative errors.\n", + "\n", + "Then we computed average population growth and used it to build a simple model with constant annual growth.\n", + "The model fits recent data pretty well; nevertheless, there are two reasons we should be skeptical:\n", + "\n", + "* There is no obvious mechanism that could cause population growth to be constant from year to year. Changes in population are determined by the fraction of people who die and the fraction of people who give birth, so we expect them to depend on the current population.\n", + "\n", + "* According to this model, world population would keep growing at the same rate forever, and that does not seem reasonable.\n", + "\n", + "In the next chapter we'll consider other models that might fit the data better and make more credible predictions." + ] + }, + { + "cell_type": "markdown", + "id": "advanced-ivory", + "metadata": {}, + "source": [ + "## Exercises" + ] + }, + { + "cell_type": "markdown", + "id": "hearing-today", + "metadata": { + "tags": [] + }, + "source": [ + "Here's the code from this chapter all in one place." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "terminal-reynolds", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "t_0 = census.index[0]\n", + "t_end = census.index[-1]\n", + "elapsed_time = t_end - t_0\n", + "\n", + "p_0 = census[t_0]\n", + "p_end = census[t_end]\n", + "\n", + "total_growth = p_end - p_0\n", + "annual_growth = total_growth / elapsed_time\n", + "\n", + "results = TimeSeries()\n", + "results[t_0] = p_0\n", + "\n", + "for t in range(t_0, t_end):\n", + " results[t+1] = results[t] + annual_growth" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "organizational-memphis", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "results.plot(color='gray', label='model')\n", + "plot_estimates()\n", + "decorate(title='Constant growth model')" + ] + }, + { + "cell_type": "markdown", + "id": "ecological-welsh", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + " Try fitting the model using data from 1970 to the present, and see if that does a better job.\n", + "\n", + "Suggestions: \n", + "\n", + "1. Define `t_1` to be 1970 and `p_1` to be the population in 1970. Use `t_1` and `p_1` to compute annual growth, but use `t_0` and `p_0` to run the simulation. \n", + "\n", + "2. You might want to add a constant to the starting value to match the data better." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "rising-anger", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "false-handbook", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "political-loading", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "89bab9ad", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap06.ipynb b/chapters/chap06.ipynb new file mode 100644 index 000000000..1656fe558 --- /dev/null +++ b/chapters/chap06.ipynb @@ -0,0 +1,710 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "creative-motel", + "metadata": {}, + "source": [ + "# Proportional Growth" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "electoral-turkey", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "375794f9", + "metadata": {}, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "amber-contrary", + "metadata": { + "tags": [] + }, + "source": [ + "Here's the data from the previous chapter again." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "critical-addition", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/data/World_population_estimates.html')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "naughty-swing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from pandas import read_html\n", + "\n", + "filename = 'World_population_estimates.html'\n", + "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", + "table2 = tables[2]\n", + "table2.columns = ['census', 'prb', 'un', 'maddison', \n", + " 'hyde', 'tanton', 'biraben', 'mj', \n", + " 'thomlinson', 'durand', 'clark']" + ] + }, + { + "cell_type": "markdown", + "id": "quality-spectrum", + "metadata": {}, + "source": [ + "In the previous chapter we simulated a model of world population with\n", + "constant growth. In this chapter we'll see if we can make a better model\n", + "with growth proportional to the population.\n", + "\n", + "But first, we'll improve the code from the previous chapter by\n", + "encapsulating it in a function and adding a new feature, a `System` object." + ] + }, + { + "cell_type": "markdown", + "id": "therapeutic-merchant", + "metadata": {}, + "source": [ + "## System Objects\n", + "\n", + "Like a `State` object, a `System` object contains variables and their\n", + "values. The difference is:\n", + "\n", + "- `State` objects contain state variables that get updated in the course of a simulation.\n", + "\n", + "- `System` objects contain *system parameters*, which usually don't get updated over the course of a simulation.\n", + "\n", + "For example, in the bike share model, state variables include the number of bikes at each location, which get updated whenever a customer moves a bike. System parameters include the number of locations, total number of bikes, and arrival rates at each location.\n", + "\n", + "In the population model, the only state variable is the population.\n", + "System parameters include the annual growth rate, the initial population, and the start and end times.\n", + "\n", + "Suppose we have the following variables, as computed in the previous\n", + "chapter (assuming `table2` is the `DataFrame` we read from the file):" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "numerous-university", + "metadata": {}, + "outputs": [], + "source": [ + "un = table2.un / 1e9\n", + "census = table2.census / 1e9\n", + "\n", + "t_0 = census.index[0]\n", + "t_end = census.index[-1]\n", + "elapsed_time = t_end - t_0\n", + "\n", + "p_0 = census[t_0]\n", + "p_end = census[t_end]\n", + "\n", + "total_growth = p_end - p_0\n", + "annual_growth = total_growth / elapsed_time" + ] + }, + { + "cell_type": "markdown", + "id": "starting-cooling", + "metadata": {}, + "source": [ + "Some of these are parameters we need to simulate the system; others are temporary values we can discard. \n", + "To distinguish between them, we'll put the parameters we need in a `System` object like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "colonial-domestic", + "metadata": {}, + "outputs": [], + "source": [ + "system = System(t_0=t_0, \n", + " t_end=t_end,\n", + " p_0=p_0,\n", + " annual_growth=annual_growth)" + ] + }, + { + "cell_type": "markdown", + "id": "fleet-beaver", + "metadata": {}, + "source": [ + "`t0` and `t_end` are the first and last years; `p_0` is the initial\n", + "population, and `annual_growth` is the estimated annual growth.\n", + "\n", + "The assignment `t_0=t_0` reads the value of the existing variable named `t_0`, which we created previously, and stores it in a new system variable, also named `t_0`.\n", + "The variables inside the `System` object are distinct from other variables, so you can change one without affecting the other, even if they have the same name.\n", + "\n", + "So this `System` object contains four new variables; here's what they look like." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "floral-routine", + "metadata": {}, + "outputs": [], + "source": [ + "show(system)" + ] + }, + { + "cell_type": "markdown", + "id": "combined-banking", + "metadata": {}, + "source": [ + "Next we'll wrap the code from the previous chapter in a function:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "pacific-challenge", + "metadata": {}, + "outputs": [], + "source": [ + "def run_simulation1(system):\n", + " results = TimeSeries()\n", + " results[system.t_0] = system.p_0\n", + " \n", + " for t in range(system.t_0, system.t_end):\n", + " results[t+1] = results[t] + system.annual_growth\n", + " \n", + " return results" + ] + }, + { + "cell_type": "markdown", + "id": "rough-strain", + "metadata": {}, + "source": [ + "`run_simulation1` takes a `System` object and reads from it the values of `t_0`, `t_end`, and `annual_growth`.\n", + "\n", + "It simulates population growth over time and returns the results in a `TimeSeries`.\n", + "Here's how we call it." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "electoral-breach", + "metadata": {}, + "outputs": [], + "source": [ + "results1 = run_simulation1(system)" + ] + }, + { + "cell_type": "markdown", + "id": "ordinary-sound", + "metadata": {}, + "source": [ + "Here's the function we used in the previous chapter to plot the estimates." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "peripheral-cassette", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_estimates():\n", + " census.plot(style=':', label='US Census')\n", + " un.plot(style='--', label='UN DESA')\n", + " decorate(xlabel='Year', \n", + " ylabel='World population (billion)') " + ] + }, + { + "cell_type": "markdown", + "id": "warming-audience", + "metadata": {}, + "source": [ + "And here are the results." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "capable-diana", + "metadata": {}, + "outputs": [], + "source": [ + "results1.plot(label='model', color='gray')\n", + "plot_estimates()\n", + "decorate(title='Constant growth model')" + ] + }, + { + "cell_type": "markdown", + "id": "exposed-witness", + "metadata": {}, + "source": [ + "It might not be obvious that using functions and `System` objects is a\n", + "big improvement, and for a simple model that we run only once, maybe\n", + "it's not. But as we work with more complex models, and when we run many simulations with different parameters, we'll see that this way of organizing the code makes a big difference.\n", + "\n", + "Now let's see if we can improve the model." + ] + }, + { + "cell_type": "markdown", + "id": "geographic-hormone", + "metadata": {}, + "source": [ + "## Proportional Growth Model\n", + "\n", + "The biggest problem with the constant growth model is that it doesn't\n", + "make any sense. It is hard to imagine how people all over the world\n", + "could conspire to keep population growth constant from year to year.\n", + "\n", + "On the other hand, if some fraction of the population dies each year,\n", + "and some fraction gives birth, we can compute the net change in the\n", + "population like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "laughing-wesley", + "metadata": {}, + "outputs": [], + "source": [ + "def run_simulation2(system):\n", + " results = TimeSeries()\n", + " results[system.t_0] = system.p_0\n", + " \n", + " for t in range(system.t_0, system.t_end):\n", + " births = system.birth_rate * results[t]\n", + " deaths = system.death_rate * results[t]\n", + " results[t+1] = results[t] + births - deaths\n", + " \n", + " return results" + ] + }, + { + "cell_type": "markdown", + "id": "educated-portugal", + "metadata": {}, + "source": [ + "Each time through the loop, we use the parameter `birth_rate` to compute the number of births, and `death_rate` to compute the number of deaths.\n", + "The rest of the function is the same as `run_simulation1`.\n", + "\n", + "Now we can choose the values of `birth_rate` and `death_rate` that best fit the data. \n", + "For the death rate, I'll use 7.7 deaths per 1000 people, which was roughly the global death rate in 2020 (see ).\n", + "I chose the birth rate by hand to fit the population data." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "wired-brief", + "metadata": {}, + "outputs": [], + "source": [ + "system.death_rate = 7.7 / 1000\n", + "system.birth_rate = 25 / 1000" + ] + }, + { + "cell_type": "markdown", + "id": "sufficient-contest", + "metadata": {}, + "source": [ + "Then I ran the simulation and plotted the results:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "looking-trace", + "metadata": {}, + "outputs": [], + "source": [ + "results2 = run_simulation2(system)\n", + "results2.plot(label='model', color='gray')\n", + "plot_estimates()\n", + "decorate(title='Proportional growth model')" + ] + }, + { + "cell_type": "markdown", + "id": "suited-costs", + "metadata": {}, + "source": [ + "The proportional model fits\n", + "the data well from 1950 to 1965, but not so well after that. Overall,\n", + "the *quality of fit* is not as good as the constant growth model,\n", + "which is surprising, because it seems like the proportional model is\n", + "more realistic.\n", + "\n", + "In the next chapter we'll try one more time to find a model that makes\n", + "sense and fits the data. But first, I want to make a few more\n", + "improvements to the code." + ] + }, + { + "cell_type": "markdown", + "id": "appropriate-checkout", + "metadata": {}, + "source": [ + "## Factoring Out the Update Function\n", + "\n", + "`run_simulation1` and `run_simulation2` are nearly identical except for the body of the `for` loop, where we compute the population for the next year.\n", + "\n", + "Rather than repeat identical code, we can separate the things that\n", + "change from the things that don't. First, I'll pull out the births and deaths from `run_simulation2` and make a function:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "handmade-permit", + "metadata": {}, + "outputs": [], + "source": [ + "def growth_func1(t, pop, system):\n", + " births = system.birth_rate * pop\n", + " deaths = system.death_rate * pop\n", + " return births - deaths" + ] + }, + { + "cell_type": "markdown", + "id": "fabulous-bankruptcy", + "metadata": {}, + "source": [ + "`growth_func1` takes as arguments the current year, current population, and a `System` object; it returns the net population growth during the current year.\n", + "\n", + "This function does not use `t`, so we could leave it out. But we will see other growth functions that need it, and it is convenient if they all take the same parameters, used or not.\n", + "Now we can write a function that runs any model:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "civilian-accused", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def run_simulation(system, growth_func):\n", + " results = TimeSeries()\n", + " results[system.t_0] = system.p_0\n", + " \n", + " for t in range(system.t_0, system.t_end):\n", + " growth = growth_func(t, results[t], system)\n", + " results[t+1] = results[t] + growth\n", + " \n", + " return results" + ] + }, + { + "cell_type": "markdown", + "id": "failing-assist", + "metadata": {}, + "source": [ + "This function demonstrates a feature we have not seen before: it takes a\n", + "function as a parameter! When we call `run_simulation`, the second\n", + "parameter is a function, like `growth_func1`, that computes the\n", + "population for the next year.\n", + "\n", + "Here's how we call it:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "wicked-seeking", + "metadata": {}, + "outputs": [], + "source": [ + "results = run_simulation(system, growth_func1)" + ] + }, + { + "cell_type": "markdown", + "id": "simple-camel", + "metadata": {}, + "source": [ + "Passing a function as an argument is the same as passing any other\n", + "value. The argument, which is `growth_func1` in this example, gets\n", + "assigned to the parameter, which is called `growth_func`. Inside\n", + "`run_simulation`, we can call `growth_func` just like any other function.\n", + "\n", + "Each time through the loop, `run_simulation` calls `growth_func1` to compute net growth, and uses it to compute the population during the next year." + ] + }, + { + "cell_type": "markdown", + "id": "spectacular-paradise", + "metadata": {}, + "source": [ + "## Combining Birth and Death\n", + "\n", + "We can simplify the code slightly by combining births and deaths to compute the net growth rate. \n", + "Instead of two parameters, `birth_rate` and `death_rate`, we can write the update function in terms of a single parameter that represents the difference:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "impressive-model", + "metadata": {}, + "outputs": [], + "source": [ + "system.alpha = system.birth_rate - system.death_rate" + ] + }, + { + "cell_type": "markdown", + "id": "modern-uncertainty", + "metadata": {}, + "source": [ + "The name of this parameter, `alpha`, is the conventional name for a\n", + "proportional growth rate.\n", + "\n", + "Here's the modified version of `growth_func1`:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "familiar-helena", + "metadata": {}, + "outputs": [], + "source": [ + "def growth_func2(t, pop, system):\n", + " return system.alpha * pop" + ] + }, + { + "cell_type": "markdown", + "id": "understanding-typing", + "metadata": {}, + "source": [ + "And here's how we run it:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "independent-effectiveness", + "metadata": {}, + "outputs": [], + "source": [ + "results = run_simulation(system, growth_func2)" + ] + }, + { + "cell_type": "markdown", + "id": "everyday-delicious", + "metadata": {}, + "source": [ + "The results are the same as the previous versions, but now the code is organized in a way that makes it easy to explore other models." + ] + }, + { + "cell_type": "markdown", + "id": "attractive-steps", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "In this chapter, we wrapped the code from the previous chapter in functions and used a `System` object to store the parameters of the system.\n", + "\n", + "We explored a new model of population growth, where the number of births and deaths is proportional to the current population. This model seems more realistic, but it turns out not to fit the data particularly well.\n", + "\n", + "In the next chapter, we'll try one more model, which is based on the assumption that the population can't keep growing forever.\n", + "But first, you might want to work on some exercises." + ] + }, + { + "cell_type": "markdown", + "id": "looking-douglas", + "metadata": {}, + "source": [ + "## Exercises" + ] + }, + { + "cell_type": "markdown", + "id": "compliant-preserve", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + " Maybe the reason the proportional model doesn't work very well is that the growth rate, `alpha`, is changing over time. So let's try a model with different growth rates before and after 1980 (as an arbitrary choice).\n", + "\n", + "Write an update function that takes `t`, `pop`, and `system` as parameters. The system object, `system`, should contain two parameters: the growth rate before 1980, `alpha1`, and the growth rate after 1980, `alpha2`. It should use `t` to determine which growth rate to use.\n", + "\n", + "Test your function by calling it directly, then pass it to `run_simulation`. Plot the results. Adjust the parameters `alpha1` and `alpha2` to fit the data as well as you can." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "minus-recommendation", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "headed-amsterdam", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "preliminary-carnival", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "adaptive-tiger", + "metadata": {}, + "source": [ + "## Under the Hood\n", + "\n", + "The `System` object defined in the ModSim library, is based on the `SimpleNamespace` object defined in a standard Python library called `types`; the documentation is at ." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "injured-mailman", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap06.py b/chapters/chap06.py new file mode 100644 index 000000000..f18bcbe40 --- /dev/null +++ b/chapters/chap06.py @@ -0,0 +1,12 @@ +from modsim import * + +def run_simulation(system, growth_func): + results = TimeSeries() + results[system.t_0] = system.p_0 + + for t in range(system.t_0, system.t_end): + growth = growth_func(t, results[t], system) + results[t+1] = results[t] + growth + + return results + diff --git a/chapters/chap07.ipynb b/chapters/chap07.ipynb new file mode 100644 index 000000000..7bf8dacf1 --- /dev/null +++ b/chapters/chap07.ipynb @@ -0,0 +1,797 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "progressive-travel", + "metadata": {}, + "source": [ + "# Limits to Growth" + ] + }, + { + "cell_type": "markdown", + "id": "black-toolbox", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "earlier-pride", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "bound-nature", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "found-pledge", + "metadata": {}, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "general-noise", + "metadata": { + "tags": [] + }, + "source": [ + "Here's the data from the previous chapter again." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "affiliated-eleven", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/data/World_population_estimates.html')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "recent-trouble", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from pandas import read_html\n", + "\n", + "filename = 'World_population_estimates.html'\n", + "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", + "table2 = tables[2]\n", + "table2.columns = ['census', 'prb', 'un', 'maddison', \n", + " 'hyde', 'tanton', 'biraben', 'mj', \n", + " 'thomlinson', 'durand', 'clark']" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "western-blowing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "un = table2.un / 1e9\n", + "census = table2.census / 1e9" + ] + }, + { + "cell_type": "markdown", + "id": "occasional-kitchen", + "metadata": { + "tags": [] + }, + "source": [ + "And here are the functions from the previous chapter." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "simple-coupon", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'chap06.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "monetary-profile", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from chap06 import run_simulation\n", + "\n", + "def plot_estimates():\n", + " census.plot(style=':', label='US Census')\n", + " un.plot(style='--', label='UN DESA')\n", + " decorate(xlabel='Year', \n", + " ylabel='World population (billions)') " + ] + }, + { + "cell_type": "markdown", + "id": "damaged-reservation", + "metadata": {}, + "source": [ + "In the previous chapter we developed a population model where net growth during each time step is proportional to the current population. This model seems more realistic than the constant growth model, but it does not fit the data as well.\n", + "\n", + "There are a few things we could try to improve the model:\n", + "\n", + "- Maybe net growth depends on the current population, but the\n", + " relationship is quadratic, not linear.\n", + "\n", + "- Maybe the net growth rate varies over time.\n", + "\n", + "In this chapter, we'll explore the first option.\n", + "In the exercises, you will have a chance to try the second. " + ] + }, + { + "cell_type": "markdown", + "id": "assigned-slovakia", + "metadata": {}, + "source": [ + "## Quadratic Growth\n", + "\n", + "It makes sense that net growth should depend on the current population, but maybe it's not a linear relationship, like this:\n", + "\n", + "```\n", + "net_growth = system.alpha * pop\n", + "```\n", + "\n", + "Maybe it's a quadratic relationship, like this:\n", + "\n", + "```\n", + "net_growth = system.alpha * pop + system.beta * pop**2\n", + "```\n", + "\n", + "We can test that conjecture with a new update function:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "beginning-belly", + "metadata": {}, + "outputs": [], + "source": [ + "def growth_func_quad(t, pop, system):\n", + " return system.alpha * pop + system.beta * pop**2" + ] + }, + { + "cell_type": "markdown", + "id": "initial-factory", + "metadata": {}, + "source": [ + "Here's the `System` object we'll use, initialized with `t_0`, `p_0`, and `t_end`." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "listed-florence", + "metadata": {}, + "outputs": [], + "source": [ + "t_0 = census.index[0]\n", + "p_0 = census[t_0]\n", + "t_end = census.index[-1]\n", + "\n", + "system = System(t_0=t_0,\n", + " p_0=p_0,\n", + " t_end=t_end)" + ] + }, + { + "cell_type": "markdown", + "id": "amber-context", + "metadata": {}, + "source": [ + "Now we have to add the parameters `alpha` and `beta` .\n", + "I chose the following values by trial and error; we'll see better ways to do it later." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "signed-impossible", + "metadata": {}, + "outputs": [], + "source": [ + "system.alpha = 25 / 1000\n", + "system.beta = -1.8 / 1000" + ] + }, + { + "cell_type": "markdown", + "id": "confidential-retreat", + "metadata": {}, + "source": [ + "And here's how we run it:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "italian-converter", + "metadata": {}, + "outputs": [], + "source": [ + "results = run_simulation(system, growth_func_quad)" + ] + }, + { + "cell_type": "markdown", + "id": "forbidden-brisbane", + "metadata": {}, + "source": [ + "Here are the results." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "simplified-sight", + "metadata": {}, + "outputs": [], + "source": [ + "results.plot(color='gray', label='model')\n", + "plot_estimates()\n", + "decorate(title='Quadratic growth model')" + ] + }, + { + "cell_type": "markdown", + "id": "primary-ending", + "metadata": {}, + "source": [ + "The model fits the data well over the whole range, with just a bit of space between them in the 1960s.\n", + "\n", + "It is not entirely surprising that the quadratic model fits better than the\n", + "constant and proportional models, because it has two parameters we can\n", + "choose, where the other models have only one. In general, the more\n", + "parameters you have to play with, the better you should expect the model\n", + "to fit.\n", + "\n", + "But fitting the data is not the only reason to think the quadratic model\n", + "might be a good choice. It also makes sense; that is, there is a\n", + "legitimate reason to expect the relationship between growth and\n", + "population to have this form.\n", + "\n", + "To understand it, let's look at net growth as a function of population." + ] + }, + { + "cell_type": "markdown", + "id": "sunset-underground", + "metadata": {}, + "source": [ + "## Net Growth\n", + "\n", + "Let's plot the relationship between growth and population in the quadratic model.\n", + "I'll use `linspace` to make an array of 101 populations from 0 to 15 billion." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "neural-guinea", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import linspace\n", + "\n", + "pop_array = linspace(0, 15, 101)" + ] + }, + { + "cell_type": "markdown", + "id": "heated-selling", + "metadata": {}, + "source": [ + "Now I'll use the quadratic model to compute net growth for each population." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "animal-spoke", + "metadata": {}, + "outputs": [], + "source": [ + "growth_array = (system.alpha * pop_array + \n", + " system.beta * pop_array**2)" + ] + }, + { + "cell_type": "markdown", + "id": "engaging-parade", + "metadata": {}, + "source": [ + "To plot growth rate versus population, we'll use the `plot` function from Matplotlib.\n", + "First we have to import it:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "informed-three", + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib.pyplot import plot" + ] + }, + { + "cell_type": "markdown", + "id": "retained-deployment", + "metadata": {}, + "source": [ + "Now we can use it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "unexpected-nigeria", + "metadata": {}, + "outputs": [], + "source": [ + "plot(pop_array, growth_array, label='net growth', color='C2')\n", + "\n", + "decorate(xlabel='Population (billions)',\n", + " ylabel='Net growth (billions)',\n", + " title='Net growth vs. population')" + ] + }, + { + "cell_type": "markdown", + "id": "precise-finish", + "metadata": {}, + "source": [ + "Note that the x-axis is not time, as in the previous figures, but population. We can divide this curve into four kinds of behavior:\n", + "\n", + "- When the population is less than 3 billion, net growth is\n", + " proportional to population, as in the proportional model. In this\n", + " range, the population grows slowly because the population is small.\n", + "\n", + "- Between 3 billion and 10 billion, the population grows quickly\n", + " because there are a lot of people.\n", + "\n", + "- Above 10 billion, population grows more slowly; this behavior models\n", + " the effect of resource limitations that decrease birth rates or\n", + " increase death rates.\n", + "\n", + "- Above 14 billion, resources are so limited that the death rate\n", + " exceeds the birth rate and net growth becomes negative.\n", + "\n", + "Just below 14 billion, there is a point where net growth is 0, which\n", + "means that the population does not change. At this point, the birth and death rates are equal, so the population is in *equilibrium*." + ] + }, + { + "cell_type": "markdown", + "id": "angry-voice", + "metadata": {}, + "source": [ + "## Finding Equilibrium\n", + "\n", + "The equilibrium point is the population, $p$, where net population growth, $\\Delta p$, is 0.\n", + "We can compute it by finding the roots, or zeros, of this equation: \n", + "\n", + "$$\\Delta p = \\alpha p + \\beta p^2$$ \n", + "\n", + "where $\\alpha$ and $\\beta$ are the parameters of the model. \n", + "If we rewrite the right-hand side like this: \n", + "\n", + "$$\\Delta p = p (\\alpha + \\beta p)$$ \n", + "\n", + "we can see that net growth is $0$ when $p=0$ or $p=-\\alpha/\\beta$.\n", + "So we can compute the (non-zero) equilibrium point like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "ordinary-honolulu", + "metadata": {}, + "outputs": [], + "source": [ + "-system.alpha / system.beta" + ] + }, + { + "cell_type": "markdown", + "id": "adaptive-pharmacy", + "metadata": {}, + "source": [ + "With these parameters, net growth is 0 when the population is about 13.9 billion\n", + "(the result is positive because `beta` is negative).\n", + "\n", + "In the context of population modeling, the quadratic model is more\n", + "conventionally written like this: \n", + "\n", + "$$\\Delta p = r p (1 - p / K)$$ \n", + "\n", + "This is the same model; it's just a different way to *parameterize* it. Given $\\alpha$ and $\\beta$, we can compute $r=\\alpha$ and $K=-\\alpha/\\beta$.\n", + "\n", + "In this version, it is easier to interpret the parameters: $r$ is the\n", + "unconstrained growth rate, observed when $p$ is small, and $K$ is the\n", + "equilibrium point. \n", + "$K$ is also called the *carrying capacity*, since it indicates the maximum population the environment can sustain." + ] + }, + { + "cell_type": "markdown", + "id": "continental-image", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "In this chapter we implemented a quadratic growth model where net growth depends on the current population and the population squared.\n", + "This model fits the data well, and we saw one reason why: it is based on the assumption that there is a limit to the number of people the Earth can support.\n", + "\n", + "In the next chapter we'll use the models we have developed to generate\n", + "predictions.\n", + "But first, I want to warn you about a few things that can go wrong when you write functions." + ] + }, + { + "cell_type": "markdown", + "id": "eligible-pride", + "metadata": {}, + "source": [ + "## Dysfunctions\n", + "\n", + "When people learn about functions, there are a few things they often\n", + "find confusing. In this section I'll present and explain some common\n", + "problems.\n", + "\n", + "As an example, suppose you want a function that takes a\n", + "`System` object, with variables `alpha` and `beta`, and computes the\n", + "carrying capacity, `-alpha/beta`. \n", + "Here's a good solution:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "realistic-opinion", + "metadata": {}, + "outputs": [], + "source": [ + "def carrying_capacity(system):\n", + " K = -system.alpha / system.beta\n", + " return K\n", + " \n", + "sys1 = System(alpha=0.025, beta=-0.0018)\n", + "pop = carrying_capacity(sys1)\n", + "print(pop)" + ] + }, + { + "cell_type": "markdown", + "id": "olive-information", + "metadata": {}, + "source": [ + "Now let's see all the ways that can go wrong." + ] + }, + { + "cell_type": "markdown", + "id": "prostate-motorcycle", + "metadata": {}, + "source": [ + "*Dysfunction #1:* Not using parameters. In the following version, the function doesn't take any parameters; when `sys1` appears inside the function, it refers to the object we create outside the function." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "marine-entry", + "metadata": {}, + "outputs": [], + "source": [ + "def carrying_capacity():\n", + " K = -sys1.alpha / sys1.beta\n", + " return K\n", + " \n", + "sys1 = System(alpha=0.025, beta=-0.0018)\n", + "pop = carrying_capacity()\n", + "print(pop)" + ] + }, + { + "cell_type": "markdown", + "id": "dated-invalid", + "metadata": {}, + "source": [ + "This version works, but it is not as versatile as it could be.\n", + "If there are several `System` objects, this function can work with only one of them, and only if it is named `sys1`." + ] + }, + { + "cell_type": "markdown", + "id": "meaningful-louisiana", + "metadata": {}, + "source": [ + "*Dysfunction #2:* Clobbering the parameters. When people first learn\n", + "about parameters, they often write functions like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "moving-brazil", + "metadata": {}, + "outputs": [], + "source": [ + "# WRONG\n", + "def carrying_capacity(system):\n", + " system = System(alpha=0.025, beta=-0.0018)\n", + " K = -system.alpha / system.beta\n", + " return K\n", + " \n", + "sys1 = System(alpha=0.03, beta=-0.002)\n", + "pop = carrying_capacity(sys1)\n", + "print(pop)" + ] + }, + { + "cell_type": "markdown", + "id": "dietary-spectacular", + "metadata": {}, + "source": [ + "In this example, we have a `System` object named `sys1` that gets passed\n", + "as an argument to `carrying_capacity`. But when the function runs, it\n", + "ignores the argument and immediately replaces it with a new `System`\n", + "object. As a result, this function always returns the same value, no\n", + "matter what argument is passed.\n", + "\n", + "When you write a function, you generally don't know what the values of\n", + "the parameters will be. Your job is to write a function that works for\n", + "any valid values. If you assign your own values to the parameters, you\n", + "defeat the whole purpose of functions." + ] + }, + { + "cell_type": "markdown", + "id": "present-estonia", + "metadata": {}, + "source": [ + "*Dysfunction #3:* No return value. Here's a version that computes the value of `K` but doesn't return it." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "sacred-physiology", + "metadata": {}, + "outputs": [], + "source": [ + "# WRONG\n", + "def carrying_capacity(system):\n", + " K = -system.alpha / system.beta\n", + " \n", + "sys1 = System(alpha=0.025, beta=-0.0018)\n", + "pop = carrying_capacity(sys1)\n", + "print(pop)" + ] + }, + { + "cell_type": "markdown", + "id": "technological-incentive", + "metadata": {}, + "source": [ + "A function that doesn't have a return statement actually returns a special value called `None`, so in this example the value of `pop` is `None`. If you are debugging a program and find that the value of a variable is `None` when it shouldn't be, a function without a return statement is a likely cause." + ] + }, + { + "cell_type": "markdown", + "id": "received-firewall", + "metadata": {}, + "source": [ + "*Dysfunction #4:* Ignoring the return value. Finally, here's a version where the function is correct, but the way it's used is not.\n", + "\n", + "```\n", + "def carrying_capacity(system):\n", + " K = -system.alpha / system.beta\n", + " return K\n", + " \n", + "sys1 = System(alpha=0.025, beta=-0.0018)\n", + "carrying_capacity(sys1) # WRONG\n", + "print(K)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "approximate-straight", + "metadata": {}, + "source": [ + "In this example, `carrying_capacity` runs and returns `K`, but the\n", + "return value doesn't get displayed or assigned to a variable.\n", + "If we try to print `K`, we get a `NameError`, because `K` only exists inside the function.\n", + "\n", + "When you call a function that returns a value, you should do something\n", + "with the result." + ] + }, + { + "cell_type": "markdown", + "id": "liable-mixture", + "metadata": {}, + "source": [ + "## Exercises" + ] + }, + { + "cell_type": "markdown", + "id": "worst-builder", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + " In a previous section, we saw a different way to parameterize the quadratic model:\n", + "\n", + "$$ \\Delta p = r p (1 - p / K) $$\n", + "\n", + "where $r=\\alpha$ and $K=-\\alpha/\\beta$. \n", + "\n", + "Write a version of `growth_func` that implements this version of the model. Test it by computing the values of `r` and `K` that correspond to `alpha=0.025` and `beta=-0.0018`, and confirm that you get the same results. " + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "stretch-check", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "tender-treat", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "passive-certificate", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "understood-cancer", + "metadata": {}, + "source": [ + "### Exercise 2\n", + "\n", + " What happens if we start with an initial population above the carrying capacity, like 20 billion? Run the model with initial populations between 1 and 20 billion, and plot the results on the same axes.\n", + "\n", + "Hint: If there are too many labels in the legend, you can plot results like this:\n", + "\n", + "```\n", + " results.plot(label='_nolegend')\n", + "```\n" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "agricultural-burke", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "colored-globe", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap08.ipynb b/chapters/chap08.ipynb new file mode 100644 index 000000000..2610feb4e --- /dev/null +++ b/chapters/chap08.ipynb @@ -0,0 +1,785 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "external-reward", + "metadata": {}, + "source": [ + "# Projecting Population Growth" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "electoral-turkey", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "global-international", + "metadata": { + "tags": [] + }, + "source": [ + "Here's the data from the previous chapters, one last time." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "necessary-factor", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/data/World_population_estimates.html')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "changed-desktop", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from pandas import read_html\n", + "\n", + "filename = 'World_population_estimates.html'\n", + "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", + "table2 = tables[2]\n", + "table2.columns = ['census', 'prb', 'un', 'maddison', \n", + " 'hyde', 'tanton', 'biraben', 'mj', \n", + " 'thomlinson', 'durand', 'clark']" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "metallic-inventory", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "un = table2.un / 1e9\n", + "census = table2.census / 1e9" + ] + }, + { + "cell_type": "markdown", + "id": "current-canberra", + "metadata": { + "tags": [] + }, + "source": [ + "And here are the functions from the previous chapter." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "measured-arthur", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'chap06.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "cutting-financing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from chap06 import run_simulation\n", + "\n", + "def plot_estimates():\n", + " census.plot(style=':', label='US Census')\n", + " un.plot(style='--', label='UN DESA')\n", + " decorate(xlabel='Year', \n", + " ylabel='World population (billions)') " + ] + }, + { + "cell_type": "markdown", + "id": "chicken-emphasis", + "metadata": {}, + "source": [ + "In the previous chapter we developed a quadratic model of world\n", + "population growth from 1950 to 2016. It is a simple model, but it fits\n", + "the data well and the mechanisms it's based on are plausible.\n", + "\n", + "In this chapter we'll use the quadratic model to generate projections of future growth, and compare our results to projections from actual\n", + "demographers." + ] + }, + { + "cell_type": "markdown", + "id": "further-armstrong", + "metadata": {}, + "source": [ + "## Generating Projections" + ] + }, + { + "cell_type": "markdown", + "id": "concrete-lightning", + "metadata": {}, + "source": [ + "Let's run the quadratic model, extending the results until 2100, and see how our projections compare to the professionals'.\n", + "\n", + "Here's the quadratic growth function again." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "indirect-russia", + "metadata": {}, + "outputs": [], + "source": [ + "def growth_func_quad(t, pop, system):\n", + " return system.alpha * pop + system.beta * pop**2" + ] + }, + { + "cell_type": "markdown", + "id": "little-struggle", + "metadata": {}, + "source": [ + "And here are the system parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "comfortable-compression", + "metadata": {}, + "outputs": [], + "source": [ + "t_0 = census.index[0]\n", + "p_0 = census[t_0]\n", + "\n", + "system = System(t_0 = t_0,\n", + " p_0 = p_0,\n", + " alpha = 25 / 1000,\n", + " beta = -1.8 / 1000,\n", + " t_end = 2100)" + ] + }, + { + "cell_type": "markdown", + "id": "legitimate-guess", + "metadata": {}, + "source": [ + "With `t_end=2100`, we can generate the projection by calling `run_simulation` the usual way." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "broken-windsor", + "metadata": {}, + "outputs": [], + "source": [ + "results = run_simulation(system, growth_func_quad)" + ] + }, + { + "cell_type": "markdown", + "id": "provincial-competition", + "metadata": {}, + "source": [ + "Here are the last few values in the results." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "latest-function", + "metadata": {}, + "outputs": [], + "source": [ + "show(results.tail())" + ] + }, + { + "cell_type": "markdown", + "id": "outer-ensemble", + "metadata": {}, + "source": [ + "Here's what the results look like." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "portable-pottery", + "metadata": {}, + "outputs": [], + "source": [ + "results.plot(color='gray', label='model')\n", + "decorate(xlabel='Year', \n", + " ylabel='World population (billions)',\n", + " title='Quadratic model projection')" + ] + }, + { + "cell_type": "markdown", + "id": "macro-carroll", + "metadata": {}, + "source": [ + "According to the model, population growth will slow gradually after 2020, approaching 12.6 billion by 2100.\n", + "\n", + "I am using the word *projection* deliberately, rather than\n", + "*prediction*, with the following distinction: \"prediction\" implies\n", + "something like \"this is what we expect to happen, at\n", + "least approximately\"; \"projection\" implies something like \"if this\n", + "model is a good description of the system, and if nothing in the future causes the system parameters to change, this is what would happen.\"\n", + "\n", + "Using \"projection\" leaves open the possibility that there are important things in the real world that are not captured in the model. It also suggests that, even if the model is good, the parameters we estimate based on the past might be different in the future.\n", + "\n", + "The quadratic model we've been working with is based on the assumption\n", + "that population growth is limited by the availability of resources; in\n", + "that scenario, as the population approaches carrying capacity, birth\n", + "rates fall and death rates rise because resources become scarce.\n", + "\n", + "If that assumption is valid, we might be able to use actual population\n", + "growth to estimate carrying capacity, provided we observe the\n", + "transition into the population range where the growth rate starts to fall.\n", + "\n", + "But in the case of world population growth, those conditions don't\n", + "apply. Over the last 50 years, the net growth rate has leveled off, but not yet started to fall, so we don't have enough data to make a credible estimate of carrying capacity. And resource limitations are probably *not* the primary reason growth has slowed. As evidence, consider:\n", + "\n", + "- First, the death rate is not increasing; rather, it has declined\n", + " from 1.9% in 1950 to 0.8% now (see ).\n", + " So the decrease in net growth is due entirely to declining birth\n", + " rates.\n", + "\n", + "- Second, the relationship between resources and birth rate is the\n", + " opposite of what the model assumes; as nations develop and people\n", + " become more wealthy, birth rates tend to fall.\n", + "\n", + "We should not take too seriously the idea that this model can estimate\n", + "carrying capacity. But the predictions of a model can be credible even\n", + "if the assumptions of the model are not strictly true. For example,\n", + "population growth might behave *as if* it is resource limited, even if\n", + "the actual mechanism is something else.\n", + "\n", + "In fact, demographers who study population growth often use models\n", + "similar to ours. In the next section, we'll compare our projections to\n", + "theirs." + ] + }, + { + "cell_type": "markdown", + "id": "small-seminar", + "metadata": {}, + "source": [ + "## Comparing Projections\n", + "\n", + "From the same Wikipedia page where we got the past population estimates, we'll read `table3`, which contains projections for population growth over the next 50-100 years, generated by the U.S. Census, U.N. DESA, and the Population Reference Bureau." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "20e6f166", + "metadata": {}, + "outputs": [], + "source": [ + "table3 = tables[3]" + ] + }, + { + "cell_type": "markdown", + "id": "coated-smoke", + "metadata": {}, + "source": [ + "The column names are long strings; for convenience, I'll replace them with abbreviations." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "headed-tuner", + "metadata": {}, + "outputs": [], + "source": [ + "table3.columns = ['census', 'prb', 'un']" + ] + }, + { + "cell_type": "markdown", + "id": "c2a91ffb", + "metadata": {}, + "source": [ + "Here are the first few rows:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "precious-contribution", + "metadata": {}, + "outputs": [], + "source": [ + "table3.head()" + ] + }, + { + "cell_type": "markdown", + "id": "f62861c1", + "metadata": {}, + "source": [ + "Some values are `NaN`, which indicates missing data, because some organizations did not publish projections for some years.\n", + "The following function plots projections from the U.N. DESA and U.S. Census. It uses `dropna` to remove the `NaN` values from each series before plotting it." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "paperback-delay", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_projections(table):\n", + " \"\"\"Plot world population projections.\n", + " \n", + " table: DataFrame with columns 'un' and 'census'\n", + " \"\"\"\n", + " census_proj = table.census.dropna() / 1e9\n", + " un_proj = table.un.dropna() / 1e9\n", + " \n", + " census_proj.plot(style=':', label='US Census')\n", + " un_proj.plot(style='--', label='UN DESA')\n", + " \n", + " decorate(xlabel='Year', \n", + " ylabel='World population (billions)')" + ] + }, + { + "cell_type": "markdown", + "id": "prostate-matrix", + "metadata": {}, + "source": [ + "Here are the professional projections compared to the results of the quadratic model." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "billion-dynamics", + "metadata": {}, + "outputs": [], + "source": [ + "results.plot(color='gray', label='model')\n", + "plot_projections(table3)\n", + "decorate(title='Quadratic model projection, with UN and Census')" + ] + }, + { + "cell_type": "markdown", + "id": "integrated-there", + "metadata": {}, + "source": [ + "The U.N. DESA expects the world population to reach 11 billion around 2100, and then level off.\n", + "Projections by U.S. Census are a little lower, and they only go until 2050.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "serial-binary", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "In this chapter we use the quadratic growth model to project world population growth between now and 2100.\n", + "\n", + "Real demographers expect world population to grow more slowly than our model, probably because their models are broken down by region and country, where conditions are different, and they take into account expected economic development.\n", + "\n", + "Nevertheless, their projections are qualitatively similar to ours, and\n", + "theirs differ from each other almost as much as they differ from ours.\n", + "So the results from the model, simple as it is, are not entirely unreasonable.\n", + "\n", + "If you are interested in some of the factors that go into the professional projections, you might like this video by Hans Rosling about the demographic changes we expect this century: ." + ] + }, + { + "cell_type": "markdown", + "id": "automated-simpson", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "You can access the notebooks at ." + ] + }, + { + "cell_type": "markdown", + "id": "authorized-suggestion", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + " The net growth rate of world population has been declining for several decades. That observation suggests one more way to generate more realistic projections, by extrapolating observed changes in growth rate.\n", + "\n", + "To compute past growth rates, we'll use a function called `diff`, which computes the difference between successive elements in a `Series`. For example, here are the changes from one year to the next in `census`:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "handmade-funeral", + "metadata": {}, + "outputs": [], + "source": [ + "diff = census.diff()\n", + "show(diff.head())" + ] + }, + { + "cell_type": "markdown", + "id": "discrete-scanner", + "metadata": {}, + "source": [ + "The first element is `NaN` because we don't have the data for 1949, so we can't compute the first difference.\n", + "\n", + "If we divide these differences by the populations, the result is an estimate of the growth rate during each year: " + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "objective-accused", + "metadata": {}, + "outputs": [], + "source": [ + "alpha = census.diff() / census\n", + "show(alpha.head())" + ] + }, + { + "cell_type": "markdown", + "id": "weighted-trouble", + "metadata": {}, + "source": [ + "The following function computes and plots the growth rates for the `census` and `un` estimates:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "unique-matrix", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_alpha():\n", + " alpha_census = census.diff() / census\n", + " alpha_census.plot(style='.', label='US Census')\n", + "\n", + " alpha_un = un.diff() / un\n", + " alpha_un.plot(style='.', label='UN DESA')\n", + "\n", + " decorate(xlabel='Year', ylabel='Net growth rate')" + ] + }, + { + "cell_type": "markdown", + "id": "flexible-amateur", + "metadata": {}, + "source": [ + "It uses `style='.'` to plot each data point with a small circle.\n", + "And here's what it looks like." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "pressing-proceeding", + "metadata": {}, + "outputs": [], + "source": [ + "plot_alpha()" + ] + }, + { + "cell_type": "markdown", + "id": "cross-reducing", + "metadata": {}, + "source": [ + "Other than a bump around 1990, the net growth rate has been declining roughly linearly since 1970.\n", + "\n", + "We can model the decline by fitting a line to this data and extrapolating into the future.\n", + "Here's a function that takes a time stamp and computes a line that roughly fits the growth rates since 1970." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "mexican-denver", + "metadata": {}, + "outputs": [], + "source": [ + "def alpha_func(t):\n", + " intercept = 0.02\n", + " slope = -0.00021\n", + " return intercept + slope * (t - 1970)" + ] + }, + { + "cell_type": "markdown", + "id": "handy-dubai", + "metadata": {}, + "source": [ + "To see what it looks like, I'll create an array of time stamps from 1960 to 2020 and use `alpha_func` to compute the corresponding growth rates." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "addressed-worker", + "metadata": {}, + "outputs": [], + "source": [ + "t_array = linspace(1960, 2020, 5)\n", + "alpha_array = alpha_func(t_array)" + ] + }, + { + "cell_type": "markdown", + "id": "jewish-operations", + "metadata": {}, + "source": [ + "Here's what it looks like, compared to the data." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "cathedral-shakespeare", + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib.pyplot import plot\n", + "\n", + "plot_alpha()\n", + "plot(t_array, alpha_array, label='model', color='gray')\n", + "\n", + "decorate(ylabel='Net growth rate',\n", + " title='Linear model of net growth rate')" + ] + }, + { + "cell_type": "markdown", + "id": "expensive-viewer", + "metadata": {}, + "source": [ + "If you don't like the `slope` and `intercept` I chose, feel free to adjust them.\n", + "\n", + "Now, as an exercise, you can use this function to project world population until 2100.\n", + "\n", + "1. Create a `System` object that includes `alpha_func` as a system parameter.\n", + "\n", + "2. Define a growth function that uses `alpha_func` to compute the net growth rate at the given time `t`.\n", + "\n", + "3. Run a simulation from 1960 to 2100 with your growth function, and plot the results.\n", + "\n", + "4. Compare your projections with those from the US Census and UN." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "common-april", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "ignored-chain", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "color-accountability", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "numeric-raise", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "included-vehicle", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "brown-rating", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "laughing-cylinder", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "personalized-parking", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "simple-version", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap09.ipynb b/chapters/chap09.ipynb new file mode 100644 index 000000000..217108dc9 --- /dev/null +++ b/chapters/chap09.ipynb @@ -0,0 +1,1119 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "asian-customer", + "metadata": {}, + "source": [ + "# Analysis of Population Growth" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "electoral-turkey", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "biblical-murder", + "metadata": {}, + "source": [ + "In this chapter we'll express the models from previous chapters as\n", + "difference equations and differential equations, solve the equations,\n", + "and derive the functional forms of the solutions. And I'll present some thoughts about the complementary roles of mathematical analysis and simulation." + ] + }, + { + "cell_type": "markdown", + "id": "hybrid-retrieval", + "metadata": {}, + "source": [ + "## Difference Equations\n", + "\n", + "The population models in the previous chapter and this one are simple\n", + "enough that we didn't really need to run simulations. We could have\n", + "solved them mathematically. For example, we wrote the constant growth\n", + "model like this:\n", + "\n", + "```\n", + "results[t+1] = results[t] + annual_growth\n", + "```\n", + "\n", + "In mathematical notation, we would write the same model like this:\n", + "\n", + "$$x_{n+1} = x_n + c$$ \n", + "\n", + "where $x_n$ is the population during year $n$, \n", + "$x_{n+1}$ is the population during year $n+1$,\n", + "and $c$ is constant annual growth.\n", + "This way of representing the model, where future population is a function of current population, is a *difference equation*; see\n", + ".\n", + "\n", + "For a given value of $n$, sometimes it is possible to compute $x_n$ directly; that\n", + "is, without computing the intervening values from $x_1$ through $x_{n-1}$." + ] + }, + { + "cell_type": "markdown", + "id": "specified-mexican", + "metadata": {}, + "source": [ + "In the case of constant growth we can see that $x_1 = x_0 + c$, and\n", + "$x_2 = x_1 + c$. Combining these, we get $x_2 = x_0 + 2c$, then\n", + "$x_3 = x_0 + 3c$, and we can see that in general\n", + "\n", + "$$x_n = x_0 + nc$$ \n", + "\n", + "So if we want to know $x_{100}$ and we don't care about the other values, we can compute it with one multiplication and one addition." + ] + }, + { + "cell_type": "markdown", + "id": "sufficient-tampa", + "metadata": {}, + "source": [ + "We can also write the proportional model as a difference equation:\n", + "\n", + "$$x_{n+1} = x_n + \\alpha x_n$$ \n", + "\n", + "Or more conventionally as:\n", + "\n", + "$$x_{n+1} = x_n (1 + \\alpha)$$ \n", + "\n", + "Now we can see that $x_1 = x_0 (1 + \\alpha)$, and $x_2 = x_0 (1 + \\alpha)^2$, and in general\n", + "\n", + "$$x_n = x_0 (1 + \\alpha)^n$$\n", + "\n", + "A sequence with this functional form is called a *geometric progression*;\n", + "see . When $\\alpha$ is positive, the factor\n", + "$1+\\alpha$ is greater than 1, so the elements of the sequence grow\n", + "without bound." + ] + }, + { + "cell_type": "markdown", + "id": "motivated-consumer", + "metadata": {}, + "source": [ + "Finally, we can write the quadratic model like this:\n", + "\n", + "$$x_{n+1} = x_n + \\alpha x_n + \\beta x_n^2$$ \n", + "\n", + "or with the more conventional parameterization like this:\n", + "\n", + "$$x_{n+1} = x_n + r x_n (1 - x_n / K)$$ \n", + "\n", + "There is no analytic solution to this equation, but we can approximate it with a differential equation and solve that, which is what we'll do in the next section." + ] + }, + { + "cell_type": "markdown", + "id": "decreased-location", + "metadata": {}, + "source": [ + "## Differential Equations\n", + "\n", + "Starting again with the constant growth model \n", + "\n", + "$$x_{n+1} = x_n + c$$ \n", + "\n", + "If we define $\\Delta x$ to be the change in $x$ from one time step to the next, we can write: \n", + "\n", + "$$\\Delta x = x_{n+1} - x_n = c$$ \n", + "\n", + "If we define\n", + "$\\Delta t$ to be the time step, which is one year in the example, we can\n", + "write the rate of change per unit of time like this:\n", + "\n", + "$$\\frac{\\Delta x}{\\Delta t} = c$$ \n", + "\n", + "This is a *discrete* model, which\n", + "means time is only defined at integer values of $n$ and not in between.\n", + "But in reality, people are born and die all the time, not once a year,\n", + "so it might be more realistic to use a *continuous* model, which means\n", + "time is defined at all values of $t$, not just integers. " + ] + }, + { + "cell_type": "markdown", + "id": "latest-impression", + "metadata": {}, + "source": [ + "In a continuous model, we write the rate of change in the\n", + "form of a derivative: \n", + "\n", + "$$\\frac{dx}{dt} = c$$ \n", + "\n", + "This way of representing the model is a *differential equation*, which is an equation that involves at least one derivative (see ).\n", + "To solve this equation, we multiply both sides by $dt$: \n", + "\n", + "$$dx = c dt$$ \n", + "\n", + "And then integrate both sides: \n", + "\n", + "$$x(t) = c t + x_0$$" + ] + }, + { + "cell_type": "markdown", + "id": "covered-defense", + "metadata": {}, + "source": [ + "Similarly, we can write the proportional growth model like this:\n", + "\n", + "$$\\frac{\\Delta x}{\\Delta t} = \\alpha x$$ \n", + "\n", + "And as a differential equation\n", + "like this: \n", + "\n", + "$$\\frac{dx}{dt} = \\alpha x$$ \n", + "\n", + "If we multiply both sides by\n", + "$dt$ and divide by $x$, we get \n", + "\n", + "$$\\frac{1}{x}~dx = \\alpha~dt$$ \n", + "\n", + "Now we\n", + "integrate both sides, yielding: \n", + "\n", + "$$\\ln x = \\alpha t + K$$ \n", + "\n", + "where $\\ln$ is the natural logarithm and $K$ is the constant of integration." + ] + }, + { + "cell_type": "markdown", + "id": "underlying-visitor", + "metadata": {}, + "source": [ + "Exponentiating both sides, we have\n", + "\n", + "$$\\exp(\\ln(x)) = \\exp(\\alpha t + K)$$ \n", + "\n", + "The exponential function can be written $\\exp(x)$ or $e^x$. In this book I use the first form because it resembles the Python code.\n", + "We can rewrite the previous equation as\n", + "\n", + "$$x = \\exp(\\alpha t) \\exp(K)$$ \n", + "\n", + "Since $K$ is an arbitrary constant,\n", + "$\\exp(K)$ is also an arbitrary constant, so we can write\n", + "\n", + "$$x = C \\exp(\\alpha t)$$ \n", + "\n", + "where $C = \\exp(K)$. There are many solutions to this differential equation, with different values of $C$. The particular solution we want is the one that has the value $x_0$ when $t=0$.\n", + "\n", + "When $t=0$, $x(t) = C$, so $C = x_0$ and the solution we want is\n", + "\n", + "$$x(t) = x_0 \\exp(\\alpha t)$$ \n", + "\n", + "If you would like to see this derivation done more carefully, you might like this video:\n", + "." + ] + }, + { + "cell_type": "markdown", + "id": "czech-scratch", + "metadata": {}, + "source": [ + "## Analysis and Simulation\n", + "\n", + "Once you have designed a model, there are generally two ways to proceed: simulation and analysis. Simulation often comes in the form of a computer program that models changes in a system over time, like births and deaths, or bikes moving from place to place. Analysis often comes in the form of algebra and calculus; that is, symbolic manipulation using mathematical notation.\n", + "\n", + "Analysis and simulation have different capabilities and limitations.\n", + "Simulation is generally more versatile; it is easy to add and remove parts of a program and test many versions of a model, as we have done in the previous examples.\n", + "\n", + "But there are several things we can do with analysis that are harder or impossible with simulations:" + ] + }, + { + "cell_type": "markdown", + "id": "embedded-miniature", + "metadata": {}, + "source": [ + "- With analysis we can sometimes compute, exactly and efficiently, a\n", + " value that we could only approximate, less efficiently, with\n", + " simulation. For example, in the quadratic model we plotted net growth versus population and saw it crosses through zero when the population is\n", + " near 14 billion. We could estimate the crossing point using a\n", + " numerical search algorithm (more about that later). But with a bit of algebra, we derived the general result that $K=-\\alpha/\\beta$.\n", + "\n", + "- Analysis sometimes provides \"computational shortcuts\", that is, the\n", + " ability to jump forward in time to compute the state of a system\n", + " many time steps in the future without computing the intervening\n", + " states.\n", + "\n", + "- We can use analysis to state and prove generalizations about models;\n", + " for example, we might prove that certain results will always or\n", + " never occur. With simulations, we can show examples and sometimes\n", + " find counterexamples, but it is hard to write proofs.\n", + "\n", + "- Analysis can provide insight into models and the systems they\n", + " describe; for example, sometimes we can identify\n", + " qualitatively different ways the system can behave and key parameters that control\n", + " those behaviors." + ] + }, + { + "cell_type": "markdown", + "id": "impressive-planning", + "metadata": {}, + "source": [ + "When people see what analysis can do, they sometimes get drunk with\n", + "power and imagine that it gives them a special ability to see past the veil of the material world and discern the laws of mathematics that govern the universe. When they analyze a model of a physical system, they talk about \"the math behind it\" as if our world is the mere shadow of a world of ideal mathematical entities (I am not making this up; see .).\n", + "\n", + "This is, of course, nonsense. Mathematical notation is a language\n", + "designed by humans for a purpose, specifically to facilitate symbolic\n", + "manipulations like algebra. Similarly, programming languages are\n", + "designed for a purpose, specifically to represent computational ideas\n", + "and run programs.\n", + "\n", + "Each of these languages is good for the purposes it was designed for and less good for other purposes. But they are often complementary, and one of the goals of this book is to show how they can be used together." + ] + }, + { + "cell_type": "markdown", + "id": "graduate-conducting", + "metadata": {}, + "source": [ + "## Analysis with WolframAlpha\n", + "\n", + "Until recently, most analysis was done by rubbing graphite on wood\n", + "pulp, a process that is laborious and error-prone. A useful\n", + "alternative is symbolic computation. If you have used services like\n", + "WolframAlpha, you have used symbolic computation.\n", + "\n", + "For example, if you go to and enter\n", + "\n", + "```\n", + "df(t) / dt = alpha f(t)\n", + "```\n", + "\n", + "WolframAlpha infers that `f(t)` is a function of `t` and `alpha` is a\n", + "parameter; it classifies the query as a \"first-order linear ordinary\n", + "differential equation\", and reports the general solution:\n", + "\n", + "$$f(t) = c_1 \\exp(\\alpha t)$$ \n", + "\n", + "If you add a second equation to specify the initial condition:\n", + "\n", + "```\n", + "df(t) / dt = alpha f(t), f(0) = p_0\n", + "```\n", + "\n", + "WolframAlpha reports the particular solution:\n", + "\n", + "$$f(t) = p_0 \\exp(\\alpha t)$$\n", + "\n", + "WolframAlpha is based on Mathematica, a powerful programming language\n", + "designed specifically for symbolic computation." + ] + }, + { + "cell_type": "markdown", + "id": "beginning-tamil", + "metadata": {}, + "source": [ + "## Analysis with SymPy\n", + "\n", + "Python has a library called SymPy that provides symbolic computation\n", + "tools similar to Mathematica. They are not as easy to use as\n", + "WolframAlpha, but they have some other advantages.\n", + "\n", + "To use it, we'll define `Symbol` objects that represent names of variables and functions.\n", + "The `symbols` function takes a string and returns `Symbol` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "flush-balance", + "metadata": {}, + "outputs": [], + "source": [ + "from sympy import symbols\n", + "\n", + "t = symbols('t')" + ] + }, + { + "cell_type": "markdown", + "id": "faced-praise", + "metadata": {}, + "source": [ + "Now when we use `t`, Python treats it like a variable name rather than a specific number. \n", + "For example, if we use `t` as part of an expression, like this," + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "ancient-anderson", + "metadata": {}, + "outputs": [], + "source": [ + "expr = t + 1\n", + "expr" + ] + }, + { + "cell_type": "markdown", + "id": "industrial-confidence", + "metadata": {}, + "source": [ + "Python doesn't try to perform numerical addition; rather, it creates a\n", + "new `Symbol` that represents the sum of `t` and `1`. We can evaluate the\n", + "sum using `subs`, which substitutes a value for a symbol. This example\n", + "substitutes 2 for `t`:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "familiar-oakland", + "metadata": {}, + "outputs": [], + "source": [ + "expr.subs(t, 2)" + ] + }, + { + "cell_type": "markdown", + "id": "progressive-celebration", + "metadata": {}, + "source": [ + "Functions in SymPy are represented by a special kind of `Symbol`:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "incomplete-sleep", + "metadata": {}, + "outputs": [], + "source": [ + "from sympy import Function\n", + "\n", + "f = Function('f')\n", + "f" + ] + }, + { + "cell_type": "markdown", + "id": "legislative-dispatch", + "metadata": {}, + "source": [ + "Now if we write `f(t)`, we get an object that represents the evaluation of a function, $f$, at a value, $t$. " + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "inside-sally", + "metadata": {}, + "outputs": [], + "source": [ + "f(t)" + ] + }, + { + "cell_type": "markdown", + "id": "interior-index", + "metadata": {}, + "source": [ + "But again SymPy doesn't actually\n", + "try to evaluate it." + ] + }, + { + "cell_type": "markdown", + "id": "indian-trout", + "metadata": {}, + "source": [ + "## Differential Equations In SymPy\n", + "\n", + "SymPy provides a function, `diff`, that can differentiate a function. We can apply it to `f(t)` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "requested-program", + "metadata": {}, + "outputs": [], + "source": [ + "from sympy import diff\n", + "\n", + "dfdt = diff(f(t), t)\n", + "dfdt" + ] + }, + { + "cell_type": "markdown", + "id": "treated-commissioner", + "metadata": {}, + "source": [ + "The result is a `Symbol` that represents the derivative of `f` with\n", + "respect to `t`. But again, SymPy doesn't try to compute the derivative\n", + "yet.\n", + "\n", + "To represent a differential equation, we use `Eq`:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "incomplete-parker", + "metadata": {}, + "outputs": [], + "source": [ + "from sympy import Eq\n", + "\n", + "alpha = symbols('alpha')\n", + "eq1 = Eq(dfdt, alpha*f(t))\n", + "eq1" + ] + }, + { + "cell_type": "markdown", + "id": "concerned-asthma", + "metadata": {}, + "source": [ + "The result is an object that represents an equation. Now\n", + "we can use `dsolve` to solve this differential equation:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "described-overhead", + "metadata": {}, + "outputs": [], + "source": [ + "from sympy import dsolve\n", + "\n", + "solution_eq = dsolve(eq1)\n", + "solution_eq" + ] + }, + { + "cell_type": "markdown", + "id": "inside-medicine", + "metadata": {}, + "source": [ + "The result is the *general solution*, which still contains an unspecified constant, $C_1$.\n", + "To get the *particular solution* where $f(0) = p_0$, we substitute $p_0$ for `C1`. \n", + "First, we have to tell Python that `C1` is a symbol." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "material-voice", + "metadata": {}, + "outputs": [], + "source": [ + "C1 = symbols('C1')" + ] + }, + { + "cell_type": "markdown", + "id": "serious-license", + "metadata": {}, + "source": [ + "Now we can substitute the value of $p_0$ for `C1`.\n", + "For example, if $p_0$ is 1000:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "supposed-domestic", + "metadata": {}, + "outputs": [], + "source": [ + "particular = solution_eq.subs(C1, 1000)\n", + "particular" + ] + }, + { + "cell_type": "markdown", + "id": "modern-raleigh", + "metadata": {}, + "source": [ + "When $t=0$, the value of $f(0)$ is $p_0$, which confirms that this is the solution we want." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "394e9410", + "metadata": {}, + "outputs": [], + "source": [ + "particular.subs(t, 0)" + ] + }, + { + "cell_type": "markdown", + "id": "negative-chance", + "metadata": {}, + "source": [ + "## Solving the Quadratic Growth Model\n", + "\n", + "To solve the quadratic growth curve, we'll use the `r, K` parameterization, so we'll need two more symbols:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "political-chinese", + "metadata": {}, + "outputs": [], + "source": [ + "r, K = symbols('r K')" + ] + }, + { + "cell_type": "markdown", + "id": "virtual-temperature", + "metadata": {}, + "source": [ + "Now we can write the differential equation." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "lined-queen", + "metadata": {}, + "outputs": [], + "source": [ + "eq2 = Eq(diff(f(t), t), r * f(t) * (1 - f(t)/K))\n", + "eq2" + ] + }, + { + "cell_type": "markdown", + "id": "relative-zoning", + "metadata": {}, + "source": [ + "And solve it." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "printable-typing", + "metadata": {}, + "outputs": [], + "source": [ + "solution_eq = dsolve(eq2)\n", + "solution_eq" + ] + }, + { + "cell_type": "markdown", + "id": "convertible-simple", + "metadata": {}, + "source": [ + "The result, `solution_eq`, contains `rhs`, which is the right-hand side of the solution." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "final-treatment", + "metadata": {}, + "outputs": [], + "source": [ + "general = solution_eq.rhs\n", + "general" + ] + }, + { + "cell_type": "markdown", + "id": "phantom-melbourne", + "metadata": {}, + "source": [ + "We can evaluate the right-hand side at $t=0$" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "freelance-admission", + "metadata": {}, + "outputs": [], + "source": [ + "at_0 = general.subs(t, 0)\n", + "at_0" + ] + }, + { + "cell_type": "markdown", + "id": "precise-radar", + "metadata": {}, + "source": [ + "Now we want to find the value of `C1` that makes `f(0) = p_0`.\n", + "\n", + "So we'll create the equation `at_0 = p_0` and solve for `C1`. Because this is just an algebraic equation, not a differential equation, we use `solve`, not `dsolve`." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "orange-glasgow", + "metadata": {}, + "outputs": [], + "source": [ + "from sympy import solve\n", + "\n", + "p_0 = symbols('p_0')\n", + "solutions = solve(Eq(at_0, p_0), C1)" + ] + }, + { + "cell_type": "markdown", + "id": "aggressive-annual", + "metadata": {}, + "source": [ + "The result from `solve` is a list of solutions. " + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "expensive-palace", + "metadata": {}, + "outputs": [], + "source": [ + "type(solutions), len(solutions)" + ] + }, + { + "cell_type": "markdown", + "id": "periodic-bikini", + "metadata": {}, + "source": [ + "In this case, there is only one solution, but we still get a list, so we have to use the bracket operator, `[0]`, to select the first one." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "worthy-sleeve", + "metadata": {}, + "outputs": [], + "source": [ + "value_of_C1 = solutions[0]\n", + "value_of_C1" + ] + }, + { + "cell_type": "markdown", + "id": "lightweight-pickup", + "metadata": {}, + "source": [ + "Now in the general solution, we want to replace `C1` with the value of `C1` we just figured out." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "aggressive-queue", + "metadata": {}, + "outputs": [], + "source": [ + "particular = general.subs(C1, value_of_C1)\n", + "particular" + ] + }, + { + "cell_type": "markdown", + "id": "electric-albania", + "metadata": {}, + "source": [ + "The result is complicated, but SymPy provides a function that tries to simplify it." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "funded-tolerance", + "metadata": {}, + "outputs": [], + "source": [ + "simpler = particular.simplify()\n", + "simpler" + ] + }, + { + "cell_type": "markdown", + "id": "scenic-preparation", + "metadata": {}, + "source": [ + "This function is called the *logistic growth curve*; see\n", + ". In the context of growth models, the\n", + "logistic function is often written like this:\n", + "\n", + "$$f(t) = \\frac{K}{1 + A \\exp(-rt)}$$ \n", + "\n", + "where $A = (K - p_0) / p_0$." + ] + }, + { + "cell_type": "markdown", + "id": "burning-sherman", + "metadata": { + "tags": [] + }, + "source": [ + "We can use SymPy to confirm that these two forms are equivalent. First we represent the alternative version of the logistic function:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "institutional-finish", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "A = (K - p_0) / p_0\n", + "A" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "noble-auditor", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from sympy import exp\n", + "\n", + "logistic = K / (1 + A * exp(-r*t))\n", + "logistic" + ] + }, + { + "cell_type": "markdown", + "id": "brave-conditions", + "metadata": { + "tags": [] + }, + "source": [ + "To see whether two expressions are equivalent, we can check whether their difference simplifies to 0." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "industrial-administrator", + "metadata": { + "scrolled": true, + "tags": [] + }, + "outputs": [], + "source": [ + "(particular - logistic).simplify()" + ] + }, + { + "cell_type": "markdown", + "id": "therapeutic-topic", + "metadata": { + "tags": [] + }, + "source": [ + "This test only works one way: if SymPy says the difference reduces to 0, the expressions are definitely equivalent (and not just numerically close).\n", + "\n", + "But if SymPy can't find a way to simplify the result to 0, that doesn't necessarily mean there isn't one. Testing whether two expressions are equivalent is a surprisingly hard problem; in fact, there is no algorithm that can solve it in general." + ] + }, + { + "cell_type": "markdown", + "id": "flying-bermuda", + "metadata": { + "tags": [] + }, + "source": [ + "If you use SymPy to compute an expression, and then want to evaluate that expression in Python, SymPy provides a function called `pycode` that generates Python code:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "bibliographic-sarah", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from sympy.printing.pycode import pycode\n", + "\n", + "pycode(simpler)" + ] + }, + { + "cell_type": "markdown", + "id": "cutting-access", + "metadata": {}, + "source": [ + "If you would like to see this differential equation solved by hand, you might like this video: " + ] + }, + { + "cell_type": "markdown", + "id": "selective-calvin", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "In this chapter we wrote the growth models from the previous chapters in terms of difference and differential equations. We solved some of these equations by hand; for others, we used WolframAlpha and SymPy.\n", + "\n", + "What I called the \"constant growth\" model is more commonly called *linear growth* because the solution is a line. If we model time as continuous, the solution is\n", + "\n", + "$$f(t) = p_0 + c t$$\n", + "\n", + "where $c$ is net annual growth.\n", + "\n", + "Similarly, the proportional growth model is usually called *exponential growth* because the solution is an exponential function:\n", + "\n", + "$$f(t) = p_0 \\exp{\\alpha t}$$" + ] + }, + { + "cell_type": "markdown", + "id": "outstanding-launch", + "metadata": {}, + "source": [ + "Finally, the quadratic growth model is called *logistic growth* because the solution is a logistic function:\n", + "\n", + "$$f(t) = \\frac{K}{1 + A \\exp(-rt)}$$ \n", + "\n", + "where $A = (K - p_0) / p_0$.\n", + "\n", + "I avoided these terms until now because they are based on results we had not derived yet.\n", + "\n", + "With that, we are done modeling world population growth.\n", + "The next chapter presents case studies where you can apply the tools we have learned so far." + ] + }, + { + "cell_type": "markdown", + "id": "offshore-passage", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "You can access the notebooks at ." + ] + }, + { + "cell_type": "markdown", + "id": "ideal-ecology", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + " Use SymPy to solve the quadratic growth equation using the alternative parameterization\n", + "\n", + "$$ \\frac{df(t)}{dt} = \\alpha f(t) + \\beta f^2(t) $$\n" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "internal-knock", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "smooth-sunglasses", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "fundamental-arlington", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "desirable-alpha", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "coupled-grounds", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "looking-ground", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "anticipated-paragraph", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "residential-spanking", + "metadata": {}, + "source": [ + "### Exercise 2\n", + "\n", + " Use [WolframAlpha](https://www.wolframalpha.com/) to solve the quadratic growth model, using either or both forms of parameterization:\n", + "\n", + " df(t) / dt = alpha f(t) + beta f(t)^2\n", + "\n", + "or\n", + "\n", + " df(t) / dt = r f(t) (1 - f(t)/K)\n", + "\n", + "Find the general solution and also the particular solution where `f(0) = p_0`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "purple-traffic", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap10.ipynb b/chapters/chap10.ipynb new file mode 100644 index 000000000..eb5a456f2 --- /dev/null +++ b/chapters/chap10.ipynb @@ -0,0 +1,434 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "dense-storm", + "metadata": {}, + "source": [ + "# Case Studies Part 1" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "thirty-medication", + "metadata": {}, + "source": [ + "This chapter presents case studies where you can apply the tools we have learned so far to problems involving population growth, queueing systems, and tree growth. \n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "industrial-mercy", + "metadata": {}, + "source": [ + "## Historical World Population\n", + "\n", + "The Wikipedia page about world population growth includes estimates for world population from 12,000 years ago to the present (see )." + ] + }, + { + "cell_type": "markdown", + "id": "weekly-class", + "metadata": { + "tags": [] + }, + "source": [ + "The following cells download an archived version of this page and read the data into a Pandas `DataFrame`." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "usual-penguin", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/data/World_population_estimates.html')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "thick-lincoln", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from pandas import read_html\n", + "\n", + "filename = 'World_population_estimates.html'\n", + "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", + "len(tables)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "conscious-orange", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "table1 = tables[1]\n", + "table1.head()" + ] + }, + { + "cell_type": "markdown", + "id": "hungry-monster", + "metadata": { + "tags": [] + }, + "source": [ + "Some of the values are null because not all researchers provide estimates for the same dates.\n", + "\n", + "Again, we'll replace the long column names with more convenient abbreviations." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "thick-blanket", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "table1.columns = ['PRB', 'UN', 'Maddison', 'HYDE', 'Tanton', \n", + " 'Biraben', 'McEvedy & Jones', 'Thomlinson', 'Durand', 'Clark']" + ] + }, + { + "cell_type": "markdown", + "id": "substantial-implement", + "metadata": { + "tags": [] + }, + "source": [ + "Some of the estimates are in a form Pandas doesn't recognize as numbers, but we can coerce them to be numeric." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "ranking-prescription", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "for col in table1.columns:\n", + " table1[col] = pd.to_numeric(table1[col], errors='coerce')" + ] + }, + { + "cell_type": "markdown", + "id": "amazing-difference", + "metadata": { + "tags": [] + }, + "source": [ + "Here are the results. Notice that we are working in millions now, not billions." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "metallic-offense", + "metadata": { + "scrolled": false, + "tags": [] + }, + "outputs": [], + "source": [ + "table1.plot()\n", + "decorate(xlim=[-10000, 2000], xlabel='Year', \n", + " ylabel='World population (millions)',\n", + " title='Prehistoric population estimates')\n", + "plt.legend(fontsize='small');" + ] + }, + { + "cell_type": "markdown", + "id": "human-conservation", + "metadata": { + "tags": [] + }, + "source": [ + "We can use `xlim` to zoom in on everything after Year 0." + ] + }, + { + "cell_type": "markdown", + "id": "conventional-nudist", + "metadata": {}, + "source": [ + "The following figure shows the estimates of several research groups from 1 CE to the near present." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "solar-action", + "metadata": {}, + "outputs": [], + "source": [ + "table1.plot()\n", + "decorate(xlim=[0, 2000], xlabel='Year', \n", + " ylabel='World population (millions)',\n", + " title='CE population estimates')" + ] + }, + { + "cell_type": "markdown", + "id": "wanted-fantasy", + "metadata": {}, + "source": [ + "See if you can find a model that fits these estimates.\n", + "How well does your best model predict actual population growth from 1940 to the present?" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "rural-express", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", + "table2 = tables[2]\n", + "table2.columns = ['census', 'prb', 'un', 'maddison', \n", + " 'hyde', 'tanton', 'biraben', 'mj', \n", + " 'thomlinson', 'durand', 'clark']" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "preceding-sheep", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "un = table2.un / 1e9\n", + "census = table2.census / 1e9" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "together-jackson", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "simple-verse", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "ruled-spain", + "metadata": {}, + "source": [ + "## One Queue Or Two?\n", + "\n", + "This case study is related to *queueing theory*, which is the study of systems that involve waiting in lines, also known as \"queues\".\n", + "\n", + "Suppose you are designing the checkout area for a new store. There is\n", + "enough room in the store for two checkout counters and a waiting area\n", + "for customers. You can make two lines, one for each counter, or one line that feeds both counters.\n", + "\n", + "In theory, you might expect a single line to be better, but it has some practical drawbacks: in order to maintain a single line, you might have to install barriers, and customers might be put off by what seems to be a longer line, even if it moves faster.\n", + "\n", + "So you'd like to check whether the single line is really better and by\n", + "how much. Simulation can help answer this question.\n", + "\n", + "This figure shows the three scenarios we'll consider:\n", + "\n", + "![One queue, one server (left), one queue, two servers (middle), two\n", + "queues, two servers (right).](https://github.com/AllenDowney/ModSim/raw/main/figs/queue.png)\n", + "\n", + "*One queue, one server (left), one queue, two servers (middle), two\n", + "queues, two servers (right).*\n", + "\n", + "As we did in the bike share model, we'll divide time into discrete time steps of one minute.\n", + "And we'll assume that a customer is equally likely to arrive during any time step. \n", + "I'll denote this probability using the Greek letter lambda, $\\lambda$, or the variable name `lam`. The value of $\\lambda$ probably varies from day to day, so we'll have to consider a range of possibilities.\n", + "\n", + "Based on data from other stores, you know that it takes 5 minutes for a customer to check out, on average. But checkout times are variable: most customers take less than 5 minutes, but some take substantially more. A simple way to model this variability is to assume that when a customer is checking out, they always have the same probability of finishing during the next time step, regardless of how long they have been checking out. I'll denote this probability using the Greek letter mu, $\\mu$, or the variable name `mu`.\n", + "\n", + "If we choose $\\mu=1/5$ per minute, the average time for each checkout\n", + "will be 5 minutes, which is consistent with the data. Most people take less than 5 minutes, but a few take substantially longer, which is probably not a bad model of the distribution in real stores.\n", + "\n", + "Now we're ready to implement the model. In the repository for this book, you'll find a notebook called *queue.ipynb* that contains some code to get you started and instructions.\n", + "You can download it from or run it on Colab at .\n", + "\n", + "As always, you should practice incremental development: write no more\n", + "than one or two lines of code at a time, and test as you go!" + ] + }, + { + "cell_type": "markdown", + "id": "forward-point", + "metadata": {}, + "source": [ + "## Predicting Salmon Populations\n", + "\n", + "Each year the U.S. Atlantic Salmon Assessment Committee reports\n", + "estimates of salmon populations in oceans and rivers in the northeastern United States. The reports are useful for monitoring changes in these populations, but they generally do not include predictions.\n", + "\n", + "The goal of this case study is to model year-to-year changes in\n", + "population, evaluate how predictable these changes are, and estimate the probability that a particular population will increase or decrease in the next 10 years.\n", + "\n", + "As an example, I use data from the 2017 report, which provides population estimates for the Narraguagus and Sheepscot Rivers\n", + "in Maine.\n", + "\n", + "In the repository for this book, you'll find a notebook called\n", + "*salmon.ipynb* that contains this data and some code to get you started.\n", + "You can download it from or run it on Colab at .\n", + "\n", + "You should take my instructions as suggestions; if you want to try\n", + "something different, please do!\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "limiting-moore", + "metadata": {}, + "source": [ + "## Tree Growth\n", + "\n", + "This case study is based on \"Height-Age Curves for Planted Stands of\n", + "Douglas Fir, with Adjustments for Density\", a working paper by\n", + "Flewelling et al.\n", + "It provides *site index curves*, which are curves that show the\n", + "expected height of the tallest tree in a stand of Douglas fir as a\n", + "function of age, for a stand where the trees are the same age.\n", + "Depending on the quality of the site, the trees might grow more quickly or slowly. So each curve is identified by a *site index* that indicates the quality of the site.\n", + "\n", + "The goal of this case study is to explain the shape of these\n", + "curves, that is, why trees grow the way they do.\n", + "The answer I propose involves fractal dimensions, so you might find it interesting.\n", + "\n", + "In the repository for this book, you'll find a notebook called\n", + "*trees.ipynb* that incrementally develops a model of tree growth and uses it to fit the data.\n", + "You can download it from or run it on Colab at .\n", + "\n", + "There are no exercises in this case study, but it is an example of what you can do with the tools we have so far and a preview of what you will be able to do with the tools in the next few chapters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "australian-gregory", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap11.ipynb b/chapters/chap11.ipynb new file mode 100644 index 000000000..3752aeb7f --- /dev/null +++ b/chapters/chap11.ipynb @@ -0,0 +1,840 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "pressing-munich", + "metadata": {}, + "source": [ + "# Epidemiology" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "electoral-turkey", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "persistent-carbon", + "metadata": {}, + "source": [ + "In this chapter, we'll develop a model of an epidemic as it spreads in a\n", + "susceptible population, and use it to evaluate the effectiveness of\n", + "possible interventions.\n", + "\n", + "My presentation of the model in the next few chapters is based on an excellent article by David Smith and Lang Moore, \"The SIR Model for Spread of Disease,\" *Journal of Online Mathematics and its Applications*, December 2001, available at ." + ] + }, + { + "cell_type": "markdown", + "id": "working-patent", + "metadata": {}, + "source": [ + "## The Freshman Plague\n", + "\n", + "Every year at Olin College, about 90 new students come to campus from\n", + "around the country and the world. Most of them arrive healthy and happy, but usually at least one brings with them some kind of infectious disease. A few weeks later, predictably, some fraction of the incoming class comes down with what we call the \"Freshman Plague\".\n", + "\n", + "In this chapter we introduce a well-known model of infectious disease,\n", + "the Kermack-McKendrick model, and use it to explain the progression of\n", + "the disease over the course of the semester, predict the effect of\n", + "possible interventions (like immunization) and design the most effective intervention campaign.\n", + "\n", + "So far we have done our own modeling; that is, we've chosen physical\n", + "systems, identified factors that seem important, and made decisions\n", + "about how to represent them. In this chapter we start with an existing\n", + "model and reverse-engineer it. Along the way, we consider the modeling\n", + "decisions that went into it and identify its capabilities and\n", + "limitations." + ] + }, + { + "cell_type": "markdown", + "id": "distant-expense", + "metadata": {}, + "source": [ + "## The Kermack-McKendrick Model\n", + "\n", + "The Kermack-McKendrick (KM) model is an example of an *SIR model*,\n", + "so-named because it represents three categories of people:\n", + "\n", + "- *S*: People who are \"susceptible\", that is, capable of\n", + " contracting the disease if they come into contact with someone who\n", + " is infected.\n", + "\n", + "- *I*: People who are \"infectious\", that is, capable of passing\n", + " along the disease if they come into contact with someone\n", + " susceptible.\n", + "\n", + "- *R*: People who are \"recovered\". In the basic version of the\n", + " model, people who have recovered are considered to be no longer\n", + " infectious and immune to reinfection. That is a reasonable model for some diseases, but not for others, so it should be on the list of assumptions to reconsider later." + ] + }, + { + "cell_type": "markdown", + "id": "reasonable-kitchen", + "metadata": {}, + "source": [ + "Let's think about how the number of people in each category changes over time. Suppose we know that people with the disease are infectious for a period of 4 days, on average.\n", + "If 100 people are infectious at a particular point in time, and we ignore the particular time each one became infected, we expect about 1 out of 4 to recover on any particular day.\n", + "\n", + "Putting that a different way, if the time between recoveries is 4 days, the recovery rate is about 0.25 recoveries per day, which we'll denote with the Greek letter gamma, $\\gamma$, or the variable name `gamma`.\n", + "\n", + "If the total number of people in the population is $N$, and the fraction currently infectious is $i$, the total number of recoveries we expect per day is $\\gamma i N$." + ] + }, + { + "cell_type": "markdown", + "id": "important-yugoslavia", + "metadata": {}, + "source": [ + "Now let's think about the number of new infections. Suppose we know that each susceptible person comes into contact with 1 person every 3 days, on average, in a way that would cause them to become infected if the other person is infected. We'll denote this contact rate with the Greek letter beta, $\\beta$, or the variables name `beta`.\n", + "\n", + "It's probably not reasonable to assume that we know $\\beta$ ahead of\n", + "time, but later we'll see how to estimate it based on data from previous outbreaks.\n", + "\n", + "If $s$ is the fraction of the population that's susceptible, $s N$ is\n", + "the number of susceptible people, $\\beta s N$ is the number of contacts per day, and $\\beta s i N$ is the number of those contacts where the other person is infectious." + ] + }, + { + "cell_type": "markdown", + "id": "virgin-cambodia", + "metadata": {}, + "source": [ + "In summary:\n", + "\n", + "- The number of recoveries we expect per day is $\\gamma i N$; dividing by $N$ yields the fraction of the population that recovers in a day, which is $\\gamma i$.\n", + "\n", + "- The number of new infections we expect per day is $\\beta s i N$; dividing by $N$ yields the fraction of the population that gets infected in a day, which is $\\beta s i$.\n", + "\n", + "The KM model assumes that the population is closed; that is, no one\n", + "arrives or departs, so the size of the population, $N$, is constant." + ] + }, + { + "cell_type": "markdown", + "id": "accomplished-franklin", + "metadata": {}, + "source": [ + "## The KM Equations\n", + "\n", + "If we treat time as a continuous quantity, we can write differential\n", + "equations that describe the rates of change for $s$, $i$, and $r$ (where $r$ is the fraction of the population that has recovered):\n", + "\n", + "$$\\begin{aligned}\n", + "\\frac{ds}{dt} &= -\\beta s i \\\\\n", + "\\frac{di}{dt} &= \\beta s i - \\gamma i\\\\\n", + "\\frac{dr}{dt} &= \\gamma i\\end{aligned}$$ \n", + "\n", + "To avoid cluttering the equations, I leave it implied that $s$ is a function of time, $s(t)$, and likewise for $i$ and $r$.\n", + "\n", + "SIR models are examples of *compartment models*, so-called because\n", + "they divide the world into discrete categories, or compartments, and\n", + "describe transitions from one compartment to another. Compartments are\n", + "also called *stocks* and transitions between them are called\n", + "*flows*.\n", + "\n", + "In this example, there are three stocks---susceptible, infectious, and\n", + "recovered---and two flows---new infections and recoveries. Compartment\n", + "models are often represented visually using stock and flow diagrams (see )." + ] + }, + { + "cell_type": "markdown", + "id": "fourth-celtic", + "metadata": {}, + "source": [ + "The following figure shows the stock and flow diagram for the KM model.\n", + "\n", + "![Stock and flow diagram for an SIR\n", + "model.](https://github.com/AllenDowney/ModSim/raw/main/figs/stock_flow1.png)\n", + "\n", + "Stocks are represented by rectangles, flows by arrows. The widget in the middle of the arrows represents a valve that controls the rate of flow; the diagram shows the parameters that control the valves." + ] + }, + { + "cell_type": "markdown", + "id": "martial-details", + "metadata": {}, + "source": [ + "## Implementing the KM model\n", + "\n", + "For a given physical system, there are many possible models, and for a\n", + "given model, there are many ways to represent it. For example, we can\n", + "represent an SIR model as a stock-and-flow diagram, as a set of\n", + "differential equations, or as a Python program. The process of\n", + "representing a model in these forms is called *implementation*. In\n", + "this section, we implement the KM model in Python.\n", + "\n", + "I'll represent the initial state of the system using a `State` object\n", + "with state variables `s`, `i`, and `r`; they represent the fraction of\n", + "the population in each compartment.\n", + "\n", + "We can initialize the `State` object with the *number* of people in each compartment; for example, here is the initial state with one infected student in a class of 90:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "criminal-change", + "metadata": {}, + "outputs": [], + "source": [ + "init = State(s=89, i=1, r=0)\n", + "show(init)" + ] + }, + { + "cell_type": "markdown", + "id": "chronic-jonathan", + "metadata": {}, + "source": [ + "We can convert the numbers to fractions by dividing by the total:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "pediatric-ratio", + "metadata": {}, + "outputs": [], + "source": [ + "init /= init.sum()\n", + "show(init)" + ] + }, + { + "cell_type": "markdown", + "id": "ordinary-scottish", + "metadata": {}, + "source": [ + "For now, let's assume we know the time between contacts and time between\n", + "recoveries:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "little-stylus", + "metadata": {}, + "outputs": [], + "source": [ + "tc = 3 # time between contacts in days \n", + "tr = 4 # recovery time in days" + ] + }, + { + "cell_type": "markdown", + "id": "covered-avenue", + "metadata": {}, + "source": [ + "We can use them to compute the parameters of the model:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "veterinary-aerospace", + "metadata": {}, + "outputs": [], + "source": [ + "beta = 1 / tc # contact rate in per day\n", + "gamma = 1 / tr # recovery rate in per day" + ] + }, + { + "cell_type": "markdown", + "id": "lightweight-delta", + "metadata": {}, + "source": [ + "I'll use a `System` object to store the parameters and initial\n", + "conditions. The following function takes the system parameters and returns a new `System` object:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "nasty-sherman", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def make_system(beta, gamma):\n", + " init = State(s=89, i=1, r=0)\n", + " init /= init.sum()\n", + "\n", + " return System(init=init, t_end=7*14,\n", + " beta=beta, gamma=gamma)" + ] + }, + { + "cell_type": "markdown", + "id": "victorian-blogger", + "metadata": {}, + "source": [ + "The default value for `t_end` is 14 weeks, about the length of a\n", + "semester.\n", + "\n", + "Here's what the `System` object looks like. " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "supported-shadow", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "system = make_system(beta, gamma)\n", + "show(system)" + ] + }, + { + "cell_type": "markdown", + "id": "requested-turning", + "metadata": {}, + "source": [ + "Now that we have object to represent the system and its state, we are ready for the update function." + ] + }, + { + "cell_type": "markdown", + "id": "alone-desire", + "metadata": {}, + "source": [ + "## The Update Function\n", + "\n", + "The purpose of an update function is to take the current state of a system and compute the state during the next time step.\n", + "Here's the update function we'll use for the KM model:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "wound-wayne", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def update_func(t, state, system):\n", + " s, i, r = state.s, state.i, state.r\n", + "\n", + " infected = system.beta * i * s \n", + " recovered = system.gamma * i\n", + " \n", + " s -= infected\n", + " i += infected - recovered\n", + " r += recovered\n", + " \n", + " return State(s=s, i=i, r=r)" + ] + }, + { + "cell_type": "markdown", + "id": "aboriginal-malpractice", + "metadata": {}, + "source": [ + "`update_func` takes as parameters the current time, a `State` object, and a `System` object.\n", + "\n", + "The first line unpacks the `State` object, assigning the values of the state variables to new variables with the same names.\n", + "This is an example of *multiple assignment*.\n", + "The left side is a sequence of variables; the right side is a sequence of expressions.\n", + "The values on the right side are assigned to the variables on the left side, in order.\n", + "By creating these variables, we avoid repeating `state` several times, which makes the code easier to read.\n", + "\n", + "The update function computes `infected` and `recovered` as a fraction of the population, then updates `s`, `i` and `r`. The return value is a `State` that contains the updated values.\n", + "\n", + "We can call `update_func` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "assigned-feelings", + "metadata": {}, + "outputs": [], + "source": [ + "state = update_func(0, init, system)\n", + "show(state)" + ] + }, + { + "cell_type": "markdown", + "id": "standard-search", + "metadata": {}, + "source": [ + "The result is the new `State` object.\n", + "\n", + "You might notice that this version of `update_func` does not use one of its parameters, `t`. I include it anyway because update functions\n", + "sometimes depend on time, and it is convenient if they all take the same parameters, whether they need them or not." + ] + }, + { + "cell_type": "markdown", + "id": "informational-cisco", + "metadata": {}, + "source": [ + "## Running the Simulation\n", + "\n", + "Now we can simulate the model over a sequence of time steps:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "occasional-pottery", + "metadata": {}, + "outputs": [], + "source": [ + "def run_simulation1(system, update_func):\n", + " state = system.init\n", + "\n", + " for t in range(0, system.t_end):\n", + " state = update_func(t, state, system)\n", + "\n", + " return state" + ] + }, + { + "cell_type": "markdown", + "id": "fifteen-metallic", + "metadata": {}, + "source": [ + "The parameters of `run_simulation` are the `System` object and the\n", + "update function. The `System` object contains the parameters, initial\n", + "conditions, and values of `0` and `t_end`.\n", + "\n", + "We can call `run_simulation` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "differential-difference", + "metadata": {}, + "outputs": [], + "source": [ + "final_state = run_simulation1(system, update_func)\n", + "show(final_state)" + ] + }, + { + "cell_type": "markdown", + "id": "behind-removal", + "metadata": {}, + "source": [ + "The result indicates that after 14 weeks (98 days), about 52% of the\n", + "population is still susceptible, which means they were never infected,\n", + "almost 48% have recovered, which means they were infected at some point, and less than 1% are actively infected." + ] + }, + { + "cell_type": "markdown", + "id": "serial-genius", + "metadata": {}, + "source": [ + "## Collecting the Results\n", + "\n", + "The previous version of `run_simulation` returns only the final state,\n", + "but we might want to see how the state changes over time. We'll consider two ways to do that: first, using three `TimeSeries` objects, then using a new object called a `TimeFrame`.\n", + "\n", + "Here's the first version:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "advanced-recommendation", + "metadata": {}, + "outputs": [], + "source": [ + "def run_simulation2(system, update_func):\n", + " S = TimeSeries()\n", + " I = TimeSeries()\n", + " R = TimeSeries()\n", + "\n", + " state = system.init\n", + " S[0], I[0], R[0] = state\n", + " \n", + " for t in range(0, system.t_end):\n", + " state = update_func(t, state, system)\n", + " S[t+1], I[t+1], R[t+1] = state.s, state.i, state.r\n", + " \n", + " return S, I, R" + ] + }, + { + "cell_type": "markdown", + "id": "behind-breathing", + "metadata": {}, + "source": [ + "First, we create `TimeSeries` objects to store the results.\n", + "Next we initialize `state` and the first elements of `S`, `I` and\n", + "`R`.\n", + "\n", + "Inside the loop, we use `update_func` to compute the state of the system at the next time step, then use multiple assignment to unpack the elements of `state`, assigning each to the corresponding `TimeSeries`.\n", + "\n", + "At the end of the function, we return the values `S`, `I`, and `R`. This is the first example we have seen where a function returns more than one value.\n", + "\n", + "We can run the function like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "subtle-zambia", + "metadata": {}, + "outputs": [], + "source": [ + "S, I, R = run_simulation2(system, update_func)" + ] + }, + { + "cell_type": "markdown", + "id": "smoking-toddler", + "metadata": {}, + "source": [ + "We'll use the following function to plot the results:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "welcome-tractor", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def plot_results(S, I, R):\n", + " S.plot(style='--', label='Susceptible')\n", + " I.plot(style='-', label='Infected')\n", + " R.plot(style=':', label='Recovered')\n", + " decorate(xlabel='Time (days)',\n", + " ylabel='Fraction of population')" + ] + }, + { + "cell_type": "markdown", + "id": "important-command", + "metadata": {}, + "source": [ + "And run it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "agricultural-joining", + "metadata": {}, + "outputs": [], + "source": [ + "plot_results(S, I, R)" + ] + }, + { + "cell_type": "markdown", + "id": "civic-pharmacy", + "metadata": {}, + "source": [ + "It takes about three weeks (21 days) for the outbreak to get going, and about five weeks (35 days) to peak. The fraction of the population that's infected is never very high, but it adds up. In total, almost half the population gets sick." + ] + }, + { + "cell_type": "markdown", + "id": "unexpected-empire", + "metadata": {}, + "source": [ + "## Now With a TimeFrame\n", + "\n", + "If the number of state variables is small, storing them as separate\n", + "`TimeSeries` objects might not be so bad. But a better alternative is to use a `TimeFrame`, which is another object defined in the ModSim\n", + "library.\n", + "A `TimeFrame` is a kind of a `DataFrame`, which we used earlier to store world population estimates.\n", + "\n", + "Here's a more concise version of `run_simulation` using a `TimeFrame`:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "native-central", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def run_simulation(system, update_func):\n", + " frame = TimeFrame(columns=system.init.index)\n", + " frame.loc[0] = system.init\n", + " \n", + " for t in range(0, system.t_end):\n", + " frame.loc[t+1] = update_func(t, frame.loc[t], system)\n", + " \n", + " return frame" + ] + }, + { + "cell_type": "markdown", + "id": "beginning-secret", + "metadata": {}, + "source": [ + "The first line creates an empty `TimeFrame` with one column for each\n", + "state variable. Then, before the loop starts, we store the initial\n", + "conditions in the `TimeFrame` at `0`. Based on the way we've been using\n", + "`TimeSeries` objects, it is tempting to write:\n", + "\n", + "```\n", + "frame[0] = system.init\n", + "```\n", + "\n", + "But when you use the bracket operator with a `TimeFrame` or `DataFrame`, it selects a column, not a row. \n", + "To select a row, we have to use `loc`, like this:\n", + "\n", + "```\n", + "frame.loc[0] = system.init\n", + "```\n", + "\n", + "Since the value on the right side is a `State`, the assignment matches\n", + "up the index of the `State` with the columns of the `TimeFrame`; that\n", + "is, it assigns the `s` value from `system.init` to the `s` column of\n", + "`frame`, and likewise with `i` and `r`.\n", + "\n", + "Each time through the loop, we assign the `State` we get from `update_func` to the next row of `frame`.\n", + "At the end, we return `frame`. \n", + "\n", + "We can call this version of `run_simulation` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "quick-fifty", + "metadata": {}, + "outputs": [], + "source": [ + "results = run_simulation(system, update_func)" + ] + }, + { + "cell_type": "markdown", + "id": "small-liability", + "metadata": {}, + "source": [ + "Here are the first few rows of the results." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "appropriate-keyboard", + "metadata": {}, + "outputs": [], + "source": [ + "results.head()" + ] + }, + { + "cell_type": "markdown", + "id": "global-complement", + "metadata": {}, + "source": [ + "The columns in the `TimeFrame` correspond to the state variables, `s`, `i`, and `r`.\n", + "As with a `DataFrame`, we can use the dot operator to select columns\n", + "from a `TimeFrame`, so we can plot the results like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "listed-enough", + "metadata": {}, + "outputs": [], + "source": [ + "plot_results(results.s, results.i, results.r)" + ] + }, + { + "cell_type": "markdown", + "id": "statutory-begin", + "metadata": {}, + "source": [ + "The results are the same as before, now in a more convenient form." + ] + }, + { + "cell_type": "markdown", + "id": "neutral-replacement", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This chapter presents an SIR model of infectious disease and two ways to collect the results, using several `TimeSeries` objects or a single `TimeFrame`.\n", + "In the next chapter we'll use the model to explore the effect of immunization.\n", + "\n", + "But first you might want to work on these exercises." + ] + }, + { + "cell_type": "markdown", + "id": "induced-tunnel", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "You can access the notebooks at ." + ] + }, + { + "cell_type": "markdown", + "id": "floral-remove", + "metadata": {}, + "source": [ + "### Exercise 1 \n", + "\n", + "Suppose the time between contacts is 4 days and the recovery time is 5 days. After 14 weeks, how many students, total, have been infected?\n", + "\n", + "Hint: what is the change in `S` between the beginning and the end of the simulation?" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "parental-thunder", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "recovered-freeze", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "suited-spanking", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "derived-mauritius", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap11.py b/chapters/chap11.py new file mode 100644 index 000000000..859f4dc41 --- /dev/null +++ b/chapters/chap11.py @@ -0,0 +1,43 @@ +from modsim import * + +def make_system(beta, gamma): + init = State(s=89, i=1, r=0) + init /= init.sum() + + return System(init=init, t_end=7*14, + beta=beta, gamma=gamma) + +from modsim import * + +def update_func(t, state, system): + s, i, r = state.s, state.i, state.r + + infected = system.beta * i * s + recovered = system.gamma * i + + s -= infected + i += infected - recovered + r += recovered + + return State(s=s, i=i, r=r) + +from modsim import * + +def plot_results(S, I, R): + S.plot(style='--', label='Susceptible') + I.plot(style='-', label='Infected') + R.plot(style=':', label='Recovered') + decorate(xlabel='Time (days)', + ylabel='Fraction of population') + +from modsim import * + +def run_simulation(system, update_func): + frame = TimeFrame(columns=system.init.index) + frame.loc[0] = system.init + + for t in range(0, system.t_end): + frame.loc[t+1] = update_func(t, frame.loc[t], system) + + return frame + diff --git a/chapters/chap12.ipynb b/chapters/chap12.ipynb new file mode 100644 index 000000000..2746d1395 --- /dev/null +++ b/chapters/chap12.ipynb @@ -0,0 +1,523 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "combined-semiconductor", + "metadata": {}, + "source": [ + "# Modeling Vaccination" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "breathing-hamilton", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://github.com/AllenDowney/ModSimPy/raw/master/chap11.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "growing-sperm", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import code from previous notebooks\n", + "\n", + "from chap11 import make_system\n", + "from chap11 import update_func\n", + "from chap11 import run_simulation" + ] + }, + { + "cell_type": "markdown", + "id": "identical-steam", + "metadata": {}, + "source": [ + "In the previous chapter I presented the Kermack-McKendrick (KM) model of infectious disease and used it to model the Freshman Plague at Olin. In this chapter we'll consider metrics intended to quantify the effects of the disease and interventions intended to reduce those effects.\n", + "\n", + "We'll use some of the functions from the previous chapter: `make_system`, `update_func`, and the last version of `run_simulation`, which returns the results in a `DataFrame` object." + ] + }, + { + "cell_type": "markdown", + "id": "complex-renewal", + "metadata": {}, + "source": [ + "## Immunization\n", + "\n", + "Models like this are useful for testing \"what if?\" scenarios. As an\n", + "example, we'll consider the effect of immunization.\n", + "\n", + "Suppose there is a vaccine that causes a student to become immune to the Freshman Plague without being infected. How might you modify the model to capture this effect?\n", + "\n", + "One option is to treat immunization as a shortcut from susceptible to\n", + "recovered without going through infectious. We can implement this\n", + "feature like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "recent-cooper", + "metadata": {}, + "outputs": [], + "source": [ + "def add_immunization(system, fraction):\n", + " system.init.s -= fraction\n", + " system.init.r += fraction" + ] + }, + { + "cell_type": "markdown", + "id": "arranged-screening", + "metadata": {}, + "source": [ + "`add_immunization` moves the given fraction of the population from `S`\n", + "to `R`.\n", + "\n", + "As a basis for comparison, I'll run the model with the same parameters as in the previous chapter, with no immunization." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "found-learning", + "metadata": {}, + "outputs": [], + "source": [ + "tc = 3 # time between contacts in days \n", + "tr = 4 # recovery time in days\n", + "\n", + "beta = 1 / tc # contact rate in per day\n", + "gamma = 1 / tr # recovery rate in per day\n", + "\n", + "system = make_system(beta, gamma)\n", + "results = run_simulation(system, update_func)" + ] + }, + { + "cell_type": "markdown", + "id": "unsigned-joseph", + "metadata": {}, + "source": [ + "Now let's see what happens if 10% of students are immune.\n", + "I'll make another `System` object with the same parameters, then run `add_immunization` to modify the initial conditions." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "enormous-abortion", + "metadata": {}, + "outputs": [], + "source": [ + "system2 = make_system(beta, gamma)\n", + "add_immunization(system2, 0.1)" + ] + }, + { + "cell_type": "markdown", + "id": "subject-ideal", + "metadata": {}, + "source": [ + "Now we can run the simulation like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "funny-copper", + "metadata": {}, + "outputs": [], + "source": [ + "results2 = run_simulation(system2, update_func)" + ] + }, + { + "cell_type": "markdown", + "id": "bronze-techno", + "metadata": {}, + "source": [ + "The following figure shows `s` as a function of time, with and\n", + "without immunization." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "divided-biotechnology", + "metadata": {}, + "outputs": [], + "source": [ + "results.s.plot(style='--', label='No immunization')\n", + "results2.s.plot(label='10% immunization')\n", + "\n", + "decorate(xlabel='Time (days)',\n", + " ylabel='Fraction of population')" + ] + }, + { + "cell_type": "markdown", + "id": "passive-dance", + "metadata": {}, + "source": [ + "With immunization, there is a smaller change in `s`; that is, fewer people are infected.\n", + "In the next section we'll compute this change and use it to quantify the effect of immunization." + ] + }, + { + "cell_type": "markdown", + "id": "postal-cemetery", + "metadata": {}, + "source": [ + "## Metrics\n", + "\n", + "When we plot a time series, we get a view of everything that happened\n", + "when the model ran, but often we want to boil it down to a few numbers\n", + "that summarize the outcome. These summary statistics are called\n", + "*metrics*.\n", + "\n", + "In the KM model, we might want to know the time until the peak of the\n", + "outbreak, the number of people who are sick at the peak, the number of\n", + "students who will still be sick at the end of the semester, or the total number of students who get sick at any point.\n", + "\n", + "As an example, I will focus on the last one --- the total number of sick students --- and we will consider interventions intended to minimize it.\n", + "\n", + "We can get the total number of infections by computing the difference in `s` at the beginning and the end of the simulation." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "synthetic-element", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def calc_total_infected(results, system):\n", + " s_0 = results.s[0]\n", + " s_end = results.s[system.t_end]\n", + " return s_0 - s_end" + ] + }, + { + "cell_type": "markdown", + "id": "parallel-pipeline", + "metadata": {}, + "source": [ + "And here are the results from the two simulations." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "recovered-picnic", + "metadata": {}, + "outputs": [], + "source": [ + "calc_total_infected(results, system)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "american-transfer", + "metadata": {}, + "outputs": [], + "source": [ + "calc_total_infected(results2, system2)" + ] + }, + { + "cell_type": "markdown", + "id": "adverse-trance", + "metadata": {}, + "source": [ + "Without immunization, almost 47% of the population gets infected at some point. With 10% immunization, only 31% get infected. That's pretty good." + ] + }, + { + "cell_type": "markdown", + "id": "eight-maximum", + "metadata": {}, + "source": [ + "## Sweeping Immunization\n", + "\n", + "Now let's see what happens if we administer more vaccines. This\n", + "following function sweeps a range of immunization rates:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "progressive-architect", + "metadata": {}, + "outputs": [], + "source": [ + "def sweep_immunity(fraction_array):\n", + " sweep = SweepSeries()\n", + "\n", + " for fraction in fraction_array:\n", + " system = make_system(beta, gamma)\n", + " add_immunization(system, fraction)\n", + " results = run_simulation(system, update_func)\n", + " sweep[fraction] = calc_total_infected(results, system)\n", + "\n", + " return sweep" + ] + }, + { + "cell_type": "markdown", + "id": "timely-industry", + "metadata": {}, + "source": [ + "The parameter of `sweep_immunity` is an array of immunization rates. The result is a `SweepSeries` object that maps from each immunization rate to the resulting fraction of students ever infected.\n", + "\n", + "We can call it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "measured-pavilion", + "metadata": {}, + "outputs": [], + "source": [ + "fraction_array = linspace(0, 1, 21)\n", + "infected_sweep = sweep_immunity(fraction_array)" + ] + }, + { + "cell_type": "markdown", + "id": "indie-seeker", + "metadata": {}, + "source": [ + "The following figure plots the `SweepSeries`. Notice that the $x$-axis is the immunization rate, not time." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "interior-humanitarian", + "metadata": {}, + "outputs": [], + "source": [ + "infected_sweep.plot(color='C2')\n", + "\n", + "decorate(xlabel='Fraction immunized',\n", + " ylabel='Total fraction infected',\n", + " title='Fraction infected vs. immunization rate')" + ] + }, + { + "cell_type": "markdown", + "id": "turkish-mumbai", + "metadata": {}, + "source": [ + "As the immunization rate increases, the number of infections drops\n", + "steeply. If 40% of the students are immunized, fewer than 4% get sick.\n", + "That's because immunization has two effects: it protects the people who get immunized (of course) but it also protects the rest of the\n", + "population.\n", + "\n", + "Reducing the number of \"susceptibles\" and increasing the number of\n", + "\"resistants\" makes it harder for the disease to spread, because some\n", + "fraction of contacts are wasted on people who cannot be infected. This\n", + "phenomenon is called *herd immunity*, and it is an important element\n", + "of public health (see )." + ] + }, + { + "cell_type": "markdown", + "id": "french-spouse", + "metadata": {}, + "source": [ + "The steepness of the curve is a blessing and a curse. It's a blessing\n", + "because it means we don't have to immunize everyone, and vaccines can\n", + "protect the \"herd\" even if they are not 100% effective.\n", + "\n", + "But it's a curse because a small decrease in immunization can cause a\n", + "big increase in infections. In this example, if we drop from 80%\n", + "immunization to 60%, that might not be too bad. But if we drop from 40% to 20%, that would trigger a major outbreak, affecting more than 15% of the population. For a serious disease like measles, just to name one, that would be a public health catastrophe." + ] + }, + { + "cell_type": "markdown", + "id": "amino-excess", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "In general, models are used to predict, explain, and design.\n", + "In this chapter, we use an SIR model to predict the effect of immunization and to explain the phenomenon of herd immunity.\n", + "\n", + "In the repository for this book, you will find a file called *plague.ipynb* that uses this model for design, that is, for making public health decisions intended to achieve a goal.\n", + "\n", + "In the next chapter, we'll explore the SIR model further by sweeping the parameters.\n", + "\n", + "But first you might want to work on this exercise." + ] + }, + { + "cell_type": "markdown", + "id": "institutional-memory", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "You can access the notebooks at ." + ] + }, + { + "cell_type": "markdown", + "id": "drawn-hindu", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + " Suppose we have the option to quarantine infected students. For example, a student who feels ill might be moved to an infirmary or a private dorm room until they are no longer infectious.\n", + "\n", + "How might you incorporate the effect of quarantine in the SIR model?" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "impressive-librarian", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "assumed-license", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "intended-premium", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "limiting-interest", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "undefined-treasury", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap12.py b/chapters/chap12.py new file mode 100644 index 000000000..7cfc3909e --- /dev/null +++ b/chapters/chap12.py @@ -0,0 +1,7 @@ +from modsim import * + +def calc_total_infected(results, system): + s_0 = results.s[0] + s_end = results.s[system.t_end] + return s_0 - s_end + diff --git a/chapters/chap13.ipynb b/chapters/chap13.ipynb new file mode 100644 index 000000000..c6137eb93 --- /dev/null +++ b/chapters/chap13.ipynb @@ -0,0 +1,608 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "colonial-jacob", + "metadata": {}, + "source": [ + "# Sweeping Parameters" + ] + }, + { + "cell_type": "markdown", + "id": "modified-discretion", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "complicated-retreat", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "selected-there", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "prepared-apparatus", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "awful-martial", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://github.com/AllenDowney/ModSimPy/raw/master/chap11.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "changing-burner", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://github.com/AllenDowney/ModSimPy/raw/master/chap12.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "defensive-automation", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import code from previous notebooks\n", + "\n", + "from chap11 import make_system\n", + "from chap11 import update_func\n", + "from chap11 import run_simulation\n", + "from chap11 import plot_results\n", + "\n", + "from chap12 import calc_total_infected" + ] + }, + { + "cell_type": "markdown", + "id": "structural-application", + "metadata": {}, + "source": [ + "In the previous chapter we extended the Kermack-McKendrick (KM) model to include immunization and used it to demonstrate herd immunity.\n", + "\n", + "But we assumed that the parameters of the model, contact rate and\n", + "recovery rate, were known. In this chapter, we'll explore the behavior of\n", + "the model as we vary these parameters.\n", + "\n", + "In the next chapter, we'll use analysis to understand these relationships better, and propose a method for using data to estimate parameters." + ] + }, + { + "cell_type": "markdown", + "id": "tracked-outreach", + "metadata": {}, + "source": [ + "## Sweeping Beta\n", + "\n", + "Recall that $\\beta$ is the contact rate, which captures both the\n", + "frequency of interaction between people and the fraction of those\n", + "interactions that result in a new infection. If $N$ is the size of the\n", + "population and $s$ is the fraction that's susceptible, $s N$ is the\n", + "number of susceptibles, $\\beta s N$ is the number of contacts per day\n", + "between susceptibles and other people, and $\\beta s i N$ is the number\n", + "of those contacts where the other person is infectious.\n", + "\n", + "As $\\beta$ increases, we expect the total number of infections to\n", + "increase. To quantify that relationship, I'll create a range of values\n", + "for $\\beta$:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "forward-responsibility", + "metadata": {}, + "outputs": [], + "source": [ + "beta_array = linspace(0.1, 1.1, 11)\n", + "gamma = 0.25" + ] + }, + { + "cell_type": "markdown", + "id": "eligible-shareware", + "metadata": {}, + "source": [ + "We'll start with a single value for `gamma`, which is the recovery rate, that is, the fraction of infected people who recover per day.\n", + "\n", + "The following function takes `beta_array` and `gamma` as parameters.\n", + "It runs the simulation for each value of `beta` and computes the same metric we used in the previous chapter, the fraction of the population that gets infected.\n", + "\n", + "The result is a `SweepSeries` that contains the values of `beta` and the corresponding metrics." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "dutch-chemistry", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def sweep_beta(beta_array, gamma):\n", + " sweep = SweepSeries()\n", + " for beta in beta_array:\n", + " system = make_system(beta, gamma)\n", + " results = run_simulation(system, update_func)\n", + " sweep[beta] = calc_total_infected(results, system)\n", + " return sweep" + ] + }, + { + "cell_type": "markdown", + "id": "mathematical-process", + "metadata": {}, + "source": [ + "We can run `sweep_beta` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "christian-singer", + "metadata": {}, + "outputs": [], + "source": [ + "infected_sweep = sweep_beta(beta_array, gamma)" + ] + }, + { + "cell_type": "markdown", + "id": "collectible-positive", + "metadata": {}, + "source": [ + "Before we plot the results, I will use a formatted string literal, also called an *f-string* to assemble a label that includes the value of `gamma`:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "proprietary-credit", + "metadata": {}, + "outputs": [], + "source": [ + "label = f'gamma = {gamma}'\n", + "label" + ] + }, + { + "cell_type": "markdown", + "id": "invisible-bible", + "metadata": {}, + "source": [ + "An f-string starts with the letter `f` followed by a string in single or double quotes. \n", + "The string can contain any number of format specifiers in squiggly brackets, `{}`.\n", + "When a variable name appears in a format specifier, it is replaced with the value of the variable.\n", + "\n", + "In this example, the value of `gamma` is `0.25`, so the value of `label` is `'gamma = 0.25'`.\n", + "\n", + "You can read more about f-strings at .\n", + "\n", + "Now let's see the results." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "developmental-video", + "metadata": {}, + "outputs": [], + "source": [ + "infected_sweep.plot(label=label, color='C1')\n", + "\n", + "decorate(xlabel='Contact rate (beta)',\n", + " ylabel='Fraction infected')" + ] + }, + { + "cell_type": "markdown", + "id": "durable-isaac", + "metadata": {}, + "source": [ + "Remember that this figure\n", + "is a parameter sweep, not a time series, so the x-axis is the parameter\n", + "`beta`, not time.\n", + "\n", + "When `beta` is small, the contact rate is low and the outbreak never\n", + "really takes off; the total number of infected students is near zero. As\n", + "`beta` increases, it reaches a threshold near 0.3 where the fraction of\n", + "infected students increases quickly. When `beta` exceeds 0.5, more than\n", + "80% of the population gets sick." + ] + }, + { + "cell_type": "markdown", + "id": "described-jacksonville", + "metadata": {}, + "source": [ + "## Sweeping Gamma\n", + "\n", + "Let's see what that looks like for a few different values of `gamma`.\n", + "We'll use `linspace` to make an array of values:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "chief-greece", + "metadata": {}, + "outputs": [], + "source": [ + "gamma_array = linspace(0.1, 0.7, 4)\n", + "gamma_array" + ] + }, + { + "cell_type": "markdown", + "id": "medium-greek", + "metadata": {}, + "source": [ + "And run `sweep_beta` for each value of `gamma`:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "thermal-pulse", + "metadata": {}, + "outputs": [], + "source": [ + "for gamma in gamma_array:\n", + " infected_sweep = sweep_beta(beta_array, gamma)\n", + " label = f'gamma = {gamma}'\n", + " infected_sweep.plot(label=label)\n", + " \n", + "decorate(xlabel='Contact rate (beta)',\n", + " ylabel='Fraction infected')" + ] + }, + { + "cell_type": "markdown", + "id": "artistic-charge", + "metadata": {}, + "source": [ + "When `gamma` is low, the recovery rate is low, which means people are infectious longer.\n", + "In that case, even a low contact rate (`beta`) results in an epidemic.\n", + "\n", + "When `gamma` is high, `beta` has to be even higher to get things going." + ] + }, + { + "cell_type": "markdown", + "id": "metropolitan-collect", + "metadata": {}, + "source": [ + "## Using a SweepFrame\n", + "\n", + "In the previous section, we swept a range of values for `gamma`, and for\n", + "each value of `gamma`, we swept a range of values for `beta`. This process is a\n", + "*two-dimensional sweep*.\n", + "\n", + "If we want to store the results, rather than plot them, we can use a\n", + "`SweepFrame`, which is a kind of `DataFrame` where the rows sweep one\n", + "parameter, the columns sweep another parameter, and the values contain\n", + "metrics from a simulation.\n", + "\n", + "This function shows how it works:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "scientific-dealer", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def sweep_parameters(beta_array, gamma_array):\n", + " frame = SweepFrame(columns=gamma_array)\n", + " for gamma in gamma_array:\n", + " frame[gamma] = sweep_beta(beta_array, gamma)\n", + " return frame" + ] + }, + { + "cell_type": "markdown", + "id": "educated-congress", + "metadata": {}, + "source": [ + "`sweep_parameters` takes as parameters an array of values for `beta` and\n", + "an array of values for `gamma`.\n", + "\n", + "It creates a `SweepFrame` to store the results, with one column for each\n", + "value of `gamma` and one row for each value of `beta`.\n", + "\n", + "Each time through the loop, we run `sweep_beta`. The result is a\n", + "`SweepSeries` object with one element for each value of `beta`. The\n", + "assignment inside the loop stores the `SweepSeries` as a new column in\n", + "the `SweepFrame`, corresponding to the current value of `gamma`.\n", + "\n", + "At the end, the `SweepFrame` stores the fraction of students infected\n", + "for each pair of parameters, `beta` and `gamma`.\n", + "\n", + "We can run `sweep_parameters` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "engaging-philosophy", + "metadata": {}, + "outputs": [], + "source": [ + "frame = sweep_parameters(beta_array, gamma_array)" + ] + }, + { + "cell_type": "markdown", + "id": "realistic-hughes", + "metadata": {}, + "source": [ + "With the results in a `SweepFrame`, we can plot each column like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "false-forestry", + "metadata": {}, + "outputs": [], + "source": [ + "for gamma in gamma_array:\n", + " label = f'gamma = {gamma}'\n", + " frame[gamma].plot(label=label)\n", + "\n", + "decorate(xlabel='Contact rate (beta)',\n", + " ylabel='Fraction infected',\n", + " title='Sweep beta, multiple values of gamma')" + ] + }, + { + "cell_type": "markdown", + "id": "armed-spending", + "metadata": {}, + "source": [ + "Alternatively, we can plot each row like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "silent-advantage", + "metadata": {}, + "outputs": [], + "source": [ + "for beta in [0.2, 0.5, 0.8, 1.1]:\n", + " label = f'beta = {beta}'\n", + " frame.loc[beta].plot(label=label)\n", + " \n", + "decorate(xlabel='Recovery rate (gamma)',\n", + " ylabel='Fraction infected',\n", + " title='Sweep gamma, multiple values of beta')" + ] + }, + { + "cell_type": "markdown", + "id": "diverse-works", + "metadata": {}, + "source": [ + "This example demonstrates one use of a `SweepFrame`: we can run the analysis once, save the results, and then generate different visualizations.\n", + "\n", + "Another way to visualize the results of a two-dimensional sweep is a\n", + "*contour plot*, which shows the parameters on the axes and contour\n", + "lines where the value of the metric is constant.\n", + "\n", + "The ModSim library provides `contour`, which takes a `SweepFrame` and uses Matplotlib to generate a contour plot." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "veterinary-cheese", + "metadata": {}, + "outputs": [], + "source": [ + "contour(frame)\n", + "\n", + "decorate(xlabel='Recovery rate (gamma)',\n", + " ylabel='Contact rate (beta)',\n", + " title='Contour plot, fraction infected')" + ] + }, + { + "cell_type": "markdown", + "id": "operational-daughter", + "metadata": {}, + "source": [ + "The values of `gamma` are on the $x$-axis, corresponding to the columns of the `SweepFrame`.\n", + "The values of `beta` are on the $y$-axis, corresponding to the rows of the `SweepFrame`.\n", + "Each line follows a contour where the infection rate is constant.\n", + "\n", + "Infection rates are lowest in the lower right, where the contact rate is low and the recovery rate is high. They increase as we move to the upper left, where the contact rate is high and the recovery rate is low.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "personal-formula", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This chapter demonstrates a two-dimensional parameter sweep using a `SweepFrame` to store the results.\n", + "\n", + "We plotted the results three ways: \n", + "\n", + "* First we plotted total infections versus `beta`, with one line for each value of `gamma`.\n", + "\n", + "* Then we plotted total infections versus `gamma`, with one line for each value of `beta`.\n", + "\n", + "* Finally, we made a contour plot with `beta` on the $y$-axis, `gamma` on the $x$-axis and contour lines where the metric is constant.\n", + "\n", + "These visualizations suggest that there is a relationship between `beta` and `gamma` that determines the outcome of the model. \n", + "In fact, there is.\n", + "In the next chapter we'll explore it by running simulations, then derive it by analysis." + ] + }, + { + "cell_type": "markdown", + "id": "variable-capture", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "You can access the notebooks at ." + ] + }, + { + "cell_type": "markdown", + "id": "conscious-blues", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + " If we know `beta` and `gamma`, we can compute the fraction of the population that gets infected. In general, we don't know these parameters, but sometimes we can estimate them based on the behavior of an outbreak.\n", + "\n", + "Suppose the infectious period for the Freshman Plague is known to be 2 days on average, and suppose during one particularly bad year, 40% of the class is infected at some point. Estimate the time between contacts, `1/beta`." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "democratic-driving", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "stretch-israel", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "controlling-strand", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "instructional-hayes", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap13.py b/chapters/chap13.py new file mode 100644 index 000000000..fd5aa0807 --- /dev/null +++ b/chapters/chap13.py @@ -0,0 +1,29 @@ +from modsim import * + +# import code from previous notebooks + +from chap11 import make_system +from chap11 import update_func +from chap11 import run_simulation +from chap11 import plot_results + +from chap12 import calc_total_infected + +from modsim import * + +def sweep_beta(beta_array, gamma): + sweep = SweepSeries() + for beta in beta_array: + system = make_system(beta, gamma) + results = run_simulation(system, update_func) + sweep[beta] = calc_total_infected(results, system) + return sweep + +from modsim import * + +def sweep_parameters(beta_array, gamma_array): + frame = SweepFrame(columns=gamma_array) + for gamma in gamma_array: + frame[gamma] = sweep_beta(beta_array, gamma) + return frame + diff --git a/chapters/chap14.ipynb b/chapters/chap14.ipynb new file mode 100644 index 000000000..3bd644a81 --- /dev/null +++ b/chapters/chap14.ipynb @@ -0,0 +1,807 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "infrared-shower", + "metadata": {}, + "source": [ + "# Nondimensionalization" + ] + }, + { + "cell_type": "markdown", + "id": "documentary-steal", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "earned-kidney", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "forty-hammer", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "middle-surge", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'chap11.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "hybrid-making", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'chap12.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "inclusive-characteristic", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'chap13.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "grave-occasions", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import code from previous notebooks\n", + "\n", + "from chap11 import make_system\n", + "from chap11 import update_func\n", + "from chap11 import run_simulation\n", + "from chap11 import plot_results\n", + "\n", + "from chap12 import calc_total_infected\n", + "\n", + "from chap13 import sweep_beta\n", + "from chap13 import sweep_parameters" + ] + }, + { + "cell_type": "markdown", + "id": "brief-trademark", + "metadata": {}, + "source": [ + "In the previous chapter we swept the parameters of the Kermack-McKendrick (KM) model: the contact rate, `beta`, and the recovery rate, `gamma`.\n", + "For each pair of parameters, we ran a simulation and computed the total fraction of the population infected.\n", + "\n", + "In this chapter we'll investigate the relationship between the parameters and this metric, using both simulation and analysis." + ] + }, + { + "cell_type": "markdown", + "id": "weekly-award", + "metadata": {}, + "source": [ + "The figures in the previous chapter suggest that there is a relationship between the parameters of the KM model, `beta` and `gamma`, and the fraction of the population that is infected. Let's think what that relationship might be.\n", + "\n", + "- When `beta` exceeds `gamma`, there are more contacts\n", + " than recoveries during each day. The difference between `beta` and `gamma` might be called the *excess contact rate*, in units of contacts per day.\n", + "\n", + "- As an alternative, we might consider the ratio `beta/gamma`, which\n", + " is the number of contacts per recovery. Because the numerator and\n", + " denominator are in the same units, this ratio is *dimensionless*, which means it has no units.\n", + "\n", + "Describing physical systems using dimensionless parameters is often a\n", + "useful move in the modeling and simulation game. In fact, it is so useful that it has a name: *nondimensionalization* (see\n", + ").\n", + "So that's what we'll try first." + ] + }, + { + "cell_type": "markdown", + "id": "legendary-terrorism", + "metadata": {}, + "source": [ + "## Exploring the Results\n", + "\n", + "In the previous chapter, we wrote a function, `sweep_parameters`,\n", + "that takes an array of values for `beta` and an array of values for `gamma`.\n", + "It runs a simulation for each pair of parameters and returns a `SweepFrame` with the results.\n", + "\n", + "I'll run it again with the following arrays of parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "genetic-morris", + "metadata": {}, + "outputs": [], + "source": [ + "beta_array = [0.1, 0.2, 0.3, 0.4, 0.5, \n", + " 0.6, 0.7, 0.8, 0.9, 1.0 , 1.1]\n", + "gamma_array = [0.2, 0.4, 0.6, 0.8]\n", + "frame = sweep_parameters(beta_array, gamma_array)" + ] + }, + { + "cell_type": "markdown", + "id": "hearing-cycle", + "metadata": {}, + "source": [ + "Here's what the first few rows look like:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "appointed-terrorist", + "metadata": {}, + "outputs": [], + "source": [ + "frame.head()" + ] + }, + { + "cell_type": "markdown", + "id": "catholic-alfred", + "metadata": {}, + "source": [ + "The `SweepFrame` has one row for each value of `beta` and one column for each value of `gamma`. \n", + "We can print the values in the `SweepFrame` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "collected-waterproof", + "metadata": {}, + "outputs": [], + "source": [ + "for gamma in frame.columns:\n", + " column = frame[gamma]\n", + " for beta in column.index:\n", + " metric = column[beta]\n", + " print(beta, gamma, metric)" + ] + }, + { + "cell_type": "markdown", + "id": "unexpected-surgeon", + "metadata": {}, + "source": [ + "This is the first example we've seen with one `for` loop inside another:\n", + "\n", + "- Each time the outer loop runs, it selects a value of `gamma` from\n", + " the columns of the `SweepFrame` and extracts the corresponding\n", + " column.\n", + "\n", + "- Each time the inner loop runs, it selects a value of `beta` from the index of the column and selects the corresponding element, which is the fraction of the population that got infected.\n", + "\n", + "Since there are 11 rows and 4 columns, the total number of lines in the output is 44.\n", + "\n", + "The following function uses the same loops to enumerate the elements of the `SweepFrame`, but instead of printing a line for each element, it plots a point." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "polar-flash", + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib.pyplot import plot\n", + "\n", + "def plot_sweep_frame(frame):\n", + " for gamma in frame.columns:\n", + " column = frame[gamma]\n", + " for beta in column.index:\n", + " metric = column[beta]\n", + " plot(beta/gamma, metric, '.', color='C1')" + ] + }, + { + "cell_type": "markdown", + "id": "shared-boxing", + "metadata": {}, + "source": [ + "For each element of the `SweepFrame` it plots a point with the ratio `beta/gamma` as the $x$ coordinate and `metric` -- which is the fraction of the population that's infected -- as the $y$ coordinate.\n", + "\n", + "Here's what it looks like:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "incorporate-launch", + "metadata": {}, + "outputs": [], + "source": [ + "plot_sweep_frame(frame)\n", + "\n", + "decorate(xlabel='Contact number (beta/gamma)',\n", + " ylabel='Fraction infected')" + ] + }, + { + "cell_type": "markdown", + "id": "tropical-colony", + "metadata": {}, + "source": [ + "The results fall on a single curve, at least approximately. That means that we can predict the fraction of the population that will be infected based on a single parameter, the ratio `beta/gamma`. We don't need to know the values of `beta` and `gamma` separately." + ] + }, + { + "cell_type": "markdown", + "id": "lightweight-surfing", + "metadata": {}, + "source": [ + "## Contact Number\n", + "\n", + "From Chapter 11, recall that the number of new infections in a\n", + "given day is $\\beta s i N$, and the number of recoveries is\n", + "$\\gamma i N$. If we divide these quantities, the result is\n", + "$\\beta s / \\gamma$, which is the number of new infections per recovery\n", + "(as a fraction of the population).\n", + "\n", + "When a new disease is introduced to a susceptible population, $s$ is\n", + "approximately 1, so the number of people infected by each sick person is $\\beta / \\gamma$. This ratio is called the *contact number* or *basic reproduction number* (see ). By convention it is usually denoted $R_0$, but in the context of an SIR model, that notation is confusing, so we'll use $c$ instead." + ] + }, + { + "cell_type": "markdown", + "id": "banner-egyptian", + "metadata": {}, + "source": [ + "The results in the previous section suggest that there is a relationship between $c$ and the total number of infections. We can derive this relationship by analyzing the differential equations from\n", + "Chapter 11:\n", + "\n", + "$$\\begin{aligned}\n", + "\\frac{ds}{dt} &= -\\beta s i \\\\\n", + "\\frac{di}{dt} &= \\beta s i - \\gamma i\\\\\n", + "\\frac{dr}{dt} &= \\gamma i\\end{aligned}$$ \n", + "\n", + "In the same way we divided the\n", + "contact rate by the infection rate to get the dimensionless quantity\n", + "$c$, now we'll divide $di/dt$ by $ds/dt$ to get a ratio of rates:\n", + "\n", + "$$\\frac{di}{ds} = \\frac{\\beta s i - \\gamma i}{-\\beta s i}$$ \n", + "\n", + "Which we can simplify as\n", + "\n", + "$$\\frac{di}{ds} = -1 + \\frac{\\gamma}{\\beta s}$$ \n", + "\n", + "Replacing $\\beta/\\gamma$ with $c$, we can write\n", + "\n", + "$$\\frac{di}{ds} = -1 + \\frac{1}{c s}$$ " + ] + }, + { + "cell_type": "markdown", + "id": "noticed-mouse", + "metadata": {}, + "source": [ + "Dividing one differential equation by another is not an obvious move, but in this case it is useful because it gives us a relationship between $i$, $s$, and $c$ that does not depend on time. From that relationship, we can derive an equation that relates $c$ to the final value of $s$. In theory, this equation makes it possible to infer $c$ by observing the course of an epidemic." + ] + }, + { + "cell_type": "markdown", + "id": "accessible-bernard", + "metadata": {}, + "source": [ + "Here's how the derivation goes. We multiply both sides of the previous\n", + "equation by $ds$: \n", + "\n", + "$$di = \\left( -1 + \\frac{1}{cs} \\right) ds$$ \n", + "\n", + "And then integrate both sides: \n", + "\n", + "$$i = -s + \\frac{1}{c} \\log s + q$$ \n", + "\n", + "where $q$ is a constant of integration. Rearranging terms yields:\n", + "\n", + "$$q = i + s - \\frac{1}{c} \\log s$$ \n", + "\n", + "Now let's see if we can figure out what $q$ is. At the beginning of an epidemic, if the fraction infected is small and nearly everyone is susceptible, we can use the approximations $i(0) = 0$ and $s(0) = 1$ to compute $q$:\n", + "\n", + "$$q = 0 + 1 + \\frac{1}{c} \\log 1$$ \n", + "\n", + "Since $\\log 1 = 0$, we get $q = 1$." + ] + }, + { + "cell_type": "markdown", + "id": "instrumental-placement", + "metadata": {}, + "source": [ + "Now, at the end of the epidemic, let's assume that $i(\\infty) = 0$, and $s(\\infty)$ is an unknown quantity, $s_{\\infty}$. Now we have:\n", + "\n", + "$$q = 1 = 0 + s_{\\infty}- \\frac{1}{c} \\log s_{\\infty}$$ \n", + "\n", + "Solving for $c$, we get \n", + "\n", + "$$c = \\frac{\\log s_{\\infty}}{s_{\\infty}- 1}$$ \n", + "\n", + "By relating $c$ and $s_{\\infty}$, this equation makes it possible to estimate $c$ based on data, and possibly predict the behavior of future epidemics." + ] + }, + { + "cell_type": "markdown", + "id": "under-dating", + "metadata": {}, + "source": [ + "## Analysis and Simulation\n", + "\n", + "Let's compare this analytic result to the results from simulation. I'll create an array of values for $s_{\\infty}$." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "architectural-growth", + "metadata": {}, + "outputs": [], + "source": [ + "s_inf_array = linspace(0.003, 0.99, 50)" + ] + }, + { + "cell_type": "markdown", + "id": "oriented-turtle", + "metadata": {}, + "source": [ + "And compute the corresponding values of $c$:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "narrative-embassy", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import log\n", + "\n", + "c_array = log(s_inf_array) / (s_inf_array - 1)" + ] + }, + { + "cell_type": "markdown", + "id": "assisted-public", + "metadata": {}, + "source": [ + "To get the total infected, we compute the difference between $s(0)$ and\n", + "$s(\\infty)$, then store the results in a `Series`:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "canadian-assumption", + "metadata": {}, + "outputs": [], + "source": [ + "frac_infected = 1 - s_inf_array" + ] + }, + { + "cell_type": "markdown", + "id": "published-generation", + "metadata": {}, + "source": [ + "The ModSim library provides a function called `make_series` we can use to put `c_array` and `frac_infected` in a Pandas `Series`." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "antique-watch", + "metadata": {}, + "outputs": [], + "source": [ + "frac_infected_series = make_series(c_array, frac_infected)" + ] + }, + { + "cell_type": "markdown", + "id": "conscious-bathroom", + "metadata": {}, + "source": [ + "Now we can plot the results:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "fallen-router", + "metadata": {}, + "outputs": [], + "source": [ + "plot_sweep_frame(frame)\n", + "frac_infected_series.plot(label='analysis')\n", + "\n", + "decorate(xlabel='Contact number (c)',\n", + " ylabel='Fraction infected')" + ] + }, + { + "cell_type": "markdown", + "id": "billion-worthy", + "metadata": {}, + "source": [ + "When the contact number exceeds 1, analysis and simulation agree. When\n", + "the contact number is less than 1, they do not: analysis indicates there should be no infections; in the simulations there are a small number of infections.\n", + "\n", + "The reason for the discrepancy is that the simulation divides time into a discrete series of days, whereas the analysis treats time as a\n", + "continuous quantity.\n", + "When the contact number is large, these two models agree; when it is small, they diverge." + ] + }, + { + "cell_type": "markdown", + "id": "alternate-surprise", + "metadata": {}, + "source": [ + "## Estimating Contact Number\n", + "\n", + "The previous figure shows that if we know the contact number, we can estimate the fraction of the population that will be infected with just a few arithmetic operations.\n", + "We don't have to run a simulation.\n", + "\n", + "We can also read the figure the other way; if we know what fraction of the population was affected by a past outbreak, we can estimate the contact number.\n", + "Then, if we know one of the parameters, like `gamma`, we can use the contact number to estimate the other parameter, like `beta`.\n", + "\n", + "At least in theory, we can.\n", + "In practice, it might not work very well, because of the shape of the curve. \n", + "\n", + "* When the contact number is low, the curve is quite steep, which means that small changes in $c$ yield big changes in the number of infections. If we observe that the total fraction infected is anywhere from 20% to 80%, we would conclude that $c$ is near 2.\n", + "\n", + "* And when the contact number is high, the curve is nearly flat, which means that it's hard to see the difference between values of $c$ between 3 and 6.\n", + "\n", + "With the uncertainty of real data, we might not be able to estimate $c$ with much precision.\n", + "But as one of the exercises below, you'll have a chance to try." + ] + }, + { + "cell_type": "markdown", + "id": "related-council", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "In this chapter we used simulations to explore the relationship between `beta`, `gamma`, and the fraction infected.\n", + "Then we used analysis to explain that relationship.\n", + "\n", + "With that, we are done with the Kermack-McKendrick model.\n", + "In the next chapter we'll move on to thermal systems and the notorious coffee cooling problem." + ] + }, + { + "cell_type": "markdown", + "id": "removed-conviction", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "You can access the notebooks at ." + ] + }, + { + "cell_type": "markdown", + "id": "textile-program", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + " At the beginning of this chapter, I suggested two ways to relate `beta` and `gamma`: we could compute their difference or their ratio.\n", + "\n", + "Because the ratio is dimensionless, I suggested we explore it first, and that led us to discover the contact number, which is `beta/gamma`.\n", + "When we plotted the fraction infected as a function of the contact number, we found that this metric falls on a single curve, at least approximately.\n", + "That indicates that the ratio is enough to predict the results; we don't have to know `beta` and `gamma` individually. \n", + "\n", + "But that leaves a question open: what happens if we do the same thing using the difference instead of the ratio?\n", + "\n", + "Write a version of `plot_sweep_frame`, called `plot_sweep_frame_difference`, that plots the fraction infected versus the difference `beta-gamma`.\n", + "\n", + "What do the results look like, and what does that imply? " + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "current-tiger", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "specialized-regression", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "canadian-authentication", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "angry-perfume", + "metadata": {}, + "source": [ + "### Exercise 2\n", + "\n", + " Suppose you run a survey at the end of the semester and find that 26% of students had the Freshman Plague at some point.\n", + "What is your best estimate of `c`?\n", + "\n", + "Hint: if you display `frac_infected_series`, you can read off the answer. " + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "surgical-mouth", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "focused-magnet", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "charged-sample", + "metadata": {}, + "source": [ + "### Exercise 3\n", + "\n", + "So far the only metric we have considered is the total fraction of the population that gets infected over the course of an epidemic. That is an important metric, but it is not the only one we care about.\n", + "\n", + "For example, if we have limited resources to deal with infected people, we might also be concerned about the number of people who are sick at the peak of the epidemic, which is the maximum of `I`.\n", + "\n", + "Write a version of `sweep_beta` that computes this metric, and use it to compute a `SweepFrame` for a range of values of `beta` and `gamma`.\n", + "Make a contour plot that shows the value of this metric as a function of `beta` and `gamma`.\n", + "\n", + "Then use `plot_sweep_frame` to plot the maximum of `I` as a function of the contact number, `beta/gamma`.\n", + "Do the results fall on a single curve?" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "greatest-palestine", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "average-practitioner", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "elegant-gamma", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "plain-dayton", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "sexual-services", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "central-fever", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "tropical-advancement", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "editorial-coating", + "metadata": {}, + "source": [ + "## Under the Hood\n", + "\n", + "ModSim provides `make_series` to make it easier to create a Pandas Series. In this chapter, we used it like this: " + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "corresponding-india", + "metadata": {}, + "outputs": [], + "source": [ + "frac_infected_series = make_series(c_array, frac_infected)" + ] + }, + { + "cell_type": "markdown", + "id": "comparable-strap", + "metadata": {}, + "source": [ + "If you import `Series` from Pandas, you can make a `Series` yourself, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "constant-christianity", + "metadata": {}, + "outputs": [], + "source": [ + "from pandas import Series\n", + "\n", + "frac_infected_series = Series(frac_infected, c_array)" + ] + }, + { + "cell_type": "markdown", + "id": "wireless-president", + "metadata": {}, + "source": [ + "The difference is that the arguments are in reverse order: the first argument is stored as the values in the `Series`; the second argument is stored as the index.\n", + "\n", + "I find that order counterintuitive, which is why I use `make_series`.\n", + "`make_series` takes the same optional keyword arguments as `Series`, which you can read about at ." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "hidden-recipient", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap15.ipynb b/chapters/chap15.ipynb new file mode 100644 index 000000000..2636a53ca --- /dev/null +++ b/chapters/chap15.ipynb @@ -0,0 +1,883 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "romantic-speech", + "metadata": {}, + "source": [ + "# Cooling Coffee" + ] + }, + { + "cell_type": "markdown", + "id": "higher-button", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "dependent-monitoring", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "continental-decrease", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "moving-trademark", + "metadata": { + "tags": [] + }, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "capital-needle", + "metadata": {}, + "source": [ + "So far the systems we have studied have been physical in the sense that they exist in the world, but they have not been physics in the sense of what physics classes are usually about. In the next few chapters, we'll do some physics, starting with *thermal systems*, that is, systems where the temperature of objects changes as heat transfers from one to another." + ] + }, + { + "cell_type": "markdown", + "id": "working-senator", + "metadata": {}, + "source": [ + "## The Coffee Cooling Problem\n", + "\n", + "The coffee cooling problem was discussed by Jearl Walker in \n", + "\"The Amateur Scientist\", *Scientific American*, Volume 237, Issue 5, November 1977. Since then it has become a standard example of modeling and simulation.\n", + "\n", + "Here is my version of the problem:\n", + "\n", + "> Suppose I stop on the way to work to pick up a cup of coffee and a small container of milk. Assuming that I want the coffee to be as hot as possible when I arrive at work, should I add the milk at the coffee shop, wait until I get to work, or add the milk at some point in between?" + ] + }, + { + "cell_type": "markdown", + "id": "backed-steel", + "metadata": {}, + "source": [ + "To help answer this question, I made a trial run with the milk and\n", + "coffee in separate containers and took some measurements (not really):\n", + "\n", + "- When served, the temperature of the coffee is 90 °C. The volume is\n", + " 300 mL.\n", + "\n", + "- The milk is at an initial temperature of 5 °C, and I take about\n", + " 50 mL.\n", + "\n", + "- The ambient temperature in my car is 22 °C.\n", + "\n", + "- The coffee is served in a well insulated cup. When I arrive at work after 30 minutes, the temperature of the coffee has fallen to 70 °C.\n", + "\n", + "- The milk container is not as well insulated. After 15 minutes, it\n", + " warms up to 20 °C, nearly the ambient temperature.\n", + "\n", + "To use this data and answer the question, we have to know something\n", + "about temperature and heat, and we have to make some modeling decisions." + ] + }, + { + "cell_type": "markdown", + "id": "retired-noise", + "metadata": {}, + "source": [ + "## Temperature and Heat\n", + "\n", + "To understand how coffee cools (and milk warms), we need a model of\n", + "temperature and heat. *Temperature* is a property of an object or a\n", + "system; in SI units it is measured in degrees Celsius (°C). Temperature quantifies how hot or cold the object is, which is related to the average velocity of the particles that make it up.\n", + "\n", + "When particles in a hot object contact particles in a cold object, the\n", + "hot object gets cooler and the cold object gets warmer as energy is\n", + "transferred from one to the other. The transferred energy is called\n", + "*heat*; in SI units it is measured in joules (J).\n", + "\n", + "Heat is related to temperature by the following equation (see\n", + "): \n", + "\n", + "$$Q = C~\\Delta T$$ \n", + "\n", + "where $Q$ is the amount of heat transferred to an object, $\\Delta T$ is the resulting change in temperature, and $C$ is the object's *thermal mass*, which is a property of the object that determines how much energy it takes to heat or cool it. In SI units, thermal mass is measured in joules per degree Celsius (J/°C)." + ] + }, + { + "cell_type": "markdown", + "id": "upper-johnson", + "metadata": {}, + "source": [ + "For objects made primarily from one material, thermal mass can be\n", + "computed like this: \n", + "\n", + "$$C = m c_p$$ \n", + "\n", + "where $m$ is the mass of the object and $c_p$ is the *specific heat capacity* of the material, which is the amount of thermal mass per gram (see ).\n", + "\n", + "We can use these equations to estimate the thermal mass of a cup of\n", + "coffee. The specific heat capacity of coffee is probably close to that\n", + "of water, which is 4.2 J/g/°C. Assuming that the density of coffee is\n", + "close to that of water, which is 1 g/mL, the mass of 300 mL of coffee is 300 g, and the thermal mass is 1260 J/°C.\n", + "\n", + "So when a cup of coffee cools from 90 °C to 70 °C, the change in\n", + "temperature, $\\Delta T$ is 20 °C, which means that 25 200 J of heat\n", + "energy was transferred from the cup and the coffee to the surrounding environment\n", + "(the cup holder and air in my car).\n", + "\n", + "To give you a sense of how much energy that is, if you were able to\n", + "harness all of that heat to do work (which you cannot), you could\n", + "use it to lift a cup of coffee from sea level to 8571 m, just shy of the height of Mount Everest, 8848 m." + ] + }, + { + "cell_type": "markdown", + "id": "closed-vegetable", + "metadata": {}, + "source": [ + "## Heat Transfer\n", + "\n", + "In a situation like the coffee cooling problem, there are three ways\n", + "heat transfers from one object to another (see ):\n", + "\n", + "- Conduction: When objects at different temperatures come into\n", + " contact, the faster-moving particles of the higher-temperature\n", + " object transfer kinetic energy to the slower-moving particles of the lower-temperature object.\n", + "\n", + "- Convection: When particles in a gas or liquid flow from place to\n", + " place, they carry heat energy with them. Fluid flows can be caused\n", + " by external action, like stirring, or by internal differences in\n", + " temperature. For example, you might have heard that hot air rises,\n", + " which is a form of \"natural convection\".\n", + "\n", + "- Radiation: As the particles in an object move due to thermal energy,\n", + " they emit electromagnetic radiation. The energy carried by this\n", + " radiation depends on the object's temperature and surface properties\n", + " (see )." + ] + }, + { + "cell_type": "markdown", + "id": "valuable-shark", + "metadata": {}, + "source": [ + "For objects like coffee in a car, the effect of radiation is much\n", + "smaller than the effects of conduction and convection, so we will ignore it.\n", + "\n", + "Convection can be a complex topic, since it often depends on details of fluid flow in three dimensions. But for this problem we will be able to get away with a simple model called \"Newton's law of cooling\"." + ] + }, + { + "cell_type": "markdown", + "id": "freelance-target", + "metadata": {}, + "source": [ + "## Newton's Law of Cooling\n", + "\n", + "*Newton's law of cooling* asserts that the temperature rate of change for an object is proportional to the difference in temperature between the object and the surrounding environment:\n", + "\n", + "$$\\frac{dT}{dt} = -r (T - T_{env})$$ \n", + "\n", + "where $t$ is time, $T$ is the temperature of the object, $T_{env}$ is the temperature of the environment, and $r$ is a constant that characterizes how quickly heat is transferred between the object and the environment." + ] + }, + { + "cell_type": "markdown", + "id": "corrected-attachment", + "metadata": {}, + "source": [ + "Newton's so-called \"law \" is really a model: it is a good approximation in some conditions and less good in others.\n", + "\n", + "For example, if the primary mechanism of heat transfer is conduction,\n", + "Newton's law is \"true\", which is to say that $r$ is constant over a\n", + "wide range of temperatures. And sometimes we can estimate $r$ based on\n", + "the material properties and shape of the object.\n", + "\n", + "When convection contributes a non-negligible fraction of heat transfer, $r$ depends on temperature, but Newton's law is often accurate enough, at least over a narrow range of temperatures. In this case $r$ usually has to be estimated experimentally, since it depends on details of surface shape, air flow, evaporation, etc.\n", + "\n", + "When radiation makes up a substantial part of heat transfer, Newton's\n", + "law is not a good model at all. This is the case for objects in space or in a vacuum, and for objects at high temperatures (more than a few\n", + "hundred degrees Celsius, say).\n", + "\n", + "However, for a situation like the coffee cooling problem, we expect Newton's model to be quite good.\n", + "\n", + "With that, we have just one more modeling decision to make: whether to treat the coffee and the cup as separate objects or a single object. If the cup is made of paper, it has less mass than the coffee, and the specific heat capacity of paper is lower, too. In that case, it would be reasonable to treat the cup and coffee as a single object. For a cup with substantial thermal mass, like a ceramic mug, we might consider a model that computes the temperature of coffee and cup separately." + ] + }, + { + "cell_type": "markdown", + "id": "selective-video", + "metadata": {}, + "source": [ + "## Implementing Newtonian Cooling\n", + "\n", + "To get started, we'll focus on the coffee. Then, as an exercise, you can simulate the milk. In the next chapter, we'll put them together, literally.\n", + "\n", + "Here's a function that takes the parameters of the system and makes a `System` object:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "incredible-greeting", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def make_system(T_init, volume, r, t_end):\n", + " return System(T_init=T_init,\n", + " T_final=T_init,\n", + " volume=volume,\n", + " r=r,\n", + " t_end=t_end,\n", + " T_env=22,\n", + " t_0=0,\n", + " dt=1)" + ] + }, + { + "cell_type": "markdown", + "id": "gross-appeal", + "metadata": {}, + "source": [ + "In addition to the parameters, `make_system` sets the temperature of the environment, `T_env`, the initial time stamp, `t_0`, and the time step, `dt`, which we will use to simulate the cooling process.\n", + "Here's a `System` object that represents the coffee." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "fuzzy-support", + "metadata": {}, + "outputs": [], + "source": [ + "coffee = make_system(T_init=90, volume=300, r=0.01, t_end=30)" + ] + }, + { + "cell_type": "markdown", + "id": "rental-pearl", + "metadata": {}, + "source": [ + "The values of `T_init`, `volume`, and `t_end` come from the statement of the problem.\n", + "I chose the value of `r` arbitrarily for now; we will see how to estimate it soon.\n", + "\n", + "Strictly speaking, Newton's law is a differential equation, but over a short period of time we can approximate it with a difference equation:\n", + "\n", + "$$\\Delta T = -r (T - T_{env}) dt$$ \n", + "\n", + "where $dt$ is the time step and $\\Delta T$ is the change in temperature during that time step.\n", + "\n", + "Note: I use $\\Delta T$ to denote a change in temperature over time, but in the context of heat transfer, you might also see $\\Delta T$ used to denote the difference in temperature between an object and its\n", + "environment, $T - T_{env}$. To minimize confusion, I avoid this second\n", + "use.\n", + "\n", + "The following function takes the current time `t`, the current temperature, `T`, and a `System` object, and computes the change in temperature during a time step:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "tight-baptist", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def change_func(t, T, system):\n", + " r, T_env, dt = system.r, system.T_env, system.dt \n", + " return -r * (T - T_env) * dt" + ] + }, + { + "cell_type": "markdown", + "id": "flexible-crest", + "metadata": {}, + "source": [ + "We can test it with the initial temperature of the coffee, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "fiscal-artwork", + "metadata": {}, + "outputs": [], + "source": [ + "change_func(0, coffee.T_init, coffee)" + ] + }, + { + "cell_type": "markdown", + "id": "sporting-morocco", + "metadata": {}, + "source": [ + "With `dt=1` minute, the temperature drops by about 0.7 °C, at least for this value of `r`.\n", + "\n", + "Now here's a version of `run_simulation` that simulates a series of time steps from `t_0` to `t_end`:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "cardiac-independence", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def run_simulation(system, change_func):\n", + " t_array = linrange(system.t_0, system.t_end, system.dt)\n", + " n = len(t_array)\n", + " \n", + " series = TimeSeries(index=t_array)\n", + " series.iloc[0] = system.T_init\n", + " \n", + " for i in range(n-1):\n", + " t = t_array[i]\n", + " T = series.iloc[i]\n", + " series.iloc[i+1] = T + change_func(t, T, system)\n", + " \n", + " system.T_final = series.iloc[-1]\n", + " return series" + ] + }, + { + "cell_type": "markdown", + "id": "together-adapter", + "metadata": {}, + "source": [ + "There are two things here that are different from previous versions of `run_simulation`.\n", + "\n", + "First, we use `linrange` to make an array of values from `t_0` to `t_end` with time step `dt`.\n", + "`linrange` is similar to `linspace`; they both take a start value and an end value and return an array of equally spaced values.\n", + "The difference is the third argument: `linspace` takes an integer that indicates the number of points in the range; `linrange` takes a step size that indicates the interval between values.\n", + "When we make the `TimeSeries`, we use the keyword argument `index` to indicate that the index of the `TimeSeries` is the array of time stamps, `t_array`." + ] + }, + { + "cell_type": "markdown", + "id": "fantastic-object", + "metadata": {}, + "source": [ + "Second, this version of `run_simulation` uses `iloc` rather than `loc` to specify the rows in the `TimeSeries`.\n", + "Here's the difference: \n", + "\n", + "* With `loc`, the label in brackets can be any kind of value, with any start, end, and time step. For example, in the world population model, the labels are years starting in 1960 and ending in 2016.\n", + "\n", + "* With `iloc`, the label in brackets is always an integer starting at 0. So we can always get the first element with `iloc[0]` and the last element with `iloc[-1]`, regardless of what the labels are.\n", + "\n", + "In this version of `run_simulation`, the loop variable is an integer, `i`, that goes from `0` to `n-1`, including `0` but not including `n-1`.\n", + "So the first time through the loop, `i` is `0` and the value we add to the `TimeSeries` has index 1.\n", + "The last time through the loop, `i` is `n-2` and the value we add has index `n-1`.\n", + "\n", + "We can run the simulation like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "numerous-metabolism", + "metadata": {}, + "outputs": [], + "source": [ + "results = run_simulation(coffee, change_func)" + ] + }, + { + "cell_type": "markdown", + "id": "earned-primary", + "metadata": {}, + "source": [ + "The result is a `TimeSeries` with one row per time step. \n", + "Here are the first few rows:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "infectious-carolina", + "metadata": {}, + "outputs": [], + "source": [ + "show(results.head())" + ] + }, + { + "cell_type": "markdown", + "id": "color-olive", + "metadata": {}, + "source": [ + "And the last few rows:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "accompanied-melbourne", + "metadata": {}, + "outputs": [], + "source": [ + "show(results.tail())" + ] + }, + { + "cell_type": "markdown", + "id": "formal-headset", + "metadata": {}, + "source": [ + "With `t_0=0`, `t_end=30`, and `dt=1`, the time stamps go from `0.0` to `30.0`.\n", + "\n", + "Here's what the `TimeSeries` looks like." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "important-constitution", + "metadata": {}, + "outputs": [], + "source": [ + "results.plot(label='coffee')\n", + "\n", + "decorate(xlabel='Time (min)',\n", + " ylabel='Temperature (C)',\n", + " title='Coffee Cooling')" + ] + }, + { + "cell_type": "markdown", + "id": "absent-arkansas", + "metadata": {}, + "source": [ + "The temperature after 30 minutes is 72.3 °C, which is a little higher than the measurement we're trying to match, which is 70 °C. " + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "absolute-desire", + "metadata": {}, + "outputs": [], + "source": [ + "coffee.T_final" + ] + }, + { + "cell_type": "markdown", + "id": "light-carpet", + "metadata": {}, + "source": [ + "To find the value of `r` where the final temperature is precisely 70 °C, we could proceed by trial and error, but it is more efficient to use a root-finding algorithm." + ] + }, + { + "cell_type": "markdown", + "id": "funny-weekly", + "metadata": {}, + "source": [ + "## Finding Roots\n", + "\n", + "The ModSim library provides a function called `root_scalar` that finds the roots of non-linear equations. As an example, suppose you want to find the roots of the polynomial \n", + "\n", + "$$f(x) = (x - 1)(x - 2)(x - 3)$$ \n", + "\n", + "A *root* is a value of $x$ that makes $f(x)=0$. Because of the way I wrote this polynomial, we can see that if $x=1$, the first factor is 0; if $x=2$, the second factor is 0; and if $x=3$, the third factor is 0, so those are the roots.\n", + "\n", + "I'll use this example to demonstrate `root_scalar`. First, we have to\n", + "write a function that evaluates $f$:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "small-shark", + "metadata": {}, + "outputs": [], + "source": [ + "def func(x):\n", + " return (x-1) * (x-2) * (x-3)" + ] + }, + { + "cell_type": "markdown", + "id": "cleared-cylinder", + "metadata": {}, + "source": [ + "Now we call `root_scalar` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "defensive-content", + "metadata": {}, + "outputs": [], + "source": [ + "res = root_scalar(func, bracket=[1.5, 2.5])\n", + "res" + ] + }, + { + "cell_type": "markdown", + "id": "adult-management", + "metadata": {}, + "source": [ + "The first argument is the function whose roots we want. The second\n", + "argument is an interval that contains or *brackets* a root. The result is an object that contains several variables, including the Boolean value `converged`, which is `True` if the search converged successfully on a root, and `root`, which is the root that was found." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "behind-perth", + "metadata": {}, + "outputs": [], + "source": [ + "res.root" + ] + }, + { + "cell_type": "markdown", + "id": "disturbed-shade", + "metadata": {}, + "source": [ + "If we provide a different interval, we find a different root." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "following-sentence", + "metadata": {}, + "outputs": [], + "source": [ + "res = root_scalar(func, bracket=[2.5, 3.5])\n", + "res.root" + ] + }, + { + "cell_type": "markdown", + "id": "eligible-updating", + "metadata": {}, + "source": [ + "If the interval doesn't contain a root, you'll get a `ValueError` and a message like `f(a) and f(b) must have different signs`.\n", + "\n", + "Now we can use `root_scalar` to estimate `r`." + ] + }, + { + "cell_type": "markdown", + "id": "commercial-correlation", + "metadata": {}, + "source": [ + "## Estimating r\n", + "\n", + "What we want is the value of `r` that yields a final temperature of\n", + "70 °C. To use `root_scalar`, we need a function that takes `r` as a parameter and returns the difference between the final temperature and the goal:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "genetic-compound", + "metadata": {}, + "outputs": [], + "source": [ + "def error_func(r, system):\n", + " system.r = r\n", + " results = run_simulation(system, change_func)\n", + " return system.T_final - 70" + ] + }, + { + "cell_type": "markdown", + "id": "happy-bridal", + "metadata": {}, + "source": [ + "This is called an *error function* because it returns the\n", + "difference between what we got and what we wanted, that is, the error.\n", + "With the right value of `r`, the error is 0.\n", + "\n", + "We can test `error_func` like this, using the initial guess `r=0.01`:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "french-financing", + "metadata": {}, + "outputs": [], + "source": [ + "coffee = make_system(T_init=90, volume=300, r=0.01, t_end=30)\n", + "error_func(0.01, coffee)" + ] + }, + { + "cell_type": "markdown", + "id": "statutory-activation", + "metadata": {}, + "source": [ + "The result is an error of 2.3 °C, which means the final temperature with `r=0.01` is too high." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "excellent-fellow", + "metadata": {}, + "outputs": [], + "source": [ + "error_func(0.02, coffee)" + ] + }, + { + "cell_type": "markdown", + "id": "unlimited-living", + "metadata": {}, + "source": [ + "With `r=0.02`, the error is  about -11°C, which means that the final temperature is too low. So we know that the correct value must be in between.\n", + "\n", + "Now we can call `root_scalar` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "french-decline", + "metadata": {}, + "outputs": [], + "source": [ + "res = root_scalar(error_func, coffee, bracket=[0.01, 0.02])\n", + "res" + ] + }, + { + "cell_type": "markdown", + "id": "tamil-absorption", + "metadata": {}, + "source": [ + "The first argument is the error function.\n", + "The second argument is the `System` object, which `root_scalar` passes as an argument to `error_func`.\n", + "The third argument is an interval that brackets the root.\n", + "\n", + "Here's the root we found." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "afraid-ordering", + "metadata": {}, + "outputs": [], + "source": [ + "r_coffee = res.root\n", + "r_coffee" + ] + }, + { + "cell_type": "markdown", + "id": "partial-definition", + "metadata": {}, + "source": [ + "In this example, `r_coffee` turns out to be about `0.0115`, in units of min$^{-1}$ (inverse minutes).\n", + "We can confirm that this value is correct by setting `r` to the root we found and running the simulation." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "derived-annual", + "metadata": {}, + "outputs": [], + "source": [ + "coffee.r = res.root\n", + "run_simulation(coffee, change_func)\n", + "coffee.T_final" + ] + }, + { + "cell_type": "markdown", + "id": "prerequisite-conditioning", + "metadata": {}, + "source": [ + "The final temperature is very close to 70 °C." + ] + }, + { + "cell_type": "markdown", + "id": "outer-powder", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This chapter presents the basics of heat, temperature, and Newton's law of cooling, which is a model that is accurate when most heat transfer is by conduction and convection, not radiation.\n", + "\n", + "To simulate a hot cup of coffee, we wrote Newton's law as a difference equation, then wrote a version of `run_simulation` that implements it. Then we used `root_scalar` to find the value of `r` that matches the measurement from my hypothetical experiment.\n", + "\n", + "All that is the first step toward solving the coffee cooling problem I posed at the beginning of the chapter. As an exercise, you'll do the next step, which is simulating the milk. In the next chapter, we'll model the mixing process and finish off the problem." + ] + }, + { + "cell_type": "markdown", + "id": "smart-yeast", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "You can access the notebooks at ." + ] + }, + { + "cell_type": "markdown", + "id": "scientific-attachment", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + "Simulate the temperature of 50 mL of milk with a starting temperature of 5 °C, in a vessel with `r=0.1`, for 15 minutes, and plot the results. Use `make_system` to make a `System` object that represents the milk, and use `run_simulation` to simulate it.\n", + "By trial and error, find a value for `r` that makes the final temperature close to 20 °C." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "committed-angel", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "military-military", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "logical-acoustic", + "metadata": {}, + "source": [ + "### Exercise 2\n", + "\n", + "Write an error function that simulates the temperature of the milk and returns the difference between the final temperature and 20 °C. Use it to estimate the value of `r` for the milk." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "agreed-excuse", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "defined-nudist", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "loose-rehabilitation", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "alert-emerald", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap15.py b/chapters/chap15.py new file mode 100644 index 000000000..f439ced24 --- /dev/null +++ b/chapters/chap15.py @@ -0,0 +1,35 @@ +from modsim import * + +def make_system(T_init, volume, r, t_end): + return System(T_init=T_init, + T_final=T_init, + volume=volume, + r=r, + t_end=t_end, + T_env=22, + t_0=0, + dt=1) + +from modsim import * + +def change_func(t, T, system): + r, T_env, dt = system.r, system.T_env, system.dt + return -r * (T - T_env) * dt + +from modsim import * + +def run_simulation(system, change_func): + t_array = linrange(system.t_0, system.t_end, system.dt) + n = len(t_array) + + series = TimeSeries(index=t_array) + series.iloc[0] = system.T_init + + for i in range(n-1): + t = t_array[i] + T = series.iloc[i] + series.iloc[i+1] = T + change_func(t, T, system) + + system.T_final = series.iloc[-1] + return series + diff --git a/chapters/chap16.ipynb b/chapters/chap16.ipynb new file mode 100644 index 000000000..3c0b142ca --- /dev/null +++ b/chapters/chap16.ipynb @@ -0,0 +1,686 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "medieval-johnston", + "metadata": {}, + "source": [ + "# Adding Milk" + ] + }, + { + "cell_type": "markdown", + "id": "fifty-chicken", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "british-place", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "determined-volunteer", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "valuable-shannon", + "metadata": { + "tags": [] + }, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "fossil-moisture", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'chap15.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cutting-scale", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import code from previous notebooks\n", + "\n", + "from chap15 import change_func\n", + "from chap15 import run_simulation\n", + "from chap15 import make_system" + ] + }, + { + "cell_type": "markdown", + "id": "enhanced-copper", + "metadata": {}, + "source": [ + "In the previous chapter we wrote a simulation of a cooling cup of\n", + "coffee. \n", + "Given the initial temperature of the coffee, the temperature of the atmosphere, and the rate parameter, `r`, we predicted the temperature of the coffee over time.\n", + "Then we used a root finding algorithm to estimate `r` based on data.\n", + "\n", + "If you did the exercises, you simulated the temperature of the milk as it warmed, and estimated its rate parameter as well.\n", + "\n", + "Now let's put it together.\n", + "In this chapter we'll write a function that simulates mixing the two liquids, and use it to answer the question we started with: is it better to mix the coffee and milk at the beginning, the end, or somewhere in the middle?" + ] + }, + { + "cell_type": "markdown", + "id": "hydraulic-belfast", + "metadata": {}, + "source": [ + "## Mixing Liquids\n", + "\n", + "When we mix two liquids, the temperature of the mixture depends on the\n", + "temperatures of the ingredients as well as their volumes, densities, and specific heat capacities (as defined in the previous chapter).\n", + "In this section I'll explain how.\n", + "\n", + "Assuming there are no chemical reactions that either produce or consume heat, the total thermal energy of the system is the same before and after mixing; in other words, thermal energy is *conserved*.\n", + "\n", + "If the temperature of the first liquid is $T_1$, the temperature of the second liquid is $T_2$, and the final temperature of the mixture is $T$, the heat transfer into the first liquid is $C_1 (T - T_1)$ and the heat transfer into the second liquid is $C_2 (T - T_2)$, where $C_1$ and $C_2$ are the thermal masses of the liquids.\n", + "\n", + "In order to conserve energy, these heat transfers must add up to 0:\n", + "\n", + "$$C_1 (T - T_1) + C_2 (T - T_2) = 0$$ \n", + "\n", + "We can solve this equation for T:\n", + "\n", + "$$T = \\frac{C_1 T_1 + C_2 T_2}{C_1 + C_2}$$ \n", + "\n", + "For the coffee cooling problem, we have the volume of each liquid; if we also know the density, $\\rho$, and the specific heat capacity, $c_p$, we can compute thermal mass: \n", + "\n", + "$$C = \\rho V c_p$$ \n", + "\n", + "If the liquids have the same density and heat capacity, they drop out of the equation, and we can write:\n", + "\n", + "$$T = \\frac{V_1 T_1 + V_2 T_2}{V_1 + V_2}$$ \n", + "\n", + "where $V_1$ and $V_2$ are the volumes of the liquids.\n", + "\n", + "As an approximation, I'll assume that milk and coffee have the same\n", + "density and specific heat. If you are interested, you can look up these\n", + "quantities and see how good this assumption is.\n", + "\n", + "Now let's simulate the mixing process.\n", + "The following function takes two `System` objects, representing the\n", + "coffee and milk, and creates a new `System` to represent the mixture:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "extensive-happening", + "metadata": {}, + "outputs": [], + "source": [ + "def mix(system1, system2):\n", + " \n", + " V1, V2 = system1.volume, system2.volume\n", + " T1, T2 = system1.T_final, system2.T_final\n", + " \n", + " V_mix = V1 + V2\n", + " T_mix = (V1 * T1 + V2 * T2) / V_mix\n", + " \n", + " return make_system(T_init=T_mix,\n", + " volume=V_mix,\n", + " r=system1.r,\n", + " t_end=30)" + ] + }, + { + "cell_type": "markdown", + "id": "overhead-architect", + "metadata": {}, + "source": [ + "The first two lines extract volume and temperature from the `System` objects. The next two lines compute the volume and temperature of the mixture. Finally, `mix` makes a new `System` object and returns it.\n", + "\n", + "This function uses the value of `r` from `system1` as the value of `r`\n", + "for the mixture. If `system1` represents the coffee, and we are adding\n", + "the milk to the coffee, this is probably a reasonable choice. On the\n", + "other hand, when we increase the amount of liquid in the coffee cup,\n", + "that might change `r`. So this is an assumption we might want to\n", + "revisit.\n", + "\n", + "Now we have everything we need to solve the problem." + ] + }, + { + "cell_type": "markdown", + "id": "foreign-campbell", + "metadata": {}, + "source": [ + "## Mix First or Last?\n", + "\n", + "First I'll create objects to represent the coffee and milk.\n", + "For `r_coffee`, I'll use the value we computed in the previous chapter." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "polyphonic-specialist", + "metadata": {}, + "outputs": [], + "source": [ + "r_coffee = 0.0115\n", + "coffee = make_system(T_init=90, volume=300, r=r_coffee, t_end=30)" + ] + }, + { + "cell_type": "markdown", + "id": "authorized-system", + "metadata": {}, + "source": [ + "For `r_milk`, I'll use the value I estimated in the exercise from the previous chapter." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "subtle-donna", + "metadata": {}, + "outputs": [], + "source": [ + "r_milk = 0.133\n", + "milk = make_system(T_init=5, volume=50, r=r_milk, t_end=15)" + ] + }, + { + "cell_type": "markdown", + "id": "satisfactory-rwanda", + "metadata": {}, + "source": [ + "Now we can mix them and simulate 30 minutes:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "domestic-tours", + "metadata": {}, + "outputs": [], + "source": [ + "mix_first = mix(coffee, milk)\n", + "run_simulation(mix_first, change_func)\n", + "\n", + "mix_first.T_final" + ] + }, + { + "cell_type": "markdown", + "id": "accredited-diagnosis", + "metadata": {}, + "source": [ + "The final temperature is 61.5 °C which is still warm enough to be\n", + "enjoyable. Would we do any better if we added the milk last?\n", + "\n", + "I'll simulate the coffee and milk separately, and then mix them:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "subject-richardson", + "metadata": {}, + "outputs": [], + "source": [ + "run_simulation(coffee, change_func)\n", + "run_simulation(milk, change_func)\n", + "mix_last = mix(coffee, milk)\n", + "mix_last.T_final" + ] + }, + { + "cell_type": "markdown", + "id": "divine-trance", + "metadata": {}, + "source": [ + "After mixing, the temperature is 62.9 °C, so it looks like adding the\n", + "milk at the end is better. \n", + "But is that the best we can do?" + ] + }, + { + "cell_type": "markdown", + "id": "linear-republican", + "metadata": {}, + "source": [ + "## Optimal Timing\n", + "\n", + "Adding the milk after 30 minutes is better than adding it immediately, but maybe there's something in between that's even better. To find out, I'll use the following function, which takes the time to add the milk, `t_add`, as a parameter:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "mental-corporation", + "metadata": {}, + "outputs": [], + "source": [ + "def run_and_mix(t_add, t_total):\n", + " coffee.t_end = t_add\n", + " coffee_results = run_simulation(coffee, change_func)\n", + " \n", + " milk.t_end = t_add\n", + " milk_results = run_simulation(milk, change_func)\n", + " \n", + " mixture = mix(coffee, milk)\n", + " mixture.t_end = t_total - t_add\n", + " results = run_simulation(mixture, change_func)\n", + "\n", + " return mixture.T_final" + ] + }, + { + "cell_type": "markdown", + "id": "better-cemetery", + "metadata": {}, + "source": [ + "`run_and_mix` simulates both systems for the given time, `t_add`.\n", + "Then it mixes them and simulates the mixture for the remaining time, `t_total - t_add`.\n", + "\n", + "When `t_add` is `0`, we add the milk immediately; when `t_add` is `30`, we add it at the end. Now we can sweep the range of values in between:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "adverse-hanging", + "metadata": {}, + "outputs": [], + "source": [ + "sweep = SweepSeries()\n", + "for t_add in linspace(0, 30, 11):\n", + " sweep[t_add] = run_and_mix(t_add, 30)" + ] + }, + { + "cell_type": "markdown", + "id": "practical-state", + "metadata": {}, + "source": [ + "Here's what the results look like:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "bright-proposal", + "metadata": {}, + "outputs": [], + "source": [ + "sweep.plot(label='mixture', color='C2')\n", + "\n", + "decorate(xlabel='Time until mixing (min)',\n", + " ylabel='Final temperature (C)')" + ] + }, + { + "cell_type": "markdown", + "id": "experienced-tribute", + "metadata": {}, + "source": [ + "Note that this is a parameter sweep, not a time series.\n", + "\n", + "The final temperature is maximized when `t_add=30`, so adding the milk\n", + "at the end is optimal." + ] + }, + { + "cell_type": "markdown", + "id": "rough-investor", + "metadata": {}, + "source": [ + "## Analytic Solution\n", + "\n", + "Simulating Newton's law of cooling isn't really necessary because we can solve the differential equation analytically. If\n", + "\n", + "$$\\frac{dT}{dt} = -r (T - T_{env})$$ \n", + "\n", + "the general solution is\n", + "\n", + "$$T{\\left (t \\right )} = C \\exp(-r t) + T_{env}$$ \n", + "\n", + "and the particular solution where $T(0) = T_{init}$ is\n", + "\n", + "$$T_{env} + \\left(- T_{env} + T_{init}\\right) \\exp(-r t)$$ \n", + "\n", + "If you would like to see this solution done by hand, you can watch this video: ." + ] + }, + { + "cell_type": "markdown", + "id": "chief-abortion", + "metadata": {}, + "source": [ + "Now we can use the observed data to estimate the parameter $r$. If we\n", + "observe the that the temperature at $t_{end}$ is $T_{final}$, we can plug these values into the particular solution and solve for $r$. The result is:\n", + "\n", + "$$r = \\frac{1}{t_{end}} \\log{\\left (\\frac{T_{init} - T_{env}}{T_{final} - T_{env}} \\right )}$$\n", + "\n", + "The following function takes a `System` object and computes `r`:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "secondary-swift", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import log\n", + "\n", + "def compute_r(system):\n", + " t_end = system.t_end\n", + " T_init = system.T_init\n", + " T_final = system.T_final\n", + " T_env = system.T_env\n", + " \n", + " r = log((T_init - T_env) / (T_final - T_env)) / t_end\n", + " return r" + ] + }, + { + "cell_type": "markdown", + "id": "correct-spare", + "metadata": {}, + "source": [ + "We can use this function to compute `r` for the coffee, given the parameters of the problem." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "south-machinery", + "metadata": {}, + "outputs": [], + "source": [ + "coffee2 = make_system(T_init=90, volume=300, r=0, t_end=30)\n", + "coffee2.T_final = 70\n", + "r_coffee2 = compute_r(coffee2)\n", + "r_coffee2" + ] + }, + { + "cell_type": "markdown", + "id": "worthy-steal", + "metadata": {}, + "source": [ + "This value is close to the value of `r` we computed in the previous chapter, `0.115`, but not exactly the same.\n", + "That's because the simulations use discrete time steps, and the analysis uses continuous time.\n", + "\n", + "Nevertheless, the results of the analysis are consistent with the simulation.\n", + "To check, we'll use the following function, which takes a `System` object and uses the analytic result to compute a time series:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "opening-transsexual", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import exp\n", + "\n", + "def run_analysis(system):\n", + " T_env, T_init, r = system.T_env, system.T_init, system.r\n", + " \n", + " t_array = linrange(system.t_0, system.t_end, system.dt) \n", + " T_array = T_env + (T_init - T_env) * exp(-r * t_array)\n", + " \n", + " system.T_final = T_array[-1]\n", + " return make_series(t_array, T_array)" + ] + }, + { + "cell_type": "markdown", + "id": "fallen-spiritual", + "metadata": {}, + "source": [ + "The first line unpacks the system variables.\n", + "The next two lines compute `t_array`, which is a NumPy array of time stamps, and `T_array`, which is an array of the corresponding temperatures.\n", + "The last two lines store the final temperature in the `System` object and use `make_series` to return the results in a Pandas `Series`.\n", + "\n", + "We can run it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "agreed-bouquet", + "metadata": {}, + "outputs": [], + "source": [ + "coffee2.r = r_coffee2\n", + "results2 = run_analysis(coffee2)\n", + "coffee2.T_final" + ] + }, + { + "cell_type": "markdown", + "id": "exact-juice", + "metadata": {}, + "source": [ + "The final temperature is 70 °C, as it should be. In fact, the results\n", + "are identical to what we got by simulation, with a small difference due to rounding." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "portuguese-sympathy", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "coffee.r = 0.011543\n", + "results = run_simulation(coffee, change_func)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "distinguished-regard", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from numpy import allclose\n", + "\n", + "allclose(results, results2)" + ] + }, + { + "cell_type": "markdown", + "id": "floral-homework", + "metadata": {}, + "source": [ + "Since we can solve this problem analytically, you might wonder why we bothered writing a simulation. \n", + "One reason is validation: since we solved the same problem two ways, we can be more confident that the answer is correct. \n", + "The other reason is flexibility: now that we have a working simulation, it would be easy to add more features. For example, the temperature of the environment might change over time, or we could simulate the coffee and container as two objects. \n", + "If the coffee and milk are next to each other, we could include the heat flow between them. A model with these features would be difficult or impossible to solve analytically. " + ] + }, + { + "cell_type": "markdown", + "id": "rapid-payroll", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "In this chapter we finished the coffee cooling problem from the previous chapter, and found that it is better to add the milk at the end, at least for the version of the problem I posed.\n", + "\n", + "As an exercise you will have a chance to explore a variation of the problem where the answer might be different.\n", + "\n", + "In the next chapter we'll move on to a new example, a model of how glucose and insulin interact to control blood sugar." + ] + }, + { + "cell_type": "markdown", + "id": "gothic-clearance", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "You can access the notebooks at ." + ] + }, + { + "cell_type": "markdown", + "id": "foreign-permission", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + "Use `compute_r` to compute `r_milk` according to the analytic solution. Run the analysis with this value of `r_milk` and confirm that the results are consistent with the simulation." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "acting-howard", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "ahead-compensation", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "assumed-shock", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "elementary-moral", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "western-mixture", + "metadata": {}, + "source": [ + "### Exercise 2\n", + "\n", + "Suppose the coffee shop won't let me take milk in a separate container, but I keep a bottle of milk in the refrigerator at my office. In that case is it better to add the milk at the coffee shop, or wait until I get to the office?\n", + "\n", + "Hint: Think about the simplest way to represent the behavior of a refrigerator in this model. The change you make to test this variation of the problem should be very small!" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "super-citizenship", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap17.ipynb b/chapters/chap17.ipynb new file mode 100644 index 000000000..56ba08b8f --- /dev/null +++ b/chapters/chap17.ipynb @@ -0,0 +1,593 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "interracial-guitar", + "metadata": {}, + "source": [ + "# Pharmacokinetics" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "plastic-trigger", + "metadata": { + "tags": [] + }, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "european-movement", + "metadata": {}, + "source": [ + "In this chapter we'll start a new example, a model of how glucose and insulin interact to control blood sugar.\n", + "We will implement a widely used model called the \"minimal model\" because it is intended to include only the elements essential to explain the observed behavior of the system.\n", + "\n", + "This chapter presents the model and some background information we need to understand it.\n", + "In the next chapter we'll implement the model and compare the results to real data.\n", + "\n", + "My presentation in this chapter follows Bergman (2005) \"Minimal Model\"\n", + "(abstract at , PDF at\n", + ")." + ] + }, + { + "cell_type": "markdown", + "id": "tamil-helping", + "metadata": {}, + "source": [ + "## The Minimal Model\n", + "\n", + "*Pharmacokinetics* is the study of how drugs and other substances move around the body, react, and are eliminated. In this chapter, I present a widely used pharmacokinetic model of glucose and insulin in the blood stream.\n", + "\n", + "*Glucose* is a form of sugar that circulates in the blood of animals; it is used as fuel for muscles, the brain, and other organs. The concentration of blood sugar is controlled by the hormone system, and especially by *insulin*, which is produced by the pancreas and has the effect of reducing blood sugar.\n", + "\n", + "In people with normal pancreatic function, the hormone system maintains *homeostasis*; that is, it keeps the concentration of blood sugar in a range that is neither too high or too low.\n", + "\n", + "But if the pancreas does not produce enough insulin, or if the cells\n", + "that should respond to insulin become insensitive, blood sugar can\n", + "become elevated, a condition called *hyperglycemia*. Long term, severe hyperglycemia is the defining symptom of *diabetes mellitus*, a serious disease that affects almost 10% of the population in the U.S. (see )." + ] + }, + { + "cell_type": "markdown", + "id": "sharp-yukon", + "metadata": {}, + "source": [ + "A widely used test for hyperglycemia and diabetes is the\n", + "frequently sampled intravenous glucose tolerance test (FSIGT), in which glucose is injected into the blood stream of a fasting subject (someone who has not eaten recently); then blood samples are collected at intervals of 2--10 minutes for 3 hours. The samples are analyzed to measure the concentrations of glucose and insulin.\n", + "\n", + "Using these measurements, we can estimate several parameters of\n", + "the subject's response; the most important is a parameter denoted $S_I$, which quantifies the effect of insulin on the rate of reduction in blood sugar." + ] + }, + { + "cell_type": "markdown", + "id": "round-roommate", + "metadata": {}, + "source": [ + "## The Glucose Minimal Model\n", + "\n", + "The minimal model, as proposed by Bergman, Ider, Bowden, and\n", + "Cobelli, consists of two parts: the glucose model and the insulin model. I will present an implementation of the glucose model; as a case study, you will have the chance to implement the insulin model.\n", + "\n", + "The original model was developed in the 1970s; since then, many\n", + "variations and extensions have been proposed. Bergman's comments on the development of the model provide insight into their process:\n", + "\n", + "> We applied the principle of Occam's Razor, i.e. by asking what was the simplest model based upon known physiology that could account for the insulin-glucose relationship revealed in the data. Such a model must be simple enough to account totally for the measured glucose (given the insulin input), yet it must be possible, using mathematical techniques, to estimate all the characteristic parameters of the model from a single data set (thus avoiding unverifiable assumptions)." + ] + }, + { + "cell_type": "markdown", + "id": "accompanied-battery", + "metadata": {}, + "source": [ + "The most useful models are the ones that achieve this balance: including enough realism to capture the essential features of the system without so much complexity that they are impractical. In this example, the practical limit is the ability to estimate the parameters of the model using data, and to interpret the parameters meaningfully.\n", + "\n", + "Bergman discusses the features he and his colleagues thought were\n", + "essential:\n", + "\n", + "> 1. Glucose, once elevated by injection, returns to basal level due to two effects: the effect of glucose itself to normalize its own concentration \\[...\\] as well as the catalytic effect of insulin to allow glucose to self-normalize.\n", + "> 2. Also, we discovered that the effect of insulin on net glucose disappearance must be sluggish --- that is, that insulin acts slowly because insulin must first move from plasma to a remote compartment \\[...\\] to exert its action on glucose disposal.\n", + "\n", + "To paraphrase the second point, the effect of insulin on glucose\n", + "disposal, as seen in the data, happens more slowly than we would expect if it depended primarily on the concentration of insulin in the blood. Bergman's group hypothesized that insulin must move relatively slowly from the blood to a remote compartment where it has its effect." + ] + }, + { + "cell_type": "markdown", + "id": "growing-logistics", + "metadata": {}, + "source": [ + "At the time, the \"remote compartment\" was a modeling abstraction that\n", + "might, or might not, represent something physical. Later, according to\n", + "Bergman, it was \"shown to be interstitial fluid\", that is, the fluid\n", + "that surrounds tissue cells. \n", + "\n", + "In the history of mathematical modeling, it is common for hypothetical entities, added to models to achieve particular effects, to be found later to correspond to physical entities. One notable example is the gene, which was defined as an inheritable unit several decades before we learned that genes are encoded in DNA (see )." + ] + }, + { + "cell_type": "markdown", + "id": "bottom-extent", + "metadata": {}, + "source": [ + "The glucose model consists of two differential equations:\n", + "\n", + "$$\\frac{dG}{dt} = -k_1 \\left[ G(t) - G_b \\right] - X(t) G(t)$$\n", + "\n", + "$$\\frac{dX}{dt} = k_3 \\left[I(t) - I_b \\right] - k_2 X(t)$$ \n", + "\n", + "where\n", + "\n", + "- $G$ is the concentration of blood glucose as a function of time and $dG/dt$ is its rate of change.\n", + "\n", + "- $X$ is the concentration of insulin in the tissue fluid as a\n", + " function of time, and $dX/dt$ is its rate of change.\n", + "\n", + "- $I$ is the concentration of insulin in the blood as a function of\n", + " time, which is taken as an input into the model, based on\n", + " measurements.\n", + "\n", + "- $G_b$ is the basal concentration of blood glucose and $I_b$ is the\n", + " basal concentration of blood insulin, that is, the concentrations at equilibrium. Both are constants estimated from measurements at the\n", + " beginning or end of the test.\n", + "\n", + "- $k_1$, $k_2$, and $k_3$ are positive-valued parameters that control the rates of appearance and disappearance for glucose and insulin." + ] + }, + { + "cell_type": "markdown", + "id": "dutch-retailer", + "metadata": {}, + "source": [ + "We can interpret the terms in the equations one by one:\n", + "\n", + "- $-k_1 \\left[ G(t) - G_b \\right]$ is the rate of glucose\n", + " disappearance due to the effect of glucose itself. When $G(t)$ is\n", + " above basal level, $G_b$, this term is negative; when $G(t)$ is\n", + " below basal level this term is positive. So in the absence of\n", + " insulin, this term tends to restore blood glucose to basal level.\n", + "\n", + "- $-X(t) G(t)$ models the interaction of glucose and insulin in tissue\n", + " fluid, so the rate increases as either $X$ or $G$ increases. This\n", + " term does not require a rate parameter because the units of $X$ are\n", + " unspecified; we can consider $X$ to be in whatever units make the\n", + " parameter of this term 1.\n", + "\n", + "- $k_3 \\left[ I(t) - I_b \\right]$ is the rate at which insulin diffuses between blood and tissue fluid. When $I(t)$ is above basal level, insulin diffuses from the blood into the tissue fluid. When $I(t)$ is below basal level, insulin diffuses from tissue to the\n", + " blood.\n", + "\n", + "- $-k_2 X(t)$ is the rate of insulin disappearance in tissue fluid as it is consumed or broken down." + ] + }, + { + "cell_type": "markdown", + "id": "regional-receptor", + "metadata": {}, + "source": [ + "The initial state of the model is $X(0) = I_b$ and $G(0) = G_0$, where\n", + "$G_0$ is a constant that represents the concentration of blood sugar\n", + "immediately after the injection. In theory we could estimate $G_0$ based on measurements, but in practice it takes time for the injected glucose to spread through the blood volume. Since $G_0$ is not measurable, it is treated as a *free parameter* of the model, which means that we are free to choose it to fit the data." + ] + }, + { + "cell_type": "markdown", + "id": "apart-giant", + "metadata": {}, + "source": [ + "## Getting the Data\n", + "\n", + "To develop and test the model, we'll use data from Pacini and Bergman, \"MINMOD: a computer program to calculate insulin sensitivity and pancreatic responsivity from the frequently sampled intravenous glucose tolerance test\", *Computer Methods and Programs in Biomedicine*, 1986 (see )." + ] + }, + { + "cell_type": "markdown", + "id": "exact-heating", + "metadata": { + "tags": [] + }, + "source": [ + "The following cell downloads the data." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "mental-wrong", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://github.com/AllenDowney/ModSim/raw/main/data/' +\n", + " 'glucose_insulin.csv')\n", + "\n", + "'https://github.com/AllenDowney/ModSim/raw/main/data/glucose_insulin.csv'" + ] + }, + { + "cell_type": "markdown", + "id": "dressed-regard", + "metadata": {}, + "source": [ + "We can use Pandas to read the data file." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cosmetic-matrix", + "metadata": {}, + "outputs": [], + "source": [ + "from pandas import read_csv\n", + "\n", + "data = read_csv('glucose_insulin.csv', index_col='time')" + ] + }, + { + "cell_type": "markdown", + "id": "written-beast", + "metadata": {}, + "source": [ + "Here are the first few rows:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "naval-geology", + "metadata": {}, + "outputs": [], + "source": [ + "data.head()" + ] + }, + { + "cell_type": "markdown", + "id": "close-payday", + "metadata": {}, + "source": [ + "`data` has two columns: `glucose` is the concentration of blood glucose in mg/dL; `insulin` is the concentration of insulin in the blood in $\\mu$U/mL (a medical \"unit\", denoted U, is an amount defined by convention in context). The index is time in minutes.\n", + "\n", + "This dataset represents glucose and insulin concentrations over\n", + "182 min for a subject with normal insulin production and sensitivity." + ] + }, + { + "cell_type": "markdown", + "id": "burning-valuable", + "metadata": {}, + "source": [ + "## Interpolation\n", + "\n", + "Before we are ready to implement the model, there's one problem we have to solve. In the differential equations, $I$ is a function that can be evaluated at any time $t$. But in the `DataFrame`, we have measurements only at discrete times. The solution is interpolation, which estimates the value of $I$ for values of $t$ between the measurements.\n", + "\n", + "To interpolate the values in `I`, we can use `interpolate`, which takes a `Series` as a parameter and returns a function. That's right, I said it returns a *function*.\n", + "We can call `interpolate` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "terminal-teaching", + "metadata": {}, + "outputs": [], + "source": [ + "I = interpolate(data.insulin)" + ] + }, + { + "cell_type": "markdown", + "id": "banner-shakespeare", + "metadata": {}, + "source": [ + "The result is a function we can call like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "intense-thursday", + "metadata": {}, + "outputs": [], + "source": [ + "I(18)" + ] + }, + { + "cell_type": "markdown", + "id": "severe-desire", + "metadata": {}, + "source": [ + "In this example the interpolated value is about 31.7, which is a linear interpolation between the actual measurements at `t=16` and `t=19`.\n", + "We can also pass an array as an argument to `I`. Here's an array of equally-spaced values from `t_0` to `t_end`." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "least-pattern", + "metadata": {}, + "outputs": [], + "source": [ + "t_0 = data.index[0]\n", + "t_end = data.index[-1]\n", + "t_array = linrange(t_0, t_end)" + ] + }, + { + "cell_type": "markdown", + "id": "empirical-tackle", + "metadata": {}, + "source": [ + "And here are the corresponding values of `I`." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "mounted-venice", + "metadata": {}, + "outputs": [], + "source": [ + "I_array = I(t_array)" + ] + }, + { + "cell_type": "markdown", + "id": "searching-respect", + "metadata": {}, + "source": [ + "We can use `make_series` to put the results in a Pandas `Series`." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "affecting-response", + "metadata": {}, + "outputs": [], + "source": [ + "I_series = make_series(t_array, I_array)" + ] + }, + { + "cell_type": "markdown", + "id": "insured-textbook", + "metadata": {}, + "source": [ + "Here's what the interpolated values look like along with the data." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "engaging-watershed", + "metadata": {}, + "outputs": [], + "source": [ + "data.insulin.plot(style='o', color='C2', label='insulin data')\n", + "I_series.plot(color='C2', label='interpolation')\n", + "\n", + "decorate(xlabel='Time (min)',\n", + " ylabel='Concentration (μU/mL)')" + ] + }, + { + "cell_type": "markdown", + "id": "blond-prince", + "metadata": {}, + "source": [ + "Linear interpolation connects the dots with straight lines, and for this dataset that's probably good enough. As an exercise, below, you can try out other kinds of interpolation." + ] + }, + { + "cell_type": "markdown", + "id": "adopted-locking", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This chapter introduces a model of the interaction between glucose and insulin in the blood stream.\n", + "And it introduces a new tool, interpolation, which we'll need to implement the model.\n", + "\n", + "In the next chapter, we will use measured concentrations of insulin to simulate the glucose-insulin system, and compare the results to measured concentrations of glucose.\n", + "\n", + "Then you'll have a chance to implement the second part of the model, which uses measured concentrations of glucose to simulate the insulin response, and compare the results to the data." + ] + }, + { + "cell_type": "markdown", + "id": "floppy-store", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "You can access the notebooks at ." + ] + }, + { + "cell_type": "markdown", + "id": "hungarian-newman", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + "`interpolate` is a wrapper for the SciPy function `interp1d`.\n", + "Read the documentation of `interp1d` at .\n", + "\n", + "In particular, notice the `kind` argument, which specifies a kind of interpolation.\n", + "The default is linear interpolation, which connects the data points with straight lines.\n", + "\n", + "Pass a keyword argument to `interpolate` to specify one of the other kinds of interpolation and plot the results." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "endless-network", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "entire-concern", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Here's the plotting code again.\n", + "\n", + "data.insulin.plot(style='o', color='C2', label='insulin data')\n", + "I_series.plot(color='C2', label='interpolation')\n", + "\n", + "decorate(xlabel='Time (min)',\n", + " ylabel='Concentration (μU/mL)')" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "invisible-activation", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "furnished-recognition", + "metadata": {}, + "source": [ + "### Exercise 2\n", + "\n", + " Interpolate the glucose data and generate a plot, similar to the previous one, that shows the data points and the interpolated curve evaluated at the time values in `t_array`." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "representative-acquisition", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "rocky-sydney", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "essential-fishing", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap18.ipynb b/chapters/chap18.ipynb new file mode 100644 index 000000000..4cf5efab6 --- /dev/null +++ b/chapters/chap18.ipynb @@ -0,0 +1,811 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "electric-netherlands", + "metadata": {}, + "source": [ + "# Glucose and Insulin" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "preliminary-mexico", + "metadata": { + "tags": [] + }, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "detected-welsh", + "metadata": {}, + "source": [ + "The previous chapter presents the minimal model of the glucose-insulin system and introduces a tool we need to implement it: interpolation.\n", + "\n", + "In this chapter, we'll implement the model two ways:\n", + "\n", + "* We'll start by rewriting the differential equations as difference equations; then we'll solve the difference equations using a version of `run_simulation` similar to what we have used in previous chapters.\n", + "\n", + "* Then we'll use a new SciPy function, called `solve_ivp`, to solve the differential equation using a better algorithm.\n", + "\n", + "We'll see that `solve_ivp` is faster and more accurate than `run_simulation`.\n", + "As a result, we will use it for the models in the rest of the book." + ] + }, + { + "cell_type": "markdown", + "id": "original-photographer", + "metadata": { + "tags": [] + }, + "source": [ + "The following cell downloads the data." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "fewer-weather", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://github.com/AllenDowney/ModSim/raw/main/data/' +\n", + " 'glucose_insulin.csv')" + ] + }, + { + "cell_type": "markdown", + "id": "postal-procedure", + "metadata": { + "tags": [] + }, + "source": [ + "We can use Pandas to read the data." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "computational-border", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from pandas import read_csv\n", + "\n", + "data = read_csv('glucose_insulin.csv', index_col='time');" + ] + }, + { + "cell_type": "markdown", + "id": "collective-orleans", + "metadata": {}, + "source": [ + "## Implementing the Model\n", + "\n", + "To get started, let's assume that the parameters of the model are known.\n", + "We'll implement the model and use it to generate time series for `G` and `X`. \n", + "Then we'll see how we can choose parameters that make the simulation fit the data.\n", + "\n", + "Here are the parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "delayed-trance", + "metadata": {}, + "outputs": [], + "source": [ + "G0 = 270\n", + "k1 = 0.02\n", + "k2 = 0.02\n", + "k3 = 1.5e-05" + ] + }, + { + "cell_type": "markdown", + "id": "sapphire-examination", + "metadata": {}, + "source": [ + "I'll put these values in a sequence which we'll pass to `make_system`:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "following-alarm", + "metadata": {}, + "outputs": [], + "source": [ + "params = G0, k1, k2, k3" + ] + }, + { + "cell_type": "markdown", + "id": "polished-burner", + "metadata": {}, + "source": [ + "Here's a version of `make_system` that takes `params` and `data` as parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "substantial-literacy", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def make_system(params, data):\n", + " G0, k1, k2, k3 = params\n", + " \n", + " t_0 = data.index[0]\n", + " t_end = data.index[-1]\n", + " \n", + " Gb = data.glucose[t_0]\n", + " Ib = data.insulin[t_0]\n", + " I = interpolate(data.insulin)\n", + " \n", + " init = State(G=G0, X=0)\n", + " \n", + " return System(init=init, params=params,\n", + " Gb=Gb, Ib=Ib, I=I,\n", + " t_0=t_0, t_end=t_end, dt=2)" + ] + }, + { + "cell_type": "markdown", + "id": "afraid-friendly", + "metadata": {}, + "source": [ + "`make_system` gets `t_0` and `t_end` from the data. \n", + "It uses the measurements at `t=0` as the basal levels, `Gb` and `Ib`. \n", + "And it uses the parameter `G0` as the initial value for `G`. Then it \n", + "packs everything into a `System` object." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "legislative-richards", + "metadata": {}, + "outputs": [], + "source": [ + "system = make_system(params, data)" + ] + }, + { + "cell_type": "markdown", + "id": "stupid-retro", + "metadata": {}, + "source": [ + "## The Update Function\n", + "\n", + "The minimal model is expressed in terms of differential equations:\n", + "\n", + "$$\\frac{dG}{dt} = -k_1 \\left[ G(t) - G_b \\right] - X(t) G(t)$$\n", + "\n", + "$$\\frac{dX}{dt} = k_3 \\left[I(t) - I_b \\right] - k_2 X(t)$$ \n", + "\n", + "To simulate this system, we will rewrite them as difference equations. \n", + "If we multiply both sides by $dt$, we have:\n", + "\n", + "$$dG = \\left[ -k_1 \\left[ G(t) - G_b \\right] - X(t) G(t) \\right] dt$$\n", + "\n", + "$$dX = \\left[ k_3 \\left[I(t) - I_b \\right] - k_2 X(t) \\right] dt$$ \n", + "\n", + "If we think of $dt$ as a small step in time, these equations tell us how to compute the corresponding changes in $G$ and $X$.\n", + "Here's an update function that computes these changes:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "roman-archive", + "metadata": {}, + "outputs": [], + "source": [ + "def update_func(t, state, system):\n", + " G, X = state\n", + " G0, k1, k2, k3 = system.params \n", + " I, Ib, Gb = system.I, system.Ib, system.Gb\n", + " dt = system.dt\n", + " \n", + " dGdt = -k1 * (G - Gb) - X*G\n", + " dXdt = k3 * (I(t) - Ib) - k2 * X\n", + " \n", + " G += dGdt * dt\n", + " X += dXdt * dt\n", + "\n", + " return State(G=G, X=X)" + ] + }, + { + "cell_type": "markdown", + "id": "basic-subdivision", + "metadata": {}, + "source": [ + "As usual, the update function takes a timestamp, a `State` object, and a `System` object as parameters. The first line uses multiple assignment to extract the current values of `G` and `X`.\n", + "\n", + "The following lines unpack the parameters we need from the `System`\n", + "object.\n", + "\n", + "To compute the derivatives `dGdt` and `dXdt` we translate the equations from math notation to Python.\n", + "Then, to perform the update, we multiply each derivative by the time step `dt`, which is 2 min in this example. \n", + "\n", + "The return value is a `State` object with the new values of `G` and `X`.\n", + "\n", + "Before running the simulation, it is a good idea to run the update\n", + "function with the initial conditions:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "sapphire-shannon", + "metadata": {}, + "outputs": [], + "source": [ + "update_func(system.t_0, system.init, system)" + ] + }, + { + "cell_type": "markdown", + "id": "growing-hormone", + "metadata": {}, + "source": [ + "If it runs without errors and there is nothing obviously wrong with the results, we are ready to run the simulation. " + ] + }, + { + "cell_type": "markdown", + "id": "strange-citation", + "metadata": {}, + "source": [ + "## Running the Simulation\n", + "\n", + "We'll use the following version of `run_simulation`:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "willing-masters", + "metadata": {}, + "outputs": [], + "source": [ + "def run_simulation(system, update_func): \n", + " t_array = linrange(system.t_0, system.t_end, system.dt)\n", + " n = len(t_array)\n", + " \n", + " frame = TimeFrame(index=t_array, \n", + " columns=system.init.index)\n", + " frame.iloc[0] = system.init\n", + " \n", + " for i in range(n-1):\n", + " t = t_array[i]\n", + " state = frame.iloc[i]\n", + " frame.iloc[i+1] = update_func(t, state, system)\n", + " \n", + " return frame" + ] + }, + { + "cell_type": "markdown", + "id": "musical-loading", + "metadata": {}, + "source": [ + "This version is similar to the one we used for the coffee cooling problem.\n", + "The biggest difference is that it makes and returns a `TimeFrame`, which contains one column for each state variable, rather than a `TimeSeries`, which can only store one state variable.\n", + "\n", + "When we make the `TimeFrame`, we use `index` to indicate that the index is the array of time stamps, `t_array`, and `columns` to indicate that the column names are the state variables we get from `init`.\n", + "\n", + "We can run it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "optional-burden", + "metadata": {}, + "outputs": [], + "source": [ + "results = run_simulation(system, update_func)" + ] + }, + { + "cell_type": "markdown", + "id": "ambient-video", + "metadata": {}, + "source": [ + "The result is a `TimeFrame` with a row for each time step and a column for each of the state variables, `G` and `X`.\n", + "Here are the first few time steps." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "international-germany", + "metadata": {}, + "outputs": [], + "source": [ + "results.head()" + ] + }, + { + "cell_type": "markdown", + "id": "least-steal", + "metadata": {}, + "source": [ + "The following plot shows the simulated glucose levels from the model along with the measured data. " + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "liked-asset", + "metadata": {}, + "outputs": [], + "source": [ + "data.glucose.plot(style='o', alpha=0.5, label='glucose data')\n", + "results.G.plot(style='-', color='C0', label='simulation')\n", + "\n", + "decorate(xlabel='Time (min)',\n", + " ylabel='Concentration (mg/dL)')" + ] + }, + { + "cell_type": "markdown", + "id": "stopped-excuse", + "metadata": {}, + "source": [ + "With the parameters I chose, the model fits the data well except during the first few minutes after the injection.\n", + "But we don't expect the model to do well in this part of the time series.\n", + "\n", + "The problem is that the model is *non-spatial*; that is, it does not\n", + "take into account different concentrations in different parts of the\n", + "body. Instead, it assumes that the concentrations of glucose and insulin in blood, and insulin in tissue fluid, are the same throughout the body. This way of representing the body is known among experts as the \"bag of blood\" model.\n", + "\n", + "Immediately after injection, it takes time for the injected glucose to\n", + "circulate. During that time, we don't expect a non-spatial model to be\n", + "accurate. For this reason, we should not take the estimated value of `G0` too seriously; it is useful for fitting the model, but not meant to correspond to a physical, measurable quantity." + ] + }, + { + "cell_type": "markdown", + "id": "internal-positive", + "metadata": {}, + "source": [ + "The following plot shows simulated insulin levels in the hypothetical \"remote compartment\", which is in unspecified units." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "damaged-protection", + "metadata": {}, + "outputs": [], + "source": [ + "results.X.plot(color='C1', label='remote insulin')\n", + "\n", + "decorate(xlabel='Time (min)', \n", + " ylabel='Concentration (arbitrary units)')" + ] + }, + { + "cell_type": "markdown", + "id": "economic-spare", + "metadata": {}, + "source": [ + "Remember that `X` represents the concentration of insulin in the \"remote compartment\", which is believed to be tissue fluid, so we can't compare it to the measured concentration of insulin in the blood.\n", + "\n", + "`X` rises quickly after the initial injection and then declines as the concentration of glucose declines. Qualitatively, this behavior is as expected, but because `X` is not an observable quantity, we can't validate this part of the model quantitatively." + ] + }, + { + "cell_type": "markdown", + "id": "spatial-scholar", + "metadata": {}, + "source": [ + "## Solving Differential Equations\n", + "\n", + "To implement the minimal model, we rewrote the differential equations as difference equations with a finite time step, `dt`.\n", + "When $dt$ is very small, or more precisely *infinitesimal*, the difference equations are the same as the differential equations.\n", + "But in our simulations, $dt$ is 2 min, which is not very small, and definitely not infinitesimal. \n", + "\n", + "In effect, the simulations assume that the derivatives $dG/dt$ and $dX/dt$ are constant during each 2 min time step.\n", + "This method, evaluating derivatives at discrete time steps and assuming that they are constant in between, is called *Euler's method* (see ).\n", + "\n", + "Euler's method is good enough for many problems, but sometimes it is not very accurate.\n", + "In that case, we can usually make it more accurate by decreasing the size of `dt`.\n", + "But then it is not very efficient.\n", + "\n", + "There are other methods that are more accurate and more efficient than Euler's method.\n", + "SciPy provides several of them wrapped in a function called `solve_ivp`.\n", + "The `ivp` stands for *initial value problem*, which is the term for problems like the ones we've been solving, where we are given the initial conditions and try to predict what will happen.\n", + "\n", + "The ModSim library provides a function called `run_solve_ivp` that makes `solve_ivp` a little easier to use.\n", + "\n", + "To use it, we have to provide a *slope function*, which is similar to an update function; in fact, it takes the same parameters: a time stamp, a `State` object, and a `System` object.\n", + "\n", + "Here's a slope function that evaluates the differential equations of the minimal model." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "expected-collapse", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def slope_func(t, state, system):\n", + " G, X = state\n", + " G0, k1, k2, k3 = system.params \n", + " I, Ib, Gb = system.I, system.Ib, system.Gb\n", + " \n", + " dGdt = -k1 * (G - Gb) - X*G\n", + " dXdt = k3 * (I(t) - Ib) - k2 * X\n", + " \n", + " return dGdt, dXdt" + ] + }, + { + "cell_type": "markdown", + "id": "modified-surname", + "metadata": {}, + "source": [ + "`slope_func` is a little simpler than `update_func` because it computes only the derivatives, that is, the slopes. It doesn't do the updates; the solver does them for us.\n", + "\n", + "Now we can call `run_solve_ivp` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "intelligent-visitor", + "metadata": {}, + "outputs": [], + "source": [ + "results2, details = run_solve_ivp(system, slope_func,\n", + " t_eval=results.index)" + ] + }, + { + "cell_type": "markdown", + "id": "promotional-result", + "metadata": {}, + "source": [ + "`run_solve_ivp` is similar to `run_simulation`: it takes a `System`\n", + "object and a slope function as parameters.\n", + "\n", + "The third argument, `t_eval`, is optional; it specifies where the solution should be evaluated.\n", + "\n", + "It returns two values: a `TimeFrame`, which we assign to `results2`, and an `OdeResult` object, which we assign to `details`.\n", + "\n", + "The `OdeResult` object contains information about how the solver ran, including a success code and a diagnostic message." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "industrial-focus", + "metadata": {}, + "outputs": [], + "source": [ + "details.success" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "frequent-exhibit", + "metadata": {}, + "outputs": [], + "source": [ + "details.message" + ] + }, + { + "cell_type": "markdown", + "id": "fatal-parliament", + "metadata": {}, + "source": [ + "It's important to check these messages after running the solver, in case anything went wrong.\n", + "\n", + "The `TimeFrame` has one row for each time step and one column for each state variable. In this example, the rows are time from 0 to 182 minutes; the columns are the state variables, `G` and `X`.\n", + "Here are the first few time steps:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "immediate-legislature", + "metadata": {}, + "outputs": [], + "source": [ + "results2.head()" + ] + }, + { + "cell_type": "markdown", + "id": "noticed-material", + "metadata": {}, + "source": [ + "Because we used `t_eval=results.index`, the time stamps in `results2` are the same as in `results`, which makes them easier to compare.\n", + "\n", + "The following figure shows the results from `run_solve_ivp` along with the results from `run_simulation`:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "peripheral-commission", + "metadata": {}, + "outputs": [], + "source": [ + "results.G.plot(style='--', label='simulation')\n", + "results2.G.plot(style='-', label='solve ivp')\n", + "\n", + "decorate(xlabel='Time (min)',\n", + " ylabel='Concentration (mg/dL)')" + ] + }, + { + "cell_type": "markdown", + "id": "advanced-provider", + "metadata": {}, + "source": [ + "The differences are barely visible.\n", + "We can compute the relative differences like this:\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "diagnostic-lawsuit", + "metadata": {}, + "outputs": [], + "source": [ + "diff = results.G - results2.G\n", + "percent_diff = diff / results2.G * 100" + ] + }, + { + "cell_type": "markdown", + "id": "comparative-boulder", + "metadata": {}, + "source": [ + "And we can use `describe` to compute summary statistics:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "clear-neighbor", + "metadata": {}, + "outputs": [], + "source": [ + "percent_diff.abs().describe()" + ] + }, + { + "cell_type": "markdown", + "id": "temporal-threat", + "metadata": {}, + "source": [ + "The mean relative difference is about 0.65% and the maximum is a little more than 1%.\n", + "Here are the results for `X`." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "happy-guess", + "metadata": {}, + "outputs": [], + "source": [ + "results.X.plot(style='--', label='simulation')\n", + "results2.X.plot(style='-', label='solve ivp')\n", + "\n", + "decorate(xlabel='Time (min)', \n", + " ylabel='Concentration (arbitrary units)')" + ] + }, + { + "cell_type": "markdown", + "id": "electronic-navigation", + "metadata": {}, + "source": [ + "These differences are a little bigger, especially at the beginning.\n", + "\n", + "If we use `run_simulation` with smaller time steps, the results are more accurate, but they take longer to compute.\n", + "For some problems, we can find a value of `dt` that produces accurate results in a reasonable time. However, if `dt` is *too* small, the results can be inaccurate again. So it can be tricky to get it right.\n", + "\n", + "The advantage of `run_solve_ivp` is that it chooses the step size automatically in order to balance accuracy and efficiency.\n", + "You can use keyword arguments to adjust this balance, but most of the time the results are accurate enough, and the computation is fast enough, without any intervention." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "julian-dublin", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "diff = results.G - results2.X\n", + "percent_diff = diff / results2.X * 100\n", + "percent_diff.abs().describe()" + ] + }, + { + "cell_type": "markdown", + "id": "prostate-psychology", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "In this chapter, we implemented the glucose minimal model two ways, using `run_simulation` and `run_solve_ivp`, and compared the results.\n", + "We found that in this example, `run_simulation`, which uses Euler's method, is probably good enough.\n", + "But soon we will see examples where it is not.\n", + "\n", + "So far, we have assumed that the parameters of the system are known, but in practice that's not true.\n", + "As one of the case studies in the next chapter, you'll have a chance to see where those parameters came from." + ] + }, + { + "cell_type": "markdown", + "id": "unusual-springer", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "You can access the notebooks at ." + ] + }, + { + "cell_type": "markdown", + "id": "substantial-grain", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + "Our solution to the differential equations is only approximate because we used a finite step size, `dt=2` minutes.\n", + "If we make the step size smaller, we expect the solution to be more accurate. Run the simulation with `dt=1` and compare the results. What is the largest relative error between the two solutions?" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "thermal-trance", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "prompt-activity", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "developed-collaboration", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "russian-qualification", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap18.py b/chapters/chap18.py new file mode 100644 index 000000000..7e3db625c --- /dev/null +++ b/chapters/chap18.py @@ -0,0 +1,30 @@ +from modsim import * + +def make_system(params, data): + G0, k1, k2, k3 = params + + t_0 = data.index[0] + t_end = data.index[-1] + + Gb = data.glucose[t_0] + Ib = data.insulin[t_0] + I = interpolate(data.insulin) + + init = State(G=G0, X=0) + + return System(init=init, params=params, + Gb=Gb, Ib=Ib, I=I, + t_0=t_0, t_end=t_end, dt=2) + +from modsim import * + +def slope_func(t, state, system): + G, X = state + G0, k1, k2, k3 = system.params + I, Ib, Gb = system.I, system.Ib, system.Gb + + dGdt = -k1 * (G - Gb) - X*G + dXdt = k3 * (I(t) - Ib) - k2 * X + + return dGdt, dXdt + diff --git a/chapters/chap19.ipynb b/chapters/chap19.ipynb new file mode 100644 index 000000000..624e8a10a --- /dev/null +++ b/chapters/chap19.ipynb @@ -0,0 +1,310 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "mighty-israeli", + "metadata": {}, + "source": [ + "# Case Studies Part 2" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "dense-seattle", + "metadata": { + "tags": [] + }, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "reflected-sitting", + "metadata": {}, + "source": [ + "This chapter presents case studies where you can apply the tools we have learned so far to the glucose-insulin minimal model, an electronic circuit, a thermal model of a wall, and the interaction of HIV and T cells." + ] + }, + { + "cell_type": "markdown", + "id": "structured-satellite", + "metadata": {}, + "source": [ + "## The Glucose Minimal Model\n", + "\n", + "In the previous chapter we implemented the glucose minimal model using given parameters, but I didn't say where those parameters came from.\n", + "\n", + "In the repository for this book, you will find a notebook,\n", + "*glucose.ipynb*, that shows how we can find the parameters that best fit the data.\n", + "You can download it from or run it on Colab at .\n", + "\n", + "It uses a SciPy function called `leastsq`, which stands for \"least squares\"; so-named because it finds the parameters that minimize the sum of squared differences between the results of the model and the data.\n", + "\n", + "You can think of `leastsq` as an optional tool for this book. We won't use it in the text itself, but it appears in a few of the case studies." + ] + }, + { + "cell_type": "markdown", + "id": "laden-gathering", + "metadata": {}, + "source": [ + "## The Insulin Minimal Model\n", + "\n", + "Along with the glucose minimal model, Berman et al. developed an insulin minimal model, in which the concentration of insulin, $I$, is governed by this differential equation:\n", + "\n", + "$$\\frac{dI}{dt} = -k I(t) + \\gamma \\left[ G(t) - G_T \\right] t$$ \n", + "\n", + "where\n", + "\n", + "- $k$ is a parameter that controls the rate of insulin disappearance\n", + " independent of blood glucose.\n", + "\n", + "- $G(t)$ is the measured concentration of blood glucose at time $t$.\n", + "\n", + "- $G_T$ is the glucose threshold; when blood glucose is above this\n", + " level, it triggers an increase in blood insulin.\n", + "\n", + "- $\\gamma$ is a parameter that controls the rate of increase (or\n", + " decrease) in blood insulin when glucose is above (or below) $G_T$.\n", + "\n", + "The initial condition is $I(0) = I_0$. As in the glucose minimal model, we treat this initial concentration as a free parameter; that is, we'll choose it to fit the data." + ] + }, + { + "cell_type": "markdown", + "id": "interstate-gibson", + "metadata": {}, + "source": [ + "The parameters of this model can be used to estimate $\\phi_1$ and\n", + "$\\phi_2$, which are quantities that \"describe the sensitivity to glucose of the first and second phase pancreatic responsivity\". These quantities are related to the parameters as follows:\n", + "\n", + "$$\\phi_1 = \\frac{I_{max} - I_b}{k (G_0 - G_b)}$$\n", + "\n", + "$$\\phi_2 = \\gamma \\times 10^4$$ \n", + "\n", + "where $I_{max}$ is the maximum measured insulin level, and $I_b$ and $G_b$ are the basal levels of insulin and glucose.\n", + "\n", + "In the repository for this book, you will find a notebook,\n", + "*insulin.ipynb*, that contains starter code for this case study. Use it to implement the insulin model, find the parameters that best fit the data, and estimate $\\phi_1$ and $\\phi_2$.\n", + "You can download it from or run it on Colab at ." + ] + }, + { + "cell_type": "markdown", + "id": "hybrid-vienna", + "metadata": {}, + "source": [ + "## Low-pass Filter\n", + "\n", + "The following circuit diagram (from ) shows a low-pass filter built with one resistor and one capacitor.\n", + "\n", + "![Circuit diagram of a low-pass filter](https://github.com/AllenDowney/ModSim/raw/main/figs/Lowpass_Filter_RC.png)\n", + "\n", + "A *filter* is a circuit that takes a signal, $V_{in}$, as input and produces a signal, $V_{out}$, as output. In this context, a *signal* is a voltage that changes over time.\n", + "\n", + "A filter is *low-pass* if it allows low-frequency signals to pass from\n", + "$V_{in}$ to $V_{out}$ unchanged, but it reduces the amplitude of\n", + "high-frequency signals." + ] + }, + { + "cell_type": "markdown", + "id": "visible-estonia", + "metadata": {}, + "source": [ + "By applying the laws of circuit analysis, we can derive a differential\n", + "equation that describes the behavior of this system. By solving the\n", + "differential equation, we can predict the effect of this circuit on any input signal.\n", + "\n", + "Suppose we are given $V_{in}$ and $V_{out}$ at a particular instant in\n", + "time. By Ohm's law, which is a simple model of the behavior of\n", + "resistors, the instantaneous current through the resistor is:\n", + "\n", + "$$I_R = (V_{in} - V_{out}) / R$$ \n", + "\n", + "where $R$ is resistance in ohms ($\\Omega$).\n", + "\n", + "Assuming that no current flows through the output of the circuit,\n", + "Kirchhoff's current law implies that the current through the capacitor\n", + "is: \n", + "\n", + "$$I_C = I_R$$ " + ] + }, + { + "cell_type": "markdown", + "id": "parallel-radical", + "metadata": {}, + "source": [ + "According to a simple model of the behavior of\n", + "capacitors, current through the capacitor causes a change in the voltage across the capacitor: \n", + "\n", + "$$I_C = C \\frac{d V_{out}}{dt}$$ \n", + "\n", + "where $C$ is capacitance in farads (F). Combining these equations yields a differential equation for $V_{out}$:\n", + "\n", + "$$\\frac{d V_{out}}{dt} = \\frac{V_{in} - V_{out}}{R C}$$ \n", + "\n", + "In the repository for this book, you will find a notebook, *filter.ipynb*, which contains starter code for this case study. You can download it from or run it on Colab at .\n", + "Follow the instructions to simulate the low-pass filter for input signals like this:\n", + "\n", + "$$V_{in}(t) = A \\cos (2 \\pi f t)$$ \n", + "\n", + "where $A$ is the amplitude of the input signal, say 5 V, and $f$ is the frequency of the signal in Hz." + ] + }, + { + "cell_type": "markdown", + "id": "violent-directive", + "metadata": {}, + "source": [ + "## Thermal Behavior of a Wall\n", + "\n", + "This case study is based on a paper that models the thermal behavior of a brick wall with the goal of understanding the \"performance gap between the expected energy use of buildings and their measured energy use\".\n", + "\n", + "The following figure shows the scenario and their model of the wall:\n", + "\n", + "![Model of a wall as a series of thermal insulators](https://github.com/AllenDowney/ModSim/raw/main/figs/wall_model.png)\n", + "\n", + "On the interior and exterior surfaces of the wall, they measure\n", + "temperature and heat flux (rate of heat flow) over a period of three days. They model the wall using two thermal masses connected to the surfaces, and to each other, by thermal resistors." + ] + }, + { + "cell_type": "markdown", + "id": "entire-stations", + "metadata": {}, + "source": [ + "The primary methodology of the paper is a statistical method for inferring the parameters of the system (two thermal masses and three thermal resistances).\n", + "\n", + "The primary result is a comparison of two models: the one shown here with two thermal masses, and a simpler model with only one thermal mass. They find that the two-mass model is able to reproduce the measured fluxes substantially better.\n", + "\n", + "For this case study we will implement their model and run it with the\n", + "estimated parameters from the paper, and then use `leastsq` to see\n", + "if we can find parameters that yield lower errors.\n", + "\n", + "In the repository for this book, you will find a notebook, *wall.ipynb* with the code and results for this case study.\n", + "You can download it from or run it on Colab at .\n", + "\n", + "The paper this case study is based on is\n", + "Gori, Marincioni, Biddulph, Elwell, \"Inferring the thermal resistance and effective thermal mass distribution of a wall from in situ measurements to characterise heat transfer at both the interior and exterior surfaces\", *Energy and Buildings*, 2017, available from .\n", + "\n", + "The authors put their paper under a Creative Commons license and made their data available at . I thank them for their commitment to open, reproducible science, which made this case study possible." + ] + }, + { + "cell_type": "markdown", + "id": "duplicate-joshua", + "metadata": {}, + "source": [ + "## HIV\n", + "\n", + "During the initial phase of HIV infection, the concentration of the virus in the bloodstream typically increases quickly and then decreases.\n", + "The most obvious explanation for the decline is an immune response that destroys the virus or controls its replication.\n", + "However, at least in some patients, the decline occurs even without any detectable immune response.\n", + "\n", + "In 1996 Andrew Phillips proposed another explanation for the decline in this paper: \"Reduction of HIV Concentration During Acute Infection: Independence from a Specific Immune Response\", available from ).\n", + "\n", + "Phillips presents a system of differential equations that models the concentrations of the HIV virus and the CD4 cells it infects.\n", + "The model does not include an immune response; nevertheless, it demonstrates behavior that is qualitatively similar to what is seen in patients during the first few weeks after infection.\n", + "\n", + "His conclusion is that the observed decline in the concentration of HIV might not be caused by an immune response; it could be due to the dynamic interaction between HIV and the cells it infects.\n", + "\n", + "In the repository for this book, you will find a notebook, *hiv_model.ipynb*, which you can use to implement Phillips's model and consider whether it does the work it is meant to do.\n", + "You can download it from or run it on Colab at ." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "international-button", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap20.ipynb b/chapters/chap20.ipynb new file mode 100644 index 000000000..e17f53ef5 --- /dev/null +++ b/chapters/chap20.ipynb @@ -0,0 +1,735 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "funded-utilization", + "metadata": {}, + "source": [ + "# The Empire State Building Strikes Back" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "electoral-turkey", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " from pint import UnitRegistry\n", + "except ImportError:\n", + " !pip install pint\n", + " \n", + "# import units\n", + "from pint import UnitRegistry\n", + "units = UnitRegistry()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "removable-zoning", + "metadata": { + "tags": [] + }, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "embedded-gentleman", + "metadata": {}, + "source": [ + "So far the differential equations we've worked with have been *first order*, which means they involve only first derivatives. In this\n", + "chapter, we turn our attention to *second order* differential equations, which can involve both first and second derivatives.\n", + "\n", + "We'll revisit the falling penny example from Chapter 1, and use `run_solve_ivp` to find the position and velocity of the penny as it falls, with and without air resistance." + ] + }, + { + "cell_type": "markdown", + "id": "isolated-louis", + "metadata": {}, + "source": [ + "## Newton's Second Law of Motion\n", + "\n", + "First order differential equations (DEs) can be written \n", + "\n", + "$$\\frac{dy}{dx} = G(x, y)$$ \n", + "\n", + "where $G$ is some function of $x$ and $y$ (see ). Second order DEs can be written \n", + "\n", + "$$\\frac{d^2y}{dx^2} = H(x, y, \\frac{dy}{dt})$$\n", + "\n", + "where $H$ is a function of $x$, $y$, and $dy/dx$.\n", + "\n", + "In this chapter, we will work with one of the most famous and useful\n", + "second order DEs, Newton's second law of motion: \n", + "\n", + "$$F = m a$$ \n", + "\n", + "where $F$ is a force or the total of a set of forces, $m$ is the mass of a moving object, and $a$ is its acceleration." + ] + }, + { + "cell_type": "markdown", + "id": "drawn-symphony", + "metadata": {}, + "source": [ + "Newton's law might not look like a differential equation, until we\n", + "realize that acceleration, $a$, is the second derivative of position,\n", + "$y$, with respect to time, $t$. With the substitution\n", + "\n", + "$$a = \\frac{d^2y}{dt^2}$$ \n", + "\n", + "Newton's law can be written\n", + "\n", + "$$\\frac{d^2y}{dt^2} = F / m$$ \n", + "\n", + "And that's definitely a second order DE.\n", + "In general, $F$ can be a function of time, position, and velocity." + ] + }, + { + "cell_type": "markdown", + "id": "swiss-vietnam", + "metadata": {}, + "source": [ + "Of course, this \"law\" is really a model in the sense that it is a\n", + "simplification of the real world. Although it is often approximately\n", + "true:\n", + "\n", + "- It only applies if $m$ is constant. If mass depends on time,\n", + " position, or velocity, we have to use a more general form of\n", + " Newton's law (see ).\n", + "\n", + "- It is not a good model for very small things, which are better\n", + " described by another model, quantum mechanics.\n", + "\n", + "- And it is not a good model for things moving very fast, which are\n", + " better described by yet another model, relativistic mechanics.\n", + "\n", + "However, for medium-sized things with constant mass, moving at\n", + "medium-sized speeds, Newton's model is extremely useful. If we can\n", + "quantify the forces that act on such an object, we can predict how it\n", + "will move." + ] + }, + { + "cell_type": "markdown", + "id": "coordinate-three", + "metadata": {}, + "source": [ + "## Dropping Pennies\n", + "\n", + "As a first example, let's get back to the penny falling from the Empire State Building, which we considered in Chapter 1. We will implement two models of this system: first without air resistance, then with.\n", + "\n", + "Given that the Empire State Building is 381 m high, and assuming that\n", + "the penny is dropped from a standstill, the initial conditions are:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "compatible-increase", + "metadata": {}, + "outputs": [], + "source": [ + "init = State(y=381, v=0)" + ] + }, + { + "cell_type": "markdown", + "id": "intellectual-radiation", + "metadata": {}, + "source": [ + "where `y` is height above the sidewalk and `v` is velocity. \n", + "\n", + "I'll put the initial conditions in a `System` object, along with the magnitude of acceleration due to gravity, `g`, and the duration of the simulations, `t_end`." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "reverse-authorization", + "metadata": {}, + "outputs": [], + "source": [ + "system = System(init=init, \n", + " g=9.8, \n", + " t_end=10)" + ] + }, + { + "cell_type": "markdown", + "id": "heavy-boards", + "metadata": {}, + "source": [ + "Now we need a slope function, and here's where things get tricky. As we have seen, `run_solve_ivp` can solve systems of first order DEs, but Newton's law is a second order DE. However, if we recognize that\n", + "\n", + "1. Velocity, $v$, is the derivative of position, $dy/dt$, and\n", + "\n", + "2. Acceleration, $a$, is the derivative of velocity, $dv/dt$,\n", + "\n", + "we can rewrite Newton's law as a system of first order ODEs:\n", + "\n", + "$$\\frac{dy}{dt} = v$$ \n", + "\n", + "$$\\frac{dv}{dt} = a$$ \n", + "\n", + "And we can translate those\n", + "equations into a slope function:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "occupied-mercury", + "metadata": {}, + "outputs": [], + "source": [ + "def slope_func(t, state, system):\n", + " y, v = state\n", + "\n", + " dydt = v\n", + " dvdt = -system.g\n", + " \n", + " return dydt, dvdt" + ] + }, + { + "cell_type": "markdown", + "id": "opening-adolescent", + "metadata": {}, + "source": [ + "As usual, the parameters are a time stamp, a `State` object, and a `System` object.\n", + "\n", + "The first line unpacks the state variables, `y` and `v`.\n", + "\n", + "The next two lines compute the derivatives of the state variables, `dydt` and `dvdt`.\n", + "The derivative of position is velocity, and the derivative of velocity is acceleration.\n", + "In this case, $a = -g$, which indicates that acceleration due to gravity is in the direction of decreasing $y$. \n", + "\n", + "`slope_func` returns a sequence containing the two derivatives.\n", + "\n", + "Before calling `run_solve_ivp`, it is a good idea to test the slope\n", + "function with the initial conditions:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "positive-feeling", + "metadata": {}, + "outputs": [], + "source": [ + "dydt, dvdt = slope_func(0, system.init, system)\n", + "dydt, dvdt" + ] + }, + { + "cell_type": "markdown", + "id": "false-charlotte", + "metadata": {}, + "source": [ + "The result is 0 m/s for velocity and -9.8 m/s$^2$ for acceleration.\n", + "\n", + "Now we call `run_solve_ivp` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "lovely-management", + "metadata": {}, + "outputs": [], + "source": [ + "results, details = run_solve_ivp(system, slope_func)\n", + "details.message" + ] + }, + { + "cell_type": "markdown", + "id": "ranging-lingerie", + "metadata": {}, + "source": [ + "`results` is a `TimeFrame` with two columns: `y` contains the height of the penny; `v` contains its velocity.\n", + "Here are the first few rows." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "assisted-swimming", + "metadata": {}, + "outputs": [], + "source": [ + "results.head()" + ] + }, + { + "cell_type": "markdown", + "id": "solved-chambers", + "metadata": {}, + "source": [ + "We can plot the results like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "authorized-barrier", + "metadata": {}, + "outputs": [], + "source": [ + "results.y.plot()\n", + "\n", + "decorate(xlabel='Time (s)',\n", + " ylabel='Position (m)')" + ] + }, + { + "cell_type": "markdown", + "id": "differential-airfare", + "metadata": {}, + "source": [ + "Since acceleration is constant, velocity increases linearly and position decreases quadratically; as a result, the height curve is a parabola.\n", + "\n", + "The last value of `results.y` is negative, which means we ran the simulation too long. " + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "protected-fiber", + "metadata": {}, + "outputs": [], + "source": [ + "results.iloc[-1].y" + ] + }, + { + "cell_type": "markdown", + "id": "metallic-tamil", + "metadata": {}, + "source": [ + "One way to solve this problem is to use the results to\n", + "estimate the time when the penny hits the sidewalk.\n", + "\n", + "The ModSim library provides `crossings`, which takes a `TimeSeries` and a value, and returns a sequence of times when the series passes through the value. We can find the time when the height of the penny is `0` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "japanese-clear", + "metadata": {}, + "outputs": [], + "source": [ + "t_crossings = crossings(results.y, 0)\n", + "t_crossings" + ] + }, + { + "cell_type": "markdown", + "id": "demonstrated-emission", + "metadata": {}, + "source": [ + "The result is an array with a single value, 8.818 s. Now, we could run\n", + "the simulation again with `t_end = 8.818`, but there's a better way." + ] + }, + { + "cell_type": "markdown", + "id": "blind-dominant", + "metadata": {}, + "source": [ + "## Events\n", + "\n", + "As an option, `run_solve_ivp` can take an *event function*, which\n", + "detects an \"event\", like the penny hitting the sidewalk, and ends the\n", + "simulation.\n", + "\n", + "Event functions take the same parameters as slope functions, `t`, `state`, and `system`. They should return a value that passes through `0` when the event occurs. Here's an event function that detects the penny hitting the sidewalk:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "comfortable-simple", + "metadata": {}, + "outputs": [], + "source": [ + "def event_func(t, state, system):\n", + " y, v = state\n", + " return y" + ] + }, + { + "cell_type": "markdown", + "id": "closing-vehicle", + "metadata": {}, + "source": [ + "The return value is the height of the penny, `y`, which passes through\n", + "`0` when the penny hits the sidewalk.\n", + "\n", + "We pass the event function to `run_solve_ivp` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "exotic-shareware", + "metadata": {}, + "outputs": [], + "source": [ + "results, details = run_solve_ivp(system, slope_func,\n", + " events=event_func)\n", + "details.message" + ] + }, + { + "cell_type": "markdown", + "id": "recreational-blair", + "metadata": {}, + "source": [ + "Then we can get the flight time like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "appropriate-roberts", + "metadata": {}, + "outputs": [], + "source": [ + "t_end = results.index[-1]\n", + "t_end" + ] + }, + { + "cell_type": "markdown", + "id": "pediatric-portal", + "metadata": {}, + "source": [ + "And the final velocity like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "orange-retro", + "metadata": {}, + "outputs": [], + "source": [ + "y, v = results.iloc[-1]\n", + "y, v" + ] + }, + { + "cell_type": "markdown", + "id": "cleared-jamaica", + "metadata": {}, + "source": [ + "If there were no air resistance, the penny would hit the sidewalk (or someone's head) at about 86 m/s. So it's a good thing there is air resistance." + ] + }, + { + "cell_type": "markdown", + "id": "induced-albert", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "In this chapter, we wrote Newton's second law, which is a second order DE, as a system of first order DEs.\n", + "Then we used `run_solve_ivp` to simulate a penny dropping from the Empire State Building in the absence of air resistance.\n", + "And we used an event function to stop the simulation when the penny reaches the sidewalk.\n", + "\n", + "In the next chapter we'll add air resistance to the model.\n", + "But first you might want to work on this exercise." + ] + }, + { + "cell_type": "markdown", + "id": "operational-bhutan", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "You can access the notebooks at ." + ] + }, + { + "cell_type": "markdown", + "id": "straight-johns", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + "Here's a question from the web site *Ask an Astronomer* (see http://curious.astro.cornell.edu/about-us/39-our-solar-system/the-earth/other-catastrophes/57-how-long-would-it-take-the-earth-to-fall-into-the-sun-intermediate):\n", + "\n", + "> \"If the Earth suddenly stopped orbiting the Sun, I know eventually it would be pulled in by the Sun's gravity and hit it. How long would it take the Earth to hit the Sun? I imagine it would go slowly at first and then pick up speed.\"\n", + "\n", + "Use `run_solve_ivp` to answer this question.\n", + "\n", + "Here are some suggestions about how to proceed:\n", + "\n", + "1. Look up the Law of Universal Gravitation and any constants you need. I suggest you work entirely in SI units: meters, kilograms, and Newtons.\n", + "\n", + "2. When the distance between the Earth and the Sun gets small, this system behaves badly, so you should use an event function to stop when the surface of Earth reaches the surface of the Sun.\n", + "\n", + "3. Express your answer in days, and plot the results as millions of kilometers versus days.\n", + "\n", + "If you read the reply by Dave Rothstein, you will see other ways to solve the problem, and a good discussion of the modeling decisions behind them.\n", + "\n", + "You might also be interested to know that it's not that easy to get to the Sun; see https://www.theatlantic.com/science/archive/2018/08/parker-solar-probe-launch-nasa/567197/." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "forbidden-distributor", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "former-taxation", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "oriental-riverside", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "radio-reproduction", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "heavy-cologne", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "little-electric", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "continental-details", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "suitable-traveler", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "upper-victory", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "transparent-treat", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "brutal-woman", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "gentle-burst", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "comfortable-galaxy", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "satisfactory-latitude", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "significant-rebound", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "listed-shelter", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap21.ipynb b/chapters/chap21.ipynb new file mode 100644 index 000000000..32bc62f1e --- /dev/null +++ b/chapters/chap21.ipynb @@ -0,0 +1,761 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "excited-advance", + "metadata": {}, + "source": [ + "# Drag" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "plastic-trigger", + "metadata": { + "tags": [] + }, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "superb-february", + "metadata": {}, + "source": [ + "In the previous chapter we simulated a penny falling in a vacuum, that\n", + "is, without air resistance. But the computational framework we used is\n", + "very general; it is easy to add additional forces, including drag.\n", + "\n", + "In this chapter, I present a model of drag force and add it to the\n", + "simulation." + ] + }, + { + "cell_type": "markdown", + "id": "interesting-freeware", + "metadata": {}, + "source": [ + "## Drag Force\n", + "\n", + "As an object moves through a fluid, like air, the object applies force\n", + "to the air and, in accordance with Newton's third law of motion, the air applies an equal and opposite force to the object (see\n", + ").\n", + "\n", + "The direction of this *drag force* is opposite the direction of\n", + "travel, and its magnitude is given by the drag equation (see\n", + "): \n", + "\n", + "$$F_d = \\frac{1}{2}~\\rho~v^2~C_d~A$$\n", + "\n", + "where\n", + "\n", + "- $F_d$ is force due to drag, in newtons (N), which are the SI units of force. A newton is 1 kg m/s$^2$.\n", + "\n", + "- $\\rho$ is the density of the fluid in kg/m$^3$.\n", + "\n", + "- $v$ is the magnitude of velocity in m/s.\n", + "\n", + "- $A$ is the *reference area* of the object, in m$^2$. In this\n", + " context, the reference area is the projected frontal area, that is, the visible area of the object as seen from a point on its line of\n", + " travel (and far away).\n", + "\n", + "- $C_d$ is the *drag coefficient*, a dimensionless quantity that\n", + " depends on the shape of the object (including length but not frontal area), its surface properties, and how it interacts with the fluid." + ] + }, + { + "cell_type": "markdown", + "id": "unable-scheduling", + "metadata": {}, + "source": [ + "For objects moving at moderate speeds through air, typical drag\n", + "coefficients are between 0.1 and 1.0, with blunt objects at the high end of the range and streamlined objects at the low end (see\n", + ").\n", + "\n", + "For simple geometric objects we can sometimes guess the drag coefficient with reasonable accuracy; for more complex objects we usually have to take measurements and estimate $C_d$ from data.\n", + "\n", + "Of course, the drag equation is itself a model, based on the assumption that $C_d$ does not depend on the other terms in the equation: density, velocity, and area. For objects moving in air at moderate speeds (below 45 mph or 20 m/s), this model might be good enough, but we will revisit this assumption in the next chapter.\n", + "\n", + "For the falling penny, we can use measurements to estimate $C_d$. In\n", + "particular, we can measure *terminal velocity*, $v_{term}$, which is\n", + "the speed where drag force equals force due to gravity:\n", + "\n", + "$$\\frac{1}{2}~\\rho~v_{term}^2~C_d~A = m g$$ \n", + "\n", + "where $m$ is the mass of the object and $g$ is acceleration due to gravity. Solving this equation for\n", + "$C_d$ yields: \n", + "\n", + "$$C_d = \\frac{2~m g}{\\rho~v_{term}^2~A}$$ \n", + "\n", + "According to *Mythbusters*, the terminal velocity of a penny is between 35 and 65 mph (see ). Using the low end of their range, 40 mph or about 18 m/s, the estimated value of $C_d$ is 0.44, which is close to the drag coefficient of a smooth sphere.\n", + "\n", + "Now we are ready to add air resistance to the model. But first I want to introduce one more computational tool, the `Params` object." + ] + }, + { + "cell_type": "markdown", + "id": "human-cloud", + "metadata": {}, + "source": [ + "## The Params Object\n", + "\n", + "As the number of system parameters increases, and as we need to do more work to compute them, we will find it useful to define a `Params` object to contain the quantities we need to make a `System` object. `Params` objects are similar to `System` objects, and we initialize them the same way.\n", + "\n", + "Here's the `Params` object for the falling penny:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "established-knitting", + "metadata": {}, + "outputs": [], + "source": [ + "params = Params(\n", + " mass = 0.0025, # kg\n", + " diameter = 0.019, # m\n", + " rho = 1.2, # kg/m**3\n", + " g = 9.8, # m/s**2\n", + " v_init = 0, # m / s\n", + " v_term = 18, # m / s\n", + " height = 381, # m\n", + " t_end = 30, # s\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "instrumental-gross", + "metadata": {}, + "source": [ + "The mass and diameter are from . The density\n", + "of air depends on temperature, barometric pressure (which depends on\n", + "altitude), humidity, and composition (see ). \n", + "I chose a value that might be typical in New York City at 20 °C.\n", + "\n", + "Here's a version of `make_system` that takes the `Params` object and computes the inital state, `init`, the area, and the coefficient of drag.\n", + "Then it returns a `System` object with the quantities we'll need for the simulation. " + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "published-jesus", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import pi\n", + "\n", + "def make_system(params):\n", + " init = State(y=params.height, v=params.v_init)\n", + "\n", + " area = pi * (params.diameter/2)**2\n", + "\n", + " C_d = (2 * params.mass * params.g / \n", + " (params.rho * area * params.v_term**2))\n", + "\n", + " return System(init=init,\n", + " area=area,\n", + " C_d=C_d,\n", + " mass=params.mass,\n", + " rho=params.rho,\n", + " g=params.g,\n", + " t_end=params.t_end)" + ] + }, + { + "cell_type": "markdown", + "id": "personalized-kruger", + "metadata": {}, + "source": [ + "And here's how we call it." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "executive-protection", + "metadata": {}, + "outputs": [], + "source": [ + "system = make_system(params)" + ] + }, + { + "cell_type": "markdown", + "id": "portable-carbon", + "metadata": {}, + "source": [ + "Based on the mass and diameter of the penny, the density of air, and acceleration due to gravity, and the observed terminal velocity, we estimate that the coefficient of drag is about 0.44." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "quick-cedar", + "metadata": {}, + "outputs": [], + "source": [ + "system.C_d" + ] + }, + { + "cell_type": "markdown", + "id": "inner-telescope", + "metadata": {}, + "source": [ + "It might not be obvious why it is useful to create a `Params` object just to create a `System` object.\n", + "In fact, if we run only one simulation, it might not be useful. But it helps when we want to change or sweep the parameters.\n", + "\n", + "For example, suppose we learn that the terminal velocity of a penny is actually closer to 20 m/s.\n", + "We can make a `Params` object with the new value, and a corresponding `System` object, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "great-crime", + "metadata": {}, + "outputs": [], + "source": [ + "params2 = params.set(v_term=20)" + ] + }, + { + "cell_type": "markdown", + "id": "outstanding-truth", + "metadata": {}, + "source": [ + "The result from `set` is a new `Params` object that is identical to the original except for the given value of `v_term`. \n", + "If we pass `params2` to `make_system`, we see that it computes a different value of `C_d`." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "independent-trace", + "metadata": {}, + "outputs": [], + "source": [ + "system2 = make_system(params2)\n", + "system2.C_d" + ] + }, + { + "cell_type": "markdown", + "id": "charged-explorer", + "metadata": {}, + "source": [ + "If the terminal velocity of the penny is 20 m/s, rather than 18 m/s, that implies that the coefficient of drag is 0.36, rather than 0.44.\n", + "And that makes sense, since lower drag implies faster terminal velocity.\n", + "\n", + "Using `Params` objects to make `System` objects helps make sure that relationships like this are consistent. And since we are always making new objects, rather than modifying existing objects, we are less likely to make a mistake." + ] + }, + { + "cell_type": "markdown", + "id": "primary-advocate", + "metadata": {}, + "source": [ + "## Simulating the Penny Drop\n", + "\n", + "Now let's get to the simulation. Here's a version of the slope function that includes drag:" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "noble-stick", + "metadata": {}, + "outputs": [], + "source": [ + "def slope_func(t, state, system):\n", + " y, v = state\n", + " rho, C_d, area = system.rho, system.C_d, system.area\n", + " mass, g = system.mass, system.g\n", + " \n", + " f_drag = rho * v**2 * C_d * area / 2\n", + " a_drag = f_drag / mass\n", + " \n", + " dydt = v\n", + " dvdt = -g + a_drag\n", + " \n", + " return dydt, dvdt" + ] + }, + { + "cell_type": "markdown", + "id": "baking-class", + "metadata": {}, + "source": [ + "As usual, the parameters of the slope function are a time stamp, a `State` object, and a `System` object. \n", + "We don't use `t` in this example, but we can't leave it out because when `run_solve_ivp` calls the slope function, it always provides the same arguments, whether they are needed or not.\n", + "\n", + "`f_drag` is force due to drag, based on the drag equation. `a_drag` is\n", + "acceleration due to drag, based on Newton's second law.\n", + "\n", + "To compute total acceleration, we add accelerations due to gravity and\n", + "drag. \n", + "`g` is negated because it is in the direction of decreasing `y`; `a_drag` is positive because it is in the direction of increasing\n", + "`y`. \n", + "In the next chapter we will use `Vector` objects to keep track of\n", + "the direction of forces and add them up in a less error-prone way.\n", + "\n", + "As usual, let's test the slope function with the initial conditions." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "velvet-tunisia", + "metadata": {}, + "outputs": [], + "source": [ + "slope_func(0, system.init, system)" + ] + }, + { + "cell_type": "markdown", + "id": "psychological-style", + "metadata": {}, + "source": [ + "Because the initial velocity is 0, so is the drag force, so the initial acceleration is still `g`. \n", + "\n", + "To stop the simulation when the penny hits the sidewalk, we'll use the\n", + "event function from the previous chapter." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "practical-nowhere", + "metadata": {}, + "outputs": [], + "source": [ + "def event_func(t, state, system):\n", + " y, v = state\n", + " return y" + ] + }, + { + "cell_type": "markdown", + "id": "executive-there", + "metadata": {}, + "source": [ + "Now we can run the simulation like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "liberal-dictionary", + "metadata": {}, + "outputs": [], + "source": [ + "results, details = run_solve_ivp(system, slope_func,\n", + " events=event_func)\n", + "details.message" + ] + }, + { + "cell_type": "markdown", + "id": "incident-paradise", + "metadata": {}, + "source": [ + "Here are the last few time steps:" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "coastal-anthropology", + "metadata": {}, + "outputs": [], + "source": [ + "results.tail()" + ] + }, + { + "cell_type": "markdown", + "id": "suburban-martial", + "metadata": {}, + "source": [ + "The final height is close to 0, as expected.\n", + "\n", + "Interestingly, the final velocity is not exactly terminal velocity, which is a reminder that the simulation results are only approximate.\n", + "\n", + "We can get the flight time from `results`." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "interim-underground", + "metadata": {}, + "outputs": [], + "source": [ + "t_sidewalk = results.index[-1]\n", + "t_sidewalk" + ] + }, + { + "cell_type": "markdown", + "id": "institutional-colors", + "metadata": {}, + "source": [ + "With air resistance, it takes about 22 seconds for the penny to reach the sidewalk.\n", + "\n", + "Here's a plot of position as a function of time." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "small-franchise", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_position(results):\n", + " results.y.plot()\n", + " \n", + " decorate(xlabel='Time (s)',\n", + " ylabel='Position (m)')\n", + " \n", + "plot_position(results)" + ] + }, + { + "cell_type": "markdown", + "id": "plain-phone", + "metadata": {}, + "source": [ + "And velocity as a function of time:" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "analyzed-criticism", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_velocity(results):\n", + "\n", + " results.v.plot(color='C1', label='v')\n", + " \n", + " decorate(xlabel='Time (s)',\n", + " ylabel='Velocity (m/s)')\n", + " \n", + "plot_velocity(results)" + ] + }, + { + "cell_type": "markdown", + "id": "careful-causing", + "metadata": {}, + "source": [ + "From an initial velocity of 0, the penny accelerates downward until it reaches terminal velocity; after that, velocity is constant." + ] + }, + { + "cell_type": "markdown", + "id": "inside-confidence", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This chapter presents a model of drag force, which we use to estimate the coefficient of drag for a penny, and then simulate, one more time, dropping a penny from the Empire State Building.\n", + "\n", + "In the next chapter we'll move from one dimension to two, simulating the flight of a baseball.\n", + "But first you might want to work on these exercises." + ] + }, + { + "cell_type": "markdown", + "id": "planned-endorsement", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "You can access the notebooks at ." + ] + }, + { + "cell_type": "markdown", + "id": "respective-address", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + " Run the simulation with a downward initial velocity that exceeds the penny's terminal velocity.\n", + "\n", + "What do you expect to happen? Plot velocity and position as a function of time, and see if they are consistent with your prediction.\n", + "\n", + "Hint: Use `params.set` to make a new `Params` object with a different initial velocity." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "inclusive-twenty", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "vertical-judge", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "greenhouse-madagascar", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "sudden-details", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "opening-jurisdiction", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "smaller-millennium", + "metadata": {}, + "source": [ + "### Exercise 2\n", + "\n", + " Suppose we drop a quarter from the Empire State Building and find that its flight time is 19.1 seconds. Use this measurement to estimate terminal velocity and coefficient of drag.\n", + "\n", + "You can get the relevant dimensions of a quarter from .\n", + "\n", + "1. Create a `Params` object with new values of `mass` and `diameter`. We don't know `v_term`, so we'll start with the initial guess 18 m/s.\n", + "\n", + "2. Use `make_system` to create a `System` object. \n", + "\n", + "3. Call `run_solve_ivp` to simulate the system. How does the flight time of the simulation compare to the measurement?\n", + "\n", + "4. Try a few different values of `v_term` and see if you can get the simulated flight time close to 19.1 seconds.\n", + "\n", + "5. Optionally, write an error function and use `root_scalar` to improve your estimate.\n", + "\n", + "6. Use your best estimate of `v_term` to compute `C_d`.\n", + "\n", + "Note: I fabricated the \"observed\" flight time, so don't take the results of this exercise too seriously." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "compact-bunny", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "shared-contrary", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "portable-account", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "arabic-shareware", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "valued-literature", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "sufficient-retail", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "comparable-lounge", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "ready-people", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "miniature-remark", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "frozen-termination", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap22.ipynb b/chapters/chap22.ipynb new file mode 100644 index 000000000..0a5f28eec --- /dev/null +++ b/chapters/chap22.ipynb @@ -0,0 +1,1455 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "collaborative-people", + "metadata": {}, + "source": [ + "# Baseball" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "electoral-turkey", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " from pint import UnitRegistry\n", + "except ImportError:\n", + " !pip install pint\n", + " \n", + "# import units\n", + "from pint import UnitRegistry\n", + "units = UnitRegistry()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "plastic-trigger", + "metadata": { + "tags": [] + }, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "advanced-guidance", + "metadata": {}, + "source": [ + "In the previous chapter we modeled an object moving in one dimension, with and without drag. Now let's move on to two dimensions, and baseball!\n", + "\n", + "In this chapter we model the flight of a baseball including the effect\n", + "of air resistance. In the next chapter we use this model to solve an\n", + "optimization problem." + ] + }, + { + "cell_type": "markdown", + "id": "offensive-chinese", + "metadata": {}, + "source": [ + "## Baseball\n", + "\n", + "To model the flight of a baseball, we have to make some decisions. To get started, we'll ignore any spin that might be on the ball, and the resulting Magnus force (see ). Under this assumption, the ball travels in a vertical plane, so we'll run simulations in two dimensions, rather than three.\n", + "\n", + "To model air resistance, we'll need the mass, frontal area, and drag\n", + "coefficient of a baseball. Mass and diameter are easy to find (see\n", + "). Drag coefficient is only a little\n", + "harder; according to *The Physics of Baseball* (see https://books.google.com/books/about/The_Physics_of_Baseball.html?id=4xE4Ngpk_2EC), the drag coefficient of a baseball is approximately 0.33 (with no units).\n", + "\n", + "However, this value *does* depend on velocity. At low velocities it\n", + "might be as high as 0.5, and at high velocities as low as 0.28.\n", + "Furthermore, the transition between these values typically happens\n", + "exactly in the range of velocities we are interested in, between 20 m/s and 40 m/s.\n", + "\n", + "Nevertheless, we'll start with a simple model where the drag coefficient does not depend on velocity; as an exercise at the end of the chapter, you can implement a more detailed model and see what effect it has on the results.\n", + "\n", + "But first we need a new computational tool, the `Vector` object." + ] + }, + { + "cell_type": "markdown", + "id": "worthy-wheel", + "metadata": {}, + "source": [ + "## Vectors\n", + "\n", + "Now that we are working in two dimensions, it will be useful to\n", + "work with *vector quantities*, that is, quantities that represent both a magnitude and a direction. We will use vectors to represent positions, velocities, accelerations, and forces in two and three dimensions.\n", + "\n", + "ModSim provides a function called `Vector` that creates a Pandas `Series` that contains the *components* of the vector.\n", + "In a `Vector` that represents a position in space, the components are the $x$ and $y$ coordinates in 2-D, plus a $z$ coordinate if the `Vector` is in 3-D.\n", + "\n", + "You can create a `Vector` by specifying its components. The following\n", + "`Vector` represents a point 3 units to the right (or east) and 4 units up (or north) from an implicit origin:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "handy-terrain", + "metadata": {}, + "outputs": [], + "source": [ + "A = Vector(3, 4)\n", + "show(A)" + ] + }, + { + "cell_type": "markdown", + "id": "settled-roommate", + "metadata": {}, + "source": [ + "You can access the components of a `Vector` by name using the dot\n", + "operator, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "vocal-latino", + "metadata": {}, + "outputs": [], + "source": [ + "A.x" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "controversial-shower", + "metadata": {}, + "outputs": [], + "source": [ + "A.y" + ] + }, + { + "cell_type": "markdown", + "id": "earlier-contemporary", + "metadata": {}, + "source": [ + "You can also access them by index using brackets, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "digital-channels", + "metadata": {}, + "outputs": [], + "source": [ + "A[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "automated-drove", + "metadata": {}, + "outputs": [], + "source": [ + "A[1]" + ] + }, + { + "cell_type": "markdown", + "id": "grave-burst", + "metadata": {}, + "source": [ + "`Vector` objects support most mathematical operations, including\n", + "addition:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "conditional-latitude", + "metadata": {}, + "outputs": [], + "source": [ + "B = Vector(1, 2)\n", + "show(A + B)" + ] + }, + { + "cell_type": "markdown", + "id": "charming-reviewer", + "metadata": {}, + "source": [ + "And subtraction:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "encouraging-cabinet", + "metadata": {}, + "outputs": [], + "source": [ + "show(A - B)" + ] + }, + { + "cell_type": "markdown", + "id": "combined-command", + "metadata": {}, + "source": [ + "For the definition and graphical interpretation of these operations, see .\n", + "\n", + "We can specify a `Vector` with coordinates `x` and `y`, as in the previous examples.\n", + "Equivalently, we can specify a `Vector` with a magnitude and angle.\n", + "\n", + "*Magnitude* is the length of the vector: if the `Vector` represents a position, magnitude is its distance from the origin; if it represents a velocity, magnitude is its speed.\n", + "\n", + "The *angle* of a `Vector` is its direction, expressed as an angle in radians from the positive $x$ axis. In the Cartesian plane, the angle 0 rad is due east, and the angle $\\pi$ rad is due west.\n", + "\n", + "ModSim provides functions to compute the magnitude and angle of a `Vector`. For example, here are the magnitude and angle of `A`:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "peripheral-tattoo", + "metadata": {}, + "outputs": [], + "source": [ + "mag = vector_mag(A)\n", + "theta = vector_angle(A)\n", + "mag, theta" + ] + }, + { + "cell_type": "markdown", + "id": "great-advice", + "metadata": {}, + "source": [ + "The magnitude is 5 because the length of `A` is the hypotenuse of a 3-4-5 triangle.\n", + "\n", + "The result from `vector_angle` is in radians.\n", + "Most Python functions, like `sin` and `cos`, work with radians, \n", + "but many people find it more natural to work with degrees. \n", + "Fortunately, NumPy provides a function to convert radians to degrees:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "strange-cleaning", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import rad2deg\n", + "\n", + "angle = rad2deg(theta)\n", + "angle" + ] + }, + { + "cell_type": "markdown", + "id": "assured-cutting", + "metadata": {}, + "source": [ + "And a function to convert degrees to radians:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "cellular-community", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import deg2rad\n", + "\n", + "theta = deg2rad(angle)\n", + "theta" + ] + }, + { + "cell_type": "markdown", + "id": "responsible-gentleman", + "metadata": {}, + "source": [ + "To avoid confusion, I'll use the variable name `angle` for a value in degrees and `theta` for a value in radians.\n", + "\n", + "If you are given an angle and magnitude, you can make a `Vector` using\n", + "`pol2cart`, which converts from polar to Cartesian coordinates. For example, here's a new `Vector` with the same angle and magnitude of `A`:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "monetary-firmware", + "metadata": {}, + "outputs": [], + "source": [ + "x, y = pol2cart(theta, mag)\n", + "C = Vector(x, y)\n", + "show(C)" + ] + }, + { + "cell_type": "markdown", + "id": "lucky-plastic", + "metadata": {}, + "source": [ + "Another way to represent the direction of `A` is a *unit vector*,\n", + "which is a vector with magnitude 1 that points in the same direction as\n", + "`A`. You can compute a unit vector by dividing a vector by its\n", + "magnitude:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "explicit-piano", + "metadata": {}, + "outputs": [], + "source": [ + "show(A / vector_mag(A))" + ] + }, + { + "cell_type": "markdown", + "id": "respected-oliver", + "metadata": {}, + "source": [ + "ModSim provides a function that does the same thing, called `vector_hat` because unit vectors are conventionally decorated with a hat, like this: $\\hat{A}$." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "relative-republic", + "metadata": {}, + "outputs": [], + "source": [ + "A_hat = vector_hat(A)\n", + "show(A_hat)" + ] + }, + { + "cell_type": "markdown", + "id": "reduced-celebration", + "metadata": {}, + "source": [ + "Now let's get back to the game." + ] + }, + { + "cell_type": "markdown", + "id": "similar-local", + "metadata": {}, + "source": [ + "## Simulating Baseball Flight\n", + "\n", + "Let's simulate the flight of a baseball that is batted from home plate\n", + "at an angle of 45° and initial speed 40 m/s. We'll use the center of home plate as the origin, a horizontal x-axis (parallel to the ground), and a vertical y-axis (perpendicular to the ground). The initial height is 1 m.\n", + "\n", + "Here's a `Params` object with the parameters we'll need." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "narrative-latest", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "params = Params(\n", + " x = 0, # m\n", + " y = 1, # m\n", + " angle = 45, # degree\n", + " speed = 40, # m / s\n", + "\n", + " mass = 145e-3, # kg \n", + " diameter = 73e-3, # m \n", + " C_d = 0.33, # dimensionless\n", + "\n", + " rho = 1.2, # kg/m**3\n", + " g = 9.8, # m/s**2\n", + " t_end = 10, # s\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "metric-collins", + "metadata": {}, + "source": [ + "I got the mass and diameter of the baseball from Wikipedia (see ) and the coefficient of drag from *The Physics of Baseball* (see ):\n", + "The density of air, `rho`, is based on a temperature of 20 °C at sea level (see ). \n", + "As usual, `g` is acceleration due to gravity.\n", + "`t_end` is 10 seconds, which is long enough for the ball to land on the ground.\n", + "\n", + "The following function uses these quantities to make a `System` object." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "bored-billy", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from numpy import pi, deg2rad\n", + "\n", + "def make_system(params):\n", + " \n", + " # convert angle to radians\n", + " theta = deg2rad(params.angle)\n", + " \n", + " # compute x and y components of velocity\n", + " vx, vy = pol2cart(theta, params.speed)\n", + " \n", + " # make the initial state\n", + " init = State(x=params.x, y=params.y, vx=vx, vy=vy)\n", + " \n", + " # compute the frontal area\n", + " area = pi * (params.diameter/2)**2\n", + "\n", + " return System(params,\n", + " init = init,\n", + " area = area)" + ] + }, + { + "cell_type": "markdown", + "id": "conscious-template", + "metadata": {}, + "source": [ + "`make_system` uses `deg2rad` to convert `angle` to radians and\n", + "`pol2cart` to compute the $x$ and $y$ components of the initial\n", + "velocity.\n", + "\n", + "`init` is a `State` object with four state variables:\n", + "\n", + "* `x` and `y` are the components of position.\n", + "\n", + "* `vx` and `vy` are the components of velocity.\n", + "\n", + "When we call `System`, we pass `params` as the first argument, which means that the variables in `params` are copied to the new `System` object.\n", + "\n", + "Here's how we make the `System` object." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "ethical-donna", + "metadata": {}, + "outputs": [], + "source": [ + "system = make_system(params)" + ] + }, + { + "cell_type": "markdown", + "id": "convinced-fellow", + "metadata": {}, + "source": [ + "And here's the initial `State`:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "legitimate-gossip", + "metadata": {}, + "outputs": [], + "source": [ + "show(system.init)" + ] + }, + { + "cell_type": "markdown", + "id": "occasional-given", + "metadata": {}, + "source": [ + "## Drag Force\n", + "\n", + "Next we need a function to compute drag force:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "legal-terminal", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def drag_force(V, system):\n", + " rho, C_d, area = system.rho, system.C_d, system.area\n", + " \n", + " mag = rho * vector_mag(V)**2 * C_d * area / 2\n", + " direction = -vector_hat(V)\n", + " f_drag = mag * direction\n", + " return f_drag" + ] + }, + { + "cell_type": "markdown", + "id": "false-confusion", + "metadata": {}, + "source": [ + "This function takes `V` as a `Vector` and returns `f_drag` as a\n", + "`Vector`. \n", + "\n", + "* It uses `vector_mag` to compute the magnitude of `V`, \n", + "and the drag equation to compute the magnitude of the drag force, `mag`.\n", + "\n", + "* Then it uses `vector_hat` to compute `direction`, which is a unit vector in the opposite direction of `V`.\n", + "\n", + "* Finally, it computes the drag force vector by multiplying `mag` and `direction`.\n", + "\n", + "We can test it like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "frank-chick", + "metadata": {}, + "outputs": [], + "source": [ + "vx, vy = system.init.vx, system.init.vy\n", + "V_test = Vector(vx, vy)\n", + "f_drag = drag_force(V_test, system)\n", + "show(f_drag)" + ] + }, + { + "cell_type": "markdown", + "id": "ultimate-upgrade", + "metadata": {}, + "source": [ + "The result is a `Vector` that represents the drag force on the baseball, in Newtons, under the initial conditions.\n", + "\n", + "Now we can add drag to the slope function." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "suitable-salem", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def slope_func(t, state, system):\n", + " x, y, vx, vy = state\n", + " mass, g = system.mass, system.g\n", + " \n", + " V = Vector(vx, vy)\n", + " a_drag = drag_force(V, system) / mass\n", + " a_grav = g * Vector(0, -1)\n", + " \n", + " A = a_grav + a_drag\n", + " \n", + " return V.x, V.y, A.x, A.y" + ] + }, + { + "cell_type": "markdown", + "id": "scheduled-courage", + "metadata": {}, + "source": [ + "As usual, the parameters of the slope function are a time stamp, a `State` object, and a `System` object. \n", + "We don't use `t` in this example, but we can't leave it out because when `run_solve_ivp` calls the slope function, it always provides the same arguments, whether they are needed or not.\n", + "\n", + "`slope_func` unpacks the `State` object into variables `x`, `y`, `vx`, and `vy`.\n", + "Then it packs `vx` and `vy` into a `Vector`, which it uses to compute acceleration due to drag, `a_drag`.\n", + "\n", + "To represent acceleration due to gravity, it makes a `Vector` with magnitude `g` in the negative $y$ direction.\n", + "\n", + "The total acceleration of the baseball, `A`, is the sum of accelerations due to gravity and drag." + ] + }, + { + "cell_type": "markdown", + "id": "enclosed-favorite", + "metadata": {}, + "source": [ + "The return value is a sequence that contains:\n", + "\n", + "* The components of velocity, `V.x` and `V.y`.\n", + "\n", + "* The components of acceleration, `A.x` and `A.y`.\n", + "\n", + "These components represent the slope of the state variables, because `V` is the derivative of position and `A` is the derivative of velocity." + ] + }, + { + "cell_type": "markdown", + "id": "crude-parcel", + "metadata": {}, + "source": [ + "As always, we can test the slope function by running it with the initial conditions:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "closing-simon", + "metadata": {}, + "outputs": [], + "source": [ + "slope_func(0, system.init, system)" + ] + }, + { + "cell_type": "markdown", + "id": "adolescent-westminster", + "metadata": {}, + "source": [ + "Using vectors to represent forces and accelerations makes the code\n", + "concise, readable, and less error-prone. In particular, when we add\n", + "`a_grav` and `a_drag`, the directions are likely to be correct, because they are encoded in the `Vector` objects." + ] + }, + { + "cell_type": "markdown", + "id": "regulated-railway", + "metadata": {}, + "source": [ + "## Adding an Event Function\n", + "\n", + "We're almost ready to run the simulation. The last thing we need is an event function that stops when the ball hits the ground." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "brief-level", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def event_func(t, state, system):\n", + " x, y, vx, vy = state\n", + " return y" + ] + }, + { + "cell_type": "markdown", + "id": "novel-farmer", + "metadata": {}, + "source": [ + "The event function takes the same parameters as the slope function, and returns the $y$ coordinate of position. When the $y$ coordinate passes through 0, the simulation stops.\n", + "\n", + "As we did with `slope_func`, we can test `event_func` with the initial conditions." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "threatened-alberta", + "metadata": {}, + "outputs": [], + "source": [ + "event_func(0, system.init, system)" + ] + }, + { + "cell_type": "markdown", + "id": "unlikely-dressing", + "metadata": {}, + "source": [ + "Here's how we run the simulation with this event function:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "special-background", + "metadata": {}, + "outputs": [], + "source": [ + "results, details = run_solve_ivp(system, slope_func,\n", + " events=event_func)\n", + "details.message" + ] + }, + { + "cell_type": "markdown", + "id": "iraqi-appeal", + "metadata": {}, + "source": [ + "The message indicates that a \"termination event\" occurred; that is, the simulated ball reached the ground.\n", + "\n", + "`results` is a `TimeFrame` with one row for each time step and one column for each of the state variables.\n", + "Here are the last few rows." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "prospective-external", + "metadata": {}, + "outputs": [], + "source": [ + "results.tail()" + ] + }, + { + "cell_type": "markdown", + "id": "dominant-weekly", + "metadata": {}, + "source": [ + "We can get the flight time like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "medieval-calvin", + "metadata": {}, + "outputs": [], + "source": [ + "flight_time = results.index[-1]\n", + "flight_time" + ] + }, + { + "cell_type": "markdown", + "id": "convenient-heading", + "metadata": {}, + "source": [ + "And the final state like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "gorgeous-survey", + "metadata": {}, + "outputs": [], + "source": [ + "final_state = results.iloc[-1]\n", + "show(final_state)" + ] + }, + { + "cell_type": "markdown", + "id": "needed-aruba", + "metadata": {}, + "source": [ + "The final value of `y` is close to 0, as it should be. The final value of `x` tells us how far the ball flew, in meters." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "failing-bangkok", + "metadata": {}, + "outputs": [], + "source": [ + "x_dist = final_state.x\n", + "x_dist" + ] + }, + { + "cell_type": "markdown", + "id": "growing-england", + "metadata": {}, + "source": [ + "We can also get the final velocity, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "copyrighted-highway", + "metadata": {}, + "outputs": [], + "source": [ + "final_V = Vector(final_state.vx, final_state.vy)\n", + "show(final_V)" + ] + }, + { + "cell_type": "markdown", + "id": "statewide-middle", + "metadata": {}, + "source": [ + "The magnitude of final velocity is the speed of the ball when it lands." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "structured-adams", + "metadata": {}, + "outputs": [], + "source": [ + "vector_mag(final_V)" + ] + }, + { + "cell_type": "markdown", + "id": "vietnamese-diagram", + "metadata": {}, + "source": [ + "The final speed is about 26 m/s, which is substantially slower than the initial speed, 40 m/s." + ] + }, + { + "cell_type": "markdown", + "id": "continuous-quick", + "metadata": {}, + "source": [ + "## Visualizing Trajectories\n", + "\n", + "To visualize the results, we can plot the $x$ and $y$ components of position like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "spare-burst", + "metadata": {}, + "outputs": [], + "source": [ + "results.x.plot(color='C4')\n", + "results.y.plot(color='C2', style='--')\n", + "\n", + "decorate(xlabel='Time (s)',\n", + " ylabel='Position (m)')" + ] + }, + { + "cell_type": "markdown", + "id": "horizontal-bench", + "metadata": {}, + "source": [ + "As expected, the $x$ component increases as the ball moves away from home plate. The $y$ position climbs initially and then descends, falling to 0 m near 5.0 s.\n", + "\n", + "Another way to view the results is to plot the $x$ component on the\n", + "$x$-axis and the $y$ component on the $y$-axis, so the plotted line follows the trajectory of the ball through the plane:" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "dated-browse", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_trajectory(results):\n", + " x = results.x\n", + " y = results.y\n", + " make_series(x, y).plot(label='trajectory')\n", + "\n", + " decorate(xlabel='x position (m)',\n", + " ylabel='y position (m)')\n", + "\n", + "plot_trajectory(results)" + ] + }, + { + "cell_type": "markdown", + "id": "certified-synthetic", + "metadata": {}, + "source": [ + "This way of visualizing the results is called a *trajectory plot* (see ).\n", + "A trajectory plot can be easier to interpret than a time series plot,\n", + "because it shows what the motion of the projectile would look like (at\n", + "least from one point of view). Both plots can be useful, but don't get\n", + "them mixed up! If you are looking at a time series plot and interpreting it as a trajectory, you will be very confused.\n", + "\n", + "Notice that the trajectory is not symmetric.\n", + "With a launch angle of 45°, the landing angle is closer to vertical, about 57° degrees." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "resistant-vegetation", + "metadata": {}, + "outputs": [], + "source": [ + "rad2deg(vector_angle(final_V))" + ] + }, + { + "cell_type": "markdown", + "id": "cosmetic-aircraft", + "metadata": {}, + "source": [ + "## Animating the Baseball\n", + "\n", + "One of the best ways to visualize the results of a physical model is animation. If there are problems with the model, animation can make them apparent.\n", + "\n", + "The ModSimPy library provides `animate`, which takes as parameters a `TimeSeries` and a draw function.\n", + "The draw function should take as parameters a time stamp and a `State`. It should draw a single frame of the animation." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "starting-fabric", + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib.pyplot import plot\n", + "\n", + "xlim = results.x.min(), results.x.max()\n", + "ylim = results.y.min(), results.y.max()\n", + "\n", + "def draw_func(t, state):\n", + " plot(state.x, state.y, 'bo')\n", + " decorate(xlabel='x position (m)',\n", + " ylabel='y position (m)',\n", + " xlim=xlim,\n", + " ylim=ylim)" + ] + }, + { + "cell_type": "markdown", + "id": "sustained-slide", + "metadata": {}, + "source": [ + "Inside the draw function, you should use `decorate` to set the limits of the $x$ and $y$ axes.\n", + "Otherwise `matplotlib` auto-scales the axes, which is usually not what you want.\n", + "\n", + "Now we can run the animation like this:\n", + "\n", + "```\n", + "animate(results, draw_func)\n", + "```\n", + "\n", + "You can see the results when you run the code from this chapter." + ] + }, + { + "cell_type": "markdown", + "id": "incomplete-beijing", + "metadata": { + "tags": [] + }, + "source": [ + "To run the animation, uncomment the following line of code and run the cell." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "prescription-boutique", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# animate(results, draw_func)" + ] + }, + { + "cell_type": "markdown", + "id": "3bede5d5", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This chapter introduces `Vector` objects, which we use to represent position, velocity, and acceleration in two dimensions.\n", + "We also represent forces using vectors, which make it easier to add up forces acting in different directions.\n", + "\n", + "Our ODE solver doesn't work with `Vector` objects, so it takes some work to pack and unpack their components.\n", + "Nevertheless, we were able to run simulations with vectors and display the results.\n", + "\n", + "In the next chapter we'll use these simulations to solve an optimization problem." + ] + }, + { + "cell_type": "markdown", + "id": "lyric-harassment", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "You can access the notebooks at ." + ] + }, + { + "cell_type": "markdown", + "id": "pressing-retrieval", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + " Run the simulation with and without air resistance. How wrong would we be if we ignored drag?" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "optical-weather", + "metadata": {}, + "outputs": [], + "source": [ + "# Hint\n", + "\n", + "system2 = make_system(params.set(C_d=0))" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "acknowledged-belgium", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "spatial-ensemble", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "domestic-apparatus", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "correct-pittsburgh", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "generic-shelter", + "metadata": {}, + "source": [ + "### Exercise 2\n", + "\n", + "The baseball stadium in Denver, Colorado is 1,580 meters above sea level, where the density of air is about 1.0 kg / m$^3$. Compared with the example near sea level, how much farther would a ball travel if hit with the same initial speed and launch angle?" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "global-referral", + "metadata": {}, + "outputs": [], + "source": [ + "# Hint\n", + "\n", + "system3 = make_system(params.set(rho=1.0))" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "appointed-sugar", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "specialized-mediterranean", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "shaped-paragraph", + "metadata": {}, + "source": [ + "### Exercise 3\n", + "\n", + " The model so far is based on the assumption that coefficient of drag does not depend on velocity, but in reality it does. The following figure, from Adair, *The Physics of Baseball*, shows coefficient of drag as a function of velocity (see ).\n", + "\n", + "![Graph of drag coefficient versus velocity](https://github.com/AllenDowney/ModSimPy/raw/master/figs/baseball_drag.png)\n", + "\n", + "I used an online graph digitizer () to extract the data and save it in a CSV file. " + ] + }, + { + "cell_type": "markdown", + "id": "german-niagara", + "metadata": { + "tags": [] + }, + "source": [ + "The following cell downloads the data file." + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "directed-moisture", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://github.com/AllenDowney/ModSim/raw/main/data/' +\n", + " 'baseball_drag.csv')" + ] + }, + { + "cell_type": "markdown", + "id": "imported-chosen", + "metadata": { + "tags": [] + }, + "source": [ + "We can use Pandas to read it." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "fuzzy-register", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from pandas import read_csv\n", + "\n", + "baseball_drag = read_csv('baseball_drag.csv')" + ] + }, + { + "cell_type": "markdown", + "id": "progressive-buyer", + "metadata": { + "tags": [] + }, + "source": [ + "Here are the first few rows." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "returning-fellowship", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "baseball_drag.head()" + ] + }, + { + "cell_type": "markdown", + "id": "bronze-acoustic", + "metadata": { + "tags": [] + }, + "source": [ + "I'll use Pint to convert miles per hour to meters per second." + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "reasonable-swaziland", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "mph_to_mps = (1 * units.mph).to(units.m/units.s).magnitude\n", + "speed = baseball_drag['Velocity in mph'] * mph_to_mps" + ] + }, + { + "cell_type": "markdown", + "id": "industrial-architecture", + "metadata": { + "tags": [] + }, + "source": [ + "I'll put the results in a `Series`." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "attached-shower", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "C_d_series = make_series(speed, baseball_drag['Drag coefficient'])" + ] + }, + { + "cell_type": "markdown", + "id": "amazing-horse", + "metadata": {}, + "source": [ + "Here's what it looks like." + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "id": "christian-camcorder", + "metadata": {}, + "outputs": [], + "source": [ + "C_d_series.plot(label='$C_d$')\n", + "decorate(xlabel='Speed (m/s)', \n", + " ylabel='Coefficient of drag')" + ] + }, + { + "cell_type": "markdown", + "id": "computational-alloy", + "metadata": { + "tags": [] + }, + "source": [ + "And, for use in the slope function, we can make a function that interpolates the data." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "heated-belfast", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "drag_interp = interpolate(C_d_series)\n", + "drag_interp(30)" + ] + }, + { + "cell_type": "markdown", + "id": "reverse-shock", + "metadata": {}, + "source": [ + "Modify the model to include the dependence of `C_d` on velocity, and see how much it affects the results." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "engaged-provision", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "directed-fiber", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "framed-dealer", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "accomplished-elizabeth", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "going-techno", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "brief-saying", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "spare-pregnancy", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "catholic-staff", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "broad-sequence", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "strong-design", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap22.py b/chapters/chap22.py new file mode 100644 index 000000000..a5b5f7451 --- /dev/null +++ b/chapters/chap22.py @@ -0,0 +1,69 @@ +from modsim import * + +params = Params( + x = 0, # m + y = 1, # m + angle = 45, # degree + speed = 40, # m / s + + mass = 145e-3, # kg + diameter = 73e-3, # m + C_d = 0.33, # dimensionless + + rho = 1.2, # kg/m**3 + g = 9.8, # m/s**2 + t_end = 10, # s +) + +from modsim import * + +from numpy import pi, deg2rad + +def make_system(params): + + # convert angle to radians + theta = deg2rad(params.angle) + + # compute x and y components of velocity + vx, vy = pol2cart(theta, params.speed) + + # make the initial state + init = State(x=params.x, y=params.y, vx=vx, vy=vy) + + # compute the frontal area + area = pi * (params.diameter/2)**2 + + return System(params, + init = init, + area = area) + +from modsim import * + +def drag_force(V, system): + rho, C_d, area = system.rho, system.C_d, system.area + + mag = rho * vector_mag(V)**2 * C_d * area / 2 + direction = -vector_hat(V) + f_drag = mag * direction + return f_drag + +from modsim import * + +def slope_func(t, state, system): + x, y, vx, vy = state + mass, g = system.mass, system.g + + V = Vector(vx, vy) + a_drag = drag_force(V, system) / mass + a_grav = g * Vector(0, -1) + + A = a_grav + a_drag + + return V.x, V.y, A.x, A.y + +from modsim import * + +def event_func(t, state, system): + x, y, vx, vy = state + return y + diff --git a/chapters/chap23.ipynb b/chapters/chap23.ipynb new file mode 100644 index 000000000..6d94cd6cf --- /dev/null +++ b/chapters/chap23.ipynb @@ -0,0 +1,629 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "foreign-pepper", + "metadata": {}, + "source": [ + "# Optimal Baseball" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "electoral-turkey", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " from pint import UnitRegistry\n", + "except ImportError:\n", + " !pip install pint\n", + " \n", + "# import units\n", + "from pint import UnitRegistry\n", + "units = UnitRegistry()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "plastic-trigger", + "metadata": { + "tags": [] + }, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "usual-institution", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'chap22.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "quantitative-montana", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from previous notebook\n", + "\n", + "from chap22 import params\n", + "from chap22 import make_system\n", + "from chap22 import slope_func\n", + "from chap22 import event_func" + ] + }, + { + "cell_type": "markdown", + "id": "angry-pledge", + "metadata": {}, + "source": [ + "In the previous chapter we developed a model of the flight of a\n", + "baseball, including gravity and a simple version of drag, but neglecting spin, Magnus force, and the dependence of the coefficient of drag on velocity.\n", + "\n", + "In this chapter we apply that model to an optimization problem. In general, *optimization* is a process for improving a design by searching for the parameters that maximize a benefit or minimize a cost. For example, in this chapter we'll find the angle you should hit a baseball to maximize the distance it travels. And we'll use a new function, called `maximize_scalar` that searches for this angle efficiently." + ] + }, + { + "cell_type": "markdown", + "id": "decent-birth", + "metadata": {}, + "source": [ + "## The Manny Ramirez Problem\n", + "\n", + "Manny Ramirez is a former member of the Boston Red Sox (an American\n", + "baseball team) who was notorious for his relaxed attitude and taste for practical jokes. Our objective in this chapter is to solve the following Manny-inspired problem:\n", + "\n", + "> What is the minimum effort required to hit a home run in Fenway Park?\n", + "\n", + "Fenway Park is a baseball stadium in Boston, Massachusetts. One of its\n", + "most famous features is the \"Green Monster\", which is a wall in left\n", + "field that is unusually close to home plate, only 310 feet away. To\n", + "compensate for the short distance, the wall is unusually high, at 37\n", + "feet (see ).\n", + "\n", + "Starting with `params` from the previous chapter, I'll make a new `Params` object with two additional parameters, `wall_distance` and `wall_height`, in meters." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "finite-warrant", + "metadata": {}, + "outputs": [], + "source": [ + "feet_to_meter = (1 * units.feet).to(units.meter).magnitude\n", + "\n", + "params = params.set(\n", + " wall_distance = 310 * feet_to_meter,\n", + " wall_height = 37 * feet_to_meter,\n", + ")\n", + "\n", + "show(params)" + ] + }, + { + "cell_type": "markdown", + "id": "exposed-diving", + "metadata": {}, + "source": [ + "The answer we want is the minimum speed at which a ball can leave home plate and still go over the Green Monster. We'll proceed in the\n", + "following steps:\n", + "\n", + "1. For a given speed, we'll find the optimal *launch angle*, that is, the angle the ball should leave home plate to maximize its height when it reaches the wall.\n", + "\n", + "2. Then we'll find the minimum speed that clears the wall, given\n", + " that it has the optimal launch angle." + ] + }, + { + "cell_type": "markdown", + "id": "received-diamond", + "metadata": {}, + "source": [ + "## Finding the Range\n", + "\n", + "Suppose we want to find the launch angle that maximizes *range*, that is, the distance the ball travels in the air before landing. We'll use a function in the ModSim library, `maximize_scalar`, which takes a function and finds its maximum.\n", + "\n", + "The function we pass to `maximize_scalar` should take launch angle in degrees, simulate the flight of a ball launched at that angle, and return the distance the ball travels along the $x$ axis." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "forty-knitting", + "metadata": {}, + "outputs": [], + "source": [ + "def range_func(angle, params):\n", + " params = params.set(angle=angle)\n", + " system = make_system(params)\n", + " results, details = run_solve_ivp(system, slope_func,\n", + " events=event_func)\n", + " x_dist = results.iloc[-1].x\n", + " print(angle, x_dist)\n", + " return x_dist" + ] + }, + { + "cell_type": "markdown", + "id": "exact-cigarette", + "metadata": {}, + "source": [ + "`range_func` makes a new `System` object with the given value of\n", + "`angle`. Then it calls `run_solve_ivp` and\n", + "returns the final value of `x` from the results.\n", + "\n", + "We can call `range_func` directly like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "senior-counter", + "metadata": {}, + "outputs": [], + "source": [ + "range_func(45, params)" + ] + }, + { + "cell_type": "markdown", + "id": "paperback-passing", + "metadata": {}, + "source": [ + "With launch angle 45°, the ball lands about 99 meters from home plate.\n", + "\n", + "Now we can sweep a sequence of angles like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "biological-evans", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "angles = linspace(20, 80, 21)\n", + "sweep = SweepSeries()\n", + "\n", + "for angle in angles:\n", + " x_dist = range_func(angle, params)\n", + " sweep[angle] = x_dist" + ] + }, + { + "cell_type": "markdown", + "id": "premium-contribution", + "metadata": {}, + "source": [ + "Here's what the results look like." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "experienced-providence", + "metadata": {}, + "outputs": [], + "source": [ + "sweep.plot()\n", + "\n", + "decorate(xlabel='Launch angle (degrees)',\n", + " ylabel='Range (m)')" + ] + }, + { + "cell_type": "markdown", + "id": "conscious-blade", + "metadata": {}, + "source": [ + "It looks like the range is maximized when the initial angle is near 40°.\n", + "We can find the optimal angle more precisely and more efficiently using `maximize_scalar`, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "invisible-jaguar", + "metadata": {}, + "outputs": [], + "source": [ + "res = maximize_scalar(range_func, params, bounds=[0, 90])" + ] + }, + { + "cell_type": "markdown", + "id": "czech-command", + "metadata": {}, + "source": [ + "The first parameter is the function we want to maximize. The second is\n", + "the range of values we want to search; in this case, it's the range of\n", + "angles from 0° to 90°. \n", + "\n", + "The return value from `maximize_scalar` is an object that contains the\n", + "results, including `x`, which is the angle that yielded the maximum\n", + "range, and `fun`, which is the range when the ball is launched at the optimal angle." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "vocal-nerve", + "metadata": {}, + "outputs": [], + "source": [ + "res" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "figured-uniform", + "metadata": {}, + "outputs": [], + "source": [ + "res.x, res.fun" + ] + }, + { + "cell_type": "markdown", + "id": "shaped-southeast", + "metadata": {}, + "source": [ + "For these parameters, the optimal angle is about 41°, which yields a range of 100 m.\n", + "Now we have what we need to finish the problem; the last step is to find the minimum velocity needed to get the ball over the wall. In the exercises at the end of the chapter, I provide some suggestions. Then it's up to you!" + ] + }, + { + "cell_type": "markdown", + "id": "temporal-extension", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This chapter introduces a new tool, `maximize_scalar`, that provides an efficient way to search for the maximum of a function. We used it to find the launch angle that maximizes the distance a baseball flies through the air, given its initial velocity.\n", + "\n", + "If you enjoy this example, you might be interested in this paper: \"How to hit home runs: Optimum baseball bat swing parameters for maximum range trajectories\", by Sawicki, Hubbard, and Stronge, at .\n", + "\n", + "In the next chapter, we start a new topic: rotation!" + ] + }, + { + "cell_type": "markdown", + "id": "processed-constitution", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "You can access the notebooks at ." + ] + }, + { + "cell_type": "markdown", + "id": "handmade-rhythm", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + "Let's finish off the Manny Ramirez problem:\n", + "\n", + "> What is the minimum effort required to hit a home run in Fenway Park?\n", + "\n", + "Although the problem asks for a minimum, it is not an optimization problem. Rather, we want to solve for the initial speed that just barely gets the ball to the top of the wall, given that it is launched at the optimal angle.\n", + "\n", + "And we have to be careful about what we mean by \"optimal\". For this problem, we don't want the longest range; we want the maximum height at the point where it reaches the wall.\n", + "\n", + "If you are ready to solve the problem on your own, go ahead. Otherwise I will walk you through the process with an outline and some starter code.\n", + "\n", + "As a first step, write an `event_func` that stops the simulation when the ball reaches the wall at `wall_distance`, which is a parameter in `params`.\n", + "Test your function with the initial conditions." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "studied-association", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "developmental-alabama", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "novel-appointment", + "metadata": {}, + "source": [ + "Next, write a function called `height_func` that takes a launch angle, simulates the flight of a baseball, and returns the height of the baseball when it reaches the wall.\n", + "Test your function with the initial conditions." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "ignored-decrease", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "western-communist", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "located-lawsuit", + "metadata": {}, + "source": [ + "Now use `maximize_scalar` to find the optimal angle. Is it higher or lower than the angle that maximizes range?" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "duplicate-madison", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "legislative-prospect", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "interpreted-telephone", + "metadata": {}, + "source": [ + "The angle that maximizes the height at the wall is a little higher than the angle that maximizes range.\n", + "\n", + "Now, let's find the initial speed that makes the height at the wall exactly 37 feet, given that it's launched at the optimal angle. \n", + "This is a root-finding problem, so we'll use `root_scalar`.\n", + "\n", + "Write an error function that takes a speed and a `System` object as parameters. It should use `maximize_scalar` to find the highest possible height of the ball at the wall, for the given speed. Then it should return the difference between that optimal height and `wall_height`, which is a parameter in `params`." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "egyptian-shadow", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "documentary-guidance", + "metadata": {}, + "source": [ + "Test your error function before you call `root_scalar`." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "sustainable-supply", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "american-biodiversity", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "reserved-shelter", + "metadata": {}, + "source": [ + "Then use `root_scalar` to find the answer to the problem, the minimum speed that gets the ball out of the park." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "certified-webster", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "silver-bernard", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "absent-encoding", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "approved-fiction", + "metadata": {}, + "source": [ + "And just to check, run `error_func` with the value you found." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "thick-jungle", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "simple-steps", + "metadata": {}, + "source": [ + "## Under the Hood\n", + "\n", + "`maximize_scalar` uses a SciPy function called `minimize_scalar`, which provides several optimization methods. By default, it uses `bounded`, a version of Brent's algorithm that is safe in the sense that it always uses values within the bounds you provide (including both ends).\n", + "You can read more about it at )." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dense-study", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap24.ipynb b/chapters/chap24.ipynb new file mode 100644 index 000000000..0c4de2f83 --- /dev/null +++ b/chapters/chap24.ipynb @@ -0,0 +1,917 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "august-parks", + "metadata": {}, + "source": [ + "# Rotation" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "plastic-trigger", + "metadata": { + "tags": [] + }, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "confirmed-california", + "metadata": {}, + "source": [ + "In this chapter and the next we'll model systems that involve rotating objects.\n", + "In general, rotation is complicated.\n", + "In three dimensions, objects can rotate around three axes and many objects are easier to spin around some axes than others.\n", + "If the configuration of an object changes over time, it might become\n", + "easier or harder to spin, which explains the surprising dynamics of\n", + "gymnasts, divers, ice skaters, etc.\n", + "And when you apply a twisting force to a rotating object, the effect is often contrary to intuition. \n", + "For an example, see this video on gyroscopic precession: ." + ] + }, + { + "cell_type": "markdown", + "id": "composed-carol", + "metadata": {}, + "source": [ + "We will not take on the physics of rotation in all its glory; rather, we will focus on simple scenarios where all rotation and all twisting forces are around a single axis. \n", + "In that case, we can treat some vector quantities as if they were scalars; that is, simple numbers.\n", + "\n", + "The fundamental ideas in these examples are angular velocity, angular acceleration, torque, and moment of inertia.\n", + "If you are not already familiar with these concepts, don't worry; I will define them as we go along, and I will point to additional reading." + ] + }, + { + "cell_type": "markdown", + "id": "integral-welsh", + "metadata": {}, + "source": [ + "## The Physics of Toilet Paper\n", + "\n", + "As an example of a system with rotation, we'll simulate the manufacture of a roll of toilet paper, as shown in this video: . \n", + "Starting with a cardboard tube at the center, we will roll up 47 m of paper, a typical length for a roll of toilet paper in the U.S. (see ).\n", + "\n", + "The following figure shows a diagram of the system: $r$ represents\n", + "the radius of the roll at a point in time. Initially, $r$ is the radius of the cardboard core, $R_{min}$. When the roll is complete, $r$ is $R_{max}$.\n", + "\n", + "![Diagram of a roll of toilet paper, showing change in paper length as a result of a small rotation, $d\\theta$.](https://github.com/AllenDowney/ModSim/raw/main/figs/paper_roll.png)\n", + "\n", + "I'll use $\\theta$ to represent the total rotation of the roll in radians. In the diagram, $d\\theta$ represents a small increase in $\\theta$, which corresponds to a distance along the circumference of $r~d\\theta$." + ] + }, + { + "cell_type": "markdown", + "id": "political-prerequisite", + "metadata": {}, + "source": [ + "I'll use $y$ to represent the total length of paper that's been rolled. \n", + "Initially, $\\theta=0$ and $y=0$. \n", + "For each small increase in $\\theta$, there is a corresponding increase in $y$: \n", + "\n", + "$$dy = r~d\\theta$$\n", + "\n", + "If we divide both sides by a small increase in time, $dt$, we get a\n", + "differential equation for $y$ as a function of time.\n", + "\n", + "$$\\frac{dy}{dt} = r \\frac{d\\theta}{dt}$$ \n", + "\n", + "As we roll up the paper, $r$ increases. Assuming it increases by a fixed amount per revolution, we can write \n", + "\n", + "$$dr = k~d\\theta$$ \n", + "\n", + "where $k$ is an unknown constant we'll have to figure out. \n", + "Again, we can divide both sides by $dt$ to get a differential equation in time:\n", + "\n", + "$$\\frac{dr}{dt} = k \\frac{d\\theta}{dt}$$ \n", + "\n", + "Finally, let's assume that $\\theta$ increases at a constant rate of $\\omega = 300$ rad/s (about 2900 revolutions per minute): \n", + "\n", + "$$\\frac{d\\theta}{dt} = \\omega$$ \n", + "\n", + "This rate of change is called an *angular velocity*. Now we have a system of differential equations we can use to simulate the system." + ] + }, + { + "cell_type": "markdown", + "id": "prostate-smell", + "metadata": {}, + "source": [ + "## Setting Parameters\n", + "\n", + "Here are the parameters of the system:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "honey-translator", + "metadata": {}, + "outputs": [], + "source": [ + "Rmin = 0.02 # m\n", + "Rmax = 0.055 # m\n", + "L = 47 # m\n", + "omega = 300 # rad / s" + ] + }, + { + "cell_type": "markdown", + "id": "provincial-logistics", + "metadata": {}, + "source": [ + "`Rmin` and `Rmax` are the initial and final values for the radius, `r`.\n", + "`L` is the total length of the paper.\n", + "`omega` is the angular velocity in radians per second.\n", + "\n", + "Figuring out `k` is not easy, but we can estimate it by pretending that `r` is constant and equal to the average of `Rmin` and `Rmax`:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "floppy-plane", + "metadata": {}, + "outputs": [], + "source": [ + "Ravg = (Rmax + Rmin) / 2" + ] + }, + { + "cell_type": "markdown", + "id": "ordinary-leader", + "metadata": {}, + "source": [ + "In that case, the circumference of the roll is also constant:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "funky-bandwidth", + "metadata": {}, + "outputs": [], + "source": [ + "Cavg = 2 * np.pi * Ravg" + ] + }, + { + "cell_type": "markdown", + "id": "square-radius", + "metadata": {}, + "source": [ + "And we can compute the number of revolutions to roll up length `L`, like this." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "front-conservation", + "metadata": {}, + "outputs": [], + "source": [ + "revs = L / Cavg" + ] + }, + { + "cell_type": "markdown", + "id": "headed-gibson", + "metadata": {}, + "source": [ + "Converting rotations to radians, we can estimate the final value of `theta`." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "defined-plymouth", + "metadata": {}, + "outputs": [], + "source": [ + "theta = 2 * np.pi * revs\n", + "theta" + ] + }, + { + "cell_type": "markdown", + "id": "naughty-narrative", + "metadata": {}, + "source": [ + "Finally, `k` is the total change in `r` divided by the total change in `theta`." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "subject-brief", + "metadata": {}, + "outputs": [], + "source": [ + "k_est = (Rmax - Rmin) / theta\n", + "k_est" + ] + }, + { + "cell_type": "markdown", + "id": "experienced-sharing", + "metadata": {}, + "source": [ + "At the end of the chapter, we'll derive `k` analytically, but this estimate is enough to get started." + ] + }, + { + "cell_type": "markdown", + "id": "phantom-yacht", + "metadata": {}, + "source": [ + "## Simulating the System\n", + "\n", + "The state variables we'll use are `theta`, `y`, and `r`.\n", + "Here are the initial conditions:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "ranging-graham", + "metadata": {}, + "outputs": [], + "source": [ + "init = State(theta=0, y=0, r=Rmin)" + ] + }, + { + "cell_type": "markdown", + "id": "caring-unemployment", + "metadata": {}, + "source": [ + "And here's a `System` object with `init` and `t_end`:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "fresh-domestic", + "metadata": {}, + "outputs": [], + "source": [ + "system = System(init=init, t_end=10)" + ] + }, + { + "cell_type": "markdown", + "id": "beginning-artwork", + "metadata": {}, + "source": [ + "Now we can use the differential equations from the previous section to\n", + "write a slope function:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "signed-eight", + "metadata": {}, + "outputs": [], + "source": [ + "def slope_func(t, state, system):\n", + " theta, y, r = state\n", + " \n", + " dydt = r * omega\n", + " drdt = k_est * omega\n", + " \n", + " return omega, dydt, drdt" + ] + }, + { + "cell_type": "markdown", + "id": "maritime-funds", + "metadata": {}, + "source": [ + "As usual, the slope function takes a time stamp, a `State` object, and a `System` object. \n", + "\n", + "The job of the slope function is to compute the time derivatives of the state variables.\n", + "The derivative of `theta` is angular velocity, `omega`.\n", + "The derivatives of `y` and `r` are given by the differential equations we derived." + ] + }, + { + "cell_type": "markdown", + "id": "trained-witness", + "metadata": {}, + "source": [ + "And as usual, we'll test the slope function with the initial conditions." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "exterior-water", + "metadata": {}, + "outputs": [], + "source": [ + "slope_func(0, system.init, system)" + ] + }, + { + "cell_type": "markdown", + "id": "russian-rochester", + "metadata": {}, + "source": [ + "We'd like to stop the simulation when the length of paper on the roll is `L`. We can do that with an event function that passes through 0 when `y` equals `L`:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "strong-custody", + "metadata": {}, + "outputs": [], + "source": [ + "def event_func(t, state, system):\n", + " theta, y, r = state\n", + " return L - y" + ] + }, + { + "cell_type": "markdown", + "id": "catholic-workshop", + "metadata": {}, + "source": [ + "We can test it with the initial conditions:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "moved-present", + "metadata": {}, + "outputs": [], + "source": [ + "event_func(0, system.init, system)" + ] + }, + { + "cell_type": "markdown", + "id": "affecting-pathology", + "metadata": {}, + "source": [ + "Now let's run the simulation:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "stable-lying", + "metadata": {}, + "outputs": [], + "source": [ + "results, details = run_solve_ivp(system, slope_func,\n", + " events=event_func)\n", + "details.message" + ] + }, + { + "cell_type": "markdown", + "id": "experienced-pathology", + "metadata": {}, + "source": [ + "Here are the last few time steps." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "careful-bahrain", + "metadata": {}, + "outputs": [], + "source": [ + "results.tail()" + ] + }, + { + "cell_type": "markdown", + "id": "underlying-variance", + "metadata": {}, + "source": [ + "The time it takes to complete one roll is about 4.2 seconds, which is consistent with what we see in the video." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "individual-patrick", + "metadata": {}, + "outputs": [], + "source": [ + "results.index[-1]" + ] + }, + { + "cell_type": "markdown", + "id": "informative-porter", + "metadata": {}, + "source": [ + "The final value of `y` is 47 meters, as expected." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "indian-skirt", + "metadata": {}, + "outputs": [], + "source": [ + "final_state = results.iloc[-1] \n", + "final_state.y" + ] + }, + { + "cell_type": "markdown", + "id": "running-tutorial", + "metadata": {}, + "source": [ + "The final value of `r` is 0.55 m, which is `Rmax`." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "higher-conflict", + "metadata": {}, + "outputs": [], + "source": [ + "final_state.r" + ] + }, + { + "cell_type": "markdown", + "id": "tamil-referral", + "metadata": {}, + "source": [ + "The total number of rotations is close to 200, which seems plausible." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "employed-ordinary", + "metadata": {}, + "outputs": [], + "source": [ + "radians = final_state.theta\n", + "rotations = radians / 2 / np.pi\n", + "rotations" + ] + }, + { + "cell_type": "markdown", + "id": "otherwise-mississippi", + "metadata": {}, + "source": [ + "As an exercise, we'll see how fast the paper is moving. But first, let's take a closer look at the results." + ] + }, + { + "cell_type": "markdown", + "id": "lesser-consumer", + "metadata": {}, + "source": [ + "## Plotting the Results\n", + "\n", + "Here's what `theta` looks like over time." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "included-schema", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_theta(results):\n", + " results.theta.plot(color='C0', label='theta')\n", + " decorate(xlabel='Time (s)',\n", + " ylabel='Angle (rad)')\n", + " \n", + "plot_theta(results)" + ] + }, + { + "cell_type": "markdown", + "id": "regulation-runner", + "metadata": {}, + "source": [ + "`theta` grows linearly, as we should expect with constant angular velocity.\n", + "\n", + "Here's what `r` looks like over time." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "persistent-siemens", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_r(results):\n", + " results.r.plot(color='C2', label='r')\n", + "\n", + " decorate(xlabel='Time (s)',\n", + " ylabel='Radius (m)')\n", + " \n", + "plot_r(results)" + ] + }, + { + "cell_type": "markdown", + "id": "smoking-spine", + "metadata": {}, + "source": [ + "`r` also increases linearly.\n", + "\n", + "Here's what `y` looks like over time." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "contrary-typing", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_y(results):\n", + " results.y.plot(color='C1', label='y')\n", + "\n", + " decorate(xlabel='Time (s)',\n", + " ylabel='Length (m)')\n", + " \n", + "plot_y(results)" + ] + }, + { + "cell_type": "markdown", + "id": "dedicated-tuning", + "metadata": {}, + "source": [ + "Since the derivative of `y` depends on `r`, and `r` is increasing, `y` grows with increasing slope.\n", + "\n", + "In the next section, we'll see that we could have solved these differential equations analytically.\n", + "However, it is often useful to start with simulation as a way of exploring and checking assumptions." + ] + }, + { + "cell_type": "markdown", + "id": "better-asbestos", + "metadata": {}, + "source": [ + "## Analytic Solution\n", + "\n", + "Since angular velocity is constant: \n", + "\n", + "$$\\frac{d\\theta}{dt} = \\omega \\quad\\quad (1)$$ \n", + "\n", + "We can find $\\theta$ as a function of time by integrating both sides:\n", + "\n", + "$$\\theta(t) = \\omega t$$ \n", + "\n", + "Similarly, we can solve this equation\n", + "\n", + "$$\\frac{dr}{dt} = k \\omega$$\n", + "\n", + "to find\n", + "\n", + "$$r(t) = k \\omega t + R_{min}$$ \n", + "\n", + "Then we can plug the solution for $r$ into the equation for $y$: \n", + "\n", + "$$\\begin{aligned}\n", + "\\frac{dy}{dt} & = r \\omega \\quad\\quad (2) \\\\\n", + " & = \\left[ k \\omega t + R_{min} \\right] \\omega \\nonumber\\end{aligned}$$\n", + " \n", + "Integrating both sides yields:\n", + "\n", + "$$y(t) = \\left[ k \\omega t^2 / 2 + R_{min} t \\right] \\omega$$ \n", + "\n", + "So $y$ is a parabola, as you might have guessed." + ] + }, + { + "cell_type": "markdown", + "id": "meaningful-kazakhstan", + "metadata": {}, + "source": [ + "We can also use these equations to find the relationship between $y$ and $r$, independent of time, which we can use to compute $k$.\n", + "Dividing Equations 1 and 2 yields\n", + "\n", + "$$\\frac{dr}{dy} = \\frac{k}{r}$$ \n", + "\n", + "Separating variables yields\n", + "\n", + "$$r~dr = k~dy$$ \n", + "\n", + "Integrating both sides yields \n", + "\n", + "$$r^2 / 2 = k y + C$$ \n", + "\n", + "Solving for $y$, we have \n", + "\n", + "$$y = \\frac{1}{2k} (r^2 - C) \\label{eqn3}$$\n", + "\n", + "When $y=0$, $r=R_{min}$, so \n", + "\n", + "$$R_{min}^2 / 2 = C$$ \n", + "\n", + "When $y=L$, $r=R_{max}$, so\n", + "\n", + "$$L = \\frac{1}{2k} (R_{max}^2 - R_{min}^2)$$ \n", + "\n", + "Solving for $k$ yields\n", + "\n", + "$$k = \\frac{1}{2L} (R_{max}^2 - R_{min}^2) \\label{eqn4}$$\n", + "\n", + "Plugging in the values of the parameters yields `2.8e-5` m/rad, the same as the \"estimate\" we computed in Section xxx. " + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "acknowledged-register", + "metadata": {}, + "outputs": [], + "source": [ + "k = (Rmax**2 - Rmin**2) / (2 * L)\n", + "k" + ] + }, + { + "cell_type": "markdown", + "id": "retired-skill", + "metadata": {}, + "source": [ + "In this case the estimate turns out to be exact." + ] + }, + { + "cell_type": "markdown", + "id": "foreign-needle", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This chapter introduces rotation, starting with an example where angular velocity is constant.\n", + "We simulated the manufacture of a roll of toilet paper, then we solved the same problem analytically.\n", + "\n", + "In the next chapter, we'll see a more interesting example where angular velocity is not constant. And we'll introduce three new concepts: torque, angular acceleration, and moment of inertia.\n", + "\n", + "But first, you might want to work on the following exercise." + ] + }, + { + "cell_type": "markdown", + "id": "variable-lambda", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "You can access the notebooks at ." + ] + }, + { + "cell_type": "markdown", + "id": "thick-luther", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + " Since we keep `omega` constant, the linear velocity of the paper increases with radius. We can use `gradient` to estimate the derivative of `results.y`." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "cardiac-hospital", + "metadata": {}, + "outputs": [], + "source": [ + "dydt = gradient(results.y)" + ] + }, + { + "cell_type": "markdown", + "id": "atomic-montana", + "metadata": {}, + "source": [ + "Here's what the result looks like." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "welsh-charleston", + "metadata": {}, + "outputs": [], + "source": [ + "dydt.plot(label='dydt')\n", + "decorate(xlabel='Time (s)',\n", + " ylabel='Linear velocity (m/s)')" + ] + }, + { + "cell_type": "markdown", + "id": "becoming-birthday", + "metadata": {}, + "source": [ + "With constant angular velocity, linear velocity is increasing, reaching its maximum at the end." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "informational-washer", + "metadata": {}, + "outputs": [], + "source": [ + "max_linear_velocity = dydt.iloc[-1]\n", + "max_linear_velocity" + ] + }, + { + "cell_type": "markdown", + "id": "bizarre-variation", + "metadata": {}, + "source": [ + "Now suppose this peak velocity is the limiting factor; that is, we can't move the paper any faster than that.\n", + "In that case, we might be able to speed up the process by keeping the linear velocity at the maximum all the time.\n", + "\n", + "Write a slope function that keeps the linear velocity, `dydt`, constant, and computes the angular velocity, `omega`, accordingly.\n", + "Then, run the simulation and see how much faster we could finish rolling the paper." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "excellent-japanese", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "steady-member", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "applied-sacramento", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "adult-chuck", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "conditional-cliff", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "arranged-queensland", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "similar-variance", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lovely-orange", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap25.ipynb b/chapters/chap25.ipynb new file mode 100644 index 000000000..11b060025 --- /dev/null +++ b/chapters/chap25.ipynb @@ -0,0 +1,1102 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "unauthorized-winter", + "metadata": {}, + "source": [ + "# Torque" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "plastic-trigger", + "metadata": { + "tags": [] + }, + "source": [ + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "Click here to access the notebooks: ." + ] + }, + { + "cell_type": "markdown", + "id": "chief-delight", + "metadata": {}, + "source": [ + "In the previous chapter we modeled a system with constant angular\n", + "velocity.\n", + "In this chapter we take the next step, modeling a system with angular acceleration and deceleration." + ] + }, + { + "cell_type": "markdown", + "id": "earned-motorcycle", + "metadata": {}, + "source": [ + "## Angular Acceleration\n", + "\n", + "Just as linear acceleration is the derivative of velocity, *angular\n", + "acceleration* is the derivative of angular velocity. And just as linear acceleration is caused by force, angular acceleration is caused by the rotational version of force, *torque*. If you are not familiar with torque, you can read about it at .\n", + "\n", + "In general, torque is a vector quantity, defined as the *cross\n", + "product* of $\\vec{r}$ and $\\vec{F}$, where $\\vec{r}$ is the *lever\n", + "arm*, a vector from the center of rotation to the point where the force is applied, and $\\vec{F}$ is the vector that represents the magnitude and direction of the force." + ] + }, + { + "cell_type": "markdown", + "id": "promotional-trigger", + "metadata": {}, + "source": [ + "For the problems in this chapter, however, we only need the *magnitude* of torque; we don't care about the direction. In that case, we can compute this product of scalar quantities:\n", + "\n", + "$$\\tau = r F \\sin \\theta$$ \n", + "\n", + "where $\\tau$ is torque, $r$ is the length of the lever arm, $F$ is the magnitude of force, and $\\theta$ is the angle between $\\vec{r}$ and $\\vec{F}$.\n", + "\n", + "Since torque is the product of a length and a force, it is expressed in newton meters (Nm)." + ] + }, + { + "cell_type": "markdown", + "id": "infrared-summit", + "metadata": {}, + "source": [ + "## Moment of Inertia\n", + "\n", + "In the same way that linear acceleration is related to force by Newton's second law of motion, $F=ma$, angular acceleration is related to torque by another form of Newton's law: \n", + "\n", + "$$\\tau = I \\alpha$$ \n", + "\n", + "where $\\alpha$ is angular acceleration and $I$ is *moment of inertia*. Just as mass is what makes it hard to accelerate an object, moment of inertia is what makes it hard to spin an object.\n", + "\n", + "In the most general case, a 3-D object rotating around an arbitrary\n", + "axis, moment of inertia is a *tensor*, which is a function that takes a\n", + "vector as a parameter and returns a vector as a result.\n", + "\n", + "Fortunately, in a system where all rotation and torque happens around a single axis, we don't have to deal with the most general case. We can treat moment of inertia as a scalar quantity.\n", + "\n", + "For a small object with mass $m$, rotating around a point at distance\n", + "$r$, the moment of inertia is $I = m r^2$. For more complex objects, we can compute $I$ by dividing the object into small masses, computing\n", + "moments of inertia for each mass, and adding them up.\n", + "For most simple shapes, people have already done the\n", + "calculations; you can just look up the answers. For example, see\n", + "." + ] + }, + { + "cell_type": "markdown", + "id": "julian-klein", + "metadata": {}, + "source": [ + "## Teapots and Turntables\n", + "\n", + "Tables in Chinese restaurants often have a rotating tray or turntable\n", + "that makes it easy for customers to share dishes. These turntables are\n", + "supported by low-friction bearings that allow them to turn easily and\n", + "glide. However, they can be heavy, especially when they are loaded with food, so they have a high moment of inertia.\n", + "\n", + "Suppose I am sitting at a table with a pot of tea on the turntable\n", + "directly in front of me, and the person sitting directly opposite asks\n", + "me to pass the tea. I push on the edge of the turntable with 2 N of\n", + "force until it has turned 0.5 rad, then let go. The turntable glides\n", + "until it comes to a stop 1.5 rad from the starting position. How much\n", + "force should I apply for a second push so the teapot glides to a stop\n", + "directly opposite me?\n", + "\n", + "We'll answer this question in these steps:\n", + "\n", + "1. I'll use the results from the first push to estimate the coefficient of friction for the turntable.\n", + "\n", + "2. As an exercise, you'll use that coefficient of friction to estimate the force needed to rotate the turntable through the remaining angle.\n", + "\n", + "Our simulation will use the following parameters:\n", + "\n", + "1. The radius of the turntable is 0.5 m, and its weight is 7 kg.\n", + "\n", + "2. The teapot weights 0.3 kg, and it sits 0.4 m from the center of the turntable.\n", + "\n", + "The following figure shows the scenario, where $F$ is the force I apply to the turntable at the perimeter, perpendicular to the lever arm, $r$, and $\\tau$ is the resulting torque. The circle near the bottom is the teapot.\n", + "\n", + "![Diagram of a turntable with a\n", + "teapot.](https://github.com/AllenDowney/ModSim/raw/main/figs/teapot.png)\n", + "\n", + "Here are the parameters from the statement of the problem:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "spiritual-disorder", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import pi\n", + "\n", + "radius_disk = 0.5 # m\n", + "mass_disk = 7 # kg\n", + "radius_pot = 0.4 # m\n", + "mass_pot = 0.3 # kg\n", + "force = 2 # N\n", + "\n", + "theta_push = 0.5 # radian\n", + "theta_test = 1.5 # radian\n", + "theta_target = pi # radian" + ] + }, + { + "cell_type": "markdown", + "id": "bound-algorithm", + "metadata": {}, + "source": [ + "`theta_push` is the angle where I stop pushing on the turntable.\n", + "`theta_test` is how far the table turns during my test push.\n", + "`theta_target` is where we want the table to be after the second push.\n", + "\n", + "We can use these parameters to compute the moment of inertia of the turntable, using the formula for a horizontal disk revolving around a vertical axis through its center: " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "recorded-administration", + "metadata": {}, + "outputs": [], + "source": [ + "I_disk = mass_disk * radius_disk**2 / 2" + ] + }, + { + "cell_type": "markdown", + "id": "economic-concord", + "metadata": {}, + "source": [ + "We can also compute the moment of inertia of the teapot, treating it as a point mass:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "present-termination", + "metadata": {}, + "outputs": [], + "source": [ + "I_pot = mass_pot * radius_pot**2" + ] + }, + { + "cell_type": "markdown", + "id": "dominican-joseph", + "metadata": {}, + "source": [ + "The total moment of inertia is the sum of these parts:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "former-driver", + "metadata": {}, + "outputs": [], + "source": [ + "I_total = I_disk + I_pot" + ] + }, + { + "cell_type": "markdown", + "id": "effective-danger", + "metadata": {}, + "source": [ + "Friction in the bearings probably depends on the weight of the turntable and its contents, but probably does not depend on angular velocity.\n", + "So we'll assume that it is a constant.\n", + "We don't know what it is, so I will start with a guess, and we will use `root_scalar` to improve it." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "enclosed-happiness", + "metadata": {}, + "outputs": [], + "source": [ + "torque_friction = 0.3 # N*m" + ] + }, + { + "cell_type": "markdown", + "id": "balanced-method", + "metadata": {}, + "source": [ + "For this problem we'll treat friction as a torque.\n", + "\n", + "The state variables we'll use are `theta`, which is the angle of the table in rad, and `omega`, which is angular velocity in rad/s." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "passing-sullivan", + "metadata": {}, + "outputs": [], + "source": [ + "init = State(theta=0, omega=0)" + ] + }, + { + "cell_type": "markdown", + "id": "second-leather", + "metadata": {}, + "source": [ + "Now we can make a `System` with the initial state, `init`, the maximum duration of the simulation, `t_end`, and the parameters we are going to vary, `force` and `torque_friction`." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "acoustic-furniture", + "metadata": {}, + "outputs": [], + "source": [ + "system = System(init=init, \n", + " force=force,\n", + " torque_friction=torque_friction,\n", + " t_end=20)" + ] + }, + { + "cell_type": "markdown", + "id": "crucial-recognition", + "metadata": {}, + "source": [ + "Here's a slope function that takes the current state, which contains angle and angular velocity, and returns the derivatives, angular velocity and angular acceleration:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "ranking-local", + "metadata": {}, + "outputs": [], + "source": [ + "def slope_func(t, state, system):\n", + " theta, omega = state\n", + " force = system.force\n", + " torque_friction = system.torque_friction\n", + " \n", + " torque = radius_disk * force - torque_friction\n", + " alpha = torque / I_total\n", + " \n", + " return omega, alpha " + ] + }, + { + "cell_type": "markdown", + "id": "exposed-court", + "metadata": {}, + "source": [ + "In this scenario, the force I apply to the turntable is always\n", + "perpendicular to the lever arm, so $\\sin \\theta = 1$ and the torque due\n", + "to force is $\\tau = r F$.\n", + "\n", + "`torque_friction` represents the torque due to friction. Because the\n", + "turntable is rotating in the direction of positive `theta`, friction\n", + "acts in the direction of negative `theta`.\n", + "\n", + "We can test the slope function with the initial conditions:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "saved-purple", + "metadata": {}, + "outputs": [], + "source": [ + "slope_func(0, system.init, system)" + ] + }, + { + "cell_type": "markdown", + "id": "forbidden-legislature", + "metadata": {}, + "source": [ + "We are almost ready to run the simulation, but first there's a problem we have to address." + ] + }, + { + "cell_type": "markdown", + "id": "tribal-disclosure", + "metadata": {}, + "source": [ + "## Two Phase Simulation\n", + "\n", + "When I stop pushing on the turntable, the angular acceleration changes\n", + "abruptly. We could implement the slope function with an `if` statement\n", + "that checks the value of `theta` and sets `force` accordingly. And for a coarse model like this one, that might be fine. But a more robust approach is to simulate the system in two phases:\n", + "\n", + "1. During the first phase, force is constant, and we run until `theta` is 0.5 radians.\n", + "\n", + "2. During the second phase, force is 0, and we run until `omega` is 0.\n", + "\n", + "Then we can combine the results of the two phases into a single\n", + "`TimeFrame`." + ] + }, + { + "cell_type": "markdown", + "id": "decent-microwave", + "metadata": {}, + "source": [ + "### Phase 1\n", + "\n", + "Here's the event function I'll use for Phase 1; it stops the simulation when `theta` reaches `theta_push`, which is when I stop pushing:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "black-wichita", + "metadata": {}, + "outputs": [], + "source": [ + "def event_func1(t, state, system):\n", + " theta, omega = state\n", + " return theta - theta_push" + ] + }, + { + "cell_type": "markdown", + "id": "separate-college", + "metadata": {}, + "source": [ + "We can test it with the initial conditions." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "ranking-google", + "metadata": {}, + "outputs": [], + "source": [ + "event_func1(0, system.init, system)" + ] + }, + { + "cell_type": "markdown", + "id": "reduced-sharp", + "metadata": {}, + "source": [ + "And run the first phase of the simulation." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "meaning-philosophy", + "metadata": {}, + "outputs": [], + "source": [ + "results1, details1 = run_solve_ivp(system, slope_func,\n", + " events=event_func1)\n", + "details1.message" + ] + }, + { + "cell_type": "markdown", + "id": "spread-application", + "metadata": {}, + "source": [ + "Here are the last few time steps." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "focused-invention", + "metadata": {}, + "outputs": [], + "source": [ + "results1.tail()" + ] + }, + { + "cell_type": "markdown", + "id": "willing-receipt", + "metadata": {}, + "source": [ + "It takes a little more than a second for me to rotate the table 0.5 rad.\n", + "When I release the table, the angular velocity is about 0.87 rad / s.\n", + "\n", + "Before we run the second phase, we have to extract the final time and\n", + "state of the first phase." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "russian-experience", + "metadata": {}, + "outputs": [], + "source": [ + "t_2 = results1.index[-1]\n", + "init2 = results1.iloc[-1]" + ] + }, + { + "cell_type": "markdown", + "id": "aware-generator", + "metadata": {}, + "source": [ + "### Phase 2\n", + "\n", + "Now we can make a `System` object for Phase 2 with the initial state\n", + "from Phase 1 and with `force=0`." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "chinese-cover", + "metadata": {}, + "outputs": [], + "source": [ + "system2 = system.set(t_0=t_2, init=init2, force=0)" + ] + }, + { + "cell_type": "markdown", + "id": "actual-monaco", + "metadata": {}, + "source": [ + "For the second phase, we need an event function that stops when the\n", + "turntable stops; that is, when angular velocity is 0." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "recovered-section", + "metadata": {}, + "outputs": [], + "source": [ + "def event_func2(t, state, system):\n", + " theta, omega = state\n", + " return omega" + ] + }, + { + "cell_type": "markdown", + "id": "ordinary-miniature", + "metadata": {}, + "source": [ + "We'll test it with the initial conditions for Phase 2." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "corporate-taste", + "metadata": {}, + "outputs": [], + "source": [ + "event_func2(system2.t_0, system2.init, system2)" + ] + }, + { + "cell_type": "markdown", + "id": "administrative-major", + "metadata": {}, + "source": [ + "The result is the angular velocity at the beginning of Phase 2, in rad/s.\n", + "\n", + "Now we can run the second phase." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "destroyed-adrian", + "metadata": {}, + "outputs": [], + "source": [ + "results2, details2 = run_solve_ivp(system2, slope_func,\n", + " events=event_func2)\n", + "details2.message" + ] + }, + { + "cell_type": "markdown", + "id": "hindu-requirement", + "metadata": {}, + "source": [ + "### Combining the Results\n", + "\n", + "Pandas provides a function called `concat`, which makes a \n", + "`DataFrame` with the rows from `results1` followed by the rows from `results2`." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "frank-scene", + "metadata": {}, + "outputs": [], + "source": [ + "results = pd.concat([results1, results2])" + ] + }, + { + "cell_type": "markdown", + "id": "wooden-butterfly", + "metadata": {}, + "source": [ + "Here are the last few time steps." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "short-singer", + "metadata": {}, + "outputs": [], + "source": [ + "results.tail()" + ] + }, + { + "cell_type": "markdown", + "id": "metropolitan-roommate", + "metadata": {}, + "source": [ + "At the end, angular velocity is close to 0, and the total rotation is about 1.7 rad, a little farther than we were aiming for.\n", + "\n", + "We can plot `theta` for both phases." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "distributed-humanitarian", + "metadata": {}, + "outputs": [], + "source": [ + "results.theta.plot(label='theta')\n", + "decorate(xlabel='Time (s)',\n", + " ylabel='Angle (rad)')" + ] + }, + { + "cell_type": "markdown", + "id": "excess-confidence", + "metadata": {}, + "source": [ + "And `omega`." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "proved-surfing", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "results.omega.plot(label='omega', color='C1')\n", + "decorate(xlabel='Time (s)',\n", + " ylabel='Angular velocity (rad/s)')" + ] + }, + { + "cell_type": "markdown", + "id": "handy-frontier", + "metadata": {}, + "source": [ + "Angular velocity, `omega`, increases linearly while I am pushing, and decreases linearly after I let go. The angle, `theta`, is the integral of angular velocity, so it forms a parabola during each phase.\n", + "\n", + "In the next section, we'll use this simulation to estimate the torque\n", + "due to friction." + ] + }, + { + "cell_type": "markdown", + "id": "careful-minneapolis", + "metadata": {}, + "source": [ + "## Estimating Friction\n", + "\n", + "Let's take the code from the previous section and wrap it in a function." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "constant-wallace", + "metadata": {}, + "outputs": [], + "source": [ + "def run_two_phases(force, torque_friction, system):\n", + " \n", + " # put the specified parameters into the System object\n", + " system1 = system.set(force=force, \n", + " torque_friction=torque_friction)\n", + "\n", + " # run phase 1\n", + " results1, details1 = run_solve_ivp(system1, slope_func, \n", + " events=event_func1)\n", + "\n", + " # get the final state from phase 1\n", + " t_2 = results1.index[-1]\n", + " init2 = results1.iloc[-1]\n", + " \n", + " # run phase 2\n", + " system2 = system1.set(t_0=t_2, init=init2, force=0)\n", + " results2, details2 = run_solve_ivp(system2, slope_func, \n", + " events=event_func2)\n", + " \n", + " # combine and return the results\n", + " results = pd.concat([results1, results2])\n", + " return results" + ] + }, + { + "cell_type": "markdown", + "id": "positive-possible", + "metadata": {}, + "source": [ + "I'll test it with the same parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "played-ranking", + "metadata": {}, + "outputs": [], + "source": [ + "force = 2\n", + "torque_friction = 0.3\n", + "results = run_two_phases(force, torque_friction, system)\n", + "results.tail()" + ] + }, + { + "cell_type": "markdown", + "id": "damaged-concert", + "metadata": {}, + "source": [ + "These results are the same as in the previous section.\n", + "\n", + "We can use `run_two_phases` to write an error function we can use, with `root_scalar`, to find the torque due to friction that yields the\n", + "observed results from the first push, a total rotation of 1.5 rad." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "skilled-diving", + "metadata": {}, + "outputs": [], + "source": [ + "def error_func1(torque_friction, system):\n", + " force = system.force\n", + " results = run_two_phases(force, torque_friction, system)\n", + " theta_final = results.iloc[-1].theta\n", + " print(torque_friction, theta_final)\n", + " return theta_final - theta_test" + ] + }, + { + "cell_type": "markdown", + "id": "generous-parcel", + "metadata": {}, + "source": [ + "This error function takes torque due to friction as an input.\n", + "It extracts `force` from the `System` object and runs the simulation.\n", + "From the results, it extracts the last value of `theta` and returns the difference between the result of the simulation and the result of the experiment.\n", + "When this difference is 0, the value of `torque_friction` is an estimate for the friction in the experiment.\n", + "\n", + "To bracket the root, we need one value that's too low and one that's too high.\n", + "With `torque_friction=0.3`, the table rotates a bit too far:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "united-ranch", + "metadata": {}, + "outputs": [], + "source": [ + "guess1 = 0.3\n", + "error_func1(guess1, system)" + ] + }, + { + "cell_type": "markdown", + "id": "heard-serial", + "metadata": {}, + "source": [ + "With `torque_friction=0.4`, it doesn't go far enough." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "rotary-brazilian", + "metadata": {}, + "outputs": [], + "source": [ + "guess2 = 0.4\n", + "error_func1(guess2, system)" + ] + }, + { + "cell_type": "markdown", + "id": "comparable-physiology", + "metadata": {}, + "source": [ + "So we can use those two values as a bracket for `root_scalar`." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "hungarian-cattle", + "metadata": {}, + "outputs": [], + "source": [ + "res = root_scalar(error_func1, system, bracket=[guess1, guess2])" + ] + }, + { + "cell_type": "markdown", + "id": "abandoned-remedy", + "metadata": {}, + "source": [ + "The result is 0.333 N m, a little less than the initial guess." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "sorted-struggle", + "metadata": {}, + "outputs": [], + "source": [ + "actual_friction = res.root\n", + "actual_friction" + ] + }, + { + "cell_type": "markdown", + "id": "democratic-praise", + "metadata": {}, + "source": [ + "Now that we know the torque due to friction, we can compute the force\n", + "needed to rotate the turntable through the remaining angle, that is,\n", + "from 1.5 rad to 3.14 rad.\n", + "You'll have a chance to do that as an exercise, but first, let's animate the results." + ] + }, + { + "cell_type": "markdown", + "id": "knowing-sleeve", + "metadata": {}, + "source": [ + "## Animating the Turntable\n", + "\n", + "Here's a function that takes the state of the system and draws it." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "funky-affect", + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib.patches import Circle\n", + "from matplotlib.pyplot import gca, axis\n", + "\n", + "def draw_func(t, state):\n", + " theta, omega = state\n", + " \n", + " # draw a circle for the table\n", + " circle1 = Circle([0, 0], radius_disk)\n", + " gca().add_patch(circle1)\n", + " \n", + " # draw a circle for the teapot\n", + " center = pol2cart(theta, radius_pot)\n", + " circle2 = Circle(center, 0.05, color='C1')\n", + " gca().add_patch(circle2)\n", + "\n", + " axis('equal')" + ] + }, + { + "cell_type": "markdown", + "id": "dangerous-envelope", + "metadata": {}, + "source": [ + "This function uses a few features we have not seen before, but you can read about them in the Matplotlib documentation.\n", + "\n", + "Here's what the initial condition looks like." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "illegal-remainder", + "metadata": {}, + "outputs": [], + "source": [ + "state = results.iloc[0]\n", + "draw_func(0, state)" + ] + }, + { + "cell_type": "markdown", + "id": "efficient-summary", + "metadata": {}, + "source": [ + "And here's how we call it." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "toxic-shark", + "metadata": {}, + "outputs": [], + "source": [ + "# animate(results, draw_func)" + ] + }, + { + "cell_type": "markdown", + "id": "horizontal-shade", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "The example in this chapter demonstrates the concepts of torque, angular acceleration, and moment of inertia.\n", + "We used these concepts to simulate a turntable, using a hypothetical observation to estimate torque due to friction.\n", + "As an exercise, you can finish off the example, estimating the force needed to rotate the table to a given target angle.\n", + "\n", + "The next chapter describes several case studies you can work on to practice the tools from the last few chapters, including projectiles, rotating objects, `root_scalar`, and `maximize_scalar`." + ] + }, + { + "cell_type": "markdown", + "id": "pediatric-precipitation", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n", + "You can access the notebooks at ." + ] + }, + { + "cell_type": "markdown", + "id": "sixth-breach", + "metadata": {}, + "source": [ + "### Exercise 1\n", + "\n", + " Continuing the example from this chapter, estimate the force that delivers the teapot to the desired position.\n", + "Use this `System` object, with the friction we computed in the previous section." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "dental-density", + "metadata": {}, + "outputs": [], + "source": [ + "system3 = system.set(torque_friction=actual_friction)" + ] + }, + { + "cell_type": "markdown", + "id": "practical-cabinet", + "metadata": {}, + "source": [ + "Write an error function that takes `force` and `system`, simulates the system, and returns the difference between `theta_final` and the remaining angle after the first push." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "grave-consent", + "metadata": {}, + "outputs": [], + "source": [ + "remaining_angle = theta_target - theta_test\n", + "remaining_angle" + ] + }, + { + "cell_type": "markdown", + "id": "hundred-judgment", + "metadata": {}, + "source": [ + "Use your error function and `root_scalar` to find the force needed for the second push.\n", + "Run the simulation with the force you computed and confirm that the table stops at the target angle after both pushes." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "apparent-lancaster", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "persistent-rings", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "previous-pittsburgh", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "governing-component", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "statistical-behalf", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "unauthorized-equity", + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eight-prototype", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap26.ipynb b/chapters/chap26.ipynb new file mode 100644 index 000000000..af4305b78 --- /dev/null +++ b/chapters/chap26.ipynb @@ -0,0 +1,375 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "early-drove", + "metadata": {}, + "source": [ + "# Case Studies Part 3" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "markdown", + "id": "acoustic-small", + "metadata": {}, + "source": [ + "This chapter is a collection of case studies you might want to read and work on.\n", + "They are based on the methods in the last few chapters, including Newtonian mechanics in 1-D and 2-D, and rotation around a single axis." + ] + }, + { + "cell_type": "markdown", + "id": "capital-vatican", + "metadata": {}, + "source": [ + "## Bungee Jumping\n", + "\n", + "Suppose you want to set the world record for the highest \"bungee dunk\", which is a stunt in which a bungee jumper dunks a cookie in a cup of tea at the lowest point of a jump. An example is shown in this video: .\n", + "\n", + "Since the record is 70 m, let's design a jump for 80 m. We'll start with the following modeling assumptions:\n", + "\n", + "- Initially the bungee cord hangs from a crane with the attachment\n", + " point 80 m above a cup of tea.\n", + "\n", + "- Until the cord is fully extended, it applies no force to the jumper. It turns out this might not be a good assumption; we'll revisit it in the next case study.\n", + "\n", + "- After the cord is fully extended, it obeys Hooke's Law; that is, it applies a force to the jumper proportional to the extension of the cord beyond its resting length. See .\n", + "\n", + "- The mass of the jumper is 75 kg.\n", + "\n", + "- The jumper is subject to drag force so that their terminal velocity is 60 m/s.\n", + "\n", + "Our objective is to choose the length of the cord, `L`, and its spring\n", + "constant, `k`, so that the jumper falls all the way to the tea cup, but no farther!\n", + "\n", + "In the repository for this book, you will find a notebook,\n", + "*bungee1.ipynb*, which contains starter code and exercises for this case study.\n", + "You can download it from or run it on Colab at ." + ] + }, + { + "cell_type": "markdown", + "id": "recognized-bidding", + "metadata": {}, + "source": [ + "## Bungee Dunk Revisited\n", + "\n", + "In the previous case study, we assume that the cord applies no force to\n", + "the jumper until it is stretched. It is tempting to say that the cord\n", + "has no effect because it falls along with the jumper, but that intuition\n", + "is incorrect. As the cord falls, it transfers energy to the jumper.\n", + "\n", + "At you'll find a paper (Heck, Uylings, and Kędzierska, \"Understanding the physics of bungee jumping\", Physics Education, Volume 45, Number 1, 2010.) that explains\n", + "this phenomenon and derives the acceleration of the jumper, $a$, as a\n", + "function of position, $y$, and velocity, $v$:\n", + "\n", + "$$a = g + \\frac{\\mu v^2/2}{\\mu(L+y) + 2L}$$ \n", + "\n", + "where $g$ is acceleration due to gravity, $L$ is the length of the cord, and $\\mu$ is the ratio of the mass of the cord, $m$, and the mass of the jumper, $M$.\n", + "\n", + "If you don't believe that their model is correct, this video might\n", + "convince you: .\n", + "\n", + "In the repository for this book, you will find a notebook,\n", + "*bungee2.ipynb*, which contains starter code and exercises for this case study. \n", + "You can download it from or run it on Colab at .\n", + "\n", + "How does the behavior of the system change as we vary the mass of the cord? When the mass of the cord equals the mass of the jumper, what is the net effect on the lowest point in the jump?" + ] + }, + { + "cell_type": "markdown", + "id": "german-penetration", + "metadata": {}, + "source": [ + "## Orbiting the Sun\n", + "\n", + "In the exercise at the end of Chapter 20, we modeled the interaction between the Earth and the Sun, simulating what would happen if the Earth stopped in its orbit and fell straight into the Sun.\n", + "\n", + "Now let's extend the model to two dimensions and simulate one revolution of the Earth around the Sun, that is, one year.\n", + "\n", + "In the repository for this book, you will find a notebook,\n", + "*orbit.ipynb*, which contains starter code and exercises for this case study.\n", + "You can download it from or run it on Colab at .\n", + "\n", + "Among other things, you will have a chance to experiment with different algorithms and see what effect they have on the accuracy of the results." + ] + }, + { + "cell_type": "markdown", + "id": "victorian-colonial", + "metadata": {}, + "source": [ + "## Spider-man\n", + "\n", + "In this case study we'll develop a model of Spider-Man swinging from a\n", + "springy cable of webbing attached to the top of the Empire State\n", + "Building. Initially, Spider-Man is at the top of a nearby building, as\n", + "shown in this figure:\n", + "\n", + "![Diagram of the initial state for the Spider-Man case\n", + "study.](https://github.com/AllenDowney/ModSim/raw/main/figs/spiderman.png)\n", + "\n", + "The origin, `O`, is at the base of the Empire State Building. The vector `H` represents the position where the webbing is attached to the building, relative to `O`. The vector `P` is the position of Spider-Man relative to `O`. And `L` is the vector from the attachment point to Spider-Man.\n", + "\n", + "By following the arrows from `O`, along `H`, and along `L`, we can see\n", + "that\n", + "\n", + "```\n", + "H + L = P\n", + "```\n", + "\n", + "So we can compute `L` like this:\n", + "\n", + "```\n", + "L = P - H\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "bizarre-strand", + "metadata": {}, + "source": [ + "The goals of this case study are:\n", + "\n", + "1. Implement a model of this scenario to predict Spider-Man's\n", + " trajectory.\n", + "\n", + "2. Choose the right time for Spider-Man to let go of the webbing in\n", + " order to maximize the distance he travels before landing.\n", + "\n", + "3. Choose the best angle for Spider-Man to jump off the building, and\n", + " let go of the webbing, to maximize range.\n", + "\n", + "We'll use the following parameters:\n", + "\n", + "1. According to the Spider-Man Wiki (), Spider-Man weighs 76 kg.\n", + "\n", + "2. Let's assume his terminal velocity is 60 m/s.\n", + "\n", + "3. The length of the web is 100 m.\n", + "\n", + "4. The initial angle of the web is 45° to the left of straight down.\n", + "\n", + "5. The spring constant of the web is 40 N/m when the cord is stretched, and 0 when it's compressed.\n", + "\n", + "In the repository for this book, you will find a notebook,\n", + "*spiderman.ipynb*, which contains starter code. \n", + "You can download it from or run it on Colab at .\n", + "\n", + "Read through the\n", + "notebook and run the code. It uses `minimize`, which is a SciPy function that can search for an optimal set of parameters (as contrasted with `minimize_scalar`, which can only search along a single axis)." + ] + }, + { + "cell_type": "markdown", + "id": "african-relative", + "metadata": {}, + "source": [ + "## Kittens\n", + "\n", + "If you have used the Internet, you have probably seen videos of kittens unrolling toilet paper.\n", + "And you might have wondered how long it would take a standard kitten to unroll 47 m of paper, the length of a standard roll.\n", + "\n", + "The interactions of the kitten and the paper rolls are complex. To keep things simple, let's assume that the kitten pulls down on the free end of the roll with constant force. And let's neglect the friction between the roll and the axle.\n", + "\n", + "This diagram shows the paper roll with the force applied by the kitten, $F$, the lever arm of the force around the axis of rotation, $r$, and the resulting torque, $\\tau$.\n", + "\n", + "![Diagram of a roll of toilet paper, showing a force, lever arm, and the resulting torque.](https://github.com/AllenDowney/ModSim/raw/main/figs/kitten.png)\n", + "\n", + "Assuming that the force applied by the kitten is 0.002 N, how long would it take to unroll a standard roll of toilet paper?\n", + "\n", + "In the repository for this book, you will find a notebook,\n", + "*kitten.ipynb*, which contains starter code for this case study. Use it to implement this model and check whether the results seem plausible.\n", + "You can download it from or run it on Colab at ." + ] + }, + { + "cell_type": "markdown", + "id": "future-burlington", + "metadata": {}, + "source": [ + "## Simulating a Yo-yo\n", + "\n", + "Suppose you are holding a yo-yo with a length of string wound around its axle, and you drop it while holding the end of the string stationary. As gravity accelerates the yo-yo downward, tension in the string exerts a force upward. Since this force acts on a point offset from the center of mass, it exerts a torque that causes the yo-yo to spin.\n", + "\n", + "The following diagram shows the forces on the yo-yo and the resulting torque. The outer shaded area shows the body of the yo-yo. The inner shaded area shows the rolled up string, the radius of which changes as the yo-yo unrolls.\n", + "\n", + "![Diagram of a yo-yo showing forces due to gravity and tension in the\n", + "string, the lever arm of tension, and the resulting\n", + "torque.](https://github.com/AllenDowney/ModSim/raw/main/figs/yoyo.png)" + ] + }, + { + "cell_type": "markdown", + "id": "turkish-result", + "metadata": {}, + "source": [ + "In this system, we can't figure out the linear and angular acceleration independently; we have to solve a system of equations: \n", + "\n", + "$$\\begin{aligned}\n", + "\\sum F &= m a \\\\\n", + "\\sum \\tau &= I \\alpha\\end{aligned}$$ \n", + "\n", + "where the summations indicate that we are adding up forces and torques.\n", + "\n", + "As in the previous examples, linear and angular velocity are related\n", + "because of the way the string unrolls:\n", + "\n", + "$$\\frac{dy}{dt} = -r \\frac{d \\theta}{dt}$$ \n", + "\n", + "In this example, the linear and angular accelerations have opposite sign. As the yo-yo rotates counter-clockwise, $\\theta$ increases and $y$, which is the length of the rolled part of the string, decreases." + ] + }, + { + "cell_type": "markdown", + "id": "surrounded-quilt", + "metadata": {}, + "source": [ + "Taking the derivative of both sides yields a similar relationship\n", + "between linear and angular acceleration:\n", + "\n", + "$$\\frac{d^2 y}{dt^2} = -r \\frac{d^2 \\theta}{dt^2}$$ \n", + "\n", + "Which we can write more concisely: \n", + "\n", + "$$a = -r \\alpha$$ \n", + "\n", + "This relationship is not a general law of nature; it is specific to scenarios like this where one object rolls along another without stretching or slipping.\n", + "\n", + "Because of the way we've set up the problem, $y$ actually has two\n", + "meanings: it represents the length of the rolled string and the height\n", + "of the yo-yo, which decreases as the yo-yo falls. Similarly, $a$\n", + "represents acceleration in the length of the rolled string and the\n", + "height of the yo-yo.\n", + "\n", + "We can compute the acceleration of the yo-yo by adding up the linear\n", + "forces: \n", + "\n", + "$$\\sum F = T - mg = ma$$ \n", + "\n", + "Where $T$ is positive because the tension force points up, and $mg$ is negative because gravity points down." + ] + }, + { + "cell_type": "markdown", + "id": "finnish-disaster", + "metadata": {}, + "source": [ + "Because gravity acts on the center of mass, it creates no torque, so the only torque is due to tension: \n", + "\n", + "$$\\sum \\tau = T r = I \\alpha$$ \n", + "\n", + "Positive (upward) tension yields positive (counter-clockwise) angular\n", + "acceleration.\n", + "\n", + "Now we have three equations in three unknowns, $T$, $a$, and $\\alpha$,\n", + "with $I$, $m$, $g$, and $r$ as known parameters. We could solve these equations by hand, but we can also get SymPy to do it for us:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "crude-tribune", + "metadata": {}, + "outputs": [], + "source": [ + "from sympy import symbols, Eq, solve\n", + "\n", + "T, a, alpha, I, m, g, r = symbols('T a alpha I m g r')\n", + "eq1 = Eq(a, -r * alpha)\n", + "eq2 = Eq(T - m*g, m * a)\n", + "eq3 = Eq(T * r, I * alpha)\n", + "soln = solve([eq1, eq2, eq3], [T, a, alpha])\n", + "soln" + ] + }, + { + "cell_type": "markdown", + "id": "insured-indie", + "metadata": {}, + "source": [ + "The results are \n", + "\n", + "$$\\begin{aligned}\n", + "T &= m g I / I^* \\\\\n", + "a &= -m g r^2 / I^* \\\\\n", + "\\alpha &= m g r / I^* \\\\\\end{aligned}$$ \n", + "\n", + "where $I^*$ is the augmented moment of inertia, $I + m r^2$. \n", + "We can use these equations for $a$ and $\\alpha$ to write a slope function and simulate this system.\n", + "\n", + "In the repository for this book, you will find a notebook, *yoyo.ipynb*, which contains starter code you can use to implement and test this model.\n", + "You can download it from or run it on Colab at ." + ] + }, + { + "cell_type": "markdown", + "id": "professional-meeting", + "metadata": {}, + "source": [ + "## Congratulations\n", + "\n", + "With that, you have reached the end of the book, so congratulations! I\n", + "hope you enjoyed it and learned a lot. I think the tools in this book\n", + "are useful, and the ways of thinking are important, not just in\n", + "engineering and science, but in practically every field of inquiry.\n", + "\n", + "Models are the tools we use to understand the world: if you build good\n", + "models, you are more likely to get things right. Good luck!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "leading-paint", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/chapters/chap27.ipynb b/chapters/chap27.ipynb new file mode 100644 index 000000000..6daf5e6b3 --- /dev/null +++ b/chapters/chap27.ipynb @@ -0,0 +1,505 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8b892c8", + "metadata": {}, + "source": [ + "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)." + ] + }, + { + "cell_type": "markdown", + "id": "early-drove", + "metadata": {}, + "source": [ + "# Under the Hood" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "approximate-working", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "suspended-occasion", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "id": "younger-bullet", + "metadata": {}, + "source": [ + "In this appendix we \"open the hood,\" looking more closely at how some of\n", + "the tools we have used work: specifically, `run_solve_ivp`, `root_scalar`, and `maximize_scalar`.\n", + "\n", + "Most of the time you don't need to know, you can use these methods without knowing much about how they work.\n", + "But there are a few reasons you might *want* to know.\n", + "\n", + "One reason is pure curiosity. \n", + "If you use these methods, and especially if you come to rely on them, you might find it unsatisfying to treat them as black boxes. \n", + "In that case, you might enjoy opening the hood.\n", + "\n", + "Another is that these methods are not infallible; sometimes things go wrong. \n", + "If you know how they work, at least in a general sense, you might find it easier to debug them.\n", + "\n", + "And if nothing else, I have found that I can remember how to use these tools more easily because I know something about how they work." + ] + }, + { + "cell_type": "markdown", + "id": "african-collector", + "metadata": {}, + "source": [ + "## How run_solve_ivp Works\n", + "\n", + "`run_solve_ivp` is a function in the ModSimPy library that checks for common errors in the parameters and then calls `solve_ivp`, which is the function in the SciPy library that does the actual work.\n", + "\n", + "By default, `solve_ivp` uses the *Dormand-Prince method*, which is a kind of *Runge-Kutta method*. You can read about it at\n", + ", but I'll give you a sense of\n", + "it here.\n", + "\n", + "The key idea behind all Runge-Kutta methods is to evaluate the slope function several times at each time step and use a weighted average of the computed slopes to estimate the value at the next time step.\n", + "Different methods evaluate the slope function in different places and compute the average with different weights.\n", + "\n", + "So let's see if we can figure out how `solve_ivp` works.\n", + "As an example, we'll solve the following differential equation:\n", + "\n", + "$$\\frac{dy}{dt}(t) = y \\sin t$$\n", + "\n", + "Here's the slope function we'll use:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "coastal-cameroon", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "def slope_func(t, state, system):\n", + " y, = state\n", + " dydt = y * np.sin(t)\n", + " return dydt" + ] + }, + { + "cell_type": "markdown", + "id": "egyptian-inventory", + "metadata": {}, + "source": [ + "I'll create a `State` object with the initial state and a `System` object with the end time." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "recovered-makeup", + "metadata": {}, + "outputs": [], + "source": [ + "init = State(y=1)\n", + "system = System(init=init, t_end=3)" + ] + }, + { + "cell_type": "markdown", + "id": "resident-document", + "metadata": {}, + "source": [ + "Now we can call `run_solve_ivp`." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "younger-affect", + "metadata": {}, + "outputs": [], + "source": [ + "results, details = run_solve_ivp(system, slope_func)\n", + "details" + ] + }, + { + "cell_type": "markdown", + "id": "sophisticated-person", + "metadata": {}, + "source": [ + "One of the variables in `details` is `nfev`, which stands for \"number of function evaluations\", that is, the number of times `solve_ivp` called the slope function.\n", + "This example took 50 evaluations.\n", + "Keep that in mind.\n", + "\n", + "Here are the first few time steps in `results`: " + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "suspended-tumor", + "metadata": {}, + "outputs": [], + "source": [ + "results.head()" + ] + }, + { + "cell_type": "markdown", + "id": "removable-queens", + "metadata": {}, + "source": [ + "And here is the number of time steps." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "noticed-portable", + "metadata": {}, + "outputs": [], + "source": [ + "len(results)" + ] + }, + { + "cell_type": "markdown", + "id": "unable-visibility", + "metadata": {}, + "source": [ + "`results` contains 101 points that are equally spaced in time.\n", + "Now you might wonder, if `solve_ivp` ran the slope function 50 times, how did we get 101 time steps?\n", + "\n", + "To answer that question, we need to know more about how the solver works.\n", + "There are actually three stages:\n", + "\n", + "1. For each time step, `solve_ivp` evaluates the slope function seven times, with different values of `t` and `y`.\n", + "\n", + "2. Using the results, it computes the best estimate for the value `y` at the next time step.\n", + "\n", + "3. After computing all of the time steps, it uses interpolation to compute equally spaced points that connect the estimates from the previous step.\n", + "\n", + "To show the first two steps, I'll modify the slope function so that every time it runs, it adds the values of `t`, `y`, and `dydt` to a list called `evals`." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "efficient-aluminum", + "metadata": {}, + "outputs": [], + "source": [ + "def slope_func(t, state, system):\n", + " y, = state\n", + " dydt = y * np.sin(t)\n", + " evals.append((t, y, dydt))\n", + " return dydt" + ] + }, + { + "cell_type": "markdown", + "id": "compliant-sampling", + "metadata": {}, + "source": [ + "Before we call `run_solve_ivp`, I'll initialize `evals` with an empty list.\n", + "And I'll use the keyword argument `dense_output=False`, which skips the interpolation step and returns time steps that are not equally spaced (that is, not \"dense\")." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "handed-gothic", + "metadata": {}, + "outputs": [], + "source": [ + "evals = []\n", + "results2, details = run_solve_ivp(system, slope_func, dense_output=False)" + ] + }, + { + "cell_type": "markdown", + "id": "mathematical-terrace", + "metadata": {}, + "source": [ + "Here are the results:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "accessory-wrapping", + "metadata": {}, + "outputs": [], + "source": [ + "results2" + ] + }, + { + "cell_type": "markdown", + "id": "worth-baseline", + "metadata": {}, + "source": [ + "Because we skipped the interpolation step, we can see that `solve_ivp` computed only seven time steps, not including the initial condition.\n", + "Also, we see that the time steps are different sizes. \n", + "The first is only 100 microseconds, the second is about 10 times bigger, and the third is 10 times bigger than that.\n", + "\n", + "The time steps are not equal because the Dormand-Prince method is *adaptive*.\n", + "At each time step, it computes two estimates of the next\n", + "value. By comparing them, it can estimate the magnitude of the error,\n", + "which it uses to adjust the time step. If the error is too big, it uses\n", + "a smaller time step; if the error is small enough, it uses a bigger time\n", + "step. By adjusting the time step in this way, it minimizes the number\n", + "of times it calls the slope function to achieve a given level of\n", + "accuracy.\n", + "In this example, it takes five steps to simulate the first second, but then only two more steps to compute the remaining two seconds." + ] + }, + { + "cell_type": "markdown", + "id": "rapid-parks", + "metadata": {}, + "source": [ + "Because we saved the values of `y` and `t`, we can plot the locations where the slope function was evaluated.\n", + "I'll need to use a couple of features we have not seen before, if you don't mind.\n", + "\n", + "First we'll unpack the values from `evals` using `np.transpose`.\n", + "Then we can use trigonometry to convert the slope, `dydt`, to components called `u` and `v`." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "indie-antigua", + "metadata": {}, + "outputs": [], + "source": [ + "t, y, slope = np.transpose(evals)\n", + "theta = np.arctan(slope)\n", + "u = np.cos(theta)\n", + "v = np.sin(theta)" + ] + }, + { + "cell_type": "markdown", + "id": "sublime-significance", + "metadata": {}, + "source": [ + "Using these values, we can generate a *quiver plot* that shows an arrow for each time the slope function ran.\n", + "The location of each arrow represents the values of `t` and `y`; the orientation of the arrow shows the slope that was computed." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "fossil-librarian", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "plt.quiver(t, y, u, v, pivot='middle', \n", + " color='C1', alpha=0.4, label='evaluation points')\n", + "results2['y'].plot(style='o', color='C0', label='solution points')\n", + "results['y'].plot(lw=1, label='interpolation')\n", + "\n", + "decorate(xlabel='Time (t)',\n", + " ylabel='Quantity (y)')" + ] + }, + { + "cell_type": "markdown", + "id": "uniform-cable", + "metadata": {}, + "source": [ + "In this figure, there are 50 arrows, one for each time the slope function was evaluated, and 8 dots, one for each time step (although several of them overlap).\n", + "The line shows the 101 points in the interpolation that connects the estimates.\n", + "\n", + "Notice that many of the arrows do not fall on the line; `solve_ivp` evaluated the slope function at these locations in order to compute the solution, but as it turned out, they are not part of the solution.\n", + "\n", + "This is good to know when you are writing a slope function; you should not assume that the time and state you get as input variables are correct." + ] + }, + { + "cell_type": "markdown", + "id": "split-arabic", + "metadata": {}, + "source": [ + "## How root_scalar Works\n", + "\n", + "`root_scalar` in the ModSim library is a wrapper for a function in the SciPy library with the same name.\n", + "Like `run_solve_ivp`, it checks for common errors and changes some of the parameters in a way that makes the SciPy function easier to use (I hope).\n", + "\n", + "According to the documentation, `root_scalar` uses \"a combination of bisection, secant, and inverse quadratic interpolation methods.\" (See\n", + ")\n", + "\n", + "To understand what that means, suppose we're trying to find a root of a\n", + "function of one variable, $f(x)$, and assume we have evaluated the\n", + "function at two places, $x_1$ and $x_2$, and found that the results have\n", + "opposite signs. Specifically, assume $f(x_1) > 0$ and $f(x_2) < 0$, as\n", + "shown in the following diagram:\n", + "\n", + "![Initial state of a root-finding search](https://github.com/AllenDowney/ModSim/raw/main/figs/secant.png)" + ] + }, + { + "cell_type": "markdown", + "id": "divided-sailing", + "metadata": {}, + "source": [ + "If $f$ is a continuous function, there must be at least one root in this\n", + "interval. In this case we would say that $x_1$ and $x_2$ *bracket* a\n", + "root.\n", + "\n", + "If this were all you knew about $f$, where would you go looking for a\n", + "root? If you said \"halfway between $x_1$ and $x_2$,\" congratulations!\n", + "`You just invented a numerical method called *bisection*!\n", + "\n", + "If you said, \"I would connect the dots with a straight line and compute\n", + "the zero of the line,\" congratulations! You just invented the *secant\n", + "method*!\n", + "\n", + "And if you said, \"I would evaluate $f$ at a third point, find the\n", + "parabola that passes through all three points, and compute the zeros of\n", + "the parabola,\" congratulations, you just invented *inverse quadratic\n", + "interpolation*!\n", + "\n", + "That's most of how `root_scalar` works. The details of how these methods are\n", + "combined are interesting, but beyond the scope of this book. You can\n", + "read more at ." + ] + }, + { + "cell_type": "markdown", + "id": "dental-archives", + "metadata": {}, + "source": [ + "## How maximize_scalar Works \n", + "\n", + "`maximize_scalar` in the ModSim library is a wrapper for a function in the SciPy library called `minimize_scalar`.\n", + "You can read about it at .\n", + "\n", + "By default, it uses Brent's method, which is related to the method I described in the previous section for root-finding.\n", + "Brent's method for finding a maximum or minimum is based on a simpler algorithm:\n", + "the *golden-section search*, which I will explain.\n", + "\n", + "Suppose we're trying to find the minimum of a function of a single variable, $f(x)$.\n", + "As a starting place, assume that we have evaluated the function at three\n", + "places, $x_1$, $x_2$, and $x_3$, and found that $x_2$ yields the lowest\n", + "value. The following diagram shows this initial state.\n", + "\n", + "![Initial state of a golden-section\n", + "search](https://github.com/AllenDowney/ModSim/raw/main/figs/golden1.png)" + ] + }, + { + "cell_type": "markdown", + "id": "alpine-metro", + "metadata": {}, + "source": [ + "We will assume that $f(x)$ is continuous and *unimodal* in this range,\n", + "which means that there is exactly one minimum between $x_1$ and $x_3$.\n", + "\n", + "The next step is to choose a fourth point, $x_4$, and evaluate $f(x_4)$.\n", + "There are two possible outcomes, depending on whether $f(x_4)$ is\n", + "greater than $f(x_2)$ or not.\n", + "The following figure shows the two possible states.\n", + "\n", + "![](https://github.com/AllenDowney/ModSim/raw/main/figs/golden2.png)" + ] + }, + { + "cell_type": "markdown", + "id": "african-check", + "metadata": {}, + "source": [ + "If $f(x_4)$ is less than $f(x_2)$ (shown on the left), the minimum must\n", + "be between $x_2$ and $x_3$, so we would discard $x_1$ and proceed with\n", + "the new bracket $(x_2, x_4, x_3)$.\n", + "\n", + "If $f(x_4)$ is greater than $f(x_2)$ (shown on the right), the local\n", + "minimum must be between $x_1$ and $x_4$, so we would discard $x_3$ and\n", + "proceed with the new bracket $(x_1, x_2, x_4)$.\n", + "\n", + "Either way, the range gets smaller and our estimate of the optimal value\n", + "of $x$ gets better.\n", + "\n", + "This method works for almost any value of $x_4$, but some choices are\n", + "better than others. You might be tempted to bisect the interval between\n", + "$x_2$ and $x_3$, but that turns out not to be optimal. You can\n", + "read about a better option at ." + ] + }, + { + "cell_type": "markdown", + "id": "broken-preparation", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/code/chap01.ipynb b/code/chap01.ipynb deleted file mode 100644 index 5dfe5445f..000000000 --- a/code/chap01.ipynb +++ /dev/null @@ -1,486 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 1\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Jupyter\n", - "\n", - "Welcome to Modeling and Simulation, welcome to Python, and welcome to Jupyter.\n", - "\n", - "This is a Jupyter notebook, which is a development environment where you can write and run Python code. Each notebook is divided into cells. Each cell contains either text (like this cell) or Python code.\n", - "\n", - "### Selecting and running cells\n", - "\n", - "To select a cell, click in the left margin next to the cell. You should see a blue frame surrounding the selected cell.\n", - "\n", - "To edit a code cell, click inside the cell. You should see a green frame around the selected cell, and you should see a cursor inside the cell.\n", - "\n", - "To edit a text cell, double-click inside the cell. Again, you should see a green frame around the selected cell, and you should see a cursor inside the cell.\n", - "\n", - "To run a cell, hold down SHIFT and press ENTER. \n", - "\n", - "* If you run a text cell, Jupyter typesets the text and displays the result.\n", - "\n", - "* If you run a code cell, it runs the Python code in the cell and displays the result, if any.\n", - "\n", - "To try it out, edit this cell, change some of the text, and then press SHIFT-ENTER to run it." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Adding and removing cells\n", - "\n", - "You can add and remove cells from a notebook using the buttons in the toolbar and the items in the menu, both of which you should see at the top of this notebook.\n", - "\n", - "Try the following exercises:\n", - "\n", - "1. From the Insert menu select \"Insert cell below\" to add a cell below this one. By default, you get a code cell, as you can see in the pulldown menu that says \"Code\".\n", - "\n", - "2. In the new cell, add a print statement like `print('Hello')`, and run it.\n", - "\n", - "3. Add another cell, select the new cell, and then click on the pulldown menu that says \"Code\" and select \"Markdown\". This makes the new cell a text cell.\n", - "\n", - "4. In the new cell, type some text, and then run it.\n", - "\n", - "5. Use the arrow buttons in the toolbar to move cells up and down.\n", - "\n", - "6. Use the cut, copy, and paste buttons to delete, add, and move cells.\n", - "\n", - "7. As you make changes, Jupyter saves your notebook automatically, but if you want to make sure, you can press the save button, which looks like a floppy disk from the 1990s.\n", - "\n", - "8. Finally, when you are done with a notebook, select \"Close and Halt\" from the File menu." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using the notebooks\n", - "\n", - "The notebooks for each chapter contain the code from the chapter along with addition examples, explanatory text, and exercises. I recommend you \n", - "\n", - "1. Read the chapter first to understand the concepts and vocabulary, \n", - "2. Run the notebook to review what you learned and see it in action, and then\n", - "3. Attempt the exercises.\n", - "\n", - "If you try to work through the notebooks without reading the book, you're gonna have a bad time. The notebooks contain some explanatory text, but it is probably not enough to make sense if you have not read the book. If you are working through a notebook and you get stuck, you might want to re-read (or read!) the corresponding section of the book." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Importing modsim\n", - "\n", - "The following cell imports `modsim`, which is a collection of functions we will use throughout the book. Whenever you start the notebook, you will have to run the following cell. It does three things:\n", - "\n", - "1. It uses a Jupyter \"magic command\" to specify whether figures should appear in the notebook, or pop up in a new window.\n", - "\n", - "2. It configures Jupyter to display some values that would otherwise be invisible. \n", - "\n", - "3. It imports everything defined in `modsim`.\n", - "\n", - "Select the following cell and press SHIFT-ENTER to run it." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim library\n", - "from modsim import *\n", - "\n", - "print('If this cell runs successfully, it produces no output other than this message.')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The first time you run this on a new installation of Python, it might produce a warning message in pink. That's probably ok, but if you get a message that says `modsim.py depends on Python 3.7 features`, that means you have an older version of Python, and some features in `modsim.py` won't work correctly.\n", - "\n", - "If you need a newer version of Python, I recommend installing Anaconda. You'll find more information in the preface of the book." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## The penny myth\n", - "\n", - "The following cells contain code from the beginning of Chapter 1.\n", - "\n", - "`modsim` defines `UNITS`, which contains variables representing pretty much every unit you've ever heard of. It uses [Pint](https://pint.readthedocs.io/en/latest/), which is a Python library that provides tools for computing with units.\n", - "\n", - "The following lines create new variables named `meter` and `second`." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "meter = UNITS.meter" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "second = UNITS.second" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To find out what other units are defined, type `UNITS.` (including the period) in the next cell and then press TAB. You should see a pop-up menu with a list of units." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a variable named `a` and give it the value of acceleration due to gravity." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "a = 9.8 * meter / second**2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create `t` and give it the value 4 seconds." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "t = 4 * second" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Compute the distance a penny would fall after `t` seconds with constant acceleration `a`. Notice that the units of the result are correct." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "a * t**2 / 2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise**: Compute the velocity of the penny after `t` seconds. Check that the units of the result are correct." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise**: Why would it be nonsensical to add `a` and `t`? What happens if you try?" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The error messages you get from Python are big and scary, but if you read them carefully, they contain a lot of useful information.\n", - "\n", - "1. Start from the bottom and read up.\n", - "2. The last line usually tells you what type of error happened, and sometimes additional information.\n", - "3. The previous lines are a \"traceback\" of what was happening when the error occurred. The first section of the traceback shows the code you wrote. The following sections are often from Python libraries.\n", - "\n", - "In this example, you should get a `DimensionalityError`, which is defined by Pint to indicate that you have violated a rules of dimensional analysis: you cannot add quantities with different dimensions.\n", - "\n", - "Before you go on, you might want to delete the erroneous code so the notebook can run without errors." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Falling pennies\n", - "\n", - "Now let's solve the falling penny problem.\n", - "\n", - "Set `h` to the height of the Empire State Building:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "h = 381 * meter" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Compute the time it would take a penny to fall, assuming constant acceleration.\n", - "\n", - "$ a t^2 / 2 = h $\n", - "\n", - "$ t = \\sqrt{2 h / a}$" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "t = sqrt(2 * h / a)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Given `t`, we can compute the velocity of the penny when it lands.\n", - "\n", - "$v = a t$" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "v = a * t" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can convert from one set of units to another like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "mile = UNITS.mile\n", - "hour= UNITS.hour" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "v.to(mile/hour)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Suppose you bring a 10 foot pole to the top of the Empire State Building and use it to drop the penny from `h` plus 10 feet.\n", - "\n", - "Define a variable named `foot` that contains the unit `foot` provided by `UNITS`. Define a variable named `pole_height` and give it the value 10 feet.\n", - "\n", - "What happens if you add `h`, which is in units of meters, to `pole_height`, which is in units of feet? What happens if you write the addition the other way around?" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** In reality, air resistance limits the velocity of the penny. At about 18 m/s, the force of air resistance equals the force of gravity and the penny stops accelerating.\n", - "\n", - "As a simplification, let's assume that the acceleration of the penny is `a` until the penny reaches 18 m/s, and then 0 afterwards. What is the total time for the penny to fall 381 m?\n", - "\n", - "You can break this question into three parts:\n", - "\n", - "1. How long until the penny reaches 18 m/s with constant acceleration `a`.\n", - "2. How far would the penny fall during that time?\n", - "3. How long to fall the remaining distance with constant velocity 18 m/s?\n", - "\n", - "Suggestion: Assign each intermediate result to a variable with a meaningful name. And assign units to all quantities!" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Restart and run all\n", - "\n", - "When you change the contents of a cell, you have to run it again for those changes to have an effect. If you forget to do that, the results can be confusing, because the code you are looking at is not the code you ran.\n", - "\n", - "If you ever lose track of which cells have run, and in what order, you should go to the Kernel menu and select \"Restart & Run All\". Restarting the kernel means that all of your variables get deleted, and running all the cells means all of your code will run again, in the right order.\n", - "\n", - "**Exercise:** Select \"Restart & Run All\" now and confirm that it does what you want." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap02.ipynb b/code/chap02.ipynb deleted file mode 100644 index 9ed8b78ce..000000000 --- a/code/chap02.ipynb +++ /dev/null @@ -1,924 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 2\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim library\n", - "from modsim import *\n", - "\n", - "# set the random number generator\n", - "np.random.seed(7)\n", - "\n", - "# If this cell runs successfully, it produces no output." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Modeling a bikeshare system" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll start with a `State` object that represents the number of bikes at each station.\n", - "\n", - "When you display a `State` object, it lists the state variables and their values:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare = State(olin=10, wellesley=2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can access the state variables using dot notation." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare.olin" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "bikeshare.wellesley" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** What happens if you spell the name of a state variable wrong? Edit the previous cell, change the spelling of `wellesley`, and run the cell again.\n", - "\n", - "The error message uses the word \"attribute\", which is another name for what we are calling a state variable. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Add a third attribute called `babson` with initial value 0, and display the state of `bikeshare` again." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Updating\n", - "\n", - "We can use the update operators `+=` and `-=` to change state variables." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare.olin -= 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we display `bikeshare`, we should see the change." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Of course, if we subtract a bike from `olin`, we should add it to `wellesley`." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare.wellesley += 1\n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Functions\n", - "\n", - "We can take the code we've written so far and encapsulate it in a function." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def bike_to_wellesley():\n", - " bikeshare.olin -= 1\n", - " bikeshare.wellesley += 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When you define a function, it doesn't run the statements inside the function, yet. When you call the function, it runs the statements inside." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "bike_to_wellesley()\n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "One common error is to omit the parentheses, which has the effect of looking up the function, but not calling it." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "bike_to_wellesley" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The output indicates that `bike_to_wellesley` is a function defined in a \"namespace\" called `__main__`, but you don't have to understand what that means." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Define a function called `bike_to_olin` that moves a bike from Wellesley to Olin. Call the new function and display `bikeshare` to confirm that it works." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Conditionals" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`modsim.py` provides `flip`, which takes a probability and returns either `True` or `False`, which are special values defined by Python.\n", - "\n", - "The Python function `help` looks up a function and displays its documentation." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "help(flip)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the following example, the probability is 0.7 or 70%. If you run this cell several times, you should get `True` about 70% of the time and `False` about 30%." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "flip(0.7)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the following example, we use `flip` as part of an if statement. If the result from `flip` is `True`, we print `heads`; otherwise we do nothing." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "if flip(0.7):\n", - " print('heads')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With an else clause, we can print heads or tails depending on whether `flip` returns `True` or `False`." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "if flip(0.7):\n", - " print('heads')\n", - "else:\n", - " print('tails')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step\n", - "\n", - "Now let's get back to the bikeshare state. Again let's start with a new `State` object." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare = State(olin=10, wellesley=2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Suppose that in any given minute, there is a 50% chance that a student picks up a bike at Olin and rides to Wellesley. We can simulate that like this." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "if flip(0.5):\n", - " bike_to_wellesley()\n", - " print('Moving a bike to Wellesley')\n", - "\n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And maybe at the same time, there is also a 40% chance that a student at Wellesley rides to Olin." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "if flip(0.4):\n", - " bike_to_olin()\n", - " print('Moving a bike to Olin')\n", - "\n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can wrap that code in a function called `step` that simulates one time step. In any given minute, a student might ride from Olin to Wellesley, from Wellesley to Olin, or both, or neither, depending on the results of `flip`." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "def step():\n", - " if flip(0.5):\n", - " bike_to_wellesley()\n", - " print('Moving a bike to Wellesley')\n", - " \n", - " if flip(0.4):\n", - " bike_to_olin()\n", - " print('Moving a bike to Olin')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Since this function takes no parameters, we call it like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "step()\n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameters\n", - "\n", - "As defined in the previous section, `step` is not as useful as it could be, because the probabilities `0.5` and `0.4` are \"hard coded\".\n", - "\n", - "It would be better to generalize this function so it takes the probabilities `p1` and `p2` as parameters:" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "def step(p1, p2):\n", - " if flip(p1):\n", - " bike_to_wellesley()\n", - " print('Moving a bike to Wellesley')\n", - " \n", - " if flip(p2):\n", - " bike_to_olin()\n", - " print('Moving a bike to Olin')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can call it like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "step(0.5, 0.4)\n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** At the beginning of `step`, add a print statement that displays the values of `p1` and `p2`. Call it again with values `0.3`, and `0.2`, and confirm that the values of the parameters are what you expect. " - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## For loop" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Before we go on, I'll redefine `step` without the print statements." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "def step(p1, p2):\n", - " if flip(p1):\n", - " bike_to_wellesley()\n", - " \n", - " if flip(p2):\n", - " bike_to_olin()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And let's start again with a new `State` object:" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare = State(olin=10, wellesley=2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use a `for` loop to move 4 bikes from Olin to Wellesley." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "for i in range(4):\n", - " bike_to_wellesley()\n", - " \n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Or we can simulate 4 random time steps." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "for i in range(4):\n", - " step(0.3, 0.2)\n", - " \n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If each step corresponds to a minute, we can simulate an entire hour like this." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "for i in range(60):\n", - " step(0.3, 0.2)\n", - "\n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After 60 minutes, you might see that the number of bike at Olin is negative. We'll fix that problem in the next notebook.\n", - "\n", - "But first, we want to plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## TimeSeries\n", - "\n", - "`modsim.py` provides an object called a `TimeSeries` that can contain a sequence of values changing over time.\n", - "\n", - "We can create a new, empty `TimeSeries` like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "results = TimeSeries()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we can add a value to the `TimeSeries` like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "results[0] = bikeshare.olin\n", - "results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `0` in brackets is an `index` that indicates that this value is associated with time step 0.\n", - "\n", - "Now we'll use a for loop to save the results of the simulation. I'll start one more time with a new `State` object." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare = State(olin=10, wellesley=2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a for loop that runs 10 steps and stores the results." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "for i in range(10):\n", - " step(0.3, 0.2)\n", - " results[i] = bikeshare.olin" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can display the results." - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `TimeSeries` is a specialized version of a Pandas `Series`, so we can use any of the functions provided by `Series`, including several that compute summary statistics:" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [], - "source": [ - "results.mean()" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [], - "source": [ - "results.describe()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can read the documentation of `Series` [here](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.html)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Plotting\n", - "\n", - "We can also plot the results like this." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "plot(results, label='Olin')\n", - "\n", - "decorate(title='Olin-Wellesley Bikeshare',\n", - " xlabel='Time step (min)', \n", - " ylabel='Number of bikes')\n", - "\n", - "savefig('figs/chap01-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`decorate`, which is defined in the `modsim` library, adds a title and labels the axes." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [], - "source": [ - "help(decorate)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`savefig()` saves a figure in a file." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [], - "source": [ - "help(savefig)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The suffix of the filename indicates the format you want. This example saves the current figure in a PDF file named `chap01-fig01.pdf`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Wrap the code from this section in a function named `run_simulation` that takes three parameters, named `p1`, `p2`, and `num_steps`.\n", - "\n", - "It should:\n", - "\n", - "1. Create a `TimeSeries` object to hold the results.\n", - "2. Use a for loop to run `step` the number of times specified by `num_steps`, passing along the specified values of `p1` and `p2`.\n", - "3. After each step, it should save the number of bikes at Olin in the `TimeSeries`.\n", - "4. After the for loop, it should plot the results and\n", - "5. Decorate the axes.\n", - "\n", - "To test your function:\n", - "\n", - "1. Create a `State` object with the initial state of the system.\n", - "2. Call `run_simulation` with appropriate parameters.\n", - "3. Save the resulting figure.\n", - "\n", - "Optional:\n", - "\n", - "1. Extend your solution so it creates two `TimeSeries` objects, keeps track of the number of bikes at Olin *and* at Wellesley, and plots both series at the end." - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Opening the hood\n", - "\n", - "The functions in `modsim.py` are built on top of several widely-used Python libraries, especially NumPy, SciPy, and Pandas. These libraries are powerful but can be hard to use. The intent of `modsim.py` is to give you the power of these libraries while making it easy to get started.\n", - "\n", - "In the future, you might want to use these libraries directly, rather than using `modsim.py`. So we will pause occasionally to open the hood and let you see how `modsim.py` works.\n", - "\n", - "You don't need to know anything in these sections, so if you are already feeling overwhelmed, you might want to skip them. But if you are curious, read on." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Pandas\n", - "\n", - "This chapter introduces two objects, `State` and `TimeSeries`. Both are based on the `Series` object defined by Pandas, which is a library primarily used for data science.\n", - "\n", - "You can read the documentation of the `Series` object [here](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.html)\n", - "\n", - "The primary differences between `TimeSeries` and `Series` are:\n", - "\n", - "1. I made it easier to create a new, empty `Series` while avoiding a [confusing inconsistency](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.html).\n", - "\n", - "2. I provide a function so the `Series` looks good when displayed in Jupyter.\n", - "\n", - "3. I provide a function called `set` that we'll use later.\n", - "\n", - "`State` has all of those capabilities; in addition, it provides an easier way to initialize state variables, and it provides functions called `T` and `dt`, which will help us avoid a confusing error later." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Pyplot\n", - "\n", - "The `plot` function in `modsim.py` is based on the `plot` function in Pyplot, which is part of Matplotlib. You can read the documentation of `plot` [here](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.plot.html).\n", - "\n", - "`decorate` provides a convenient way to call the `pyplot` functions `title`, `xlabel`, and `ylabel`, and `legend`. It also avoids an annoying warning message if you try to make a legend when you don't have any labelled lines." - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [], - "source": [ - "help(decorate)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### NumPy\n", - "\n", - "The `flip` function in `modsim.py` uses NumPy's `random` function to generate a random number between 0 and 1.\n", - "\n", - "You can get the source code for `flip` by running the following cell." - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [], - "source": [ - "%psource flip" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap03.ipynb b/code/chap03.ipynb deleted file mode 100644 index dd48de529..000000000 --- a/code/chap03.ipynb +++ /dev/null @@ -1,596 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 3\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim library\n", - "from modsim import *\n", - "\n", - "# set the random number generator\n", - "np.random.seed(7)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## More than one State object\n", - "\n", - "Here's the code from the previous chapter, with two changes:\n", - "\n", - "1. I've added DocStrings that explain what each function does, and what parameters it takes.\n", - "\n", - "2. I've added a parameter named `state` to the functions so they work with whatever `State` object we give them, instead of always using `bikeshare`. That makes it possible to work with more than one `State` object." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "def step(state, p1, p2):\n", - " \"\"\"Simulate one minute of time.\n", - " \n", - " state: bikeshare State object\n", - " p1: probability of an Olin->Wellesley customer arrival\n", - " p2: probability of a Wellesley->Olin customer arrival\n", - " \"\"\"\n", - " if flip(p1):\n", - " bike_to_wellesley(state)\n", - " \n", - " if flip(p2):\n", - " bike_to_olin(state)\n", - " \n", - "def bike_to_wellesley(state):\n", - " \"\"\"Move one bike from Olin to Wellesley.\n", - " \n", - " state: bikeshare State object\n", - " \"\"\"\n", - " state.olin -= 1\n", - " state.wellesley += 1\n", - " \n", - "def bike_to_olin(state):\n", - " \"\"\"Move one bike from Wellesley to Olin.\n", - " \n", - " state: bikeshare State object\n", - " \"\"\"\n", - " state.wellesley -= 1\n", - " state.olin += 1\n", - " \n", - "def decorate_bikeshare():\n", - " \"\"\"Add a title and label the axes.\"\"\"\n", - " decorate(title='Olin-Wellesley Bikeshare',\n", - " xlabel='Time step (min)', \n", - " ylabel='Number of bikes')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's `run_simulation`, which is a solution to the exercise at the end of the previous notebook." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(state, p1, p2, num_steps):\n", - " \"\"\"Simulate the given number of time steps.\n", - " \n", - " state: State object\n", - " p1: probability of an Olin->Wellesley customer arrival\n", - " p2: probability of a Wellesley->Olin customer arrival\n", - " num_steps: number of time steps\n", - " \"\"\"\n", - " results = TimeSeries() \n", - " for i in range(num_steps):\n", - " step(state, p1, p2)\n", - " results[i] = state.olin\n", - " \n", - " plot(results, label='Olin')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can create more than one `State` object:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare1 = State(olin=10, wellesley=2)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare2 = State(olin=2, wellesley=10)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Whenever we call a function, we indicate which `State` object to work with:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "bike_to_olin(bikeshare1)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "bike_to_wellesley(bikeshare2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And you can confirm that the different objects are getting updated independently:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare1" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Negative bikes" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the code we have so far, the number of bikes at one of the locations can go negative, and the number of bikes at the other location can exceed the actual number of bikes in the system.\n", - "\n", - "If you run this simulation a few times, it happens often." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare = State(olin=10, wellesley=2)\n", - "run_simulation(bikeshare, 0.4, 0.2, 60)\n", - "decorate_bikeshare()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can fix this problem using the `return` statement to exit the function early if an update would cause negative bikes." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "def bike_to_wellesley(state):\n", - " \"\"\"Move one bike from Olin to Wellesley.\n", - " \n", - " state: bikeshare State object\n", - " \"\"\"\n", - " if state.olin == 0:\n", - " return\n", - " state.olin -= 1\n", - " state.wellesley += 1\n", - " \n", - "def bike_to_olin(state):\n", - " \"\"\"Move one bike from Wellesley to Olin.\n", - " \n", - " state: bikeshare State object\n", - " \"\"\"\n", - " if state.wellesley == 0:\n", - " return\n", - " state.wellesley -= 1\n", - " state.olin += 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now if you run the simulation again, it should behave." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare = State(olin=10, wellesley=2)\n", - "run_simulation(bikeshare, 0.4, 0.2, 60)\n", - "decorate_bikeshare()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Comparison operators" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `if` statements in the previous section used the comparison operator `<`. The other comparison operators are listed in the book.\n", - "\n", - "It is easy to confuse the comparison operator `==` with the assignment operator `=`.\n", - "\n", - "Remember that `=` creates a variable or gives an existing variable a new value." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "x = 5" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Whereas `==` compared two values and returns `True` if they are equal." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "x == 5" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can use `==` in an `if` statement." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "if x == 5:\n", - " print('yes, x is 5')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But if you use `=` in an `if` statement, you get an error." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "# If you remove the # from the if statement and run it, you'll get\n", - "# SyntaxError: invalid syntax\n", - "\n", - "#if x = 5:\n", - "# print('yes, x is 5')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Add an `else` clause to the `if` statement above, and print an appropriate message.\n", - "\n", - "Replace the `==` operator with one or two of the other comparison operators, and confirm they do what you expect." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Metrics" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we have a working simulation, we'll use it to evaluate alternative designs and see how good or bad they are. The metric we'll use is the number of customers who arrive and find no bikes available, which might indicate a design problem." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First we'll make a new `State` object that creates and initializes additional state variables to keep track of the metrics." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare = State(olin=10, wellesley=2, \n", - " olin_empty=0, wellesley_empty=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next we need versions of `bike_to_wellesley` and `bike_to_olin` that update the metrics." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "def bike_to_wellesley(state):\n", - " \"\"\"Move one bike from Olin to Wellesley.\n", - " \n", - " state: bikeshare State object\n", - " \"\"\"\n", - " if state.olin == 0:\n", - " state.olin_empty += 1\n", - " return\n", - " state.olin -= 1\n", - " state.wellesley += 1\n", - " \n", - "def bike_to_olin(state):\n", - " \"\"\"Move one bike from Wellesley to Olin.\n", - " \n", - " state: bikeshare State object\n", - " \"\"\"\n", - " if state.wellesley == 0:\n", - " state.wellesley_empty += 1\n", - " return\n", - " state.wellesley -= 1\n", - " state.olin += 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now when we run a simulation, it keeps track of unhappy customers." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "run_simulation(bikeshare, 0.4, 0.2, 60)\n", - "decorate_bikeshare()\n", - "savefig('figs/chap02-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After the simulation, we can print the number of unhappy customers at each location." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare.olin_empty" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare.wellesley_empty" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Exercise:** As another metric, we might be interested in the time until the first customer arrives and doesn't find a bike. To make that work, we have to add a \"clock\" to keep track of how many time steps have elapsed:\n", - "\n", - "1. Create a new `State` object with an additional state variable, `clock`, initialized to 0. \n", - "\n", - "2. Write a modified version of `step` that adds one to the clock each time it is invoked.\n", - "\n", - "Test your code by running the simulation and check the value of `clock` at the end." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare = State(olin=10, wellesley=2, \n", - " olin_empty=0, wellesley_empty=0,\n", - " clock=0)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Continuing the previous exercise, let's record the time when the first customer arrives and doesn't find a bike.\n", - "\n", - "1. Create a new `State` object with an additional state variable, `t_first_empty`, initialized to -1 as a special value to indicate that it has not been set. \n", - "\n", - "2. Write a modified version of `step` that checks whether`olin_empty` and `wellesley_empty` are 0. If not, it should set `t_first_empty` to `clock` (but only if `t_first_empty` has not already been set).\n", - "\n", - "Test your code by running the simulation and printing the values of `olin_empty`, `wellesley_empty`, and `t_first_empty` at the end." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap04.ipynb b/code/chap04.ipynb deleted file mode 100644 index 138fc23f3..000000000 --- a/code/chap04.ipynb +++ /dev/null @@ -1,677 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 4\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim library\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Returning values" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a simple function that returns a value:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "def add_five(x):\n", - " return x + 5" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's how we call it." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "y = add_five(3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you run a function on the last line of a cell, Jupyter displays the result:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "add_five(5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But that can be a bad habit, because usually if you call a function and don't assign the result in a variable, the result gets discarded.\n", - "\n", - "In the following example, Jupyter shows the second result, but the first result just disappears." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "add_five(3)\n", - "add_five(5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When you call a function that returns a variable, it is generally a good idea to assign the result to a variable." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "y1 = add_five(3)\n", - "y2 = add_five(5)\n", - "\n", - "print(y1, y2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Write a function called `make_state` that creates a `State` object with the state variables `olin=10` and `wellesley=2`, and then returns the new `State` object.\n", - "\n", - "Write a line of code that calls `make_state` and assigns the result to a variable named `init`." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Running simulations" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the code from the previous notebook." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "def step(state, p1, p2):\n", - " \"\"\"Simulate one minute of time.\n", - " \n", - " state: bikeshare State object\n", - " p1: probability of an Olin->Wellesley customer arrival\n", - " p2: probability of a Wellesley->Olin customer arrival\n", - " \"\"\"\n", - " if flip(p1):\n", - " bike_to_wellesley(state)\n", - " \n", - " if flip(p2):\n", - " bike_to_olin(state)\n", - " \n", - "def bike_to_wellesley(state):\n", - " \"\"\"Move one bike from Olin to Wellesley.\n", - " \n", - " state: bikeshare State object\n", - " \"\"\"\n", - " if state.olin == 0:\n", - " state.olin_empty += 1\n", - " return\n", - " state.olin -= 1\n", - " state.wellesley += 1\n", - " \n", - "def bike_to_olin(state):\n", - " \"\"\"Move one bike from Wellesley to Olin.\n", - " \n", - " state: bikeshare State object\n", - " \"\"\"\n", - " if state.wellesley == 0:\n", - " state.wellesley_empty += 1\n", - " return\n", - " state.wellesley -= 1\n", - " state.olin += 1\n", - " \n", - "def decorate_bikeshare():\n", - " \"\"\"Add a title and label the axes.\"\"\"\n", - " decorate(title='Olin-Wellesley Bikeshare',\n", - " xlabel='Time step (min)', \n", - " ylabel='Number of bikes')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a modified version of `run_simulation` that creates a `State` object, runs the simulation, and returns the `State` object." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(p1, p2, num_steps):\n", - " \"\"\"Simulate the given number of time steps.\n", - " \n", - " p1: probability of an Olin->Wellesley customer arrival\n", - " p2: probability of a Wellesley->Olin customer arrival\n", - " num_steps: number of time steps\n", - " \"\"\"\n", - " state = State(olin=10, wellesley=2, \n", - " olin_empty=0, wellesley_empty=0)\n", - " \n", - " for i in range(num_steps):\n", - " step(state, p1, p2)\n", - " \n", - " return state" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now `run_simulation` doesn't plot anything:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "state = run_simulation(0.4, 0.2, 60)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But after the simulation, we can read the metrics from the `State` object." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "state.olin_empty" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run simulations with different values for the parameters. When `p1` is small, we probably don't run out of bikes at Olin." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "state = run_simulation(0.2, 0.2, 60)\n", - "state.olin_empty" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When `p1` is large, we probably do." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "state = run_simulation(0.6, 0.2, 60)\n", - "state.olin_empty" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## More for loops" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`linspace` creates a NumPy array of equally spaced numbers." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "p1_array = linspace(0, 1, 5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use an array in a `for` loop, like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "for p1 in p1_array:\n", - " print(p1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This will come in handy in the next section.\n", - "\n", - "`linspace` is defined in `modsim.py`. You can get the documentation using `help`." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "help(linspace)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`linspace` is based on a NumPy function with the same name. [Click here](https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html) to read more about how to use it." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** \n", - "Use `linspace` to make an array of 10 equally spaced numbers from 1 to 10 (including both)." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** The `modsim` library provides a related function called `linrange`. You can view the documentation by running the following cell:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "help(linrange)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use `linrange` to make an array of numbers from 1 to 11 with a step size of 2." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Sweeping parameters" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`p1_array` contains a range of values for `p1`." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "p2 = 0.2\n", - "num_steps = 60\n", - "p1_array = linspace(0, 1, 11)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following loop runs a simulation for each value of `p1` in `p1_array`; after each simulation, it prints the number of unhappy customers at the Olin station:" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "for p1 in p1_array:\n", - " state = run_simulation(p1, p2, num_steps)\n", - " print(p1, state.olin_empty)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can do the same thing, but storing the results in a `SweepSeries` instead of printing them.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "sweep = SweepSeries()\n", - "\n", - "for p1 in p1_array:\n", - " state = run_simulation(p1, p2, num_steps)\n", - " sweep[p1] = state.olin_empty" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And then we can plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "plot(sweep, label='Olin')\n", - "\n", - "decorate(title='Olin-Wellesley Bikeshare',\n", - " xlabel='Arrival rate at Olin (p1 in customers/min)', \n", - " ylabel='Number of unhappy customers')\n", - "\n", - "savefig('figs/chap02-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Exercise:** Wrap this code in a function named `sweep_p1` that takes an array called `p1_array` as a parameter. It should create a new `SweepSeries`, run a simulation for each value of `p1` in `p1_array`, store the results in the `SweepSeries`, and return the `SweepSeries`.\n", - "\n", - "Use your function to plot the number of unhappy customers at Olin as a function of `p1`. Label the axes." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Write a function called `sweep_p2` that runs simulations with `p1=0.5` and a range of values for `p2`. It should store the results in a `SweepSeries` and return the `SweepSeries`.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Optional exercises\n", - "\n", - "The following two exercises are a little more challenging. If you are comfortable with what you have learned so far, you should give them a try. If you feel like you have your hands full, you might want to skip them for now.\n", - "\n", - "**Exercise:** Because our simulations are random, the results vary from one run to another, and the results of a parameter sweep tend to be noisy. We can get a clearer picture of the relationship between a parameter and a metric by running multiple simulations with the same parameter and taking the average of the results.\n", - "\n", - "Write a function called `run_multiple_simulations` that takes as parameters `p1`, `p2`, `num_steps`, and `num_runs`.\n", - "\n", - "`num_runs` specifies how many times it should call `run_simulation`.\n", - "\n", - "After each run, it should store the total number of unhappy customers (at Olin or Wellesley) in a `TimeSeries`. At the end, it should return the `TimeSeries`.\n", - "\n", - "Test your function with parameters\n", - "\n", - "```\n", - "p1 = 0.3\n", - "p2 = 0.3\n", - "num_steps = 60\n", - "num_runs = 10\n", - "```\n", - "\n", - "Display the resulting `TimeSeries` and use the `mean` function provided by the `TimeSeries` object to compute the average number of unhappy customers." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Continuting the previous exercise, use `run_multiple_simulations` to run simulations with a range of values for `p1` and\n", - "\n", - "```\n", - "p2 = 0.3\n", - "num_steps = 60\n", - "num_runs = 20\n", - "```\n", - "\n", - "Store the results in a `SweepSeries`, then plot the average number of unhappy customers as a function of `p1`. Label the axes.\n", - "\n", - "What value of `p1` minimizes the average number of unhappy customers?" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap05.ipynb b/code/chap05.ipynb deleted file mode 100644 index 72f2db303..000000000 --- a/code/chap05.ipynb +++ /dev/null @@ -1,576 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 5\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Reading data\n", - "\n", - "Pandas is a library that provides tools for reading and processing data. `read_html` reads a web page from a file or the Internet and creates one `DataFrame` for each table on the page." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "from pandas import read_html" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The data directory contains a downloaded copy of https://en.wikipedia.org/wiki/World_population_estimates\n", - "\n", - "The arguments of `read_html` specify the file to read and how to interpret the tables in the file. The result, `tables`, is a sequence of `DataFrame` objects; `len(tables)` reports the length of the sequence." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "filename = 'data/World_population_estimates.html'\n", - "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", - "len(tables)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can select the `DataFrame` we want using the bracket operator. The tables are numbered from 0, so `tables[2]` is actually the third table on the page.\n", - "\n", - "`head` selects the header and the first five rows." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "table2 = tables[2]\n", - "table2.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`tail` selects the last five rows." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "table2.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Long column names are awkard to work with, but we can replace them with abbreviated names." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "table2.columns = ['census', 'prb', 'un', 'maddison', \n", - " 'hyde', 'tanton', 'biraben', 'mj', \n", - " 'thomlinson', 'durand', 'clark']" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the DataFrame looks like now. " - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "table2.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The first column, which is labeled `Year`, is special. It is the **index** for this `DataFrame`, which means it contains the labels for the rows.\n", - "\n", - "Some of the values use scientific notation; for example, `2.544000e+09` is shorthand for $2.544 \\cdot 10^9$ or 2.544 billion.\n", - "\n", - "`NaN` is a special value that indicates missing data." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Series\n", - "\n", - "We can use dot notation to select a column from a `DataFrame`. The result is a `Series`, which is like a `DataFrame` with a single column." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "census = table2.census\n", - "census.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "census.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Like a `DataFrame`, a `Series` contains an index, which labels the rows.\n", - "\n", - "`1e9` is scientific notation for $1 \\cdot 10^9$ or 1 billion." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "From here on, we will work in units of billions." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "un = table2.un / 1e9\n", - "un.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "census = table2.census / 1e9\n", - "census.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what these estimates look like." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "scrolled": false - }, - "outputs": [], - "source": [ - "plot(census, ':', label='US Census')\n", - "plot(un, '--', label='UN DESA')\n", - " \n", - "decorate(xlabel='Year',\n", - " ylabel='World population (billion)')\n", - "savefig('figs/chap03-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following expression computes the elementwise differences between the two series, then divides through by the UN value to produce [relative errors](https://en.wikipedia.org/wiki/Approximation_error), then finds the largest element.\n", - "\n", - "So the largest relative error between the estimates is about 1.3%." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "max(abs(census - un) / un) * 100" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Break down that expression into smaller steps and display the intermediate results, to make sure you understand how it works.\n", - "\n", - "1. Compute the elementwise differences, `census - un`\n", - "2. Compute the absolute differences, `abs(census - un)`\n", - "3. Compute the relative differences, `abs(census - un) / un`\n", - "4. Compute the percent differences, `abs(census - un) / un * 100`\n" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`max` and `abs` are built-in functions provided by Python, but NumPy also provides version that are a little more general. When you import `modsim`, you get the NumPy versions of these functions." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Constant growth" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can select a value from a `Series` using bracket notation. Here's the first element:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "census[1950]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And the last value." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "census[2016]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But rather than \"hard code\" those dates, we can get the first and last labels from the `Series`:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "t_0 = get_first_label(census)" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "t_end = get_last_label(census)" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "elapsed_time = t_end - t_0" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we can get the first and last values:" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "p_0 = get_first_value(census)" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "p_end = get_last_value(census)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then we can compute the average annual growth in billions of people per year." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "total_growth = p_end - p_0" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "annual_growth = total_growth / elapsed_time" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### TimeSeries" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's create a `TimeSeries` to contain values generated by a linear growth model." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "results = TimeSeries()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Initially the `TimeSeries` is empty, but we can initialize it so the starting value, in 1950, is the 1950 population estimated by the US Census." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "results[t_0] = census[t_0]\n", - "results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After that, the population in the model grows by a constant amount each year." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "for t in linrange(t_0, t_end):\n", - " results[t+1] = results[t] + annual_growth" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the results looks like, compared to the actual data." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "plot(census, ':', label='US Census')\n", - "plot(un, '--', label='UN DESA')\n", - "plot(results, color='gray', label='model')\n", - "\n", - "decorate(xlabel='Year', \n", - " ylabel='World population (billion)',\n", - " title='Constant growth')\n", - "savefig('figs/chap03-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The model fits the data pretty well after 1990, but not so well before." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises\n", - "\n", - "**Optional Exercise:** Try fitting the model using data from 1970 to the present, and see if that does a better job.\n", - "\n", - "Hint: \n", - "\n", - "1. Copy the code from above and make a few changes. Test your code after each small change.\n", - "\n", - "2. Make sure your `TimeSeries` starts in 1950, even though the estimated annual growth is based on later data.\n", - "\n", - "3. You might want to add a constant to the starting value to match the data better." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap06.ipynb b/code/chap06.ipynb deleted file mode 100644 index 20ac99f57..000000000 --- a/code/chap06.ipynb +++ /dev/null @@ -1,536 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 6\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *\n", - "\n", - "from pandas import read_html" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Code from the previous chapter\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "filename = 'data/World_population_estimates.html'\n", - "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", - "table2 = tables[2]\n", - "table2.columns = ['census', 'prb', 'un', 'maddison', \n", - " 'hyde', 'tanton', 'biraben', 'mj', \n", - " 'thomlinson', 'durand', 'clark']" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "un = table2.un / 1e9\n", - "un.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "census = table2.census / 1e9\n", - "census.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "t_0 = get_first_label(census)\n", - "t_end = get_last_label(census)\n", - "elapsed_time = t_end - t_0\n", - "\n", - "p_0 = get_first_value(census)\n", - "p_end = get_last_value(census)\n", - "total_growth = p_end - p_0\n", - "\n", - "annual_growth = total_growth / elapsed_time" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### System objects" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can rewrite the code from the previous chapter using system objects." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "system = System(t_0=t_0, \n", - " t_end=t_end,\n", - " p_0=p_0,\n", - " annual_growth=annual_growth)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we can encapsulate the code that runs the model in a function." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation1(system):\n", - " \"\"\"Runs the constant growth model.\n", - " \n", - " system: System object\n", - " \n", - " returns: TimeSeries\n", - " \"\"\"\n", - " results = TimeSeries()\n", - " results[system.t_0] = system.p_0\n", - " \n", - " for t in linrange(system.t_0, system.t_end):\n", - " results[t+1] = results[t] + system.annual_growth\n", - " \n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also encapsulate the code that plots the results." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_results(census, un, timeseries, title):\n", - " \"\"\"Plot the estimates and the model.\n", - " \n", - " census: TimeSeries of population estimates\n", - " un: TimeSeries of population estimates\n", - " timeseries: TimeSeries of simulation results\n", - " title: string\n", - " \"\"\"\n", - " plot(census, ':', label='US Census')\n", - " plot(un, '--', label='UN DESA')\n", - " plot(timeseries, color='gray', label='model')\n", - " \n", - " decorate(xlabel='Year', \n", - " ylabel='World population (billion)',\n", - " title=title)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we run it." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "results = run_simulation1(system)\n", - "plot_results(census, un, results, 'Constant growth model')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Proportional growth" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a more realistic model where the number of births and deaths is proportional to the current population." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation2(system):\n", - " \"\"\"Run a model with proportional birth and death.\n", - " \n", - " system: System object\n", - " \n", - " returns: TimeSeries\n", - " \"\"\"\n", - " results = TimeSeries()\n", - " results[system.t_0] = system.p_0\n", - " \n", - " for t in linrange(system.t_0, system.t_end):\n", - " births = system.birth_rate * results[t]\n", - " deaths = system.death_rate * results[t]\n", - " results[t+1] = results[t] + births - deaths\n", - " \n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "I picked a death rate that seemed reasonable and then adjusted the birth rate to fit the data." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "system.death_rate = 0.01\n", - "system.birth_rate = 0.027" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what it looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "results = run_simulation2(system)\n", - "plot_results(census, un, results, 'Proportional model')\n", - "savefig('figs/chap03-fig03.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The model fits the data pretty well for the first 20 years, but not so well after that." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Factoring out the update function" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`run_simulation1` and `run_simulation2` are nearly identical except the body of the loop. So we can factor that part out into a function." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def update_func1(pop, t, system):\n", - " \"\"\"Compute the population next year.\n", - " \n", - " pop: current population\n", - " t: current year\n", - " system: system object containing parameters of the model\n", - " \n", - " returns: population next year\n", - " \"\"\"\n", - " births = system.birth_rate * pop\n", - " deaths = system.death_rate * pop\n", - " return pop + births - deaths" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The name `update_func` refers to a function object." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "update_func1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Which we can confirm by checking its type." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "type(update_func1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`run_simulation` takes the update function as a parameter and calls it just like any other function." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Simulate the system using any update function.\n", - " \n", - " system: System object\n", - " update_func: function that computes the population next year\n", - " \n", - " returns: TimeSeries\n", - " \"\"\"\n", - " results = TimeSeries()\n", - " results[system.t_0] = system.p_0\n", - " \n", - " for t in linrange(system.t_0, system.t_end):\n", - " results[t+1] = update_func(results[t], t, system)\n", - " \n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we use it." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "t_0 = get_first_label(census)\n", - "t_end = get_last_label(census)\n", - "p_0 = census[t_0]\n", - "\n", - "system = System(t_0=t_0, \n", - " t_end=t_end,\n", - " p_0=p_0,\n", - " birth_rate=0.027,\n", - " death_rate=0.01)" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "results = run_simulation(system, update_func1)\n", - "plot_results(census, un, results, 'Proportional model, factored')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Remember not to put parentheses after `update_func1`. What happens if you try?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** When you run `run_simulation`, it runs `update_func1` once for each year between `t_0` and `t_end`. To see that for yourself, add a print statement at the beginning of `update_func1` that prints the values of `t` and `pop`, then run `run_simulation` again." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Combining birth and death" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Since births and deaths get added up, we don't have to compute them separately. We can combine the birth and death rates into a single net growth rate." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def update_func2(pop, t, system):\n", - " \"\"\"Compute the population next year.\n", - " \n", - " pop: current population\n", - " t: current year\n", - " system: system object containing parameters of the model\n", - " \n", - " returns: population next year\n", - " \"\"\"\n", - " net_growth = system.alpha * pop\n", - " return pop + net_growth" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how it works:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "system.alpha = system.birth_rate - system.death_rate\n", - "\n", - "results = run_simulation(system, update_func2)\n", - "plot_results(census, un, results, 'Proportional model, combined birth and death')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises\n", - "\n", - "**Exercise:** Maybe the reason the proportional model doesn't work very well is that the growth rate, `alpha`, is changing over time. So let's try a model with different growth rates before and after 1980 (as an arbitrary choice).\n", - "\n", - "Write an update function that takes `pop`, `t`, and `system` as parameters. The system object, `system`, should contain two parameters: the growth rate before 1980, `alpha1`, and the growth rate after 1980, `alpha2`. It should use `t` to determine which growth rate to use. Note: Don't forget the `return` statement.\n", - "\n", - "Test your function by calling it directly, then pass it to `run_simulation`. Plot the results. Adjust the parameters `alpha1` and `alpha2` to fit the data as well as you can.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": true, - "scrolled": false - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap07.ipynb b/code/chap07.ipynb deleted file mode 100644 index 5beafe79a..000000000 --- a/code/chap07.ipynb +++ /dev/null @@ -1,534 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 7\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *\n", - "\n", - "from pandas import read_html" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Code from the previous chapter" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "filename = 'data/World_population_estimates.html'\n", - "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", - "table2 = tables[2]\n", - "table2.columns = ['census', 'prb', 'un', 'maddison', \n", - " 'hyde', 'tanton', 'biraben', 'mj', \n", - " 'thomlinson', 'durand', 'clark']" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "un = table2.un / 1e9\n", - "un.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "census = table2.census / 1e9\n", - "census.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_results(census, un, timeseries, title):\n", - " \"\"\"Plot the estimates and the model.\n", - " \n", - " census: TimeSeries of population estimates\n", - " un: TimeSeries of population estimates\n", - " timeseries: TimeSeries of simulation results\n", - " title: string\n", - " \"\"\"\n", - " plot(census, ':', label='US Census')\n", - " plot(un, '--', label='UN DESA')\n", - " plot(timeseries, color='gray', label='model')\n", - " \n", - " decorate(xlabel='Year', \n", - " ylabel='World population (billion)',\n", - " title=title)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Simulate the system using any update function.\n", - " \n", - " system: System object\n", - " update_func: function that computes the population next year\n", - " \n", - " returns: TimeSeries\n", - " \"\"\"\n", - " results = TimeSeries()\n", - " results[system.t_0] = system.p_0\n", - " \n", - " for t in linrange(system.t_0, system.t_end):\n", - " results[t+1] = update_func(results[t], t, system)\n", - " \n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Quadratic growth" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the implementation of the quadratic growth model." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def update_func_quad(pop, t, system):\n", - " \"\"\"Compute the population next year with a quadratic model.\n", - " \n", - " pop: current population\n", - " t: current year\n", - " system: system object containing parameters of the model\n", - " \n", - " returns: population next year\n", - " \"\"\"\n", - " net_growth = system.alpha * pop + system.beta * pop**2\n", - " return pop + net_growth" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a `System` object with the parameters `alpha` and `beta`:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "t_0 = get_first_label(census)\n", - "t_end = get_last_label(census)\n", - "p_0 = census[t_0]\n", - "\n", - "system = System(t_0=t_0, \n", - " t_end=t_end,\n", - " p_0=p_0,\n", - " alpha=0.025,\n", - " beta=-0.0018)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here are the results." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "results = run_simulation(system, update_func_quad)\n", - "plot_results(census, un, results, 'Quadratic model')\n", - "savefig('figs/chap03-fig04.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Can you find values for the parameters that make the model fit better?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Equilibrium\n", - "\n", - "To understand the quadratic model better, let's plot net growth as a function of population." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "pop_array = linspace(0, 15, 100)\n", - "net_growth_array = system.alpha * pop_array + system.beta * pop_array**2\n", - "None" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what it looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "sns.set_style('whitegrid')\n", - "\n", - "plot(pop_array, net_growth_array)\n", - "decorate(xlabel='Population (billions)',\n", - " ylabel='Net growth (billions)')\n", - "savefig('figs/chap03-fig05.pdf')\n", - "\n", - "sns.set_style('white')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what it looks like. Remember that the x axis is population now, not time." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It looks like the growth rate passes through 0 when the population is a little less than 14 billion.\n", - "\n", - "In the book we found that the net growth is 0 when the population is $-\\alpha/\\beta$:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "-system.alpha / system.beta" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This is the equilibrium the population tends toward." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`sns` is a library called Seaborn which provides functions that control the appearance of plots. In this case I want a grid to make it easier to estimate the population where the growth rate crosses through 0." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Dysfunctions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When people first learn about functions, there are a few things they often find confusing. In this section I present and explain some common problems with functions.\n", - "\n", - "As an example, suppose you want a function that takes a `System` object, with variables `alpha` and `beta`, as a parameter and computes the carrying capacity, `-alpha/beta`. Here's a good solution:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "def carrying_capacity(system):\n", - " K = -system.alpha / system.beta\n", - " return K\n", - " \n", - "sys1 = System(alpha=0.025, beta=-0.0018)\n", - "pop = carrying_capacity(sys1)\n", - "print(pop)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's see all the ways that can go wrong.\n", - "\n", - "**Dysfunction #1:** Not using parameters. In the following version, the function doesn't take any parameters; when `sys1` appears inside the function, it refers to the object we created outside the function.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "def carrying_capacity():\n", - " K = -sys1.alpha / sys1.beta\n", - " return K\n", - " \n", - "sys1 = System(alpha=0.025, beta=-0.0018)\n", - "pop = carrying_capacity()\n", - "print(pop)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This version actually works, but it is not as versatile as it could be. If there are several `System` objects, this function can only work with one of them, and only if it is named `system`.\n", - "\n", - "**Dysfunction #2:** Clobbering the parameters. When people first learn about parameters, they often write functions like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "def carrying_capacity(system):\n", - " system = System(alpha=0.025, beta=-0.0018)\n", - " K = -system.alpha / system.beta\n", - " return K\n", - " \n", - "sys1 = System(alpha=0.025, beta=-0.0018)\n", - "pop = carrying_capacity(sys1)\n", - "print(pop)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this example, we have a `System` object named `sys1` that gets passed as an argument to `carrying_capacity`. But when the function runs, it ignores the argument and immediately replaces it with a new `System` object. As a result, this function always returns the same value, no matter what argument is passed.\n", - "\n", - "When you write a function, you generally don't know what the values of the parameters will be. Your job is to write a function that works for any valid values. If you assign your own values to the parameters, you defeat the whole purpose of functions.\n", - "\n", - "\n", - "**Dysfunction #3:** No return value. Here's a version that computes the value of `K` but doesn't return it." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "def carrying_capacity(system):\n", - " K = -system.alpha / system.beta\n", - " \n", - "sys1 = System(alpha=0.025, beta=-0.0018)\n", - "pop = carrying_capacity(sys1)\n", - "print(pop)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A function that doesn't have a return statement always returns a special value called `None`, so in this example the value of `pop` is `None`. If you are debugging a program and find that the value of a variable is `None` when it shouldn't be, a function without a return statement is a likely cause.\n", - "\n", - "**Dysfunction #4:** Ignoring the return value. Finally, here's a version where the function is correct, but the way it's used is not." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "def carrying_capacity(system):\n", - " K = -system.alpha / system.beta\n", - " return K\n", - " \n", - "sys2 = System(alpha=0.025, beta=-0.0018)\n", - "carrying_capacity(sys2)\n", - "\n", - "# print(K) This line won't work because K only exists inside the function." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this example, `carrying_capacity` runs and returns `K`, but the return value is dropped.\n", - "\n", - "When you call a function that returns a value, you should do something with the result. Often you assign it to a variable, as in the previous examples, but you can also use it as part of an expression.\n", - "\n", - "For example, you could eliminate the temporary variable `pop` like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "print(carrying_capacity(sys1))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Or if you had more than one system, you could compute the total carrying capacity like this:\n" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "total = carrying_capacity(sys1) + carrying_capacity(sys2)\n", - "total" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Exercise:** In the book, I present a different way to parameterize the quadratic model:\n", - "\n", - "$ \\Delta p = r p (1 - p / K) $\n", - "\n", - "where $r=\\alpha$ and $K=-\\alpha/\\beta$. Write a version of `update_func` that implements this version of the model. Test it by computing the values of `r` and `K` that correspond to `alpha=0.025, beta=-0.0018`, and confirm that you get the same results. " - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap08.ipynb b/code/chap08.ipynb deleted file mode 100644 index 0f16233f1..000000000 --- a/code/chap08.ipynb +++ /dev/null @@ -1,571 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 8\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *\n", - "\n", - "from pandas import read_html" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Functions from the previous chapter" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def plot_results(census, un, timeseries, title):\n", - " \"\"\"Plot the estimates and the model.\n", - " \n", - " census: TimeSeries of population estimates\n", - " un: TimeSeries of population estimates\n", - " timeseries: TimeSeries of simulation results\n", - " title: string\n", - " \"\"\"\n", - " plot(census, ':', label='US Census')\n", - " plot(un, '--', label='UN DESA')\n", - " plot(timeseries, color='gray', label='model')\n", - " \n", - " decorate(xlabel='Year', \n", - " ylabel='World population (billion)',\n", - " title=title)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Simulate the system using any update function.\n", - " \n", - " system: System object\n", - " update_func: function that computes the population next year\n", - " \n", - " returns: TimeSeries\n", - " \"\"\"\n", - " results = TimeSeries()\n", - " results[system.t_0] = system.p_0\n", - " \n", - " for t in linrange(system.t_0, system.t_end):\n", - " results[t+1] = update_func(results[t], t, system)\n", - " \n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Reading the data" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "filename = 'data/World_population_estimates.html'\n", - "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", - "table2 = tables[2]\n", - "table2.columns = ['census', 'prb', 'un', 'maddison', \n", - " 'hyde', 'tanton', 'biraben', 'mj', \n", - " 'thomlinson', 'durand', 'clark']" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "un = table2.un / 1e9\n", - "census = table2.census / 1e9\n", - "plot(census, ':', label='US Census')\n", - "plot(un, '--', label='UN DESA')\n", - " \n", - "decorate(xlabel='Year', \n", - " ylabel='World population (billion)',\n", - " title='Estimated world population')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Running the quadratic model" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the update function for the quadratic growth model with parameters `alpha` and `beta`." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def update_func_quad(pop, t, system):\n", - " \"\"\"Update population based on a quadratic model.\n", - " \n", - " pop: current population in billions\n", - " t: what year it is\n", - " system: system object with model parameters\n", - " \"\"\"\n", - " net_growth = system.alpha * pop + system.beta * pop**2\n", - " return pop + net_growth" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Extract the starting time and population." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "t_0 = get_first_label(census)\n", - "t_end = get_last_label(census)\n", - "p_0 = get_first_value(census)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Initialize the system object." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "system = System(t_0=t_0, \n", - " t_end=t_end,\n", - " p_0=p_0,\n", - " alpha=0.025,\n", - " beta=-0.0018)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Run the model and plot results." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "results = run_simulation(system, update_func_quad)\n", - "plot_results(census, un, results, 'Quadratic model')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Generating projections" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To generate projections, all we have to do is change `t_end`" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "scrolled": false - }, - "outputs": [], - "source": [ - "system.t_end = 2250\n", - "results = run_simulation(system, update_func_quad)\n", - "plot_results(census, un, results, 'World population projection')\n", - "savefig('figs/chap04-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The population in the model converges on the equilibrium population, `-alpha/beta`" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "results[system.t_end]" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "-system.alpha / system.beta" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** What happens if we start with an initial population above the carrying capacity, like 20 billion? Run the model with initial populations between 1 and 20 billion, and plot the results on the same axes." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Comparing projections" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can compare the projection from our model with projections produced by people who know what they are doing." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "table3 = tables[3]\n", - "table3.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`NaN` is a special value that represents missing data, in this case because some agencies did not publish projections for some years." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "table3.columns = ['census', 'prb', 'un']" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This function plots projections from the UN DESA and U.S. Census. It uses `dropna` to remove the `NaN` values from each series before plotting it." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def plot_projections(table):\n", - " \"\"\"Plot world population projections.\n", - " \n", - " table: DataFrame with columns 'un' and 'census'\n", - " \"\"\"\n", - " census_proj = table.census / 1e9\n", - " un_proj = table.un / 1e9\n", - " \n", - " plot(census_proj.dropna(), 'b:', label='US Census')\n", - " plot(un_proj.dropna(), 'g--', label='UN DESA')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Run the model until 2100, which is as far as the other projections go." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "system = System(t_0=t_0, \n", - " t_end=2100,\n", - " p_0=p_0,\n", - " alpha=0.025,\n", - " beta=-0.0018)" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "results = run_simulation(system, update_func_quad)\n", - "\n", - "plot_results(census, un, results, 'World population projections')\n", - "plot_projections(table3)\n", - "savefig('figs/chap04-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "People who know what they are doing expect the growth rate to decline more sharply than our model projects." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Optional exercise:** The net growth rate of world population has been declining for several decades. That observation suggests one more way to generate projections, by extrapolating observed changes in growth rate.\n", - "\n", - "The `modsim` library provides a function, `compute_rel_diff`, that computes relative differences of the elements in a sequence. It is a wrapper for the NumPy function `ediff1d`:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "%psource compute_rel_diff" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we can use it to compute the relative differences in the `census` and `un` estimates:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "alpha_census = compute_rel_diff(census)\n", - "plot(alpha_census)\n", - "\n", - "alpha_un = compute_rel_diff(un)\n", - "plot(alpha_un)\n", - "\n", - "decorate(xlabel='Year', label='Net growth rate')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Other than a bump around 1990, net growth rate has been declining roughly linearly since 1965. As an exercise, you can use this data to make a projection of world population until 2100.\n", - "\n", - "1. Define a function, `alpha_func`, that takes `t` as a parameter and returns an estimate of the net growth rate at time `t`, based on a linear function `alpha = intercept + slope * t`. Choose values of `slope` and `intercept` to fit the observed net growth rates since 1965.\n", - "\n", - "2. Call your function with a range of `ts` from 1960 to 2020 and plot the results.\n", - "\n", - "3. Create a `System` object that includes `alpha_func` as a system variable.\n", - "\n", - "4. Define an update function that uses `alpha_func` to compute the net growth rate at the given time `t`.\n", - "\n", - "5. Test your update function with `t_0 = 1960` and `p_0 = census[t_0]`.\n", - "\n", - "6. Run a simulation from 1960 to 2100 with your update function, and plot the results.\n", - "\n", - "7. Compare your projections with those from the US Census and UN." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Related viewing:** You might be interested in this [video by Hans Rosling about the demographic changes we expect in this century](https://www.youtube.com/watch?v=ezVk1ahRF78)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap09.ipynb b/code/chap09.ipynb deleted file mode 100644 index 5289f4a26..000000000 --- a/code/chap09.ipynb +++ /dev/null @@ -1,632 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 9\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import everything from SymPy.\n", - "from sympy import *\n", - "\n", - "# Set up Jupyter notebook to display math.\n", - "init_printing() " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following displays SymPy expressions and provides the option of showing results in LaTeX format." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from sympy.printing import latex\n", - "\n", - "def show(expr, show_latex=False):\n", - " \"\"\"Display a SymPy expression.\n", - " \n", - " expr: SymPy expression\n", - " show_latex: boolean\n", - " \"\"\"\n", - " if show_latex:\n", - " print(latex(expr))\n", - " return expr" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Analysis with SymPy" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a symbol for time." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "t = symbols('t')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you combine symbols and numbers, you get symbolic expressions." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "expr = t + 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result is an `Add` object, which just represents the sum without trying to compute it." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "type(expr)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`subs` can be used to replace a symbol with a number, which allows the addition to proceed." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "expr.subs(t, 2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`f` is a special class of symbol that represents a function." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "f = Function('f')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The type of `f` is `UndefinedFunction`" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "type(f)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "SymPy understands that `f(t)` means `f` evaluated at `t`, but it doesn't try to evaluate it yet." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "f(t)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`diff` returns a `Derivative` object that represents the time derivative of `f`" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "dfdt = diff(f(t), t)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "type(dfdt)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We need a symbol for `alpha`" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "alpha = symbols('alpha')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can write the differential equation for proportional growth." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "eq1 = Eq(dfdt, alpha*f(t))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And use `dsolve` to solve it. The result is the general solution." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "solution_eq = dsolve(eq1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can tell it's a general solution because it contains an unspecified constant, `C1`.\n", - "\n", - "In this example, finding the particular solution is easy: we just replace `C1` with `p_0`" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "C1, p_0 = symbols('C1 p_0')" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "particular = solution_eq.subs(C1, p_0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the next example, we have to work a little harder to find the particular solution." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Solving the quadratic growth equation \n", - "\n", - "We'll use the (r, K) parameterization, so we'll need two more symbols:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "r, K = symbols('r K')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can write the differential equation." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "eq2 = Eq(diff(f(t), t), r * f(t) * (1 - f(t)/K))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And solve it." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "solution_eq = dsolve(eq2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result, `solution_eq`, contains `rhs`, which is the right-hand side of the solution." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "general = solution_eq.rhs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can evaluate the right-hand side at $t=0$" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "at_0 = general.subs(t, 0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we want to find the value of `C1` that makes `f(0) = p_0`.\n", - "\n", - "So we'll create the equation `at_0 = p_0` and solve for `C1`. Because this is just an algebraic identity, not a differential equation, we use `solve`, not `dsolve`.\n", - "\n", - "The result from `solve` is a list of solutions. In this case, [we have reason to expect only one solution](https://en.wikipedia.org/wiki/Picard%E2%80%93Lindel%C3%B6f_theorem), but we still get a list, so we have to use the bracket operator, `[0]`, to select the first one." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "solutions = solve(Eq(at_0, p_0), C1)\n", - "type(solutions), len(solutions)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "value_of_C1 = solutions[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now in the general solution, we want to replace `C1` with the value of `C1` we just figured out." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "particular = general.subs(C1, value_of_C1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result is complicated, but SymPy provides a method that tries to simplify it." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "particular = simplify(particular)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Often simplicity is in the eye of the beholder, but that's about as simple as this expression gets.\n", - "\n", - "Just to double-check, we can evaluate it at `t=0` and confirm that we get `p_0`" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "particular.subs(t, 0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This solution is called the [logistic function](https://en.wikipedia.org/wiki/Population_growth#Logistic_equation).\n", - "\n", - "In some places you'll see it written in a different form:\n", - "\n", - "$f(t) = \\frac{K}{1 + A e^{-rt}}$\n", - "\n", - "where $A = (K - p_0) / p_0$.\n", - "\n", - "We can use SymPy to confirm that these two forms are equivalent. First we represent the alternative version of the logistic function:" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "A = (K - p_0) / p_0" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "logistic = K / (1 + A * exp(-r*t))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To see whether two expressions are equivalent, we can check whether their difference simplifies to 0." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "simplify(particular - logistic)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This test only works one way: if SymPy says the difference reduces to 0, the expressions are definitely equivalent (and not just numerically close).\n", - "\n", - "But if SymPy can't find a way to simplify the result to 0, that doesn't necessarily mean there isn't one. Testing whether two expressions are equivalent is a surprisingly hard problem; in fact, there is no algorithm that can solve it in general." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises\n", - "\n", - "**Exercise:** Solve the quadratic growth equation using the alternative parameterization\n", - "\n", - "$\\frac{df(t)}{dt} = \\alpha f(t) + \\beta f^2(t) $" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Use [WolframAlpha](https://www.wolframalpha.com/) to solve the quadratic growth model, using either or both forms of parameterization:\n", - "\n", - " df(t) / dt = alpha f(t) + beta f(t)^2\n", - "\n", - "or\n", - "\n", - " df(t) / dt = r f(t) (1 - f(t)/K)\n", - "\n", - "Find the general solution and also the particular solution where `f(0) = p_0`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap09sympy.ipynb b/code/chap09sympy.ipynb deleted file mode 100644 index 5289f4a26..000000000 --- a/code/chap09sympy.ipynb +++ /dev/null @@ -1,632 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 9\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import everything from SymPy.\n", - "from sympy import *\n", - "\n", - "# Set up Jupyter notebook to display math.\n", - "init_printing() " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following displays SymPy expressions and provides the option of showing results in LaTeX format." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from sympy.printing import latex\n", - "\n", - "def show(expr, show_latex=False):\n", - " \"\"\"Display a SymPy expression.\n", - " \n", - " expr: SymPy expression\n", - " show_latex: boolean\n", - " \"\"\"\n", - " if show_latex:\n", - " print(latex(expr))\n", - " return expr" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Analysis with SymPy" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a symbol for time." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "t = symbols('t')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you combine symbols and numbers, you get symbolic expressions." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "expr = t + 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result is an `Add` object, which just represents the sum without trying to compute it." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "type(expr)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`subs` can be used to replace a symbol with a number, which allows the addition to proceed." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "expr.subs(t, 2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`f` is a special class of symbol that represents a function." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "f = Function('f')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The type of `f` is `UndefinedFunction`" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "type(f)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "SymPy understands that `f(t)` means `f` evaluated at `t`, but it doesn't try to evaluate it yet." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "f(t)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`diff` returns a `Derivative` object that represents the time derivative of `f`" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "dfdt = diff(f(t), t)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "type(dfdt)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We need a symbol for `alpha`" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "alpha = symbols('alpha')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can write the differential equation for proportional growth." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "eq1 = Eq(dfdt, alpha*f(t))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And use `dsolve` to solve it. The result is the general solution." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "solution_eq = dsolve(eq1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can tell it's a general solution because it contains an unspecified constant, `C1`.\n", - "\n", - "In this example, finding the particular solution is easy: we just replace `C1` with `p_0`" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "C1, p_0 = symbols('C1 p_0')" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "particular = solution_eq.subs(C1, p_0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the next example, we have to work a little harder to find the particular solution." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Solving the quadratic growth equation \n", - "\n", - "We'll use the (r, K) parameterization, so we'll need two more symbols:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "r, K = symbols('r K')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can write the differential equation." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "eq2 = Eq(diff(f(t), t), r * f(t) * (1 - f(t)/K))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And solve it." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "solution_eq = dsolve(eq2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result, `solution_eq`, contains `rhs`, which is the right-hand side of the solution." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "general = solution_eq.rhs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can evaluate the right-hand side at $t=0$" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "at_0 = general.subs(t, 0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we want to find the value of `C1` that makes `f(0) = p_0`.\n", - "\n", - "So we'll create the equation `at_0 = p_0` and solve for `C1`. Because this is just an algebraic identity, not a differential equation, we use `solve`, not `dsolve`.\n", - "\n", - "The result from `solve` is a list of solutions. In this case, [we have reason to expect only one solution](https://en.wikipedia.org/wiki/Picard%E2%80%93Lindel%C3%B6f_theorem), but we still get a list, so we have to use the bracket operator, `[0]`, to select the first one." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "solutions = solve(Eq(at_0, p_0), C1)\n", - "type(solutions), len(solutions)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "value_of_C1 = solutions[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now in the general solution, we want to replace `C1` with the value of `C1` we just figured out." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "particular = general.subs(C1, value_of_C1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result is complicated, but SymPy provides a method that tries to simplify it." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "particular = simplify(particular)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Often simplicity is in the eye of the beholder, but that's about as simple as this expression gets.\n", - "\n", - "Just to double-check, we can evaluate it at `t=0` and confirm that we get `p_0`" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "particular.subs(t, 0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This solution is called the [logistic function](https://en.wikipedia.org/wiki/Population_growth#Logistic_equation).\n", - "\n", - "In some places you'll see it written in a different form:\n", - "\n", - "$f(t) = \\frac{K}{1 + A e^{-rt}}$\n", - "\n", - "where $A = (K - p_0) / p_0$.\n", - "\n", - "We can use SymPy to confirm that these two forms are equivalent. First we represent the alternative version of the logistic function:" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "A = (K - p_0) / p_0" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "logistic = K / (1 + A * exp(-r*t))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To see whether two expressions are equivalent, we can check whether their difference simplifies to 0." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "simplify(particular - logistic)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This test only works one way: if SymPy says the difference reduces to 0, the expressions are definitely equivalent (and not just numerically close).\n", - "\n", - "But if SymPy can't find a way to simplify the result to 0, that doesn't necessarily mean there isn't one. Testing whether two expressions are equivalent is a surprisingly hard problem; in fact, there is no algorithm that can solve it in general." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises\n", - "\n", - "**Exercise:** Solve the quadratic growth equation using the alternative parameterization\n", - "\n", - "$\\frac{df(t)}{dt} = \\alpha f(t) + \\beta f^2(t) $" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Use [WolframAlpha](https://www.wolframalpha.com/) to solve the quadratic growth model, using either or both forms of parameterization:\n", - "\n", - " df(t) / dt = alpha f(t) + beta f(t)^2\n", - "\n", - "or\n", - "\n", - " df(t) / dt = r f(t) (1 - f(t)/K)\n", - "\n", - "Find the general solution and also the particular solution where `f(0) = p_0`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap10.ipynb b/code/chap10.ipynb deleted file mode 100644 index b4918c33a..000000000 --- a/code/chap10.ipynb +++ /dev/null @@ -1,496 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 10\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *\n", - "\n", - "from pandas import read_html" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Under the hood\n", - "\n", - "To get a `DataFrame` and a `Series`, I'll read the world population data and select a column.\n", - "\n", - "`DataFrame` and `Series` contain a variable called `shape` that indicates the number of rows and columns." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "filename = 'data/World_population_estimates.html'\n", - "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", - "table2 = tables[2]\n", - "table2.columns = ['census', 'prb', 'un', 'maddison', \n", - " 'hyde', 'tanton', 'biraben', 'mj', \n", - " 'thomlinson', 'durand', 'clark']\n", - "table2.shape" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "census = table2.census / 1e9\n", - "census.shape" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "un = table2.un / 1e9\n", - "un.shape" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `DataFrame` contains `index`, which labels the rows. It is an `Int64Index`, which is similar to a NumPy array." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "table2.index" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And `columns`, which labels the columns." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "table2.columns" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And `values`, which is an array of values." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "scrolled": false - }, - "outputs": [], - "source": [ - "table2.values" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `Series` does not have `columns`, but it does have `name`." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "census.name" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It contains `values`, which is an array." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "census.values" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And it contains `index`:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "census.index" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you ever wonder what kind of object a variable refers to, you can use the `type` function. The result indicates what type the object is, and the module where that type is defined.\n", - "\n", - "`DataFrame`, `Int64Index`, `Index`, and `Series` are defined by Pandas.\n", - "\n", - "`ndarray` is defined by NumPy." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "type(table2)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "type(table2.index)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "type(table2.columns)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "type(table2.values)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "type(census)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "type(census.index)" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "type(census.values)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Optional exercise\n", - "\n", - "The following exercise provides a chance to practice what you have learned so far, and maybe develop a different growth model. If you feel comfortable with what we have done so far, you might want to give it a try.\n", - "\n", - "**Optional Exercise:** On the Wikipedia page about world population estimates, the first table contains estimates for prehistoric populations. The following cells process this table and plot some of the results." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Select `tables[1]`, which is the second table on the page." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "table1 = tables[1]\n", - "table1.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Not all agencies and researchers provided estimates for the same dates. Again `NaN` is the special value that indicates missing data." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "table1.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Some of the estimates are in a form we can't read as numbers. We could clean them up by hand, but for simplicity I'll replace any value that has an `M` in it with `NaN`." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "table1.replace('M', np.nan, regex=True, inplace=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Again, we'll replace the long column names with more convenient abbreviations." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "table1.columns = ['prb', 'un', 'maddison', 'hyde', 'tanton', \n", - " 'biraben', 'mj', 'thomlinson', 'durand', 'clark']" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This function plots selected estimates." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def plot_prehistory(table):\n", - " \"\"\"Plots population estimates.\n", - " \n", - " table: DataFrame\n", - " \"\"\"\n", - " plot(table.prb, 'ro', label='PRB')\n", - " plot(table.un, 'co', label='UN')\n", - " plot(table.hyde, 'yo', label='HYDE')\n", - " plot(table.tanton, 'go', label='Tanton')\n", - " plot(table.biraben, 'bo', label='Biraben')\n", - " plot(table.mj, 'mo', label='McEvedy & Jones')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the results. Notice that we are working in millions now, not billions." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "scrolled": false - }, - "outputs": [], - "source": [ - "plot_prehistory(table1)\n", - "decorate(xlabel='Year', \n", - " ylabel='World population (millions)',\n", - " title='Prehistoric population estimates')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use `xlim` to zoom in on everything after Year 0." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "plot_prehistory(table1)\n", - "decorate(xlim=[0, 2000], xlabel='Year', \n", - " ylabel='World population (millions)',\n", - " title='Prehistoric population estimates')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "See if you can find a model that fits these data well from Year -1000 to 1940, or from Year 1 to 1940.\n", - "\n", - "How well does your best model predict actual population growth from 1950 to the present?" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap11.ipynb b/code/chap11.ipynb deleted file mode 100644 index ab16fe1da..000000000 --- a/code/chap11.ipynb +++ /dev/null @@ -1,463 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 11\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### SIR implementation\n", - "\n", - "We'll use a `State` object to represent the number (or fraction) of people in each compartment." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "init = State(S=89, I=1, R=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To convert from number of people to fractions, we divide through by the total." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "init /= sum(init)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`make_system` creates a `System` object with the given parameters." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(beta, gamma):\n", - " \"\"\"Make a system object for the SIR model.\n", - " \n", - " beta: contact rate in days\n", - " gamma: recovery rate in days\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " init = State(S=89, I=1, R=0)\n", - " init /= sum(init)\n", - "\n", - " t0 = 0\n", - " t_end = 7 * 14\n", - "\n", - " return System(init=init, t0=t0, t_end=t_end,\n", - " beta=beta, gamma=gamma)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's an example with hypothetical values for `beta` and `gamma`." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "tc = 3 # time between contacts in days \n", - "tr = 4 # recovery time in days\n", - "\n", - "beta = 1 / tc # contact rate in per day\n", - "gamma = 1 / tr # recovery rate in per day\n", - "\n", - "system = make_system(beta, gamma)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The update function takes the state during the current time step and returns the state during the next time step." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def update_func(state, t, system):\n", - " \"\"\"Update the SIR model.\n", - " \n", - " state: State with variables S, I, R\n", - " t: time step\n", - " system: System with beta and gamma\n", - " \n", - " returns: State object\n", - " \"\"\"\n", - " s, i, r = state\n", - "\n", - " infected = system.beta * i * s \n", - " recovered = system.gamma * i\n", - " \n", - " s -= infected\n", - " i += infected - recovered\n", - " r += recovered\n", - " \n", - " return State(S=s, I=i, R=r)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To run a single time step, we call it like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "state = update_func(init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run a simulation by calling the update function for each time step." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \n", - " returns: State object for final state\n", - " \"\"\"\n", - " state = system.init\n", - " \n", - " for t in linrange(system.t0, system.t_end):\n", - " state = update_func(state, t, system)\n", - " \n", - " return state" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result is the state of the system at `t_end`" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "run_simulation(system, update_func)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise** Suppose the time between contacts is 4 days and the recovery time is 5 days. After 14 weeks, how many students, total, have been infected?\n", - "\n", - "Hint: what is the change in `S` between the beginning and the end of the simulation?" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using TimeSeries objects" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we want to store the state of the system at each time step, we can use one `TimeSeries` object for each state variable." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " Add three Series objects to the System: S, I, R\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \"\"\"\n", - " S = TimeSeries()\n", - " I = TimeSeries()\n", - " R = TimeSeries()\n", - "\n", - " state = system.init\n", - " t0 = system.t0\n", - " S[t0], I[t0], R[t0] = state\n", - " \n", - " for t in linrange(system.t0, system.t_end):\n", - " state = update_func(state, t, system)\n", - " S[t+1], I[t+1], R[t+1] = state\n", - " \n", - " return S, I, R" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we call it." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "tc = 3 # time between contacts in days \n", - "tr = 4 # recovery time in days\n", - "\n", - "beta = 1 / tc # contact rate in per day\n", - "gamma = 1 / tr # recovery rate in per day\n", - "\n", - "system = make_system(beta, gamma)\n", - "S, I, R = run_simulation(system, update_func)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And then we can plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_results(S, I, R):\n", - " \"\"\"Plot the results of a SIR model.\n", - " \n", - " S: TimeSeries\n", - " I: TimeSeries\n", - " R: TimeSeries\n", - " \"\"\"\n", - " plot(S, '--', label='Susceptible')\n", - " plot(I, '-', label='Infected')\n", - " plot(R, ':', label='Recovered')\n", - " decorate(xlabel='Time (days)',\n", - " ylabel='Fraction of population')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what they look like." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "plot_results(S, I, R)\n", - "savefig('figs/chap05-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using a DataFrame" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Instead of making three `TimeSeries` objects, we can use one `DataFrame`.\n", - "\n", - "We have to use `row` to selects rows, rather than columns. But then Pandas does the right thing, matching up the state variables with the columns of the `DataFrame`." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \n", - " returns: TimeFrame\n", - " \"\"\"\n", - " frame = TimeFrame(columns=system.init.index)\n", - " frame.row[system.t0] = system.init\n", - " \n", - " for t in linrange(system.t0, system.t_end):\n", - " frame.row[t+1] = update_func(frame.row[t], t, system)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we run it, and what the result looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "tc = 3 # time between contacts in days \n", - "tr = 4 # recovery time in days\n", - "\n", - "beta = 1 / tc # contact rate in per day\n", - "gamma = 1 / tr # recovery rate in per day\n", - "\n", - "system = make_system(beta, gamma)\n", - "results = run_simulation(system, update_func)\n", - "results.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can extract the results and plot them." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "plot_results(results.S, results.I, results.R)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Exercise** Suppose the time between contacts is 4 days and the recovery time is 5 days. Simulate this scenario for 14 weeks and plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap12.ipynb b/code/chap12.ipynb deleted file mode 100644 index b29d02003..000000000 --- a/code/chap12.ipynb +++ /dev/null @@ -1,879 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 12\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Code\n", - "\n", - "Here's the code from the previous notebook that we'll need." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(beta, gamma):\n", - " \"\"\"Make a system object for the SIR model.\n", - " \n", - " beta: contact rate in days\n", - " gamma: recovery rate in days\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " init = State(S=89, I=1, R=0)\n", - " init /= sum(init)\n", - "\n", - " t0 = 0\n", - " t_end = 7 * 14\n", - "\n", - " return System(init=init, t0=t0, t_end=t_end,\n", - " beta=beta, gamma=gamma)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def update_func(state, t, system):\n", - " \"\"\"Update the SIR model.\n", - " \n", - " state: State with variables S, I, R\n", - " t: time step\n", - " system: System with beta and gamma\n", - " \n", - " returns: State object\n", - " \"\"\"\n", - " s, i, r = state\n", - "\n", - " infected = system.beta * i * s \n", - " recovered = system.gamma * i\n", - " \n", - " s -= infected\n", - " i += infected - recovered\n", - " r += recovered\n", - " \n", - " return State(S=s, I=i, R=r)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \n", - " returns: TimeFrame\n", - " \"\"\"\n", - " frame = TimeFrame(columns=system.init.index)\n", - " frame.row[system.t0] = system.init\n", - " \n", - " for t in linrange(system.t0, system.t_end):\n", - " frame.row[t+1] = update_func(frame.row[t], t, system)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Metrics" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Given the results, we can compute metrics that quantify whatever we are interested in, like the total number of sick students, for example." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "def calc_total_infected(results):\n", - " \"\"\"Fraction of population infected during the simulation.\n", - " \n", - " results: DataFrame with columns S, I, R\n", - " \n", - " returns: fraction of population\n", - " \"\"\"\n", - " return get_first_value(results.S) - get_last_value(results.S)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's an example.|" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "beta = 0.333\n", - "gamma = 0.25\n", - "system = make_system(beta, gamma)\n", - "\n", - "results = run_simulation(system, update_func)\n", - "print(beta, gamma, calc_total_infected(results))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Write functions that take a `TimeFrame` object as a parameter and compute the other metrics mentioned in the book:\n", - "\n", - "1. The fraction of students who are sick at the peak of the outbreak.\n", - "\n", - "2. The day the outbreak peaks.\n", - "\n", - "3. The fraction of students who are sick at the end of the semester.\n", - "\n", - "Note: Not all of these functions require the `System` object, but when you write a set of related functons, it is often convenient if they all take the same parameters.\n", - "\n", - "Hint: If you have a `TimeSeries` called `I`, you can compute the largest value of the series like this:\n", - "\n", - " I.max()\n", - "\n", - "And the index of the largest value like this:\n", - "\n", - " I.idxmax()\n", - "\n", - "You can read about these functions in the `Series` [documentation](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.html)." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### What if?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use this model to evaluate \"what if\" scenarios. For example, this function models the effect of immunization by moving some fraction of the population from S to R before the simulation starts." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "def add_immunization(system, fraction):\n", - " \"\"\"Immunize a fraction of the population.\n", - " \n", - " Moves the given fraction from S to R.\n", - " \n", - " system: System object\n", - " fraction: number from 0 to 1\n", - " \"\"\"\n", - " system.init.S -= fraction\n", - " system.init.R += fraction" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's start again with the system we used in the previous sections." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "tc = 3 # time between contacts in days \n", - "tr = 4 # recovery time in days\n", - "\n", - "beta = 1 / tc # contact rate in per day\n", - "gamma = 1 / tr # recovery rate in per day\n", - "\n", - "system = make_system(beta, gamma)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And run the model without immunization." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "results = run_simulation(system, update_func)\n", - "calc_total_infected(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now with 10% immunization." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "system2 = make_system(beta, gamma)\n", - "add_immunization(system2, 0.1)\n", - "results2 = run_simulation(system2, update_func)\n", - "calc_total_infected(results2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "10% immunization leads to a drop in infections of 16 percentage points.\n", - "\n", - "Here's what the time series looks like for S, with and without immunization." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "plot(results.S, '-', label='No immunization')\n", - "plot(results2.S, '--', label='10% immunization')\n", - "\n", - "decorate(xlabel='Time (days)',\n", - " ylabel='Fraction susceptible')\n", - "\n", - "savefig('figs/chap05-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can sweep through a range of values for the fraction of the population who are immunized." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "immunize_array = linspace(0, 1, 11)\n", - "for fraction in immunize_array:\n", - " system = make_system(beta, gamma)\n", - " add_immunization(system, fraction)\n", - " results = run_simulation(system, update_func)\n", - " print(fraction, calc_total_infected(results))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This function does the same thing and stores the results in a `Sweep` object." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "def sweep_immunity(immunize_array):\n", - " \"\"\"Sweeps a range of values for immunity.\n", - " \n", - " immunize_array: array of fraction immunized\n", - " \n", - " returns: Sweep object\n", - " \"\"\"\n", - " sweep = SweepSeries()\n", - " \n", - " for fraction in immunize_array:\n", - " system = make_system(beta, gamma)\n", - " add_immunization(system, fraction)\n", - " results = run_simulation(system, update_func)\n", - " sweep[fraction] = calc_total_infected(results)\n", - " \n", - " return sweep" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we run it." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "immunize_array = linspace(0, 1, 21)\n", - "infected_sweep = sweep_immunity(immunize_array)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's what the results look like." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "plot(infected_sweep)\n", - "\n", - "decorate(xlabel='Fraction immunized',\n", - " ylabel='Total fraction infected',\n", - " title='Fraction infected vs. immunization rate',\n", - " legend=False)\n", - "\n", - "savefig('figs/chap05-fig03.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If 40% of the population is immunized, less than 4% of the population gets sick." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Logistic function" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To model the effect of a hand-washing campaign, I'll use a [generalized logistic function](https://en.wikipedia.org/wiki/Generalised_logistic_function) (GLF), which is a convenient function for modeling curves that have a generally sigmoid shape. The parameters of the GLF correspond to various features of the curve in a way that makes it easy to find a function that has the shape you want, based on data or background information about the scenario." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "def logistic(x, A=0, B=1, C=1, M=0, K=1, Q=1, nu=1):\n", - " \"\"\"Computes the generalize logistic function.\n", - " \n", - " A: controls the lower bound\n", - " B: controls the steepness of the transition \n", - " C: not all that useful, AFAIK\n", - " M: controls the location of the transition\n", - " K: controls the upper bound\n", - " Q: shift the transition left or right\n", - " nu: affects the symmetry of the transition\n", - " \n", - " returns: float or array\n", - " \"\"\"\n", - " exponent = -B * (x - M)\n", - " denom = C + Q * exp(exponent)\n", - " return A + (K-A) / denom ** (1/nu)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following array represents the range of possible spending." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "spending = linspace(0, 1200, 21)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`compute_factor` computes the reduction in `beta` for a given level of campaign spending.\n", - "\n", - "`M` is chosen so the transition happens around \\$500.\n", - "\n", - "`K` is the maximum reduction in `beta`, 20%.\n", - "\n", - "`B` is chosen by trial and error to yield a curve that seems feasible." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "def compute_factor(spending):\n", - " \"\"\"Reduction factor as a function of spending.\n", - " \n", - " spending: dollars from 0 to 1200\n", - " \n", - " returns: fractional reduction in beta\n", - " \"\"\"\n", - " return logistic(spending, M=500, K=0.2, B=0.01)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what it looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "percent_reduction = compute_factor(spending) * 100\n", - "\n", - "plot(spending, percent_reduction)\n", - "\n", - "decorate(xlabel='Hand-washing campaign spending (USD)',\n", - " ylabel='Percent reduction in infection rate',\n", - " title='Effect of hand washing on infection rate',\n", - " legend=False)\n", - "\n", - "savefig('figs/chap05-fig04.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Modify the parameters `M`, `K`, and `B`, and see what effect they have on the shape of the curve. Read about the [generalized logistic function on Wikipedia](https://en.wikipedia.org/wiki/Generalised_logistic_function). Modify the other parameters and see what effect they have." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Hand washing" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can model the effect of a hand-washing campaign by modifying `beta`" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "def add_hand_washing(system, spending):\n", - " \"\"\"Modifies system to model the effect of hand washing.\n", - " \n", - " system: System object\n", - " spending: campaign spending in USD\n", - " \"\"\"\n", - " factor = compute_factor(spending)\n", - " system.beta *= (1 - factor)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's start with the same values of `beta` and `gamma` we've been using." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "tc = 3 # time between contacts in days \n", - "tr = 4 # recovery time in days\n", - "\n", - "beta = 1 / tc # contact rate in per day\n", - "gamma = 1 / tr # recovery rate in per day\n", - "\n", - "beta, gamma" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can sweep different levels of campaign spending." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "spending_array = linspace(0, 1200, 13)\n", - "\n", - "for spending in spending_array:\n", - " system = make_system(beta, gamma)\n", - " add_hand_washing(system, spending)\n", - " results = run_simulation(system, update_func)\n", - " print(spending, system.beta, calc_total_infected(results))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a function that sweeps a range of spending and stores the results in a `SweepSeries`." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "def sweep_hand_washing(spending_array):\n", - " \"\"\"Run simulations with a range of spending.\n", - " \n", - " spending_array: array of dollars from 0 to 1200\n", - " \n", - " returns: Sweep object\n", - " \"\"\"\n", - " sweep = SweepSeries()\n", - " \n", - " for spending in spending_array:\n", - " system = make_system(beta, gamma)\n", - " add_hand_washing(system, spending)\n", - " results = run_simulation(system, update_func)\n", - " sweep[spending] = calc_total_infected(results)\n", - " \n", - " return sweep" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we run it." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "spending_array = linspace(0, 1200, 20)\n", - "infected_sweep = sweep_hand_washing(spending_array)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's what it looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "plot(infected_sweep)\n", - "\n", - "decorate(xlabel='Hand-washing campaign spending (USD)',\n", - " ylabel='Total fraction infected',\n", - " title='Effect of hand washing on total infections',\n", - " legend=False)\n", - "\n", - "savefig('figs/chap05-fig05.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's put it all together to make some public health spending decisions." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Optimization" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Suppose we have \\$1200 to spend on any combination of vaccines and a hand-washing campaign." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "num_students = 90\n", - "budget = 1200\n", - "price_per_dose = 100\n", - "max_doses = int(budget / price_per_dose)\n", - "dose_array = linrange(max_doses, endpoint=True)\n", - "max_doses" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can sweep through a range of doses from, 0 to `max_doses`, model the effects of immunization and the hand-washing campaign, and run simulations.\n", - "\n", - "For each scenario, we compute the fraction of students who get sick." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "for doses in dose_array:\n", - " fraction = doses / num_students\n", - " spending = budget - doses * price_per_dose\n", - " \n", - " system = make_system(beta, gamma)\n", - " add_immunization(system, fraction)\n", - " add_hand_washing(system, spending)\n", - " \n", - " results, run_simulation(system, update_func)\n", - " print(doses, system.init.S, system.beta, calc_total_infected(results))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function wraps that loop and stores the results in a `Sweep` object." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "def sweep_doses(dose_array):\n", - " \"\"\"Runs simulations with different doses and campaign spending.\n", - " \n", - " dose_array: range of values for number of vaccinations\n", - " \n", - " return: Sweep object with total number of infections \n", - " \"\"\"\n", - " sweep = SweepSeries()\n", - " \n", - " for doses in dose_array:\n", - " fraction = doses / num_students\n", - " spending = budget - doses * price_per_dose\n", - " \n", - " system = make_system(beta, gamma)\n", - " add_immunization(system, fraction)\n", - " add_hand_washing(system, spending)\n", - " \n", - " results = run_simulation(system, update_func)\n", - " sweep[doses] = calc_total_infected(results)\n", - "\n", - " return sweep" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can compute the number of infected students for each possible allocation of the budget." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [], - "source": [ - "infected_sweep = sweep_doses(dose_array)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "plot(infected_sweep)\n", - "\n", - "decorate(xlabel='Doses of vaccine',\n", - " ylabel='Total fraction infected',\n", - " title='Total infections vs. doses',\n", - " legend=False)\n", - "\n", - "savefig('figs/chap05-fig06.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises\n", - "\n", - "**Exercise:** Suppose the price of the vaccine drops to $50 per dose. How does that affect the optimal allocation of the spending?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Suppose we have the option to quarantine infected students. For example, a student who feels ill might be moved to an infirmary, or a private dorm room, until they are no longer infectious.\n", - "\n", - "How might you incorporate the effect of quarantine in the SIR model?" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap13.ipynb b/code/chap13.ipynb deleted file mode 100644 index 7d3c3d294..000000000 --- a/code/chap13.ipynb +++ /dev/null @@ -1,433 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 13\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Code from previous chapters" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`make_system`, `plot_results`, and `calc_total_infected` are unchanged." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(beta, gamma):\n", - " \"\"\"Make a system object for the SIR model.\n", - " \n", - " beta: contact rate in days\n", - " gamma: recovery rate in days\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " init = State(S=89, I=1, R=0)\n", - " init /= np.sum(init)\n", - "\n", - " t0 = 0\n", - " t_end = 7 * 14\n", - "\n", - " return System(init=init, t0=t0, t_end=t_end,\n", - " beta=beta, gamma=gamma)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_results(S, I, R):\n", - " \"\"\"Plot the results of a SIR model.\n", - " \n", - " S: TimeSeries\n", - " I: TimeSeries\n", - " R: TimeSeries\n", - " \"\"\"\n", - " plot(S, '--', label='Susceptible')\n", - " plot(I, '-', label='Infected')\n", - " plot(R, ':', label='Recovered')\n", - " decorate(xlabel='Time (days)',\n", - " ylabel='Fraction of population')" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def calc_total_infected(results):\n", - " \"\"\"Fraction of population infected during the simulation.\n", - " \n", - " results: DataFrame with columns S, I, R\n", - " \n", - " returns: fraction of population\n", - " \"\"\"\n", - " return get_first_value(results.S) - get_last_value(results.S)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's an updated version of `run_simulation` that uses `unpack`." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \n", - " returns: TimeFrame\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " frame = TimeFrame(columns=init.index)\n", - " frame.row[t0] = init\n", - " \n", - " for t in linrange(t0, t_end):\n", - " frame.row[t+1] = update_func(frame.row[t], t, system)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Write a version of `update_func` that uses `unpack`." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "# Original\n", - "\n", - "def update_func(state, t, system):\n", - " \"\"\"Update the SIR model.\n", - " \n", - " state: State (s, i, r)\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: State (sir)\n", - " \"\"\"\n", - " s, i, r = state\n", - "\n", - " infected = system.beta * i * s \n", - " recovered = system.gamma * i\n", - " \n", - " s -= infected\n", - " i += infected - recovered\n", - " r += recovered\n", - " \n", - " return State(S=s, I=i, R=r)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test the updated code with this example." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "system = make_system(0.333, 0.25)\n", - "results = run_simulation(system, update_func)\n", - "results.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "plot_results(results.S, results.I, results.R)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Sweeping beta" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make a range of values for `beta`, with constant `gamma`." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "beta_array = linspace(0.1, 1.1, 11)\n", - "gamma = 0.25" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Run the simulation once for each value of `beta` and print total infections." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "for beta in beta_array:\n", - " system = make_system(beta, gamma)\n", - " results = run_simulation(system, update_func)\n", - " print(system.beta, calc_total_infected(results))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Wrap that loop in a function and return a `SweepSeries` object." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "def sweep_beta(beta_array, gamma):\n", - " \"\"\"Sweep a range of values for beta.\n", - " \n", - " beta_array: array of beta values\n", - " gamma: recovery rate\n", - " \n", - " returns: SweepSeries that maps from beta to total infected\n", - " \"\"\"\n", - " sweep = SweepSeries()\n", - " for beta in beta_array:\n", - " system = make_system(beta, gamma)\n", - " results = run_simulation(system, update_func)\n", - " sweep[system.beta] = calc_total_infected(results)\n", - " return sweep" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Sweep `beta` and plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "infected_sweep = sweep_beta(beta_array, gamma)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "label = 'gamma = ' + str(gamma)\n", - "plot(infected_sweep, label=label)\n", - "\n", - "decorate(xlabel='Contacts per day (beta)',\n", - " ylabel='Fraction infected')\n", - "\n", - "savefig('figs/chap06-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Sweeping gamma" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Using the same array of values for `beta`" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "beta_array" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And now an array of values for `gamma`" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "gamma_array = [0.2, 0.4, 0.6, 0.8]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For each value of `gamma`, sweep `beta` and plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "for gamma in gamma_array:\n", - " infected_sweep = sweep_beta(beta_array, gamma)\n", - " label = 'γ = ' + str(gamma)\n", - " plot(infected_sweep, label=label)\n", - " \n", - "decorate(xlabel='Contacts per day (beta)',\n", - " ylabel='Fraction infected',\n", - " loc='upper left')\n", - "\n", - "savefig('figs/chap06-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "** Exercise:** Suppose the infectious period for the Freshman Plague is known to be 2 days on average, and suppose during one particularly bad year, 40% of the class is infected at some point. Estimate the time between contacts." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap14.ipynb b/code/chap14.ipynb deleted file mode 100644 index 95fd99e79..000000000 --- a/code/chap14.ipynb +++ /dev/null @@ -1,525 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 14\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Code from previous chapters" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(beta, gamma):\n", - " \"\"\"Make a system object for the SIR model.\n", - " \n", - " beta: contact rate in days\n", - " gamma: recovery rate in days\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " init = State(S=89, I=1, R=0)\n", - " init /= np.sum(init)\n", - "\n", - " t0 = 0\n", - " t_end = 7 * 14\n", - "\n", - " return System(init=init, t0=t0, t_end=t_end,\n", - " beta=beta, gamma=gamma)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def update_func(state, t, system):\n", - " \"\"\"Update the SIR model.\n", - " \n", - " state: State (s, i, r)\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: State (sir)\n", - " \"\"\"\n", - " s, i, r = state\n", - "\n", - " infected = system.beta * i * s \n", - " recovered = system.gamma * i\n", - " \n", - " s -= infected\n", - " i += infected - recovered\n", - " r += recovered\n", - " \n", - " return State(S=s, I=i, R=r)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \n", - " returns: TimeFrame\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " frame = TimeFrame(columns=init.index)\n", - " frame.row[t0] = init\n", - " \n", - " for t in linrange(t0, t_end):\n", - " frame.row[t+1] = update_func(frame.row[t], t, system)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def calc_total_infected(results):\n", - " \"\"\"Fraction of population infected during the simulation.\n", - " \n", - " results: DataFrame with columns S, I, R\n", - " \n", - " returns: fraction of population\n", - " \"\"\"\n", - " return get_first_value(results.S) - get_last_value(results.S)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def sweep_beta(beta_array, gamma):\n", - " \"\"\"Sweep a range of values for beta.\n", - " \n", - " beta_array: array of beta values\n", - " gamma: recovery rate\n", - " \n", - " returns: SweepSeries that maps from beta to total infected\n", - " \"\"\"\n", - " sweep = SweepSeries()\n", - " for beta in beta_array:\n", - " system = make_system(beta, gamma)\n", - " results = run_simulation(system, update_func)\n", - " sweep[system.beta] = calc_total_infected(results)\n", - " return sweep" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## SweepFrame\n", - "\n", - "The following sweeps two parameters and stores the results in a `SweepFrame`" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "def sweep_parameters(beta_array, gamma_array):\n", - " \"\"\"Sweep a range of values for beta and gamma.\n", - " \n", - " beta_array: array of infection rates\n", - " gamma_array: array of recovery rates\n", - " \n", - " returns: SweepFrame with one row for each beta\n", - " and one column for each gamma\n", - " \"\"\"\n", - " frame = SweepFrame(columns=gamma_array)\n", - " for gamma in gamma_array:\n", - " frame[gamma] = sweep_beta(beta_array, gamma)\n", - " return frame" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the results look like." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "beta_array = linspace(0.1, 0.9, 11)\n", - "gamma_array = linspace(0.1, 0.7, 4)\n", - "frame = sweep_parameters(beta_array, gamma_array)\n", - "frame.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's how we can plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "for gamma in gamma_array:\n", - " label = 'gamma = ' + str(gamma)\n", - " plot(frame[gamma], label=label)\n", - " \n", - "decorate(xlabel='Contacts per day (beta)',\n", - " ylabel='Fraction infected',\n", - " loc='upper left')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It's often useful to separate the code that generates results from the code that plots the results, so we can run the simulations once, save the results, and then use them for different analysis, visualization, etc." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Contact number" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After running `sweep_parameters`, we have a `SweepFrame` with one row for each value of `beta` and one column for each value of `gamma`." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "frame.shape" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following loop shows how we can loop through the columns and rows of the `SweepFrame`. With 11 rows and 4 columns, there are 44 elements." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "for gamma in frame.columns:\n", - " series = frame[gamma]\n", - " for beta in series.index:\n", - " frac_infected = series[beta]\n", - " print(beta, gamma, frac_infected)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can wrap that loop in a function and plot the results. For each element of the `SweepFrame`, we have `beta`, `gamma`, and `frac_infected`, and we plot `beta/gamma` on the x-axis and `frac_infected` on the y-axis." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_sweep_frame(frame):\n", - " \"\"\"Plot the values from a SweepFrame.\n", - " \n", - " For each (beta, gamma), compute the contact number,\n", - " beta/gamma\n", - " \n", - " frame: SweepFrame with one row per beta, one column per gamma\n", - " \"\"\"\n", - " for gamma in frame.columns:\n", - " series = frame[gamma]\n", - " for beta in series.index:\n", - " frac_infected = series[beta]\n", - " plot(beta/gamma, frac_infected, 'ro')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what it looks like:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "plot_sweep_frame(frame)\n", - "\n", - "decorate(xlabel='Contact number (beta/gamma)',\n", - " ylabel='Fraction infected',\n", - " legend=False)\n", - "\n", - "savefig('figs/chap06-fig03.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It turns out that the ratio `beta/gamma`, called the \"contact number\" is sufficient to predict the total number of infections; we don't have to know `beta` and `gamma` separately.\n", - "\n", - "We can see that in the previous plot: when we plot the fraction infected versus the contact number, the results fall close to a curve." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Analysis" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the book we figured out the relationship between $c$ and $s_{\\infty}$ analytically. Now we can compute it for a range of values:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "s_inf_array = linspace(0.0001, 0.9999, 101);" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "c_array = log(s_inf_array) / (s_inf_array - 1);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`total_infected` is the change in $s$ from the beginning to the end." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "frac_infected = 1 - s_inf_array\n", - "frac_infected_series = Series(frac_infected, index=c_array);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can plot the analytic results and compare them to the simulations." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "plot_sweep_frame(frame)\n", - "plot(frac_infected_series, label='Analysis')\n", - "\n", - "decorate(xlabel='Contact number (c)',\n", - " ylabel='Fraction infected')\n", - "\n", - "savefig('figs/chap06-fig04.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The agreement is generally good, except for values of `c` less than 1." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** If we didn't know about contact numbers, we might have explored other possibilities, like the difference between `beta` and `gamma`, rather than their ratio.\n", - "\n", - "Write a version of `plot_sweep_frame`, called `plot_sweep_frame_difference`, that plots the fraction infected versus the difference `beta-gamma`.\n", - "\n", - "What do the results look like, and what does that imply? " - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Suppose you run a survey at the end of the semester and find that 26% of students had the Freshman Plague at some point.\n", - "\n", - "What is your best estimate of `c`?\n", - "\n", - "Hint: if you print `frac_infected_series`, you can read off the answer. " - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "# Alternative solution\n", - "\n", - "\"\"\"We can use `np.interp` to look up `s_inf` and\n", - "estimate the corresponding value of `c`, but it only\n", - "works if the index of the series is sorted in ascending\n", - "order. So we have to use `sort_index` first.\n", - "\"\"\"\n", - "\n", - "frac_infected_series.sort_index(inplace=True)\n", - "np.interp(0.26, frac_infected_series, frac_infected_series.index)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap15.ipynb b/code/chap15.ipynb deleted file mode 100644 index 9a203a9f7..000000000 --- a/code/chap15.ipynb +++ /dev/null @@ -1,318 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 15\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The coffee cooling problem\n", - "\n", - "I'll use a `State` object to store the initial temperature.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "init = State(T=90)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And a `System` object to contain the system parameters." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "coffee = System(init=init,\n", - " volume=300,\n", - " r=0.01,\n", - " T_env=22,\n", - " t_end=30,\n", - " dt=1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The update function implements Newton's law of cooling." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def update_func(state, t, system):\n", - " \"\"\"Update the thermal transfer model.\n", - " \n", - " state: State (temp)\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: State (temp)\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " T = state.T\n", - " T += -r * (T - T_env) * dt\n", - " \n", - " return State(T=T)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how it works." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "update_func(init, 0, coffee)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a version of `run_simulation` that uses `linrange` to make an array of time steps." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " Add a TimeFrame to the System: results\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " frame = TimeFrame(columns=init.index)\n", - " frame.row[0] = init\n", - " ts = linrange(0, t_end, dt)\n", - " \n", - " for t in ts:\n", - " frame.row[t+dt] = update_func(frame.row[t], t, system)\n", - " \n", - " # store the final temperature in T_final\n", - " system.T_final = get_last_value(frame.T)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's how it works." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "results = run_simulation(coffee, update_func)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the results look like." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "plot(results.T, label='coffee')\n", - "decorate(xlabel='Time (minutes)',\n", - " ylabel='Temperature (C)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the final temperature:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "coffee.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Encapsulation\n", - "\n", - "Before we go on, let's define a function to initialize `System` objects with relevant parameters:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(T_init, r, volume, t_end):\n", - " \"\"\"Makes a System object with the given parameters.\n", - "\n", - " T_init: initial temperature in degC\n", - " r: heat transfer rate, in 1/min\n", - " volume: volume of liquid in mL\n", - " t_end: end time of simulation\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " init = State(T=T_init)\n", - " \n", - " # T_final is used to store the final temperature.\n", - " # Before the simulation runs, T_final = T_init\n", - " T_final = T_init\n", - "\n", - " T_env = 22 \n", - " dt = 1\n", - " \n", - " return System(locals())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we use it:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "coffee = make_system(T_init=90, r=0.01, volume=300, t_end=30)\n", - "results = run_simulation(coffee, update_func)\n", - "coffee.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Exercise:** Simulate the temperature of 50 mL of milk with a starting temperature of 5 degC, in a vessel with the same insulation, for 15 minutes, and plot the results.\n", - "\n", - "By trial and error, find a values for `r` that makes the final temperature close to 20 C." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "plot(results.T, label='milk')\n", - "decorate(xlabel='Time (minutes)',\n", - " ylabel='Temperature (C)')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap16.ipynb b/code/chap16.ipynb deleted file mode 100644 index eabf766c6..000000000 --- a/code/chap16.ipynb +++ /dev/null @@ -1,726 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 16\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Code from previous notebooks" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "def update_func(state, t, system):\n", - " \"\"\"Update the thermal transfer model.\n", - " \n", - " state: State (temp)\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: State (temp)\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " T = state.T\n", - " T += -r * (T - T_env) * dt\n", - " \n", - " return State(T=T)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " Add a TimeFrame to the System: results\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " frame = TimeFrame(columns=init.index)\n", - " frame.row[0] = init\n", - " ts = linrange(0, t_end, dt)\n", - " \n", - " for t in ts:\n", - " frame.row[t+dt] = update_func(frame.row[t], t, system)\n", - " \n", - " # store the final temperature in T_final\n", - " system.T_final = get_last_value(frame.T)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(T_init, r, volume, t_end):\n", - " \"\"\"Makes a System object with the given parameters.\n", - "\n", - " T_init: initial temperature in degC\n", - " r: heat transfer rate, in 1/min\n", - " volume: volume of liquid in mL\n", - " t_end: end time of simulation\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " init = State(T=T_init)\n", - " \n", - " # T_final is used to store the final temperature.\n", - " # Before the simulation runs, T_final = T_init\n", - " T_final = T_init\n", - "\n", - " T_env = 22 \n", - " dt = 1\n", - " \n", - " return System(locals())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using `fsolve`\n", - "\n", - "As a simple example, let's find the roots of this function; that is, the values of `x` that make the result 0." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def func(x):\n", - " return (x-1) * (x-2) * (x-3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`modsim.py` provides `fsolve`, which does some error-checking and then runs `scipy.optimize.fsolve`. The first argument is the function whose roots we want. The second argument is an initial guess." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "fsolve(func, x0=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Usually the root we get is the one that's closest to the initial guess." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "fsolve(func, 1.9)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "fsolve(func, 2.9)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But not always." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "fsolve(func, 1.5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We want to find the value of `r` that makes the final temperature 70, so we define an \"error function\" that takes `r` as a parameter and returns the difference between the final temperature and the goal." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def error_func1(r):\n", - " \"\"\"Runs a simulation and returns the `error`.\n", - " \n", - " r: heat transfer rate, in 1/min\n", - " \n", - " returns: difference between final temp and 70 C\n", - " \"\"\"\n", - " system = make_system(T_init=90, r=r, volume=300, t_end=30)\n", - " results = run_simulation(system, update_func)\n", - " return system.T_final - 70" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With `r=0.01`, we end up a little too warm." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "error_func1(r=0.01)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The return value from `fsolve` is an array with a single element, the estimated value of `r`." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "solution = fsolve(error_func1, 0.01)\n", - "r_coffee = solution[0]\n", - "r_coffee" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we run the simulation with the estimated value of `r`, the final temperature is 70 C, as expected." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "coffee = make_system(T_init=90, r=r_coffee, volume=300, t_end=30)\n", - "results = run_simulation(coffee, update_func)\n", - "coffee.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** When you call `fsolve`, it calls `error_func1` several times. To see how this works, add a print statement to `error_func1` and run `fsolve` again." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Repeat this process to estimate `r_milk`, given that it starts at 5 C and reaches 20 C after 15 minutes. \n", - "\n", - "Before you use `fsolve`, you might want to try a few values for `r_milk` and see how close you can get by trial and error. Here's an initial guess to get you started:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "r_milk = 0.1\n", - "milk = make_system(T_init=5, t_end=15, r=r_milk, volume=50)\n", - "results = run_simulation(milk, update_func)\n", - "milk.T_final" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Mixing liquids" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function takes `System` objects that represent two liquids, computes the temperature of the mixture, and returns a new `System` object that represents the mixture." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "def mix(s1, s2):\n", - " \"\"\"Simulates the mixture of two liquids.\n", - " \n", - " s1: System representing coffee\n", - " s2: System representing milk\n", - " \n", - " returns: System representing the mixture\n", - " \"\"\"\n", - " assert s1.t_end == s2.t_end\n", - " \n", - " V_mix = s1.volume + s2.volume\n", - " \n", - " T_mix = (s1.volume * s1.T_final + \n", - " s2.volume * s2.T_final) / V_mix\n", - " \n", - " mixture = make_system(T_init=T_mix,\n", - " t_end=0,\n", - " r=s1.r,\n", - " volume=V_mix)\n", - " \n", - " return mixture" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Mixing at the end\n", - "\n", - "First we'll see what happens if we add the milk at the end. We'll simulate the coffee and the milk separately." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "coffee = make_system(T_init=90, t_end=30, r=r_coffee, volume=300)\n", - "coffee_results = run_simulation(coffee, update_func)\n", - "coffee.T_final" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "milk = make_system(T_init=5, t_end=30, r=r_milk, volume=50)\n", - "milk_results = run_simulation(milk, update_func)\n", - "milk.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the results look like." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "plot(coffee_results.T, label='coffee')\n", - "plot(milk_results.T, '--', label='milk')\n", - "\n", - "decorate(xlabel='Time (minutes)',\n", - " ylabel='Temperature (C)',\n", - " loc='center left')\n", - "\n", - "savefig('figs/chap07-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what happens when we mix them." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "mix_last = mix(coffee, milk)\n", - "mix_last.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Mixing immediately\n", - "\n", - "Next here's what we get if we add the milk immediately." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "coffee = make_system(T_init=90, t_end=0, r=r_coffee, volume=300)\n", - "milk = make_system(T_init=5, t_end=0, r=r_milk, volume=50)" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "mix_first = mix(coffee, milk)\n", - "mix_first.t_end = 30\n", - "results = run_simulation(mix_first, update_func)\n", - "mix_first.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function takes `t_add`, which is the time when the milk is added, and returns the final temperature." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "def run_and_mix(t_add, t_total):\n", - " \"\"\"Simulates two liquids and them mixes them at t_add.\n", - " \n", - " t_add: time in minutes\n", - " t_total: total time to simulate, min\n", - " \n", - " returns: final temperature\n", - " \"\"\"\n", - " coffee = make_system(T_init=90, t_end=t_add, \n", - " r=r_coffee, volume=300)\n", - " coffee_results = run_simulation(coffee, update_func)\n", - "\n", - " milk = make_system(T_init=5, t_end=t_add, \n", - " r=r_milk, volume=50)\n", - " milk_results = run_simulation(milk, update_func)\n", - " \n", - " mixture = mix(coffee, milk)\n", - " mixture.t_end = t_total - t_add\n", - " results = run_simulation(mixture, update_func)\n", - "\n", - " return mixture.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can try it out with a few values." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "run_and_mix(t_add=0, t_total=30)" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "run_and_mix(t_add=15, t_total=30)" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "run_and_mix(t_add=30, t_total=30)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And then sweep a range of values for `t_add`" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "sweep = SweepSeries()\n", - "for t_add in linspace(0, 30, 11):\n", - " sweep[t_add] = run_and_mix(t_add, 30)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the result looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "plot(sweep, label='final temp', color='C2')\n", - "decorate(xlabel='Time added (min)',\n", - " ylabel='Final temperature (C)')\n", - "\n", - "savefig('figs/chap07-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Analysis" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can use the analytic result to compute temperature as a function of time. The following function is similar to `run_simulation`." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "def run_analysis(system):\n", - " \"\"\"Computes temperature using the analytic solution.\n", - " \n", - " system: System object\n", - " \n", - " returns: TimeFrame\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " T_init = init.T \n", - " ts = linrange(0, t_end, dt)\n", - " \n", - " T_array = T_env + (T_init - T_env) * exp(-r * ts)\n", - " \n", - " # to be consistent with run_simulation, we have to\n", - " # put the array into a TimeFrame\n", - " results = TimeFrame(T_array, index=ts, columns=['T'])\n", - " system.T_final = get_last_value(results.T)\n", - "\n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we run it. From the analysis (see `chap14analysis.ipynb`), we have the computed value of `r_coffee2`" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "r_coffee2 = 0.011610223142273859\n", - "coffee2 = make_system(T_init=90, r=r_coffee2, volume=300, t_end=30)\n", - "results = run_analysis(coffee2)\n", - "coffee2.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we can compare to the results from simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [], - "source": [ - "coffee = make_system(T_init=90, r=r_coffee, volume=300, t_end=30)\n", - "results = run_simulation(coffee, update_func)\n", - "coffee.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "They are identical except for a small roundoff error." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "coffee.T_final - coffee2.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Exercise:** Suppose the coffee shop won't let me take milk in a separate container, but I keep a bottle of milk in the refrigerator at my office. In that case is it better to add the milk at the coffee shop, or wait until I get to the office?\n", - "\n", - "Hint: Think about the simplest way to represent the behavior of a refrigerator in this model. The change you make to test this variation of the problem should be very small!" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap16sympy.ipynb b/code/chap16sympy.ipynb deleted file mode 100644 index 622a85577..000000000 --- a/code/chap16sympy.ipynb +++ /dev/null @@ -1,466 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "SymPy code for Chapter 16\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Mixing liquids" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can figure out the final temperature of a mixture by setting the total heat flow to zero and then solving for $T$." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from sympy import *\n", - "\n", - "init_printing() " - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAARIAAAAVBAMAAAB4a3wcAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEHa7q2Yiie9Umd3NRDIfxLosAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADtElEQVRIDc1WS4hTWRA96fR7L3+DLhRHNOpClz3QutABwxA3KpqVC/HzQERxk+gMI7gxILpRsWE2fkDfoIKMqK0LQVroCG4aWzs7BUEC4kYEbf89jsaquvfmffLprQW5t+qck+pKVb2kgZ/WXs5W2c5ZBLnyLAKr2UsQW15ab1c1aw+hcrTUGt70OSQPYIOdieYOl8p7jX437C/DW96XpkxKIYLYZiNt3wsP3mB/ZRnJq+aDZPI4BJwArrRl7ASxeoihYOAYcI5eyu7DqSIxipRJKXAQyxhp+57ALy4w7xYBN8kRWwEUYf8H7NCAuoIYScRirr7X5oHUJxVgoIpBoOLBamhEriBGkrBZNTh1YIry4KmhjoPmbM0ASw3CdwhboJlkQTmZabqzNRUg4YJWbdcIBjhv24JYrNaGlRMvIjmNFP1V4JXmsqPIAoOUO/SJQlhmRIlNJTc5znk6wzN565Y85wkYpfOxiQDB7pwisjOojLK/mA8yx+OTptxhPqY0gK7E/sraWFO/47DctGgd5mNK4wuWeMi+w5GCQnJDfMfLfNKUO0ywFwxbdUXqSpx3KnQu/cPOKj540aIm2OE1ecK3M+dMinnkVqrIfcZ4k1zq7mXpQ3qEA5pyhzH2aiPDSVG2e5L+rrR/YRM7d/ngRYsaY447WCf8fIRTlZwWNK/zJxoc0kTFFo2xPRZfMEmRe6tY3ZOEhDHcwRuXiFNM8qKxxR5ygntN9hlLuzl+xt5wHLAlVZ7OOCPJZqgSf6K+XDC/Eurtw5OTkx5ll0ouUAmVBsmlEn+p/ASMJRr2N0KilczxkJxR3yQpKoaElHaEjp5TVm2l50tM90R9k3gEXczTIdPpuWj8tALP6AWH2zU2ViQ3PQRrmrcFOGAqkY3tNWU94MjG2vy74HARt+mlNrbXotEfLZBGNpbF2gZqSNWR/Ehf1ZRH9cQpEhnXUzZCvhUm00l5itA9wUX6teDHRj3dfzK5jguLmMLkAf43QlEjFzSBhcMbGsSoSrjzf0z9/3tUqjGpJMNyMlNJ7FLpOsfb+ODOO7+1HhQk8A+NWR5Dj/gI2vxlgV9FVQk9Af1MKtmnFaYSHWar2Sa5aVfHXa8neE57WOvKGTCpaPXBDBi9zzBwUKPZcoh+ffbvPAGWR0cvy02c/ZV6VujFM25f+yB83KWgl+0fp7HF6t3p062WEKu704ImWq23wKI+Cp+i/5RmsbjbX7C7P01sv1oDb94T8Lu6W7uiPmiHh+YTxrNc44XuH/q3++lTknDqAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$C_{1} \\left(T - T_{1}\\right) + C_{2} \\left(T - T_{2}\\right) = 0$$" - ], - "text/plain": [ - "C₁⋅(T - T₁) + C₂⋅(T - T₂) = 0" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "C1, C2, T1, T2, T = symbols('C1 C2 T1 T2 T')\n", - "\n", - "eq = Eq(C1 * (T - T1) + C2 * (T - T2), 0)\n", - "eq" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIUAAAAyBAMAAACAOwXCAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMA74lUMhB2u6tmIpndzURTbmnuAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAC/ElEQVRIDe2Xy2tTQRTGv7xq87AUFEQQGgRxUcSK4kbBgBWRbrIQBd3EhfgXCAEVXUmKSvMXaHDrInEh4sbWhesIbgQJieLGXX3j83rOmTP3ziS3SbRd9kByvzlz7u/OnEzyEcwEH7GOSAbBNLbNn1gHApn549PYbghnqrX6fZHNHbWgeidmcZlLtcVckWtyH6pL32pd0UhbxsRO4Ai9KHYDu4Anor23C3WkHtY5lSwisYK86JCRuVoG8j/klhvI/QTuhrefU3X2OYnlBo8mgWYL2R7rkLH1Cw2mpiVVR/YTcJG1xGG9dukxeCODU8C9OUxwImIsz/GgxakpegwRe6wllJEnMLAgqR6wVOZKDu1H7hcPMiV+p6C9OqGMpiRP2wlqmYYykl91fFKutFfASBLK2F+ROcxe4S1wy1Qqo/DZzC/clivtFSpprIx2SeaSjckXJLhlKpWRWOX5DHBU6mivVpJQxoxMlQuNNH983DKVdh3COGRvlL0a3L5O51mn85ruajMjVUr0cn9IcMtUKsOcjJYyZK+6JCrXdcjJyDOHz4G0zEhl5PhkJ2kH8nDeq10SCWU0i6T30KtQoTdumUpl4Bgd40eUFMYWftDAOlLf6dtAj8Esz15nZaRlZB7XnnKSGXu7v2+q5JRdB85Xb/VomG3Rkq8FrypGhmeMpkyYRooOpe7FlrzFO1/addjsASuAUL6PcqTSLw9e1oTKPsaDNu9CwpE2JddEEKxqQmUfw6sed7DJ8Dul/Qj+O+gz2uxpbE/95D+OYnsaWa9Pi/zWy8cxHOv1ahH5rZePYbjWK7UxfjuK4VivKbW/QY7fjmI41usxXL8dweizXqq2v+srfGfotzwwMdiPPuulMmX4fmsBdB1k9FlvxPD9diijz3ojhu+3QxkF+cUNrXctvx3K8K2XS7Ufvt8OZfjW6zB8vx3K8K3XYfh+O5zhWq9U2nPq+K1DiPtsw+kx/NbUDp6PkDHab7V0bcYYfjuSEa5npKB1bMR/jw34D/QXAgUS4RvxulUAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$\\left [ \\frac{C_{1} T_{1} + C_{2} T_{2}}{C_{1} + C_{2}}\\right ]$$" - ], - "text/plain": [ - "⎡C₁⋅T₁ + C₂⋅T₂⎤\n", - "⎢─────────────⎥\n", - "⎣ C₁ + C₂ ⎦" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solve(eq, T)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Analysis" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use SymPy to solve the cooling differential equation." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPsAAAArBAMAAABMYuO6AAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMARLvvmVQQid3NIqt2MmaorGxOAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADi0lEQVRYCcVXz2sTQRT+Nmk26bZNAyIIFVoQD1rR6kXtQfYqguQfkEZEsLSQIIKVVslFrPWSg0p7EEPFk2CDF5EWjBe9RMjFq6TQk1CsohEPWt/M7MzOzm7qcR7szvv5fdmZnck+gIlzyOejrdtw0RYz5x0tWaWfs8qOhxbpvdcff1qk31dPde3Ru+fQV7NH39dFrmKPPj2GcsEefbmCKZSs8ZeLOJKpW6Pvb+YfZK2xw924/eJdhH6ow8ztiC/R+Jzo3dsZghsEa6fWd8ff/AJWOcBAe28cwBtTGapWeQxFJYTgBsFN4AxwEXgMuEvAfw+D/pLiULXKYygqQQOPEjTg/QA2MVgBMjvABwMhZmoJsjaW49aFSybo4Fo9QIuSp7P/PdJUwc7BKzEswzGpbFWrPFLJNLmmEnTwCIEDDNDZX8ABKmDnYH9RYiSPTlX5Va3ySCWgVwk6uEmQ5oA3gJET401kfYlhjM785YUmjLioNTJpFSlRSBzcJFjzWeZxuqboytMCEMkKF5/pgWRTj1ITQKojHWwUtbqH6yF9HFwQhCVf+XTfJcd5ujLVMBLRPuUaTpsWiGfLiKiVlhpD+ji4SfC2xMpO0nWPrqEdZmniLi+S3G+XxF9UugBMM8/iE0oStVq2UEP6ODgR7EphXLTtSYje/UZjjJ5H2Y0tDcDoQxG1oc00WrjlsysrPvfGwQ0Ctu1JaPKdHZTgVJmV5c+32GC6FLY0xuTzWnehha3tlo+DjWxFJKunTwAXBCKP7mzbk9Crl61l2sGrx13G7TS3Ux3NzWunSzPY/ItJVLqZMRFU9Ang+ZoGQK+y+OSbp1OnMUs3PxINDY8tDf1GfUZ47Xqr481W0fWuVmE+fQJ4lOD6098bDJZOBu9Yk46dArMSJJg0fe5ELXtpB5rOBAYqXlAsnz4JPJkgVxeUzxKYI66liEXGH7hIt/v8dq4wGMQkvUzVwQWB2WDmfZG7IEt6jVtm4DBmsIbc/nau/TyIOZ1okg4eEAwXca2uZR3lulvTXIlqqm64Z1pNjGDwDpyNjhFSZgguCUZLwU4OcsQXQQxcIUhF+9yQrv+PIbgkmBOHrCr1Okz9ouyeysuekd6BEFwSUIPJDlkrwhpM59V3vuUs/ADeYLJvKysiGkxrPaZoMK31mGneYFrrMUWDOUqnpRUp8wbzFi5ZYYdoMC+4TTv0osFcbdlh/wdTXw3MbiYF2AAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$\\frac{d}{d t} T{\\left (t \\right )} = - r \\left(- T_{env} + T{\\left (t \\right )}\\right)$$" - ], - "text/plain": [ - "d \n", - "──(T(t)) = -r⋅(-Tₑₙᵥ + T(t))\n", - "dt " - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "T_init, T_env, r, t = symbols('T_init T_env r t')\n", - "T = Function('T')\n", - "\n", - "eqn = Eq(diff(T(t), t), -r * (T(t) - T_env))\n", - "eqn" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the general solution:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMAAAAAYBAMAAABen+92AAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIu+7q82J3ZlmRDJUdhDTAJzgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAC50lEQVRIDa1VTWgTQRT+Nk02TZpuIoKoh7aepOhBseJNAxY9SGgPBoqK7qXiRageFOrBgIpVUPciVA8atFA8NeBFD2LQQ4Ui7aHUFgmsB7WXYrWg4v97M7OT2TZthfSD2fe97715b2cmswHqwtG6Zq8+2fmzek5dGam+uqavPHnwzuS6qz2llZNC0afsJf57xt0f8WKsHKpQ07G+dHQtdJ5wYe0Q8UM1suY6Or0Ni3RrjvLTmbCqixmy7SLSh0YPMR/4mEHMiCna3A68oqGQ2kXYi+gE8CDQlNXFDD0KtBaQKGKKxHGg2TWCgjp7fKDx82I5UgImHbWEdzKqixnJtPP3y2j2sYXEK4DTZgQFjfFvMblEbiX1hi0ygNfS6mJKZlMEunwkkewj5xyNYzRCuFUmt6kQ0sh5SGPorVJVg6IqptTAbGViF5Dcv5ADTgWystY3Jg5tCJvT02pThKsfqgH5opjWBbG+s2nwgNQ8kdvs2XmBAtPfLBAe0xjOWC7zxdANZLFwOPGL/XiZTrKbyCh7BuI/pfPkIGBtx2zJiGmqG8hiWhckKj4okSI1cUl4LsTqI8LLgiM6R9sr96oRg+kGspgRIUrXgJ9F4BONJQ3iosGYaJDOUsIS9ObzZ/P5HqHLYuEUugYE3qIRftFh9uzzAlmi8gYUZAOXhFrQKxDFPlQmEteeTSM5iZOUTdeAwIfcC3pRccisKFhfidi+aBBxgQmlh4xuwMWc4042MeJFMnbaf0lp+3zOtbPAZYdmD7BnYtxD6iYJo3TNuzGbMWMB1w24WDQ3g5Y3SPsto7Bh7/57mF+KL9r6CpEjwaTAOpc6rzOnBpie8QI5ZFUDWYyPEzn+UB3A5mraGUmttqoUZtxgOegVcEK6BB/bMNCEfnNDB+Vkm5dTEy9qqlJ8ZMYayk7Wmkf/GC5Y5WqgISP4+6oSZhsv5sLCsp6TqyCZxZSHoRkjSf3h7DSkNaabuF5CrmPtSv8DUyuwq8EudNcAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$T{\\left (t \\right )} = C_{1} e^{- r t} + T_{env}$$" - ], - "text/plain": [ - " -r⋅t \n", - "T(t) = C₁⋅ℯ + Tₑₙᵥ" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solution_eq = dsolve(eqn)\n", - "solution_eq" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAHwAAAAWBAMAAADwX+WxAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEHa7q2Yiie9Umd3NRDIfxLosAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAB+0lEQVQ4EYVTP0jcUBj/5bwkl6O5e1Co0MGGs0dL61CoY8EjWJUuBsHZu0kRyl2njnXroNgDtwpFihZR8M/i5CmCS+mBNzi4GQSXTorWP13a770k73JpL35Dvt/3+5P3wnsBYuqeGyPeLT1id3tiHPMxWrykLo0MTG6vxZvaq+pxo4bv7fWmonT3v9YqzVmgUje1q1ZSu+kdvug/iFifOjCWHemce0nFYAPmlCQF0CtIbSDdtHL2/iY91oschmsWSBQeeIxSFD0JlBegNjzSfx4wAkctFA3aGZC2Kh5tWKKfAmNVJLhfVvqWw59y9oH5gjZvu97kxxvAMIPZYi1v8LHL4zpz31pEf/DjNE1H5Q+Wx2T4clsYj+p8lnHtd1TedQWTWaRdTFSV/14UGVfFl4Zf8UkMDAbFh0byLKwFWMaT5wEV9F0ODFfELwMy3PV6/cdMvb7AOTr2SIkTT9MbSLmOaMEoV6djBzpzlrp08gzmY7xHuULMOy/+ka5KEAl3Gadjh7KnFNRjJ1XUs2wOxi/gM/PiPVDiD66PjEk7j9JzZFlpHjrwsPdNg5bim098HQ0vKrG/uv7qz76FFHfb/J4O4m3TQvF2JTfPDVkXDLNYyaAGSyaMKQn/AaYTojqqSoH+iNoX7GjVgNdWL60Ax3fFzsEs4ImDwzz+AioJZBj7Bd1mAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$C_{1} e^{- r t} + T_{env}$$" - ], - "text/plain": [ - " -r⋅t \n", - "C₁⋅ℯ + Tₑₙᵥ" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "general = solution_eq.rhs\n", - "general" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use the initial condition to solve for $C_1$. First we evaluate the general solution at $t=0$" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAFYAAAARBAMAAAC1JUYQAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEHa7q2Yiie9Umd3NRDIfxLosAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABZUlEQVQoFW2RPUvDUBSG32g+C2kvOAgOEqpF0cWho2AoFcGl+Qe2P0BSJ0fdFS24KUgHBamD9QfUIrgZaAcHN4vg4qSooC56bnJ7U2PPkPve5zwcLicAlKnisl5FovTPfOmt2EnwWQ/WmZdQYVRhNpH6y8cuSbsoS1eJogr4dWhdyXnoMPrcx8hywvwErNUwwpuyUl88Pss7hEsDSwx2zCn5TX6djJlwCezEMEpbTnSmF0RHuvq3IPJo98KYPgnnU5auFr5OihT2wwuD9c9VXwc9ntv8Y/WEawTB7W4Q1Dmk9SYq3GyK9H5HvoHWC4xnHe30cQ72NDbhV4lsDHNpvVCuFVd78MyykWEHsD6AQzbMXSKqFnKozCPDKkcwgIn8apdGJ99gLP7cODB5q8B/6wrWKUaVdCOa6YFhD400WnCESXO3RbQ9yYDRmuLqL2gd40qv9bl+/u7088CpFLKwXcx4uMvhF3itS7lwyWUgAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$C_{1} + T_{env}$$" - ], - "text/plain": [ - "C₁ + Tₑₙᵥ" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "at0 = general.subs(t, 0)\n", - "at0" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we set $T(0) = T_{init}$ and solve for $C_1$" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAHkAAAARBAMAAAALcx5NAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEM3dMiLvu6uJmWZEVHYiGvycAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABg0lEQVQ4EX2TMUvDQBiG31jTNprWUhyyqBCdXCz4B7K49w9IRREXB0cXoTh1qnUQEUECjoJ0cJJCO4mLpfgLIjiIQyDioiJ6d99drmlJb7j3+773eXMkRwC5anuNv8PjL9WO60TgAtgBbsZTajIRqMP8BjqK1WpUqU4FmD3dxuwv8KJTqioEokoHmG0D+QjoCTKxyXQ6QHS2kkipRqZZmwIQWPNVIKE6LYB3aSpVbHegqoTqtAA2pMl0BSiv88UKnJQSKdXo9AhgsO+kF7vusWW57vK26/rCGAEKw9+JXzeMqxD3d6GPft1q0bPiswXQryvfvF71iOA7v26USw46P9hFKyockBenBXAakc90akCA2HP8NRph21ysIDIXKhg9mwNsHvtzVZ0+X/toAvtskA/sLeRbZo9MdTYBuYB8pg/kD+2fMJD1Znwv0yvKsUpTuzRPPtM39QfE+SM4qCHz5GW8TTm027HLimeLfKa31rDBaycM8IjiJexmIhRzzhn5XF/xD1YId0zuAa6QAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$- T_{env} + T_{init}$$" - ], - "text/plain": [ - "-Tₑₙᵥ + Tᵢₙᵢₜ" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solutions = solve(Eq(at0, T_init), C1)\n", - "value_of_C1 = solutions[0]\n", - "value_of_C1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then we plug the result into the general solution to get the particular solution:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPUAAAAYBAMAAADQRaYKAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIu+7q82J3ZlmRDJUdhDTAJzgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADN0lEQVRIDcVWTWgTURD+Nkk3zTbZjUdFmhYR8edQsd5Ec6heJNSDQVGxK4LooRJPQr3kZnsQchFqBQ1a6EVxPXoQAyI9eNlDqSklsodUCloURfGnpM57u2+zf4HmlA+y82a+b97b7swbCvQKF3t1MKC2end2utSbs2cfLO24e97YzuGvtiPqrEmFDpn7m6z21QDp++j4j7EJvXOuNOJyuT1jW6Mnf7l+aBEpOB2QSRu0o5YFZB2xEvorAd7j9lmucw3YB8y4fmgRKehzZekjhONImMATCiaAXBmpqsuLhUovxrEsIkAe0m9grh0QKyGOFGR0IXNszACWKIWK+biGjBWggTS9HMewSw1UkNoEProBd+GIowXqkKuzFzky92SgCoxbUAIsueJspeRypErQtaSUIBxxB8GlgPwp+fOrPEgljIA4Wy57SeqNKAgxWPOEcJ1F1Ml6NsSwEkZAbBeveEnqjSgIMW8eoO5oHHufuQtZSXfCbcNKGAGxXbLmJak3oiDEvHmAl46GLBvbi/STDmHdCKWyEkZAbBerAmu3Gdg21BtREOKQgI/tN5SS2N94FM6MqpBcLJ7bWyyWSc3ObiOqNzxifv/baupYVn52tpb3hsWal/BTw0zNvK5DWQLNBwbxp/i+Oe8NdbKB2YeNMp7naTJxCDG//xQXvMTH9gJpNEdp68WTlVC9rOZTLyqxrKxZb21CbOfrNd4ba1YTc38wDL2VHvGL+f3HzZbNk2VjG6zXYjpgkvXjhEXVKKxg8AM0a3ARdOkZxNlynrv2I856Y6xRkTZKaElfStDtuBCDCSju8lqWBNP0y5zBOlt7IR/dOmvaRS2wIXsKu2xabOeZLbg68bMAHCBBwlSGkNClql9sC+KmzZOlHYEL7FFfqTAThmbAwkFMD2BKfBlxNm4F5f+gImb0l41kNeNwrpj7Xz/bPFk2tqUhHu30iNfUvPQNU+9xR6rZIkW85mww6RiayCH5zEgaux3OFXP/nWzzZNnYls3gDj5fLTSg5LFcwfyKjyAnHixTs2FiHpkbUAri/fw5zSs2z+wqTQc/243n+d+hm7S29nB72fVqZ9cZvoRU8LsJ9j9FpekmignN5gAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$T_{env} + \\left(- T_{env} + T_{init}\\right) e^{- r t}$$" - ], - "text/plain": [ - " -r⋅t\n", - "Tₑₙᵥ + (-Tₑₙᵥ + Tᵢₙᵢₜ)⋅ℯ " - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "particular = general.subs(C1, value_of_C1)\n", - "particular" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We use a similar process to estimate $r$ based on the observation $T(t_{end}) = T_{end}$" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "t_end, T_end = symbols('t_end T_end')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the particular solution evaluated at $t_{end}$" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQ0AAAAYBAMAAAASULWnAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIu+7q82J3ZlmRDJUdhDTAJzgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADOElEQVRIDcVWTWgTURD+Nkm33fxsol7EQ1PxIP4cWqwHL7qH6kVCczBaKLQRfzCHShREqCK51YJCxEubogYtVsFiPHoQAqIiIgQsNaVGoogUtFgURUVa523e/mSb5pVeMrCZv2/mzb6ZfS9Ao6i3UQtXr6suVuuN0vzJRq1srTsyOrVuqCdnGVYn+dKrw62EGnM6Mn+bs015m3UIkL53dv/o6o/brE5xg2kQgsNbupY69/8yA3TB43hzab4dCIZsoHuAHIcriZZ6r9xnRgjBJ4GtwLAZUBGihu7fRbQXngJw27AxngA8QDgFJWs3V+QP3BSImz4hWIP0G8iYARVh2qG7csCUGlISaeXKaBxK6QzwGLiVR6DsgJL6mptcIdMnAtMkKf+A92ZARfjo0MOkX5VxHSn/G7ThOT3IAt1leB1Iphp1TFi+rABMaTx0LhCsiqpmkjx36BmfxeGXZRyUouj1tetw6mgtMuo4VeUUgNmwLSM5RSZ1oGhtrA7pQBnJQAaX5HdMZx2tRUYde+xOEZgNG1DkIZwrUdInQlKcmznbOJpWNX8Cd2/og806WouMOi7anSIwGzbgEQ8hzq4RdnZKOzGX4+bajHW0Fhl1bKc9PXKe6BzlEYHZsNlJv0Z8CxS4rXTT7lgu1+ooTsRiZ2OxHoamOiwSgdnxYSe2FWB1BDW7uZasd/RzqaAMPynCOwU6i3Qy9qOqLyKwPj+TGkbGSilMapJ+jXipmGCcp12RsY6qfaqmPEy7QnKw/JQjjTqq5lQE1ufn9CIyf7AZxPVPls2pKw4UVqyBOfaVqXuRGbS+RbDc+gIyRxt1HOe6zkRgNw2b9DUpzSexSLxyjbSkgEAUcyF7Jocs7146VIArS+YIO/QPYBNHGHVMWBFC8LH+nxHAXaArxNvGOGUEmrL0U5xJ06+Agjk6U3bgsg+D5u4ZdTQ7X6MeWF/n2xe4ci2pHHG6RoD1gtUttzuvatICBl/hgpTn5vucKykuGKweWMc8kxFG84MccbpGgAEjUsjVSAleDdNpjM8sA3c4LHXBDPvpKMYRSOh8lj6CqCPBGlXrf9DaEridjV1bGkjpNQbysGurCP8PLrr5vxec5+oAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$T_{env} + \\left(- T_{env} + T_{init}\\right) e^{- r t_{end}}$$" - ], - "text/plain": [ - " -r⋅t_end\n", - "Tₑₙᵥ + (-Tₑₙᵥ + Tᵢₙᵢₜ)⋅ℯ " - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "at_end = particular.subs(t, t_end)\n", - "at_end" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we set $T(t_{end}) = T_{end}$ and solve for $r$" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/latex": [ - "$$\\frac{1}{t_{end}} \\log{\\left (\\frac{- T_{env} + T_{init}}{T_{end} - T_{env}} \\right )}$$" - ], - "text/plain": [ - " ⎛-Tₑₙᵥ + Tᵢₙᵢₜ⎞\n", - "log⎜─────────────⎟\n", - " ⎝ T_end - Tₑₙᵥ⎠\n", - "──────────────────\n", - " t_end " - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solutions = solve(Eq(at_end, T_end), r)\n", - "value_of_r = solutions[0]\n", - "value_of_r" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use `evalf` to plug in numbers for the symbols. The result is a SymPy float, which we have to convert to a Python float." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "sympy.core.numbers.Float" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "subs = dict(t_end=30, T_end=70, T_init=90, T_env=22)\n", - "r_coffee2 = value_of_r.evalf(subs=subs)\n", - "type(r_coffee2)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMoAAAAPBAMAAABXbk2cAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEJmJdjLNVN0iZu+7q0QgoRR7AAAACXBIWXMAAA7EAAAOxAGVKw4bAAADP0lEQVQ4Eb2U3WtcVRTFf3fuzJ3c+chc9KWgMENDBbExQyaKaMWxBKGU0sH8AUlFKPigA+KLLxMUFMHgUEEQBa8oghVJfKhSP+pVEAU/MhRUUEoHEV98SEdjWiPpuM45MzfjP+B9WHufvc7Z65x99zlww8IhzOdsEMm14C/cIfcUeDNHW3Dm8BcjeLHxpZkvBnK1CSa3NtNoEDaOtPEW5/sE9+duN3lv1cTTHOiaBdaGm1Kx4D3KLfDqQJpt7zm8Hvf1LYQ1OrFjYCqZYArD4XCPM+R3ycCPFIfDJt6HvNKlsIG/As56n1+OsEBQ5xtunpXKO3An5YjihoXsVSqrjoHjyQST0W57nGxzjSfhJYJ7j0F5g3JCqUdeqcb2pM6CgXUtgayob6ETFVcp/2Vh+gLVFccQvpWwz/jgt/i97+1wBA5QNhkqK/gDKj2mr5qRs6nK12aKVdmDrXYwkIoFRU3FjD5+kPAf5lcFTcW2PuNup1JNyF6nWmN6W9TIpio7S0f7Npf3h1SamjFl8lp4SI5VeTtI5E4wdhjUKA/fiCmfVdsUdZZdXquT+1MzR3as4u3EfGBzhWKX65qxNYLcXTWNjIrXDBK5KYMfa7T0lGBuJ6IUhdfxrxBsj7OPbaoyjLgtMrlSlee1GAv3tJyKT5Ao5oKGqZoR/jkKhx67YNyv4GEe36VaH1XM2bGK+oT1vlHxdBZTsUyiVRYovudUniBIg5Z5QFP0fRS9S+GfSN6cmnhxSV1ZI2//vrOpyvtSiW1d9F86bThh1gvCLiUtMPp1gsQFR7Q5z8uw2dY2lts/oDumSHlg7lXB/lZnUxX1mDuLqcd6RL6m9QYqA0q7ViV/6dLlT5r7DJ7aEZV68/Uryh1ro3PmQEGP7AaZFbkjm6p09F9sLo5jXoEH4ZiFYsLU344BDfYZcjo2H8ML3fO6gN0aPEPhPMtdeJabWrpH1roLaW9lqe65HmOq7Z0jfLoxu2qhENNpjlUqyQRD3qicJb/NbxFv6q2RRPaU94iiNy5+D2sjO/vpWowF5mdbZH6+9hPewokWgd6oVQv8cvA7tYFh8Df34gkm1L6YPni4T+6iXstw5qIKNq/h//P9CyM1PThWyTs8AAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$0.011610223142273859$$" - ], - "text/plain": [ - "0.011610223142273859" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "r_coffee2 = float(r_coffee2)\n", - "r_coffee2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap17.ipynb b/code/chap17.ipynb deleted file mode 100644 index 765e35060..000000000 --- a/code/chap17.ipynb +++ /dev/null @@ -1,526 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 17\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Data\n", - "\n", - "We have data from Pacini and Bergman (1986), \"MINMOD: a computer program to calculate insulin sensitivity and pancreatic responsivity from the frequently sampled intravenous glucose tolerance test\", *Computer Methods and Programs in Biomedicine*, 23: 113-122.." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "data = pd.read_csv('data/glucose_insulin.csv', index_col='time')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the glucose time series looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "plot(data.glucose, 'bo', label='glucose')\n", - "decorate(xlabel='Time (min)',\n", - " ylabel='Concentration (mg/dL)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And the insulin time series." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "plot(data.insulin, 'go', label='insulin')\n", - "decorate(xlabel='Time (min)',\n", - " ylabel='Concentration ($\\mu$U/mL)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For the book, I put them in a single figure, using `subplot`" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "subplot(2, 1, 1)\n", - "plot(data.glucose, 'bo', label='glucose')\n", - "decorate(ylabel='mg/dL')\n", - "\n", - "subplot(2, 1, 2)\n", - "plot(data.insulin, 'go', label='insulin')\n", - "decorate(xlabel='Time (min)',\n", - " ylabel='$\\mu$U/mL')\n", - "\n", - "savefig('figs/chap08-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Interpolation\n", - "\n", - "We have measurements of insulin concentration at discrete points in time, but we need to estimate it at intervening points. We'll use `interpolate`, which takes a `Series` and returns a function:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The return value from `interpolate` is a function." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "I = interpolate(data.insulin)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use the result, `I`, to estimate the insulin level at any point in time." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "I(7)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`I` can also take an array of time and return an array of estimates:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "t_0 = get_first_label(data)\n", - "t_end = get_last_label(data)\n", - "ts = linrange(t_0, t_end, endpoint=True)\n", - "I(ts)\n", - "type(ts)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the interpolated values look like." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "plot(data.insulin, 'go', label='insulin data')\n", - "plot(ts, I(ts), color='green', label='interpolated')\n", - "\n", - "decorate(xlabel='Time (min)',\n", - " ylabel='Concentration ($\\mu$U/mL)')\n", - "\n", - "savefig('figs/chap08-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** [Read the documentation](https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html) of `scipy.interpolate.interp1d`. Pass a keyword argument to `interpolate` to specify one of the other kinds of interpolation, and run the code again to see what it looks like. " - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The glucose minimal model\n", - "\n", - "I'll cheat by starting with parameters that fit the data roughly; then we'll see how to improve them." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "params = Params(G0 = 290,\n", - " k1 = 0.03,\n", - " k2 = 0.02,\n", - " k3 = 1e-05)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a version of `make_system` that takes the parameters and data:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params, data):\n", - " \"\"\"Makes a System object with the given parameters.\n", - " \n", - " params: sequence of G0, k1, k2, k3\n", - " data: DataFrame with `glucose` and `insulin`\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " G0, k1, k2, k3 = params\n", - " \n", - " Gb = data.glucose[0]\n", - " Ib = data.insulin[0]\n", - " \n", - " t_0 = get_first_label(data)\n", - " t_end = get_last_label(data)\n", - "\n", - " init = State(G=G0, X=0)\n", - " \n", - " return System(G0=G0, k1=k1, k2=k2, k3=k3,\n", - " init=init, Gb=Gb, Ib=Ib,\n", - " t_0=t_0, t_end=t_end, dt=2)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "system = make_system(params, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the update function. It uses `unpack` to make the system variables accessible without using dot notation, which makes the translation of the differential equations more readable and less error prone." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def update_func(state, t, system):\n", - " \"\"\"Updates the glucose minimal model.\n", - " \n", - " state: State object\n", - " t: time in min\n", - " system: System object\n", - " \n", - " returns: State object\n", - " \"\"\"\n", - " G, X = state\n", - " unpack(system)\n", - " \n", - " dGdt = -k1 * (G - Gb) - X*G\n", - " dXdt = k3 * (I(t) - Ib) - k2 * X\n", - " \n", - " G += dGdt * dt\n", - " X += dXdt * dt\n", - "\n", - " return State(G=G, X=X)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Before running the simulation, it is always a good idea to test the update function using the initial conditions. In this case we can veryify that the results are at least qualitatively correct." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "update_func(system.init, system.t_0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now `run_simulation` is pretty much the same as it always is." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \n", - " returns: TimeFrame\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " frame = TimeFrame(columns=init.index)\n", - " frame.row[t_0] = init\n", - " ts = linrange(t_0, t_end, dt)\n", - " \n", - " for t in ts:\n", - " frame.row[t+dt] = update_func(frame.row[t], t, system)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's how we run it. `%time` is a Jupyter magic command that runs the function and reports its run time." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "%time results = run_simulation(system, update_func);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The results are in a `TimeFrame object` with one column per state variable." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following plot shows the results of the simulation along with the actual glucose data." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "subplot(2, 1, 1)\n", - "\n", - "plot(results.G, 'b-', label='simulation')\n", - "plot(data.glucose, 'bo', label='glucose data')\n", - "decorate(ylabel='mg/dL')\n", - "\n", - "subplot(2, 1, 2)\n", - "\n", - "plot(results.X, 'g-', label='remote insulin')\n", - "\n", - "decorate(xlabel='Time (min)', \n", - " ylabel='Arbitrary units')\n", - "\n", - "savefig('figs/chap08-fig03.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Under the hood" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "%psource interpolate" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises\n", - "\n", - "**Exercise:** Our solution to the differential equations is only approximate because we used a finite step size, `dt=2` minutes.\n", - "\n", - "If we make the step size smaller, we expect the solution to be more accurate. Run the simulation with `dt=1` and compare the results. What is the largest relative error between the two solutions?" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap18.ipynb b/code/chap18.ipynb deleted file mode 100644 index cbfd4fe77..000000000 --- a/code/chap18.ipynb +++ /dev/null @@ -1,738 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 18\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Code from the previous chapter\n", - "\n", - "Read the data." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "data = pd.read_csv('data/glucose_insulin.csv', index_col='time');" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Interpolate the insulin data." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "I = interpolate(data.insulin)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Initialize the parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "G0 = 290\n", - "k1 = 0.03\n", - "k2 = 0.02\n", - "k3 = 1e-05" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To estimate basal levels, we'll use the concentrations at `t=0`." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "Gb = data.glucose[0]\n", - "Ib = data.insulin[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create the initial condtions." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "init = State(G=G0, X=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make the `System` object." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "t_0 = get_first_label(data)\n", - "t_end = get_last_label(data)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "system = System(init=init, \n", - " k1=k1, k2=k2, k3=k3,\n", - " I=I, Gb=Gb, Ib=Ib,\n", - " t_0=t_0, t_end=t_end, dt=2)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "def update_func(state, t, system):\n", - " \"\"\"Updates the glucose minimal model.\n", - " \n", - " state: State object\n", - " t: time in min\n", - " system: System object\n", - " \n", - " returns: State object\n", - " \"\"\"\n", - " G, X = state\n", - " unpack(system)\n", - " \n", - " dGdt = -k1 * (G - Gb) - X*G\n", - " dXdt = k3 * (I(t) - Ib) - k2 * X\n", - " \n", - " G += dGdt * dt\n", - " X += dXdt * dt\n", - "\n", - " return State(G=G, X=X)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \n", - " returns: TimeFrame\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " frame = TimeFrame(columns=init.index)\n", - " frame.row[t_0] = init\n", - " ts = linrange(t_0, t_end, dt)\n", - " \n", - " for t in ts:\n", - " frame.row[t+dt] = update_func(frame.row[t], t, system)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "%time results = run_simulation(system, update_func);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Numerical solution\n", - "\n", - "In the previous chapter, we approximated the differential equations with difference equations, and solved them using `run_simulation`.\n", - "\n", - "In this chapter, we solve the differential equation numerically using `run_ode_solver`, which is a wrapper for the SciPy ODE solver.\n", - "\n", - "Instead of an update function, we provide a slope function that evaluates the right-hand side of the differential equations. We don't have to do the update part; the solver does it for us." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes derivatives of the glucose minimal model.\n", - " \n", - " state: State object\n", - " t: time in min\n", - " system: System object\n", - " \n", - " returns: derivatives of G and X\n", - " \"\"\"\n", - " G, X = state\n", - " unpack(system)\n", - " \n", - " dGdt = -k1 * (G - Gb) - X*G\n", - " dXdt = k3 * (I(t) - Ib) - k2 * X\n", - " \n", - " return dGdt, dXdt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can test the slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "slope_func(init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we run the ODE solver." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "%time results2, details = run_ode_solver(system, slope_func, t_eval=data.index);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`details` is a `ModSimSeries` object with information about how the solver worked." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`results` is a `TimeFrame` with one row for each time step and one column for each state variable:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "results2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting the results from `run_simulation` and `run_ode_solver`, we can see that they are not very different." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "plot(results.G, 'g-')\n", - "plot(results2.G, 'b-')\n", - "plot(data.glucose, 'bo')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The differences in `G` are less than 1%." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "diff = results.G - results2.G\n", - "percent_diff = diff / results2.G * 100\n", - "percent_diff.dropna()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Optimization" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's find the parameters that yield the best fit for the data. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll use these values as an initial estimate and iteratively improve them." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "params = Params(G0 = 290,\n", - " k1 = 0.03,\n", - " k2 = 0.02,\n", - " k3 = 1e-05)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`make_system` takes the parameters and actual data and returns a `System` object." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params, data):\n", - " \"\"\"Makes a System object with the given parameters.\n", - " \n", - " params: sequence of G0, k1, k2, k3\n", - " data: DataFrame with `glucose` and `insulin`\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " G0, k1, k2, k3 = params\n", - " \n", - " Gb = data.glucose[0]\n", - " Ib = data.insulin[0]\n", - " \n", - " t_0 = get_first_label(data)\n", - " t_end = get_last_label(data)\n", - "\n", - " init = State(G=G0, X=0)\n", - " \n", - " return System(G0=G0, k1=k1, k2=k2, k3=k3,\n", - " init=init, Gb=Gb, Ib=Ib,\n", - " t_0=t_0, t_end=t_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "system = make_system(params, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`error_func` takes the parameters and actual data, makes a `System` object, and runs `odeint`, then compares the results to the data. It returns an array of errors." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def error_func(params, data):\n", - " \"\"\"Computes an array of errors to be minimized.\n", - " \n", - " params: sequence of parameters\n", - " data: DataFrame of values to be matched\n", - " \n", - " returns: array of errors\n", - " \"\"\"\n", - " print(params)\n", - " \n", - " # make a System with the given parameters\n", - " system = make_system(params, data)\n", - " \n", - " # solve the ODE\n", - " results, details = run_ode_solver(system, slope_func, t_eval=data.index)\n", - " \n", - " # compute the difference between the model\n", - " # results and actual data\n", - " errors = results.G - data.glucose\n", - " return errors" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When we call `error_func`, we provide a sequence of parameters as a single object." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how that works:" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "error_func(params, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`fit_leastsq` is a wrapper for `scipy.optimize.leastsq`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we call it." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "best_params, fit_details = fit_leastsq(error_func, params, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The first return value is a `Params` object with the best parameters:" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "best_params" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The second return value is a `ModSimSeries` object with information about the results." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "fit_details" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "fit_details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we have `best_params`, we can use it to make a `System` object and run it." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "system = make_system(best_params, data)\n", - "results, details = run_ode_solver(system, slope_func, t_eval=data.index)\n", - "details.message" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the results, along with the data. The first few points of the model don't fit the data, but we don't expect them to." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "plot(results.G, label='simulation')\n", - "plot(data.glucose, 'bo', label='glucose data')\n", - "\n", - "decorate(xlabel='Time (min)',\n", - " ylabel='Concentration (mg/dL)')\n", - "\n", - "savefig('figs/chap08-fig04.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Interpreting parameters\n", - "\n", - "Based on the parameters of the model, we can estimate glucose effectiveness and insulin sensitivity." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def indices(params):\n", - " \"\"\"Compute glucose effectiveness and insulin sensitivity.\n", - " \n", - " params: sequence of G0, k1, k2, k3\n", - " data: DataFrame with `glucose` and `insulin`\n", - " \n", - " returns: State object containing S_G and S_I\n", - " \"\"\"\n", - " G0, k1, k2, k3 = params\n", - " return State(S_G=k1, S_I=k3/k2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the results." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "indices(best_params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Under the hood\n", - "\n", - "Here's the source code for `run_ode_solver` and `fit_leastsq`, if you'd like to know how they work." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "%psource run_ode_solver" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "%psource fit_leastsq" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Exercise:** Since we don't expect the first few points to agree, it's probably better not to make them part of the optimization process. We can ignore them by leaving them out of the `Series` returned by `error_func`. Modify the last line of `error_func` to return `errors.loc[8:]`, which includes only the elements of the `Series` from `t=8` and up.\n", - "\n", - "Does that improve the quality of the fit? Does it change the best parameters by much?\n", - "\n", - "Note: You can read more about this use of `loc` [in the Pandas documentation](https://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-integer)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** How sensitive are the results to the starting guess for the parameters. If you try different values for the starting guess, do we get the same values for the best parameters?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Related reading:** You might be interested in this article about [people making a DIY artificial pancreas](https://www.bloomberg.com/news/features/2018-08-08/the-250-biohack-that-s-revolutionizing-life-with-diabetes)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap20.ipynb b/code/chap20.ipynb deleted file mode 100644 index eccfea85c..000000000 --- a/code/chap20.ipynb +++ /dev/null @@ -1,676 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 20\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Dropping pennies\n", - "\n", - "I'll start by getting the units we need from Pint." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "m = UNITS.meter\n", - "s = UNITS.second" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And defining the initial state." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "init = State(y=381 * m, \n", - " v=0 * m/s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Acceleration due to gravity is about 9.8 m / s$^2$." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "g = 9.8 * m/s**2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When we call `odeint`, we need an array of timestamps where we want to compute the solution.\n", - "\n", - "I'll start with a duration of 10 seconds." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "t_end = 10 * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we make a `System` object." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "system = System(init=init, g=g, t_end=t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And define the slope function." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Compute derivatives of the state.\n", - " \n", - " state: position, velocity\n", - " t: time\n", - " system: System object containing `g`\n", - " \n", - " returns: derivatives of y and v\n", - " \"\"\"\n", - " y, v = state\n", - " unpack(system) \n", - "\n", - " dydt = v\n", - " dvdt = -g\n", - " \n", - " return dydt, dvdt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It's always a good idea to test the slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "dydt, dvdt = slope_func(init, 0, system)\n", - "print(dydt)\n", - "print(dvdt)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we're ready to call `run_ode_solver`" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "results, details = run_ode_solver(system, slope_func, max_step=0.5*s)\n", - "details.message" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the results:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's position as a function of time:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_position(results):\n", - " plot(results.y, label='y')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Position (m)')\n", - "\n", - "plot_position(results)\n", - "savefig('figs/chap09-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Onto the sidewalk\n", - "\n", - "To figure out when the penny hit the sidewalk, we can use `crossings`, which finds the times where a `Series` passes through a given value." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "t_crossings = crossings(results.y, 0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For this example there should be just one crossing, the time when the penny hits the sidewalk." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "t_sidewalk = t_crossings[0] * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can compare that to the exact result. Without air resistance, we have\n", - "\n", - "$v = -g t$\n", - "\n", - "and\n", - "\n", - "$y = 381 - g t^2 / 2$\n", - "\n", - "Setting $y=0$ and solving for $t$ yields\n", - "\n", - "$t = \\sqrt{\\frac{2 y_{init}}{g}}$" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "sqrt(2 * init.y / g)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The estimate is accurate to about 10 decimal places." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Events\n", - "\n", - "Instead of running the simulation until the penny goes through the sidewalk, it would be better to detect the point where the penny hits the sidewalk and stop. `run_ode_solver` provides exactly the tool we need, **event functions**.\n", - "\n", - "Here's an event function that returns the height of the penny above the sidewalk:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def event_func(state, t, system):\n", - " \"\"\"Return the height of the penny above the sidewalk.\n", - " \"\"\"\n", - " y, v = state\n", - " return y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's how we pass it to `run_ode_solver`. The solver should run until the event function returns 0, and then terminate." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "results, details = run_ode_solver(system, slope_func, events=event_func)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The message from the solver indicates the solver stopped because the event we wanted to detect happened.\n", - "\n", - "Here are the results:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With the `events` option, the solver returns the actual time steps it computed, which are not necessarily equally spaced. \n", - "\n", - "The last time step is when the event occurred:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "t_sidewalk = get_last_label(results) * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Unfortunately, `run_ode_solver` does not carry the units through the computation, so we have to put them back at the end.\n", - "\n", - "We could also get the time of the event from `details`, but it's a minor nuisance because it comes packed in an array:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "details.t_events[0][0] * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result is accurate to about 15 decimal places.\n", - "\n", - "We can also check the velocity of the penny when it hits the sidewalk:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "v_sidewalk = get_last_value(results.v) * m / s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And convert to kilometers per hour." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "km = UNITS.kilometer\n", - "h = UNITS.hour\n", - "v_sidewalk.to(km / h)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If there were no air resistance, the penny would hit the sidewalk (or someone's head) at more than 300 km/h.\n", - "\n", - "So it's a good thing there is air resistance." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Under the hood\n", - "\n", - "Here is the source code for `crossings` so you can see what's happening under the hood:" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "%psource crossings" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The [documentation of InterpolatedUnivariateSpline is here](https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.InterpolatedUnivariateSpline.html).\n", - "\n", - "And you can read the [documentation of `scipy.integrate.solve_ivp`](https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.solve_ivp.html) to learn more about how `run_ode_solver` works." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises\n", - "\n", - "**Exercise:** Here's a question from the web site [Ask an Astronomer](http://curious.astro.cornell.edu/about-us/39-our-solar-system/the-earth/other-catastrophes/57-how-long-would-it-take-the-earth-to-fall-into-the-sun-intermediate):\n", - "\n", - "\"If the Earth suddenly stopped orbiting the Sun, I know eventually it would be pulled in by the Sun's gravity and hit it. How long would it take the Earth to hit the Sun? I imagine it would go slowly at first and then pick up speed.\"\n", - "\n", - "Use `run_ode_solver` to answer this question.\n", - "\n", - "Here are some suggestions about how to proceed:\n", - "\n", - "1. Look up the Law of Universal Gravitation and any constants you need. I suggest you work entirely in SI units: meters, kilograms, and Newtons.\n", - "\n", - "2. When the distance between the Earth and the Sun gets small, this system behaves badly, so you should use an event function to stop when the surface of Earth reaches the surface of the Sun.\n", - "\n", - "3. Express your answer in days, and plot the results as millions of kilometers versus days.\n", - "\n", - "If you read the reply by Dave Rothstein, you will see other ways to solve the problem, and a good discussion of the modeling decisions behind them.\n", - "\n", - "You might also be interested to know that [it's actually not that easy to get to the Sun](https://www.theatlantic.com/science/archive/2018/08/parker-solar-probe-launch-nasa/567197/)." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap22.ipynb b/code/chap22.ipynb deleted file mode 100644 index afa7640f1..000000000 --- a/code/chap22.ipynb +++ /dev/null @@ -1,1091 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 22\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Vectors" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `Vector` object represents a vector quantity. In the context of mechanics, vector quantities include position, velocity, acceleration, and force, all of which might be in 2D or 3D.\n", - "\n", - "You can define a `Vector` object without units, but if it represents a physical quantity, you will often want to attach units to it.\n", - "\n", - "I'll start by grabbing the units we'll need." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a two dimensional `Vector` in meters." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "A = Vector(3, 4) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can access the elements by name." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "A.x" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "A.y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The magnitude is the length of the vector." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "A.mag" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The angle is the number of radians between the vector and the positive x axis." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "A.angle" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we make another `Vector` with the same units," - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "B = Vector(1, 2) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can add `Vector` objects like this" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "A + B" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And subtract like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "A - B" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can compute the Euclidean distance between two Vectors." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "A.dist(B)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And the difference in angle" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "A.diff_angle(B)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we are given the magnitude and angle of a vector, what we have is the representation of the vector in polar coordinates." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "mag = A.mag\n", - "angle = A.angle" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use `pol2cart` to convert from polar to Cartesian coordinates, and then use the Cartesian coordinates to make a `Vector` object.\n", - "\n", - "In this example, the `Vector` we get should have the same components as `A`." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "x, y = pol2cart(angle, mag)\n", - "Vector(x, y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another way to represent the direction of `A` is a unit vector, which is a vector with magnitude 1 that points in the same direction as `A`. You can compute a unit vector by dividing a vector by its magnitude:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "A / A.mag" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Or by using the `hat` function, so named because unit vectors are conventionally decorated with a hat, like this: $\\hat{A}$:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "A.hat()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Create a `Vector` named `a_grav` that represents acceleration due to gravity, with x component 0 and y component $-9.8$ meters / second$^2$." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Degrees and radians\n", - "\n", - "Pint provides units to represent degree and radians." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "degree = UNITS.degree\n", - "radian = UNITS.radian" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you have an angle in degrees," - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "angle = 45 * degree\n", - "angle" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can convert to radians." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "angle_rad = angle.to(radian)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If it's already in radians, `to` does the right thing." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "angle_rad.to(radian)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also convert from radians to degrees." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "angle_rad.to(degree)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As an alterative, you can use `np.deg2rad`, which works with Pint quantities, but it also works with simple numbers and NumPy arrays:" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "np.deg2rad(angle)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Create a `Vector` named `a_force` that represents acceleration due to a force of 0.5 Newton applied to an object with mass 0.3 kilograms, in a direction 45 degrees up from the positive x-axis.\n", - "\n", - "Add `a_force` to `a_grav` from the previous exercise. If that addition succeeds, that means that the units are compatible. Confirm that the total acceleration seems to make sense." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Baseball" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a `Params` object that contains parameters for the flight of a baseball." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "params = Params(x = 0 * m, \n", - " y = 1 * m,\n", - " g = 9.8 * m/s**2,\n", - " mass = 145e-3 * kg,\n", - " diameter = 73e-3 * m,\n", - " rho = 1.2 * kg/m**3,\n", - " C_d = 0.33,\n", - " angle = 45 * degree,\n", - " velocity = 40 * m / s,\n", - " t_end = 10 * s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the function that uses the `Params` object to make a `System` object." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Make a system object.\n", - " \n", - " params: Params object with angle, velocity, x, y,\n", - " diameter, duration, g, mass, rho, and C_d\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " # convert angle to degrees\n", - " theta = np.deg2rad(angle)\n", - " \n", - " # compute x and y components of velocity\n", - " vx, vy = pol2cart(theta, velocity)\n", - " \n", - " # make the initial state\n", - " init = State(x=x, y=y, vx=vx, vy=vy)\n", - " \n", - " # compute area from diameter\n", - " area = np.pi * (diameter/2)**2\n", - " \n", - " return System(params, init=init, area=area)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we use it:" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "system = make_system(params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a function that computes drag force using vectors:" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "def drag_force(v, system):\n", - " \"\"\"Computes drag force in the opposite direction of `v`.\n", - " \n", - " v: velocity Vector\n", - " system: System object with rho, C_d, area\n", - " \n", - " returns: Vector drag force\n", - " \"\"\"\n", - " unpack(system)\n", - " mag = -rho * v.mag**2 * C_d * area / 2\n", - " direction = v.hat()\n", - " f_drag = direction * mag\n", - " return f_drag" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can test it like this." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "v_test = Vector(10, 10) * m/s\n", - "drag_force(v_test, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the slope function that computes acceleration due to gravity and drag." - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes derivatives of the state variables.\n", - " \n", - " state: State (x, y, x velocity, y velocity)\n", - " t: time\n", - " system: System object with g, rho, C_d, area, mass\n", - " \n", - " returns: sequence (vx, vy, ax, ay)\n", - " \"\"\"\n", - " x, y, vx, vy = state\n", - " unpack(system)\n", - "\n", - " v = Vector(vx, vy) \n", - " a_drag = drag_force(v, system) / mass\n", - " a_grav = Vector(0, -g)\n", - " \n", - " a = a_grav + a_drag\n", - " \n", - " return vx, vy, a.x, a.y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Always test the slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [], - "source": [ - "slope_func(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use an event function to stop the simulation when the ball hits the ground:" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func(state, t, system):\n", - " \"\"\"Stop when the y coordinate is 0.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: y coordinate\n", - " \"\"\"\n", - " x, y, vx, vy = state\n", - " return y" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "event_func(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can call `run_ode_solver`" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "results, details = run_ode_solver(system, slope_func, events=event_func, max_step=0.2*s)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The final label tells us the flight time." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [], - "source": [ - "flight_time = get_last_label(results) * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The final value of `x` tells us the how far the ball landed from home plate:" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [], - "source": [ - "x_dist = get_last_value(results.x) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Visualizing the results\n", - "\n", - "The simplest way to visualize the results is to plot x and y as functions of time." - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [], - "source": [ - "plot(results.x, label='x')\n", - "plot(results.y, label='y')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Position (m)')\n", - "\n", - "savefig('figs/chap10-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can plot the velocities the same way." - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [], - "source": [ - "plot(results.vx, label='vx')\n", - "plot(results.vy, label='vy')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Velocity (m/s)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The x velocity slows down due to drag.\n", - "\n", - "The y velocity drops quickly while drag and gravity are in the same direction, then more slowly after the ball starts to fall.\n", - "\n", - "Another way to visualize the results is to plot y versus x. The result is the trajectory of the ball through its plane of motion." - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_trajectory(results):\n", - " plot(results.x, results.y, label='trajectory')\n", - "\n", - " decorate(xlabel='x position (m)',\n", - " ylabel='y position (m)')\n", - "\n", - "plot_trajectory(results)\n", - "savefig('figs/chap10-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Under the hood\n", - "\n", - "`Vector` is a function that returns a `ModSimVector` object." - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [], - "source": [ - "v = Vector(3, 4)\n", - "type(v)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `ModSimVector` is a specialized kind of Pint `Quantity`." - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [], - "source": [ - "isinstance(v, Quantity)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There's one gotcha you might run into with Vectors and Quantities. If you multiply a `Vector` and a `Quantity`, you get a `Vector`:" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [], - "source": [ - "v1 = v * m" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": {}, - "outputs": [], - "source": [ - "type(v1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But if you multiply a `Quantity` and a `Vector`, you get a `Quantity`:" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": {}, - "outputs": [], - "source": [ - "v2 = m * v" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": {}, - "outputs": [], - "source": [ - "type(v2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With a `Vector` you can get the coordinates using dot notation, as well as `mag`, `mag2`, and `angle`:" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": {}, - "outputs": [], - "source": [ - "v1.x, v1.y, v1.mag, v1.angle" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With a `Quantity`, you can't. But you can use indexing to get the coordinates:" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": {}, - "outputs": [], - "source": [ - "v2[0], v2[1]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And you can use vector functions to get the magnitude and angle." - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "metadata": {}, - "outputs": [], - "source": [ - "vector_mag(v2), vector_angle(v2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And often you can avoid the whole issue by doing the multiplication with the `Vector` on the left." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Run the simulation for a few different launch angles and visualize the results. Are they consistent with your expectations?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** The baseball stadium in Denver, Colorado is 1,580 meters above sea level, where the density of air is about 1.0 kg / meter$^3$. How much farther would a ball hit with the same velocity and launch angle travel?\n", - "\n", - "Hint: create a new `Params` object like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "metadata": {}, - "outputs": [], - "source": [ - "params2 = Params(params, rho=1*kg/m**3)" - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** The model so far is based on the assumption that coefficient of drag does not depend on velocity, but in reality it does. The following figure, from Adair, [*The Physics of Baseball*](https://books.google.com/books/about/The_Physics_of_Baseball.html?id=4xE4Ngpk_2EC), shows coefficient of drag as a function of velocity.\n", - "\n", - "\n", - "\n", - "\n", - "I used [an online graph digitizer](https://automeris.io/WebPlotDigitizer/) to extract the data and save it in a CSV file. Here's how we can read it:" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Modify the model to include the dependence of `C_d` on velocity, and see how much it affects the results. Hint: use `interpolate`." - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 66, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 67, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap23.ipynb b/code/chap23.ipynb deleted file mode 100644 index 2031f90b1..000000000 --- a/code/chap23.ipynb +++ /dev/null @@ -1,572 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 23\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Code from the previous chapter" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram\n", - "degree = UNITS.degree" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "params = Params(x = 0 * m, \n", - " y = 1 * m,\n", - " g = 9.8 * m/s**2,\n", - " mass = 145e-3 * kg,\n", - " diameter = 73e-3 * m,\n", - " rho = 1.2 * kg/m**3,\n", - " C_d = 0.3,\n", - " angle = 45 * degree,\n", - " velocity = 40 * m / s,\n", - " t_end = 20 * s)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Make a system object.\n", - " \n", - " params: Params object with angle, velocity, x, y,\n", - " diameter, duration, g, mass, rho, and C_d\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " # convert angle to degrees\n", - " theta = np.deg2rad(angle)\n", - " \n", - " # compute x and y components of velocity\n", - " vx, vy = pol2cart(theta, velocity)\n", - " \n", - " # make the initial state\n", - " init = State(x=x, y=y, vx=vx, vy=vy)\n", - " \n", - " # compute area from diameter\n", - " area = np.pi * (diameter/2)**2\n", - " \n", - " return System(params, init=init, area=area)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def drag_force(V, system):\n", - " \"\"\"Computes drag force in the opposite direction of `V`.\n", - " \n", - " V: velocity\n", - " system: System object with rho, C_d, area\n", - " \n", - " returns: Vector drag force\n", - " \"\"\"\n", - " unpack(system)\n", - " mag = -rho * V.mag**2 * C_d * area / 2\n", - " direction = V.hat()\n", - " f_drag = mag * direction\n", - " return f_drag" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes derivatives of the state variables.\n", - " \n", - " state: State (x, y, x velocity, y velocity)\n", - " t: time\n", - " system: System object with g, rho, C_d, area, mass\n", - " \n", - " returns: sequence (vx, vy, ax, ay)\n", - " \"\"\"\n", - " x, y, vx, vy = state\n", - " unpack(system)\n", - "\n", - " V = Vector(vx, vy) \n", - " a_drag = drag_force(V, system) / mass\n", - " a_grav = Vector(0, -g)\n", - " \n", - " a = a_grav + a_drag\n", - " \n", - " return vx, vy, a.x, a.y" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func(state, t, system):\n", - " \"\"\"Stop when the y coordinate is 0.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: y coordinate\n", - " \"\"\"\n", - " x, y, vx, vy = state\n", - " return y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Optimal launch angle\n", - "\n", - "To find the launch angle that maximizes distance from home plate, we need a function that takes launch angle and returns range." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def range_func(angle, params): \n", - " \"\"\"Computes range for a given launch angle.\n", - " \n", - " angle: launch angle in degrees\n", - " params: Params object\n", - " \n", - " returns: distance in meters\n", - " \"\"\"\n", - " params = Params(params, angle=angle)\n", - " system = make_system(params)\n", - " results, details = run_ode_solver(system, slope_func, events=event_func)\n", - " x_dist = get_last_value(results.x) * m\n", - " return x_dist" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's test `range_func`." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "%time range_func(45, params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And sweep through a range of angles." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "angles = linspace(20, 80, 21)\n", - "sweep = SweepSeries()\n", - "\n", - "for angle in angles:\n", - " x_dist = range_func(angle, params)\n", - " print(angle, x_dist)\n", - " sweep[angle] = x_dist" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting the `Sweep` object, it looks like the peak is between 40 and 45 degrees." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "plot(sweep, color='C2')\n", - "decorate(xlabel='Launch angle (degree)',\n", - " ylabel='Range (m)',\n", - " title='Range as a function of launch angle',\n", - " legend=False)\n", - "\n", - "savefig('figs/chap10-fig03.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use `max_bounded` to search for the peak efficiently." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "%time res = max_bounded(range_func, [0, 90], params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`res` is an `ModSimSeries` object with detailed results:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "res" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`x` is the optimal angle and `fun` the optional range." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "optimal_angle = res.x * degree" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "max_x_dist = res.fun" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Under the hood\n", - "\n", - "Read the source code for `max_bounded` and `min_bounded`, below.\n", - "\n", - "Add a print statement to `range_func` that prints `angle`. Then run `max_bounded` again so you can see how many times it calls `range_func` and what the arguments are." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "%psource max_bounded" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "%psource min_bounded" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The Manny Ramirez problem\n", - "\n", - "Finally, let's solve the Manny Ramirez problem:\n", - "\n", - "*What is the minimum effort required to hit a home run in Fenway Park?*\n", - "\n", - "Fenway Park is a baseball stadium in Boston, Massachusetts. One of its most famous features is the \"Green Monster\", which is a wall in left field that is unusually close to home plate, only 310 feet along the left field line. To compensate for the short distance, the wall is unusually high, at 37 feet.\n", - "\n", - "Although the problem asks for a minimum, it is not an optimization problem. Rather, we want to solve for the initial velocity that just barely gets the ball to the top of the wall, given that it is launched at the optimal angle.\n", - "\n", - "And we have to be careful about what we mean by \"optimal\". For this problem, we don't want the longest range, we want the maximum height at the point where it reaches the wall.\n", - "\n", - "If you are ready to solve the problem on your own, go ahead. Otherwise I will walk you through the process with an outline and some starter code.\n", - "\n", - "As a first step, write a function called `height_func` that takes a launch angle and a params as parameters, simulates the flights of a baseball, and returns the height of the baseball when it reaches a point 94.5 meters (310 feet) from home plate." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Always test the slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your function with a launch angle of 45 degrees:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now use `max_bounded` to find the optimal angle. Is it higher or lower than the angle that maximizes range?" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With initial velocity 40 m/s and an optimal launch angle, the ball clears the Green Monster with a little room to spare.\n", - "\n", - "Which means we can get over the wall with a lower initial velocity." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Finding the minimum velocity\n", - "\n", - "Even though we are finding the \"minimum\" velocity, we are not really solving a minimization problem. Rather, we want to find the velocity that makes the height at the wall exactly 11 m, given given that it's launched at the optimal angle. And that's a job for `fsolve`.\n", - "\n", - "Write an error function that takes a velocity and a `Params` object as parameters. It should use `max_bounded` to find the highest possible height of the ball at the wall, for the given velocity. Then it should return the difference between that optimal height and 11 meters." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your error function before you call `fsolve`." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then use `fsolve` to find the answer to the problem, the minimum velocity that gets the ball out of the park." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And just to check, run `error_func` with the value you found." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap24.ipynb b/code/chap24.ipynb deleted file mode 100644 index 027995537..000000000 --- a/code/chap24.ipynb +++ /dev/null @@ -1,574 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 24\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Rolling paper\n", - "\n", - "We'll start by loading the units we need." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "radian = UNITS.radian\n", - "m = UNITS.meter\n", - "s = UNITS.second" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And creating a `Params` object with the system parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "params = Params(Rmin = 0.02 * m,\n", - " Rmax = 0.055 * m,\n", - " L = 47 * m,\n", - " t_end = 130 * s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function estimates the parameter `k`, which is the increase in the radius of the roll for each radian of rotation. " - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def estimate_k(params):\n", - " \"\"\"Estimates the parameter `k`.\n", - " \n", - " params: Params with Rmin, Rmax, and L\n", - " \n", - " returns: k in meters per radian\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " Ravg = (Rmax + Rmin) / 2\n", - " Cavg = 2 * pi * Ravg\n", - " revs = L / Cavg\n", - " rads = 2 * pi * revs\n", - " k = (Rmax - Rmin) / rads\n", - " return k" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As usual, `make_system` takes a `Params` object and returns a `System` object." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Make a system object.\n", - " \n", - " params: Params with Rmin, Rmax, and L\n", - " \n", - " returns: System with init, k, and ts\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " init = State(theta = 0 * radian,\n", - " y = 0 * m,\n", - " r = Rmin)\n", - " \n", - " k = estimate_k(params)\n", - " \n", - " return System(init=init, k=k, t_end=t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `make_system`" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "system = make_system(params)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "system.init" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can write a slope function based on the differential equations\n", - "\n", - "$\\omega = \\frac{d\\theta}{dt} = 10$\n", - "\n", - "$\\frac{dy}{dt} = r \\frac{d\\theta}{dt}$\n", - "\n", - "$\\frac{dr}{dt} = k \\frac{d\\theta}{dt}$\n" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object with theta, y, r\n", - " t: time\n", - " system: System object with r, k\n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " theta, y, r = state\n", - " unpack(system)\n", - " \n", - " omega = 10 * radian / s\n", - " dydt = r * omega\n", - " drdt = k * omega\n", - " \n", - " return omega, dydt, drdt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `slope_func`" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "slope_func(system.init, 0*s, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll use an event function to stop when `y=L`." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func(state, t, system):\n", - " \"\"\"Detects when we've rolled length `L`.\n", - " \n", - " state: State object with theta, y, r\n", - " t: time\n", - " system: System object with r, k\n", - " \n", - " returns: difference between `y` and `L`\n", - " \"\"\"\n", - " theta, y, r = state\n", - " unpack(system)\n", - " \n", - " return y - L" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "results, details = run_ode_solver(system, slope_func, events=event_func, max_step=1*s)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And look at the results." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The final value of `y` is 47 meters, as expected." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "unrolled = get_last_value(results.y) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The final value of radius is `R_max`." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "radius = get_last_value(results.r) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The total number of rotations is close to 200, which seems credible." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "rotation = get_last_value(results.theta) / 2 / np.pi" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The elapsed time is plausible." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "t_final = get_last_label(results) * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Plotting" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `theta`" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_theta(results):\n", - " plot(results.theta, color='C0', label='theta')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Angle (rad)')\n", - " \n", - "plot_theta(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `y`" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_y(results):\n", - " plot(results.y, color='C1', label='y')\n", - "\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Length (m)')\n", - " \n", - "plot_y(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `r`" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_r(results):\n", - " plot(results.r, color='C2', label='r')\n", - "\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Radius (mm)')\n", - " \n", - "plot_r(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also see the relationship between `y` and `r`, which I derive analytically in the book." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "plot(results.r, results.y, color='C3')\n", - "\n", - "decorate(xlabel='Radius (mm)',\n", - " ylabel='Length (m)',\n", - " legend=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the figure from the book." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_three(results):\n", - " subplot(3, 1, 1)\n", - " plot_theta(results)\n", - "\n", - " subplot(3, 1, 2)\n", - " plot_y(results)\n", - "\n", - " subplot(3, 1, 3)\n", - " plot_r(results)\n", - "\n", - "plot_three(results)\n", - "savefig('figs/chap11-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises\n", - "\n", - "**Exercise:** Since we keep `omega` constant, the linear velocity of the paper increases with radius. Use `gradient` to estimate the derivative of `results.y`. What is the peak linear velocity?" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now suppose the peak velocity is the limit; that is, we can't move the paper any faster than that.\n", - "\n", - "Nevertheless, we might be able to speed up the process by keeping the linear velocity at the maximum all the time.\n", - "\n", - "Write a slope function that keeps the linear velocity, `dydt`, constant, and computes the angular velocity, `omega`, accordingly.\n", - "\n", - "Run the simulation and see how much faster we could finish rolling the paper." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/chap25.ipynb b/code/chap25.ipynb deleted file mode 100644 index 66f81337f..000000000 --- a/code/chap25.ipynb +++ /dev/null @@ -1,853 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 25\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Teapots and Turntables" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Tables in Chinese restaurants often have a rotating tray or turntable that makes it easy for customers to share dishes. These turntables are supported by low-friction bearings that allow them to turn easily and glide. However, they can be heavy, especially when they are loaded with food, so they have a high moment of inertia.\n", - "\n", - "Suppose I am sitting at a table with a pot of tea on the turntable directly in front of me, and the person sitting directly opposite asks me to pass the tea. I push on the edge of the turntable with 1 Newton of force until it has turned 0.5 radians, then let go. The turntable glides until it comes to a stop 1.5 radians from the starting position. How much force should I apply for a second push so the teapot glides to a stop directly opposite me?\n", - "\n", - "The following figure shows the scenario, where `F` is the force I apply to the turntable at the perimeter, perpendicular to the moment arm, `r`, and `tau` is the resulting torque. The blue circle near the bottom is the teapot.\n", - "\n", - "![](diagrams/teapot.png)\n", - "\n", - "We'll answer this question in these steps:\n", - "\n", - "1. We'll use the results from the first push to estimate the coefficient of friction for the turntable.\n", - "\n", - "2. We'll use that coefficient of friction to estimate the force needed to rotate the turntable through the remaining angle.\n", - "\n", - "Our simulation will use the following parameters:\n", - "\n", - "1. The radius of the turntable is 0.5 meters, and its weight is 7 kg.\n", - "\n", - "2. The teapot weights 0.3 kg, and it sits 0.4 meters from the center of the turntable.\n", - "\n", - "As usual, I'll get units from Pint." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "radian = UNITS.radian\n", - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram\n", - "N = UNITS.newton" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And store the parameters in a `Params` object." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "params = Params(radius_disk=0.5*m,\n", - " mass_disk=7*kg,\n", - " radius_pot=0.4*m,\n", - " mass_pot=0.3*kg,\n", - " force=1*N,\n", - " torque_friction=0.2*N*m,\n", - " theta_end=0.5*radian)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`make_system` creates the initial state, `init`, and computes the total moment of inertia for the turntable and the teapot." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Make a system object.\n", - " \n", - " params: Params object\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " init = State(theta=0, omega=0)\n", - " \n", - " I_disk = mass_disk * radius_disk**2 / 2\n", - " I_pot = mass_pot * radius_pot**2\n", - " \n", - " return System(params, init=init, t_end=20*s,\n", - " I=I_disk+I_pot)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the `System` object we'll use for the first phase of the simulation, while I am pushing the turntable." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "system1 = make_system(params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Simulation\n", - "\n", - "When I stop pushing on the turntable, the angular acceleration changes abruptly. We could implement the slope function with an `if` statement that checks the value of `theta` and sets `force` accordingly. And for a coarse model like this one, that might be fine. But we will get more accurate results if we simulate the system in two phases:\n", - "\n", - "1. During the first phase, force is constant, and we run until `theta` is 0.5 radians.\n", - "\n", - "2. During the second phase, force is 0, and we run until `omega` is 0.\n", - "\n", - "Then we can combine the results of the two phases into a single `TimeFrame`.\n", - "\n", - "Here's the slope function we'll use:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object \n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " theta, omega = state\n", - " unpack(system)\n", - " \n", - " torque = radius_disk * force - torque_friction\n", - " alpha = torque / I\n", - " \n", - " return omega, alpha " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As always, we'll test the slope function before running the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "slope_func(system1.init, 0*s, system1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's an event function that stops the simulation when `theta` reaches `theta_end`." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func1(state, t, system):\n", - " \"\"\"Stops when theta reaches theta_end.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object \n", - " \n", - " returns: difference from target\n", - " \"\"\"\n", - " theta, omega = state\n", - " unpack(system)\n", - " return theta - theta_end " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run the first phase." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "results1, details1 = run_ode_solver(system1, slope_func,\n", - " events=event_func1, max_step=0.1*s)\n", - "details1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And look at the results." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "results1.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Phase 2\n", - "\n", - "Before we run the second phase, we have to extract the final time and state of the first phase." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "t_0 = get_last_label(results1) * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And make an initial `State` object for Phase 2." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "theta, omega = get_last_value(results1)\n", - "init2 = State(theta=theta*radian, omega=omega*radian/s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And a new `System` object with `force=0`." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "system2 = System(system1, t_0=t_0, init=init2, force=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's an event function that stops when angular velocity is 0." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func2(state, t, system):\n", - " \"\"\"Stops when omega is 0.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object \n", - " \n", - " returns: omega\n", - " \"\"\"\n", - " theta, omega = state\n", - " return omega" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run the second phase." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "results2, details2 = run_ode_solver(system2, slope_func,\n", - " events=event_func2, max_step=0.1*s)\n", - "details2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And check the results." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "results2.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Pandas provides `combine_first`, which combines `results1` and `results2`." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "results = results1.combine_first(results2)\n", - "results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can plot `theta` for both phases." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_theta(results):\n", - " plot(results.theta, label='theta')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Angle (rad)')\n", - " \n", - "plot_theta(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And `omega`." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_omega(results):\n", - " plot(results.omega, label='omega', color='C1')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Angular velocity (rad/s)')\n", - " \n", - "plot_omega(results)" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "subplot(2, 1, 1)\n", - "plot_theta(results)\n", - "subplot(2, 1, 2)\n", - "plot_omega(results)\n", - "savefig('figs/chap25-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Estimating friction\n", - "\n", - "Let's take the code from the previous section and wrap it in a function." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "def run_two_phases(force, torque_friction, params):\n", - " \"\"\"Run both phases.\n", - " \n", - " force: force applied to the turntable\n", - " torque_friction: friction due to torque\n", - " params: Params object\n", - " \n", - " returns: TimeFrame of simulation results\n", - " \"\"\"\n", - " # put the specified parameters into the Params object\n", - " params = Params(params, force=force, torque_friction=torque_friction)\n", - "\n", - " # run phase 1\n", - " system1 = make_system(params)\n", - " results1, details1 = run_ode_solver(system1, slope_func, \n", - " events=event_func1, max_step=0.1*s)\n", - "\n", - " # get the final state from phase 1\n", - " t_0 = get_last_label(results1) * s\n", - " theta, omega = get_last_value(results1)\n", - " init2 = State(theta=theta, omega=omega)\n", - " \n", - " # run phase 2\n", - " system2 = System(system1, t_0=t_0, init=init2, force=0)\n", - " results2, details2 = run_ode_solver(system2, slope_func, \n", - " events=event_func2, max_step=0.1*s)\n", - " \n", - " # combine and return the results\n", - " results = results1.combine_first(results2)\n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's test it with the same parameters." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "force = 1*N\n", - "torque_friction = 0.2*N*m\n", - "results = run_two_phases(force, torque_friction, params)\n", - "results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And check the results." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "theta_final = get_last_value(results.theta)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To estimate the coefficient of friction, we'll use `fsolve`, which doesn't always work well with units.\n", - "\n", - "So for the rest of this example, we'll run without units. Here's a version of the `Params` object with no units." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "params_nodim = remove_units(params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the error function we'll use with `fsolve`. It takes a hypothetical value for `torque_friction` and returns the difference between `theta_final` and the observed result of the first push." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "def error_func1(torque_friction, params):\n", - " \"\"\"Error function for fsolve.\n", - " \n", - " torque_friction: hypothetical value\n", - " params: Params object\n", - " \n", - " returns: offset from target value\n", - " \"\"\"\n", - " force = 1\n", - " results = run_two_phases(force, torque_friction, params)\n", - " theta_final = get_last_value(results.theta)\n", - " print(torque_friction, theta_final)\n", - " return theta_final - 1.5" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing the error function." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "guess = 0.2\n", - "error_func1(guess, params_nodim)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And running `fsolve`." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "res = fsolve(error_func1, guess, params_nodim)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result is an array, so we'll extract the first (and only) element." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "torque_friction = res[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And do a test run with the estimated value." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "force = 1\n", - "results = run_two_phases(force, torque_friction, params_nodim)\n", - "theta_final = get_last_value(results.theta)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Looks good.\n", - "\n", - "### Exercises\n", - "\n", - "Now finish off the example by estimating the force that delivers the teapot to the desired position.\n", - "\n", - "Write an error function that takes `force` and `params` and returns the offset from the desired angle." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test the error function with `force=1`" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And run `fsolve` to find the desired force." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "force = res[0]\n", - "results = run_two_phases(force, torque_friction, params_nodim)\n", - "theta_final = get_last_value(results.theta)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Now suppose my friend pours 0.1 kg of tea and puts the teapot back on the turntable at distance 0.3 meters from the center. If I ask for the tea back, how much force should they apply, over an arc of 0.5 radians, to make the teapot glide to a stop back in front of me? You can assume that torque due to friction is proportional to the total mass of the teapot and the turntable." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [], - "source": [ - "subplot(2, 1, 1)\n", - "plot_theta(results)\n", - "subplot(2, 1, 2)\n", - "plot_omega(results)" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "**Exercise:** Run these simulations with different values for the weight and size of the turntable, or weight and location of the teapot. What effect do they have on the results?\n", - "\n", - "If you do some analysis, you might find that you can answer the original question without doing any simulation." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/data/USASAC2018-Report-30-2017-Activities-Page11.pdf b/code/data/USASAC2018-Report-30-2017-Activities-Page11.pdf deleted file mode 100644 index ac49bb752..000000000 Binary files a/code/data/USASAC2018-Report-30-2017-Activities-Page11.pdf and /dev/null differ diff --git a/code/data/USASAC2018-Report-30-2017-Activities.pdf b/code/data/USASAC2018-Report-30-2017-Activities.pdf deleted file mode 100644 index 7e589a635..000000000 Binary files a/code/data/USASAC2018-Report-30-2017-Activities.pdf and /dev/null differ diff --git a/code/data/USASAC_Report_2017_Page18.png b/code/data/USASAC_Report_2017_Page18.png deleted file mode 100644 index adac004ea..000000000 Binary files a/code/data/USASAC_Report_2017_Page18.png and /dev/null differ diff --git a/code/diagrams/RC_Divider.png b/code/diagrams/RC_Divider.png deleted file mode 100644 index 83c136163..000000000 Binary files a/code/diagrams/RC_Divider.png and /dev/null differ diff --git a/code/diagrams/RC_Divider.svg b/code/diagrams/RC_Divider.svg deleted file mode 100644 index 16834bdc9..000000000 --- a/code/diagrams/RC_Divider.svg +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - R - C - V - in - V - out - - diff --git a/code/diagrams/queue.png b/code/diagrams/queue.png deleted file mode 100644 index 25f5d200a..000000000 Binary files a/code/diagrams/queue.png and /dev/null differ diff --git a/code/diagrams/throwingaxe1.png b/code/diagrams/throwingaxe1.png deleted file mode 100644 index 12f81a3fc..000000000 Binary files a/code/diagrams/throwingaxe1.png and /dev/null differ diff --git a/code/diagrams/throwingaxe2.png b/code/diagrams/throwingaxe2.png deleted file mode 100644 index dd34730b9..000000000 Binary files a/code/diagrams/throwingaxe2.png and /dev/null differ diff --git a/code/examples/bigbelly.ipynb b/code/examples/bigbelly.ipynb deleted file mode 100644 index 26426d997..000000000 --- a/code/examples/bigbelly.ipynb +++ /dev/null @@ -1,1104 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim library\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Bigbelly" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "https://www.youtube.com/watch?v=frix_zTkPEs\n", - "\n", - "If the following import fails, open a terminal and run\n", - "\n", - "`conda install -c conda-forge pysolar`" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "from pysolar.solar import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "from datetime import datetime, timedelta" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "datetime.datetime(2018, 10, 29, 14, 13, 30, 886072)" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "dt = datetime.now()" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "datetime.datetime(2018, 10, 29, 14, 13, 30, 886072, tzinfo=)" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from pytz import timezone\n", - "\n", - "dt = pytz.timezone('EST').localize(dt)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "22.52017419635824" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "get_altitude(42.2931671, -71.263665, dt)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "781.1690621573555" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "latitude_deg = 42.3\n", - "longitude_deg = -71.3\n", - "altitude_deg = get_altitude(latitude_deg, longitude_deg, dt)\n", - "azimuth_deg = get_azimuth(latitude_deg, longitude_deg, dt)\n", - "radiation.get_radiation_direct(dt, altitude_deg)\n", - "\n", - "# result is in Watts per square meter" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "

\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
lat_deg42.3
lon_deg-71.3
\n", - "
" - ], - "text/plain": [ - "lat_deg 42.3\n", - "lon_deg -71.3\n", - "dtype: float64" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "location = State(lat_deg=42.3, lon_deg=-71.3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "datetime.datetime(2017, 9, 15, 12, 30, tzinfo=)" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "dt = datetime(year=2017, month=9, day=15, hour=12, minute=30)\n", - "dt = pytz.timezone('EST').localize(dt)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def compute_irradiance(location, dt):\n", - " degree = UNITS.degree\n", - " watt = UNITS.watt\n", - " meter = UNITS.meter\n", - " \n", - " sun = State(\n", - " altitude_deg = get_altitude(location.lat_deg, location.lon_deg, dt),\n", - " azimuth_deg = get_azimuth(location.lat_deg, location.lon_deg, dt)\n", - " )\n", - "\n", - " if sun.altitude_deg <= 0:\n", - " irradiance = 0\n", - " else:\n", - " irradiance = radiation.get_radiation_direct(dt, sun.altitude_deg)\n", - "\n", - " sun.set(irradiance = irradiance * watt / meter**2)\n", - " return sun" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
altitude_deg48.9295
azimuth_deg199.128
irradiance886.7325337613937 watt / meter ** 2
\n", - "
" - ], - "text/plain": [ - "altitude_deg 48.9295\n", - "azimuth_deg 199.128\n", - "irradiance 886.7325337613937 watt / meter ** 2\n", - "dtype: object" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "sun = compute_irradiance(location, dt)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
2017-09-15 00:15:00-05:000.000000
2017-09-15 00:30:00-05:000.000000
2017-09-15 00:45:00-05:000.000000
2017-09-15 01:00:00-05:000.000000
2017-09-15 01:15:00-05:000.000000
2017-09-15 01:30:00-05:000.000000
2017-09-15 01:45:00-05:000.000000
2017-09-15 02:00:00-05:000.000000
2017-09-15 02:15:00-05:000.000000
2017-09-15 02:30:00-05:000.000000
2017-09-15 02:45:00-05:000.000000
2017-09-15 03:00:00-05:000.000000
2017-09-15 03:15:00-05:000.000000
2017-09-15 03:30:00-05:000.000000
2017-09-15 03:45:00-05:000.000000
2017-09-15 04:00:00-05:000.000000
2017-09-15 04:15:00-05:000.000000
2017-09-15 04:30:00-05:000.000000
2017-09-15 04:45:00-05:000.000000
2017-09-15 05:00:00-05:000.000000
2017-09-15 05:15:00-05:000.000000
2017-09-15 05:30:00-05:000.000000
2017-09-15 05:45:00-05:0032.163770
2017-09-15 06:00:00-05:00171.524281
2017-09-15 06:15:00-05:00315.417455
2017-09-15 06:30:00-05:00430.773528
2017-09-15 06:45:00-05:00519.951046
2017-09-15 07:00:00-05:00589.344983
2017-09-15 07:15:00-05:00644.186729
2017-09-15 07:30:00-05:00688.226752
......
2017-09-15 16:45:00-05:00461.969588
2017-09-15 17:00:00-05:00355.515852
2017-09-15 17:15:00-05:00219.817019
2017-09-15 17:30:00-05:0069.743423
2017-09-15 17:45:00-05:000.218675
2017-09-15 18:00:00-05:000.000000
2017-09-15 18:15:00-05:000.000000
2017-09-15 18:30:00-05:000.000000
2017-09-15 18:45:00-05:000.000000
2017-09-15 19:00:00-05:000.000000
2017-09-15 19:15:00-05:000.000000
2017-09-15 19:30:00-05:000.000000
2017-09-15 19:45:00-05:000.000000
2017-09-15 20:00:00-05:000.000000
2017-09-15 20:15:00-05:000.000000
2017-09-15 20:30:00-05:000.000000
2017-09-15 20:45:00-05:000.000000
2017-09-15 21:00:00-05:000.000000
2017-09-15 21:15:00-05:000.000000
2017-09-15 21:30:00-05:000.000000
2017-09-15 21:45:00-05:000.000000
2017-09-15 22:00:00-05:000.000000
2017-09-15 22:15:00-05:000.000000
2017-09-15 22:30:00-05:000.000000
2017-09-15 22:45:00-05:000.000000
2017-09-15 23:00:00-05:000.000000
2017-09-15 23:15:00-05:000.000000
2017-09-15 23:30:00-05:000.000000
2017-09-15 23:45:00-05:000.000000
2017-09-16 00:00:00-05:000.000000
\n", - "

96 rows × 1 columns

\n", - "
" - ], - "text/plain": [ - "2017-09-15 00:15:00-05:00 0.000000\n", - "2017-09-15 00:30:00-05:00 0.000000\n", - "2017-09-15 00:45:00-05:00 0.000000\n", - "2017-09-15 01:00:00-05:00 0.000000\n", - "2017-09-15 01:15:00-05:00 0.000000\n", - "2017-09-15 01:30:00-05:00 0.000000\n", - "2017-09-15 01:45:00-05:00 0.000000\n", - "2017-09-15 02:00:00-05:00 0.000000\n", - "2017-09-15 02:15:00-05:00 0.000000\n", - "2017-09-15 02:30:00-05:00 0.000000\n", - "2017-09-15 02:45:00-05:00 0.000000\n", - "2017-09-15 03:00:00-05:00 0.000000\n", - "2017-09-15 03:15:00-05:00 0.000000\n", - "2017-09-15 03:30:00-05:00 0.000000\n", - "2017-09-15 03:45:00-05:00 0.000000\n", - "2017-09-15 04:00:00-05:00 0.000000\n", - "2017-09-15 04:15:00-05:00 0.000000\n", - "2017-09-15 04:30:00-05:00 0.000000\n", - "2017-09-15 04:45:00-05:00 0.000000\n", - "2017-09-15 05:00:00-05:00 0.000000\n", - "2017-09-15 05:15:00-05:00 0.000000\n", - "2017-09-15 05:30:00-05:00 0.000000\n", - "2017-09-15 05:45:00-05:00 32.163770\n", - "2017-09-15 06:00:00-05:00 171.524281\n", - "2017-09-15 06:15:00-05:00 315.417455\n", - "2017-09-15 06:30:00-05:00 430.773528\n", - "2017-09-15 06:45:00-05:00 519.951046\n", - "2017-09-15 07:00:00-05:00 589.344983\n", - "2017-09-15 07:15:00-05:00 644.186729\n", - "2017-09-15 07:30:00-05:00 688.226752\n", - " ... \n", - "2017-09-15 16:45:00-05:00 461.969588\n", - "2017-09-15 17:00:00-05:00 355.515852\n", - "2017-09-15 17:15:00-05:00 219.817019\n", - "2017-09-15 17:30:00-05:00 69.743423\n", - "2017-09-15 17:45:00-05:00 0.218675\n", - "2017-09-15 18:00:00-05:00 0.000000\n", - "2017-09-15 18:15:00-05:00 0.000000\n", - "2017-09-15 18:30:00-05:00 0.000000\n", - "2017-09-15 18:45:00-05:00 0.000000\n", - "2017-09-15 19:00:00-05:00 0.000000\n", - "2017-09-15 19:15:00-05:00 0.000000\n", - "2017-09-15 19:30:00-05:00 0.000000\n", - "2017-09-15 19:45:00-05:00 0.000000\n", - "2017-09-15 20:00:00-05:00 0.000000\n", - "2017-09-15 20:15:00-05:00 0.000000\n", - "2017-09-15 20:30:00-05:00 0.000000\n", - "2017-09-15 20:45:00-05:00 0.000000\n", - "2017-09-15 21:00:00-05:00 0.000000\n", - "2017-09-15 21:15:00-05:00 0.000000\n", - "2017-09-15 21:30:00-05:00 0.000000\n", - "2017-09-15 21:45:00-05:00 0.000000\n", - "2017-09-15 22:00:00-05:00 0.000000\n", - "2017-09-15 22:15:00-05:00 0.000000\n", - "2017-09-15 22:30:00-05:00 0.000000\n", - "2017-09-15 22:45:00-05:00 0.000000\n", - "2017-09-15 23:00:00-05:00 0.000000\n", - "2017-09-15 23:15:00-05:00 0.000000\n", - "2017-09-15 23:30:00-05:00 0.000000\n", - "2017-09-15 23:45:00-05:00 0.000000\n", - "2017-09-16 00:00:00-05:00 0.000000\n", - "Length: 96, dtype: float64" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "dt = datetime(year=2017, month=9, day=15)\n", - "dt = pytz.timezone('EST').localize(dt)\n", - "\n", - "delta_t = timedelta(minutes=15)\n", - "\n", - "result = TimeSeries()\n", - "for i in range(24 * 4):\n", - " dt += delta_t\n", - " sun = compute_irradiance(location, dt)\n", - " result[dt] = sun.irradiance.magnitude\n", - "\n", - "result" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "result.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "joule" - ], - "text/latex": [ - "$joule$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "cm = UNITS.centimeter\n", - "meter = UNITS.meter\n", - "watt = UNITS.watt\n", - "second = UNITS.second\n", - "joule = UNITS.joule" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "45 centimeter" - ], - "text/latex": [ - "$45 centimeter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "width = 45 * cm" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "2025 centimeter2" - ], - "text/latex": [ - "$2025 centimeter^{2}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "area = width**2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.0 centimeter2 watt/meter2" - ], - "text/latex": [ - "$0.0 \\frac{centimeter^{2} \\cdot watt}{meter^{2}}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "power = sun.irradiance * area" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.2025 meter2" - ], - "text/latex": [ - "$0.2025 meter^{2}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "area = area.to(meter**2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.0 watt" - ], - "text/latex": [ - "$0.0 watt$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "power = sun.irradiance * area" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.0 second watt" - ], - "text/latex": [ - "$0.0 second \\cdot watt$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "delta_t = 1 * second\n", - "energy = power * delta_t" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.0 joule" - ], - "text/latex": [ - "$0.0 joule$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "energy.to(joule)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/code/examples/world_pop_transition.ipynb b/code/examples/world_pop_transition.ipynb deleted file mode 100644 index 2c57f7984..000000000 --- a/code/examples/world_pop_transition.ipynb +++ /dev/null @@ -1,233 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Project 1 example\n", - "\n", - "Copyright 2018 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim library\n", - "from modsim import *" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pandas import read_html\n", - "\n", - "filename = 'data/World_population_estimates.html'\n", - "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", - "table2 = tables[2]\n", - "table2.columns = ['census', 'prb', 'un', 'maddison', \n", - " 'hyde', 'tanton', 'biraben', 'mj', \n", - " 'thomlinson', 'durand', 'clark']" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_results(census, un, timeseries, title):\n", - " \"\"\"Plot the estimates and the model.\n", - " \n", - " census: TimeSeries of population estimates\n", - " un: TimeSeries of population estimates\n", - " timeseries: TimeSeries of simulation results\n", - " title: string\n", - " \"\"\"\n", - " plot(census, ':', label='US Census')\n", - " plot(un, '--', label='UN DESA')\n", - " if len(timeseries):\n", - " plot(timeseries, color='gray', label='model')\n", - " \n", - " decorate(xlabel='Year', \n", - " ylabel='World population (billion)',\n", - " title=title)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "un = table2.un / 1e9\n", - "census = table2.census / 1e9\n", - "empty = TimeSeries()\n", - "plot_results(census, un, empty, 'World population estimates')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "half = get_first_value(census) / 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "init = State(young=half, old=half)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "system = System(birth_rate1 = 1/18,\n", - " mature_rate = 1/40,\n", - " death_rate = 1/40,\n", - " t_0 = 1950,\n", - " t_end = 2016,\n", - " init=init)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def update_func1(state, t, system):\n", - " births = system.birth_rate1 * state.young\n", - " \n", - " maturings = system.mature_rate * state.young\n", - " deaths = system.death_rate * state.old\n", - " \n", - " young = state.young + births - maturings\n", - " old = state.old + maturings - deaths\n", - " \n", - " return State(young=young, old=old)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "state = update_func1(init, system.t_0, system)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "state = update_func1(state, system.t_0, system)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Simulate the system using any update function.\n", - " \n", - " init: initial State object\n", - " system: System object\n", - " update_func: function that computes the population next year\n", - " \n", - " returns: TimeSeries\n", - " \"\"\"\n", - " results = TimeSeries()\n", - " \n", - " state = system.init\n", - " results[system.t_0] = state.young + state.old\n", - " \n", - " for t in linrange(system.t_0, system.t_end):\n", - " state = update_func(state, t, system)\n", - " results[t+1] = state.young + state.old\n", - " \n", - " return results" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "results = run_simulation(system, update_func1);" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot_results(census, un, results, 'World population estimates')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/examples/world_pop_transition2.ipynb b/code/examples/world_pop_transition2.ipynb deleted file mode 100644 index 0efe7220d..000000000 --- a/code/examples/world_pop_transition2.ipynb +++ /dev/null @@ -1,580 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Project 1 example\n", - "\n", - "Copyright 2018 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim library\n", - "from modsim import *" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "from pandas import read_html\n", - "\n", - "filename = '../data/World_population_estimates.html'\n", - "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", - "table2 = tables[2]\n", - "table2.columns = ['census', 'prb', 'un', 'maddison', \n", - " 'hyde', 'tanton', 'biraben', 'mj', \n", - " 'thomlinson', 'durand', 'clark']" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_results(census, un, timeseries, title):\n", - " \"\"\"Plot the estimates and the model.\n", - " \n", - " census: TimeSeries of population estimates\n", - " un: TimeSeries of population estimates\n", - " timeseries: TimeSeries of simulation results\n", - " title: string\n", - " \"\"\"\n", - " plot(census, ':', label='US Census')\n", - " plot(un, '--', label='UN DESA')\n", - " if len(timeseries):\n", - " plot(timeseries, color='gray', label='model')\n", - " \n", - " decorate(xlabel='Year', \n", - " ylabel='World population (billion)',\n", - " title=title)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "un = table2.un / 1e9\n", - "census = table2.census / 1e9\n", - "empty = TimeSeries()\n", - "plot_results(census, un, empty, 'World population estimates')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Why is world population growing linearly?\n", - "\n", - "Since 1970, world population has been growing approximately linearly, as shown in the previous figure. During this time, death and birth rates have decreased in most regions, but it is hard to imagine a mechanism that would cause them to decrease in a way that yields constant net growth year after year. So why is world population growing linearly?\n", - "\n", - "To explore this question, we will look for a model that reproduces linear growth, and identify the essential features that yield this behavior.\n", - "\n", - "Specifically, we'll add two new features to the model:\n", - "\n", - "1. Age: The current model does not account for age; we will extend the model by including two age groups, young and old, roughly corresponding to people younger or older than 40.\n", - "\n", - "2. The demographic transition: Birth rates have decreased substantially since 1970. We model this transition with an abrupt change in 1970 from an initial high level to a lower level." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll use the 1950 world population from the US Census as an initial condition, assuming that half the population is young and half old." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1.278814327" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "half = get_first_value(census) / 2" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
young1.278814
old1.278814
\n", - "
" - ], - "text/plain": [ - "young 1.278814\n", - "old 1.278814\n", - "dtype: float64" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "init = State(young=half, old=half)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll use a `System` object to store the parameters of the model." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
birth_rate10.0555556
birth_rate20.04
transition_year1970
mature_rate0.025
death_rate0.025
t_01950
t_end2016
inityoung 1.278814\n", - "old 1.278814\n", - "dtype: flo...
\n", - "
" - ], - "text/plain": [ - "birth_rate1 0.0555556\n", - "birth_rate2 0.04\n", - "transition_year 1970\n", - "mature_rate 0.025\n", - "death_rate 0.025\n", - "t_0 1950\n", - "t_end 2016\n", - "init young 1.278814\n", - "old 1.278814\n", - "dtype: flo...\n", - "dtype: object" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = System(birth_rate1 = 1/18,\n", - " birth_rate2 = 1/25,\n", - " transition_year = 1970,\n", - " mature_rate = 1/40,\n", - " death_rate = 1/40,\n", - " t_0 = 1950,\n", - " t_end = 2016,\n", - " init=init)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's an update function that computes the state of the system during the next year, given the current state and time." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def update_func1(state, t, system):\n", - " if t <= system.transition_year:\n", - " births = system.birth_rate1 * state.young\n", - " else: \n", - " births = system.birth_rate2 * state.young\n", - " \n", - " maturings = system.mature_rate * state.young\n", - " deaths = system.death_rate * state.old\n", - " \n", - " young = state.young + births - maturings\n", - " old = state.old + maturings - deaths\n", - " \n", - " return State(young=young, old=old)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll test the update function with the initial condition." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
young1.317889
old1.278814
\n", - "
" - ], - "text/plain": [ - "young 1.317889\n", - "old 1.278814\n", - "dtype: float64" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "state = update_func1(init, system.t_0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we can do one more update using the state we just computed:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
young1.358158
old1.279791
\n", - "
" - ], - "text/plain": [ - "young 1.358158\n", - "old 1.279791\n", - "dtype: float64" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "state = update_func1(state, system.t_0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `run_simulation` function is similar to the one in the book; it returns a time series of total population." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Simulate the system using any update function.\n", - " \n", - " init: initial State object\n", - " system: System object\n", - " update_func: function that computes the population next year\n", - " \n", - " returns: TimeSeries\n", - " \"\"\"\n", - " results = TimeSeries()\n", - " \n", - " state = system.init\n", - " results[system.t_0] = state.young + state.old\n", - " \n", - " for t in linrange(system.t_0, system.t_end):\n", - " state = update_func(state, t, system)\n", - " results[t+1] = state.young + state.old\n", - " \n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run the simulation and plot the results:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "results = run_simulation(system, update_func1);" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzs3XdcneXd+PHPWRz2HiFhB7ggIWTHGI0jUWutq6nWuvpYbWutoz9Hax3tY4ezVn3aOrps9VGrT62ztq64G0dMzAJykQAhgRBGIIRxOHDOuX9/3AcCBMghYYbv+/XKK3Dd63tu4HzPdV/LYhgGQgghxERjHe8AhBBCiIFIghJCCDEhSYISQggxIUmCEkIIMSFJghJCCDEhSYISQggxIUmCEuNCKfWeUupPh9jnDqXU9rGKaSQopQyl1CUjcJ5J99r7C+RnLMRQ7OMdgBhfSqkrgMeAWK11S6/yTUD+IOVfaK3/a8yDPQoppY4HPgQytdY7em26H/jduAQ1TEqp24Fva60z+m1aBXjG4PopwC7gZK31e6N9PTF2pAYl3sb8oHJid4FSKh6YDdQMUF4ArD7ciymlgg470ilEa92qtW4Y7ziOhNa6UWu9f7zjEJOX1KCmOK11pVKqDFgJ/NNfvALYAnwwQLmFXglKKfVfwM3ATKAe+Ctwh9ba49/+HlAG7Aa+jfk7l9A/DqWUE3gIuAjwAc8C+w4Vv1LKAP4fcAJwOtAM3K+1fqDXPsnAg/7tTuBT4Cat9ef+7ScB7wJnAz8BCv0x/z+t9Vv99knVWlf1OrcHs/bw10Hi+wHwLSAbaAXeA67XWtcopTIwa08AFUopgPe11icppe4ALtFaZ/c6VyD3ejtQCVwNBAGvAFdrrduGuIfhwJ3AeUAMoIFfaK1f6LXPrZg/vxnAfmA9cC5wAfAL/z7d09L8TGt9R3c8Wutv94qvDPODz3f98T2Mec9v98dsBf6gtb6t17UvAn4A5AFdmD+/67XWpf5ddvn/f9d/Dyu7a3NKqVOBO4AFQCPwJubPfq9/+2zgAeAYwAHsBO7SWv/vYPdLjB2pQQkwE87KXt+vAN7x/+tfvlVrXQ2glPoK8Djwv8Ac4EbMN5n/7nf+r2MmpZX+cwzkHuBrwDeBY4E2/7kC8d+Yb/zzgXuB+5RSq/wxWoCXMN/czgSWALXAW/4aYW8PAD/3n+cT4BWl1IwAYxjKTZj356tAGmbyBfON9Rz/10uAZMzHYgcZxr0+D4gFTsJM9ucCPxosMP/9eRWYi5lsCoBHgWeVUiv9+6wCfoyZJHKAU4F/+0/xHOY9r/LHn4z5eHIw52EmguOBG4BbMT8AhQPLMe/VrUqpL/c6xomZBBf4r+0FXutVG1/g//9r/usv9se9AngZ834X+u9FBvCi/3UD/A3YCyzDvK83AE1DxC/GkNSgBJgJ6jtKqUStdR1mErkJ+AiY1a/8jV7H/Rj4h9b6bv/3pUqpacA9SqlfaK07/eU1wPe11r6BLq6UCgOuAq7VWr/sL77JX2uJDiD+17TWv+0VwzGYbzQv+GNeAszWWhf7r/dNYAfwfcyE1O0erfU//ftcCZzij+v2AGIYkNb6f3p9W6GUuhpYr5SaobWuVko1+rfVa633DHGqQO/1Tq319f6vtyqlngVO4+BE1u1EzA8ESVrrZn/ZH5RSS4FrMX830oE9wOta6y7MWsaG7hMopVoB7yHi77kHWuube72GGzFrpWf0KrsB88PMvwG01n/pfQKl1GWYSWUx8B/M2iRAY78Yfgr8ptfvRncttBIzIW/wv7YHun83gPIAXoMYI5KgBJg1JYCVSqkPgSzMR03N/k4R3eU5wA97HTcb8xN0b+8DwZiPoUr8ZesGS05+MzE/Ja/pV/4RZq3nUD7u9/1/MB/ndce4t9cbEFprt1LqU/+2Ac+jtfYopT4DZgVw/UH5k+wt/vNEc+CpRTpQPYxTBXqvN/TbpxozQQ1mMeajtmr/47FuQcA2/9f/B1wHVCql3sRMWi/17jwzDBv7fb/H/69/WWL3N0qpeZgJdh4Qj/mYGcx7+J8hrrUYWKqUumaAbTmY9+p+4E/+pPce8IrWen0gL0SMPnnEJ/A3xm/E/NS6Eljf69P0u73KvZh/xL31nw7fMkD5oO0fQxxzJCz9vh/ovJYArtf7PL7+ZUopG0P8DSml0oB/YdbWvgEswmznAjMBDFcg97qz3z7GUDH6tzVjvvn3/jcL+DKA/5FuHnA5UIfZZqSVUqnDfwl0DRDfQGVWAKVUKGa7keG//hLMxGNw6HtoxXz82P+15XCgdvYLIBczCRcAnyilfnkYr0uMAqlBiW6rMZ/hOzhQowIzQf3WX/55r8QFUIT5iOjhXmUnAC6G96hkO+Yb63FAca/yZQEevxR4pNf3x3KgRlEExCulZvV6xOfEfKN7hL6Wdl9fKWXHfCN8yr+tzv//dA40ys/j4GTY22IgBLOzhct/3oX99ulOKLYhztP9OkbiXvf3OWbNLlhrvWWwnbTWbuB14HWl1E8w2/HOxfzd6OTQ8R+ufMz2y9u01iUASqll9L3vg93DzzEf7Q45nkxrXY75u/CIUurHmE8JDvuxrhg5kqBEt9WYDe9xmA3Z3T4AUjEb+H/b75i7gVf9f9QvYL5h3wH8ulebyCFprduUUo8Bv1RK1WL2IrsC81N73ZAHm870P8Z5A/PR3gWYNRYwk+1nwDP+9p9mzBpAMGZngN5+rJTaA1RgtmEl9dqnu3fcHUqp6zEfNd3F0LWwbf7tNyqlnsZs9/hpv30qMWtnZyilngPc/T4EdBuRez2AdzCHGryglLoZsyYdg/nhoENr/Uf/WDkr5n3ch1mbjuDAh4kKYJpS6lj/a27XWrcfQUy9VQJu4Fql1K8xOzncQ9/73oDZQ/I0pVQR5j1swrzXbyqlHgSeAFowa0/nA9dgJrR7gX/4X0M05u9P7w9JYhzJIz7R7QPMRy3BmG0/APjHsazDfEN6u/cBWut/YT52+S/MbukPYn4S/dlhXP/HmL3t/hfzjTCavrWFofwcs0PDRsxeYbdorZ/3x2hgftLfCrwGrAWmAacOMM7oJszeYhswa3PndHcp93flvgCzbeQLf2y3ceDR30G01pswOxpcifmmdxNml/je+9RitlH9GLMzycsMYITvde/zGpiPHV/A7MXYfZ++gtklHMxebd/CfLxbgpm8v6u17h5u8BLwd/9x9QzRa/Aw4msALsHsvVeE2WZ0E73uu79982rM3qK7MH8+aK3fxewkMwezO/8mzPvWgvm77sFMxn/2v643MGuGF41U/OLIWGRFXTGZ+cfeXKq1fuqQOw9+jpMYYIyTEGJ8SQ1KCCHEhCQJSgghxIQkj/iEEEJMSJOmF5+/a/BizIZk7ziHI4QQYnhsmFNRrfUPWzikSZOgMJPTh4fcSwghxES2nF49hYcymRJUDcDTTz/NtGnTxjsWIYQQw7Bnzx4uvvhi8L+XB2IyJSgvwLRp00hJSRnvWIQQQhyegJtopBefEEKICUkSlBBCiAlJEpQQQogJSRKUEEKICWkydZIYks/no6qqira2Qy09JCYDh8NBYmIikZGR4x2KEGKcHDUJqqGhAYvFglIKq1UqhpOZYRi4XC6qq80FZyVJCTF+3G43lZWVhISEkJKSgsUy1BJoI+uoSVD79u0jIyNDktNRwGKxEBoayowZM9i9e7ckKCHGyb59+1i7di0dHR2A+WExKipqzK5/1CQor9eLw+EY7zDECAoJCaGrq/9q4EKIsVBdXc3GjRvxes1hSzabjaCgoDGN4ahJUMCYVj3F6JOfpxBjzzAMtNZs27atp8zhcLBw4UJCQkLGNJajKkEJIYQ4fB6Phy+++II9e/b0lIWHh7N48WLCw8PHPB5JUEIIIWhra2Pt2rW0tLT0lCUmJrJgwYJxaz6RHgXjYMWKFbzwwgsHlV966aX89re/7fn+gw8+4IILLmD+/PksWrSIVatW8fe//33Ic7e1tfHrX/+aU089lTlz5nDyySdzww03sHXr1hF/HUKIo0N9fT0ffvhhn+Q0c+ZMlixZMq5t+8OqQSmlQoAQrXXjKMUj/LTWXH311VxzzTXce++9eDwetmzZQmPj4Lfe5XJx6aWX0tnZyc0330x+fj4tLS28/fbbPPTQQzz22GNj+AqEEBOdYRhUVFRQXFxM9+K1VquVuXPnTohJuQ+ZoJRSZwCXACcDif4yL/AF8BrwZ6119WgGORV9/PHHZGdnc+WVV/aUZWdnD3nMn//8ZyoqKnjrrbeIj4/vKc/Ly2P//v093xcVFXHPPfewceNG4uPjWbVqFVdddRU2mw0ApRT33nsvL7zwAhs3biQ7O5u7776b3NzcnuN/+ctfUlJSgsPhQCnFI488QmRkJJdeeilLlizh2muv7bneihUruOaaa1i1ahVut5tf/OIXrF69mra2NqZPn85NN93EKaecMiL3TQgRGK/Xy6ZNm6iqquopCw4OZvHixURHR49jZAcM+ohPKXWGUmor8ATQCfwcOBc4HbgUeAs4DShXSv1OKRU7BvFOGXFxcezcuZPi4uKAj3njjTc4++yz+ySnbt1jiZqamrj88ss56aSTePXVV7n77rt55ZVXeOKJJ/rs/7vf/Y7LLruMl156idjYWG677baebT/84Q+ZN28er776Ks888wxnnXVWwDE++eSTbNmyhd///ve89tpr3HLLLYSFhQV8vBDiyLlcLtasWdMnOcXExLB8+fIJk5xg6BrUr4CfAC9qrT0DbH8OQCmVBdwIfBe4Z8QjPAKfFe3hs2KzN8qSWdNYMrvvQocfbaxmQ2k9AMcVTme+Suyz/d11uygq3wvAyQtTmZ0V12f7G59Usm1X04DnPlJf/vKXeeutt/jqV79KSkoKCxcu5JRTTuG0004b9JjKykq+9rWvDXnep59+mmXLlnHFFVcAkJ6ezrXXXsvDDz/M5Zdf3rPfxRdfzIoVKwC46qqruPDCC+no6CA4OJiamhpWrlxJamoqADk5OQG/rj179jB79mwKCwsBes4hhBgbe/fuZd26dbjdB1ZdT0tLY86cORNuooOhEtQcrbXvUCfQWpcDVyulZNDKCLLb7fzmN7+hsrKSNWvW8Nlnn3H99ddzxhln8Ktf/eqwz1taWso777zD/Pnze8q8Xi8+X98fdffjPICEhAQAGhsbmT59OpdccglXXHEFxx13HMcddxxnnHEGMTExAV3/nHPO4Vvf+hYlJSUcf/zxnH766cyaNeuwX48QIjCGYbBjxw6Kiop62pssFgsFBQWkp6cD0FGlCUpIxeoMHc9QewyaoAJJTv32N448nKkhLCyM1tbWg8pbWloOGmuQnp5Oeno6F154If/85z+58cYbue666waseaSnp1NRUTHktdvb2znrrLP43ve+N+R+dvuBX43uAbPdSezGG2/krLPO4r333uPll1/mN7/5Dc8//zypqalYLJaeX/5uvWeDKCwsZPXq1bz33nt8+OGHfOMb3+CGG27gsssuGzIeIcThG6i9yel0snDhQuLi4jC8XbRu+ZCO6lIccdOJWnImFsv416YC7sWnlJoJrACS6Nd2pbX++QjHNSKWzB760dvxc2dw/NwZg24/eWEqJy8c/BHUl5am86Wl6cOOKz09/aC2pdbWViorK8nIyBj0uKysLMBMMgPG86Uv8fjjj3PdddcRF9f3cWRLSwsRERHk5eXx2Wef9XxiOly5ubnk5uby3e9+l6985Su8/fbbfOtb3yI2NpaGhoae/RobG/t8DxAdHc25557Lueeeyx//+Ef+8Y9/SIISYpS0t7fz+eef09zc3FMWHR3NokWLCAkJwdvWzP71b+JpMZszuvbupq1iM01hWaQmRYxX2ECACUopdQnwF6ADqAV6f0Q2MDtQiABdcsklfPvb32b27NksX76c1tZWHn30UZKSkjj++OMBeOaZZ9ixYwcrV65kxowZ1NbW8utf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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_results(census, un, results, 'World population estimates')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This figure shows the results from our model along with world population estimates from the United Nations Department of Economic and Social Affairs (UN DESA) and the US Census Bureau.\n", - "\n", - "We adjusted the parameters by hand to fit the data as well as possible. Overall, the model fits the data well.\n", - "\n", - "Nevertheless, between 1970 and 2016 there is clear curvature in the model that does not appear in the data, and in the most recent years it looks like the model is diverging from the data.\n", - "\n", - "In particular, the model would predict accelerating growth in the near future, which does not seem consistent with the trend in the data, and it contradicts predictions by experts.\n", - "\n", - "It seems that this model does not explain why world population is growing linear. We conclude that adding two age groups to the model is not sufficient to produce linear growth. Modeling the demographic transition with an abrupt change in birth rate is not sufficient either.\n", - "\n", - "In future work, we might explore whether a gradual change in birth rate would work better, possibly using a logistic function. We also might explore the behavior of the model with more than two age groups." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/figs/README b/code/figs/README deleted file mode 100644 index c6b3d34bb..000000000 --- a/code/figs/README +++ /dev/null @@ -1 +0,0 @@ -This is the directory where saved figs go when you run the notebooks. \ No newline at end of file diff --git a/code/hopper.ipynb b/code/hopper.ipynb deleted file mode 100644 index b4d8bd83c..000000000 --- a/code/hopper.ipynb +++ /dev/null @@ -1,1342 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Case study: Hopper optimization\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# If you want the figures to appear in the notebook, \n", - "# and you want to interact with them, use\n", - "# %matplotlib notebook\n", - "\n", - "# If you want the figures to appear in the notebook, \n", - "# and you don't want to interact with them, use\n", - "# %matplotlib inline\n", - "\n", - "# If you want the figures to appear in separate windows, use\n", - "# %matplotlib qt5\n", - "\n", - "# tempo switch from one to another, you have to select Kernel->Restart\n", - "\n", - "%matplotlib inline\n", - "\n", - "from modsim import *" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "kg = UNITS.kilogram\n", - "m = UNITS.meter\n", - "s = UNITS.second\n", - "N = UNITS.newton" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 371, - "metadata": {}, - "outputs": [], - "source": [ - "condition = Condition(mass = 0.03 * kg,\n", - " fraction = 1 / 3,\n", - " k = 9810.0 * N / m,\n", - " duration = 0.3 * s,\n", - " L = 0.05 * m,\n", - " d = 0.005 * m,\n", - " v1 = 0 * m / s,\n", - " v2 = 0 * m / s,\n", - " g = 9.8 * m / s**2)" - ] - }, - { - "cell_type": "code", - "execution_count": 384, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "condition = Condition(mass = 0.03,\n", - " fraction = 1 / 3,\n", - " k = 9810.0,\n", - " duration = 0.3,\n", - " L = 0.05,\n", - " d = 0.005,\n", - " v1 = 0,\n", - " v2 = 0,\n", - " g = 9.8)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 398, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(condition):\n", - " \"\"\"Make a system object.\n", - " \n", - " condition: Condition with \n", - " \n", - " returns: System with init\n", - " \"\"\"\n", - " unpack(condition)\n", - " \n", - " x1 = L - d # upper mass\n", - " x2 = 0 # lower mass \n", - " \n", - " init = State(x1=x1, x2=x2, v1=v1, v2=v2)\n", - " \n", - " m1, m2 = fraction*mass, (1-fraction)*mass\n", - " ts = linspace(0, duration, 1001)\n", - " \n", - " return System(init=init, m1=m1, m2=m2, k=k, L=L, ts=ts)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `make_system`" - ] - }, - { - "cell_type": "code", - "execution_count": 399, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
initx1 0.045\n", - "x2 0.000\n", - "v1 0.000\n", - "v2 0.00...
m10.01
m20.02
k9810
L0.05
ts[0.0, 0.0003, 0.0006, 0.0009, 0.0012, 0.0015, ...
\n", - "
" - ], - "text/plain": [ - "init x1 0.045\n", - "x2 0.000\n", - "v1 0.000\n", - "v2 0.00...\n", - "m1 0.01\n", - "m2 0.02\n", - "k 9810\n", - "L 0.05\n", - "ts [0.0, 0.0003, 0.0006, 0.0009, 0.0012, 0.0015, ...\n", - "dtype: object" - ] - }, - "execution_count": 399, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(condition)\n", - "system" - ] - }, - { - "cell_type": "code", - "execution_count": 400, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
x10.045
x20.000
v10.000
v20.000
\n", - "
" - ], - "text/plain": [ - "x1 0.045\n", - "x2 0.000\n", - "v1 0.000\n", - "v2 0.000\n", - "dtype: float64" - ] - }, - "execution_count": 400, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.init" - ] - }, - { - "cell_type": "code", - "execution_count": 401, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object with theta, y, r\n", - " t: time\n", - " system: System object with r, k\n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " x1, x2, v1, v2 = state\n", - " unpack(system)\n", - " \n", - " dx = x1 - x2\n", - " f_spring = k * (L - dx)\n", - " \n", - " a1 = f_spring/m1 - g\n", - " a2 = -f_spring/m2 - g\n", - " \n", - " if t < 0.003 and a2 < 0:\n", - " a2 = 0\n", - " \n", - " return v1, v2, a1, a2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `slope_func`" - ] - }, - { - "cell_type": "code", - "execution_count": 402, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(0.0, 0.0, 4895.199999999998, 0)" - ] - }, - "execution_count": 402, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 403, - "metadata": {}, - "outputs": [], - "source": [ - "run_odeint(system, slope_func)" - ] - }, - { - "cell_type": "code", - "execution_count": 404, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
x1x2v1v2
0.29880.1086550.055882-3.678405-0.058611
0.29910.1074460.055916-4.3217060.258630
0.29940.1061020.056017-4.5650520.375893
0.29970.1047500.056120-4.3769550.277434
0.30000.1035170.056163-3.782438-0.024235
\n", - "
" - ], - "text/plain": [ - " x1 x2 v1 v2\n", - "0.2988 0.108655 0.055882 -3.678405 -0.058611\n", - "0.2991 0.107446 0.055916 -4.321706 0.258630\n", - "0.2994 0.106102 0.056017 -4.565052 0.375893\n", - "0.2997 0.104750 0.056120 -4.376955 0.277434\n", - "0.3000 0.103517 0.056163 -3.782438 -0.024235" - ] - }, - "execution_count": 404, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.results.tail()" - ] - }, - { - "cell_type": "code", - "execution_count": 405, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAfcAAAFOCAYAAACbsFSaAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XtwVOd5P/Dv2dVetbva1a6k1Q0hxB1JGHN1wI4NSUts\npz8TGqOf3cT2L41DMqXjcZMM43ESD20ybdpOkpJpmwBt7DouqZvGNnYwbrDjOGBjx4AwAsxF97u0\nd0l73/P7Y7VH52gXJIFuK30/M5lozzm7exaDnn3f93mfRxBFUQQRERHNGaqZvgEiIiKaXAzuRERE\ncwyDOxER0RzD4E5ERDTHMLgTERHNMTkzfQPjFQqFcP78eRQUFECtVs/07RAREU25eDyOvr4+VFdX\nQ6/Xj/t5WRPcz58/j4cffnimb4OIiGja/fznP8e6devGfX3WBPeCggIAyQ/odDpn+G6IiIimXnd3\nNx5++GEpBo5X1gT31FS80+lEWVnZDN8NERHR9JnocjQT6oiIiOaYrBm5E1H2i8bi+LjFA7VKhWUV\nNqhUguLc26c70OcZwprlhVheka94bkffAM5d6YM9z4C1K4qglj0XAERRhChC8ZpE8xWDOxFNqpYu\nP5q7/CgrNKGqzCodF0URR082o7UnAADo8w7hrjUjS2zvne/GpRY3AOD4B22wWwwosBkAAL6BMF59\npxHReALXOnwQBGD9ypHcm86+ARx9txnRWAKfXFOGFZXKLwZE8w2n5YlowgaCUXgCIYzuO9XtGsSr\nJ5rw0bV+HH23GW3DgRwAulyDUmAHgPPXXBgKRQEAsXgCF5vd0jlRFNHQ2C89vtDkRjSekB7XX+lH\nIpF870RCxPE/tCEYjiEWT+DtM+0YDEYVr/V+QzdeOHYJ7zd0p90z0VzE4E5EE9LY4cNzr13Az1+/\nhN+d6VCcO3O5TxE8z1zulX6+1u5TXJsQRTR1+gEAPe4hRKJxxfm23gHp56ZO5XNDkRi6XYMAkl8a\nfANh6VwsnsDVNq/0+FKzB+9f6IbbH8L7F7pxudWT9pkGgtG09yfKZgzuRJQmnhDR0u1XBE0gOUp+\n+3Q7EsMB/KNr/ej3BgEkg2prl19xfVvPAEKRGACgXTZqT+nsSwbwrv7BtHO+gTD8gxEMhaJw+0Np\n59uHn9s86j0BoKVn5Nh52QwAkJwxkPvDxR48+9oF/PuRBrR0p78WUTZicCcihURCxCu/u4Yj7zTi\nhWOXFFPrHX0DGAxFFdenRsK9niHF1DmQnBLv6E0GeFeGAN3RN5C8pm8g7RwAdPQOZAz8ANDZNzj8\n/+nP7XENQRRFhKNx9HqCinPdslmCoVBUmqqPxhP47Yft0nQ/UTZjcCcihWsdXinYxhMifn+2Q5pq\nb+9ND6Qt3cng390/lPH12noC6HGPnHNYDdBqknt2k2v3YWmKHQCqF9mlnzv7B9ApC+7LFtikn3tc\ngwhH4+iTBe9Upnw4GofbH0Ln8JcHOVEU0TX8fm09AWkWAgACQxHpHJAM/r/67VX87NUGNDQqR/xE\nsxmDO9E8FI0lcPyDVjz36wv46Kpy2lq+Xg0ALn8I3uHp+UyjaJcviKFQFN3uzEG4tSeAHtdIcC9x\n5KLEkSs9PnelD9FYcsRvMmiwtGLkuV2uQcXIfOkCGyy52uRniCdw/lq/FJztFj0qS/Kkazv7BjN+\nGUmdA4DW7vSlAvkswjtnO9HRN4CBYBRvfdim+CJBNJsxuBPNQ3+42I2LzW74ByN4+0w7OvuTAU0U\nRcVIOaWtJ4B4QkSvZyRIp4IskAyI8sB/29JCaHKSv178gxFpixsAOO25KHGYpMfnZSPi0gITCm1G\naQTuDYTRN7ymLwgCih25KLaPfDH44EKP9HNxgQklsnNdrkFFoF4p2x7X1T8IURQV2fspqS8ToXAM\n1zqUX3QuNClH7/GECJcvyGQ8mnUY3InmmURCREOjW3Hso6vJoOUfjCAYjqU9p607gH5vELHhNXVL\nrlYxOm9odEnP02nVcFj1igDuH4xIP5cUmFBSMBKE5YoduchRq1BoM6adKxiezi8pGHndmGyNv6zQ\nhGLZjEBTp09K9lMJAm5fViSd6/UMocc9lPGzdruGEI8n0NYbSFt/lyfcpXIT/vONj/HsaxcyJv0R\nzRQGd6I5qqt/ED9//RJ+/vol9MrWvHs9Q1IGe0pbT0CxFg0AVpNO+rmjXzkKdtpzUVZklh7Lp79L\n7LkQBAELZOel1zTrYDJoUGA1QKNO//VTOhy4S2UBfPQ5+ZR+iiAIKCswwWE1SDMGqal+ACjMN8Jq\n1kmzDbF4Aqc/Htmmt0w23R+LJ9DvC6Ejw5S+fzCCgeE99Jda3NKfSTgax8lznWnXE80UBneiOSga\nS+DXJ5vgCYTgCYTw+nvN0ig00zp0KBJDnzeIbnnyWoUNJoMGABCJxnH+2sjafLE9F0X5xrQSsEBy\nehwAyp3pwb2sMHlMrVZhQbFFcc5q1sFqTn6hWFSal/bcyhKLdJ1Rr1Gcc+TpodflQKUSUJSfHvxT\nXwzk5xo7RvbOL3CaFed63Ndfr08l/33cotwv39odQCjDTADRTGBwJ5qDmjp9iiln/2AEbb3J9WV5\n5rpce88AumXnnPZcxQhaPrWemj4vdqSPsFMja9vwKF0uFaABoKbKrjhXu9gBQUh+WSi0GVBWOPLa\nRflGacpdEAQskyXdAVDUoV+Q4UtF6n2LHenT/QBQXmSGM3/k3LV2n5REmKNW4balI+02u12DiMbi\nacmFiVFb+rr6B/H86xfxs1e5f56mH4M7URZz+0M483GvYisZALRmCCZNHT6IoqgI7muWFko/N3b6\n4PIl140FQUBRvlGxvp2i1aiRb9EDSA+kJoNGWi8XBAG3Lx95/bJCs2KqvqzQjLtvL0OJw4S1y4tQ\nvcghnRMEAdvvWIjViwuwapEd922ulAI/AKxdXgjncPLcwmILqmVfFJaWWxUzCnaLHkXDgTs1cyBX\naDPCqNegyD4S3OVButih/JLT7RpCZ/+gYguddG74zzaREHH8g1Z4A2EMBKN441QLk+5oWrFxDFGW\n8g9G8OLxy4jGElAJAnbcvVga3WbKeG/u8mNdMCrVc9fkqFBdZZdKxMq/IDjy9NBq1BlHwaWOXCmb\nfWVlPs5d6ZPWoW9fXqjoyla7uAA2sx7BcAyLSvMUARoAqqscqK5yIBO9Ngd3rim97rmd9yxGMBxL\nm6I3GbW4+/ZyvH2mHXqtGvesK5fe12bWwWbWwxMYSX5bXJ5sblNgNSBHrVIk6QFAeaFZ+nIAAH2e\nIcUWOrNRi8BQclYjteWvyzUojfwBIByJo7HTl9bpjmiqcOROlKXqZfvDE6KIDy50A0gWXklNoasE\nQUpcGwhGFYVYCm1G5Jl0yJMlzqUUDY+KzUZtWnLbElmWvF6bg8/dswTrVhTh0xsWoCZDoC4vMmPp\nAhtyMiTQ3QpBENICe8qKynx8ZUcNHrlvpTTCTz1nw6qRrHmTQSNtkVOrVRmT9cqKTDDqNVKCYTwh\nov5Kn3R+rWx2otczhHhCzLh/vm3UsXg8gT5PkBXxaEpw5E6UpUaXXW3rHUAwHFOUWy3MN8Js1ODK\ncGGaDy6O7AtPBb3yQlNaDXn59Pnm1SX41W+vIhpLoLzIjCXlVsW1llwtNlUXT86HmkSjZwlSlpTb\noNfmwOULSj+nVJVZFXvfLblaFFiTbWeddqNiNA4kvzwtXWDDh5d6ERiKIBZPwOULKkr2psjzGYZC\nUfzyravwDYRRYDVgx92Lpap9RJOBI3eiWUwURXzc4sbZy73SdDqQzF7v94XSrm3rCSi2vRXlG1Ex\nKis9JbXXXD4SB5Jr6uVFI6P1QpsRj9y3EnWfXobPbll03aCZTcqLzLhtaSFyRyX8LV1gk/IJAGBT\ndbH0eeUzACnFjlxoNWo4Zev1zV1+qfCOnG8gLP03fL+hW/pC1ecN4tyoKoFEt4ojd6JZ7MNLvXjv\nfBcA4GKTGw9+ehnUKgE97qGMfclbuwOKxi5F+UaUF5khCILiekEQpEpvJY5cVDgtUkb3xlVOaHKU\no0i9Nkcxwp2rNDkqfO7uxbjS7oUjz6AoilOeYd9+hTP5xako3yjNjvzhYo/0Z536M07VD+jqH0Rl\nSR6utCsr311p9WDdiiIQTZZxjdyDwSCeeeYZbN26FWvXrsWuXbtw4sSJ614viiKef/55rFmzBnv3\n7k07X19fj0cffRQbNmzAxo0b8cUvfhGnT5+++U9BNAeNLrTi8ofQOFwOVV5sRl7NrXVUkxanPRcG\nXY4iIQwYqfYGJAP9vZ9YiD/aWIEddy/G6iUFmM/0uhzUVDkUgR0A8kw6xfY8tUrA0gXJJQr5qF6+\nhl7uNMMpe51UZbxwRJk57/KHuEeeJtW4gvu+fftw5swZHDp0CCdPnsSOHTuwe/duNDY2pl0biUTw\nyCOP4NixY3A6nWnnvV4vvvSlL2HZsmV466238Oabb2LFihV4/PHH4fP50q4nmq/aegJp26eaO5Oj\na/ke69olDmlUPRSKSs8x6jUwG5PTzgtHTc0vX6icilerVVi6wJaxMhyN2LpuAYryjbCadPjMJyph\nMiar2hVYDRkL+iwoMqNI9uWrxx287p53+Re2q+1e/OzVBvzPW1elTHyiiRgzuPt8Phw5cgR79uxB\nZWUldDod6urqUFVVhcOHD6ddHwqFsGXLFjz77LOwWq1p51taWhAIBPDggw8iNzcXubm5ePDBBxEI\nBNDc3DwpH4oo28QzZEx3u9KLzbR0BxCPJxSj8xKHSbFGnlJsN0rrxauXOKTRZYXTglWV9rTraWyW\nXC0+v20p/uwzKxRfmNRqVVq5XaM+uee/UDZr0pthG11K6gvbQDCK/z3VgoFgFJ39Azj+QdtUfRya\nw8YM7g0NDYhGo6ipqVEcr62tRX19fdr1FosFjz/+OFSqzC+9fPlyVFRU4IUXXkAgEEAoFMKLL76I\nhQsXYsWKFTf5MYiy19nLvTjw0kf4zzc+VmStZ6okF4rEcKHJLY3Oc4dH55nWg+UFaDQ5auy8ZzH+\n32dX4bN3LoJ6krelEbBykfIL08rKfKhUAsxGDQy65MxKJBqXOusJgoD1K0fW2VNf5i41uxVf9tp7\nA/AGlFn6RGMZ81+4253sHjV6FG6z2eByuTI95YZ0Oh1+8pOf4O2338a6deuwevVqvPHGG9i/fz+0\nWu3YL0A0h7h8Qfy+vlPaQvXWh+0Aknkr8vaq8spq7w/vZwcApyPZpKXCaYFKlsUuCAKqypT/Zm+0\nL5xu3cJiC9YsK4TJoEFVmVVKkBMEQVHaNqUo34gFzpHRf593CImEiOau9Gn71h6Wr6WJuaWv7zez\nJcbr9eKxxx7Dpz/9aZw6dQqnTp3CZz/7WTz22GPSFwmi+eJSs7L5SHtvAL6BMDyBsDQ6N+hyFL3I\n5TXjU/3Lcw0aRanX24eDDE0fQRCwubYEj96/Cp+5Y6GiaE9ZhpmVBUVm5OpzpC9c0VhyuSXTjE1n\nn7LiYDQWz9iulihlzOButyenmrxe5dYNj8cDhyNz2cgbOXr0KHw+H77xjW/AarXCarXiiSeeQDgc\nxtGjRyf8ekTZrDVDsZPWbuVedWe+UZGlLSfP6N64yonPb1uKnfcswabq9GRWmjmVJemld6vKkseK\nbAbp2OmPe6VtdPIEPXlp4D5PEM/9+iIOvXIev/2Q6/GU2ZjBvbq6GlqtFmfPnlUcP336NNatWzfh\nN0wkEhBFUbHnVhRFxONxJBKJGzyTKLuNTpoLhWNw+dKLnbR2+xXVzIrsucnGJqOmdg26HBTIAkOq\n2Uvx8FQ9zR6WXC1Wydbkly6wwZ6X/G9XIPvv2tQ5smPotqWFUm/6gWAUA8NZ87870y6N2s83utDZ\nn7k1Lc1vYwZ3s9mMnTt3Yv/+/WhqakIwGMShQ4fQ0dGBuro6nDt3Dtu3b0dnZ+e43vCuu+6CKIr4\nwQ9+gIGBAQwNDeHHP/4xAODuu+++pQ9DNBtFonG8/Ltr+MmvzuHt0+3SF1t5FTOddqRoTHvvANpl\nI/pUUF85KsN9+cJ8BvEsctdtpdh+x0J8asMCbFu/QDpenKHyHZBsUyvvMd/lGsRQKKrYMgcAl1u9\no59KNL4196eeegqbNm3CQw89hI0bN+KNN97AwYMHUVpaimAwiKamJkSjyapYL730EmpqalBTU4Mz\nZ87g5Zdflh53dHSgvLwcBw8eRH19PbZt24Y77rgD7777Lg4cOIDy8vIp/bBEM+H9C91o6wkgkRDx\n0bV+qZKZPGFucZlVakwSjSekGuYqQZBKm65YmI8l5cn96eVFZlY0yzIqlYDFZVYsr8hXTLk77UZF\nJz0gOStTaDMqytp29w+hpSvTMg6T7SjduOpJarVaPP3003j66afTzm3cuBEff/yx9PiBBx7AAw88\ncMPXW79+Pf7jP/5jgrdKlH0SCTEtaa6h0YWlC2zKBi82I9QqAd6ryi1PDqtBKgWrUgn4o40L8Kn1\n5dzKNodoctSocFoUU/ILiy1QqQTFqL7LNYgBWWnhFP9gBEOhKHdCkAJ/QxBNoT5vEKGIMqu5q38Q\noUgMfbKRe4HNINUpl0s1d0kRBIGBfQ5au7xQ2sqo06ilnQ9FdmUBnKaOkS8A8i5yfbIvih9d68eL\nxy/j7dPtiMeZxzRfzf1OEEQzqCtDslNCFHG51SP1XFerBNgteoiW5HSsfIvT4rL0Ko809zjtufjT\nrUvQ1hvAotI82MzJznR6bQ7yLXq4/ckOgInhfA1LrhYLiy1SN7kezxAqii3o7BvA26eTtRJ63EPI\nNWi4fDNPcQhANAli8QQuNLnQ0OhSjJbkU++pKmUAcKphpBBNoc0ItVqFHLUKW1aXSElyS8ptaRny\nNHcV5huxdnmRFNhT5OvuKQuLLYqytj3D1e0aGpWFxRoaXRm7B9Lcx5E70SQ4/kGrlCjX3juAP95U\nAUCZNLdhpRNvn0mOquRdweQdxZZV5KMw34ihUAwl3NJGSP79uNCkLPC1qDQPubI19l5PsrpdS7cy\n4S4wFIHbH5K23dH8wZE70S3yBsJSYAeAK20euP0hhKNxqSa4ShCwfGG+YvSeUjRqZGYz61FaYGJg\nJwDAAqdFkV2fZ9KhtMAEq1knrbsHwzFc6/Cm5XcAmRsQ0dzH4E50izIVEfm4xaNImMvP00OTo0pL\nmlMJAkocmfc5EwGAyaDBltWlyFGrYNDl4FPrF0AQBAiCgALryIhcvtQjl6mcLafq5z5OyxPdokwj\no+YuP3SybObU2vmyChsutYxMsS5wmrmFicZUs9iBVYvsEARlT4/CfCM6+pJfLuWd41YvKUD9lT4A\nyuDe2T+AN95rQTwh4lMbFmTcoUFzA0fuRLco08jI5QviavvIVH2hLRncywpNqKlyQBAEWHK1uPO2\n0mm7T8puKpWQtlSTqducWiVg7fJC6Vq3P4RoLI5YPIHX3032iQ+GY/jfU62IxrhVbq7iyJ1onHwD\nYbR2B1CYb5RG4pFoXNqmJAjJoiOpaXp5Ml0quAuCgE/eXobNq0ugEoS0ymREE1FaaIJKEKQtcqlj\nRr0GNrMObn8Ioiii3xvCYCiKIVkRnFAkhuYun1T1kOYWjtyJxmEoFMWLx6/g7TPt+OWbV6Sp0F7P\nkLR+mW/RY8mC9H3pOWoV8vP0accY2OlW6bU5WFiinFpP9SCQr8f3eYfQLKuAl9LRy6YzcxWDO9E4\n1F/pkzKRE6KI94eTl+RT8k67EZXFmavMqRnIaYpsri2BJVcLAFheYUNVaR6AkdkiAOh1D6VtkwOg\n6D5Icwun5YnG4Vq7ctTT2Z/s0CUP7kX5RpiMWhTajIop+apSVpmjqZNn0uHPtq/AUDgGk2EkOVPe\nDvhSy0h/A02OSlprd/mS6/Gp/gU0d3DkTjSGYDgmdWlLEUURTZ1+RaZ8ah1+Y7VTqhOeb9FjeQXX\nNGlqqVSCIrADyaZDqgy1EpaUj/SSF0VR+oIqiiIutbjxxqkWtPWkj/Ipu3DkTjQG+Shc7qNr/VKC\nklajlsqGVjgtePBTS+EbCKO8yMxGLzQjtBo1CmyGtN0clcNr9C5fsjRyryeIskIzrrR58Zv3WwEA\nV9u8ePBTS+GwsrJdtuJvHaIxyDtuLSgySz/3e5UtW+UJcg6rAVVlVkXnLqLpVlmSp3ico1ahrNCk\n6FmQKrb0h4s90rGEKOLc1b7puUmaEgzuRMOGQlG89PY1vHDsEq7J9qjLR+5Lym2wmnVpz2WVOZqN\nli/MhyZn5Nd8zWIHNDlqxXp8j3sInkBI2tKZ0twVYCW7LMbgTjTst6fb0d4bgNsfwrH3WqSKX71u\nZd/1hRky4suKTNN2n0TjZTJo8Cd3VqGyJA/rVxRhU3UxAMBu0Us7OPyDEXwsS7hLGQpF4QmE045T\ndmBwJ0LyF1lTp196nBBFXGx2YSgUxUAwua6eo1YNJ8jlK55rydXCmc+RO81OxY5c3Le5Ehuri6WA\nrlarFOvp8il5uR42nclaDO5EANp60qcgmzr9imQkh9UAlUqAw2rA2uVFAJIB/5O3l7EgDWWdAlt6\n6VoAqF3skH7ucQ9O1+3QJGO2PBGS+9ZHc/tDuNw6Ml0pT0K6o6YYqxbZodeqmTRHWanIZsT5UceK\n7bmoLMnDuav9AIAeWb5Je28A7zd0w2rW4c7bSrk3fpZjcCeCMmlOXuRD3qfdOarveqoqGFE2WuA0\nQxAExYzVotI8RbKdyxtCLJ5AJBrHayeaEI0l0Nk/iERCxKc2VMzEbdM4cVqe5r14QoTbN5IpvGGl\nM+N1TjvX1WnuyDVoFMmhmhwVli6wQa/NkXaEJEQR/d4grrX7FB3kLrd6EQrHpv2eafwY3GleaesJ\n4MXjl3HsvWapAI3HH0I8kRy9WHK1WFZhS2utmavXpFUAI8p296wtQ2VJHpz2XGzftBC5w3/H5a1k\ne1xDuNahLL+cEEW0sordrMZpeZo3hkJR/Ppkcmqxxz2EeELEvZ+oVBSpsecZYNRrUOLIlTq/Acnp\nytEBnyjbGfUa3Le5Mu14Yb5Rqkff0u1HZ19697hu1yCWLmBp5dmKI3eaNy63ehRTi40dPvgGwujz\nKvexA8DGVU5p25AmR4XVSwqm92aJZlCRbGtna09A0S8+pVf2pZhmH47cad7I1PLyWodP8Usq1Saz\npMCEnfcsQWtPAItK8zJWpSOaqxx5eqhUAhIJZVBfs6wQZz7uBZAsv5xIiNwGOktx5E7zQiIhojvD\ndrdr7d5RNeJHMoUL841Yt6II+Rb9tNwj0WyhVqtQkKFpTPUiu5R7Eosn4AmMJKK2dvtxqdmNUISJ\ndrMBgzvNC55ACNF4cko+R62SWmH2uIcQGz5uNmph1DNpjggASguUJZWL7bnIM+kUxW/6hr8Yf3S1\nH6+804jffNCKI+80po34afoxuNO8IB+dlxeaUFKQvq1NPmonmu9qlxRIX3YFQcCGVcktovJ98H2e\nIOIJEacauqVjPe4hNHUqs+tp+o0ruAeDQTzzzDPYunUr1q5di127duHEiRPXvV4URTz//PNYs2YN\n9u7dm/GaAwcOYOvWraitrcW9996LV1555eY+AdEokWgcXf2DiMdHkuf6ZMG9wGbEotK8tOeVFrL5\nC1GKyaDB57ctwZ2rS7HznsUoH253LJ+u7/ME0dk3kDYV39jB4D7TxpVQt2/fPly4cAGHDh1CSUkJ\nfvWrX2H37t14+eWXsWjRIsW1kUgEf/7nfw5RFOF0Zi4G8tOf/hSHDx/GD3/4QyxbtgxvvfUWfvSj\nH2HDhg3XfQ7ReASGIvift64iMBSB3aLH57YugU6jVmx3c1gNKLQZ8M7ZTqk6lyAIWFSSHvCJ5jOz\nUYvVS5U7ReTBvd8XVLRHTulysSb9TBtz5O7z+XDkyBHs2bMHlZWV0Ol0qKurQ1VVFQ4fPpx2fSgU\nwpYtW/Dss8/CarWmnY9EIjhw4AC+/vWvo7a2FjqdDtu3b8fRo0cZ2OmWvd/QjcBQBADg8odQf7kP\n4nCVrRSH1QCTUYvbZL+0Vi9xwGRkOVmiseQaNNBrk+PCSDSO842utGv8gxGpSBTNjDFH7g0NDYhG\no6ipqVEcr62tRX19fdr1FosFjz/++A1fz+/3IxqNYseOHWhpaUFlZSWefPJJbN68+SY+AlFSPCGm\nVdK60OTC0gU2hKNxAIBemwOzMbmO+ImaYiwoMkOlElDiYGlZovEQBAGF+Qa0jtpaatDlwGzUSn0a\n+rxBVDiZoDpTxhy5u91uAEgbhdtsNrhc6d/YxtLV1QUA+OUvf4l/+qd/wu9+9zts2rQJX/nKV9DS\n0jLh1yNK6XUPITIcxFMGglGcb+yXHhfYDFKlOUEQUF5kRmmBidXniCagrMCcdmxRaZ6ic2Ifi9zM\nqFvKlr+VX4hf/epXUV5eDpPJhCeffBJ5eXl49dVXb+V2aJ6Td3aTO3u5T/o5095dIpqYhSWWtGOL\ny6xSESggmTWf4gmE8H5DN1q6/dNyfzSO4G632wEAXq8yacLj8cDhcEz4DQsLCwEoZwLUajVKS0vR\n09Mz4dcjSpGvq1c403/5AMptPER0c/ItelTKOsoV23NRVmhStov1Jf89egIh/NdvLuP9C9048k4j\nWhngp8WYwb26uhparRZnz55VHD99+jTWrVs34TesqqpCTk4OPvroI+lYPB5HR0cHysrKJvx6RCny\n4H7b0gIp6UdudGEOIro5n95YgXUrirB2eRHu21wJQRBgM+ukcrT+wQhCkRjOfNyr6Onw4aXembrl\neWXM4G42m7Fz507s378fTU1NCAaDOHToEDo6OlBXV4dz585h+/bt6OzsHNcb2mw2fO5zn8OPf/xj\nNDQ0IBQK4Uc/+hGGhobwwAMP3PIHovkhFIkp+knH4wm4/COlMAtsBiwqVY7eC6wGVqAjmiRajRqb\nqotxR00x9LrkF2m1WqUo19znCaYluXb2D7IX/DQY1z73p556Ct///vfx0EMPYXBwECtWrMDBgwdR\nWlqK9vZ2NDU1IRpNbnt46aWX8K1vfQsAEI1GcfbsWbz22msAgNdffx2lpaX41re+BZ1Ohy9/+csI\nBAJYuXLNVx4YAAAgAElEQVQlnnvuOWnKnuhGPm5x4/gHbUiIIj55exlqqhxw+8NSyUtLrhZ6bQ5W\nVtpxocktPW/NMv79IppqjjyDNIt27kofwhFlkqsoiuj1DGHBdZbOaHIIopihl98s1N7ejm3btuH4\n8eOcvp/HorE4fvbaBekXhlol4JH7VqKlK4Djf2gFAFSV5uEzn0j2qL7W7sWlZjcqii2orpp4jggR\nTUz9lT68c7bjhtdsqi7GuhVF03RH2e1mYx9bvlJWae0OKEYC8YSIq+1eePxh6Zi8sUVVmRVVZenF\nlIhoahTbM9eMWLXIjobhgjc9rGA35dg4hrJKW+9A2rErrV7FthtmxBPNHIfVAJ1WrTiWq9egdvHI\nzFmPJ4gsmTTOWgzulFX6Muxl73INKva4ywtpENH0UqkEVJUqZ8sWl1uRb9FDk5MMOUOhKAZDI0l1\nkWicSXaTjMGdskYiIcLlG8mIt+elj9BtZn3GLXBENH3WryyCYTiD3mTQYO3yQgiCAIfs32wq6e5C\nkwsHXz6Pn712AZdbPTNyv3MRfwtS1vAEQogNt3E1GTSoXmTH22faFddw1E4088xGLf7vHy2DJxBG\ngdUArSY5TV9gM0gd4/q9QRTaDPjdmQ4kRBGJuIi3T7ejsiRPGuHTzeOfIGUNeZGaAqsBi0rzoBpV\nAjlTWUwimn5GvQalBSYpsAPJ9fiUPs8QLrd6pC/sABCOxtHcxV7wk4HBnWYltz9Zi7qjbySBrm9U\n29ZcgwbVVXbpmM2sLIlJRLNLgVXWWMYbRGNHeina7v7MPSJoYjgtT7POQDCKX751BeFIHMJFAX+8\nqQKLy6yKLlOp7W5bVpciz6TDQDCK25YUQK3m91Wi2SrfkixPm0iI8A9G4B+MpF3T7eY2ucnA34Q0\n65y/1i/tZRdFEe+d74IoimkjdyCZmbt6SQE215Yg18DSskSzmVqtgl1WnjZFUbLWG1RM1dPNYXCn\nWadxVC1qbyCMax0+qVe7QZcDs5GBnCgblWRo3rSyMh9Wsw5AcleMPL+Gbg6DO80qoXAMblkDmJST\n50YaExXajBBGJdIRUXaoHJX0qhIELF1gU/SC72Nwv2UM7jSryCvNycnX5gpZgY4oa5UWmFDiGBm9\nr15aAKNeo9gD75IF92gsjl7PkNQYisaHCXU0q8i/sa+szMfV9pHp+JRC7mUnylqCIOD+LZW40uaF\nJkeFJeXJanYO68i6e/9wsSpvIIxfvnUFwXAMJQ4T/uSuRchh0uy48E+JZhWXbyS4F+XnYtGoKTy1\nSkBphjU7IsoeWo0aqxbZsXSBTVpik++Bd3mDSCREnGroRnC4LG1n/wAuNbszvh6lY3CnWaXfO7Le\n7rAasKLSrji/qDRPURSDiOYGo14Doz6ZKBuNJ9DvC6KpU5lce6XNOxO3lpUY3GnG9LiH8HGLG9FY\ncto9Fk/AO5Bs3SoIAvItepQWmLBhpROaHBUKbUZsXl06k7dMRFNIPjX/wYWetC1xXa5BRGPcJjce\nXHOnGXGlzYNj77UASJaS3bl1CVy+kNQGMs+klepLb1jlxIZVzhm7VyKaHo48A1q7AwCQNmoHUs2j\ngnBep2c8jeDInaadKIo4db5betznDeJis1vRzlVeppKI5ofrBW35cXmlSro+Bneadm5/SJp+T7nU\n7E5rDENE80tJQW5aDQunPReLSvOkx/0+BvfxYHCnaZepQEWPewhX20em4Qq4l51o3tFrc7DQaVYc\nW1VpV3zZ58h9fBjcadpd7x9nKBKTfnZw5E40L32itkTKmq8stmBZhU0R3F2+IAvajAMT6mjayYP7\nknIbrrR5FOetJh0MOv7VJJqPbBY9vnjvCgSGIrCadBAEAXpdDkwGDQaCUcQTIrwDYeRb9Gjt9uPD\nS72wmnXYXFvCbbIyHLnTtBJFUbFmtmFVETSjKk6VF5lHP42I5pEctQo2s16x/m6Xlaft9wbhH4zg\n1yeb0dE3gIZGl6L/BDG40zTzDUQU3d2sJh0WD5efTFm1yJ7pqUQ0j8n3wLt8IVxocin2wV9sHqmZ\nQZyWp2nW55Vtd7MZIAgCNteWIDjcDW7diiKutxNRmtEjd09A2T0ynhDR3juAypK80U+dlxjcacr0\ne4N4/0I39Fo1PlFbAr02R7HentrLrtfl4P4ti2bqNokoC8i/9Ld0+zNe0+MeYnAfxuBOUyIai+OV\ndxoxFIoCAIKhGO7bskixDY7b3YhovGxmHXRaNcIR5dS7VqOWlvq4TW4E19xpSlxp80qBHQCauvxw\n+YKjRu4M7kQ0PoIgKPrAp9xRUyz93OsZkkpYz3fjCu7BYBDPPPMMtm7dirVr12LXrl04ceLEda8X\nRRHPP/881qxZg717997wtV999VUsW7YM//M//zOxO6dZLVUfWu7s5T5pL7tOo4YlVzvdt0VEWWxx\nmXLK3WTQYGWlXepDEQzHMBiKZXrqvDOu4L5v3z6cOXMGhw4dwsmTJ7Fjxw7s3r0bjY2NaddGIhE8\n8sgjOHbsGJzOGzf76O/vx/e+9z0YjawjPtd09g+mHbso68XssBrSykwSEd3I4jKrNOOnEgRsXl0C\ntUoYVcFu6HpPn1fGDO4+nw9HjhzBnj17UFlZCZ1Oh7q6OlRVVeHw4cNp14dCIWzZsgXPPvssrFZr\nhlcc8Z3vfAf33nsvbDbbzX8CmnWGQlFpSl4QBOSo0/+aOe38QkdEE6NWq7Dj7sX41IYF+Py2pVhS\nnowdDkUFu9D1nj6vjBncGxoaEI1GUVNTozheW1uL+vr6tOstFgsef/xxqFQ3fukjR47g0qVLePLJ\nJyd4yzTbyf9xFdoMGbNXSwrS186IiMai1aixvCJfkZArD+6ppN2BYBSvnWjCf795BW096cuEc92Y\nwd3tTk6ljh6F22w2uFyum3rTvr4+fPe738V3v/tdTsnPQfLubg6rAcsXKmdmDLoclDG4E9Ekccj2\nwLuGf/8c/6AVTZ0+dLsG8fq7zQiG59da/C1ly9/smul3vvMdbN++HZs2bbqVt6dZyiUrL+vIM2BB\nkVkxet9UXQx1hql6IqKbkZ+nh2o4HnkHwuhxDylG6+FoHNfavTN1ezNizH3udnuyFKjX60VRUZF0\n3OPxwOFwTPgNX3nlFVy6dAl///d/P+HnUnbol03L2/OS9aE/c8dCtPUGoNfmoCifszVENHmSteh1\ncPmTv3ve/Si9znxLlx/VVROPWdlqzOFTdXU1tFotzp49qzh++vRprFu3bsJv+OKLL8LlcmHr1q3Y\nuHEjNm7ciK6uLvz1X/81vvrVr0749WhmtXT58c7ZDulbcjyegNsvC+6pzFaVgAqnhYGdiKaEfA2+\nvXcg7XyPJziv9sCPOXI3m83YuXMn9u/fj6VLl8LpdOKFF15AR0cH6urqcO7cOXzzm9/Ev/3bv6Gk\npGTMN/zRj36ESCSiOLZr1y489thj+JM/+ZOb/yQ07dp7Azjy++R2yI+u9uNz9yxGjlol9Vq25Gqh\nYwtGIpoGDqsBaFG2j9Zq1IjFEkiIIoZCUQyGYjAZNDN0h9NrXOVnn3rqKXz/+9/HQw89hMHBQaxY\nsQIHDx5EaWkp2tvb0dTUhGg0ufXppZdewre+9S0AQDQaxdmzZ/Haa68BAF5//XWUlpamvb5arYbF\nYkF+fv5kfS6aBqc/7pV+TogizlzuQ4VzpF0rK9AR0XTJ1Cp6abkVbn8Ynf3JkXyfZwgmw/yoPT+u\n4K7VavH000/j6aefTju3ceNGfPzxx9LjBx54AA888MCEbuLNN9+c0PU086KxODpGTX01dfqgkuVY\nFtg4BU9E0yPfoofdopfW3QFg+cJ8XG33SsG9dx41lmHKMt2UXk8Q8YRy/SqREHGlbSQjtZCNYYho\nmgiCgE/eXiaVol2ztBBOe65iBrF/HhW4YVc4uin94+i+VMiROxFNo5ICE/7fZ6sRicaRO7y2Lu8D\nL9+mO9dx5E43pV/2j2TdiiJpj2lKvkUPvY7fHYloemlyVFJgB5KtYlXD64X+wQjCw+1hh0JRXGnz\nIDAUyfg62Y6/femmyIN7WaEJLl8ITZ0+6djishv3FSAimg5qtQr5Fr1UOdPtC8Fs1OC/37yCgWAU\nWo0af7p1CfIt+hm+08nFkTtNWCIhwi1bu3LkGXBHTTG0w9veLLla1C6ZP8UiiGh2s8sCd783iPcv\n9GAgmNzhFYnG8eHFnpm6tSnDkTtNmCcQkpLpTAYN9Loc6HU5+LPty+HyhVBgM0Cv5V8tIpod7FYD\n0JrcA9/jHkRjp19xvrHTh0RClKbv5wL+BqYJk3d9k3djMuo1MOrnR4EIIsoejryRkfulUYVuACAa\nS1bWdMyh2hwM7nRDLl8Qxz9oQyyewObVJahwWqSWioAyE5WIaDYaT9DucQ/NqeDONXe6LlEU8cap\nVvR6huD2h/DGqRaEwjFFS9cC7mUnolnOqNco1t1TViwcqYra4x6azluacgzudF29nqBiX2g4Esfl\nNg/6ZHvcWWKWiLJB1agdPEX5RiyXBfdeD4M7zRMt3f60Y6cv9SIUiQFINmWw5Gqn+7aIiCasusou\nNY1RqwRsri1RTMO7/aG0qpvZjGvudF3drsG0Y6ntI0ByC5wgzJ3sUiKau4x6DR781FI0d/lRbM+F\nbXia3mzUIjAUQSIhwhsIzZk8Io7cKSNRFNHrHpl+z5Ro4rSzvCwRZQ+jXoOVlXYpsAOAXZZJ75pD\ntecZ3CmjwVBMMf2+qbo47ZqKYst03xYR0aTKFNwTCRFNnT609QQgitk5Vc9pecpInkiXb9FjQZEZ\nVrMO3kAYQHIkX+LInanbIyKaFPJpePfw7703/9Aq7YffsNKJDaucM3Jvt4Ijd8pIXl7WnqeHSiXg\n/s2LUFmSh8VlVty/ZRHX24ko6ylG7v4QPIGQotDNh5d6EArHZuLWbglH7pSRa1RwBwCrWYf7NlfO\n1C0REU06q0kHlSAgIYrwD0ZQf6VfcT6eENHc7cfyivzrvMLsxJE7ZeTyswodEc19arVKkWB3/lp/\n2jUdvQPTeUuTgsGd0NYTwGsnmvDhpR6IopjW9S1TZSciormieIydP72ywl3ZgsF9nvMGwnj1941o\n6vTh3Y+68MGFHngHwmld34iI5qrSQlPasZqqkbbVHn8I8XhiOm/pljG4z3MNTS5FVaYzl3vR1T9S\nvIZT8kQ01y0stkCrUUuPVYKA25YWSBU4E6IIz/BOoWzB4D7PtXQpS8xGYwmcauiWHs+lLklERJlo\nctS4a00pVIIAQRCweXUJ8kw6xe+/fl92Tc1zvnUeC4ZjcPvTKzINhWQlZq1cbyeiuW95RT7KCs1Q\nCclKdkCyxHZjhw8A4PKGgIqZvMOJ4ch9HuuTdUGy5GqhUqXvWy8tSF+LIiKai0wGjRTYASBfsQc+\nu0buDO7zmHzUXl5kRmVJnuK8w2pQ/EUnIppP5AVu3FlWd57T8vOYvFBNvkUPh9WAa+1e6VjtYkem\npxERzQt5uTqoVQLiCREDwShCkRhUgoC3PmxDnzeI25cVYmWlfaZvMyMG93lMPnK35+lRWmDCtnUL\ncLHZjYXFFqxYmF0VmYiIJpNKJSDfokefNzkl7/aHcKXViyttyUHQm39oQ75FD6d99vXZYHCfp0RR\nWagmf7hQzYrKfKyoZFAnIgKgCO5d/YO41OxWnL/Q5JqVwX1ca+7BYBDPPPMMtm7dirVr12LXrl04\nceLEda8XRRHPP/881qxZg71796adb2trw549e3DHHXdg/fr1eOSRR9DQ0HDzn4ImzD8YQXS4KINB\nl8O1dSKiDOTb4d79qEv6vZnS2j0728KOK7jv27cPZ86cwaFDh3Dy5Ens2LEDu3fvRmNjY9q1kUgE\njzzyCI4dOwanM71NXjgcxqOPPgqj0Yhjx47hrbfegtPpxFe+8hWEw9lVJCDbyIvVKKfkuZediCiT\nojFK0w4EoxgMzb6ucWMGd5/PhyNHjmDPnj2orKyETqdDXV0dqqqqcPjw4bTrQ6EQtmzZgmeffRZW\nqzXtfG9vL9avX4+9e/fCYrHAZDLh0UcfRV9fH65duzY5n4oUBoNR/PebV3DgpY9w6nwXRFEclUyn\nm8G7IyKavQptRmjUylCpHl6LT+n3zr5tcmMG94aGBkSjUdTU1CiO19bWor6+Pu16i8WCxx9/HCpV\n5pcuLy/H3/7t38Jms0nH2traoFarUVhYONH7p3H4fX0nul2DiMUT+OBiD1p7AuiV7XEvsN74mykR\n0XyVo1ZhgdOsOFZZkocyWT36rAzubncyeWD0KNxms8Hlct3yDfT09OBv/uZv8PDDD8Ph4NaryRaO\nxnGtw6s4Vn+lD73ukeBemM9peSKi61m7okgq8pWjVmH9yiJladpZGNxvKVteENIrmk3ExYsXsXv3\nbmzatClj4h3duq7+QSQSymSP1u6A9HOOWgWbmSVmiYiup9BmxJ/eswQdfQMoLzLDnmdAPD7ye3U2\nBvcxR+52e3KDvterHP15PJ5bGmm//fbbePjhh7Fr1y58//vfh1qtHvtJNGHyDm+ZFFgNGcvOEhHR\niMJ8I9YsK5RG7Pl5emmA6xuMIBqLz+TtpRkzuFdXV0Or1eLs2bOK46dPn8a6detu6k3fffddPPHE\nE/je976Hr33tazf1GjQ+blkno9HrRgDgdMy+/ZlERLNdjlqFPFOyJawoinD7Z9durzGDu9lsxs6d\nO7F//340NTUhGAzi0KFD6OjoQF1dHc6dO4ft27ejs7NzXG84ODiIvXv34pvf/Ca2b99+yx+Abswl\n2/K2YaVT0bMYAJaUpe9oICKisTlk24hds6wl7Lj2uT/11FPYtGkTHnroIWzcuBFvvPEGDh48iNLS\nUgSDQTQ1NSEaTbYJfemll1BTU4OamhqcOXMGL7/8svS4o6MDv/nNb9Dd3Y3vfe970vHU//75n/95\nSj/sfBOJxuEfjAAAVIKAAqsBW1aXSOdXLMxHYT4z5YmIboa8sUxqe3FDowsvHLuEoyebEIrM3P53\nQZyNpXUyaG9vx7Zt23D8+HGUlZXN9O1khW7XIP77zSsAALtFj//7x8sBAN5AGMFwDE678ZaTIomI\n5qvGDh9+fbIJAFBWaMYnaorx4ptXpIp1qxbZcc/a8lt6j5uNfWz5OocpCtXIvmFazToUO3IZ2ImI\nboFy5B7ER9f6FaVoP27xIDaqXO10YXCfw+SNYVhilohocllytdDkJMNoMBzDxVFNZWLxBDr6Bmbi\n1hjc5zKXfyTBQ/4Nk4iIbp0wnMt0I32emUm0Y3CfI9z+EF77fSOOvdeMwWAUoihKbQoBKOogExHR\n5CgrTN9iLP992ycr9T2dGNzngFg8gVd+dw1NXX5cafPi9XebERiKIhxJFlXQadSw5Gpn9iaJiOag\nqrI8xWONWoU7byuVHvfNUPU6Bvc5oLHDh4FgVHrc5RrEuat90mOH1cDkOSKiKWDPM6CmaqRa6x21\nxShx5EqVP/2DkRnZEndLteVpdmjt9qcdO3tZGdyJiGhq3LWmFMsqbMhRq6Tft3aLXhq1u30hlBSY\nbvQSk44j9zmg23XjNZ0CG4M7EdFUEQQBTnuuYiCl2CYnqxQ6XRjcs1w0FodPVoXOniFxrmyavzES\nEc13+ZaRQC/fljxdGNyznNsfloom5Jl0qF6s7NRXlG+EychkOiKi6ZSpNO10YnDPcvJmBfY8fbJe\nvC1ZL14lCPhEbcn1nkpERFNEXhXU7Q9Jg7A+TxAtXX4kElNb+Z0JdVnOpahCp0eOWoXP3bMYHX0D\nsJn13AJHRDQDTAYNtBo1ItE4QpEYguEYrrZ78c7ZToiiiCXlNvzxpoope3+O3LOcK0OJ2Ry1ChVO\nCwM7EdEMEQRBUcymvXcAJ+o7pRH8lTYPet1TV+CGwT3LjZ6WJyKi2UEe3E+e60R81FT81XbvlL03\ng3sWGwpFEQwniyNoclQcqRMRzSKFsm3I8kJjKWNtY74VDO5ZRp6EoWjpatGzCh0R0SxS7Mi94fk+\n75CiRexkYnDPErF4AkfeacQ//7IeL//uGqKxBPplNYtZhY6IaHbJt+iRZ9Ipjq1YmA+jXgMAiMYS\n8AbCU/LeDO5Z4tzVfrQMl5lt6wngw0s9XG8nIprFBEHAbUsKpMcatQprlxcpput7p6hrHLfCZYnL\nrR7F44ZGFwy6kf98jjyO3ImIZpvqKjtUKgG9niGsqrTDatahwGpAc1dysNbvC2HZFLwvg3sWGAxG\nFVPwABAMx6RkOkBZMIGIiGYHQRCwapEdq2CXjikK3ExR9TpOy2eBnjH2Qtoteui1/J5GRJQN5Fvk\nPAEG93lLviazdIFN6hOcUu40T/ctERHRTbKadFAJI/3eo7H4pL8Hg3sWcMvaBVY4zVheYZMeqwQB\nKyvtmZ5GRESzkFqtgtU8kkXv9k9+xjzncrOAPLjbLHosLMlDLC7C5Q1i7YoixRQPERHNfjaLXvrd\n7vaFUJRvnNTXZ3Cf5eLxBHwDyX7tgiDAZtZDk6PCH22cuoYDREQ0tewWPa4N/+yegnV3TsvPct6B\nkX7tZqMGmhz+JyMiynY2y8i0vNfP4D7vKLq+cfqdiGhOKC0wQatRAwDyzLoxrp44TsvPcp5R6+1E\nRJT9jHoNdt6zGC5fCFVl1kl/fQb3Wc4tqzvMQjVERHOHPc8A+xRVFx3XtHwwGMQzzzyDrVu3Yu3a\ntdi1axdOnDhx3etFUcTzzz+PNWvWYO/evWnn3W43/uqv/gp33XUX1q9fjy9+8Ys4f/78zX+KOWIo\nFMWx91rw2u8bpbrxLlllunwzgzsREY1tXMF93759OHPmDA4dOoSTJ09ix44d2L17NxobG9OujUQi\neOSRR3Ds2DE4nc6Mr/fEE0/A7Xbjv/7rv/Db3/4Wt99+O770pS/B4/FkvH6++M0HrbjS5kFTlx8v\nvX0NvoEwvAPJkbtKEDhyJyKicRkzuPt8Phw5cgR79uxBZWUldDod6urqUFVVhcOHD6ddHwqFsGXL\nFjz77LOwWtPXES5fvoxTp07hm9/8JpxOJ3Jzc/EXf/EXEAQBr7zyyuR8qizkGwijtTsgPQ6GY3jz\nD23SY3ueHjlq5j8SEdHYxlxzb2hoQDQaRU1NjeJ4bW0t6uvr0663WCx4/PHHr/t69fX10Gg0WL58\n+chN5ORg1apVGV9vvki1c5Xr6BuQfi6c5AIHREQ0d405FHS73QCQNgq32WxwuVwTfkO32428vDwI\ngrI+utVqvanXmyu6+m/cHKbAypauREQ0Prc0zzs6QN+qyX69bNInaw5TVZqXdr6skM1hiIhofMYM\n7nZ7simJ1+tVHPd4PHA4HBN+Q7vdDp/PJ1VdS/F6vTf1enNBLJ6Ab3CkxOyda8qgka2vlzhMiiYD\nRERENzJmcK+uroZWq8XZs2cVx0+fPo1169ZN+A3XrFmDaDSKhoYG6VgkEsFHH310U683F3gDIyVm\nLblamAwa3LOuHFaTDkX5RmxbXz7Dd0hERNlkzOBuNpuxc+dO7N+/H01NTQgGgzh06BA6OjpQV1eH\nc+fOYfv27ejs7BzXG1ZVVeGuu+7C3/3d36GnpwcDAwP4h3/4B+h0Otx///23/IGykbzrW/7wCH3p\nAhv+7DMr8PltS5Fn4qidiIjGb1xr7k899RQ2bdqEhx56CBs3bsQbb7yBgwcPorS0FMFgEE1NTYhG\nowCAl156CTU1NaipqcGZM2fw8ssvS487OjoAAP/4j/+I4uJi3H///diyZQuuXLmCf//3f4fJZJq6\nTzqLKYI797ITEdEtEsTRi9+zVHt7O7Zt24bjx4+jrKxspm9nUh092YRrHT4AwKc2LMDyivwZviMi\nIpoNbjb2sSrKLOCSj9zZHIaIiG4Rg/sMi8cT8A+MZMrbWD+eiIhuEYP7DEgkRlZC3P4wEsMrI2aj\nBpoc/ichIqJbw5av0+wPF3vwwYVu2PMM+MwnFqJf1vWNVeiIiGgyMLhPo27XIN473wUA6PUM4fgH\nbbDL1tgdDO5ERDQJGNyn0cVmt+Jxe28APe5B6TGDOxERTQYu8E6jtp5A2rFoLCH9zGl5IiKaDAzu\n02QoFIV/uH58JmajFrkGzTTeERERzVUM7tPE5RvZy+6056ZNwVeWWOZ1VzwiIpo8DO7TxO2TF6rR\nYVN1sfRYk6PCbUsLZ+K2iIhoDmJC3TRxB5RV6BYWW/DAJ6vQ7RpCZYkFllztDN4dERHNJQzu00Q+\ncrcNb38rKzSjrNA8U7dERERzFKflp4EoioqRu53144mIaAoxuE+DoVAM4UgcAKDVqJkVT0REU4rB\nfRq4R3V9Y1Y8ERFNJQb3aTA6U56IiGgqMbhPA5d/pDmM3cIqdERENLWYLT/JRFHE6Y970d0/iOoq\nByqKLej3ykbueUymIyKiqcXgPsnOXO7Dux8lO7+1dAewc+sSxZq7ncGdiIimGKflJ5Eoijh3pU96\nnBBF/O/7LYjFk81hjHoNjHpmyhMR0dRicJ9ELl8IA8Go4pg3EJZ+dlg5aicioqnH4D6JuvoHb3i+\ntMA0TXdCRETzGYP7JHL5RrLiFxSll5WtcFqm83aIiGieYnCfRPLEudVLCuC050qPK5yWtDavRERE\nU4HZ8pNEFEW4/Motb/dvrsSZy71Qq1W4bUnBDN4dERHNJwzuk2R0/XiTQQNBEHBHTckM3xkREc03\nnJafJKwfT0REswWD+yRh/XgiIpotxhXcg8EgnnnmGWzduhVr167Frl27cOLEietef+LECdTV1WHd\nunW455578O1vfxvB4EgmeX19PR599FFs2LABGzduxBe/+EWcPn361j/NDHKNGrkTERHNlHEF9337\n9uHMmTM4dOgQTp48iR07dmD37t1obGxMu7a5uRm7d+/Gfffdh3feeQfPPfcczp8/j3379gEAvF4v\nvvSlL2HZsmV466238Oabb2LFihV4/PHH4fP5JvfTTaPR0/JEREQzZczg7vP5cOTIEezZsweVlZXQ\n6dgrM3sAABMGSURBVHSoq6tDVVUVDh8+nHb9L37xCyxatAhf+MIXYDAYUF5ejq997Wt45ZVX4Ha7\n0dLSgkAggAcffBC5ubnIzc3Fgw8+iEAggObm5qn4jFNOFEVlcM/jljciIpo5Ywb3hoYGRKNR1NTU\nKI7X1taivr4+7fqzZ8+itrY27dpYLIaGhgYsX74cFRUVeOGFFxAIBBAKhfDiiy9i4cKFWLFixS1+\nnJkxGIwiEk1myuu0auTquQmBiIhmzphRyO12AwCsVqviuM1mg8vlynh9Xl5e2rUA4HK5oNPp8JOf\n/ARf/vKX8fzzzwMASktL8a//+q/QarU39ymmWWu3HyfqOyGoBGxdV45gOCadszNTnoiIZtgtZctP\nNIgJggCv14vHHnsMn/70p3Hq1CmcOnUKn/3sZ/HYY49JXyRms2gsjv99vxUufwj93iCOnmxGn2ck\nWZDr7URENNPGDO52ux1AMhFOzuPxwOFwpF3vcDgyXgsABQUFOHr0KHw+H77xjW/AarXCarXiiSee\nQDgcxtGjR2/6g0yXpk6/YqQeGIrgvfNd0mM719uJiGiGjRncq6urodVqcfbsWcXx06dPY926dWnX\nr1mzJm0t/sMPP4RWq0VNTQ0SiQREUYQoitJ5URQRj8eRSCRu9nNMm9Zu/w3Py+vJExERzYQxg7vZ\nbMbOnTuxf/9+NDU1IRgM4tChQ+jo6EBdXR3OnTuH7du3o7OzEwBQV1eHtrY2/OxnP0MoFEJjYyP2\n79+Pz3/+8zCbzbjrrrsgiiJ+8IMfYGBgAENDQ/jxj38MALj77run9MNOhm730HXP6bRq2PM4LU9E\nRDNrXGvuTz31FDZt2oSHHnoIGzduxBtvvIGDBw+itLQUwWAQTU1NiEajAICysjIcOHAAr732Gtav\nX48vfOELuPPOO7F3714AQHl5OQ4ePIj6+nps27YNd9xxB959910cOHAA5eXlU/dJJ0E4Goc3EAYA\nqAQBKxbmK84vLrNCpWIyHRERzSxBlM+Pz2Lt7e3Ytm0bjh8/jrKyshm5h67+QfzyrSsAAIfVgP9z\nVxVeevsaXL4gTAYN/nTrEpiM2ZHxT0REs9/Nxj5uyJ4AT2CkUI3NrINBl4MHty1Bvy8Em1kHrUY9\ng3dHRESUxOA+AZ7hKXkAsA1veVOrVSjKN87ULREREaVhV7gJ8PiVI3ciIqLZiMF9AhQjdzOz4omI\naHZicB+nWDwB/2AEQLLSnpUjdyIimqUY3MfJNxCWCu+YjRrkqPlHR0REsxMj1Di5FevtnJInIqLZ\ni8F9nFw+eb92BnciIpq9uBXuOtp6Amju8qPEkYuqMitc3pHObw4GdyIimsUY3DPo8wRx5J1GJEQR\n9Vf6sP2OheiTB3crO78REdHsxeCeQf2VXiRkVXnf+rAN4UgcAJCjVnHNnYiIZjWuuY+SSIho6lK2\ndU0FdgCw5+nZHIaIiGY1BvdRPIGQIpiPVuxgv3YiIprdGNxH6ZH1ay/KN6aN0heV5k33LREREU0I\ng/so8v3sFcUWbFjplB5XluSh2M6ROxERzW5MqBtFHtzzLXosLrOi2JGLSDSO8iIzBIHr7URENLsx\nuI/iVTSHSdaPLy0wzdTtEBERTRin5WWisQQCQ1EAw81hTGwOQ0RE2YfBXcYbGGkOk5erhZrNYYiI\nKAsxesl4AvLmMBy1ExFRdmJwl1Gst1tYhY6IiLITg7sM27oSEdFcwOAuo9gGx85vRESUpRjch8Xi\nCWlaXhAE5Fu45k5ERNlp3u5zD0ViOFHfiVA4hvUrnRAEQeoEl5erhSZHPcN3SEREdHPmbXD/7Yft\nuNruBQB09A/i9mWF0jk7p+SJiCiLzctp+cFgFNc6fNLjSDSO9853SY/teYaZuC0iIqJJMS+De1tP\nQCpWk0lhvnEa74aIiGhyjSu4B4NBPPPMM9i6dSvWrl2LXbt24cSJE9e9/sSJE6irq8O6detwzz33\n4Nvf/jaCwaDimgMHDmDr1q2ora3Fvffei1deeeXWPskEdLkGr3tOJQgoYc92IiLKYuMK7vv27cOZ\nM2dw6NAhnDx5Ejt27MDu3bvR2NiYdm1zczN2796N++67D++88w6ee+45nD9/Hvv27ZOu+elPf4r/\n/M//xA9/+EN88MEH+Mu//Ev8y7/8C7q7uyfvk91An2fki0ZNlUNxrqrMCq2GyXRERJS9xgzuPp8P\nR44cwZ49e1BZWQmdToe6ujpUVVXh8OHDadf/4he/wKJFi/CFL3wBBoMB5eXl+NrXvoZXXnkFbrcb\nkUgEBw4cwNe//nXU1tZCp9Nh+/btOHr0KJxOZ4Y7mFyiKCrKzK5fWYTVSwqgEgQU2oy487aSKb8H\nIiKiqTRmtnxDQwOi0ShqamoUx2tra1FfX592/dmzZ1FbW5t2bSwWQ0NDA0wmE/x+P6LRKHbs2IGW\nlhZUVlbiySefxObNm2/x44xtIBhFNJYAAOi1OTDocnDnbaX4RE0xG8UQEdGcMGY0c7vdAACr1ao4\nbrPZ4HK5Ml6fl5eXdi0AuFwudHUls9J/+f/bu9OYqO41DODPsAyKDEsBvSrWJhgW52DlBpdbUnqb\nqqU3amutdrpwS2Jsa5v0g3axmx8qNEpquoxpI4UEbEy/WIlNExNJk9bQxLiClOa2BYaWTS4FZmSZ\ngWF47wfLlBGuw8AMM57z/BITOOfP8fxfX3xyljnnq6/wySef4Ny5c1i/fj1eeOEF/PbbbzObhQ88\nHzEbBZ1OBwAMdiIiUo1ZJdp4MM5k/J49e7Bs2TLExMRg7969iIuLwzfffDOb3ZkW6w2+HIaIiNTN\na7gnJiYCAKxWq8fyvr4+JCUlTRqflJQ05VgASE5OxsKFNx8WM/FMQHh4OJYuXYquri4fd993vROu\nt/MRs0REpEZew11RFOj1etTW1nosv3LlCnJyciaNz87OnnQt/vLly9Dr9cjKykJqaioiIiJQX1/v\nXu9yudDe3o6UlJSZzmPa+iYeufPNb0REpEJew91gMGD79u0wm82wWCyw2+0oLy9He3s7TCYTrl27\nhvz8fHR0dAAATCYTWltbUVFRAYfDgebmZpjNZuzYsQMGgwEJCQl4/PHHcfToUTQ0NMDhcODjjz/G\n0NAQHnvssYBPeOKd8jwtT0REajStZ8u/9dZbKCkpwdNPP43BwUFkZmairKwMS5cuRVtbGywWC5xO\nJwAgJSUFn3/+OUpKSnDkyBHExsZi8+bN2Ldvn3t77777LqKiorB792709/dj5cqVOH78uPuUfaAM\nOZywD48CACLDw2CIjgzo30dERBQMOrndc1hDSFtbGx566CF8++23Mz593949gKrvGgEACxOisXND\nmj93kYiIyK9mmn2a+vxXr4030xERkfqpOtydoy78p6UX1/98lnzPjYnhzje/ERGROqn2fe4u1xiq\nvmvCf/uGAAD//HuKx5E739lORERqpdpwb2q3uYMdAGrqOjDqGnN/zzvliYhIrVR7Wt7SYfP4fmKw\nL5gXyTvliYhItVQb7l29Q/933ZLkGJ8fnUtERHSnUGW4DzmcuDE4AgAI0+kQM9/zKD3t7vipfoyI\niEgVVBnuE4/aF94VjU3rlkMfGQ4ASE2Jxz2LY4O1a0RERAGnyhvq/rDa3V8vSojGkuQY/PtfmRi0\nO3FX7DyekiciIlVTZbhb+/96Ocxdf37kbZ4+AvP0qpwuERGRB1Welu+bEO7xBj6JjoiItEV14S4i\nnm9+Y7gTEZHGqC7cBx2jcI7e/Ex7VGQ45kfxVDwREWmL6sLdOuGoPd4QxZvniIhIc1QX7hOvt/OU\nPBERaZH6wv3GxCN3Pj+eiIi0R3Xh3nvjryN3vvmNiIi0SHXhfus1dyIiIq1RVbgPO10YsDsBAOFh\nOsQtYLgTEZH2qCrceyY8djY+JgphYbxTnoiItEdV4X79lhfGEBERaZFqwl1E0Nxuc3//t8QFQdwb\nIiKi4FFNuHf+MYjrPYMAAJ1Oh+V8rSsREWmUasLd6Rpzf52xPAEx8yODuDdERETBo5oHr9+9yID8\nf9wDl2sMK5YlBHt3iIiIgkY14a7T6bAiJT7Yu0FERBR0qjktT0RERDcx3ImIiFTmjjkt73K5AADX\nr18P8p4QERHNjfHMG8/A6bpjwr27uxsA8MwzzwR5T4iIiOZWd3c3li9fPu3xOhGRAO6P3zgcDvz4\n449ITk5GeHh4sHeHiIgo4FwuF7q7u6EoCubNm/6bTu+YcCciIqLp4Q11REREKsNwJyIiUhmGOxER\nkcow3ImIiFSG4U5ERKQyIf05d7vdjsOHD+PcuXOw2WxYsWIFXnnlFeTm5k45/ocffoDZbEZjYyMM\nBgPuv/9+vPnmm5g/fz4AoLe3F8XFxbh48SLsdjsyMzPx+uuvQ1GUuZyW3/i7Punp6YiMjIROp/P4\nucuXL0Ov1wd8Pv7ka21EBCdOnMCRI0fw8MMP49ChQx7rtd473uqj5d45c+YMSktL0dLSAoPBgI0b\nN+LVV1/l/zt/8lYfrfaOiKCsrAwnT55EZ2cnoqOj3bWJi4sDMMvekRC2f/9+2bp1qzQ3N4vD4ZAv\nv/xSFEWRpqamSWMtFosoiiLHjx+XoaEh+f3332Xbtm2yf/9+95iCggIpLCyUzs5OGRgYkA8//FDW\nrl0rvb29czktv/F3fdLS0uT8+fNzOYWA8aU2w8PDUlBQIM8++6zk5+fLG2+8MWmMlntnOvXRau98\n//33YjQa5cyZM+J0OuWXX36RvLw8KS4udo/Rcu9Mpz5a7Z1jx45Jbm6u1NXVicvlkqamJtm0aZPs\n3bvXPWY2vROy4W61WsVoNEp1dbXH8kcffdSjMcYdOnRItm7d6rGsurpaVq5cKT09PfLzzz9LWlqa\n/PTTT+71TqdT1q1bJxUVFYGZRAD5uz4i6vkl87U2NptNjh07Ji6XS0wm06Tw0nrveKuPiHZ75+uv\nv5bPPvvMY1lRUZFs2bJFRNg73uojot3eqampkQsXLngsKy4ulkceeUREZt87IXvNvaGhAU6nE1lZ\nWR7LV61ahbq6uknja2trsWrVqkljR0dH0dDQgLq6OkRGRiIjI8O9PiIiAkajccrthTp/12fcF198\ngY0bNyInJwdPPfUULl26FJgJBJCvtYmNjcXzzz+PsLCpfx203jve6jNOi72zZcsWvPjiix7LWltb\nsXjxYgDsHW/1GafF3snNzcWaNWsA3HwK3dWrV3H27Fls27YNwOx7J2TDvbe3FwAQH+/5jvaEhAT0\n9PRMOX78OsXEsQDQ09PjXn/rdZ34+Pgptxfq/F0fADAajTAajaiqqkJ1dTXS09Oxa9cutLW1BWIK\nAeNrbaazPS33znSwd26qqqpCTU0NXn75Zff22Dt/ubU+AHvn008/haIoKCwsxJNPPondu3e7tzeb\n3gnZcL+dWyc72/G+bi/UzbQ+p06dwp49exATE4OEhAS88847WLBgAU6fPh2I3QwKf/9ba713xrF3\ngLKyMrz33nv46KOPJp0lm8n27jQzrY/We+ell15CfX09Kisrcfr0aRw8eHBW2xsXsuGemJgIALBa\nrR7L+/r6kJSUNGl8UlLSlGMBIDk5GYmJibDZbJBbHqVvtVqn3F6o83d9phIREYElS5agq6vLH7s8\nZ3ytzXS2p+XemQkt9c7Y2BjefvttVFZWorKyEhs2bPDYntZ753b1mYqWemdcREQEVq9ejX379uHE\niRPo7++fde+EbLgrigK9Xo/a2lqP5VeuXEFOTs6k8dnZ2ZOuQ4x/lCIrKwvZ2dlwOp0e15dHRkZQ\nX18/5fZCnb/r09DQgKKiIoyNjbnXj4yMoLW11afXDIYCX2vjjdZ7xxut986BAwdQV1eHkydPTjpi\nZ+/cvj5a7p2CggKUlpZ6LBsZGQEAhIeHz7p3QjbcDQYDtm/fDrPZDIvFArvdjvLycrS3t8NkMuHa\ntWvIz89HR0cHAMBkMqG1tRUVFRVwOBxobm6G2WzGjh07YDAYkJqairy8PBw+fBhdXV0YGBjABx98\ngKioKGzevDnIs/Wdv+uTmJiIU6dOoaSkBAMDA7DZbCgqKgIA9w0edwpfa+ON1nvHGy33TnV1Nc6e\nPYvy8nIsWrRo0va03jve6qPl3lm7di3Ky8tx8eJFuFwuWCwWlJaWIi8vD9HR0bPvHV9v959Lw8PD\ncvDgQVm/fr1kZWXJzp075dKlSyIicv78eUlLS5OWlhb3+AsXLsgTTzwhiqLIfffdJ++//74MDw+7\n19tsNnnttdckJydH7r33XiksLJRff/11zuflL/6uz9WrV6WgoEDWrFkjq1evll27dkljY+Ocz8sf\nfKlNVVWVKIoiiqJIenq6ZGRkuL9va2sTEW33znTqo9Xeee655zzqMfEPe2d69dFq74yOjkppaak8\n+OCDoiiKPPDAA3LgwAHp6+tzb282vcP3uRMREalMyJ6WJyIioplhuBMREakMw52IiEhlGO5EREQq\nw3AnIiJSGYY7ERGRyjDciYiIVIbhTkREpDIMdyIiIpX5H+2TR580F6EhAAAAAElFTkSuQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(system.results.x1)" - ] - }, - { - "cell_type": "code", - "execution_count": 406, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\nQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(system.results.x2)" - ] - }, - { - "cell_type": "code", - "execution_count": 407, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAf8AAAFOCAYAAACIZxRuAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzsvXusncV1N/zbx8bAC+YSoCUEFEFS0YiLQnFEpSSqipoK\nvc0/iKRxLkip+KMkEkmlqFVD3TYiqFVcJWpkqer7BfMl5CsvJG9MDM2bpJCQQMytXGywCQbjCzYG\ng69gY+Nzzn6+P/bZe8+sy2/NPHu7kbrPSHC29zwzs2ZmzVq/dZlnd5qmaTBf5st8mS/zZb7Ml4kp\nU79pAubLfJkv82W+zJf58l9b5pX/fJkv82W+zJf5MmFlXvnPl/kyX+bLfJkvE1bmlf98mS/zZb7M\nl/kyYWVe+c+X+TJf5st8mS8TVhb+pgkYZzly5AjWr1+Ps846CwsWLPhNkzNf5st8mS/zZb4c0zI7\nO4vXX38dF198MU444YTidv+tlP/69evx6U9/+jdNxnyZL/NlvsyX+fJfWv7t3/4NS5YsKX7+v5Xy\nP+usswD0FuHss8/+DVMzX+bLfJkv82W+HNvy6quv4tOf/vRA/5WW/1bKv+/qP/vss3Huuef+hqmZ\nL/NlvsyX+TJf/mtKbah7PuFvvsyX+TJf5st8mbBSpPwPHz6Mr3zlK7jyyitx+eWX4xOf+ATWrFnj\nPr9mzRosXboUS5YswR/+4R/i7/7u73D48OFB/YUXXoiLL74Yl1xySfbf0aNHVV/79+/Hhz70IVx7\n7bUtpte+TM/M4v4ntuP+J7ZjemZW1W/YvAc/fWQb9hw4bLSOi/dW5S07D+B79z2PJze+pupmZrt4\nZtNuPP/SPrc9K/vePII339Jr3DQNfv74dtx570a8uueQqp/tNtj5+kHMzHarxzxw8G08/utd5jrt\ne/MIVt2/Cfc9tg3drj2fNvNsmgaP/3oXfv74SzhozPfFHfvx77/ajG2vvlHdNyt7DhzGD3+5CQ+u\nfVnR3TQN1r+4G4//epfJT1GZnpnFocPTZt2Tz72G//3T57Bpx35VN9ttsGXnAXMdonJ0ehbPbtmD\n1/fpvTtydAb3PLgZdz/wIt46oulqmqYVvwDAph37cf8T27H3jSOqbufrB7H6gRfxzKbdrfr2yuG3\nZ/CTh7fivse2mfuzcdtePPDUDvP8RKVpGhydtvd80/b9uOPejVj3/OuqbrbbYNOO/Xhltz6TUel2\n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\n1qzJPvvZz2arVq3K/vZv/9br08y8E7I+JxLvZFnc+vzyl7/M5s2bl/385z/Pent7s9/85jfZihUr\nsnXr1lX6nEj8U++1aWbe+f73v58tX74827JlS9bf359t27Yt+9jHPpZdddVVlT55eed9q/wPHDiQ\nzZs3L9u0aZP1/Sc/+UmLccrwjW98I/vEJz5hfbdp06bsd37nd7K9e/dmL7zwQjZr1qzsv//7vyvt\nvb292dKlS7M77rijMQ/RQKj3+mTZiXMIY9fm4MGD2fe///2sv78/W716tafcmp13tPXJshOHd7Is\nfn0eeOCB7Hvf+5713Y033phdfPHFWZadWPxT77XJsubmnV//+tfZY489Zn23bt267OMf/3iWZbXx\nzvs2579161bq7e2l+fPnW98vWLCAtmzZ4vXfvHkzLViwwOvb19dHW7dupS1btlBbWxvNmTOn0t7a\n2krz5s1j8b3Xod7rU4Z/+qd/ovPOO4+WLFlCn/70p+mJJ55ozAM0EGLXZtSoUfRnf/ZnVCzyx6XZ\neUdbnzKcCLxDFL8+F198MV1xxRXWdzt37qRJkyYR0YnFP/VemzI0K+8sX76czjjjDCIqvcnv6aef\npl/84hd0ySWXEFFtvPO+Vf779u0jIqIxY8ZY33d2dtLevXvZ/uUcidmXiGjv3r2VdjevNGbMGBbf\nex3qvT5ERPPmzaN58+bRfffdR5s2baLZs2fT5z//edq1a1cjHqFhELs2IfiamXdC4EThHaLa1+e+\n++6jX//61/QXf/EXFXwnCv/Ue22IEu8QEf3jP/4jdXd30+WXX06XXXYZ/emf/mkFX17eed8qfwTu\nQtTaPxbfex3yrs+9995LX/jCF2jEiBHU2dlJ11xzDQ0fPpzuv//+RpB5XKDee93svFOGZuAdIn19\nNmzYQGvXrqVbb73Vi7Tlwfd+grxrk3iH6Morr6Rnn32W7rzzTrr//vvphhtuqAkf0ftY+Y8bN46I\niA4cOGB9v3//furq6vL6d3V1sX2JiMaPH0/jxo2jgwcPUub81MGBAwdYfO91qPf6cNDa2kqTJ0+m\nN954ox4kHzOIXZsQfM3MO3ng/co7RPnWZ2BggP7+7/+e7rzzTrrzzjvp3HPPtfCdKPxT77XhoNl4\npwytra10+umn09VXX0133303vf322zXxzvtW+Xd3d1N7eztt3rzZ+v6pp56iJUuWeP0XLVrk5UDK\nV0Xmz59PixYtot7eXiu/ffToUXr22WdZfO91qPf6bN26lW688UYaGBiotB89epR27twZ9TOS7wWI\nXRsNmp13NDiReIco3/pce+21tGXLFvrpT3/qefwnEv/Ue22anXfWrFlDt912m/Xd0aNHiYiopaWl\nJt553yr/kSNH0qc+9Slav3497dixg3p6emjjxo306quv0urVq+mZZ56hVatW0WuvvUZERKtXr6ad\nO3fSHXfcQYcPH6bt27fT+vXr6dJLL6WRI0fSzJkzacWKFfTNb36T3njjDTp06BB9+9vfpo6ODrro\noouO89PGQ73XZ9y4cXTvvffSzTffTIcOHaKDBw/SjTfeSERUKT55v0Ds2mjQ7LyjwYnEO0Tx67Np\n0yb6xS9+QRs3bqSJEyd6+E4k/qn32jQ775x55pm0ceNGevzxx6m/v5927NhBt912G61YsYKGDRtW\nG+/kua7wXoEjR45kN9xwQ7Zs2bJs/vz52R/8wR9kTzzxRJZlWfboo49ms2bNyl5++eVK/8ceeyz7\n/d///ay7uzs766yzsptuuik7cuRIpf3gwYPZV77ylWzJkiXZwoULs8svvzx78cUXj/lz1QvqvT5P\nP/10tmbNmuyMM87ITj/99Ozzn/989tJLLx3z56oHxKzNfffdl3V3d2fd3d3Z7Nmzszlz5lT+3rVr\nV5Zlzc07IetzIvFOlsWtzx/90R9Za2L+dyLyT73Xppl5p6+vL7vtttuyc845J+vu7s7OPvvs7Npr\nr832799fwZeXdwpZ5iQLEiRIkCBBggQnNLxvw/4JEiRIkCBBgnyQlH+CBAkSJEjQZJCUf4IECRIk\nSNBkkJR/ggQJEiRI0GSQlH+CBAkSJEjQZJCUf4IECRIkSNBkkJR/ggQJEiRI0GSQlH+CBAkSJEjQ\nZJCUf4IECRIkSNBk8P8BwAYqZsPJv20AAAAASUVORK5CYII=\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(system.results.x1 - system.results.x2)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "plot(ys, color='green', label='y')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Length (m)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `r`" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "plot(rs, color='red', label='r')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Radius (mm)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also see the relationship between `y` and `r`, which I derive analytically in the book." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "plot(rs, ys, color='purple')\n", - "\n", - "decorate(xlabel='Radius (mm)',\n", - " ylabel='Length (m)',\n", - " legend=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the figure from the book." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "subplot(3, 1, 1)\n", - "plot(thetas, label='theta')\n", - "decorate(ylabel='Angle (rad)')\n", - "\n", - "subplot(3, 1, 2)\n", - "plot(ys, color='green', label='y')\n", - "decorate(ylabel='Length (m)')\n", - "\n", - "subplot(3, 1, 3)\n", - "plot(rs, color='red', label='r')\n", - "\n", - "decorate(xlabel='Time(s)',\n", - " ylabel='Radius (mm)')\n", - "\n", - "savefig('chap11-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use interpolation to find the time when `y` is 47 meters." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "T = interp_inverse(ys, kind='cubic')\n", - "t_end = T(47)\n", - "t_end" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "At that point `r` is 55 mm, which is `Rmax`, as expected." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "R = interpolate(rs, kind='cubic')\n", - "R(t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The total amount of rotation is 1253 rad." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "THETA = interpolate(thetas, kind='cubic')\n", - "THETA(t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Unrolling" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For unrolling the paper, we need more units:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "kg = UNITS.kilogram\n", - "N = UNITS.newton" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And a few more parameters in the `Condition` object." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "condition = Condition(Rmin = 0.02 * m,\n", - " Rmax = 0.055 * m,\n", - " Mcore = 15e-3 * kg,\n", - " Mroll = 215e-3 * kg,\n", - " L = 47 * m,\n", - " tension = 2e-4 * N,\n", - " duration = 180 * s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`make_system` computes `rho_h`, which we'll need to compute moment of inertia, and `k`, which we'll use to compute `r`." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(condition):\n", - " \"\"\"Make a system object.\n", - " \n", - " condition: Condition with Rmin, Rmax, Mcore, Mroll,\n", - " L, tension, and duration\n", - " \n", - " returns: System with init, k, rho_h, Rmin, Rmax,\n", - " Mcore, Mroll, ts\n", - " \"\"\"\n", - " unpack(condition)\n", - " \n", - " init = State(theta = 0 * radian,\n", - " omega = 0 * radian/s,\n", - " y = L)\n", - " \n", - " area = pi * (Rmax**2 - Rmin**2)\n", - " rho_h = Mroll / area\n", - " k = (Rmax**2 - Rmin**2) / 2 / L / radian \n", - " ts = linspace(0, duration, 101)\n", - " \n", - " return System(init=init, k=k, rho_h=rho_h,\n", - " Rmin=Rmin, Rmax=Rmax,\n", - " Mcore=Mcore, Mroll=Mroll, \n", - " ts=ts)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `make_system`" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "system = make_system(condition)\n", - "system" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "system.init" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we compute `I` as a function of `r`:" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def moment_of_inertia(r, system):\n", - " \"\"\"Moment of inertia for a roll of toilet paper.\n", - " \n", - " r: current radius of roll in meters\n", - " system: System object with Mcore, rho, Rmin, Rmax\n", - " \n", - " returns: moment of inertia in kg m**2\n", - " \"\"\"\n", - " unpack(system)\n", - " Icore = Mcore * Rmin**2 \n", - " Iroll = pi * rho_h / 2 * (r**4 - Rmin**4)\n", - " return Icore + Iroll" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When `r` is `Rmin`, `I` is small." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "moment_of_inertia(system.Rmin, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As `r` increases, so does `I`." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "moment_of_inertia(system.Rmax, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the slope function." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object with theta, omega, y\n", - " t: time\n", - " system: System object with Rmin, k, Mcore, rho_h, tension\n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " theta, omega, y = state\n", - " unpack(system)\n", - " \n", - " r = sqrt(2*k*y + Rmin**2)\n", - " I = moment_of_inertia(r, system)\n", - " tau = r * tension\n", - " alpha = tau / I\n", - " dydt = -r * omega\n", - " \n", - " return omega, alpha, dydt " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `slope_func`" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "slope_func(system.init, 0*s, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "run_odeint(system, slope_func)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And look at the results." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "system.results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Extrating the time series" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "thetas = system.results.theta\n", - "omegas = system.results.omega\n", - "ys = system.results.y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `theta`" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "plot(thetas, label='theta')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Angle (rad)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `omega`" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "plot(omegas, color='orange', label='omega')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Angular velocity (rad/s)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `y`" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "plot(ys, color='green', label='y')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Length (m)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the figure from the book." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "subplot(3, 1, 1)\n", - "plot(thetas, label='theta')\n", - "decorate(ylabel='Angle (rad)')\n", - "\n", - "subplot(3, 1, 2)\n", - "plot(omegas, color='orange', label='omega')\n", - "decorate(ylabel='Angular velocity (rad/s)')\n", - "\n", - "subplot(3, 1, 3)\n", - "plot(ys, color='green', label='y')\n", - "\n", - "decorate(xlabel='Time(s)',\n", - " ylabel='Length (m)')\n", - "\n", - "savefig('chap11-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Yo-yo" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Simulate the descent of a yo-yo. How long does it take to reach the end of the string.\n", - "\n", - "I provide a `Condition` object with the system parameters:\n", - "\n", - "* `Rmin` is the radius of the axle. `Rmax` is the radius of the axle plus rolled string.\n", - "\n", - "* `Rout` is the radius of the yo-yo body. `mass` is the total mass of the yo-yo, ignoring the string. \n", - "\n", - "* `L` is the length of the string.\n", - "\n", - "* `g` is the acceleration of gravity." - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "condition = Condition(Rmin = 8e-3 * m,\n", - " Rmax = 16e-3 * m,\n", - " Rout = 35e-3 * m,\n", - " mass = 50e-3 * kg,\n", - " L = 1 * m,\n", - " g = 9.8 * m / s**2,\n", - " duration = 1 * s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a `make_system` function that computes `I` and `k` based on the system parameters.\n", - "\n", - "I estimated `I` by modeling the yo-yo as a solid cylinder with uniform density ([see here](https://en.wikipedia.org/wiki/List_of_moments_of_inertia)). In reality, the distribution of weight in a yo-yo is often designed to achieve desired effects. But we'll keep it simple." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(condition):\n", - " \"\"\"Make a system object.\n", - " \n", - " condition: Condition with Rmin, Rmax, Rout, \n", - " mass, L, g, duration\n", - " \n", - " returns: System with init, k, Rmin, Rmax, mass,\n", - " I, g, ts\n", - " \"\"\"\n", - " unpack(condition)\n", - " \n", - " init = State(theta = 0 * radian,\n", - " omega = 0 * radian/s,\n", - " y = L,\n", - " v = 0 * m / s)\n", - " \n", - " I = mass * Rout**2 / 2\n", - " k = (Rmax**2 - Rmin**2) / 2 / L / radian \n", - " ts = linspace(0, duration, 101)\n", - " \n", - " return System(init=init, k=k,\n", - " Rmin=Rmin, Rmax=Rmax,\n", - " mass=mass, I=I, g=g,\n", - " ts=ts)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `make_system`" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "system = make_system(condition)\n", - "system" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "system.init" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Write a slope function for this system, using these results from the book:\n", - "\n", - "$ r = \\sqrt{2 k y + R_{min}^2} $ \n", - "\n", - "$ T = m g I / I^* $\n", - "\n", - "$ a = -m g r^2 / I^* $\n", - "\n", - "$ \\alpha = m g r / I^* $\n", - "\n", - "where $I^*$ is the augmented moment of inertia, $I + m r^2$.\n", - "\n", - "Hint: If `y` is less than 0, it means you have reached the end of the string, so the equation for `r` is no longer valid. In this case, the simplest thing to do it return the sequence of derivatives `0, 0, 0, 0`" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "slope_func(system.init, 0*s, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": { - "collapsed": true, - "scrolled": false - }, - "outputs": [], - "source": [ - "run_odeint(system, slope_func)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Check the final conditions. If things have gone according to plan, the final value of `y` should be close to 0." - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "system.results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "thetas = system.results.theta\n", - "ys = system.results.y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`theta` should increase and accelerate." - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "plot(thetas, label='theta')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Angle (rad)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`y` should decrease and accelerate down." - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "plot(ys, color='green', label='y')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Length (m)')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.1" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/code/insulin.ipynb b/code/insulin.ipynb deleted file mode 100644 index 32dcd3d48..000000000 --- a/code/insulin.ipynb +++ /dev/null @@ -1,346 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Insulin minimal model\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Data\n", - "\n", - "We have data from Pacini and Bergman (1986), \"MINMOD: a computer program to calculate insulin sensitivity and pancreatic responsivity from the frequently sampled intravenous glucose tolerance test\", *Computer Methods and Programs in Biomedicine*, 23: 113-122.." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "data = pd.read_csv('data/glucose_insulin.csv', index_col='time');" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The return value from `interpolate` is a function." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The insulin minimal model\n", - "\n", - "In addition to the glucose minimal mode, Pacini and Bergman present an insulin minimal model, in which the concentration of insulin, $I$, is governed by this differential equation:\n", - "\n", - "$ \\frac{dI}{dt} = -k I(t) + \\gamma (G(t) - G_T) t $" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Write a version of `make_system` that takes the parameters of this model, `I0`, `k`, `gamma`, and `G_T` as parameters, along with a `DataFrame` containing the measurements, and returns a `System` object suitable for use with `run_simulation` or `run_odeint`.\n", - "\n", - "Use it to make a `System` object with the following parameters:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "params = Params(I0 = 360,\n", - " k = 0.25,\n", - " gamma = 0.004,\n", - " G_T = 80)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Write a slope function that takes state, t, system as parameters and returns the derivative of `I` with respect to time. Test your function with the initial condition $I(0)=360$." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Run `run_ode_solver` with your `System` object and slope function, and plot the results, along with the measured insulin levels." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Write an error function that takes a sequence of parameters as an argument, along with the `DataFrame` containing the measurements. It should make a `System` object with the given parameters, run it, and compute the difference between the results of the simulation and the measured values. Test your error function by calling it with the parameters from the previous exercise.\n", - "\n", - "Hint: As we did in a previous exercise, you might want to drop the errors for times prior to `t=8`." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Use `fit_leastsq` to find the parameters that best fit the data. Make a `System` object with those parameters, run it, and plot the results along with the measurements." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Using the best parameters, estimate the sensitivity to glucose of the first and second phase pancreatic responsivity:\n", - "\n", - "$ \\phi_1 = \\frac{I_{max} - I_b}{k (G_0 - G_b)} $\n", - "\n", - "$ \\phi_2 = \\gamma \\times 10^4 $\n", - "\n", - "For $G_0$, use the best estimate from the glucose model, 290. For $G_b$ and $I_b$, use the inital measurements from the data.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/kitten.ipynb b/code/kitten.ipynb deleted file mode 100644 index fb5033ce4..000000000 --- a/code/kitten.ipynb +++ /dev/null @@ -1,407 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Case study.\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Unrolling\n", - "\n", - "Let's simulate a kitten unrolling toilet paper. As reference material, see [this video](http://modsimpy.com/kitten).\n", - "\n", - "The interactions of the kitten and the paper roll are complex. To keep things simple, let's assume that the kitten pulls down on the free end of the roll with constant force. Also, we will neglect the friction between the roll and the axle. \n", - "\n", - "![](diagrams/kitten.png)\n", - "\n", - "This figure shows the paper roll with $r$, $F$, and $\\tau$. As a vector quantity, the direction of $\\tau$ is into the page, but we only care about its magnitude for now." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll start by loading the units we need." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "radian = UNITS.radian\n", - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram\n", - "N = UNITS.newton" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And a few more parameters in the `Params` object." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "params = Params(Rmin = 0.02 * m,\n", - " Rmax = 0.055 * m,\n", - " Mcore = 15e-3 * kg,\n", - " Mroll = 215e-3 * kg,\n", - " L = 47 * m,\n", - " tension = 2e-4 * N,\n", - " t_end = 120 * s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`make_system` computes `rho_h`, which we'll need to compute moment of inertia, and `k`, which we'll use to compute `r`." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Make a system object.\n", - " \n", - " params: Params with Rmin, Rmax, Mcore, Mroll,\n", - " L, tension, and t_end\n", - " \n", - " returns: System with init, k, rho_h, Rmin, Rmax,\n", - " Mcore, Mroll, ts\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " init = State(theta = 0 * radian,\n", - " omega = 0 * radian/s,\n", - " y = L)\n", - " \n", - " area = pi * (Rmax**2 - Rmin**2)\n", - " rho_h = Mroll / area\n", - " k = (Rmax**2 - Rmin**2) / 2 / L / radian \n", - " \n", - " return System(init=init, k=k, rho_h=rho_h,\n", - " Rmin=Rmin, Rmax=Rmax,\n", - " Mcore=Mcore, Mroll=Mroll, \n", - " t_end=t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `make_system`" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "system = make_system(params)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "system.init" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we compute `I` as a function of `r`:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def moment_of_inertia(r, system):\n", - " \"\"\"Moment of inertia for a roll of toilet paper.\n", - " \n", - " r: current radius of roll in meters\n", - " system: System object with Mcore, rho, Rmin, Rmax\n", - " \n", - " returns: moment of inertia in kg m**2\n", - " \"\"\"\n", - " unpack(system)\n", - " Icore = Mcore * Rmin**2 \n", - " Iroll = pi * rho_h / 2 * (r**4 - Rmin**4)\n", - " return Icore + Iroll" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When `r` is `Rmin`, `I` is small." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "moment_of_inertia(system.Rmin, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As `r` increases, so does `I`." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "moment_of_inertia(system.Rmax, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "Write a slope function we can use to simulate this system. Here are some suggestions and hints:\n", - "\n", - "* `r` is no longer part of the `State` object. Instead, we compute `r` at each time step, based on the current value of `y`, using\n", - "\n", - "$y = \\frac{1}{2k} (r^2 - R_{min}^2)$\n", - "\n", - "* Angular velocity, `omega`, is no longer constant. Instead, we compute torque, `tau`, and angular acceleration, `alpha`, at each time step.\n", - "\n", - "* I changed the definition of `theta` so positive values correspond to clockwise rotation, so `dydt = -r * omega`; that is, positive values of `omega` yield decreasing values of `y`, the amount of paper still on the roll.\n", - "\n", - "* Your slope function should return `omega`, `alpha`, and `dydt`, which are the derivatives of `theta`, `omega`, and `y`, respectively.\n", - "\n", - "* Because `r` changes over time, we have to compute moment of inertia, `I`, at each time step.\n", - "\n", - "That last point might be more of a problem than I have made it seem. In the same way that $F = m a$ only applies when $m$ is constant, $\\tau = I \\alpha$ only applies when $I$ is constant. When $I$ varies, we usually have to use a more general version of Newton's law. However, I believe that in this example, mass and moment of inertia vary together in a way that makes the simple approach work out. Not all of my collegues are convinced." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test `slope_func` with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And look at the results." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Check the results to see if they seem plausible:\n", - "\n", - "* The final value of `theta` should be about 220 radians.\n", - "\n", - "* The final value of `omega` should be near 4 radians/second, which is less one revolution per second, so that seems plausible.\n", - "\n", - "* The final value of `y` should be about 35 meters of paper left on the roll, which means the kitten pulls off 12 meters in two minutes. That doesn't seem impossible, although it is based on a level of consistency and focus that is unlikely in a kitten.\n", - "\n", - "* Angular velocity, `omega`, should increase almost linearly at first, as constant force yields almost constant torque. Then, as the radius decreases, the lever arm decreases, yielding lower torque, but moment of inertia decreases even more, yielding higher angular acceleration." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot `theta`" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_theta(results):\n", - " plot(results.theta, color='C0', label='theta')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Angle (rad)')\n", - " \n", - "plot_theta(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot `omega`" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_omega(results):\n", - " plot(results.omega, color='C2', label='omega')\n", - "\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Angular velocity (rad/s)')\n", - " \n", - "plot_omega(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot `y`" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_y(results):\n", - " plot(results.y, color='C1', label='y')\n", - "\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Length (m)')\n", - " \n", - "plot_y(results)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/lotka-volterra.ipynb b/code/lotka-volterra.ipynb deleted file mode 100644 index f584ca2d9..000000000 --- a/code/lotka-volterra.ipynb +++ /dev/null @@ -1,944 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Implementation of Lotka-Volterra using Euler and `run_ode_solver`\n", - "\n", - "Copyright 2018 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Euler with implicit `dt=1`" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \n", - " returns: TimeFrame\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " frame = TimeFrame(columns=init.index)\n", - " frame.row[t0] = init\n", - " \n", - " for t in linrange(t0, t_end):\n", - " frame.row[t+1] = update_func(frame.row[t], t, system)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def update_func(state, t, system):\n", - " \"\"\"Update the Lotka-Volterra model.\n", - " \n", - " state: State(x, y)\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: State(x, y)\n", - " \"\"\"\n", - " unpack(system)\n", - " x, y = state\n", - "\n", - " dxdt = alpha * x - beta * x * y\n", - " dydt = delta * x * y - gamma * y\n", - " \n", - " x += dxdt\n", - " y += dydt\n", - " \n", - " return State(x=x, y=y)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
x1
y1
\n", - "
" - ], - "text/plain": [ - "x 1\n", - "y 1\n", - "dtype: int64" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "init = State(x=1, y=1)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
alpha0.05
beta0.10
gamma0.10
delta0.10
t00.00
t_end200.00
\n", - "
" - ], - "text/plain": [ - "alpha 0.05\n", - "beta 0.10\n", - "gamma 0.10\n", - "delta 0.10\n", - "t0 0.00\n", - "t_end 200.00\n", - "dtype: float64" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = System(alpha=0.05,\n", - " beta=0.1,\n", - " gamma=0.1,\n", - " delta=0.1,\n", - " t0=0,\n", - " t_end=200)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
x0.95
y1.00
\n", - "
" - ], - "text/plain": [ - "x 0.95\n", - "y 1.00\n", - "dtype: float64" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "update_func(init, 0, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
xy
0.011
1.00.951
2.00.90250.995
3.00.8578260.985299
4.00.8161960.97129
\n", - "
" - ], - "text/plain": [ - " x y\n", - "0.0 1 1\n", - "1.0 0.95 1\n", - "2.0 0.9025 0.995\n", - "3.0 0.857826 0.985299\n", - "4.0 0.816196 0.97129" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results = run_simulation(system, update_func)\n", - "results.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "results.plot()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using the ODE solver" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Compute slopes for the Lotka-Volterra model.\n", - " \n", - " state: State(x, y)\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: pair of derivatives\n", - " \"\"\"\n", - " unpack(system)\n", - " x, y = state\n", - "\n", - " dxdt = alpha * x - beta * x * y\n", - " dydt = delta * x * y - gamma * y\n", - " \n", - " return dxdt, dydt" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "system.set(init=init, t_end=200)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[]
nfev134
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events []\n", - "nfev 134\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results, details = run_ode_solver(system, slope_func)\n", - "details" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYEAAAEFCAYAAAAFeFvqAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzsvXlUXOeVr/2cmoBinichEAi9SAIJzbM1WbYc27I8pWM7k+PEuTdJd276dt/cdNLpdBIn6dzuvunvy5eO407sTuzEnTge5UG2EJasWSAJgQRHCIHEjJgLqiio4fujQJagJBVQVaeG86zFWubUOXV+yFD7vO/ev70lp9OJioqKikp4olFagIqKioqKcqhBQEVFRSWMUYOAioqKShijBgEVFRWVMEYNAioqKiphjBoEVFRUVMIYNQioqKiohDFqEFBRUVEJY9QgoKKiohLGqEFARUVFJYxRg4CKiopKGKNTWoA7hBARwCqgHbArLEdFRUUlWNACmcBJWZatnlwQkEEAVwD4SGkRKioqKkHKJuCQJycGahBoB3jppZfIyMhQWouKiopKUNDR0cETTzwB45+hnhCoQcAOkJGRwZw5c5TWoqKiohJseLyNriaGVVRUVMIYNQioqKiohDFqEFBRUVEJYwI1J3BLBgcH6erqYmxsTGkpXkOv15OWlkZcXJzSUlRUVMKIoAsCg4ODdHZ2kp2dTVRUFJIkKS1p1jidTiwWC62trQBqIFBRUfEbQbcd1NXVRXZ2NkajMSQCAIAkSRiNRrKzs+nq6lJajoqKShgRdCuBsbExoqKilJbhE6KiokJqi2smOJ1O/u2/TiMh8fVPLVNajkoA88r+el7+QCYtMYqM5Ggyk6PJTIl2/XdKNGmJRvS6oHvO9TtBFwSAkFkBTCZUf67pcPJ8J2UnmwG4d8M85uckKKxIJRCxO5zsOXSJtMQoslNj6Ogxc/ZiN9bRj8vjNRLkpMfyk69tIiZKr6DawCYog4BKaDI6Zue5N6rJTo2h3zTCn/Zf4FufW620LJUApPriVXoGRvjiAyvZuDQbcK0i+0xW2ruH6egZpr65n7cPN3K2/irrl2QprDhwUddKKgHD6wca6Ogx8+UHS7h3Yz5Hq9tp7jQpLUslACmvbCE6UsfqRR+3lZEkiaS4SBbnJ7N91Vye2lWMQa+l5lKPgkoDH49WAkKIh4CvAiuBOFmWb7lvIYTQAX8PPAmkA5eBr8qy/MHs5KqEKlf7LPyx7ALrSjJZJtLIz47njYMNvLK/nm88tlxpeSoBhMVq48jZNjYvn4NBr73peXqdhqLcRGoauv2oLvjwdCVgBPYDP/Hw/GeBB4CnAAF8kWk0NFIJP57fcw6nw8lTu4oBiI+J4O61uXx4qoXOXrPC6lQCiaPV7YyM2tm6Iue25xYXpNDUPsiQedQPyoITj4KALMsvyrL8DHD0ducKIUqAzwIPyrL8gSzLTbIsH5RluWaWWoOOzs5OVq1axZ49e64d+/nPf85dd92FxWJRUFlgUd3QzUdnWnl4WyHpScZrxx/cPB+NBK+W1yuoTiXQKK9oJj3JyKJ5Sbc9t6QgGacTzqlbQjfFFzmBe4GLwKeEEM1CiDohxHeEEDdft4Uo6enpfPvb3+YHP/gBV69e5fz58zz77LP86Ec/Ctky1+litzv41WvVpCVG8fC2whteS0mIYtvKuXxw4gp9gyMKKVQJJLr7LVRdvMq2lTkeVdMtmJuIXqdR8wK3wBfVQXlAPnAX8AiQhWt7aBT4qQ/ux/6KK3xw4oov3noKO1bPZdvKuR6fv3v3bvbu3cvf//3f09rayuOPP87KlSt9qDC4ePdoE03tg/zvz60iws3+7sPb5rPvxGVeP9DAk/cv9r9AlYDiwKkWnE7YssKzFvMGvRah5gVuiS9WAhrAAHxeluXjsiy/BjyDKz8Qlnz/+9/n+PHjWK1WvvGNbygtJ2AYGLLy4nt1LC1MYX1JpttzslJi2FiazbtHGzGp+7phjdPppKyimYV5SWSlxHh8XXF+CpdaBxi2hLcR82b4YiXQCVhlWb583TEZuH0WZ4ZsWzm9p3N/U1dXx+joKP39/QwODhIZGam0pIDgd+/WMmK18fTuklsu7R/dvoCDp1vZc6iRx+4SflSoEkg0tA7Q3GniK48sndZ1xQXJvPwBnG/sYdUidVLhZHyxEjgGRAghrl+vzQf8s18TYJhMJr7zne/wN3/zN6xYsYLvfve7SksKCC429/P+8cvctzGfuRm3bpiXlxnHmsUZvPVRAxarzU8KVQKN8opmdFoNm5ZOz/glchPRaSVqGtS8gDs8CgJCiCQhRCmuD3OEEKXjXwYhxOrx5G/2+Ol7gVrgOSHEYiHEncC3gF/54gcIdH70ox+RmZnJ5z73Ob7//e9z+vRpXn/9daVlKYrD4eTZ184SHx3h8ZP9I9sLMZnHeO9ok0+1qQQmNruDA6dbWLM4gxijYVrXRhp0FOYkUnNJzQu4w9OVwC7gNPDc+Penx7+ycHkIBKAHkGXZhqtCSAJOjl/zC+DfvKY6SDhw4ADvvPMOP/7xj9FoNKSmpvKd73yHZ555hs7OTqXlKcaHp5qpu9zH5+5dSLSHPV2KcpNYMj+F1w9cZHTM4/GpKiHCabmLgaFRtnqYEJ5McUEyF1sGMI+oeYHJeJQTkGX5BeCFm7zchOsD//rzG4Gds9AVEmzevJmqqqobjt1///3cf//9CilSHvPIGC/sOY+YmzjtPM4nty/gO88eoayimXvW5flGoEpAsr+imbhoA8uL0md0fXFBCn8qq6euqY/lRWleVhfcqL2DVPzKyx9coM9k5ekHS9Boptc1dUlhCmJuIn/eX4/d7vCRQpVAY8gyxvFzHdyxLHvGraEX5iWh0UjqlpAb1CCg4jeaO028ebCBHavnsmBu4rSvlySJR7cX0tlr5uCZVh8oVAlEDle1MmZzsG3lzAsMoyJ0FOYkqMlhN6hBQMUvOJ1Onnu9mkiDls9+YtGM32fVogxyM2L5U1k9DofTiwpVApXyyhbmpMUwf87sZksU5ydT39zHyKhaYXY9ahBQ8Qs1DT2cvnCVx+4uIiE2Ysbvo9FIPLJ9Ac2dJo6f6/CiQpVApKNnmHOXejxuE3ErigtSsNmdyE19XlIXGqhBQMUvfHDiMsZIHTu9kNDdtDSLzORo/lh2AadTXQ2EMuWVLUgSbF4+s6qg61k0LwmN5GpYqPIxahBQ8TnmkTEOn21nU2m22/5A00Wr1fDwtvlcbO7nzIWrXlCoEog4nU7KK5opKUghLdF4+wtugzFST/6cBLWZ3CTUIKDicw5XtTE6ZufOVd5r7bFtZQ7J8ZH8qUxtMx2q1DX10d4zPKuE8GSK85ORL/dhVb0m11CDgIrPKatoJjs1GpE7/Yqgm6HXaXlwy3yqG7qpbez12vuqBA7llc0Y9FrW3aS54EwoKUjBZndw4bKaF5hADQIqPqWte4hzl3rYvmrurBN7k7l7TS6xRgN/LLvg1fdVUZ4xm52DZ1pZX5KJMdIzV7knLMpPRpJQW0tfhxoEVHzK/opmJAmPRgFOl8gIHQ/ckU9FbSeXWge8/v4qynHifCfDljG2enErCCAmSs+8zHg1L3AdahBQ8RkOh5P9Fc2UFqaSkuCbSWr3bphHVISOV/aruYFQoryimaS4CJYWpnr9vYvnJ1PX1MuYTc0LgBoEVHxIdUM3V/ssbPdiQngyMUYD926Yx6GqVlqvDvnsPir+Y2DISkVtJ1uW56CdZmsRTyjOT2HU5uDClX6vv3cwogYBH/Lss8/y6KOP3nCsp6eHxYsXU1tbq5Aq/1F28grGSB1rvZjYc8euO/LRazX8WV0NhAQfnWnF7nB6fStogsX5yQBqH6Fx1CDgQ3bv3k1NTQ2NjY3Xju3Zs4f8/HwWLlyooDLfYx4Z40i197wBtyIxNpK71uSyv6KZrj6zT++l4nv2VzSTnxVPXuathw3NlLhoA3mZcdRcVPMC4Jvxkn7HdPZDTFX7/XKv2KXbiF2yxaNz09PTWbduHW+++SZf//rXAXjzzTfZtWuXDxUGBkfOtmEd9a434FY8uHU+7x5t4vUDDTy9u8Qv91TxPs2dJuqb+3lqV7FP71Ocn8wHJ69gszvQacP7WTi8f3o/sHv3bt566y0ALl26xPnz58MiCOw76X1vwK1ISzSydUUOe49dpt9k9cs9VbxPeWUzGo3E5mXZtz95FhQXpGAdtXOxWc0LeLQSEEI8BHwVWAnEybLsUbZGCLEC18zhw7Isb5mpyNsRu2SLx0/n/mbHjh1873vf49SpUxw8eJC1a9eSnj6zwRjBQnu3q+nXZz+x0OvegFvx8Lb5lFVc4c2PGmbVqVRFGRwOJ+WVLSwXaSTGRfr0XhN5geqGboryknx6r0DH05WAEdgP/MTTNxZCROCaRnZg+rJCh6ioKHbu3Mmbb77Jnj17eOCBB5SW5HN86Q24FXPSYlm/JIu3DzcyZFHHCAYbNZe66e63zHiE5HRIiI0gJz1G9QvgYRCQZflFWZafAY5O471/iCtwHJqJsFDioYce4pVXXqGnp4cdO3YoLcenuLwBV3zqDbgVD22Zj3nExgm1zXTQsb+iGWOkjjXFvq0mm6A4P4Xaxp6wn1Lnk5yAEGIDruH03/LF+wcbK1asIC0tjTvvvJPo6Gil5fiUmkvddPnYG3ArCuYkEBWhQ76s9hMKJkasNo6cbWPDkiyfV5NNUFyQjMVqpyHM3eZeDwJCCCPwPPC0LMtqvR4wOjrK0NBQWGwFlZ1s9os34GZoNRIL5iZQpzYICyqO1bRjsdq92jH0dhQXpACE/chJX6wEfgLslWU5rHMBE3R3d/OLX/yC+Ph4NmzYoLQcn+KaG9DmF2/ArSjKTaKpfZARqzpGMFgor2whLcnIonnJfrtnUlwk2anRYW8a84VP4A6gWAjx38e/1wCSEMIGFMiyfNkH9wxYNmzYQFpaGj/96U/9WimjBBPegO0rldkKmkDkJuJwOKlv6adk/GlPJXDpGbBw5kIXj25fgMYHbSJuRXFBCofGHcq+aFERDPgiCDwMXJ8R/AqwCngSaPPB/QIaWZaVluA39p1sJislmqI8/3gDbobIdZX8yZf71CAQBBw41YrDic/aRNyK4vxk9h67TGPbwKwH2QcrnvoEkoC5wPzx70vHXzoPlAK/BbbLstwqy3LDpGu7gGFZlmu8plol4JjwBnzmHv96A9wRF20gKyWauiY1ORzoOJ2uajKRm0h2aozf7399XiBcg4CnOYFdwGngufHvT49/ZeHyEAjAe5MfbkOoDhcP5p9LKW/AzSjKS0K+0hfU/6bhQGPbIJc7TH5NCF9PSkIUGcnGsB4y49FKQJblF3AZv9zRBNz00U+W5e9NU9Mt0ev1WCwWjMbZD54ONCwWC3q932Kp15jwBiwtTCU10f/eAHeI3ET2VzTT2WsmIzm0y3KDmfLKZnRaiU2lvm0TcSuK81M4fq4dh8Pp95xEIBB0vYPS0tJobW3FbDaHzFOe0+nEbDbT2tpKWlqa0nKmjdLeAHcUXZcXUAlM7HYHH55qYdWiDGKNBsV0FBckYzKPcbljUDENShJ0XUTj4lztZdva2hgbC53WAHq9nvT09Gs/XzBxzRtQnKG0lGvkZsQSadBSd7mXzct934ZAZfqcvnCVfpNV8S3E6/MC87LiFdWiBEEXBMAVCILxwzIUsYw7PTcvn0OkIXB+nbRaDYU5iepKIIApr2gm1qhn5UJlGyqmJxlJS4yi5lI392/KV1SLEgTddpBKYHG4qo2RAPAGuEPkJnKpdQDrmDpLNtAYtoxxrKadO5bNQa9T/mOouCCFc5d6QmaLeToo/6+vEtSUVVwJCG+AO4pyE7E7nDS0qD3jA40jZ9sYtTn80jHUE4rzkxkYGqW506S0FL+jBgGVGdPRM0xNQw/bV81V3BvgjgnTWF2TuiUUaOyvdA0dWjA3MB4eJvIC1WHYR0gNAiozJtC8AZNJiI0gI9mIfEU1jQUSnb1mahp62LoyJ2AeHjKSjSTHR4alX0ANAiozwuFwUlbRHFDeAHeIuUnUNammsUDiw8pmALYuD5yHB0mSKM5PoSYM8wJqEFCZEecu9dDVaw4ob4A7ivIS6R0cobt/RGkpKrg8MeWVzZQUpJCWFFiGz+KCZPpNVlqvDiktxa+oQUBlRuw7eYWoiMDyBrhjYtB9nTpkJiC4cKWP1qvDAZMQvp7iAlcb63CbL6AGAZVpM+EN2FSaHVDeAHfMy4rHoNOoQSBA2F/RjEGnYcPSLKWlTCE7NYaE2Ag1CKio3I5r3oBVgbOnezN0Wg3zcxJU01gAMGaz89GZVtaWZGKMDLweWa68QDI1l7rDKi+gBgGVaVNWcYXMlGgW5iUpLcUjinKTaGgZYMymmsaUpKK2E5N5TLGOoZ5QMj+FnoEROnrCZzKuGgRUpsXH3oDAKe+7HSI3EZvdEfYDxZWmvLKFxNgISgtTlZZyU4rzJ/IC4VMqqgYBlWkR6N4Ad1xLDqumMcUYHB7l5PkONi+fg1YbuB87OemxxMcYqFaDgIrKVK55A+ankpYYWOV9tyI5PorUxChkNTmsGB+dacVmdwb0VhC48gKL85OpuRQ+yWFPx0s+BHwVWAnEybJ8032A8dGT3wY2ArFADfAdWZb3zV6uipJMeAM+s7NIaSnTpig3iVp13KRilFc0k5cZFxStmovzUzhytp3OXjPpAeZl8AWergSMwH7gJx6cuwy4DDyCa/7wXuAtIUTwfXKo3EBZxbg3oCRTaSnTpig3ke5+Cz0DFqWlhB0tXSbkK30BvwqY4GO/QHhsCXk6XvJFACHEFg/OfX7SoX8QQjwC3AXUTVegSmBgsdo4XNXGHcsCa26Ap3xsGutjw5LAbXMRinxY2YJGImiG++RmxBFr1F9rjhjq+DwnIISQgGRA7ecbxBw5GzzeAHfkZyeg12lUv4CfcThcbSJKF6SRFBeptByP0GgkFs1z+QXCAX8khr8GaIG3/HAvFR9RdrI5qLwBk9HrNBRkx1On5gX8yrnGHrr6LGwNkq2gCYoLUujoMdPdH/rbhz4NAkKInbjyCE/Isqw+ggUpHT3DVDd0B5U3wB1FeUk0tPQzZnMoLSVsKK9oJipCG/A9piZTEkZ5AZ8FASHEJuBPwFOyLL/vq/uo+J7yIPQGuEPkJjJqc9DYpprG/IF1zM6hqjY2LAn8HlOTycuKJzpSFxaloj4JAkKI1cAe4H/KsvyyL+6h4h+C1RvgjqLxSWNqXsA/HK9px2K1BU1V0PVoNRKL8pPDYiXgqU8gCZgLzB//vnT8pfO4ykB/C2yXZblVCFECvAc8C7wphJhYB1pkWVYfwYKMc409dPaa+XQQegMmk5IQRXJ8JHWXe7l/U77SckKe/RXNpCZGsXi8FUOwUZyfwsnznfQOjgRNUnsmeLoS2AWcBp4b//70+FcWLg+BACbaAj4MJAJ/C7Rf9/Vv3pGs4k/KTgavN8AdRblJ6krAD/QNjnBa7mLL8jloNMGZRwoXv4CnPoEXgBdu8nITIF137veA781KlUpAMOENCIa5AZ4ichM5fLaNPtMIibGh+3SnNAdOt+BwBnceqSA7nqgIHTUNPdyxLDg8DjNB7R2kclM+9gaEjmFGzQv4h/KKFhbMTSAnPVZpKTNGq9WwcF5SyPsF1CCgclPKTjaTmRzNonnB6Q1wR8GceHRaSfUL+JDGtgEutQ0E9SpgguL8ZJo7h+g3WZWW4jPUIKDillDxBkzGoNeSnx2PfEVdCfiK8soWtBqJTaXZSkuZNSUFKYCreWKoogYBFbdc8wYEYXnf7RC5SdQ392O3q6Yxb2O3O/iwspmVC9OJj4lQWs6smZ+TQIRBG9LJYTUIqExhwhuwZH5K0HsD3FGUm4h11E5T+6DSUkKOqvpu+kzWoPQGuEOn1bAwLymkTWNqEFCZwvlxb0AoJYSvR4wnh+vU5LDXKa9sJiZKz6pF6UpL8RrFBck0tQ8yODyqtBSfoAYBlSmUnWwmKkLHuuLQ8AZMJi0xisTYCHXSmJcxj4xxpLqdTaXZ6HVapeV4jeL8ibxAaG4JqUFA5QYsVhuHqlrZuDSLyIjQ8AZMRpIkRG6iuhLwMkfOtjM6Zg+ZraAJFsxNwKDTUNMQmltCahBQuYGj1aHnDXBHUW4S7d3DDAyFbumfvymvdLUbnxjgEyrodVqK8pLUIKASHoSiN8AdEx9Uaqmod+jqM1Pd0M22laFVUjxBcX4yje0DDJlDLy+gBgGVa3T2mjl7MfS8Ae6Yn5OARiOpzmEvceBUC04nbAmSEZLTpbggBacTzjeGXh5JDQIq19gfwt6AyUQadMzLilOdw17A6XSyv6KZxfnJZCRHKy3HJyzITUSn1VAdgn4BNQioAOPegJNXQtYb4I6i3CTqm/uwO5xKSwlq6pv7aekaCok2ETcjQq9F5CaGpF9ADQIqQOh7A9whchOxWO1c6VBNY7OhvKIZvU7DxqVZSkvxKcUFyVxq6cc8Mqa0FK+iBgEVYMIboA1Zb4A7ilTT2KwZszk4eKaVtcWZREfpb39BEFOSn4IjBPMCahBQuc4bkB2y3gB3ZCQbiYs2qKaxWXCqrpPB4dGQ8wa4Q+QlotNKIddHyNPxkg8BXwVWAnGyLN+ydEQIsQD4FbAG6AD+cXwwjUoAEi7egMlIkkRRbhJ1TepKYKbsr2wmISaCZQtSlZbicyINOgpzEkPOL+DpSsAI7Ad+crsThRB64G2gE1gF/BD4lRBi80xFqviWspPNZCQbQ94b4I6ivERarw5hCsH6b19jMo9y4lwndyzPRqsNj02F4oJk6lv6Q8ov4NH/OVmWX5Rl+RngqAen3wNkA1+QZblGluVfAy8DfzlzmSq+4mNvwNyQ9wa445ppTM0LTJtDZ1qx2R1sC+GqoMmsWZyBw+HkxPlOpaV4DV+E79XAcVmWh687VoZra8jn2MdGGTMP+eNWIcH+imaAsPpDvp7CnEQ0khoEZkJ5ZQu5GbHkZ8crLcVvFGRGUxA/ytHqNqWleA1fBIE0oGvSsavjx33OR//29zT97Cm6P3gem0n9w74VLpPPuDcgKTy8AZOJitCRmxlHnZocnhZt3UPUNvWGbJuImzFw8A98TfcnWi5cYMRqU1qOV/BFEFD0N6I2aRs1jnwGT75D8y++Qt9Hf1JSTkBzvrGXjp7w8ga4oyg3iQtX+nCopjGPeftwIxqNxOYQbRPhDqdtDNPZcjROO7sjjlJZFxpbQr4IAp1MfepPZerqwCfMKSzkN31rMT7+U6LyS+k7+DLWjkZ/3DroKDt5hagILetLwscb4A6Rm4h5xEZzl0lpKUHB1T4L7x5pYvvKHJLjo5SW4zeGL5zAYRnCWLSOQn0nLUf3KS3JK/giCJwA1gghrt9f2AYc98G9prA4PxmAuh4dqfd/DckQycDxN/1x66BiJEy9Ae4oynNVRal5Ac/4r30yTid8aodQWopfMZ0pQxeXQvqD36AvIpuF3e9jHRpQWtas8SgICCGShBClwPzx70vHvwxCiNVCiDohRPb46e8BbcCvhRCLhRBfAB4D/l9f/ACTyc2Mwxip41xjL9rIaOJK72To3CHGBvyyEAkajlS3Y7GGnzfAHVkp0cQa9WozOQ9o6x7igxNX2LkuN6zySGMDXVgazxK7dDuSRotmw2cxYqXhzeeVljZrPF0J7AJOA8+Nf396/CsLl4dAAHoAWZZHgXuBTKAS+C7wtCzLB7wn++ZoNRIL85I4N97oKX71fSBJDJx42x+3DxrKTl4JW2/AZFyTxpKmzBYY621j4MQenA67QsoCjz/sldFpNXxy+wKlpfgVU1U5ALFLtwJQvGoZR2wLiWj8iJHWC0pKmzUe7QOMu31fuMnLTUxKBsuyLANbZi5rdizOT6bynVoGhqzEx6cSs2gDptP7SNz4KNqoGKVkBQxd496AJ3YWhVVlx60QuYlU1nUybBkjOkqPub6Szjd+htNqRhefRrRYrbRExbncPsiB0y08tGU+iXGRSsvxG06HHVPVfqLyl6CLdzmj9TotPfk7MV1p4uo7zzLnqZ8iaYJzrnJI2vwWzXPlBSYaPcWvfQDn2AiDp95XUlbAsL8yvL0B7ijKTcTphAuXe+g79Aodf/wx+oR0tDGJmM6ERgJwtrz4Xi1REToe3laotBS/Ymk8i32wm9il2284vnrpPP48tJKxriYGK95VSN3sCckgsGBuAnqdhvONri2hiPQ8ovJLGTz5Ng5b6Ni9Z4LTed3cgDDa070dC+YmEimNYd//C/oO/IGY4k1kfe4ZYpdux9xwGtvAVaUlKsqFK30cq+ngwS3ziTUalJbjV0xVZWiiYolecONqcHlRGued8+iOLqD3wMvYBoOzp1BIBgG9TsuCuYnX8gIACWsfwD7cz1D1QQWVKY/qDXCP3tzN3ybtJaHvPEl3fp7UXX+FRh9BbOl2cDoZrNqvtERFefHdWuKiDezalK+0FL9iNw8yLJ8kpmQzku7GVtlRETqWiXT+MLgKp91Gz77gTBKHZBAAV16goXUAy7irLzKvBEP6PAaOv4HT6VBYnXKo3oCpmBtO0/r8N4mTLDxv3Uncqvuu5Ur0CWlE5S/FdKYsbBPE1Q3dnL5wlUe2FWKMDO2ZAZMZqjkIDhtxk7aCJli/JIuLAwbsxZ9guPYo5obTflY4e0I3CMxLxuFwXusVL0kSCet2M9bThvlChcLqlGHCG7BhieoNANfWWP+RV+l4+Rl0cam0r/trzg6n0tZ9Y++puGU7sJt6sDScUUipcjidTn73Ti1JcZF8YsM8peX4FafTyeCZfURkFWJIc79yXr0oHa1G4ohjKfqkLLrfew7HmNXPSmdHyAaBojxXY7Bzlz6u/Y5euA5dfBr9x95QUJlyfOwNUBPCjtERul77V3rLXyJ60XqyPvcMBUXzAabMFzAWrkQbncDg6Q+UkKoolXVd1Db18qkdC4jQB2f1y0yxttUzdrWZ2KXbbnpOjNHAkvkpHD53leSdX8LW30n/kVf9qHL2hGwQMEbqyc+OvyEvIGm0xK+5D2tLHSMtdQqqU4aPvQHJSktRlLG+Dtr+81sM1x0jadv/b+irAAAgAElEQVRnSNv9DTSGSOakxRIdqZviF5C0OmKXbsV8sTJok38zweFw8rt3a0lPMnLn6lyl5fgd05kyJH0EMYs33vK8dUuyaO8episyj5jFm+g/+jqjPa1+Ujl7QjYIABQXpFDb1MvA0MfLs9il29FExdB/NLxWAxPegG0r56LRhK83wNxYRevz38Q22EvGp75Nwrrd1/b/NRqJBXMT3TqHY0vvBKcD09lyf0tWjKPV7VxqHeDxuwV6XUh/VEzBMTrC0PnDRC9cjybi1lV0axdnIElw9GwbSXd+Do3OQPd7z+F0BkdDwpD+P7tj9VxsdgfvH7987ZjGEEnc8p2YL5xktCd0eoLfjmvegDCYBesOp9NJ/7E36PjDD9HGJJH9hX/CmF865byivCSudAxiHhm74bg+MYOovBJMZ/aFRWGB3eHkpb215KTHsHl5+P3ODNcewTlqIa7UfUL4ehLjIlmYl8SR6nZ0MYkkbnmCkaZqhs8d8oPS2RPSQWBuRhxL5qfw7tEm7PaP/3DjVt6DpNWFTWM5p9PJ/pPNLJmfQnoYegMcY1a63vgZvWW/JVqsIfvzP0KfmOH2XJGbiMMJ9c39U16LXbYD28BVLJeqfC1ZcQ6caqa5c4gndi5EG4YrR1PVfvTJWUTMKfLo/HUlWTS1D9LWPUTc8h1EZBbQs+8F7CPDt79YYUI6CADct3EeV/ssN4yD08UkELNkK0NnP8Q2NPWPPdQ439hLe89wWCaExwa6aPvPbzN87jCJW54g7aH/icZw8/bHYu7Nx01Gi9VojHEhnyAeszn4/V6ZgjnxYVlKPNrdwkhzratZnIdtVdaN/zsdq25H0mhJuefL2M2D9H34e19K9QohHwRWL8ogJSGKdw7fOFMgYe39OO02BiveUUiZ//jYG5CltBS/YmmqpvU338TW30nGX3yLxA0P3faPOsZoYE5ajNtJY5JWT+ySLZjrK7ANhW7b6Q9OXKaz18yndy4My95Spqr9IGmIKdns8TXpSUbmz4nnSHU7ABGZBcSt2Mlg5V5G2i76SqpXCPkgoNVquGddHmfqr9Lc+fHQEH1SFkaxmsHKvThGLQoq9C0ub0BbWHkDnE4nAyf20P7776M1xrn2/+ev8Pj6otwk5Mt9bhN7saU7wGFnKEQTxNYxO//1wQUWzUtiRZFfJsIGFE67jaHqDzEWrkQXkzita9eVZCFf7qO73/V5krT5U2hjEuh+99mANhqGfBAAuGtNLjqtZupqYN1uHCNDmM6UKaTM9xytacditYXNVpDTYefqWz+n54PnMRauJPvzP0GfNL0VkMhNZHB4lPaeqfu5huQsInMXM3g6NBPE7xxupHdwhM/cE56rAPPFSuzDA652IdPk2pZQjWs1oImMJnnHk4x2XGKwcq9XdXqTsAgCCbERbCrNoqziyg1VH5HZC4jMWcjA8bdw2kNjaPRkws0bYL54iqHqD0lY/xDpj/wtmojpjz+83aSxuNId2Po7sTRVz0proGEeGeNPZfUsW5BKcUGK0nIUwXSmDG1MIsaCZdO+Nic9lpz0GI6ObwkBRC9cT9S8pfR++HtspsAcWhQWQQDgvo35WKx2yiuabzgev/YBbIPdDNceVUiZ7whHb4C5vgLJEEXiHZ9Ekmb2652THktUhO6mk8aMRWvQRMVgOh1aLabfOHgJk3mUT9+zUGkpimAb7MHccJrYJVtnPBtgXUkWNQ3d17xJkiSRsvOLYLfRs+8FL6r1HmETBBbMTaQwJ4E9hxtv2Os1Fq5An5xN/7E3gsbc4Snllc04neHjDXA6HZgvVmLML0XSzrzRmVYjsWBuwhTn8AQanYHYki0MyyewDwf/jFkAk3mU1w9cZF1JJgvmTm8vPFQwVX8ITsct20TcjnUlmTiccOJcx7Vj+qQsEtY/xPD5w5gDsLzY4yAghPiWEKJNCGEWQrwuhLhp1kgIsUsIcXr83BYhxM+EEIo3Ib9v4zxauoY4W9997ZgkaYhfu4vRzkYsTWcVVOddXHMDwssbMNp+CftQH8bClbN+r6LcJBrbBhmxut8mjF22Axy2kHEQ/3l/PRarjSd2elYXH2o4nQ5MVfuJzF2MPmnmZbEF2fGkJUZdqxKaIH79bnSJGXS/96uAm2ni6aD5J4G/A74KrAcSgD/c5NwC4BXgJWAx8FngYeDbXtA7KzYuzSYu2sDbR25MEMcWb0YbncBACLWSCEdvwHB9BUgajPOXz/q9RG4iDoeTiy3ufSSGlDlE5iwcdxAH9wqyb3CEtw41snnZHHIz4pSWowgjV85j6+uYMj1sukiSxLqSLM5cuHpD/lGjM5Cy82lsfR30H3lttnK9iqcrgb8E/lWW5ddkWT4DfAHYJoQodnPucmBQluV/lmW5UZbl/cAfAc9r9HyEQa/lrjW5HK9pp6vPfO24pNMTv/peLI1VWDsab/EOwUM4egPM9RVEZC9Aa5z9B5nIdSWH626SHAaIXXYnY73tjFw5N+v7Kckf913AZnfw2N1CaSmKYTpThibCSHTR2lm/1/olmdjsDipqO284bsxfSvSiDfQfeZWx3sBpWXPbICCEiACWAtdGK8myfAnXgPk1bi6pBGKFELuFEJIQIgfYCQTEgN971uUB8N7RphuOxy6/G8kQGRKtJCa8AeuXZIWNN8A22MNoZyPRhd551oiLNpCVEn3T5DBAdNE6NJHRQe0g7uo1896xJnasnktWSozSchTBPjLMcN0xYhZvQqOPmPX7FeUmkRgbwZGz7VNeS77zSSSdge73A2cKmScrgeTx87omHb8KTMkLjAeIXcALwChwBTggy/L/MyulXiItycjqxRnsPXaZ0bGPDRzayGjilu1g6NwhxgYm/6jBxcfegPAZIWm+WAnglXzABEV5SchX3JvGADT6CGKKNzNcdwy72eT2nEDn5Q9kJEniUzvCdxUwVPMRTtvojLwB7tBoJNYWZ1JR14l17EaTmC42kYQ1u7A0nGKsr+Mm7+BfPAkC06otFEJkAb8AfoJrC2gXsFMI8dfTl+cb7tuQz+DwKIeqbuz5Hb/6PpAkBk68rZAy71B28grpSUYWh4k3AFxbQbqENPQp3suBFOUl0W+yUnuL1UDcsjvBbnNVlgQZLV0myiqauWd9HikJ0/dThAqmqjIM6fMwZHhvfvK6kkyso3ZOy1MfKF3VR1LAFBV4EgS6AQdTn/pTmbo6APgK0CDL8k9kWT4ry/JbwHeAb85KqRdZUpjCnLQY9hy6cf9fF5dCzKINmE7vw24ZusnVgU1Xn8sbsH1lTth4AxxjVixN1RgLV3rV5bpl+RyS4yN59rVq7A73qwFDWi4R2Qswnf4g6BLEv98rY9BpeHTbAqWlKIa1o5HRjkvELt3m1d+dkvkpREfpbzCOTaCLSyYqvxRTVXlAtJO4bRCQZdkKVAFbJ44JIeYBecBxN5cYgck/mcOTe/kLSZK4b8M86pv7uTCpFjx+7QM4x0YYPBW4Nu9bMeEN2Bom3gAAS+NZnLZRr24FAURF6PjiA8Vcah3gvSM3LxiIW7aDsZ5WrEE0ra6xbYCPzrSy644CEmJnvw8erJiqypC0emKK7/Dq++q0GtYszuD4uQ5s9qntRWJLt7vmVjcqX5bu6Qfzz4G/FkI8IIRYCvwaKJdluUYIsVoIUSeEyB4/9x3gLiHE14QQ+UKIbcAPgLe8L3/mbF2ZQ1SEjj2HLt1wPCI9j6j8UgZPvhNw9by3w2Z3UHaymZKCFDKSo5WW4zfM9RVIEUai5i7y+ntvWJJFaWEqv3u3ln6T+wHi0QvXI0UYgypB/OK7dURH6Xlwy3ylpSiGY8zKUM1BjEVr0EZ5Pym+riSTYcsY1Re7p7wWXbgSTVSsq2OpwngUBGRZ/g3wY+CXwFHABDw2/rIREIB+/Nx9uEpIvwzUAL/FVRn0P7wpfLYYI/VsW5nDR2fabhg/CZCw9gHsw/0MVR9USN30cTqd/PLVs7R3D3P/Ju/tbQY6TqcDc30Fxvyls3IJ3wxJknj6wRKsY3ZeeNt9KajGEEns4k0M1x4Nim3Eusu9nDjfwUNb5hMT5f1/s2DBLJ/AMTJM3Cy9ATdjmUgj0qB1uyUk6Vyrj+ELJxQvKvB4i0aW5R/Lspwpy7JRluUHZFnuHD/+oSzLkizLTded+5+yLJeMnztHluWvyrI86AP9s+LeDfOmjJ8EiMwrwZCRz8DxN4KmU+QbBxvYe+wyj24vvNbNMBywtl/CPtzv9a2g68lJj2X35vmUnWzmfKP7QfOxy3bgtI0yVBP4Dw6/e6eWhJiIsHpYcMdgVRm6hDQi89zZnWZPhF7LioXpHK1pd5tTiivdDnYbQ+eU/Z0JmH16JchJj2VpYQrvHLlx/KQkSSSsfYCxnjbMFyoUVOgZx2va+c1b59iwJItP7wyv5l/m+pMul3CBb72If3HnAlISovjlq2dv+F2ZICJjHhGZ8xkM8ARxVf1Vzl7s5tHthUSFiYfEHWN9HYw0VY9PD/Pdx+D6kkz6TVa3fhNDWi4RmQWYzpQp+jsT1kEA4N4N+XT3Wzhx/saa3eiF69DFp9F/LLBbSVxqHeCfX6pk/pwE/sdjy8KmImgCc30lkXMEWmOsT+8TOZ4kbmwb5J0jTW7PiV12J2NXr2BtveBTLTPF6XTyu3drSYmPZOe4aTJcMVWVg6QhdsnW2588C1YuTEen1bjdEgJXueho12VGOy65fd0fhH0QWL0ondTEqCnlopJGS/ya+7G21DESoFUfPQMWvv/rY8QYDXznC2uINITXk92ES9iXW0HXs74kk2ULUnnxvVr6TCNTXo9ZtBHJEMlggLaYPlnbiXy5j0/dJTDoZ9YqORRwOuyYzu4nKn8pujjfemmMkXpKF6RytLrN7dN+9OJNSDqDognisA8CE+Mnz17s5krHjWmL2KXb0ETF0B+AjeVGrDZ++JvjmEfG+O5Ta0iKi1Rakt8x17u26qYzOnI2SJLElx9awuiYnRf2nJ/yuiYiipjFmxg+fwjHyNSpZEricDj53Tu1ZCZHh5WT3B2WS2ewm3q95hC+HetLMunqs9DQOrXtuDYymuiitQyd+wjHmPvqM18T9kEAXOMn9ToNb08aP6kxRBK3YifmCycZ7Wm9ydX+x+Fw8q9/OMWl1gH+5tMrmZcVr7QkRRiur0CXkI4+ZY7f7pmdGsODW+azv6KZc5emJonjSu/EaRvFVPOR3zR5wuGqNpraB3l8ZxE6bXj/2Q+eKUNjjCPaTyvI1Ysz0EjcckvIMTKMWT7hFz2TCe/fhnHiYyLYVJpNeWXzDe1fAeJXfgJJp2fgWOA0lvvtO+c5Wt3OF3YVs3pRhtJyFMExOsKID1zCnvDJ7QtITYzi3/9cNcUIZMgswJA+L6AcxHa7g5f21pKbEcsdpdm3vyCEsQ8PYK6vILZki09Kit0RHxNBcUEKR6vddw6NzF2MLiENU5Uys87VIDDOvRvmYbHa2T9p/KQ2Op7YJVsxVX8YEDNC9524zJ/LL3LPujx2hXGJn6XxLE77mN+e5q4nMkLHlx4o5nKHacrqUZIk4pbtYLSrCWt7g9+1uaO8spnWq8M8sXNh2BUOTMZUfQAcdr9tBU2wriST5s4hmjunegIkSUPskm1YmqoZ6/d/80o1CIyzYG4iC+YmsOdQ45QnuPi1u8DhYOCEsqbn6ovd/PxPVZQuSOXpB0v8/gQcSEy4hCPnKlMSu7Y4k+VFabz0Xh29gzcmiWOKNyHpIzAFgIN4zGbn9+/LFOYksLY4PFeNEzidTkxVZURkCwx+3EIErnl3br4ltBWQFEkQq0HgOu7dkE/r1SGq6q/ecFyfmEHMog0MnnpfMUdo69UhfvTCCbJSo/nmZ1eF9b6ut2YJzwZJkvjygyWM2Rw8/9aNTmJNhJGYRRsYOncIh9WiiL4J9h67zNU+C5+5Z2FYPzQAWFtlxrpbiC2d+QzhmZIcH4XITeTITbaEdHEpROUvxXTW/03lwveTxA0bl2YRH2OYUi4KEL9uN87REQYr3/O7LpN5lO//xzE0GonvPrU2rK3+ANa2Bp+7hD0hKyWGh7fN58NTLVQ33NgfJnbZDpxjIwydUy5BPDJq44/7LlBckEzpglTFdAQKpjNlSIZIYhZtUOT+60syaWgZoLPX7Pb12KXbsA92Y2mq8asuNQhcx8T4yZPnO6b8j4pIz8M4fwUDJ9/2aynXmM3Bj144QVefhW8/uTqsGsPdDPPELOGC2c8Sni2PbCskLcnIL189e0OSOCKrEEPaXEU9A28faqTPZFVXAYDDamHo/BFiFm5AY1BmdsLa22wJRS9YjSYqxu8JYjUITOKedfMAeNdN6+CE9Q/hMA9iOuOfP2yn08kvXqmipqGHr/9FKYvCaEjMrTBf9I9L2BMiDTqefqCYKx2mGzrSSpJEbOkORjsasLb73w06bBnjlf31rChKU39vgKHawzjHRvyeEL6erJQY8jLjblolJOn0xJRswdJY5VddahCYRGpiFGuKM3n/+JUbxk8CROYUEZmzkP5jb+K023yu5c/lF9l38gqf2iHYsiJ85gPcCttgt19dwp6wenEGKxem8/u9dfQMfJwDiCnZjKQzMHjG/wni1w80MGQZ49P3hFcvqZthOrMffcocIrKVHaCzviST2qZe+ganOs4BkrY+QeZj3/WrJjUIuOHeDfMwmUf56MxUg1jC+gexD3b7fK/3yNk2/vPt89xRms3jd4fv/NfJXHMJB1AQkCSJp3eXYLM7+c11SWJtZDTRC9czVPMRjlH/JYgHhqy8cfAiG5ZkMX9Ogt/uG6iMXm3G2iqPN4tTdlts3ZIsnE44ds79fGGNzkBEZoFfNalBwA1L5qeQkx7DnkOXppSLRhUsx5CWS//R133WZrq+uY9/+f0pRG4iX//UMsV/cQOJ4foKdIkZ6JMDy/SUmRLNI9sKOXi69YbqsrhlO3COuvaj/cWfyy9iHbXzxM4iv90zkDFVlYFGR2zJZqWlkJsRS2ZKNAdOtQSMmVANAm6QJIl7N+RzsWVgyvhJSZJIWP8gY90tmC+c9Pq9u/st/PA3x0mIMfDtJ1eHdaOvybhcwjWKuIQ94eFthaQnGXn2tbOM2VwPCBFzBPqUOX7zDHT3W3j70CW2rMghJ135nInSOO1jmKoPEL1gJdpo5durSJLE/RvzOXeph1f21ystB5hGEBBCfEsI0SaEMAshXhdCTB48f/25OiHEPwohrgghrEKIC0KIHd6R7B+2rpjjGj95eGqCOHrhenQJ6fQfec2r0dxitfGDXx/HYrXz3afWkhgbfk3hboWlsUoxl7AnROi1PP1gCc2dQ7z1kcstPOEgtrbVY+1s8tm9L3cM8stXz/KVn+7H4YTH7lK3EMG1cnSYB4n10fSwmXDfxnlsKs3md+/WUlnXqbQcz4KAEOJJ4O+ArwLrgQTgD7e45FngAeApXKMnvwi4r4sKUIyReravyuHQmbYpbYMljZaEtQ9gbatn5LJ3anrtDif//GIlTe0DfPOzK8nNjPPK+4YS5voKNBFGInMCN9m5elEGqxdl8If3Zbr7XXmAmOLNSFq916vKxmwOPjrdyrd+cYiv/Z9y9h67zNriDP7PX21SS4nHMZ0pQxubTFT+UqWlXEOSJP7qk6XkZsTxf16spK1b2ZGknq4E/hL4V1mWX5Nl+QyuGcLbhBBT5rIJIUqAzwIPyrL8gSzLTbIsH5Rl2b8OCC/wifXux08CxCzdijY6gf4jr3nlXi/sOceJ8x08vbuEFUXpXnnPUMLlEj5FVMEyJG1gz0340u5iHA4nv37T9SuvNca62gVXH/CKx+Rqn4UX363lCz98n5++WMHVPgtP3reIF757F3/9+Ao1GTyObbAby6UqYpdsRdIE1rZqZISObz+5Go0Ezzx/YkrjSn9y2yAghIgAlgLXmlrIsnwJaALWuLnkXuAi8CkhRLMQok4I8R0hRGD9X/CAnPRYShek8t6k8ZPgyuLHr7kfS2PVrBuFvXe0idcPNHDfxnncuzF8m8LdCmvbxYBwCXtCRnI0j2xfwKGqNs5ccDUEi122A4fVzHDtzBLEDoeTU3IXzzx/nC8+8z5/LLtAYU4C//DFtfzqW3fy0NZC4mMivPljBD1D5w6B0zHelyfwyEiO5n99ZiUtnSZ+9vJpxRLFnqwEksfPm9ze7irgLi+QB+QDdwGPAN8C/gr4nzNWqSD3bZhH98AIe49fZmT0Rm9A3PK70EQYZ7UaOHOhi39/9SwritL44i7fDLwOBT52CS9TWopHPLx1PpnJ0fzy1WrGbA4i5y5CnzKH/sOv4rR5/tRnMo/y+oGL/Ld/KuMffnWU2qZeHt5WyHN/t4PvPrWWlQvTw74z6M0Yrj1CROZ89ImB2zivdEEaT96/mKPV7fyxTJmxpJ6sq6f7G6YBDMDnZVm+DCCEmAt8BfjpNN9LcVYuyiA7NYZ///NZnn31LHPSYynIjmf+nAQK5iSQUnoXw8ffYLSnFcM0yxabO0385D9PkpMWw//6zEq0YdwU7naY6yuJzClCGxUcFS+G8STxP/7HMd442MAj2wpJvvPzdLz8Q/qPv0nihodvef2FK328e6SJg6dbGLU5WJiXxON3F7FhSSZ6XdAtqv3OWH8n1vYGkrZ9Rmkpt+WBOwpoaBngpffqmJcV7/cZIZ4EgW7Ageupv/a646lMXR0AdALWiQAwjgwEpeVVq5H456/fQU1DNw0tA1xs6aeq/irllS0AxGr0/EOCho9e/DWDSx+nYE48+dkJt23yNjBk5fu/PoZer+W7T63FGBneTeFuhW3gKqNdTSRt/6zSUqbFyoXprC3O4OUPZDYvm0NqwTKMYg39h14hpngT+vgbF9IjozYOnWnl7SNNXGzuJ9KgZfuqudyzPi9sp8fNlOHaowBEL1ynsJLbI0kSX310KVc6TfzLS5X8y9fvYE6a/x52bhsEZFm2CiGqgK3AAQAhxDxc2z7H3VxyDIgQQsyRZbll/Nh84IpXFCtATJSetcWZrC3OvHasd3CEhpZ+GloHaKy5SIGpin/cc4IBp6sqIzM5mvw58RRkx1MwJ4GC7Phre7ZjNjs/euEEvQMj/OgrG0hLMirycwULw/WVQGC5hD3liw+U8JWf7ufXb9bwvz+3ipQdT9J86Qw97z9PxqPfBKDt6hDvHm1i34krDFnGyEmP5b89WMLWlTnqw8EMGa49SkRmAfqE4CiyiDTo+PbnV/ONnx3gn1+q5Gff2OK3e3taZvFz4GdCiNO4EsL/FyiXZblGCLEa+C2wXZblVmAvrhXDc0KIvwEyceUF/sXb4pUkKS6SpEUZrFqUwdiqp2n+xVf56TYLHQV3uoJDywANLf0crvq4WVRqYhQF2fGMWO2cb+zlf31mJSI3ScGfIjgwT7iEk7KUljJt0pOMfPLOQl58t45TchfLRRqJGx+ht/wlKvft4/UGI2cuXEWrkVi/JItPrM9jcX5yQJrhgoWx/i6s7ReDYivoetKSjPzDF9dSWetf74BHQUCW5d8IIdKBXwLxwAfA0+MvG3F5AfTj59qEEPcC/w6cxLU99Avg37wrPXDQJ6QRs3gjwzX7Wbrlkyy/zkc3ZB6loXXAFRRa+2lo6aez18Ln7l3EpjCf9+oJjlELlsvVxK/YGbQfjA9tmU/ZyWaeffUsP/jyespNgnnOeJxHXqSdR/n0ziLuWpNLYpxqDvQGw3XHAIguWquwkunjmnCY6Nd7elxwLcvyj4Efuzn+IZOSx7IsNwI7ZysumEhY9yBDNQcZrHiXxDs+ee14jNHA0sJUlhZ+PNTD7nCiVSs6PMJy6SzYbUG5FTSBXqflyw+W8L3njvHUM672EZ/I28Hdg6/wTxuHSN6sunu9yXDtEQwZ+QFdFRRIBLbrJogwpM3FWLiSgYq3iV+7C43h5k91agDwnOEgcAl7woqidB67S2Cx2rhnXR5ZqTF0vtbO4NHXiFuyWf3A8hK2gatY2+pJ2vqE0lKCBrUm0YskrH8Ih2XIb0NnQh2n04GloTIoXMKe8PjdRTy1q5is1BgAkrd/DrRaet7/TcB0lAx2huomqoLWK6wkeFCDgBeJnCOInLt4fOiMcjbwUMHlEh4gunCV0lJ8gi4umcRNf4H5YuW1OQkqs2O49iiG9HnqymoaqEHAyySsfxC7qYehGuUGjIcKEy7hqCBxCc+E+FWfQJ+aQ8/7v/br7OpQxDbYjbX1QlB4AwIJNQh4maj8Ugzp8+g/+hpOh/32F6jcFHN9BZE5C9FGxSgtxWdIWh0pd38J28BV+g+/qrScoGZo3CAWowaBaaEGAS9zbehMT5tPhs6EC2MDXYx2XQ7qqiBPicpdTEzxHfQfe52xXvdDyFVuz3DdUQxpeUHpJ1ESNQj4gOiitegSM+g/8qqa8Jsh5iB2Cc+EpO2fRdIZ6N77H+rvzAywDfZgbZHVraAZoAYBHyBptCSs2421vYGRpmql5QQl5voK9ElZGJLD46lOF5NI0uZPYblUxbB8TGk5QcewWhU0Y9Qg4CNiS7agjUmk/4i6zztdHFYLlss1GAtXKC3Fr8St2IkhLY+e95/HMWpRWk5QMVR7FENabtg8NHgTNQj4CEmndw2daapmpO2i0nKCCktjVdC7hGeCpNGSsvNL2E099B16RWk5QYNrK6iO6CJ1K2gmqEHAh8QtuwtNZLS6Gpgmw/Un0URGEzmnSGkpficyp4iYJVsZOP4Wo90tt79A5dr2mZoPmBlqEPAhmogo4lbcg1k+of5Be4htqI/hc4eJLloXEi7hmZC87TNoDJFqkthDhmuPok+diyFljtJSghI1CPiY+FWfQNLp6T/6htJSgoKB42/idNhJWLdbaSmKoY2OJ3Hz44w0VTN8/rDScgIam6mPkeY61RswC9Qg4GO00fHElt7JUM0BbIPdSssJaOzmQQYr3ydm8Ub0SZm3vyCEiVu+A0NGAT37XkIFNJsAABRISURBVMBhNSstJ2BxVQU51aqgWaAGAT+QsHYXAP3H3lRYSWAzcGIPzjErCesfUlqK4lxLEg/10/fRH5WWE7AM1x1Fn5qjbgXNAjUI+AFdfCoxxZswndmH3TyotJyAxD4yzEDFu0QXrcWQGpTjqL1OZHYhsaXbGTjxNqNdl29/QZhhM/UxcqVWrQqaJR4HASHEt4QQbUIIsxDidSFEmgfXrBBCjAkhPpyVyhAgYe1unGNWBk6+o7SUgGSw4l2cVjMJGx5WWkpAkbT102gijXS/95yaJJ7EUM0BwKnmA2aJR0FACPEk8HfAV4H1QALwh9tcEwG8wPhw+nDHkJqDccFqBiveVY1Ak3BYLQyceAvj/BVEZMxTWk5AoTXGkrT104w0145/6KkADJ7eR2/5S0TmlWBInau0nKDG05XAXwL/Ksvya7IsnwG+AGwTQhTf4pofAvuBQ7PUGDIkrH8Qx8gQg6c/UFpKQDF4ai8OyxAJGx9RWkpAElu6nYisQnrLfot9ZFhpOYrTf/R1ut/5d6LmLSXj0f+ttJyg57ZBYPyJfimuD3QAZFm+BDQBa25yzQZgF/Atr6gMESKzFxCZW8zAsbdw2tShMwCOMSsDx98kat5SIrMXKC0nIJEkjStJPDxI34GXlZajGE6nk56y39K7/3dEL95Ixie/ecsxriqe4clKIHn8vK5Jx68CU/ICQggj8DzwtCzLam3bJBLWP4R9qJfegy+re7yA6UwZ9uEBEjaquYBbEZFZQNyKuxmsfA9rxyWl5fgdp8NO99u/YODYG8St2EnaA19H0uqVlhUSeBIEpjsV/SfAXlmW1Q1MN0TNW0Lssh0MHH2d3vIXwzoQOG1j9B99jcichUTNXay0nIAncfNjaKJi6H7vP3A6HUrL8RsO2yidr/4Lpqr9JGx8lOS7v4gkqYWN3sITX3434MD11F973fFUpq4OAO4AioUQ/338ew0gCSFsQIEsy2Fd6yZJEin3PI0kaRg4+jrYbSTd+XkkabqxNvgxnS3Hbuol9b6vKS0lKNBGxZC87TNc3fP/YaoqJ650u9KSfI7DaqbjT//EyOUaku/6AvGr7lVaUshx23Aqy7IVqAK2ThwTQswD8oDjbi55GCi97uuXQMX4f6tjk3Dt8Sbv/BJxq+5l4MQeet7/dditCJx2G/1HXiMiq5CoeUuUlhM0xCzZQsScInrLX8RuMSktx6fYhwdoe/F7jDTXkvrA19UA4CM87dD1c+BnQojTuBLC/xcol2W5RgixGvgtsF2W5VZZlhuuv1AI0QUMy7Jc40XdQY8kSSTveBJJq2Xg2Js47XZS7vlS2Cxzh859hG2gi+S7vhCWq6CZMpEkbv3139L74e9JvefLSkvyCbaBq7T//vvYBrvJeOSbYTdbwp949Ikjy/JvgB/jeqo/CpiAx8ZfNgICULM000SSJJK2fZaE9Q9hOv0+3W//e1gMp3c67PQffhVDWl7YzQzwBhHpecStvAfTqQ+whuCsitHuFlr/89vYh/vJfPy7agDwMR736pVl+ce4AsHk4x9yi+SxLMvfm4mwcEGSJBK3PA5aHf0f/RGnw07qfV9F0miVluYzhuuOMdbbRtpDf6OuAmZI0h1/wfD5w3S/9xxZn/9RyPy+jLTW0/FfzyBptGR+5gdEpOcpLSnkCY+9hwBHkiSS7vgLEjc/xlD1Abre+LeQXRE4///27jw8qvJe4Ph3Mtkm62QhJpQtAX0RUAIiKWhcAKm4IIgrrpU+9rkt3ntttfeiFkFx70V7W6vSe60PFQEBUVGpC6LVGoHLEirKi5qw2WwISQjZM3P/eM/UaUjKkGXOZOb3eZ55Hua858z88nLO/N73nPe8x+vhyMerickcQOLwDm8zEQGIik8kfcotNJV9xdEdG+wOp0c0lO6kbNkCouJc9L/lIUkAQSJJIISknXsV6ZNu4tjnf6Fy7ZN421rtDqnH1e/ZQkvVftwTr4yY6x+9JWlkIfGDRnJ447I+PzFh3e4iylY+RExaFv1vfpiYtGy7Q4oYchSGGPeEGaRPuZVju4uoeOW/8LaFz53FXq+XIx+vITotm6SR59odTp/ncDjIvPhHeJrqOfz+i3aH02W129+l8pXFxOUMJefGB4lOTrM7pIgiSSAEuQsuJ+MHP6J+z2YqVj+Bp7XZ7pB6REPJDprLv8Y9cWbYnMO2W2y/QaSOv4yjxRto/GaP3eGcFK/XS/Unazn01rO4ckeTc/18nK4ku8OKOJIEQlTquGlkTvsx9V9tpWLVY3hamuwOqVtML2AVzpRMks843+5wwkpa4TU4k9I5tH5Jn7mW5PV6Ofz+Ug5vfFHmAbKZJIEQljJ2KpmX/oSGkmIqXn6kzyUCT+MxjulNHFq/hAPPzKXpoMY9YYbM+dLDouJcZFx0K80VpdRue8fucE7I62mj6o3fUfPp6zIPUAgIeIiosEdK/mQcTidV656mfMVDZF87j6hYl91hdcjraaOp7GsaSoqpL9lB0zd7wOvBERuPa/Ao3N+/guQImOrADomnT8S14z2OfPASicMnEJ3ktjukDnlam6lcu5j6PVtwF15DWuE1MkzYZpIE+oDkMy7AEeWk8rX/pmz5InKuu5eouAS7wwLMnZ31JcU0lOygYe9f8TTWAQ7icvJwT5yJK89MES0tvd7lcDjImDqHg7//Od+++zxZ0+8IuTo38wA9SuO+XWRMnUPq2ZfYHZJAkkCfkTSyEKKiqXz1ScqWP0j2dffhjE8Mehye5gYa9u2ioaSYhtIdtHxrpoNyJqeTqMbjysvHNeQMnAkpQY8t0sVmDsA9cQbVH69m/4EvSB1/GSljLgqJBkPbsRrKViyiuXIf/a74N5JHnWd3SMIiSaAPSTp9Ao6oKCpeWUz5SwvJvv6XOF3JvfqdXq+H5vJS09ov3UHjAQ2eVhzRscQPHknK2B/gyh1NTOYA6daHgLTzriN+wHCqi17l8IalVH+8mpSzLibl7EuITrJn6GVLTSXlLz1o5gG6+j9IGCbTQIQSSQJ9TKIq4JSr7qZizROULVtIzuz5Pd7qbq39lobSYupLi2ko3YnHuhEp9pRcUgsuIyEvn7gBiqjo2B79XtF9DoeDhKFjSBg6hsa/fUVN0atUf7KWmk3rSDrzAtzfn05Mev+gxdNcdYCy5Q/gbW4kZ/Z84geeHrTvFoGRJNAHJZ46juyr/5OK1Y9Ttux+cmYvwJmY2uXP87Q00bj/c/PDX7KDlqoDADgT3SQMHWOd4jkzZC82io7F9x9G/Ky7aDn8N6o/fZ26nR9wdPt7JA4vIHXCTOL7D+vV7zfzAC3CERUt8wCFMEcozmOvlBoClG7YsIEBAwbYHU7IaijdSfnLjxDtziLnhgUBd/e9Xi/NlfvMxdzSYhr3f4G3rQWHM4b4QSNw5Y3GlTua2KzBcoonjLTWHaF2y1vUbv0TnqZ64gePxD1hJq68/B7/f64vLaZi1eM4E1PJmT1fpoEIkoMHDzJ58mSAXK313kC2kSTQxzXs20X5yoeJTkkn54aFRCend7hea101DaXF5lVSTNuxagBi+g0iwfrRjx80gqiYuGCGL2zgaaqndvt71GxeR9vRw8RmDSZ1wgySRpzTI3dy131RROWrTxGb2Z/s6+bLNBBBJEkgQjUe+IKyFYtwJrrpf+NColMy8bQ203RgtzmvX1JMc0UpAFEJKbhyzyQh1/zwR6dk2By9sIu3rYW6zz6i+tPXaDl0kOjUfqQWTCd59KQu371bu+0dDq1fQtyA08i+5h6ZBiLIJAlEsMZv9lC2/EGc8UnEZH6Pxn278LY2Q1Q08QMVCXn5uHLzic0eIrN3in/g9Xqo/3Ir1UVraTqoiXIlkzJuGqnjpgU86MA3D9CRD5bhGjqGU2bdLb1KG3QlCQR8YVgpNQ+4A3AD7wC3a62Pe9C8UiofuBc4F0gGPgPu01q/F+h3iZMX/73T6D/7fsrXPEFrdSXJY6aQkJtP/OARIXuHsQgNDkcUiaedTeJpZ9N4YDfVRWup/uhlaopeJTl/MqkF04lxZ3W6vdfr5fCGpdRsep2kkYX0u3wuDqeMOekrAvqfUkr9ELgHuBkoBZ4ClgMdzQEwBthnrVMB3ASsU0qN0Vrv7omgRcfi+g9j0Nxn5WKu6LL4gcPJHjiP5qoDVH/6GrXb3qV269skjTiH1Akzjhvh4/W0UfXmM9Tt3EjKuGnWM6Olp9mXBJqu7wAWa63XAiilbgO+VkqNav8Aea31H9pte79S6ipgKiBJoJdJAhA9IbbfQLIun0v6+ddTs/kNare/Q92uj3Dl5eOeMIP4waPwtrX8fR6gtMJrcRdeLftfH3TCJKCUigNGA3f6lmmtS5RSe4ECzOmef7a9A8gAqrsTqBAi+KJTMsiYcgvuc2ZRu+1tare8SdmyBcTlDAOnk6aDWuYB6uMC6QlkYKacbn/+vwro/EThd+YCTmDdyYUmhAgVTlcSaefMIrXgcup2fkD1p6/RWlNF1hX/TtKoQrvDE90QSBLocv9OKXUx8CgwU2t9pKufI4QIDVHRsaSMnUpy/mQ8DXXdulNdhIZAruAcAjwc3+rvx/G9g79TShUCq4A5WuvQf9KFECJgjiinJIAwccIkoLVuAoqBC33LlFK5wBBgU0fbKKXGA28AP9dar+iRSIUQQvS4QEcH/RZ4Sim1HdgLPAls1Fp/Zv3gLwUma62/UUqdAfwJeA54XSnlmzSkQWtd07PhCyGE6I6ABvRqrZ8HHgGeBYqAo8D1VnECoADfY4xmAWnA3UCZ3+vXPRa1EEKIHhHwbX1a60cwiaD98g/wu3istV4ALOh+aEIIIXqb3NonhBARTJKAEEJEsFCd5ckJUF5ebnccQgjRZ/j9Zgb8YIhQTQI5ADfccIPdcQghRF+UA3wdyIqhmgS2AIWYUUVtNscihBB9hROTALYEukFIPlRGCCFEcMiFYSGEiGCSBIQQIoJJEhBCiAgmSUAIISKYJAEhhIhgkgSEECKCSRIQQogIJklACCEiWKjeMdxlSql5wB2AG3gHuF1r3eljMCOFUuoF4JZ2i+/UWj/lt86lwK+AXOAz4F+01gHfediXKaWuBH4KjANStNaOduUFwNPAKKAE89S89X7lSZiHL10JtAAvAL/QWoflHe//rL6UUhcAG9ttUqy1zvdbJxvzfJKpQDXwa631Y70dt12UUvcCVwOnAkeAV4B7tNZ1fuucBiwBCoByYKHW+gW/8mjgccxxHAOsAeZqrY91J7aw6gkopX4I3IPZOSdiEsFyW4MKLS9jbin3vZb4CpRSwzE75lJgLPAJsF4plWZDnHZIAN4HHm1foJTKANYDf8HUzR+BtUqpU/1Wexo4G5iCOdivB+7t5Zjt1Gl9+fHf1ya3K1sJpGKO058C9ymlbu6FOEPFRMwP+FhgNib5/cZXqJSKAd4EKjD70SJgiVLqfL/P+CVwHWb/mgKM9/+MrgqraSOUUtuAdVrr+633eZhJlM7QWn9ma3A2s3oCaK1v7aR8MXCW1vp8670D8yjRJ7TWvw1OlPbztWLbtWz/FfgZkKu19lrL/gxs1lrfZSXKKuAirfVGq/w2zEOYcrTWniD/GUHTSX0dt6zdNmdinls+VGtdYi17ALhEaz2u14MOAUqpq4HntNbp1vvpwAqgn69lr5RaCiRora9SSkVhEsQvtNZ/sMonAW9b21R3NZaw6QkopeKA0ZjWCQDWDrYX070SMF0pVaWU2qmUmmd1L33G849157XeS92ZutnoSwCWDXxXN2cBXuDP7cqzMKfWIpJSaq9Sar9SaoVSapBf0Xhgry8BWDYA+Uqp2OBGaZtMzGkwn/HApnandvz3sTxrm/f9yj/EPNXxrO4EEjZJAMjA/D3tz/9XYQ7GSPcWphs6CViMadk+4FeehdRdZ05UN1nA4Xbn/6v8yiJNGTAHuAJz/joL2KiUclnlndWnE3MchzWlVCpwF/C83+JA9jH817H2t8N0cx8LpwvDHXY9haG1ftnv7V+VUm3AM0qpe60WrtRf505UNx2Vh8951pOktdaA9r1XSv0fsB+4DFhFBO9r1hmLNZjBBf7XU7qyj/WIcOoJHAI8HJ8V+3F8hhWwDUjEdDHBnG+UuuvYieqmAkhXSvk/zem4lluk0lofBb4ChliLOqvPNuDb4EUWXNbp1xVAMjBTa93qVxzIPob/Otb+lk4397GwSQJa6ybMxaYLfcuUUrmYHW+TTWGFslFAPSZ5AmzGr+4sFyJ1B6ZuLmi3bBLf1c02TEutsF15JVDa28GFOus0UB6wz1q0GRhiHZ8+k4AdWuvmYMcXDNaF3aXAMGCa/9BQy2agQCmV4LfMfx8rwRyr/sfoeZge57buxBZuo4NuA54CbsJcEH4SQGs9ycawQoI1+mclpkUxFjO0bKXW+mdW+XBMEp0PvA78GLgROFVrfcSWoINIKZUODMKMe/89MMYq+hzTcvsScxA/B0wHFmJGnX1pbb/U2mYOpof1Imb0h/91l7Bxgvq6FTPOfRdmmPb9mEEbI30/fkqpDzEt/zsxDbU/Ysa8Lw3aHxFESqn/AS6xXv4PT6/SWrdZF8Q/xzwRbBHmgvCzmBFnH1qf8QBm/7oROAb8L7BFa31bd2ILm54AgNb6ecywvGeBIuAoZry2gBHAG5hztY9hfszm+Qq11ruBWZgDeAemVXtJJCQAy3RgO+YHDevf24H+WutvMQdvIaZubgWu9CUAy08wLbINmHO+K4GHghK5PTqtL8yNTIsxSeAtoBWY3K71ey3m+CzCHK8Ph2sCsMzB3C+xHXPh3PcaCGD1gC611tmKaYzd7ksAlgcw9/qswexnWzE3xnZLWPUEhBBCnJyw6gkIIYQ4OZIEhBAigkkSEEKICCZJQAghIpgkASGEiGCSBIQQIoJJEhBCiAgmSUAIISKYJAEhhIhg/w+3M0I9zbZMLQAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "results.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[]
nfev608
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events []\n", - "nfev 608\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.set(init=init, t_end=200)\n", - "results, details = run_ode_solver(system, slope_func, max_step=2)\n", - "details" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "results.plot()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Euler, running for a longer duration" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
xy
0.011
1.00.951
2.00.90250.995
3.00.8578260.985299
4.00.8161960.97129
\n", - "
" - ], - "text/plain": [ - " x y\n", - "0.0 1 1\n", - "1.0 0.95 1\n", - "2.0 0.9025 0.995\n", - "3.0 0.857826 0.985299\n", - "4.0 0.816196 0.97129" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.set(t_end=2000)\n", - "results = run_simulation(system, update_func)\n", - "results.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "results.plot()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Running the ODE solver for a longer duration" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[]
nfev1172
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events []\n", - "nfev 1172\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results, details = run_ode_solver(system, slope_func)\n", - "details" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "results.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/modsim.html b/code/modsim.html deleted file mode 100644 index 19f90956b..000000000 --- a/code/modsim.html +++ /dev/null @@ -1,58638 +0,0 @@ - -Python: module modsim - - - - - -
 
- 
modsim
index
/home/downey/ModSimPy/code/modsim.py
-

Code from Modeling and Simulation in Python.

-Copyright 2017 Allen Downey

-License: https://creativecommons.org/licenses/by/4.0)

-

- - - - - -
 
-Modules
       
inspect
-logging
-numpy
-
pandas
-pint
-matplotlib.pyplot
-
scipy
-seaborn
-sympy
-

- - - - - -
 
-Classes
       
-
builtins.object -
-
-
FigureState -
Simplot -
SubPlots -
-
-
numpy.ndarray(builtins.object) -
-
-
Array -
-
-
pandas.core.frame.DataFrame(pandas.core.generic.NDFrame) -
-
-
MyDataFrame -
-
-
SweepFrame -
TimeFrame -
-
-
-
-
pandas.core.series.Series(pandas.core.base.IndexOpsMixin, pandas.core.strings.StringAccessorMixin, pandas.core.generic.NDFrame) -
-
-
MySeries -
-
-
SweepSeries -
System -
-
-
Condition -
State -
-
-
TimeSeries -
-
-
-
-
-

- - - - - - - -
 
-class Array(numpy.ndarray)
   ndarray(shape, dtype=float, buffer=None, offset=0,
-        strides=None, order=None)

-An array object represents a multidimensional, homogeneous array
-of fixed-size items.  An associated data-type object describes the
-format of each element in the array (its byte-order, how many bytes it
-occupies in memory, whether it is an integer, a floating point number,
-or something else, etc.)

-Arrays should be constructed using `array`, `zeros` or `empty` (refer
-to the See Also section below).  The parameters given here refer to
-a low-level method (`ndarray(...)`) for instantiating an array.

-For more information, refer to the `numpy` module and examine the
-methods and attributes of an array.

-Parameters
-----------
-(for the __new__ method; see Notes below)

-shape : tuple of ints
-    Shape of created array.
-dtype : data-type, optional
-    Any object that can be interpreted as a numpy data type.
-buffer : object exposing buffer interface, optional
-    Used to fill the array with data.
-offset : int, optional
-    Offset of array data in buffer.
-strides : tuple of ints, optional
-    Strides of data in memory.
-order : {'C', 'F'}, optional
-    Row-major (C-style) or column-major (Fortran-style) order.

-Attributes
-----------
-T : ndarray
-    Transpose of the array.
-data : buffer
-    The array's elements, in memory.
-dtype : dtype object
-    Describes the format of the elements in the array.
-flags : dict
-    Dictionary containing information related to memory use, e.g.,
-    'C_CONTIGUOUS', 'OWNDATA', 'WRITEABLE', etc.
-flat : numpy.flatiter object
-    Flattened version of the array as an iterator.  The iterator
-    allows assignments, e.g., ``x.flat = 3`` (See `ndarray.flat` for
-    assignment examples; TODO).
-imag : ndarray
-    Imaginary part of the array.
-real : ndarray
-    Real part of the array.
-size : int
-    Number of elements in the array.
-itemsize : int
-    The memory use of each array element in bytes.
-nbytes : int
-    The total number of bytes required to store the array data,
-    i.e., ``itemsize * size``.
-ndim : int
-    The array's number of dimensions.
-shape : tuple of ints
-    Shape of the array.
-strides : tuple of ints
-    The step-size required to move from one element to the next in
-    memory. For example, a contiguous ``(3, 4)`` array of type
-    ``int16`` in C-order has strides ``(8, 2)``.  This implies that
-    to move from element to element in memory requires jumps of 2 bytes.
-    To move from row-to-row, one needs to jump 8 bytes at a time
-    (``2 * 4``).
-ctypes : ctypes object
-    Class containing properties of the array needed for interaction
-    with ctypes.
-base : ndarray
-    If the array is a view into another array, that array is its `base`
-    (unless that array is also a view).  The `base` array is where the
-    array data is actually stored.

-See Also
---------
-array : Construct an array.
-zeros : Create an array, each element of which is zero.
-empty : Create an array, but leave its allocated memory unchanged (i.e.,
-        it contains "garbage").
-dtype : Create a data-type.

-Notes
------
-There are two modes of creating an array using ``__new__``:

-1. If `buffer` is None, then only `shape`, `dtype`, and `order`
-   are used.
-2. If `buffer` is an object exposing the buffer interface, then
-   all keywords are interpreted.

-No ``__init__`` method is needed because the array is fully initialized
-after the ``__new__`` method.

-Examples
---------
-These examples illustrate the low-level `ndarray` constructor.  Refer
-to the `See Also` section above for easier ways of constructing an
-ndarray.

-First mode, `buffer` is None:

->>> np.ndarray(shape=(2,2), dtype=float, order='F')
-array([[ -1.13698227e+002,   4.25087011e-303],
-       [  2.88528414e-306,   3.27025015e-309]])         #random

-Second mode:

->>> np.ndarray((2,), buffer=np.array([1,2,3]),
-...            offset=np.int_().itemsize,
-...            dtype=int) # offset = 1*itemsize, i.e. skip first element
-array([2, 3])
 
 
Method resolution order:
-
Array
-
numpy.ndarray
-
builtins.object
-
-
-Data descriptors defined here:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
-Methods inherited from numpy.ndarray:
-
__abs__(self, /)
abs(self)
- -
__add__(self, value, /)
Return self+value.
- -
__and__(self, value, /)
Return self&value.
- -
__array__(...)
a.__array__(|dtype) -> reference if type unchanged, copy otherwise.

-Returns either a new reference to self if dtype is not given or a new array
-of provided data type if dtype is different from the current dtype of the
-array.
- -
__array_prepare__(...)
a.__array_prepare__(obj) -> Object of same type as ndarray object obj.
- -
__array_wrap__(...)
a.__array_wrap__(obj) -> Object of same type as ndarray object a.
- -
__bool__(self, /)
self != 0
- -
__complex__(...)
- -
__contains__(self, key, /)
Return key in self.
- -
__copy__(...)
a.__copy__([order])

-Return a copy of the array.

-Parameters
-----------
-order : {'C', 'F', 'A'}, optional
-    If order is 'C' (False) then the result is contiguous (default).
-    If order is 'Fortran' (True) then the result has fortran order.
-    If order is 'Any' (None) then the result has fortran order
-    only if the array already is in fortran order.
- -
__deepcopy__(...)
a.__deepcopy__() -> Deep copy of array.

-Used if copy.deepcopy is called on an array.
- -
__delitem__(self, key, /)
Delete self[key].
- -
__divmod__(self, value, /)
Return divmod(self, value).
- -
__eq__(self, value, /)
Return self==value.
- -
__float__(self, /)
float(self)
- -
__floordiv__(self, value, /)
Return self//value.
- -
__ge__(self, value, /)
Return self>=value.
- -
__getitem__(self, key, /)
Return self[key].
- -
__gt__(self, value, /)
Return self>value.
- -
__iadd__(self, value, /)
Return self+=value.
- -
__iand__(self, value, /)
Return self&=value.
- -
__ifloordiv__(self, value, /)
Return self//=value.
- -
__ilshift__(self, value, /)
Return self<<=value.
- -
__imatmul__(self, value, /)
Return self@=value.
- -
__imod__(self, value, /)
Return self%=value.
- -
__imul__(self, value, /)
Return self*=value.
- -
__index__(self, /)
Return self converted to an integer, if self is suitable for use as an index into a list.
- -
__int__(self, /)
int(self)
- -
__invert__(self, /)
~self
- -
__ior__(self, value, /)
Return self|=value.
- -
__ipow__(self, value, /)
Return self**=value.
- -
__irshift__(self, value, /)
Return self>>=value.
- -
__isub__(self, value, /)
Return self-=value.
- -
__iter__(self, /)
Implement iter(self).
- -
__itruediv__(self, value, /)
Return self/=value.
- -
__ixor__(self, value, /)
Return self^=value.
- -
__le__(self, value, /)
Return self<=value.
- -
__len__(self, /)
Return len(self).
- -
__lshift__(self, value, /)
Return self<<value.
- -
__lt__(self, value, /)
Return self<value.
- -
__matmul__(self, value, /)
Return self@value.
- -
__mod__(self, value, /)
Return self%value.
- -
__mul__(self, value, /)
Return self*value.
- -
__ne__(self, value, /)
Return self!=value.
- -
__neg__(self, /)
-self
- -
__new__(*args, **kwargs) from builtins.type
Create and return a new object.  See help(type) for accurate signature.
- -
__or__(self, value, /)
Return self|value.
- -
__pos__(self, /)
+self
- -
__pow__(self, value, mod=None, /)
Return pow(self, value, mod).
- -
__radd__(self, value, /)
Return value+self.
- -
__rand__(self, value, /)
Return value&self.
- -
__rdivmod__(self, value, /)
Return divmod(value, self).
- -
__reduce__(...)
a.__reduce__()

-For pickling.
- -
__repr__(self, /)
Return repr(self).
- -
__rfloordiv__(self, value, /)
Return value//self.
- -
__rlshift__(self, value, /)
Return value<<self.
- -
__rmatmul__(self, value, /)
Return value@self.
- -
__rmod__(self, value, /)
Return value%self.
- -
__rmul__(self, value, /)
Return value*self.
- -
__ror__(self, value, /)
Return value|self.
- -
__rpow__(self, value, mod=None, /)
Return pow(value, self, mod).
- -
__rrshift__(self, value, /)
Return value>>self.
- -
__rshift__(self, value, /)
Return self>>value.
- -
__rsub__(self, value, /)
Return value-self.
- -
__rtruediv__(self, value, /)
Return value/self.
- -
__rxor__(self, value, /)
Return value^self.
- -
__setitem__(self, key, value, /)
Set self[key] to value.
- -
__setstate__(...)
a.__setstate__(version, shape, dtype, isfortran, rawdata)

-For unpickling.

-Parameters
-----------
-version : int
-    optional pickle version. If omitted defaults to 0.
-shape : tuple
-dtype : data-type
-isFortran : bool
-rawdata : string or list
-    a binary string with the data (or a list if 'a' is an object array)
- -
__sizeof__(...)
__sizeof__() -> int
-size of object in memory, in bytes
- -
__str__(self, /)
Return str(self).
- -
__sub__(self, value, /)
Return self-value.
- -
__truediv__(self, value, /)
Return self/value.
- -
__xor__(self, value, /)
Return self^value.
- -
all(...)
a.all(axis=None, out=None, keepdims=False)

-Returns True if all elements evaluate to True.

-Refer to `numpy.all` for full documentation.

-See Also
---------
-numpy.all : equivalent function
- -
any(...)
a.any(axis=None, out=None, keepdims=False)

-Returns True if any of the elements of `a` evaluate to True.

-Refer to `numpy.any` for full documentation.

-See Also
---------
-numpy.any : equivalent function
- -
argmax(...)
a.argmax(axis=None, out=None)

-Return indices of the maximum values along the given axis.

-Refer to `numpy.argmax` for full documentation.

-See Also
---------
-numpy.argmax : equivalent function
- -
argmin(...)
a.argmin(axis=None, out=None)

-Return indices of the minimum values along the given axis of `a`.

-Refer to `numpy.argmin` for detailed documentation.

-See Also
---------
-numpy.argmin : equivalent function
- -
argpartition(...)
a.argpartition(kth, axis=-1, kind='introselect', order=None)

-Returns the indices that would partition this array.

-Refer to `numpy.argpartition` for full documentation.

-.. versionadded:: 1.8.0

-See Also
---------
-numpy.argpartition : equivalent function
- -
argsort(...)
a.argsort(axis=-1, kind='quicksort', order=None)

-Returns the indices that would sort this array.

-Refer to `numpy.argsort` for full documentation.

-See Also
---------
-numpy.argsort : equivalent function
- -
astype(...)
a.astype(dtype, order='K', casting='unsafe', subok=True, copy=True)

-Copy of the array, cast to a specified type.

-Parameters
-----------
-dtype : str or dtype
-    Typecode or data-type to which the array is cast.
-order : {'C', 'F', 'A', 'K'}, optional
-    Controls the memory layout order of the result.
-    'C' means C order, 'F' means Fortran order, 'A'
-    means 'F' order if all the arrays are Fortran contiguous,
-    'C' order otherwise, and 'K' means as close to the
-    order the array elements appear in memory as possible.
-    Default is 'K'.
-casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
-    Controls what kind of data casting may occur. Defaults to 'unsafe'
-    for backwards compatibility.

-      * 'no' means the data types should not be cast at all.
-      * 'equiv' means only byte-order changes are allowed.
-      * 'safe' means only casts which can preserve values are allowed.
-      * 'same_kind' means only safe casts or casts within a kind,
-        like float64 to float32, are allowed.
-      * 'unsafe' means any data conversions may be done.
-subok : bool, optional
-    If True, then sub-classes will be passed-through (default), otherwise
-    the returned array will be forced to be a base-class array.
-copy : bool, optional
-    By default, astype always returns a newly allocated array. If this
-    is set to false, and the `dtype`, `order`, and `subok`
-    requirements are satisfied, the input array is returned instead
-    of a copy.

-Returns
--------
-arr_t : ndarray
-    Unless `copy` is False and the other conditions for returning the input
-    array are satisfied (see description for `copy` input parameter), `arr_t`
-    is a new array of the same shape as the input array, with dtype, order
-    given by `dtype`, `order`.

-Notes
------
-Starting in NumPy 1.9, astype method now returns an error if the string
-dtype to cast to is not long enough in 'safe' casting mode to hold the max
-value of integer/float array that is being casted. Previously the casting
-was allowed even if the result was truncated.

-Raises
-------
-ComplexWarning
-    When casting from complex to float or int. To avoid this,
-    one should use ``a.real.astype(t)``.

-Examples
---------
->>> x = np.array([1, 2, 2.5])
->>> x
-array([ 1. ,  2. ,  2.5])

->>> x.astype(int)
-array([1, 2, 2])
- -
byteswap(...)
a.byteswap(inplace)

-Swap the bytes of the array elements

-Toggle between low-endian and big-endian data representation by
-returning a byteswapped array, optionally swapped in-place.

-Parameters
-----------
-inplace : bool, optional
-    If ``True``, swap bytes in-place, default is ``False``.

-Returns
--------
-out : ndarray
-    The byteswapped array. If `inplace` is ``True``, this is
-    a view to self.

-Examples
---------
->>> A = np.array([1, 256, 8755], dtype=np.int16)
->>> map(hex, A)
-['0x1', '0x100', '0x2233']
->>> A.byteswap(True)
-array([  256,     1, 13090], dtype=int16)
->>> map(hex, A)
-['0x100', '0x1', '0x3322']

-Arrays of strings are not swapped

->>> A = np.array(['ceg', 'fac'])
->>> A.byteswap()
-array(['ceg', 'fac'],
-      dtype='|S3')
- -
choose(...)
a.choose(choices, out=None, mode='raise')

-Use an index array to construct a new array from a set of choices.

-Refer to `numpy.choose` for full documentation.

-See Also
---------
-numpy.choose : equivalent function
- -
clip(...)
a.clip(min=None, max=None, out=None)

-Return an array whose values are limited to ``[min, max]``.
-One of max or min must be given.

-Refer to `numpy.clip` for full documentation.

-See Also
---------
-numpy.clip : equivalent function
- -
compress(...)
a.compress(condition, axis=None, out=None)

-Return selected slices of this array along given axis.

-Refer to `numpy.compress` for full documentation.

-See Also
---------
-numpy.compress : equivalent function
- -
conj(...)
a.conj()

-Complex-conjugate all elements.

-Refer to `numpy.conjugate` for full documentation.

-See Also
---------
-numpy.conjugate : equivalent function
- -
conjugate(...)
a.conjugate()

-Return the complex conjugate, element-wise.

-Refer to `numpy.conjugate` for full documentation.

-See Also
---------
-numpy.conjugate : equivalent function
- -
copy(...)
a.copy(order='C')

-Return a copy of the array.

-Parameters
-----------
-order : {'C', 'F', 'A', 'K'}, optional
-    Controls the memory layout of the copy. 'C' means C-order,
-    'F' means F-order, 'A' means 'F' if `a` is Fortran contiguous,
-    'C' otherwise. 'K' means match the layout of `a` as closely
-    as possible. (Note that this function and :func:numpy.copy are very
-    similar, but have different default values for their order=
-    arguments.)

-See also
---------
-numpy.copy
-numpy.copyto

-Examples
---------
->>> x = np.array([[1,2,3],[4,5,6]], order='F')

->>> y = x.copy()

->>> x.fill(0)

->>> x
-array([[0, 0, 0],
-       [0, 0, 0]])

->>> y
-array([[1, 2, 3],
-       [4, 5, 6]])

->>> y.flags['C_CONTIGUOUS']
-True
- -
cumprod(...)
a.cumprod(axis=None, dtype=None, out=None)

-Return the cumulative product of the elements along the given axis.

-Refer to `numpy.cumprod` for full documentation.

-See Also
---------
-numpy.cumprod : equivalent function
- -
cumsum(...)
a.cumsum(axis=None, dtype=None, out=None)

-Return the cumulative sum of the elements along the given axis.

-Refer to `numpy.cumsum` for full documentation.

-See Also
---------
-numpy.cumsum : equivalent function
- -
diagonal(...)
a.diagonal(offset=0, axis1=0, axis2=1)

-Return specified diagonals. In NumPy 1.9 the returned array is a
-read-only view instead of a copy as in previous NumPy versions.  In
-a future version the read-only restriction will be removed.

-Refer to :func:`numpy.diagonal` for full documentation.

-See Also
---------
-numpy.diagonal : equivalent function
- -
dot(...)
a.dot(b, out=None)

-Dot product of two arrays.

-Refer to `numpy.dot` for full documentation.

-See Also
---------
-numpy.dot : equivalent function

-Examples
---------
->>> a = np.eye(2)
->>> b = np.ones((2, 2)) * 2
->>> a.dot(b)
-array([[ 2.,  2.],
-       [ 2.,  2.]])

-This array method can be conveniently chained:

->>> a.dot(b).dot(b)
-array([[ 8.,  8.],
-       [ 8.,  8.]])
- -
dump(...)
a.dump(file)

-Dump a pickle of the array to the specified file.
-The array can be read back with pickle.load or numpy.load.

-Parameters
-----------
-file : str
-    A string naming the dump file.
- -
dumps(...)
a.dumps()

-Returns the pickle of the array as a string.
-pickle.loads or numpy.loads will convert the string back to an array.

-Parameters
-----------
-None
- -
fill(...)
a.fill(value)

-Fill the array with a scalar value.

-Parameters
-----------
-value : scalar
-    All elements of `a` will be assigned this value.

-Examples
---------
->>> a = np.array([1, 2])
->>> a.fill(0)
->>> a
-array([0, 0])
->>> a = np.empty(2)
->>> a.fill(1)
->>> a
-array([ 1.,  1.])
- -
flatten(...)
a.flatten(order='C')

-Return a copy of the array collapsed into one dimension.

-Parameters
-----------
-order : {'C', 'F', 'A', 'K'}, optional
-    'C' means to flatten in row-major (C-style) order.
-    'F' means to flatten in column-major (Fortran-
-    style) order. 'A' means to flatten in column-major
-    order if `a` is Fortran *contiguous* in memory,
-    row-major order otherwise. 'K' means to flatten
-    `a` in the order the elements occur in memory.
-    The default is 'C'.

-Returns
--------
-y : ndarray
-    A copy of the input array, flattened to one dimension.

-See Also
---------
-ravel : Return a flattened array.
-flat : A 1-D flat iterator over the array.

-Examples
---------
->>> a = np.array([[1,2], [3,4]])
->>> a.flatten()
-array([1, 2, 3, 4])
->>> a.flatten('F')
-array([1, 3, 2, 4])
- -
getfield(...)
a.getfield(dtype, offset=0)

-Returns a field of the given array as a certain type.

-A field is a view of the array data with a given data-type. The values in
-the view are determined by the given type and the offset into the current
-array in bytes. The offset needs to be such that the view dtype fits in the
-array dtype; for example an array of dtype complex128 has 16-byte elements.
-If taking a view with a 32-bit integer (4 bytes), the offset needs to be
-between 0 and 12 bytes.

-Parameters
-----------
-dtype : str or dtype
-    The data type of the view. The dtype size of the view can not be larger
-    than that of the array itself.
-offset : int
-    Number of bytes to skip before beginning the element view.

-Examples
---------
->>> x = np.diag([1.+1.j]*2)
->>> x[1, 1] = 2 + 4.j
->>> x
-array([[ 1.+1.j,  0.+0.j],
-       [ 0.+0.j,  2.+4.j]])
->>> x.getfield(np.float64)
-array([[ 1.,  0.],
-       [ 0.,  2.]])

-By choosing an offset of 8 bytes we can select the complex part of the
-array for our view:

->>> x.getfield(np.float64, offset=8)
-array([[ 1.,  0.],
-   [ 0.,  4.]])
- -
item(...)
a.item(*args)

-Copy an element of an array to a standard Python scalar and return it.

-Parameters
-----------
-\*args : Arguments (variable number and type)

-    * none: in this case, the method only works for arrays
-      with one element (`a.size == 1`), which element is
-      copied into a standard Python scalar object and returned.

-    * int_type: this argument is interpreted as a flat index into
-      the array, specifying which element to copy and return.

-    * tuple of int_types: functions as does a single int_type argument,
-      except that the argument is interpreted as an nd-index into the
-      array.

-Returns
--------
-z : Standard Python scalar object
-    A copy of the specified element of the array as a suitable
-    Python scalar

-Notes
------
-When the data type of `a` is longdouble or clongdouble, item() returns
-a scalar array object because there is no available Python scalar that
-would not lose information. Void arrays return a buffer object for item(),
-unless fields are defined, in which case a tuple is returned.

-`item` is very similar to a[args], except, instead of an array scalar,
-a standard Python scalar is returned. This can be useful for speeding up
-access to elements of the array and doing arithmetic on elements of the
-array using Python's optimized math.

-Examples
---------
->>> x = np.random.randint(9, size=(3, 3))
->>> x
-array([[3, 1, 7],
-       [2, 8, 3],
-       [8, 5, 3]])
->>> x.item(3)
-2
->>> x.item(7)
-5
->>> x.item((0, 1))
-1
->>> x.item((2, 2))
-3
- -
itemset(...)
a.itemset(*args)

-Insert scalar into an array (scalar is cast to array's dtype, if possible)

-There must be at least 1 argument, and define the last argument
-as *item*.  Then, ``a.itemset(*args)`` is equivalent to but faster
-than ``a[args] = item``.  The item should be a scalar value and `args`
-must select a single item in the array `a`.

-Parameters
-----------
-\*args : Arguments
-    If one argument: a scalar, only used in case `a` is of size 1.
-    If two arguments: the last argument is the value to be set
-    and must be a scalar, the first argument specifies a single array
-    element location. It is either an int or a tuple.

-Notes
------
-Compared to indexing syntax, `itemset` provides some speed increase
-for placing a scalar into a particular location in an `ndarray`,
-if you must do this.  However, generally this is discouraged:
-among other problems, it complicates the appearance of the code.
-Also, when using `itemset` (and `item`) inside a loop, be sure
-to assign the methods to a local variable to avoid the attribute
-look-up at each loop iteration.

-Examples
---------
->>> x = np.random.randint(9, size=(3, 3))
->>> x
-array([[3, 1, 7],
-       [2, 8, 3],
-       [8, 5, 3]])
->>> x.itemset(4, 0)
->>> x.itemset((2, 2), 9)
->>> x
-array([[3, 1, 7],
-       [2, 0, 3],
-       [8, 5, 9]])
- -
max(...)
a.max(axis=None, out=None)

-Return the maximum along a given axis.

-Refer to `numpy.amax` for full documentation.

-See Also
---------
-numpy.amax : equivalent function
- -
mean(...)
a.mean(axis=None, dtype=None, out=None, keepdims=False)

-Returns the average of the array elements along given axis.

-Refer to `numpy.mean` for full documentation.

-See Also
---------
-numpy.mean : equivalent function
- -
min(...)
a.min(axis=None, out=None, keepdims=False)

-Return the minimum along a given axis.

-Refer to `numpy.amin` for full documentation.

-See Also
---------
-numpy.amin : equivalent function
- -
newbyteorder(...)
arr.newbyteorder(new_order='S')

-Return the array with the same data viewed with a different byte order.

-Equivalent to::

-    arr.view(arr.dtype.newbytorder(new_order))

-Changes are also made in all fields and sub-arrays of the array data
-type.



-Parameters
-----------
-new_order : string, optional
-    Byte order to force; a value from the byte order specifications
-    below. `new_order` codes can be any of:

-    * 'S' - swap dtype from current to opposite endian
-    * {'<', 'L'} - little endian
-    * {'>', 'B'} - big endian
-    * {'=', 'N'} - native order
-    * {'|', 'I'} - ignore (no change to byte order)

-    The default value ('S') results in swapping the current
-    byte order. The code does a case-insensitive check on the first
-    letter of `new_order` for the alternatives above.  For example,
-    any of 'B' or 'b' or 'biggish' are valid to specify big-endian.


-Returns
--------
-new_arr : array
-    New array object with the dtype reflecting given change to the
-    byte order.
- -
nonzero(...)
a.nonzero()

-Return the indices of the elements that are non-zero.

-Refer to `numpy.nonzero` for full documentation.

-See Also
---------
-numpy.nonzero : equivalent function
- -
partition(...)
a.partition(kth, axis=-1, kind='introselect', order=None)

-Rearranges the elements in the array in such a way that value of the
-element in kth position is in the position it would be in a sorted array.
-All elements smaller than the kth element are moved before this element and
-all equal or greater are moved behind it. The ordering of the elements in
-the two partitions is undefined.

-.. versionadded:: 1.8.0

-Parameters
-----------
-kth : int or sequence of ints
-    Element index to partition by. The kth element value will be in its
-    final sorted position and all smaller elements will be moved before it
-    and all equal or greater elements behind it.
-    The order all elements in the partitions is undefined.
-    If provided with a sequence of kth it will partition all elements
-    indexed by kth of them into their sorted position at once.
-axis : int, optional
-    Axis along which to sort. Default is -1, which means sort along the
-    last axis.
-kind : {'introselect'}, optional
-    Selection algorithm. Default is 'introselect'.
-order : str or list of str, optional
-    When `a` is an array with fields defined, this argument specifies
-    which fields to compare first, second, etc.  A single field can
-    be specified as a string, and not all fields need be specified,
-    but unspecified fields will still be used, in the order in which
-    they come up in the dtype, to break ties.

-See Also
---------
-numpy.partition : Return a parititioned copy of an array.
-argpartition : Indirect partition.
-sort : Full sort.

-Notes
------
-See ``np.partition`` for notes on the different algorithms.

-Examples
---------
->>> a = np.array([3, 4, 2, 1])
->>> a.partition(3)
->>> a
-array([2, 1, 3, 4])

->>> a.partition((1, 3))
-array([1, 2, 3, 4])
- -
prod(...)
a.prod(axis=None, dtype=None, out=None, keepdims=False)

-Return the product of the array elements over the given axis

-Refer to `numpy.prod` for full documentation.

-See Also
---------
-numpy.prod : equivalent function
- -
ptp(...)
a.ptp(axis=None, out=None)

-Peak to peak (maximum - minimum) value along a given axis.

-Refer to `numpy.ptp` for full documentation.

-See Also
---------
-numpy.ptp : equivalent function
- -
put(...)
a.put(indices, values, mode='raise')

-Set ``a.flat[n] = values[n]`` for all `n` in indices.

-Refer to `numpy.put` for full documentation.

-See Also
---------
-numpy.put : equivalent function
- -
ravel(...)
a.ravel([order])

-Return a flattened array.

-Refer to `numpy.ravel` for full documentation.

-See Also
---------
-numpy.ravel : equivalent function

-ndarray.flat : a flat iterator on the array.
- -
repeat(...)
a.repeat(repeats, axis=None)

-Repeat elements of an array.

-Refer to `numpy.repeat` for full documentation.

-See Also
---------
-numpy.repeat : equivalent function
- -
reshape(...)
a.reshape(shape, order='C')

-Returns an array containing the same data with a new shape.

-Refer to `numpy.reshape` for full documentation.

-See Also
---------
-numpy.reshape : equivalent function
- -
resize(...)
a.resize(new_shape, refcheck=True)

-Change shape and size of array in-place.

-Parameters
-----------
-new_shape : tuple of ints, or `n` ints
-    Shape of resized array.
-refcheck : bool, optional
-    If False, reference count will not be checked. Default is True.

-Returns
--------
-None

-Raises
-------
-ValueError
-    If `a` does not own its own data or references or views to it exist,
-    and the data memory must be changed.
-    PyPy only: will always raise if the data memory must be changed, since
-    there is no reliable way to determine if references or views to it
-    exist.

-SystemError
-    If the `order` keyword argument is specified. This behaviour is a
-    bug in NumPy.

-See Also
---------
-resize : Return a new array with the specified shape.

-Notes
------
-This reallocates space for the data area if necessary.

-Only contiguous arrays (data elements consecutive in memory) can be
-resized.

-The purpose of the reference count check is to make sure you
-do not use this array as a buffer for another Python object and then
-reallocate the memory. However, reference counts can increase in
-other ways so if you are sure that you have not shared the memory
-for this array with another Python object, then you may safely set
-`refcheck` to False.

-Examples
---------
-Shrinking an array: array is flattened (in the order that the data are
-stored in memory), resized, and reshaped:

->>> a = np.array([[0, 1], [2, 3]], order='C')
->>> a.resize((2, 1))
->>> a
-array([[0],
-       [1]])

->>> a = np.array([[0, 1], [2, 3]], order='F')
->>> a.resize((2, 1))
->>> a
-array([[0],
-       [2]])

-Enlarging an array: as above, but missing entries are filled with zeros:

->>> b = np.array([[0, 1], [2, 3]])
->>> b.resize(2, 3) # new_shape parameter doesn't have to be a tuple
->>> b
-array([[0, 1, 2],
-       [3, 0, 0]])

-Referencing an array prevents resizing...

->>> c = a
->>> a.resize((1, 1))
-Traceback (most recent call last):
-...
-ValueError: cannot resize an array that has been referenced ...

-Unless `refcheck` is False:

->>> a.resize((1, 1), refcheck=False)
->>> a
-array([[0]])
->>> c
-array([[0]])
- -
round(...)
a.round(decimals=0, out=None)

-Return `a` with each element rounded to the given number of decimals.

-Refer to `numpy.around` for full documentation.

-See Also
---------
-numpy.around : equivalent function
- -
searchsorted(...)
a.searchsorted(v, side='left', sorter=None)

-Find indices where elements of v should be inserted in a to maintain order.

-For full documentation, see `numpy.searchsorted`

-See Also
---------
-numpy.searchsorted : equivalent function
- -
setfield(...)
a.setfield(val, dtype, offset=0)

-Put a value into a specified place in a field defined by a data-type.

-Place `val` into `a`'s field defined by `dtype` and beginning `offset`
-bytes into the field.

-Parameters
-----------
-val : object
-    Value to be placed in field.
-dtype : dtype object
-    Data-type of the field in which to place `val`.
-offset : int, optional
-    The number of bytes into the field at which to place `val`.

-Returns
--------
-None

-See Also
---------
-getfield

-Examples
---------
->>> x = np.eye(3)
->>> x.getfield(np.float64)
-array([[ 1.,  0.,  0.],
-       [ 0.,  1.,  0.],
-       [ 0.,  0.,  1.]])
->>> x.setfield(3, np.int32)
->>> x.getfield(np.int32)
-array([[3, 3, 3],
-       [3, 3, 3],
-       [3, 3, 3]])
->>> x
-array([[  1.00000000e+000,   1.48219694e-323,   1.48219694e-323],
-       [  1.48219694e-323,   1.00000000e+000,   1.48219694e-323],
-       [  1.48219694e-323,   1.48219694e-323,   1.00000000e+000]])
->>> x.setfield(np.eye(3), np.int32)
->>> x
-array([[ 1.,  0.,  0.],
-       [ 0.,  1.,  0.],
-       [ 0.,  0.,  1.]])
- -
setflags(...)
a.setflags(write=None, align=None, uic=None)

-Set array flags WRITEABLE, ALIGNED, and UPDATEIFCOPY, respectively.

-These Boolean-valued flags affect how numpy interprets the memory
-area used by `a` (see Notes below). The ALIGNED flag can only
-be set to True if the data is actually aligned according to the type.
-The UPDATEIFCOPY flag can never be set to True. The flag WRITEABLE
-can only be set to True if the array owns its own memory, or the
-ultimate owner of the memory exposes a writeable buffer interface,
-or is a string. (The exception for string is made so that unpickling
-can be done without copying memory.)

-Parameters
-----------
-write : bool, optional
-    Describes whether or not `a` can be written to.
-align : bool, optional
-    Describes whether or not `a` is aligned properly for its type.
-uic : bool, optional
-    Describes whether or not `a` is a copy of another "base" array.

-Notes
------
-Array flags provide information about how the memory area used
-for the array is to be interpreted. There are 6 Boolean flags
-in use, only three of which can be changed by the user:
-UPDATEIFCOPY, WRITEABLE, and ALIGNED.

-WRITEABLE (W) the data area can be written to;

-ALIGNED (A) the data and strides are aligned appropriately for the hardware
-(as determined by the compiler);

-UPDATEIFCOPY (U) this array is a copy of some other array (referenced
-by .base). When this array is deallocated, the base array will be
-updated with the contents of this array.

-All flags can be accessed using their first (upper case) letter as well
-as the full name.

-Examples
---------
->>> y
-array([[3, 1, 7],
-       [2, 0, 0],
-       [8, 5, 9]])
->>> y.flags
-  C_CONTIGUOUS : True
-  F_CONTIGUOUS : False
-  OWNDATA : True
-  WRITEABLE : True
-  ALIGNED : True
-  UPDATEIFCOPY : False
->>> y.setflags(write=0, align=0)
->>> y.flags
-  C_CONTIGUOUS : True
-  F_CONTIGUOUS : False
-  OWNDATA : True
-  WRITEABLE : False
-  ALIGNED : False
-  UPDATEIFCOPY : False
->>> y.setflags(uic=1)
-Traceback (most recent call last):
-  File "<stdin>", line 1, in <module>
-ValueError: cannot set UPDATEIFCOPY flag to True
- -
sort(...)
a.sort(axis=-1, kind='quicksort', order=None)

-Sort an array, in-place.

-Parameters
-----------
-axis : int, optional
-    Axis along which to sort. Default is -1, which means sort along the
-    last axis.
-kind : {'quicksort', 'mergesort', 'heapsort'}, optional
-    Sorting algorithm. Default is 'quicksort'.
-order : str or list of str, optional
-    When `a` is an array with fields defined, this argument specifies
-    which fields to compare first, second, etc.  A single field can
-    be specified as a string, and not all fields need be specified,
-    but unspecified fields will still be used, in the order in which
-    they come up in the dtype, to break ties.

-See Also
---------
-numpy.sort : Return a sorted copy of an array.
-argsort : Indirect sort.
-lexsort : Indirect stable sort on multiple keys.
-searchsorted : Find elements in sorted array.
-partition: Partial sort.

-Notes
------
-See ``sort`` for notes on the different sorting algorithms.

-Examples
---------
->>> a = np.array([[1,4], [3,1]])
->>> a.sort(axis=1)
->>> a
-array([[1, 4],
-       [1, 3]])
->>> a.sort(axis=0)
->>> a
-array([[1, 3],
-       [1, 4]])

-Use the `order` keyword to specify a field to use when sorting a
-structured array:

->>> a = np.array([('a', 2), ('c', 1)], dtype=[('x', 'S1'), ('y', int)])
->>> a.sort(order='y')
->>> a
-array([('c', 1), ('a', 2)],
-      dtype=[('x', '|S1'), ('y', '<i4')])
- -
squeeze(...)
a.squeeze(axis=None)

-Remove single-dimensional entries from the shape of `a`.

-Refer to `numpy.squeeze` for full documentation.

-See Also
---------
-numpy.squeeze : equivalent function
- -
std(...)
a.std(axis=None, dtype=None, out=None, ddof=0, keepdims=False)

-Returns the standard deviation of the array elements along given axis.

-Refer to `numpy.std` for full documentation.

-See Also
---------
-numpy.std : equivalent function
- -
sum(...)
a.sum(axis=None, dtype=None, out=None, keepdims=False)

-Return the sum of the array elements over the given axis.

-Refer to `numpy.sum` for full documentation.

-See Also
---------
-numpy.sum : equivalent function
- -
swapaxes(...)
a.swapaxes(axis1, axis2)

-Return a view of the array with `axis1` and `axis2` interchanged.

-Refer to `numpy.swapaxes` for full documentation.

-See Also
---------
-numpy.swapaxes : equivalent function
- -
take(...)
a.take(indices, axis=None, out=None, mode='raise')

-Return an array formed from the elements of `a` at the given indices.

-Refer to `numpy.take` for full documentation.

-See Also
---------
-numpy.take : equivalent function
- -
tobytes(...)
a.tobytes(order='C')

-Construct Python bytes containing the raw data bytes in the array.

-Constructs Python bytes showing a copy of the raw contents of
-data memory. The bytes object can be produced in either 'C' or 'Fortran',
-or 'Any' order (the default is 'C'-order). 'Any' order means C-order
-unless the F_CONTIGUOUS flag in the array is set, in which case it
-means 'Fortran' order.

-.. versionadded:: 1.9.0

-Parameters
-----------
-order : {'C', 'F', None}, optional
-    Order of the data for multidimensional arrays:
-    C, Fortran, or the same as for the original array.

-Returns
--------
-s : bytes
-    Python bytes exhibiting a copy of `a`'s raw data.

-Examples
---------
->>> x = np.array([[0, 1], [2, 3]])
->>> x.tobytes()
-b'\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00'
->>> x.tobytes('C') == x.tobytes()
-True
->>> x.tobytes('F')
-b'\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00'
- -
tofile(...)
a.tofile(fid, sep="", format="%s")

-Write array to a file as text or binary (default).

-Data is always written in 'C' order, independent of the order of `a`.
-The data produced by this method can be recovered using the function
-fromfile().

-Parameters
-----------
-fid : file or str
-    An open file object, or a string containing a filename.
-sep : str
-    Separator between array items for text output.
-    If "" (empty), a binary file is written, equivalent to
-    ``file.write(a.tobytes())``.
-format : str
-    Format string for text file output.
-    Each entry in the array is formatted to text by first converting
-    it to the closest Python type, and then using "format" % item.

-Notes
------
-This is a convenience function for quick storage of array data.
-Information on endianness and precision is lost, so this method is not a
-good choice for files intended to archive data or transport data between
-machines with different endianness. Some of these problems can be overcome
-by outputting the data as text files, at the expense of speed and file
-size.
- -
tolist(...)
a.tolist()

-Return the array as a (possibly nested) list.

-Return a copy of the array data as a (nested) Python list.
-Data items are converted to the nearest compatible Python type.

-Parameters
-----------
-none

-Returns
--------
-y : list
-    The possibly nested list of array elements.

-Notes
------
-The array may be recreated, ``a = np.array(a.tolist())``.

-Examples
---------
->>> a = np.array([1, 2])
->>> a.tolist()
-[1, 2]
->>> a = np.array([[1, 2], [3, 4]])
->>> list(a)
-[array([1, 2]), array([3, 4])]
->>> a.tolist()
-[[1, 2], [3, 4]]
- -
tostring(...)
a.tostring(order='C')

-Construct Python bytes containing the raw data bytes in the array.

-Constructs Python bytes showing a copy of the raw contents of
-data memory. The bytes object can be produced in either 'C' or 'Fortran',
-or 'Any' order (the default is 'C'-order). 'Any' order means C-order
-unless the F_CONTIGUOUS flag in the array is set, in which case it
-means 'Fortran' order.

-This function is a compatibility alias for tobytes. Despite its name it returns bytes not strings.

-Parameters
-----------
-order : {'C', 'F', None}, optional
-    Order of the data for multidimensional arrays:
-    C, Fortran, or the same as for the original array.

-Returns
--------
-s : bytes
-    Python bytes exhibiting a copy of `a`'s raw data.

-Examples
---------
->>> x = np.array([[0, 1], [2, 3]])
->>> x.tobytes()
-b'\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00'
->>> x.tobytes('C') == x.tobytes()
-True
->>> x.tobytes('F')
-b'\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00'
- -
trace(...)
a.trace(offset=0, axis1=0, axis2=1, dtype=None, out=None)

-Return the sum along diagonals of the array.

-Refer to `numpy.trace` for full documentation.

-See Also
---------
-numpy.trace : equivalent function
- -
transpose(...)
a.transpose(*axes)

-Returns a view of the array with axes transposed.

-For a 1-D array, this has no effect. (To change between column and
-row vectors, first cast the 1-D array into a matrix object.)
-For a 2-D array, this is the usual matrix transpose.
-For an n-D array, if axes are given, their order indicates how the
-axes are permuted (see Examples). If axes are not provided and
-``a.shape = (i[0], i[1], ... i[n-2], i[n-1])``, then
-``a.transpose().shape = (i[n-1], i[n-2], ... i[1], i[0])``.

-Parameters
-----------
-axes : None, tuple of ints, or `n` ints

- * None or no argument: reverses the order of the axes.

- * tuple of ints: `i` in the `j`-th place in the tuple means `a`'s
-   `i`-th axis becomes `a.transpose()`'s `j`-th axis.

- * `n` ints: same as an n-tuple of the same ints (this form is
-   intended simply as a "convenience" alternative to the tuple form)

-Returns
--------
-out : ndarray
-    View of `a`, with axes suitably permuted.

-See Also
---------
-ndarray.T : Array property returning the array transposed.

-Examples
---------
->>> a = np.array([[1, 2], [3, 4]])
->>> a
-array([[1, 2],
-       [3, 4]])
->>> a.transpose()
-array([[1, 3],
-       [2, 4]])
->>> a.transpose((1, 0))
-array([[1, 3],
-       [2, 4]])
->>> a.transpose(1, 0)
-array([[1, 3],
-       [2, 4]])
- -
var(...)
a.var(axis=None, dtype=None, out=None, ddof=0, keepdims=False)

-Returns the variance of the array elements, along given axis.

-Refer to `numpy.var` for full documentation.

-See Also
---------
-numpy.var : equivalent function
- -
view(...)
a.view(dtype=None, type=None)

-New view of array with the same data.

-Parameters
-----------
-dtype : data-type or ndarray sub-class, optional
-    Data-type descriptor of the returned view, e.g., float32 or int16. The
-    default, None, results in the view having the same data-type as `a`.
-    This argument can also be specified as an ndarray sub-class, which
-    then specifies the type of the returned object (this is equivalent to
-    setting the ``type`` parameter).
-type : Python type, optional
-    Type of the returned view, e.g., ndarray or matrix.  Again, the
-    default None results in type preservation.

-Notes
------
-``a.view()`` is used two different ways:

-``a.view(some_dtype)`` or ``a.view(dtype=some_dtype)`` constructs a view
-of the array's memory with a different data-type.  This can cause a
-reinterpretation of the bytes of memory.

-``a.view(ndarray_subclass)`` or ``a.view(type=ndarray_subclass)`` just
-returns an instance of `ndarray_subclass` that looks at the same array
-(same shape, dtype, etc.)  This does not cause a reinterpretation of the
-memory.

-For ``a.view(some_dtype)``, if ``some_dtype`` has a different number of
-bytes per entry than the previous dtype (for example, converting a
-regular array to a structured array), then the behavior of the view
-cannot be predicted just from the superficial appearance of ``a`` (shown
-by ``print(a)``). It also depends on exactly how ``a`` is stored in
-memory. Therefore if ``a`` is C-ordered versus fortran-ordered, versus
-defined as a slice or transpose, etc., the view may give different
-results.


-Examples
---------
->>> x = np.array([(1, 2)], dtype=[('a', np.int8), ('b', np.int8)])

-Viewing array data using a different type and dtype:

->>> y = x.view(dtype=np.int16, type=np.matrix)
->>> y
-matrix([[513]], dtype=int16)
->>> print(type(y))
-<class 'numpy.matrixlib.defmatrix.matrix'>

-Creating a view on a structured array so it can be used in calculations

->>> x = np.array([(1, 2),(3,4)], dtype=[('a', np.int8), ('b', np.int8)])
->>> xv = x.view(dtype=np.int8).reshape(-1,2)
->>> xv
-array([[1, 2],
-       [3, 4]], dtype=int8)
->>> xv.mean(0)
-array([ 2.,  3.])

-Making changes to the view changes the underlying array

->>> xv[0,1] = 20
->>> print(x)
-[(1, 20) (3, 4)]

-Using a view to convert an array to a recarray:

->>> z = x.view(np.recarray)
->>> z.a
-array([1], dtype=int8)

-Views share data:

->>> x[0] = (9, 10)
->>> z[0]
-(9, 10)

-Views that change the dtype size (bytes per entry) should normally be
-avoided on arrays defined by slices, transposes, fortran-ordering, etc.:

->>> x = np.array([[1,2,3],[4,5,6]], dtype=np.int16)
->>> y = x[:, 0:2]
->>> y
-array([[1, 2],
-       [4, 5]], dtype=int16)
->>> y.view(dtype=[('width', np.int16), ('length', np.int16)])
-Traceback (most recent call last):
-  File "<stdin>", line 1, in <module>
-ValueError: new type not compatible with array.
->>> z = y.copy()
->>> z.view(dtype=[('width', np.int16), ('length', np.int16)])
-array([[(1, 2)],
-       [(4, 5)]], dtype=[('width', '<i2'), ('length', '<i2')])
- -
-Data descriptors inherited from numpy.ndarray:
-
T
-
Same as self.transpose(), except that self is returned if
-self.ndim < 2.

-Examples
---------
->>> x = np.array([[1.,2.],[3.,4.]])
->>> x
-array([[ 1.,  2.],
-       [ 3.,  4.]])
->>> x.T
-array([[ 1.,  3.],
-       [ 2.,  4.]])
->>> x = np.array([1.,2.,3.,4.])
->>> x
-array([ 1.,  2.,  3.,  4.])
->>> x.T
-array([ 1.,  2.,  3.,  4.])
-
-
__array_finalize__
-
None.
-
-
__array_interface__
-
Array protocol: Python side.
-
-
__array_priority__
-
Array priority.
-
-
__array_struct__
-
Array protocol: C-struct side.
-
-
base
-
Base object if memory is from some other object.

-Examples
---------
-The base of an array that owns its memory is None:

->>> x = np.array([1,2,3,4])
->>> x.base is None
-True

-Slicing creates a view, whose memory is shared with x:

->>> y = x[2:]
->>> y.base is x
-True
-
-
ctypes
-
An object to simplify the interaction of the array with the ctypes
-module.

-This attribute creates an object that makes it easier to use arrays
-when calling shared libraries with the ctypes module. The returned
-object has, among others, data, shape, and strides attributes (see
-Notes below) which themselves return ctypes objects that can be used
-as arguments to a shared library.

-Parameters
-----------
-None

-Returns
--------
-c : Python object
-    Possessing attributes data, shape, strides, etc.

-See Also
---------
-numpy.ctypeslib

-Notes
------
-Below are the public attributes of this object which were documented
-in "Guide to NumPy" (we have omitted undocumented public attributes,
-as well as documented private attributes):

-* data: A pointer to the memory area of the array as a Python integer.
-  This memory area may contain data that is not aligned, or not in correct
-  byte-order. The memory area may not even be writeable. The array
-  flags and data-type of this array should be respected when passing this
-  attribute to arbitrary C-code to avoid trouble that can include Python
-  crashing. User Beware! The value of this attribute is exactly the same
-  as self._array_interface_['data'][0].

-* shape (c_intp*self.ndim): A ctypes array of length self.ndim where
-  the basetype is the C-integer corresponding to dtype('p') on this
-  platform. This base-type could be c_int, c_long, or c_longlong
-  depending on the platform. The c_intp type is defined accordingly in
-  numpy.ctypeslib. The ctypes array contains the shape of the underlying
-  array.

-* strides (c_intp*self.ndim): A ctypes array of length self.ndim where
-  the basetype is the same as for the shape attribute. This ctypes array
-  contains the strides information from the underlying array. This strides
-  information is important for showing how many bytes must be jumped to
-  get to the next element in the array.

-* data_as(obj): Return the data pointer cast to a particular c-types object.
-  For example, calling self._as_parameter_ is equivalent to
-  self.data_as(ctypes.c_void_p). Perhaps you want to use the data as a
-  pointer to a ctypes array of floating-point data:
-  self.data_as(ctypes.POINTER(ctypes.c_double)).

-* shape_as(obj): Return the shape tuple as an array of some other c-types
-  type. For example: self.shape_as(ctypes.c_short).

-* strides_as(obj): Return the strides tuple as an array of some other
-  c-types type. For example: self.strides_as(ctypes.c_longlong).

-Be careful using the ctypes attribute - especially on temporary
-arrays or arrays constructed on the fly. For example, calling
-``(a+b).ctypes.data_as(ctypes.c_void_p)`` returns a pointer to memory
-that is invalid because the array created as (a+b) is deallocated
-before the next Python statement. You can avoid this problem using
-either ``c=a+b`` or ``ct=(a+b).ctypes``. In the latter case, ct will
-hold a reference to the array until ct is deleted or re-assigned.

-If the ctypes module is not available, then the ctypes attribute
-of array objects still returns something useful, but ctypes objects
-are not returned and errors may be raised instead. In particular,
-the object will still have the as parameter attribute which will
-return an integer equal to the data attribute.

-Examples
---------
->>> import ctypes
->>> x
-array([[0, 1],
-       [2, 3]])
->>> x.ctypes.data
-30439712
->>> x.ctypes.data_as(ctypes.POINTER(ctypes.c_long))
-<ctypes.LP_c_long object at 0x01F01300>
->>> x.ctypes.data_as(ctypes.POINTER(ctypes.c_long)).contents
-c_long(0)
->>> x.ctypes.data_as(ctypes.POINTER(ctypes.c_longlong)).contents
-c_longlong(4294967296L)
->>> x.ctypes.shape
-<numpy.core._internal.c_long_Array_2 object at 0x01FFD580>
->>> x.ctypes.shape_as(ctypes.c_long)
-<numpy.core._internal.c_long_Array_2 object at 0x01FCE620>
->>> x.ctypes.strides
-<numpy.core._internal.c_long_Array_2 object at 0x01FCE620>
->>> x.ctypes.strides_as(ctypes.c_longlong)
-<numpy.core._internal.c_longlong_Array_2 object at 0x01F01300>
-
-
data
-
Python buffer object pointing to the start of the array's data.
-
-
dtype
-
Data-type of the array's elements.

-Parameters
-----------
-None

-Returns
--------
-d : numpy dtype object

-See Also
---------
-numpy.dtype

-Examples
---------
->>> x
-array([[0, 1],
-       [2, 3]])
->>> x.dtype
-dtype('int32')
->>> type(x.dtype)
-<type 'numpy.dtype'>
-
-
flags
-
Information about the memory layout of the array.

-Attributes
-----------
-C_CONTIGUOUS (C)
-    The data is in a single, C-style contiguous segment.
-F_CONTIGUOUS (F)
-    The data is in a single, Fortran-style contiguous segment.
-OWNDATA (O)
-    The array owns the memory it uses or borrows it from another object.
-WRITEABLE (W)
-    The data area can be written to.  Setting this to False locks
-    the data, making it read-only.  A view (slice, etc.) inherits WRITEABLE
-    from its base array at creation time, but a view of a writeable
-    array may be subsequently locked while the base array remains writeable.
-    (The opposite is not true, in that a view of a locked array may not
-    be made writeable.  However, currently, locking a base object does not
-    lock any views that already reference it, so under that circumstance it
-    is possible to alter the contents of a locked array via a previously
-    created writeable view onto it.)  Attempting to change a non-writeable
-    array raises a RuntimeError exception.
-ALIGNED (A)
-    The data and all elements are aligned appropriately for the hardware.
-UPDATEIFCOPY (U)
-    This array is a copy of some other array. When this array is
-    deallocated, the base array will be updated with the contents of
-    this array.
-FNC
-    F_CONTIGUOUS and not C_CONTIGUOUS.
-FORC
-    F_CONTIGUOUS or C_CONTIGUOUS (one-segment test).
-BEHAVED (B)
-    ALIGNED and WRITEABLE.
-CARRAY (CA)
-    BEHAVED and C_CONTIGUOUS.
-FARRAY (FA)
-    BEHAVED and F_CONTIGUOUS and not C_CONTIGUOUS.

-Notes
------
-The `flags` object can be accessed dictionary-like (as in ``a.flags['WRITEABLE']``),
-or by using lowercased attribute names (as in ``a.flags.writeable``). Short flag
-names are only supported in dictionary access.

-Only the UPDATEIFCOPY, WRITEABLE, and ALIGNED flags can be changed by
-the user, via direct assignment to the attribute or dictionary entry,
-or by calling `ndarray.setflags`.

-The array flags cannot be set arbitrarily:

-- UPDATEIFCOPY can only be set ``False``.
-- ALIGNED can only be set ``True`` if the data is truly aligned.
-- WRITEABLE can only be set ``True`` if the array owns its own memory
-  or the ultimate owner of the memory exposes a writeable buffer
-  interface or is a string.

-Arrays can be both C-style and Fortran-style contiguous simultaneously.
-This is clear for 1-dimensional arrays, but can also be true for higher
-dimensional arrays.

-Even for contiguous arrays a stride for a given dimension
-``arr.strides[dim]`` may be *arbitrary* if ``arr.shape[dim] == 1``
-or the array has no elements.
-It does *not* generally hold that ``self.strides[-1] == self.itemsize``
-for C-style contiguous arrays or ``self.strides[0] == self.itemsize`` for
-Fortran-style contiguous arrays is true.
-
-
flat
-
A 1-D iterator over the array.

-This is a `numpy.flatiter` instance, which acts similarly to, but is not
-a subclass of, Python's built-in iterator object.

-See Also
---------
-flatten : Return a copy of the array collapsed into one dimension.

-flatiter

-Examples
---------
->>> x = np.arange(1, 7).reshape(2, 3)
->>> x
-array([[1, 2, 3],
-       [4, 5, 6]])
->>> x.flat[3]
-4
->>> x.T
-array([[1, 4],
-       [2, 5],
-       [3, 6]])
->>> x.T.flat[3]
-5
->>> type(x.flat)
-<type 'numpy.flatiter'>

-An assignment example:

->>> x.flat = 3; x
-array([[3, 3, 3],
-       [3, 3, 3]])
->>> x.flat[[1,4]] = 1; x
-array([[3, 1, 3],
-       [3, 1, 3]])
-
-
imag
-
The imaginary part of the array.

-Examples
---------
->>> x = np.sqrt([1+0j, 0+1j])
->>> x.imag
-array([ 0.        ,  0.70710678])
->>> x.imag.dtype
-dtype('float64')
-
-
itemsize
-
Length of one array element in bytes.

-Examples
---------
->>> x = np.array([1,2,3], dtype=np.float64)
->>> x.itemsize
-8
->>> x = np.array([1,2,3], dtype=np.complex128)
->>> x.itemsize
-16
-
-
nbytes
-
Total bytes consumed by the elements of the array.

-Notes
------
-Does not include memory consumed by non-element attributes of the
-array object.

-Examples
---------
->>> x = np.zeros((3,5,2), dtype=np.complex128)
->>> x.nbytes
-480
->>> np.prod(x.shape) * x.itemsize
-480
-
-
ndim
-
Number of array dimensions.

-Examples
---------
->>> x = np.array([1, 2, 3])
->>> x.ndim
-1
->>> y = np.zeros((2, 3, 4))
->>> y.ndim
-3
-
-
real
-
The real part of the array.

-Examples
---------
->>> x = np.sqrt([1+0j, 0+1j])
->>> x.real
-array([ 1.        ,  0.70710678])
->>> x.real.dtype
-dtype('float64')

-See Also
---------
-numpy.real : equivalent function
-
-
shape
-
Tuple of array dimensions.

-Notes
------
-May be used to "reshape" the array, as long as this would not
-require a change in the total number of elements

-Examples
---------
->>> x = np.array([1, 2, 3, 4])
->>> x.shape
-(4,)
->>> y = np.zeros((2, 3, 4))
->>> y.shape
-(2, 3, 4)
->>> y.shape = (3, 8)
->>> y
-array([[ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
-       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
-       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.]])
->>> y.shape = (3, 6)
-Traceback (most recent call last):
-  File "<stdin>", line 1, in <module>
-ValueError: total size of new array must be unchanged
-
-
size
-
Number of elements in the array.

-Equivalent to ``np.prod(a.shape)``, i.e., the product of the array's
-dimensions.

-Examples
---------
->>> x = np.zeros((3, 5, 2), dtype=np.complex128)
->>> x.size
-30
->>> np.prod(x.shape)
-30
-
-
strides
-
Tuple of bytes to step in each dimension when traversing an array.

-The byte offset of element ``(i[0], i[1], ..., i[n])`` in an array `a`
-is::

-    offset = sum(np.array(i) * a.strides)

-A more detailed explanation of strides can be found in the
-"ndarray.rst" file in the NumPy reference guide.

-Notes
------
-Imagine an array of 32-bit integers (each 4 bytes)::

-  x = np.array([[0, 1, 2, 3, 4],
-                [5, 6, 7, 8, 9]], dtype=np.int32)

-This array is stored in memory as 40 bytes, one after the other
-(known as a contiguous block of memory).  The strides of an array tell
-us how many bytes we have to skip in memory to move to the next position
-along a certain axis.  For example, we have to skip 4 bytes (1 value) to
-move to the next column, but 20 bytes (5 values) to get to the same
-position in the next row.  As such, the strides for the array `x` will be
-``(20, 4)``.

-See Also
---------
-numpy.lib.stride_tricks.as_strided

-Examples
---------
->>> y = np.reshape(np.arange(2*3*4), (2,3,4))
->>> y
-array([[[ 0,  1,  2,  3],
-        [ 4,  5,  6,  7],
-        [ 8,  9, 10, 11]],
-       [[12, 13, 14, 15],
-        [16, 17, 18, 19],
-        [20, 21, 22, 23]]])
->>> y.strides
-(48, 16, 4)
->>> y[1,1,1]
-17
->>> offset=sum(y.strides * np.array((1,1,1)))
->>> offset/y.itemsize
-17

->>> x = np.reshape(np.arange(5*6*7*8), (5,6,7,8)).transpose(2,3,1,0)
->>> x.strides
-(32, 4, 224, 1344)
->>> i = np.array([3,5,2,2])
->>> offset = sum(i * x.strides)
->>> x[3,5,2,2]
-813
->>> offset / x.itemsize
-813
-
-
-Data and other attributes inherited from numpy.ndarray:
-
__hash__ = None
- -

- - - - - - - -
 
-class Condition(System)
   One-dimensional ndarray with axis labels (including time series).

-Labels need not be unique but must be a hashable type. The object
-supports both integer- and label-based indexing and provides a host of
-methods for performing operations involving the index. Statistical
-methods from ndarray have been overridden to automatically exclude
-missing data (currently represented as NaN).

-Operations between Series (+, -, /, *, **) align values based on their
-associated index values-- they need not be the same length. The result
-index will be the sorted union of the two indexes.

-Parameters
-----------
-data : array-like, dict, or scalar value
-    Contains data stored in Series
-index : array-like or Index (1d)
-    Values must be hashable and have the same length as `data`.
-    Non-unique index values are allowed. Will default to
-    RangeIndex(len(data)) if not provided. If both a dict and index
-    sequence are used, the index will override the keys found in the
-    dict.
-dtype : numpy.dtype or None
-    If None, dtype will be inferred
-copy : boolean, default False
-    Copy input data
 
 
Method resolution order:
-
Condition
-
System
-
MySeries
-
pandas.core.series.Series
-
pandas.core.base.IndexOpsMixin
-
pandas.core.strings.StringAccessorMixin
-
pandas.core.generic.NDFrame
-
pandas.core.base.PandasObject
-
pandas.core.base.StringMixin
-
pandas.core.base.SelectionMixin
-
builtins.object
-
-
-Methods inherited from System:
-
__init__(self, *args, **kwargs)
Initialize the series.

-If there are no positional arguments, use kwargs.

-If there is one positional argument, copy it.

-More than one positional argument is an error.
- -
-Data descriptors inherited from System:
-
T
-
Intercept the Series accessor object so we can use `T`
-as a row label and access it using dot notation.

-https://pandas.pydata.org/pandas-docs/stable/generated/
-pandas.Series.T.html#pandas.Series.T
-
-
dt
-
Intercept the Series accessor object so we can use `dt`
-as a row label and access it using dot notation.

-https://pandas.pydata.org/pandas-docs/stable/generated/
-pandas.Series.dt.html
-
-
-Methods inherited from MySeries:
-
set(self, **kwargs)
Uses keyword arguments to update the Series in place.

-Example: series.update(a=1, b=2)
- -
-Methods inherited from pandas.core.series.Series:
-
__add__ = wrapper(left, right, name='__add__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f2f0>)
- -
__and__ = wrapper(self, other)
- -
__array__(self, result=None)
the array interface, return my values
- -
__array_prepare__(self, result, context=None)
Gets called prior to a ufunc
- -
__array_wrap__(self, result, context=None)
Gets called after a ufunc
- -
__div__ = wrapper(left, right, name='__truediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f9d8>)
- -
__divmod__ = wrapper(left, right, name='__divmod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca343bf8>)
- -
__eq__ = wrapper(self, other, axis=None)
- -
__float__ = wrapper(self)
- -
__floordiv__ = wrapper(left, right, name='__floordiv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fb70>)
- -
__ge__ = wrapper(self, other, axis=None)
- -
__getitem__(self, key)
- -
__gt__ = wrapper(self, other, axis=None)
- -
__iadd__ = f(self, other)
- -
__imul__ = f(self, other)
- -
__int__ = wrapper(self)
- -
__ipow__ = f(self, other)
- -
__isub__ = f(self, other)
- -
__iter__(self)
provide iteration over the values of the Series
-box values if necessary
- -
__itruediv__ = f(self, other)
- -
__le__ = wrapper(self, other, axis=None)
- -
__len__(self)
return the length of the Series
- -
__long__ = wrapper(self)
- -
__lt__ = wrapper(self, other, axis=None)
- -
__mod__ = wrapper(left, right, name='__mod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fd08>)
- -
__mul__ = wrapper(left, right, name='__mul__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f840>)
- -
__ne__ = wrapper(self, other, axis=None)
- -
__or__ = wrapper(self, other)
- -
__pow__ = wrapper(left, right, name='__pow__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fea0>)
- -
__radd__ = wrapper(left, right, name='__radd__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f510>)
- -
__rand__ = wrapper(self, other)
- -
__rdiv__ = wrapper(left, right, name='__rtruediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342510>)
- -
__rfloordiv__ = wrapper(left, right, name='__rfloordiv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342730>)
- -
__rmod__ = wrapper(left, right, name='__rmod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342b70>)
- -
__rmul__ = wrapper(left, right, name='__rmul__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca3420d0>)
- -
__ror__ = wrapper(self, other)
- -
__rpow__ = wrapper(left, right, name='__rpow__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342950>)
- -
__rsub__ = wrapper(left, right, name='__rsub__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca3422f0>)
- -
__rtruediv__ = wrapper(left, right, name='__rtruediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342510>)
- -
__rxor__ = wrapper(self, other)
- -
__setitem__(self, key, value)
- -
__sub__ = wrapper(left, right, name='__sub__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f6a8>)
- -
__truediv__ = wrapper(left, right, name='__truediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f9d8>)
- -
__unicode__(self)
Return a string representation for a particular DataFrame

-Invoked by unicode(df) in py2 only. Yields a Unicode String in both
-py2/py3.
- -
__xor__ = wrapper(self, other)
- -
add(self, other, level=None, fill_value=None, axis=0)
Addition of series and other, element-wise (binary operator `add`).

-Equivalent to ``series + other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.radd
- -
agg = aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a Series or when passed to Series.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : Series

-Examples
---------

->>> s = Series(np.random.randn(10))

->>> s.agg('min')
--1.3018049988556679

->>> s.agg(['min', 'max'])
-min   -1.301805
-max    1.127688
-dtype: float64

-See also
---------
-pandas.Series.apply
-pandas.Series.transform
- -
aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a Series or when passed to Series.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : Series

-Examples
---------

->>> s = Series(np.random.randn(10))

->>> s.agg('min')
--1.3018049988556679

->>> s.agg(['min', 'max'])
-min   -1.301805
-max    1.127688
-dtype: float64

-See also
---------
-pandas.Series.apply
-pandas.Series.transform
- -
align(self, other, join='outer', axis=None, level=None, copy=True, fill_value=None, method=None, limit=None, fill_axis=0, broadcast_axis=None)
Align two object on their axes with the
-specified join method for each axis Index

-Parameters
-----------
-other : DataFrame or Series
-join : {'outer', 'inner', 'left', 'right'}, default 'outer'
-axis : allowed axis of the other object, default None
-    Align on index (0), columns (1), or both (None)
-level : int or level name, default None
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-copy : boolean, default True
-    Always returns new objects. If copy=False and no reindexing is
-    required then original objects are returned.
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-method : str, default None
-limit : int, default None
-fill_axis : {0, 'index'}, default 0
-    Filling axis, method and limit
-broadcast_axis : {0, 'index'}, default None
-    Broadcast values along this axis, if aligning two objects of
-    different dimensions

-    .. versionadded:: 0.17.0

-Returns
--------
-(left, right) : (Series, type of other)
-    Aligned objects
- -
all(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether all elements are True over requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-all : scalar or Series (if level specified)
- -
any(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether any element is True over requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-any : scalar or Series (if level specified)
- -
append(self, to_append, ignore_index=False, verify_integrity=False)
Concatenate two or more Series.

-Parameters
-----------
-to_append : Series or list/tuple of Series
-ignore_index : boolean, default False
-    If True, do not use the index labels.

-    .. versionadded: 0.19.0

-verify_integrity : boolean, default False
-    If True, raise Exception on creating index with duplicates

-Returns
--------
-appended : Series

-Examples
---------
->>> s1 = pd.Series([1, 2, 3])
->>> s2 = pd.Series([4, 5, 6])
->>> s3 = pd.Series([4, 5, 6], index=[3,4,5])
->>> s1.append(s2)
-0    1
-1    2
-2    3
-0    4
-1    5
-2    6
-dtype: int64

->>> s1.append(s3)
-0    1
-1    2
-2    3
-3    4
-4    5
-5    6
-dtype: int64

-With `ignore_index` set to True:

->>> s1.append(s2, ignore_index=True)
-0    1
-1    2
-2    3
-3    4
-4    5
-5    6
-dtype: int64

-With `verify_integrity` set to True:

->>> s1.append(s2, verify_integrity=True)
-Traceback (most recent call last):
-...
-ValueError: Indexes have overlapping values: [0, 1, 2]
- -
apply(self, func, convert_dtype=True, args=(), **kwds)
Invoke function on values of Series. Can be ufunc (a NumPy function
-that applies to the entire Series) or a Python function that only works
-on single values

-Parameters
-----------
-func : function
-convert_dtype : boolean, default True
-    Try to find better dtype for elementwise function results. If
-    False, leave as dtype=object
-args : tuple
-    Positional arguments to pass to function in addition to the value
-Additional keyword arguments will be passed as keywords to the function

-Returns
--------
-y : Series or DataFrame if func returns a Series

-See also
---------
-Series.map: For element-wise operations
-Series.agg: only perform aggregating type operations
-Series.transform: only perform transformating type operations

-Examples
---------

-Create a series with typical summer temperatures for each city.

->>> import pandas as pd
->>> import numpy as np
->>> series = pd.Series([20, 21, 12], index=['London',
-... 'New York','Helsinki'])
->>> series
-London      20
-New York    21
-Helsinki    12
-dtype: int64

-Square the values by defining a function and passing it as an
-argument to ``apply()``.

->>> def square(x):
-...     return x**2
->>> series.apply(square)
-London      400
-New York    441
-Helsinki    144
-dtype: int64

-Square the values by passing an anonymous function as an
-argument to ``apply()``.

->>> series.apply(lambda x: x**2)
-London      400
-New York    441
-Helsinki    144
-dtype: int64

-Define a custom function that needs additional positional
-arguments and pass these additional arguments using the
-``args`` keyword.

->>> def subtract_custom_value(x, custom_value):
-...     return x-custom_value

->>> series.apply(subtract_custom_value, args=(5,))
-London      15
-New York    16
-Helsinki     7
-dtype: int64

-Define a custom function that takes keyword arguments
-and pass these arguments to ``apply``.

->>> def add_custom_values(x, **kwargs):
-...     for month in kwargs:
-...         x+=kwargs[month]
-...         return x

->>> series.apply(add_custom_values, june=30, july=20, august=25)
-London      95
-New York    96
-Helsinki    87
-dtype: int64

-Use a function from the Numpy library.

->>> series.apply(np.log)
-London      2.995732
-New York    3.044522
-Helsinki    2.484907
-dtype: float64
- -
argmax = idxmax(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of maximum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmax : Index of maximum of values

-Notes
------
-This method is the Series version of ``ndarray.argmax``.

-See Also
---------
-DataFrame.idxmax
-numpy.ndarray.argmax
- -
argmin = idxmin(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of minimum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmin : Index of minimum of values

-Notes
------
-This method is the Series version of ``ndarray.argmin``.

-See Also
---------
-DataFrame.idxmin
-numpy.ndarray.argmin
- -
argsort(self, axis=0, kind='quicksort', order=None)
Overrides ndarray.argsort. Argsorts the value, omitting NA/null values,
-and places the result in the same locations as the non-NA values

-Parameters
-----------
-axis : int (can only be zero)
-kind : {'mergesort', 'quicksort', 'heapsort'}, default 'quicksort'
-    Choice of sorting algorithm. See np.sort for more
-    information. 'mergesort' is the only stable algorithm
-order : ignored

-Returns
--------
-argsorted : Series, with -1 indicated where nan values are present

-See also
---------
-numpy.ndarray.argsort
- -
autocorr(self, lag=1)
Lag-N autocorrelation

-Parameters
-----------
-lag : int, default 1
-    Number of lags to apply before performing autocorrelation.

-Returns
--------
-autocorr : float
- -
between(self, left, right, inclusive=True)
Return boolean Series equivalent to left <= series <= right. NA values
-will be treated as False

-Parameters
-----------
-left : scalar
-    Left boundary
-right : scalar
-    Right boundary

-Returns
--------
-is_between : Series
- -
combine(self, other, func, fill_value=nan)
Perform elementwise binary operation on two Series using given function
-with optional fill value when an index is missing from one Series or
-the other

-Parameters
-----------
-other : Series or scalar value
-func : function
-fill_value : scalar value

-Returns
--------
-result : Series
- -
combine_first(self, other)
Combine Series values, choosing the calling Series's values
-first. Result index will be the union of the two indexes

-Parameters
-----------
-other : Series

-Returns
--------
-y : Series
- -
compound(self, axis=None, skipna=None, level=None)
Return the compound percentage of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-compounded : scalar or Series (if level specified)
- -
compress(self, condition, *args, **kwargs)
Return selected slices of an array along given axis as a Series

-See also
---------
-numpy.ndarray.compress
- -
corr(self, other, method='pearson', min_periods=None)
Compute correlation with `other` Series, excluding missing values

-Parameters
-----------
-other : Series
-method : {'pearson', 'kendall', 'spearman'}
-    * pearson : standard correlation coefficient
-    * kendall : Kendall Tau correlation coefficient
-    * spearman : Spearman rank correlation
-min_periods : int, optional
-    Minimum number of observations needed to have a valid result


-Returns
--------
-correlation : float
- -
count(self, level=None)
Return number of non-NA/null observations in the Series

-Parameters
-----------
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a smaller Series

-Returns
--------
-nobs : int or Series (if level specified)
- -
cov(self, other, min_periods=None)
Compute covariance with Series, excluding missing values

-Parameters
-----------
-other : Series
-min_periods : int, optional
-    Minimum number of observations needed to have a valid result

-Returns
--------
-covariance : float

-Normalized by N-1 (unbiased estimator).
- -
cummax(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative max over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummax : scalar



-See also
---------
-pandas.core.window.Expanding.max : Similar functionality
-    but ignores ``NaN`` values.
- -
cummin(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative minimum over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummin : scalar



-See also
---------
-pandas.core.window.Expanding.min : Similar functionality
-    but ignores ``NaN`` values.
- -
cumprod(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative product over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumprod : scalar



-See also
---------
-pandas.core.window.Expanding.prod : Similar functionality
-    but ignores ``NaN`` values.
- -
cumsum(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative sum over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumsum : scalar



-See also
---------
-pandas.core.window.Expanding.sum : Similar functionality
-    but ignores ``NaN`` values.
- -
diff(self, periods=1)
1st discrete difference of object

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming difference

-Returns
--------
-diffed : Series
- -
div = truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
divide = truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
dot(self, other)
Matrix multiplication with DataFrame or inner-product with Series
-objects

-Parameters
-----------
-other : Series or DataFrame

-Returns
--------
-dot_product : scalar or Series
- -
drop_duplicates(self, keep='first', inplace=False)
Return Series with duplicate values removed

-Parameters
-----------

-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Drop duplicates except for the first occurrence.
-    - ``last`` : Drop duplicates except for the last occurrence.
-    - False : Drop all duplicates.
-inplace : boolean, default False
-If True, performs operation inplace and returns None.

-Returns
--------
-deduplicated : Series
- -
dropna(self, axis=0, inplace=False, **kwargs)
Return Series without null values

-Returns
--------
-valid : Series
-inplace : boolean, default False
-    Do operation in place.
- -
duplicated(self, keep='first')
Return boolean Series denoting duplicate values

-Parameters
-----------
-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Mark duplicates as ``True`` except for the first
-      occurrence.
-    - ``last`` : Mark duplicates as ``True`` except for the last
-      occurrence.
-    - False : Mark all duplicates as ``True``.

-Returns
--------
-duplicated : Series
- -
eq(self, other, level=None, fill_value=None, axis=0)
Equal to of series and other, element-wise (binary operator `eq`).

-Equivalent to ``series == other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
ewm(self, com=None, span=None, halflife=None, alpha=None, min_periods=0, freq=None, adjust=True, ignore_na=False, axis=0)
Provides exponential weighted functions

-.. versionadded:: 0.18.0

-Parameters
-----------
-com : float, optional
-    Specify decay in terms of center of mass,
-    :math:`\alpha = 1 / (1 + com),\text{ for } com \geq 0`
-span : float, optional
-    Specify decay in terms of span,
-    :math:`\alpha = 2 / (span + 1),\text{ for } span \geq 1`
-halflife : float, optional
-    Specify decay in terms of half-life,
-    :math:`\alpha = 1 - exp(log(0.5) / halflife),\text{ for } halflife > 0`
-alpha : float, optional
-    Specify smoothing factor :math:`\alpha` directly,
-    :math:`0 < \alpha \leq 1`

-    .. versionadded:: 0.18.0

-min_periods : int, default 0
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : None or string alias / date offset object, default=None (DEPRECATED)
-    Frequency to conform to before computing statistic
-adjust : boolean, default True
-    Divide by decaying adjustment factor in beginning periods to account
-    for imbalance in relative weightings (viewing EWMA as a moving average)
-ignore_na : boolean, default False
-    Ignore missing values when calculating weights;
-    specify True to reproduce pre-0.15.0 behavior

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.ewm(com=0.5).mean()
-          B
-0  0.000000
-1  0.750000
-2  1.615385
-3  1.615385
-4  3.670213

-Notes
------
-Exactly one of center of mass, span, half-life, and alpha must be provided.
-Allowed values and relationship between the parameters are specified in the
-parameter descriptions above; see the link at the end of this section for
-a detailed explanation.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-When adjust is True (default), weighted averages are calculated using
-weights (1-alpha)**(n-1), (1-alpha)**(n-2), ..., 1-alpha, 1.

-When adjust is False, weighted averages are calculated recursively as:
-   weighted_average[0] = arg[0];
-   weighted_average[i] = (1-alpha)*weighted_average[i-1] + alpha*arg[i].

-When ignore_na is False (default), weights are based on absolute positions.
-For example, the weights of x and y used in calculating the final weighted
-average of [x, None, y] are (1-alpha)**2 and 1 (if adjust is True), and
-(1-alpha)**2 and alpha (if adjust is False).

-When ignore_na is True (reproducing pre-0.15.0 behavior), weights are based
-on relative positions. For example, the weights of x and y used in
-calculating the final weighted average of [x, None, y] are 1-alpha and 1
-(if adjust is True), and 1-alpha and alpha (if adjust is False).

-More details can be found at
-http://pandas.pydata.org/pandas-docs/stable/computation.html#exponentially-weighted-windows
- -
expanding(self, min_periods=1, freq=None, center=False, axis=0)
Provides expanding transformations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-axis : int or string, default 0

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.expanding(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  3.0
-4  7.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).
- -
fillna(self, value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)
Fill NA/NaN values using the specified method

-Parameters
-----------
-value : scalar, dict, Series, or DataFrame
-    Value to use to fill holes (e.g. 0), alternately a
-    dict/Series/DataFrame of values specifying which value to use for
-    each index (for a Series) or column (for a DataFrame). (values not
-    in the dict/Series/DataFrame will not be filled). This value cannot
-    be a list.
-method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
-    Method to use for filling holes in reindexed Series
-    pad / ffill: propagate last valid observation forward to next valid
-    backfill / bfill: use NEXT valid observation to fill gap
-axis : {0, 'index'}
-inplace : boolean, default False
-    If True, fill in place. Note: this will modify any
-    other views on this object, (e.g. a no-copy slice for a column in a
-    DataFrame).
-limit : int, default None
-    If method is specified, this is the maximum number of consecutive
-    NaN values to forward/backward fill. In other words, if there is
-    a gap with more than this number of consecutive NaNs, it will only
-    be partially filled. If method is not specified, this is the
-    maximum number of entries along the entire axis where NaNs will be
-    filled. Must be greater than 0 if not None.
-downcast : dict, default is None
-    a dict of item->dtype of what to downcast if possible,
-    or the string 'infer' which will try to downcast to an appropriate
-    equal type (e.g. float64 to int64 if possible)

-See Also
---------
-reindex, asfreq

-Returns
--------
-filled : Series
- -
first_valid_index(self)
Return label for first non-NA/null value
- -
floordiv(self, other, level=None, fill_value=None, axis=0)
Integer division of series and other, element-wise (binary operator `floordiv`).

-Equivalent to ``series // other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rfloordiv
- -
ge(self, other, level=None, fill_value=None, axis=0)
Greater than or equal to of series and other, element-wise (binary operator `ge`).

-Equivalent to ``series >= other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
get_value(self, label, takeable=False)
Quickly retrieve single value at passed index label

-Parameters
-----------
-index : label
-takeable : interpret the index as indexers, default False

-Returns
--------
-value : scalar value
- -
get_values(self)
same as values (but handles sparseness conversions); is a view
- -
gt(self, other, level=None, fill_value=None, axis=0)
Greater than of series and other, element-wise (binary operator `gt`).

-Equivalent to ``series > other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
hist = hist_series(self, by=None, ax=None, grid=True, xlabelsize=None, xrot=None, ylabelsize=None, yrot=None, figsize=None, bins=10, **kwds)
Draw histogram of the input series using matplotlib

-Parameters
-----------
-by : object, optional
-    If passed, then used to form histograms for separate groups
-ax : matplotlib axis object
-    If not passed, uses gca()
-grid : boolean, default True
-    Whether to show axis grid lines
-xlabelsize : int, default None
-    If specified changes the x-axis label size
-xrot : float, default None
-    rotation of x axis labels
-ylabelsize : int, default None
-    If specified changes the y-axis label size
-yrot : float, default None
-    rotation of y axis labels
-figsize : tuple, default None
-    figure size in inches by default
-bins: integer, default 10
-    Number of histogram bins to be used
-kwds : keywords
-    To be passed to the actual plotting function

-Notes
------
-See matplotlib documentation online for more on this
- -
idxmax(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of maximum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmax : Index of maximum of values

-Notes
------
-This method is the Series version of ``ndarray.argmax``.

-See Also
---------
-DataFrame.idxmax
-numpy.ndarray.argmax
- -
idxmin(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of minimum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmin : Index of minimum of values

-Notes
------
-This method is the Series version of ``ndarray.argmin``.

-See Also
---------
-DataFrame.idxmin
-numpy.ndarray.argmin
- -
isin(self, values)
Return a boolean :class:`~pandas.Series` showing whether each element
-in the :class:`~pandas.Series` is exactly contained in the passed
-sequence of ``values``.

-Parameters
-----------
-values : set or list-like
-    The sequence of values to test. Passing in a single string will
-    raise a ``TypeError``. Instead, turn a single string into a
-    ``list`` of one element.

-    .. versionadded:: 0.18.1

-    Support for values as a set

-Returns
--------
-isin : Series (bool dtype)

-Raises
-------
-TypeError
-  * If ``values`` is a string

-See Also
---------
-pandas.DataFrame.isin

-Examples
---------

->>> s = pd.Series(list('abc'))
->>> s.isin(['a', 'c', 'e'])
-0     True
-1    False
-2     True
-dtype: bool

-Passing a single string as ``s.isin('a')`` will raise an error. Use
-a list of one element instead:

->>> s.isin(['a'])
-0     True
-1    False
-2    False
-dtype: bool
- -
items = iteritems(self)
Lazily iterate over (index, value) tuples
- -
iteritems(self)
Lazily iterate over (index, value) tuples
- -
keys(self)
Alias for index
- -
kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : scalar or Series (if level specified)
- -
kurtosis = kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : scalar or Series (if level specified)
- -
last_valid_index(self)
Return label for last non-NA/null value
- -
le(self, other, level=None, fill_value=None, axis=0)
Less than or equal to of series and other, element-wise (binary operator `le`).

-Equivalent to ``series <= other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
lt(self, other, level=None, fill_value=None, axis=0)
Less than of series and other, element-wise (binary operator `lt`).

-Equivalent to ``series < other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
mad(self, axis=None, skipna=None, level=None)
Return the mean absolute deviation of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mad : scalar or Series (if level specified)
- -
map(self, arg, na_action=None)
Map values of Series using input correspondence (which can be
-a dict, Series, or function)

-Parameters
-----------
-arg : function, dict, or Series
-na_action : {None, 'ignore'}
-    If 'ignore', propagate NA values, without passing them to the
-    mapping function

-Returns
--------
-y : Series
-    same index as caller

-Examples
---------

-Map inputs to outputs (both of type `Series`)

->>> x = pd.Series([1,2,3], index=['one', 'two', 'three'])
->>> x
-one      1
-two      2
-three    3
-dtype: int64

->>> y = pd.Series(['foo', 'bar', 'baz'], index=[1,2,3])
->>> y
-1    foo
-2    bar
-3    baz

->>> x.map(y)
-one   foo
-two   bar
-three baz

-If `arg` is a dictionary, return a new Series with values converted
-according to the dictionary's mapping:

->>> z = {1: 'A', 2: 'B', 3: 'C'}

->>> x.map(z)
-one   A
-two   B
-three C

-Use na_action to control whether NA values are affected by the mapping
-function.

->>> s = pd.Series([1, 2, 3, np.nan])

->>> s2 = s.map('this is a string {}'.format, na_action=None)
-0    this is a string 1.0
-1    this is a string 2.0
-2    this is a string 3.0
-3    this is a string nan
-dtype: object

->>> s3 = s.map('this is a string {}'.format, na_action='ignore')
-0    this is a string 1.0
-1    this is a string 2.0
-2    this is a string 3.0
-3                     NaN
-dtype: object

-See Also
---------
-Series.apply: For applying more complex functions on a Series
-DataFrame.apply: Apply a function row-/column-wise
-DataFrame.applymap: Apply a function elementwise on a whole DataFrame

-Notes
------
-When `arg` is a dictionary, values in Series that are not in the
-dictionary (as keys) are converted to ``NaN``. However, if the
-dictionary is a ``dict`` subclass that defines ``__missing__`` (i.e.
-provides a method for default values), then this default is used
-rather than ``NaN``:

->>> from collections import Counter
->>> counter = Counter()
->>> counter['bar'] += 1
->>> y.map(counter)
-1    0
-2    1
-3    0
-dtype: int64
- -
max(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the maximum of the values in the object.
-            If you want the *index* of the maximum, use ``idxmax``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmax``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-max : scalar or Series (if level specified)
- -
mean(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the mean of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mean : scalar or Series (if level specified)
- -
median(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the median of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-median : scalar or Series (if level specified)
- -
memory_usage(self, index=True, deep=False)
Memory usage of the Series

-Parameters
-----------
-index : bool
-    Specifies whether to include memory usage of Series index
-deep : bool
-    Introspect the data deeply, interrogate
-    `object` dtypes for system-level memory consumption

-Returns
--------
-scalar bytes of memory consumed

-Notes
------
-Memory usage does not include memory consumed by elements that
-are not components of the array if deep=False

-See Also
---------
-numpy.ndarray.nbytes
- -
min(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the minimum of the values in the object.
-            If you want the *index* of the minimum, use ``idxmin``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmin``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-min : scalar or Series (if level specified)
- -
mod(self, other, level=None, fill_value=None, axis=0)
Modulo of series and other, element-wise (binary operator `mod`).

-Equivalent to ``series % other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmod
- -
mode(self)
Return the mode(s) of the dataset.

-Always returns Series even if only one value is returned.

-Returns
--------
-modes : Series (sorted)
- -
mul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `mul`).

-Equivalent to ``series * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmul
- -
multiply = mul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `mul`).

-Equivalent to ``series * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmul
- -
ne(self, other, level=None, fill_value=None, axis=0)
Not equal to of series and other, element-wise (binary operator `ne`).

-Equivalent to ``series != other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
nlargest(self, n=5, keep='first')
Return the largest `n` elements.

-Parameters
-----------
-n : int
-    Return this many descending sorted values
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-top_n : Series
-    The n largest values in the Series, in sorted order

-Notes
------
-Faster than ``.sort_values(ascending=False).head(n)`` for small `n`
-relative to the size of the ``Series`` object.

-See Also
---------
-Series.nsmallest

-Examples
---------
->>> import pandas as pd
->>> import numpy as np
->>> s = pd.Series(np.random.randn(10**6))
->>> s.nlargest(10)  # only sorts up to the N requested
-219921    4.644710
-82124     4.608745
-421689    4.564644
-425277    4.447014
-718691    4.414137
-43154     4.403520
-283187    4.313922
-595519    4.273635
-503969    4.250236
-121637    4.240952
-dtype: float64
- -
nonzero(self)
Return the indices of the elements that are non-zero

-This method is equivalent to calling `numpy.nonzero` on the
-series data. For compatability with NumPy, the return value is
-the same (a tuple with an array of indices for each dimension),
-but it will always be a one-item tuple because series only have
-one dimension.

-Examples
---------
->>> s = pd.Series([0, 3, 0, 4])
->>> s.nonzero()
-(array([1, 3]),)
->>> s.iloc[s.nonzero()[0]]
-1    3
-3    4
-dtype: int64

-See Also
---------
-numpy.nonzero
- -
nsmallest(self, n=5, keep='first')
Return the smallest `n` elements.

-Parameters
-----------
-n : int
-    Return this many ascending sorted values
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-bottom_n : Series
-    The n smallest values in the Series, in sorted order

-Notes
------
-Faster than ``.sort_values().head(n)`` for small `n` relative to
-the size of the ``Series`` object.

-See Also
---------
-Series.nlargest

-Examples
---------
->>> import pandas as pd
->>> import numpy as np
->>> s = pd.Series(np.random.randn(10**6))
->>> s.nsmallest(10)  # only sorts up to the N requested
-288532   -4.954580
-732345   -4.835960
-64803    -4.812550
-446457   -4.609998
-501225   -4.483945
-669476   -4.472935
-973615   -4.401699
-621279   -4.355126
-773916   -4.347355
-359919   -4.331927
-dtype: float64
- -
pow(self, other, level=None, fill_value=None, axis=0)
Exponential power of series and other, element-wise (binary operator `pow`).

-Equivalent to ``series ** other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rpow
- -
prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : scalar or Series (if level specified)
- -
product = prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : scalar or Series (if level specified)
- -
ptp(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Returns the difference between the maximum value and the
-            minimum value in the object. This is the equivalent of the
-            ``numpy.ndarray`` method ``ptp``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-ptp : scalar or Series (if level specified)
- -
put(self, *args, **kwargs)
Applies the `put` method to its `values` attribute
-if it has one.

-See also
---------
-numpy.ndarray.put
- -
quantile(self, q=0.5, interpolation='linear')
Return value at the given quantile, a la numpy.percentile.

-Parameters
-----------
-q : float or array-like, default 0.5 (50% quantile)
-    0 <= q <= 1, the quantile(s) to compute
-interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'}
-    .. versionadded:: 0.18.0

-    This optional parameter specifies the interpolation method to use,
-    when the desired quantile lies between two data points `i` and `j`:

-        * linear: `i + (j - i) * fraction`, where `fraction` is the
-          fractional part of the index surrounded by `i` and `j`.
-        * lower: `i`.
-        * higher: `j`.
-        * nearest: `i` or `j` whichever is nearest.
-        * midpoint: (`i` + `j`) / 2.

-Returns
--------
-quantile : float or Series
-    if ``q`` is an array, a Series will be returned where the
-    index is ``q`` and the values are the quantiles.

-Examples
---------
->>> s = Series([1, 2, 3, 4])
->>> s.quantile(.5)
-2.5
->>> s.quantile([.25, .5, .75])
-0.25    1.75
-0.50    2.50
-0.75    3.25
-dtype: float64
- -
radd(self, other, level=None, fill_value=None, axis=0)
Addition of series and other, element-wise (binary operator `radd`).

-Equivalent to ``other + series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.add
- -
ravel(self, order='C')
Return the flattened underlying data as an ndarray

-See also
---------
-numpy.ndarray.ravel
- -
rdiv = rtruediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.truediv
- -
reindex(self, index=None, **kwargs)
Conform Series to new index with optional filling logic, placing
-NA/NaN in locations having no value in the previous index. A new object
-is produced unless the new index is equivalent to the current one and
-copy=False

-Parameters
-----------
-index : array-like, optional (can be specified in order, or as
-    keywords)
-    New labels / index to conform to. Preferably an Index object to
-    avoid duplicating data
-method : {None, 'backfill'/'bfill', 'pad'/'ffill', 'nearest'}, optional
-    method to use for filling holes in reindexed DataFrame.
-    Please note: this is only  applicable to DataFrames/Series with a
-    monotonically increasing/decreasing index.

-    * default: don't fill gaps
-    * pad / ffill: propagate last valid observation forward to next
-      valid
-    * backfill / bfill: use next valid observation to fill gap
-    * nearest: use nearest valid observations to fill gap

-copy : boolean, default True
-    Return a new object, even if the passed indexes are the same
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-limit : int, default None
-    Maximum number of consecutive elements to forward or backward fill
-tolerance : optional
-    Maximum distance between original and new labels for inexact
-    matches. The values of the index at the matching locations most
-    satisfy the equation ``abs(index[indexer] - target) <= tolerance``.

-    .. versionadded:: 0.17.0

-Examples
---------

-Create a dataframe with some fictional data.

->>> index = ['Firefox', 'Chrome', 'Safari', 'IE10', 'Konqueror']
->>> df = pd.DataFrame({
-...      'http_status': [200,200,404,404,301],
-...      'response_time': [0.04, 0.02, 0.07, 0.08, 1.0]},
-...       index=index)
->>> df
-           http_status  response_time
-Firefox            200           0.04
-Chrome             200           0.02
-Safari             404           0.07
-IE10               404           0.08
-Konqueror          301           1.00

-Create a new index and reindex the dataframe. By default
-values in the new index that do not have corresponding
-records in the dataframe are assigned ``NaN``.

->>> new_index= ['Safari', 'Iceweasel', 'Comodo Dragon', 'IE10',
-...             'Chrome']
->>> df.reindex(new_index)
-               http_status  response_time
-Safari               404.0           0.07
-Iceweasel              NaN            NaN
-Comodo Dragon          NaN            NaN
-IE10                 404.0           0.08
-Chrome               200.0           0.02

-We can fill in the missing values by passing a value to
-the keyword ``fill_value``. Because the index is not monotonically
-increasing or decreasing, we cannot use arguments to the keyword
-``method`` to fill the ``NaN`` values.

->>> df.reindex(new_index, fill_value=0)
-               http_status  response_time
-Safari                 404           0.07
-Iceweasel                0           0.00
-Comodo Dragon            0           0.00
-IE10                   404           0.08
-Chrome                 200           0.02

->>> df.reindex(new_index, fill_value='missing')
-              http_status response_time
-Safari                404          0.07
-Iceweasel         missing       missing
-Comodo Dragon     missing       missing
-IE10                  404          0.08
-Chrome                200          0.02

-To further illustrate the filling functionality in
-``reindex``, we will create a dataframe with a
-monotonically increasing index (for example, a sequence
-of dates).

->>> date_index = pd.date_range('1/1/2010', periods=6, freq='D')
->>> df2 = pd.DataFrame({"prices": [100, 101, np.nan, 100, 89, 88]},
-...                    index=date_index)
->>> df2
-            prices
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88

-Suppose we decide to expand the dataframe to cover a wider
-date range.

->>> date_index2 = pd.date_range('12/29/2009', periods=10, freq='D')
->>> df2.reindex(date_index2)
-            prices
-2009-12-29     NaN
-2009-12-30     NaN
-2009-12-31     NaN
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-The index entries that did not have a value in the original data frame
-(for example, '2009-12-29') are by default filled with ``NaN``.
-If desired, we can fill in the missing values using one of several
-options.

-For example, to backpropagate the last valid value to fill the ``NaN``
-values, pass ``bfill`` as an argument to the ``method`` keyword.

->>> df2.reindex(date_index2, method='bfill')
-            prices
-2009-12-29     100
-2009-12-30     100
-2009-12-31     100
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-Please note that the ``NaN`` value present in the original dataframe
-(at index value 2010-01-03) will not be filled by any of the
-value propagation schemes. This is because filling while reindexing
-does not look at dataframe values, but only compares the original and
-desired indexes. If you do want to fill in the ``NaN`` values present
-in the original dataframe, use the ``fillna()`` method.

-Returns
--------
-reindexed : Series
- -
reindex_axis(self, labels, axis=0, **kwargs)
for compatibility with higher dims
- -
rename(self, index=None, **kwargs)
Alter axes input function or functions. Function / dict values must be
-unique (1-to-1). Labels not contained in a dict / Series will be left
-as-is. Extra labels listed don't throw an error. Alternatively, change
-``Series.name`` with a scalar value (Series only).

-Parameters
-----------
-index : scalar, list-like, dict-like or function, optional
-    Scalar or list-like will alter the ``Series.name`` attribute,
-    and raise on DataFrame or Panel.
-    dict-like or functions are transformations to apply to
-    that axis' values
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False
-    Whether to return a new Series. If True then value of copy is
-    ignored.
-level : int or level name, default None
-    In case of a MultiIndex, only rename labels in the specified
-    level.

-Returns
--------
-renamed : Series (new object)

-See Also
---------
-pandas.NDFrame.rename_axis

-Examples
---------
->>> s = pd.Series([1, 2, 3])
->>> s
-0    1
-1    2
-2    3
-dtype: int64
->>> s.rename("my_name") # scalar, changes Series.name
-0    1
-1    2
-2    3
-Name: my_name, dtype: int64
->>> s.rename(lambda x: x ** 2)  # function, changes labels
-0    1
-1    2
-4    3
-dtype: int64
->>> s.rename({1: 3, 2: 5})  # mapping, changes labels
-0    1
-3    2
-5    3
-dtype: int64
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename(2)
-Traceback (most recent call last):
-...
-TypeError: 'int' object is not callable
->>> df.rename(index=str, columns={"A": "a", "B": "c"})
-   a  c
-0  1  4
-1  2  5
-2  3  6
->>> df.rename(index=str, columns={"A": "a", "C": "c"})
-   a  B
-0  1  4
-1  2  5
-2  3  6
- -
reorder_levels(self, order)
Rearrange index levels using input order. May not drop or duplicate
-levels

-Parameters
-----------
-order : list of int representing new level order.
-       (reference level by number or key)
-axis : where to reorder levels

-Returns
--------
-type of caller (new object)
- -
repeat(self, repeats, *args, **kwargs)
Repeat elements of an Series. Refer to `numpy.ndarray.repeat`
-for more information about the `repeats` argument.

-See also
---------
-numpy.ndarray.repeat
- -
reset_index(self, level=None, drop=False, name=None, inplace=False)
Analogous to the :meth:`pandas.DataFrame.reset_index` function, see
-docstring there.

-Parameters
-----------
-level : int, str, tuple, or list, default None
-    Only remove the given levels from the index. Removes all levels by
-    default
-drop : boolean, default False
-    Do not try to insert index into dataframe columns
-name : object, default None
-    The name of the column corresponding to the Series values
-inplace : boolean, default False
-    Modify the Series in place (do not create a new object)

-Returns
-----------
-resetted : DataFrame, or Series if drop == True
- -
reshape(self, *args, **kwargs)
DEPRECATED: calling this method will raise an error in a
-future release. Please call ``.values.reshape(...)`` instead.

-return an ndarray with the values shape
-if the specified shape matches exactly the current shape, then
-return self (for compat)

-See also
---------
-numpy.ndarray.reshape
- -
rfloordiv(self, other, level=None, fill_value=None, axis=0)
Integer division of series and other, element-wise (binary operator `rfloordiv`).

-Equivalent to ``other // series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.floordiv
- -
rmod(self, other, level=None, fill_value=None, axis=0)
Modulo of series and other, element-wise (binary operator `rmod`).

-Equivalent to ``other % series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.mod
- -
rmul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `rmul`).

-Equivalent to ``other * series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.mul
- -
rolling(self, window, min_periods=None, freq=None, center=False, win_type=None, on=None, axis=0, closed=None)
Provides rolling window calculcations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-window : int, or offset
-    Size of the moving window. This is the number of observations used for
-    calculating the statistic. Each window will be a fixed size.

-    If its an offset then this will be the time period of each window. Each
-    window will be a variable sized based on the observations included in
-    the time-period. This is only valid for datetimelike indexes. This is
-    new in 0.19.0
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA). For a window that is specified by an offset,
-    this will default to 1.
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-win_type : string, default None
-    Provide a window type. See the notes below.
-on : string, optional
-    For a DataFrame, column on which to calculate
-    the rolling window, rather than the index
-closed : string, default None
-    Make the interval closed on the 'right', 'left', 'both' or
-    'neither' endpoints.
-    For offset-based windows, it defaults to 'right'.
-    For fixed windows, defaults to 'both'. Remaining cases not implemented
-    for fixed windows.

-    .. versionadded:: 0.20.0

-axis : int or string, default 0

-Returns
--------
-a Window or Rolling sub-classed for the particular operation

-Examples
---------

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]})
->>> df
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

-Rolling sum with a window length of 2, using the 'triang'
-window type.

->>> df.rolling(2, win_type='triang').sum()
-     B
-0  NaN
-1  1.0
-2  2.5
-3  NaN
-4  NaN

-Rolling sum with a window length of 2, min_periods defaults
-to the window length.

->>> df.rolling(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  NaN
-4  NaN

-Same as above, but explicity set the min_periods

->>> df.rolling(2, min_periods=1).sum()
-     B
-0  0.0
-1  1.0
-2  3.0
-3  2.0
-4  4.0

-A ragged (meaning not-a-regular frequency), time-indexed DataFrame

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]},
-....:                 index = [pd.Timestamp('20130101 09:00:00'),
-....:                          pd.Timestamp('20130101 09:00:02'),
-....:                          pd.Timestamp('20130101 09:00:03'),
-....:                          pd.Timestamp('20130101 09:00:05'),
-....:                          pd.Timestamp('20130101 09:00:06')])

->>> df
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  2.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0


-Contrasting to an integer rolling window, this will roll a variable
-length window corresponding to the time period.
-The default for min_periods is 1.

->>> df.rolling('2s').sum()
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  3.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-To learn more about the offsets & frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-The recognized win_types are:

-* ``boxcar``
-* ``triang``
-* ``blackman``
-* ``hamming``
-* ``bartlett``
-* ``parzen``
-* ``bohman``
-* ``blackmanharris``
-* ``nuttall``
-* ``barthann``
-* ``kaiser`` (needs beta)
-* ``gaussian`` (needs std)
-* ``general_gaussian`` (needs power, width)
-* ``slepian`` (needs width).
- -
round(self, decimals=0, *args, **kwargs)
Round each value in a Series to the given number of decimals.

-Parameters
-----------
-decimals : int
-    Number of decimal places to round to (default: 0).
-    If decimals is negative, it specifies the number of
-    positions to the left of the decimal point.

-Returns
--------
-Series object

-See Also
---------
-numpy.around
-DataFrame.round
- -
rpow(self, other, level=None, fill_value=None, axis=0)
Exponential power of series and other, element-wise (binary operator `rpow`).

-Equivalent to ``other ** series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.pow
- -
rsub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `rsub`).

-Equivalent to ``other - series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.sub
- -
rtruediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.truediv
- -
searchsorted(self, value, side='left', sorter=None)
Find indices where elements should be inserted to maintain order.

-Find the indices into a sorted Series `self` such that, if the
-corresponding elements in `value` were inserted before the indices,
-the order of `self` would be preserved.

-Parameters
-----------
-value : array_like
-    Values to insert into `self`.
-side : {'left', 'right'}, optional
-    If 'left', the index of the first suitable location found is given.
-    If 'right', return the last such index.  If there is no suitable
-    index, return either 0 or N (where N is the length of `self`).
-sorter : 1-D array_like, optional
-    Optional array of integer indices that sort `self` into ascending
-    order. They are typically the result of ``np.argsort``.

-Returns
--------
-indices : array of ints
-    Array of insertion points with the same shape as `value`.

-See Also
---------
-numpy.searchsorted

-Notes
------
-Binary search is used to find the required insertion points.

-Examples
---------

->>> x = pd.Series([1, 2, 3])
->>> x
-0    1
-1    2
-2    3
-dtype: int64

->>> x.searchsorted(4)
-array([3])

->>> x.searchsorted([0, 4])
-array([0, 3])

->>> x.searchsorted([1, 3], side='left')
-array([0, 2])

->>> x.searchsorted([1, 3], side='right')
-array([1, 3])

->>> x = pd.Categorical(['apple', 'bread', 'bread', 'cheese', 'milk' ])
-[apple, bread, bread, cheese, milk]
-Categories (4, object): [apple < bread < cheese < milk]

->>> x.searchsorted('bread')
-array([1])     # Note: an array, not a scalar

->>> x.searchsorted(['bread'])
-array([1])

->>> x.searchsorted(['bread', 'eggs'])
-array([1, 4])

->>> x.searchsorted(['bread', 'eggs'], side='right')
-array([3, 4])    # eggs before milk
- -
sem(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased standard error of the mean over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sem : scalar or Series (if level specified)
- -
set_value(self, label, value, takeable=False)
Quickly set single value at passed label. If label is not contained, a
-new object is created with the label placed at the end of the result
-index

-Parameters
-----------
-label : object
-    Partial indexing with MultiIndex not allowed
-value : object
-    Scalar value
-takeable : interpret the index as indexers, default False

-Returns
--------
-series : Series
-    If label is contained, will be reference to calling Series,
-    otherwise a new object
- -
shift(self, periods=1, freq=None, axis=0)
Shift index by desired number of periods with an optional time freq

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, optional
-    Increment to use from the tseries module or time rule (e.g. 'EOM').
-    See Notes.
-axis : {0, 'index'}

-Notes
------
-If freq is specified then the index values are shifted but the data
-is not realigned. That is, use freq if you would like to extend the
-index when shifting and preserve the original data.

-Returns
--------
-shifted : Series
- -
skew(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased skew over requested axis
-Normalized by N-1

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-skew : scalar or Series (if level specified)
- -
sort_index(self, axis=0, level=None, ascending=True, inplace=False, kind='quicksort', na_position='last', sort_remaining=True)
Sort object by labels (along an axis)

-Parameters
-----------
-axis : index to direct sorting
-level : int or level name or list of ints or list of level names
-    if not None, sort on values in specified index level(s)
-ascending : boolean, default True
-    Sort ascending vs. descending
-inplace : bool, default False
-    if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end.
-     Not implemented for MultiIndex.
-sort_remaining : bool, default True
-    if true and sorting by level and index is multilevel, sort by other
-    levels too (in order) after sorting by specified level

-Returns
--------
-sorted_obj : Series
- -
sort_values(self, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last')
Sort by the values along either axis

-.. versionadded:: 0.17.0

-Parameters
-----------
-axis : {0, 'index'}, default 0
-    Axis to direct sorting
-ascending : bool or list of bool, default True
-     Sort ascending vs. descending. Specify list for multiple sort
-     orders.  If this is a list of bools, must match the length of
-     the by.
-inplace : bool, default False
-     if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end

-Returns
--------
-sorted_obj : Series
- -
sortlevel(self, level=0, ascending=True, sort_remaining=True)
DEPRECATED: use :meth:`Series.sort_index`

-Sort Series with MultiIndex by chosen level. Data will be
-lexicographically sorted by the chosen level followed by the other
-levels (in order)

-Parameters
-----------
-level : int or level name, default None
-ascending : bool, default True

-Returns
--------
-sorted : Series

-See Also
---------
-Series.sort_index(level=...)
- -
std(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return sample standard deviation over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-std : scalar or Series (if level specified)
- -
sub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `sub`).

-Equivalent to ``series - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rsub
- -
subtract = sub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `sub`).

-Equivalent to ``series - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rsub
- -
sum(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the sum of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sum : scalar or Series (if level specified)
- -
swaplevel(self, i=-2, j=-1, copy=True)
Swap levels i and j in a MultiIndex

-Parameters
-----------
-i, j : int, string (can be mixed)
-    Level of index to be swapped. Can pass level name as string.

-Returns
--------
-swapped : Series

-.. versionchanged:: 0.18.1

-   The indexes ``i`` and ``j`` are now optional, and default to
-   the two innermost levels of the index.
- -
take(self, indices, axis=0, convert=True, is_copy=False, **kwargs)
return Series corresponding to requested indices

-Parameters
-----------
-indices : list / array of ints
-convert : translate negative to positive indices (default)

-Returns
--------
-taken : Series

-See also
---------
-numpy.ndarray.take
- -
to_csv(self, path=None, index=True, sep=',', na_rep='', float_format=None, header=False, index_label=None, mode='w', encoding=None, date_format=None, decimal='.')
Write Series to a comma-separated values (csv) file

-Parameters
-----------
-path : string or file handle, default None
-    File path or object, if None is provided the result is returned as
-    a string.
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-header : boolean, default False
-    Write out series name
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.
-mode : Python write mode, default 'w'
-sep : character, default ","
-    Field delimiter for the output file.
-encoding : string, optional
-    a string representing the encoding to use if the contents are
-    non-ascii, for python versions prior to 3
-date_format: string, default None
-    Format string for datetime objects.
-decimal: string, default '.'
-    Character recognized as decimal separator. E.g. use ',' for
-    European data
- -
to_dict(self)
Convert Series to {label -> value} dict

-Returns
--------
-value_dict : dict
- -
to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='', float_format=None, columns=None, header=True, index=True, index_label=None, startrow=0, startcol=0, engine=None, merge_cells=True, encoding=None, inf_rep='inf', verbose=True)
Write Series to an excel sheet

-.. versionadded:: 0.20.0


-Parameters
-----------
-excel_writer : string or ExcelWriter object
-    File path or existing ExcelWriter
-sheet_name : string, default 'Sheet1'
-    Name of sheet which will contain DataFrame
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-columns : sequence, optional
-    Columns to write
-header : boolean or list of string, default True
-    Write out column names. If a list of string is given it is
-    assumed to be aliases for the column names
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.
-startrow :
-    upper left cell row to dump data frame
-startcol :
-    upper left cell column to dump data frame
-engine : string, default None
-    write engine to use - you can also set this via the options
-    ``io.excel.xlsx.writer``, ``io.excel.xls.writer``, and
-    ``io.excel.xlsm.writer``.
-merge_cells : boolean, default True
-    Write MultiIndex and Hierarchical Rows as merged cells.
-encoding: string, default None
-    encoding of the resulting excel file. Only necessary for xlwt,
-    other writers support unicode natively.
-inf_rep : string, default 'inf'
-    Representation for infinity (there is no native representation for
-    infinity in Excel)
-freeze_panes : tuple of integer (length 2), default None
-    Specifies the one-based bottommost row and rightmost column that
-    is to be frozen

-    .. versionadded:: 0.20.0

-Notes
------
-If passing an existing ExcelWriter object, then the sheet will be added
-to the existing workbook.  This can be used to save different
-DataFrames to one workbook:

->>> writer = pd.ExcelWriter('output.xlsx')
->>> df1.to_excel(writer,'Sheet1')
->>> df2.to_excel(writer,'Sheet2')
->>> writer.save()

-For compatibility with to_csv, to_excel serializes lists and dicts to
-strings before writing.
- -
to_frame(self, name=None)
Convert Series to DataFrame

-Parameters
-----------
-name : object, default None
-    The passed name should substitute for the series name (if it has
-    one).

-Returns
--------
-data_frame : DataFrame
- -
to_period(self, freq=None, copy=True)
Convert Series from DatetimeIndex to PeriodIndex with desired
-frequency (inferred from index if not passed)

-Parameters
-----------
-freq : string, default

-Returns
--------
-ts : Series with PeriodIndex
- -
to_sparse(self, kind='block', fill_value=None)
Convert Series to SparseSeries

-Parameters
-----------
-kind : {'block', 'integer'}
-fill_value : float, defaults to NaN (missing)

-Returns
--------
-sp : SparseSeries
- -
to_string(self, buf=None, na_rep='NaN', float_format=None, header=True, index=True, length=False, dtype=False, name=False, max_rows=None)
Render a string representation of the Series

-Parameters
-----------
-buf : StringIO-like, optional
-    buffer to write to
-na_rep : string, optional
-    string representation of NAN to use, default 'NaN'
-float_format : one-parameter function, optional
-    formatter function to apply to columns' elements if they are floats
-    default None
-header: boolean, default True
-    Add the Series header (index name)
-index : bool, optional
-    Add index (row) labels, default True
-length : boolean, default False
-    Add the Series length
-dtype : boolean, default False
-    Add the Series dtype
-name : boolean, default False
-    Add the Series name if not None
-max_rows : int, optional
-    Maximum number of rows to show before truncating. If None, show
-    all.

-Returns
--------
-formatted : string (if not buffer passed)
- -
to_timestamp(self, freq=None, how='start', copy=True)
Cast to datetimeindex of timestamps, at *beginning* of period

-Parameters
-----------
-freq : string, default frequency of PeriodIndex
-    Desired frequency
-how : {'s', 'e', 'start', 'end'}
-    Convention for converting period to timestamp; start of period
-    vs. end

-Returns
--------
-ts : Series with DatetimeIndex
- -
tolist(self)
Convert Series to a nested list
- -
transform(self, func, *args, **kwargs)
Call function producing a like-indexed NDFrame
-and return a NDFrame with the transformed values`

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    To apply to column

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Returns
--------
-transformed : NDFrame

-Examples
---------
->>> df = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
-...                   index=pd.date_range('1/1/2000', periods=10))
-df.iloc[3:7] = np.nan

->>> df.transform(lambda x: (x - x.mean()) / x.std())
-                   A         B         C
-2000-01-01  0.579457  1.236184  0.123424
-2000-01-02  0.370357 -0.605875 -1.231325
-2000-01-03  1.455756 -0.277446  0.288967
-2000-01-04       NaN       NaN       NaN
-2000-01-05       NaN       NaN       NaN
-2000-01-06       NaN       NaN       NaN
-2000-01-07       NaN       NaN       NaN
-2000-01-08 -0.498658  1.274522  1.642524
-2000-01-09 -0.540524 -1.012676 -0.828968
-2000-01-10 -1.366388 -0.614710  0.005378

-See also
---------
-pandas.NDFrame.aggregate
-pandas.NDFrame.apply
- -
truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
unique(self)
Return unique values in the object. Uniques are returned in order
-of appearance, this does NOT sort. Hash table-based unique.

-Parameters
-----------
-values : 1d array-like

-Returns
--------
-unique values.
-  - If the input is an Index, the return is an Index
-  - If the input is a Categorical dtype, the return is a Categorical
-  - If the input is a Series/ndarray, the return will be an ndarray

-See Also
---------
-unique
-Index.unique
-Series.unique
- -
unstack(self, level=-1, fill_value=None)
Unstack, a.k.a. pivot, Series with MultiIndex to produce DataFrame.
-The level involved will automatically get sorted.

-Parameters
-----------
-level : int, string, or list of these, default last level
-    Level(s) to unstack, can pass level name
-fill_value : replace NaN with this value if the unstack produces
-    missing values

-    .. versionadded: 0.18.0

-Examples
---------
->>> s = pd.Series([1, 2, 3, 4],
-...     index=pd.MultiIndex.from_product([['one', 'two'], ['a', 'b']]))
->>> s
-one  a    1
-     b    2
-two  a    3
-     b    4
-dtype: int64

->>> s.unstack(level=-1)
-     a  b
-one  1  2
-two  3  4

->>> s.unstack(level=0)
-   one  two
-a    1    3
-b    2    4

-Returns
--------
-unstacked : DataFrame
- -
update(self, other)
Modify Series in place using non-NA values from passed
-Series. Aligns on index

-Parameters
-----------
-other : Series
- -
valid lambda self, inplace=False, **kwargs
- -
var(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased variance over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-var : scalar or Series (if level specified)
- -
view(self, dtype=None)
- -
-Class methods inherited from pandas.core.series.Series:
-
from_array(arr, index=None, name=None, dtype=None, copy=False, fastpath=False) from builtins.type
- -
from_csv(path, sep=',', parse_dates=True, header=None, index_col=0, encoding=None, infer_datetime_format=False) from builtins.type
Read CSV file (DISCOURAGED, please use :func:`pandas.read_csv`
-instead).

-It is preferable to use the more powerful :func:`pandas.read_csv`
-for most general purposes, but ``from_csv`` makes for an easy
-roundtrip to and from a file (the exact counterpart of
-``to_csv``), especially with a time Series.

-This method only differs from :func:`pandas.read_csv` in some defaults:

-- `index_col` is ``0`` instead of ``None`` (take first column as index
-  by default)
-- `header` is ``None`` instead of ``0`` (the first row is not used as
-  the column names)
-- `parse_dates` is ``True`` instead of ``False`` (try parsing the index
-  as datetime by default)

-With :func:`pandas.read_csv`, the option ``squeeze=True`` can be used
-to return a Series like ``from_csv``.

-Parameters
-----------
-path : string file path or file handle / StringIO
-sep : string, default ','
-    Field delimiter
-parse_dates : boolean, default True
-    Parse dates. Different default from read_table
-header : int, default None
-    Row to use as header (skip prior rows)
-index_col : int or sequence, default 0
-    Column to use for index. If a sequence is given, a MultiIndex
-    is used. Different default from read_table
-encoding : string, optional
-    a string representing the encoding to use if the contents are
-    non-ascii, for python versions prior to 3
-infer_datetime_format: boolean, default False
-    If True and `parse_dates` is True for a column, try to infer the
-    datetime format based on the first datetime string. If the format
-    can be inferred, there often will be a large parsing speed-up.

-See also
---------
-pandas.read_csv

-Returns
--------
-y : Series
- -
-Data descriptors inherited from pandas.core.series.Series:
-
asobject
-
return object Series which contains boxed values

-*this is an internal non-public method*
-
-
axes
-
Return a list of the row axis labels
-
-
dtype
-
return the dtype object of the underlying data
-
-
dtypes
-
return the dtype object of the underlying data
-
-
ftype
-
return if the data is sparse|dense
-
-
ftypes
-
return if the data is sparse|dense
-
-
imag
-
-
index
-
-
name
-
-
real
-
-
values
-
Return Series as ndarray or ndarray-like
-depending on the dtype

-Returns
--------
-arr : numpy.ndarray or ndarray-like

-Examples
---------
->>> pd.Series([1, 2, 3]).values
-array([1, 2, 3])

->>> pd.Series(list('aabc')).values
-array(['a', 'a', 'b', 'c'], dtype=object)

->>> pd.Series(list('aabc')).astype('category').values
-[a, a, b, c]
-Categories (3, object): [a, b, c]

-Timezone aware datetime data is converted to UTC:

->>> pd.Series(pd.date_range('20130101', periods=3,
-...                         tz='US/Eastern')).values
-array(['2013-01-01T05:00:00.000000000',
-       '2013-01-02T05:00:00.000000000',
-       '2013-01-03T05:00:00.000000000'], dtype='datetime64[ns]')
-
-
-Data and other attributes inherited from pandas.core.series.Series:
-
cat = <class 'pandas.core.categorical.CategoricalAccessor'>
Accessor object for categorical properties of the Series values.

-Be aware that assigning to `categories` is a inplace operation, while all
-methods return new categorical data per default (but can be called with
-`inplace=True`).

-Examples
---------
->>> s.cat.categories
->>> s.cat.categories = list('abc')
->>> s.cat.rename_categories(list('cab'))
->>> s.cat.reorder_categories(list('cab'))
->>> s.cat.add_categories(['d','e'])
->>> s.cat.remove_categories(['d'])
->>> s.cat.remove_unused_categories()
->>> s.cat.set_categories(list('abcde'))
->>> s.cat.as_ordered()
->>> s.cat.as_unordered()
- -
plot = <class 'pandas.plotting._core.SeriesPlotMethods'>
Series plotting accessor and method

-Examples
---------
->>> s.plot.line()
->>> s.plot.bar()
->>> s.plot.hist()

-Plotting methods can also be accessed by calling the accessor as a method
-with the ``kind`` argument:
-``s.plot(kind='line')`` is equivalent to ``s.plot.line()``
- -
-Methods inherited from pandas.core.base.IndexOpsMixin:
-
factorize(self, sort=False, na_sentinel=-1)
Encode the object as an enumerated type or categorical variable

-Parameters
-----------
-sort : boolean, default False
-    Sort by values
-na_sentinel: int, default -1
-    Value to mark "not found"

-Returns
--------
-labels : the indexer to the original array
-uniques : the unique Index
- -
item(self)
return the first element of the underlying data as a python
-scalar
- -
nunique(self, dropna=True)
Return number of unique elements in the object.

-Excludes NA values by default.

-Parameters
-----------
-dropna : boolean, default True
-    Don't include NaN in the count.

-Returns
--------
-nunique : int
- -
transpose(self, *args, **kwargs)
return the transpose, which is by definition self
- -
value_counts(self, normalize=False, sort=True, ascending=False, bins=None, dropna=True)
Returns object containing counts of unique values.

-The resulting object will be in descending order so that the
-first element is the most frequently-occurring element.
-Excludes NA values by default.

-Parameters
-----------
-normalize : boolean, default False
-    If True then the object returned will contain the relative
-    frequencies of the unique values.
-sort : boolean, default True
-    Sort by values
-ascending : boolean, default False
-    Sort in ascending order
-bins : integer, optional
-    Rather than count values, group them into half-open bins,
-    a convenience for pd.cut, only works with numeric data
-dropna : boolean, default True
-    Don't include counts of NaN.

-Returns
--------
-counts : Series
- -
-Data descriptors inherited from pandas.core.base.IndexOpsMixin:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-
base
-
return the base object if the memory of the underlying data is
-shared
-
-
data
-
return the data pointer of the underlying data
-
-
empty
-
-
flags
-
return the ndarray.flags for the underlying data
-
-
hasnans
-
-
is_monotonic
-
Return boolean if values in the object are
-monotonic_increasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic : boolean
-
-
is_monotonic_decreasing
-
Return boolean if values in the object are
-monotonic_decreasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic_decreasing : boolean
-
-
is_monotonic_increasing
-
Return boolean if values in the object are
-monotonic_increasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic : boolean
-
-
is_unique
-
Return boolean if values in the object are unique

-Returns
--------
-is_unique : boolean
-
-
itemsize
-
return the size of the dtype of the item of the underlying data
-
-
nbytes
-
return the number of bytes in the underlying data
-
-
ndim
-
return the number of dimensions of the underlying data,
-by definition 1
-
-
shape
-
return a tuple of the shape of the underlying data
-
-
size
-
return the number of elements in the underlying data
-
-
strides
-
return the strides of the underlying data
-
-
-Data and other attributes inherited from pandas.core.base.IndexOpsMixin:
-
__array_priority__ = 1000
- -
-Data and other attributes inherited from pandas.core.strings.StringAccessorMixin:
-
str = <class 'pandas.core.strings.StringMethods'>
Vectorized string functions for Series and Index. NAs stay NA unless
-handled otherwise by a particular method. Patterned after Python's string
-methods, with some inspiration from R's stringr package.

-Examples
---------
->>> s.str.split('_')
->>> s.str.replace('_', '')
- -
-Methods inherited from pandas.core.generic.NDFrame:
-
__abs__(self)
- -
__bool__ = __nonzero__(self)
- -
__contains__(self, key)
True if the key is in the info axis
- -
__copy__(self, deep=True)
- -
__deepcopy__(self, memo=None)
- -
__delitem__(self, key)
Delete item
- -
__finalize__(self, other, method=None, **kwargs)
Propagate metadata from other to self.

-Parameters
-----------
-other : the object from which to get the attributes that we are going
-    to propagate
-method : optional, a passed method name ; possibly to take different
-    types of propagation actions based on this
- -
__getattr__(self, name)
After regular attribute access, try looking up the name
-This allows simpler access to columns for interactive use.
- -
__getstate__(self)
- -
__hash__(self)
Return hash(self).
- -
__invert__(self)
- -
__neg__(self)
- -
__nonzero__(self)
- -
__round__(self, decimals=0)
- -
__setattr__(self, name, value)
After regular attribute access, try setting the name
-This allows simpler access to columns for interactive use.
- -
__setstate__(self, state)
- -
abs(self)
Return an object with absolute value taken--only applicable to objects
-that are all numeric.

-Returns
--------
-abs: type of caller
- -
add_prefix(self, prefix)
Concatenate prefix string with panel items names.

-Parameters
-----------
-prefix : string

-Returns
--------
-with_prefix : type of caller
- -
add_suffix(self, suffix)
Concatenate suffix string with panel items names.

-Parameters
-----------
-suffix : string

-Returns
--------
-with_suffix : type of caller
- -
as_blocks(self, copy=True)
Convert the frame to a dict of dtype -> Constructor Types that each has
-a homogeneous dtype.

-NOTE: the dtypes of the blocks WILL BE PRESERVED HERE (unlike in
-      as_matrix)

-Parameters
-----------
-copy : boolean, default True

-       .. versionadded: 0.16.1

-Returns
--------
-values : a dict of dtype -> Constructor Types
- -
as_matrix(self, columns=None)
Convert the frame to its Numpy-array representation.

-Parameters
-----------
-columns: list, optional, default:None
-    If None, return all columns, otherwise, returns specified columns.

-Returns
--------
-values : ndarray
-    If the caller is heterogeneous and contains booleans or objects,
-    the result will be of dtype=object. See Notes.


-Notes
------
-Return is NOT a Numpy-matrix, rather, a Numpy-array.

-The dtype will be a lower-common-denominator dtype (implicit
-upcasting); that is to say if the dtypes (even of numeric types)
-are mixed, the one that accommodates all will be chosen. Use this
-with care if you are not dealing with the blocks.

-e.g. If the dtypes are float16 and float32, dtype will be upcast to
-float32.  If dtypes are int32 and uint8, dtype will be upcase to
-int32. By numpy.find_common_type convention, mixing int64 and uint64
-will result in a flot64 dtype.

-This method is provided for backwards compatibility. Generally,
-it is recommended to use '.values'.

-See Also
---------
-pandas.DataFrame.values
- -
asfreq(self, freq, method=None, how=None, normalize=False, fill_value=None)
Convert TimeSeries to specified frequency.

-Optionally provide filling method to pad/backfill missing values.

-Returns the original data conformed to a new index with the specified
-frequency. ``resample`` is more appropriate if an operation, such as
-summarization, is necessary to represent the data at the new frequency.

-Parameters
-----------
-freq : DateOffset object, or string
-method : {'backfill'/'bfill', 'pad'/'ffill'}, default None
-    Method to use for filling holes in reindexed Series (note this
-    does not fill NaNs that already were present):

-    * 'pad' / 'ffill': propagate last valid observation forward to next
-      valid
-    * 'backfill' / 'bfill': use NEXT valid observation to fill
-how : {'start', 'end'}, default end
-    For PeriodIndex only, see PeriodIndex.asfreq
-normalize : bool, default False
-    Whether to reset output index to midnight
-fill_value: scalar, optional
-    Value to use for missing values, applied during upsampling (note
-    this does not fill NaNs that already were present).

-    .. versionadded:: 0.20.0

-Returns
--------
-converted : type of caller

-Examples
---------

-Start by creating a series with 4 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=4, freq='T')
->>> series = pd.Series([0.0, None, 2.0, 3.0], index=index)
->>> df = pd.DataFrame({'s':series})
->>> df
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:03:00    3.0

-Upsample the series into 30 second bins.

->>> df.asfreq(freq='30S')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    NaN
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``fill value``.

->>> df.asfreq(freq='30S', fill_value=9.0)
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    9.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    9.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    9.0
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``method``.

->>> df.asfreq(freq='30S', method='bfill')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    2.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    3.0
-2000-01-01 00:03:00    3.0

-See Also
---------
-reindex

-Notes
------
-To learn more about the frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
- -
asof(self, where, subset=None)
The last row without any NaN is taken (or the last row without
-NaN considering only the subset of columns in the case of a DataFrame)

-.. versionadded:: 0.19.0 For DataFrame

-If there is no good value, NaN is returned for a Series
-a Series of NaN values for a DataFrame

-Parameters
-----------
-where : date or array of dates
-subset : string or list of strings, default None
-   if not None use these columns for NaN propagation

-Notes
------
-Dates are assumed to be sorted
-Raises if this is not the case

-Returns
--------
-where is scalar

-  - value or NaN if input is Series
-  - Series if input is DataFrame

-where is Index: same shape object as input

-See Also
---------
-merge_asof
- -
astype(self, dtype, copy=True, errors='raise', **kwargs)
Cast object to input numpy.dtype
-Return a copy when copy = True (be really careful with this!)

-Parameters
-----------
-dtype : data type, or dict of column name -> data type
-    Use a numpy.dtype or Python type to cast entire pandas object to
-    the same type. Alternatively, use {col: dtype, ...}, where col is a
-    column label and dtype is a numpy.dtype or Python type to cast one
-    or more of the DataFrame's columns to column-specific types.
-errors : {'raise', 'ignore'}, default 'raise'.
-    Control raising of exceptions on invalid data for provided dtype.

-    - ``raise`` : allow exceptions to be raised
-    - ``ignore`` : suppress exceptions. On error return original object

-    .. versionadded:: 0.20.0

-raise_on_error : DEPRECATED use ``errors`` instead
-kwargs : keyword arguments to pass on to the constructor

-Returns
--------
-casted : type of caller
- -
at_time(self, time, asof=False)
Select values at particular time of day (e.g. 9:30AM).

-Parameters
-----------
-time : datetime.time or string

-Returns
--------
-values_at_time : type of caller
- -
between_time(self, start_time, end_time, include_start=True, include_end=True)
Select values between particular times of the day (e.g., 9:00-9:30 AM).

-Parameters
-----------
-start_time : datetime.time or string
-end_time : datetime.time or string
-include_start : boolean, default True
-include_end : boolean, default True

-Returns
--------
-values_between_time : type of caller
- -
bfill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='bfill') <DataFrame.fillna>`
- -
bool(self)
Return the bool of a single element PandasObject.

-This must be a boolean scalar value, either True or False.  Raise a
-ValueError if the PandasObject does not have exactly 1 element, or that
-element is not boolean
- -
clip(self, lower=None, upper=None, axis=None, *args, **kwargs)
Trim values at input threshold(s).

-Parameters
-----------
-lower : float or array_like, default None
-upper : float or array_like, default None
-axis : int or string axis name, optional
-    Align object with lower and upper along the given axis.

-Returns
--------
-clipped : Series

-Examples
---------
->>> df
-  0         1
-0  0.335232 -1.256177
-1 -1.367855  0.746646
-2  0.027753 -1.176076
-3  0.230930 -0.679613
-4  1.261967  0.570967
->>> df.clip(-1.0, 0.5)
-          0         1
-0  0.335232 -1.000000
-1 -1.000000  0.500000
-2  0.027753 -1.000000
-3  0.230930 -0.679613
-4  0.500000  0.500000
->>> t
-0   -0.3
-1   -0.2
-2   -0.1
-3    0.0
-4    0.1
-dtype: float64
->>> df.clip(t, t + 1, axis=0)
-          0         1
-0  0.335232 -0.300000
-1 -0.200000  0.746646
-2  0.027753 -0.100000
-3  0.230930  0.000000
-4  1.100000  0.570967
- -
clip_lower(self, threshold, axis=None)
Return copy of the input with values below given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
clip_upper(self, threshold, axis=None)
Return copy of input with values above given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
consolidate(self, inplace=False)
DEPRECATED: consolidate will be an internal implementation only.
- -
convert_objects(self, convert_dates=True, convert_numeric=False, convert_timedeltas=True, copy=True)
Deprecated.

-Attempt to infer better dtype for object columns

-Parameters
-----------
-convert_dates : boolean, default True
-    If True, convert to date where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-convert_numeric : boolean, default False
-    If True, attempt to coerce to numbers (including strings), with
-    unconvertible values becoming NaN.
-convert_timedeltas : boolean, default True
-    If True, convert to timedelta where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-copy : boolean, default True
-    If True, return a copy even if no copy is necessary (e.g. no
-    conversion was done). Note: This is meant for internal use, and
-    should not be confused with inplace.

-See Also
---------
-pandas.to_datetime : Convert argument to datetime.
-pandas.to_timedelta : Convert argument to timedelta.
-pandas.to_numeric : Return a fixed frequency timedelta index,
-    with day as the default.

-Returns
--------
-converted : same as input object
- -
copy(self, deep=True)
Make a copy of this objects data.

-Parameters
-----------
-deep : boolean or string, default True
-    Make a deep copy, including a copy of the data and the indices.
-    With ``deep=False`` neither the indices or the data are copied.

-    Note that when ``deep=True`` data is copied, actual python objects
-    will not be copied recursively, only the reference to the object.
-    This is in contrast to ``copy.deepcopy`` in the Standard Library,
-    which recursively copies object data.

-Returns
--------
-copy : type of caller
- -
describe(self, percentiles=None, include=None, exclude=None)
Generates descriptive statistics that summarize the central tendency,
-dispersion and shape of a dataset's distribution, excluding
-``NaN`` values.

-Analyzes both numeric and object series, as well
-as ``DataFrame`` column sets of mixed data types. The output
-will vary depending on what is provided. Refer to the notes
-below for more detail.

-Parameters
-----------
-percentiles : list-like of numbers, optional
-    The percentiles to include in the output. All should
-    fall between 0 and 1. The default is
-    ``[.25, .5, .75]``, which returns the 25th, 50th, and
-    75th percentiles.
-include : 'all', list-like of dtypes or None (default), optional
-    A white list of data types to include in the result. Ignored
-    for ``Series``. Here are the options:

-    - 'all' : All columns of the input will be included in the output.
-    - A list-like of dtypes : Limits the results to the
-      provided data types.
-      To limit the result to numeric types submit
-      ``numpy.number``. To limit it instead to categorical
-      objects submit the ``numpy.object`` data type. Strings
-      can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will include all numeric columns.
-exclude : list-like of dtypes or None (default), optional,
-    A black list of data types to omit from the result. Ignored
-    for ``Series``. Here are the options:

-    - A list-like of dtypes : Excludes the provided data types
-      from the result. To select numeric types submit
-      ``numpy.number``. To select categorical objects submit the data
-      type ``numpy.object``. Strings can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will exclude nothing.

-Returns
--------
-summary:  Series/DataFrame of summary statistics

-Notes
------
-For numeric data, the result's index will include ``count``,
-``mean``, ``std``, ``min``, ``max`` as well as lower, ``50`` and
-upper percentiles. By default the lower percentile is ``25`` and the
-upper percentile is ``75``. The ``50`` percentile is the
-same as the median.

-For object data (e.g. strings or timestamps), the result's index
-will include ``count``, ``unique``, ``top``, and ``freq``. The ``top``
-is the most common value. The ``freq`` is the most common value's
-frequency. Timestamps also include the ``first`` and ``last`` items.

-If multiple object values have the highest count, then the
-``count`` and ``top`` results will be arbitrarily chosen from
-among those with the highest count.

-For mixed data types provided via a ``DataFrame``, the default is to
-return only an analysis of numeric columns. If ``include='all'``
-is provided as an option, the result will include a union of
-attributes of each type.

-The `include` and `exclude` parameters can be used to limit
-which columns in a ``DataFrame`` are analyzed for the output.
-The parameters are ignored when analyzing a ``Series``.

-Examples
---------
-Describing a numeric ``Series``.

->>> s = pd.Series([1, 2, 3])
->>> s.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0

-Describing a categorical ``Series``.

->>> s = pd.Series(['a', 'a', 'b', 'c'])
->>> s.describe()
-count     4
-unique    3
-top       a
-freq      2
-dtype: object

-Describing a timestamp ``Series``.

->>> s = pd.Series([
-...   np.datetime64("2000-01-01"),
-...   np.datetime64("2010-01-01"),
-...   np.datetime64("2010-01-01")
-... ])
->>> s.describe()
-count                       3
-unique                      2
-top       2010-01-01 00:00:00
-freq                        2
-first     2000-01-01 00:00:00
-last      2010-01-01 00:00:00
-dtype: object

-Describing a ``DataFrame``. By default only numeric fields
-are returned.

->>> df = pd.DataFrame([[1, 'a'], [2, 'b'], [3, 'c']],
-...                   columns=['numeric', 'object'])
->>> df.describe()
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Describing all columns of a ``DataFrame`` regardless of data type.

->>> df.describe(include='all')
-        numeric object
-count       3.0      3
-unique      NaN      3
-top         NaN      b
-freq        NaN      1
-mean        2.0    NaN
-std         1.0    NaN
-min         1.0    NaN
-25%         1.5    NaN
-50%         2.0    NaN
-75%         2.5    NaN
-max         3.0    NaN

-Describing a column from a ``DataFrame`` by accessing it as
-an attribute.

->>> df.numeric.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0
-Name: numeric, dtype: float64

-Including only numeric columns in a ``DataFrame`` description.

->>> df.describe(include=[np.number])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Including only string columns in a ``DataFrame`` description.

->>> df.describe(include=[np.object])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding numeric columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.number])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding object columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.object])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-See Also
---------
-DataFrame.count
-DataFrame.max
-DataFrame.min
-DataFrame.mean
-DataFrame.std
-DataFrame.select_dtypes
- -
drop(self, labels, axis=0, level=None, inplace=False, errors='raise')
Return new object with labels in requested axis removed.

-Parameters
-----------
-labels : single label or list-like
-axis : int or axis name
-level : int or level name, default None
-    For MultiIndex
-inplace : bool, default False
-    If True, do operation inplace and return None.
-errors : {'ignore', 'raise'}, default 'raise'
-    If 'ignore', suppress error and existing labels are dropped.

-    .. versionadded:: 0.16.1

-Returns
--------
-dropped : type of caller
- -
equals(self, other)
Determines if two NDFrame objects contain the same elements. NaNs in
-the same location are considered equal.
- -
ffill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='ffill') <DataFrame.fillna>`
- -
filter(self, items=None, like=None, regex=None, axis=None)
Subset rows or columns of dataframe according to labels in
-the specified index.

-Note that this routine does not filter a dataframe on its
-contents. The filter is applied to the labels of the index.

-Parameters
-----------
-items : list-like
-    List of info axis to restrict to (must not all be present)
-like : string
-    Keep info axis where "arg in col == True"
-regex : string (regular expression)
-    Keep info axis with re.search(regex, col) == True
-axis : int or string axis name
-    The axis to filter on.  By default this is the info axis,
-    'index' for Series, 'columns' for DataFrame

-Returns
--------
-same type as input object

-Examples
---------
->>> df
-one  two  three
-mouse     1    2      3
-rabbit    4    5      6

->>> # select columns by name
->>> df.filter(items=['one', 'three'])
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select columns by regular expression
->>> df.filter(regex='e$', axis=1)
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select rows containing 'bbi'
->>> df.filter(like='bbi', axis=0)
-one  two  three
-rabbit    4    5      6

-See Also
---------
-pandas.DataFrame.select

-Notes
------
-The ``items``, ``like``, and ``regex`` parameters are
-enforced to be mutually exclusive.

-``axis`` defaults to the info axis that is used when indexing
-with ``[]``.
- -
first(self, offset)
Convenience method for subsetting initial periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.first('10D') -> First 10 days

-Returns
--------
-subset : type of caller
- -
get(self, key, default=None)
Get item from object for given key (DataFrame column, Panel slice,
-etc.). Returns default value if not found.

-Parameters
-----------
-key : object

-Returns
--------
-value : type of items contained in object
- -
get_dtype_counts(self)
Return the counts of dtypes in this object.
- -
get_ftype_counts(self)
Return the counts of ftypes in this object.
- -
groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, **kwargs)
Group series using mapper (dict or key function, apply given function
-to group, return result as series) or by a series of columns.

-Parameters
-----------
-by : mapping, function, str, or iterable
-    Used to determine the groups for the groupby.
-    If ``by`` is a function, it's called on each value of the object's
-    index. If a dict or Series is passed, the Series or dict VALUES
-    will be used to determine the groups (the Series' values are first
-    aligned; see ``.align()`` method). If an ndarray is passed, the
-    values are used as-is determine the groups. A str or list of strs
-    may be passed to group by the columns in ``self``
-axis : int, default 0
-level : int, level name, or sequence of such, default None
-    If the axis is a MultiIndex (hierarchical), group by a particular
-    level or levels
-as_index : boolean, default True
-    For aggregated output, return object with group labels as the
-    index. Only relevant for DataFrame input. as_index=False is
-    effectively "SQL-style" grouped output
-sort : boolean, default True
-    Sort group keys. Get better performance by turning this off.
-    Note this does not influence the order of observations within each
-    group.  groupby preserves the order of rows within each group.
-group_keys : boolean, default True
-    When calling apply, add group keys to index to identify pieces
-squeeze : boolean, default False
-    reduce the dimensionality of the return type if possible,
-    otherwise return a consistent type

-Examples
---------
-DataFrame results

->>> data.groupby(func, axis=0).mean()
->>> data.groupby(['col1', 'col2'])['col3'].mean()

-DataFrame with hierarchical index

->>> data.groupby(['col1', 'col2']).mean()

-Returns
--------
-GroupBy object
- -
head(self, n=5)
Returns first n rows
- -
interpolate(self, method='linear', axis=0, limit=None, inplace=False, limit_direction='forward', downcast=None, **kwargs)
Interpolate values according to different methods.

-Please note that only ``method='linear'`` is supported for
-DataFrames/Series with a MultiIndex.

-Parameters
-----------
-method : {'linear', 'time', 'index', 'values', 'nearest', 'zero',
-          'slinear', 'quadratic', 'cubic', 'barycentric', 'krogh',
-          'polynomial', 'spline', 'piecewise_polynomial',
-          'from_derivatives', 'pchip', 'akima'}

-    * 'linear': ignore the index and treat the values as equally
-      spaced. This is the only method supported on MultiIndexes.
-      default
-    * 'time': interpolation works on daily and higher resolution
-      data to interpolate given length of interval
-    * 'index', 'values': use the actual numerical values of the index
-    * 'nearest', 'zero', 'slinear', 'quadratic', 'cubic',
-      'barycentric', 'polynomial' is passed to
-      ``scipy.interpolate.interp1d``. Both 'polynomial' and 'spline'
-      require that you also specify an `order` (int),
-      e.g. df.interpolate(method='polynomial', order=4).
-      These use the actual numerical values of the index.
-    * 'krogh', 'piecewise_polynomial', 'spline', 'pchip' and 'akima'
-      are all wrappers around the scipy interpolation methods of
-      similar names. These use the actual numerical values of the
-      index. For more information on their behavior, see the
-      `scipy documentation
-      <http://docs.scipy.org/doc/scipy/reference/interpolate.html#univariate-interpolation>`__
-      and `tutorial documentation
-      <http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html>`__
-    * 'from_derivatives' refers to BPoly.from_derivatives which
-      replaces 'piecewise_polynomial' interpolation method in
-      scipy 0.18

-    .. versionadded:: 0.18.1

-       Added support for the 'akima' method
-       Added interpolate method 'from_derivatives' which replaces
-       'piecewise_polynomial' in scipy 0.18; backwards-compatible with
-       scipy < 0.18

-axis : {0, 1}, default 0
-    * 0: fill column-by-column
-    * 1: fill row-by-row
-limit : int, default None.
-    Maximum number of consecutive NaNs to fill. Must be greater than 0.
-limit_direction : {'forward', 'backward', 'both'}, default 'forward'
-    If limit is specified, consecutive NaNs will be filled in this
-    direction.

-    .. versionadded:: 0.17.0

-inplace : bool, default False
-    Update the NDFrame in place if possible.
-downcast : optional, 'infer' or None, defaults to None
-    Downcast dtypes if possible.
-kwargs : keyword arguments to pass on to the interpolating function.

-Returns
--------
-Series or DataFrame of same shape interpolated at the NaNs

-See Also
---------
-reindex, replace, fillna

-Examples
---------

-Filling in NaNs

->>> s = pd.Series([0, 1, np.nan, 3])
->>> s.interpolate()
-0    0
-1    1
-2    2
-3    3
-dtype: float64
- -
isnull(self)
Return a boolean same-sized object indicating if the values are null.

-See Also
---------
-notnull : boolean inverse of isnull
- -
last(self, offset)
Convenience method for subsetting final periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.last('5M') -> Last 5 months

-Returns
--------
-subset : type of caller
- -
mask(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is False and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The mask method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``False`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``mask`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.where`
- -
notnull(self)
Return a boolean same-sized object indicating if the values are
-not null.

-See Also
---------
-isnull : boolean inverse of notnull
- -
pct_change(self, periods=1, fill_method='pad', limit=None, freq=None, **kwargs)
Percent change over given number of periods.

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming percent change
-fill_method : str, default 'pad'
-    How to handle NAs before computing percent changes
-limit : int, default None
-    The number of consecutive NAs to fill before stopping
-freq : DateOffset, timedelta, or offset alias string, optional
-    Increment to use from time series API (e.g. 'M' or BDay())

-Returns
--------
-chg : NDFrame

-Notes
------

-By default, the percentage change is calculated along the stat
-axis: 0, or ``Index``, for ``DataFrame`` and 1, or ``minor`` for
-``Panel``. You can change this with the ``axis`` keyword argument.
- -
pipe(self, func, *args, **kwargs)
Apply func(self, \*args, \*\*kwargs)

-.. versionadded:: 0.16.2

-Parameters
-----------
-func : function
-    function to apply to the NDFrame.
-    ``args``, and ``kwargs`` are passed into ``func``.
-    Alternatively a ``(callable, data_keyword)`` tuple where
-    ``data_keyword`` is a string indicating the keyword of
-    ``callable`` that expects the NDFrame.
-args : positional arguments passed into ``func``.
-kwargs : a dictionary of keyword arguments passed into ``func``.

-Returns
--------
-object : the return type of ``func``.

-Notes
------

-Use ``.pipe`` when chaining together functions that expect
-on Series or DataFrames. Instead of writing

->>> f(g(h(df), arg1=a), arg2=b, arg3=c)

-You can write

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe(f, arg2=b, arg3=c)
-... )

-If you have a function that takes the data as (say) the second
-argument, pass a tuple indicating which keyword expects the
-data. For example, suppose ``f`` takes its data as ``arg2``:

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe((f, 'arg2'), arg1=a, arg3=c)
-...  )

-See Also
---------
-pandas.DataFrame.apply
-pandas.DataFrame.applymap
-pandas.Series.map
- -
pop(self, item)
Return item and drop from frame. Raise KeyError if not found.
- -
rank(self, axis=0, method='average', numeric_only=None, na_option='keep', ascending=True, pct=False)
Compute numerical data ranks (1 through n) along axis. Equal values are
-assigned a rank that is the average of the ranks of those values

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    index to direct ranking
-method : {'average', 'min', 'max', 'first', 'dense'}
-    * average: average rank of group
-    * min: lowest rank in group
-    * max: highest rank in group
-    * first: ranks assigned in order they appear in the array
-    * dense: like 'min', but rank always increases by 1 between groups
-numeric_only : boolean, default None
-    Include only float, int, boolean data. Valid only for DataFrame or
-    Panel objects
-na_option : {'keep', 'top', 'bottom'}
-    * keep: leave NA values where they are
-    * top: smallest rank if ascending
-    * bottom: smallest rank if descending
-ascending : boolean, default True
-    False for ranks by high (1) to low (N)
-pct : boolean, default False
-    Computes percentage rank of data

-Returns
--------
-ranks : same type as caller
- -
reindex_like(self, other, method=None, copy=True, limit=None, tolerance=None)
Return an object with matching indices to myself.

-Parameters
-----------
-other : Object
-method : string or None
-copy : boolean, default True
-limit : int, default None
-    Maximum number of consecutive labels to fill for inexact matches.
-tolerance : optional
-    Maximum distance between labels of the other object and this
-    object for inexact matches.

-    .. versionadded:: 0.17.0

-Notes
------
-Like calling s.reindex(index=other.index, columns=other.columns,
-                       method=...)

-Returns
--------
-reindexed : same as input
- -
rename_axis(self, mapper, axis=0, copy=True, inplace=False)
Alter index and / or columns using input function or functions.
-A scalar or list-like for ``mapper`` will alter the ``Index.name``
-or ``MultiIndex.names`` attribute.
-A function or dict for ``mapper`` will alter the labels.
-Function / dict values must be unique (1-to-1). Labels not contained in
-a dict / Series will be left as-is.

-Parameters
-----------
-mapper : scalar, list-like, dict-like or function, optional
-axis : int or string, default 0
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False

-Returns
--------
-renamed : type of caller

-See Also
---------
-pandas.NDFrame.rename
-pandas.Index.rename

-Examples
---------
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename_axis("foo")  # scalar, alters df.index.name
-     A  B
-foo
-0    1  4
-1    2  5
-2    3  6
->>> df.rename_axis(lambda x: 2 * x)  # function: alters labels
-   A  B
-0  1  4
-2  2  5
-4  3  6
->>> df.rename_axis({"A": "ehh", "C": "see"}, axis="columns")  # mapping
-   ehh  B
-0    1  4
-1    2  5
-2    3  6
- -
replace(self, to_replace=None, value=None, inplace=False, limit=None, regex=False, method='pad', axis=None)
Replace values given in 'to_replace' with 'value'.

-Parameters
-----------
-to_replace : str, regex, list, dict, Series, numeric, or None

-    * str or regex:

-        - str: string exactly matching `to_replace` will be replaced
-          with `value`
-        - regex: regexs matching `to_replace` will be replaced with
-          `value`

-    * list of str, regex, or numeric:

-        - First, if `to_replace` and `value` are both lists, they
-          **must** be the same length.
-        - Second, if ``regex=True`` then all of the strings in **both**
-          lists will be interpreted as regexs otherwise they will match
-          directly. This doesn't matter much for `value` since there
-          are only a few possible substitution regexes you can use.
-        - str and regex rules apply as above.

-    * dict:

-        - Nested dictionaries, e.g., {'a': {'b': nan}}, are read as
-          follows: look in column 'a' for the value 'b' and replace it
-          with nan. You can nest regular expressions as well. Note that
-          column names (the top-level dictionary keys in a nested
-          dictionary) **cannot** be regular expressions.
-        - Keys map to column names and values map to substitution
-          values. You can treat this as a special case of passing two
-          lists except that you are specifying the column to search in.

-    * None:

-        - This means that the ``regex`` argument must be a string,
-          compiled regular expression, or list, dict, ndarray or Series
-          of such elements. If `value` is also ``None`` then this
-          **must** be a nested dictionary or ``Series``.

-    See the examples section for examples of each of these.
-value : scalar, dict, list, str, regex, default None
-    Value to use to fill holes (e.g. 0), alternately a dict of values
-    specifying which value to use for each column (columns not in the
-    dict will not be filled). Regular expressions, strings and lists or
-    dicts of such objects are also allowed.
-inplace : boolean, default False
-    If True, in place. Note: this will modify any
-    other views on this object (e.g. a column form a DataFrame).
-    Returns the caller if this is True.
-limit : int, default None
-    Maximum size gap to forward or backward fill
-regex : bool or same types as `to_replace`, default False
-    Whether to interpret `to_replace` and/or `value` as regular
-    expressions. If this is ``True`` then `to_replace` *must* be a
-    string. Otherwise, `to_replace` must be ``None`` because this
-    parameter will be interpreted as a regular expression or a list,
-    dict, or array of regular expressions.
-method : string, optional, {'pad', 'ffill', 'bfill'}
-    The method to use when for replacement, when ``to_replace`` is a
-    ``list``.

-See Also
---------
-NDFrame.reindex
-NDFrame.asfreq
-NDFrame.fillna

-Returns
--------
-filled : NDFrame

-Raises
-------
-AssertionError
-    * If `regex` is not a ``bool`` and `to_replace` is not ``None``.
-TypeError
-    * If `to_replace` is a ``dict`` and `value` is not a ``list``,
-      ``dict``, ``ndarray``, or ``Series``
-    * If `to_replace` is ``None`` and `regex` is not compilable into a
-      regular expression or is a list, dict, ndarray, or Series.
-ValueError
-    * If `to_replace` and `value` are ``list`` s or ``ndarray`` s, but
-      they are not the same length.

-Notes
------
-* Regex substitution is performed under the hood with ``re.sub``. The
-  rules for substitution for ``re.sub`` are the same.
-* Regular expressions will only substitute on strings, meaning you
-  cannot provide, for example, a regular expression matching floating
-  point numbers and expect the columns in your frame that have a
-  numeric dtype to be matched. However, if those floating point numbers
-  *are* strings, then you can do this.
-* This method has *a lot* of options. You are encouraged to experiment
-  and play with this method to gain intuition about how it works.
- -
resample(self, rule, how=None, axis=0, fill_method=None, closed=None, label=None, convention='start', kind=None, loffset=None, limit=None, base=0, on=None, level=None)
Convenience method for frequency conversion and resampling of time
-series.  Object must have a datetime-like index (DatetimeIndex,
-PeriodIndex, or TimedeltaIndex), or pass datetime-like values
-to the on or level keyword.

-Parameters
-----------
-rule : string
-    the offset string or object representing target conversion
-axis : int, optional, default 0
-closed : {'right', 'left'}
-    Which side of bin interval is closed
-label : {'right', 'left'}
-    Which bin edge label to label bucket with
-convention : {'start', 'end', 's', 'e'}
-loffset : timedelta
-    Adjust the resampled time labels
-base : int, default 0
-    For frequencies that evenly subdivide 1 day, the "origin" of the
-    aggregated intervals. For example, for '5min' frequency, base could
-    range from 0 through 4. Defaults to 0
-on : string, optional
-    For a DataFrame, column to use instead of index for resampling.
-    Column must be datetime-like.

-    .. versionadded:: 0.19.0

-level : string or int, optional
-    For a MultiIndex, level (name or number) to use for
-    resampling.  Level must be datetime-like.

-    .. versionadded:: 0.19.0

-Notes
------
-To learn more about the offset strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-Examples
---------

-Start by creating a series with 9 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=9, freq='T')
->>> series = pd.Series(range(9), index=index)
->>> series
-2000-01-01 00:00:00    0
-2000-01-01 00:01:00    1
-2000-01-01 00:02:00    2
-2000-01-01 00:03:00    3
-2000-01-01 00:04:00    4
-2000-01-01 00:05:00    5
-2000-01-01 00:06:00    6
-2000-01-01 00:07:00    7
-2000-01-01 00:08:00    8
-Freq: T, dtype: int64

-Downsample the series into 3 minute bins and sum the values
-of the timestamps falling into a bin.

->>> series.resample('3T').sum()
-2000-01-01 00:00:00     3
-2000-01-01 00:03:00    12
-2000-01-01 00:06:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but label each
-bin using the right edge instead of the left. Please note that the
-value in the bucket used as the label is not included in the bucket,
-which it labels. For example, in the original series the
-bucket ``2000-01-01 00:03:00`` contains the value 3, but the summed
-value in the resampled bucket with the label``2000-01-01 00:03:00``
-does not include 3 (if it did, the summed value would be 6, not 3).
-To include this value close the right side of the bin interval as
-illustrated in the example below this one.

->>> series.resample('3T', label='right').sum()
-2000-01-01 00:03:00     3
-2000-01-01 00:06:00    12
-2000-01-01 00:09:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but close the right
-side of the bin interval.

->>> series.resample('3T', label='right', closed='right').sum()
-2000-01-01 00:00:00     0
-2000-01-01 00:03:00     6
-2000-01-01 00:06:00    15
-2000-01-01 00:09:00    15
-Freq: 3T, dtype: int64

-Upsample the series into 30 second bins.

->>> series.resample('30S').asfreq()[0:5] #select first 5 rows
-2000-01-01 00:00:00   0.0
-2000-01-01 00:00:30   NaN
-2000-01-01 00:01:00   1.0
-2000-01-01 00:01:30   NaN
-2000-01-01 00:02:00   2.0
-Freq: 30S, dtype: float64

-Upsample the series into 30 second bins and fill the ``NaN``
-values using the ``pad`` method.

->>> series.resample('30S').pad()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    0
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    1
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Upsample the series into 30 second bins and fill the
-``NaN`` values using the ``bfill`` method.

->>> series.resample('30S').bfill()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    1
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    2
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Pass a custom function via ``apply``

->>> def custom_resampler(array_like):
-...     return np.sum(array_like)+5

->>> series.resample('3T').apply(custom_resampler)
-2000-01-01 00:00:00     8
-2000-01-01 00:03:00    17
-2000-01-01 00:06:00    26
-Freq: 3T, dtype: int64

-For DataFrame objects, the keyword ``on`` can be used to specify the
-column instead of the index for resampling.

->>> df = pd.DataFrame(data=9*[range(4)], columns=['a', 'b', 'c', 'd'])
->>> df['time'] = pd.date_range('1/1/2000', periods=9, freq='T')
->>> df.resample('3T', on='time').sum()
-                     a  b  c  d
-time
-2000-01-01 00:00:00  0  3  6  9
-2000-01-01 00:03:00  0  3  6  9
-2000-01-01 00:06:00  0  3  6  9

-For a DataFrame with MultiIndex, the keyword ``level`` can be used to
-specify on level the resampling needs to take place.

->>> time = pd.date_range('1/1/2000', periods=5, freq='T')
->>> df2 = pd.DataFrame(data=10*[range(4)],
-                       columns=['a', 'b', 'c', 'd'],
-                       index=pd.MultiIndex.from_product([time, [1, 2]])
-                       )
->>> df2.resample('3T', level=0).sum()
-                     a  b   c   d
-2000-01-01 00:00:00  0  6  12  18
-2000-01-01 00:03:00  0  4   8  12
- -
sample(self, n=None, frac=None, replace=False, weights=None, random_state=None, axis=None)
Returns a random sample of items from an axis of object.

-.. versionadded:: 0.16.1

-Parameters
-----------
-n : int, optional
-    Number of items from axis to return. Cannot be used with `frac`.
-    Default = 1 if `frac` = None.
-frac : float, optional
-    Fraction of axis items to return. Cannot be used with `n`.
-replace : boolean, optional
-    Sample with or without replacement. Default = False.
-weights : str or ndarray-like, optional
-    Default 'None' results in equal probability weighting.
-    If passed a Series, will align with target object on index. Index
-    values in weights not found in sampled object will be ignored and
-    index values in sampled object not in weights will be assigned
-    weights of zero.
-    If called on a DataFrame, will accept the name of a column
-    when axis = 0.
-    Unless weights are a Series, weights must be same length as axis
-    being sampled.
-    If weights do not sum to 1, they will be normalized to sum to 1.
-    Missing values in the weights column will be treated as zero.
-    inf and -inf values not allowed.
-random_state : int or numpy.random.RandomState, optional
-    Seed for the random number generator (if int), or numpy RandomState
-    object.
-axis : int or string, optional
-    Axis to sample. Accepts axis number or name. Default is stat axis
-    for given data type (0 for Series and DataFrames, 1 for Panels).

-Returns
--------
-A new object of same type as caller.

-Examples
---------

-Generate an example ``Series`` and ``DataFrame``:

->>> s = pd.Series(np.random.randn(50))
->>> s.head()
-0   -0.038497
-1    1.820773
-2   -0.972766
-3   -1.598270
-4   -1.095526
-dtype: float64
->>> df = pd.DataFrame(np.random.randn(50, 4), columns=list('ABCD'))
->>> df.head()
-          A         B         C         D
-0  0.016443 -2.318952 -0.566372 -1.028078
-1 -1.051921  0.438836  0.658280 -0.175797
-2 -1.243569 -0.364626 -0.215065  0.057736
-3  1.768216  0.404512 -0.385604 -1.457834
-4  1.072446 -1.137172  0.314194 -0.046661

-Next extract a random sample from both of these objects...

-3 random elements from the ``Series``:

->>> s.sample(n=3)
-27   -0.994689
-55   -1.049016
-67   -0.224565
-dtype: float64

-And a random 10% of the ``DataFrame`` with replacement:

->>> df.sample(frac=0.1, replace=True)
-           A         B         C         D
-35  1.981780  0.142106  1.817165 -0.290805
-49 -1.336199 -0.448634 -0.789640  0.217116
-40  0.823173 -0.078816  1.009536  1.015108
-15  1.421154 -0.055301 -1.922594 -0.019696
-6  -0.148339  0.832938  1.787600 -1.383767
- -
select(self, crit, axis=0)
Return data corresponding to axis labels matching criteria

-Parameters
-----------
-crit : function
-    To be called on each index (label). Should return True or False
-axis : int

-Returns
--------
-selection : type of caller
- -
set_axis(self, axis, labels)
public verson of axis assignment
- -
slice_shift(self, periods=1, axis=0)
Equivalent to `shift` without copying data. The shifted data will
-not include the dropped periods and the shifted axis will be smaller
-than the original.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative

-Notes
------
-While the `slice_shift` is faster than `shift`, you may pay for it
-later during alignment.

-Returns
--------
-shifted : same type as caller
- -
squeeze(self, axis=None)
Squeeze length 1 dimensions.

-Parameters
-----------
-axis : None, integer or string axis name, optional
-    The axis to squeeze if 1-sized.

-    .. versionadded:: 0.20.0

-Returns
--------
-scalar if 1-sized, else original object
- -
swapaxes(self, axis1, axis2, copy=True)
Interchange axes and swap values axes appropriately

-Returns
--------
-y : same as input
- -
tail(self, n=5)
Returns last n rows
- -
to_clipboard(self, excel=None, sep=None, **kwargs)
Attempt to write text representation of object to the system clipboard
-This can be pasted into Excel, for example.

-Parameters
-----------
-excel : boolean, defaults to True
-        if True, use the provided separator, writing in a csv
-        format for allowing easy pasting into excel.
-        if False, write a string representation of the object
-        to the clipboard
-sep : optional, defaults to tab
-other keywords are passed to to_csv

-Notes
------
-Requirements for your platform
-  - Linux: xclip, or xsel (with gtk or PyQt4 modules)
-  - Windows: none
-  - OS X: none
- -
to_dense(self)
Return dense representation of NDFrame (as opposed to sparse)
- -
to_hdf(self, path_or_buf, key, **kwargs)
Write the contained data to an HDF5 file using HDFStore.

-Parameters
-----------
-path_or_buf : the path (string) or HDFStore object
-key : string
-    identifier for the group in the store
-mode : optional, {'a', 'w', 'r+'}, default 'a'

-  ``'w'``
-      Write; a new file is created (an existing file with the same
-      name would be deleted).
-  ``'a'``
-      Append; an existing file is opened for reading and writing,
-      and if the file does not exist it is created.
-  ``'r+'``
-      It is similar to ``'a'``, but the file must already exist.
-format : 'fixed(f)|table(t)', default is 'fixed'
-    fixed(f) : Fixed format
-               Fast writing/reading. Not-appendable, nor searchable
-    table(t) : Table format
-               Write as a PyTables Table structure which may perform
-               worse but allow more flexible operations like searching
-               / selecting subsets of the data
-append : boolean, default False
-    For Table formats, append the input data to the existing
-data_columns :  list of columns, or True, default None
-    List of columns to create as indexed data columns for on-disk
-    queries, or True to use all columns. By default only the axes
-    of the object are indexed. See `here
-    <http://pandas.pydata.org/pandas-docs/stable/io.html#query-via-data-columns>`__.

-    Applicable only to format='table'.
-complevel : int, 1-9, default 0
-    If a complib is specified compression will be applied
-    where possible
-complib : {'zlib', 'bzip2', 'lzo', 'blosc', None}, default None
-    If complevel is > 0 apply compression to objects written
-    in the store wherever possible
-fletcher32 : bool, default False
-    If applying compression use the fletcher32 checksum
-dropna : boolean, default False.
-    If true, ALL nan rows will not be written to store.
- -
to_json(self, path_or_buf=None, orient=None, date_format=None, double_precision=10, force_ascii=True, date_unit='ms', default_handler=None, lines=False)
Convert the object to a JSON string.

-Note NaN's and None will be converted to null and datetime objects
-will be converted to UNIX timestamps.

-Parameters
-----------
-path_or_buf : the path or buffer to write the result string
-    if this is None, return a StringIO of the converted string
-orient : string

-    * Series

-      - default is 'index'
-      - allowed values are: {'split','records','index'}

-    * DataFrame

-      - default is 'columns'
-      - allowed values are:
-        {'split','records','index','columns','values'}

-    * The format of the JSON string

-      - split : dict like
-        {index -> [index], columns -> [columns], data -> [values]}
-      - records : list like
-        [{column -> value}, ... , {column -> value}]
-      - index : dict like {index -> {column -> value}}
-      - columns : dict like {column -> {index -> value}}
-      - values : just the values array
-      - table : dict like {'schema': {schema}, 'data': {data}}
-        describing the data, and the data component is
-        like ``orient='records'``.

-        .. versionchanged:: 0.20.0

-date_format : {None, 'epoch', 'iso'}
-    Type of date conversion. `epoch` = epoch milliseconds,
-    `iso` = ISO8601. The default depends on the `orient`. For
-    `orient='table'`, the default is `'iso'`. For all other orients,
-    the default is `'epoch'`.
-double_precision : The number of decimal places to use when encoding
-    floating point values, default 10.
-force_ascii : force encoded string to be ASCII, default True.
-date_unit : string, default 'ms' (milliseconds)
-    The time unit to encode to, governs timestamp and ISO8601
-    precision.  One of 's', 'ms', 'us', 'ns' for second, millisecond,
-    microsecond, and nanosecond respectively.
-default_handler : callable, default None
-    Handler to call if object cannot otherwise be converted to a
-    suitable format for JSON. Should receive a single argument which is
-    the object to convert and return a serialisable object.
-lines : boolean, default False
-    If 'orient' is 'records' write out line delimited json format. Will
-    throw ValueError if incorrect 'orient' since others are not list
-    like.

-    .. versionadded:: 0.19.0

-Returns
--------
-same type as input object with filtered info axis

-See Also
---------
-pd.read_json

-Examples
---------

->>> df = pd.DataFrame([['a', 'b'], ['c', 'd']],
-...                   index=['row 1', 'row 2'],
-...                   columns=['col 1', 'col 2'])
->>> df.to_json(orient='split')
-'{"columns":["col 1","col 2"],
-  "index":["row 1","row 2"],
-  "data":[["a","b"],["c","d"]]}'

-Encoding/decoding a Dataframe using ``'index'`` formatted JSON:

->>> df.to_json(orient='index')
-'{"row 1":{"col 1":"a","col 2":"b"},"row 2":{"col 1":"c","col 2":"d"}}'

-Encoding/decoding a Dataframe using ``'records'`` formatted JSON.
-Note that index labels are not preserved with this encoding.

->>> df.to_json(orient='records')
-'[{"col 1":"a","col 2":"b"},{"col 1":"c","col 2":"d"}]'

-Encoding with Table Schema

->>> df.to_json(orient='table')
-'{"schema": {"fields": [{"name": "index", "type": "string"},
-                        {"name": "col 1", "type": "string"},
-                        {"name": "col 2", "type": "string"}],
-             "primaryKey": "index",
-             "pandas_version": "0.20.0"},
-  "data": [{"index": "row 1", "col 1": "a", "col 2": "b"},
-           {"index": "row 2", "col 1": "c", "col 2": "d"}]}'
- -
to_msgpack(self, path_or_buf=None, encoding='utf-8', **kwargs)
msgpack (serialize) object to input file path

-THIS IS AN EXPERIMENTAL LIBRARY and the storage format
-may not be stable until a future release.

-Parameters
-----------
-path : string File path, buffer-like, or None
-    if None, return generated string
-append : boolean whether to append to an existing msgpack
-    (default is False)
-compress : type of compressor (zlib or blosc), default to None (no
-    compression)
- -
to_pickle(self, path, compression='infer')
Pickle (serialize) object to input file path.

-Parameters
-----------
-path : string
-    File path
-compression : {'infer', 'gzip', 'bz2', 'xz', None}, default 'infer'
-    a string representing the compression to use in the output file

-    .. versionadded:: 0.20.0
- -
to_sql(self, name, con, flavor=None, schema=None, if_exists='fail', index=True, index_label=None, chunksize=None, dtype=None)
Write records stored in a DataFrame to a SQL database.

-Parameters
-----------
-name : string
-    Name of SQL table
-con : SQLAlchemy engine or DBAPI2 connection (legacy mode)
-    Using SQLAlchemy makes it possible to use any DB supported by that
-    library. If a DBAPI2 object, only sqlite3 is supported.
-flavor : 'sqlite', default None
-    DEPRECATED: this parameter will be removed in a future version,
-    as 'sqlite' is the only supported option if SQLAlchemy is not
-    installed.
-schema : string, default None
-    Specify the schema (if database flavor supports this). If None, use
-    default schema.
-if_exists : {'fail', 'replace', 'append'}, default 'fail'
-    - fail: If table exists, do nothing.
-    - replace: If table exists, drop it, recreate it, and insert data.
-    - append: If table exists, insert data. Create if does not exist.
-index : boolean, default True
-    Write DataFrame index as a column.
-index_label : string or sequence, default None
-    Column label for index column(s). If None is given (default) and
-    `index` is True, then the index names are used.
-    A sequence should be given if the DataFrame uses MultiIndex.
-chunksize : int, default None
-    If not None, then rows will be written in batches of this size at a
-    time.  If None, all rows will be written at once.
-dtype : dict of column name to SQL type, default None
-    Optional specifying the datatype for columns. The SQL type should
-    be a SQLAlchemy type, or a string for sqlite3 fallback connection.
- -
to_xarray(self)
Return an xarray object from the pandas object.

-Returns
--------
-a DataArray for a Series
-a Dataset for a DataFrame
-a DataArray for higher dims

-Examples
---------
->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)})
->>> df
-   A    B    C
-0  1  foo  4.0
-1  1  bar  5.0
-2  2  foo  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (index: 3)
-Coordinates:
-  * index    (index) int64 0 1 2
-Data variables:
-    A        (index) int64 1 1 2
-    B        (index) object 'foo' 'bar' 'foo'
-    C        (index) float64 4.0 5.0 6.0

->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)}
-                     ).set_index(['B','A'])
->>> df
-         C
-B   A
-foo 1  4.0
-bar 1  5.0
-foo 2  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (A: 2, B: 2)
-Coordinates:
-  * B        (B) object 'bar' 'foo'
-  * A        (A) int64 1 2
-Data variables:
-    C        (B, A) float64 5.0 nan 4.0 6.0

->>> p = pd.Panel(np.arange(24).reshape(4,3,2),
-                 items=list('ABCD'),
-                 major_axis=pd.date_range('20130101', periods=3),
-                 minor_axis=['first', 'second'])
->>> p
-<class 'pandas.core.panel.Panel'>
-Dimensions: 4 (items) x 3 (major_axis) x 2 (minor_axis)
-Items axis: A to D
-Major_axis axis: 2013-01-01 00:00:00 to 2013-01-03 00:00:00
-Minor_axis axis: first to second

->>> p.to_xarray()
-<xarray.DataArray (items: 4, major_axis: 3, minor_axis: 2)>
-array([[[ 0,  1],
-        [ 2,  3],
-        [ 4,  5]],
-       [[ 6,  7],
-        [ 8,  9],
-        [10, 11]],
-       [[12, 13],
-        [14, 15],
-        [16, 17]],
-       [[18, 19],
-        [20, 21],
-        [22, 23]]])
-Coordinates:
-  * items       (items) object 'A' 'B' 'C' 'D'
-  * major_axis  (major_axis) datetime64[ns] 2013-01-01 2013-01-02 2013-01-03  # noqa
-  * minor_axis  (minor_axis) object 'first' 'second'

-Notes
------
-See the `xarray docs <http://xarray.pydata.org/en/stable/>`__
- -
truncate(self, before=None, after=None, axis=None, copy=True)
Truncates a sorted NDFrame before and/or after some particular
-index value. If the axis contains only datetime values, before/after
-parameters are converted to datetime values.

-Parameters
-----------
-before : date
-    Truncate before index value
-after : date
-    Truncate after index value
-axis : the truncation axis, defaults to the stat axis
-copy : boolean, default is True,
-    return a copy of the truncated section

-Returns
--------
-truncated : type of caller
- -
tshift(self, periods=1, freq=None, axis=0)
Shift the time index, using the index's frequency if available.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, default None
-    Increment to use from the tseries module or time rule (e.g. 'EOM')
-axis : int or basestring
-    Corresponds to the axis that contains the Index

-Notes
------
-If freq is not specified then tries to use the freq or inferred_freq
-attributes of the index. If neither of those attributes exist, a
-ValueError is thrown

-Returns
--------
-shifted : NDFrame
- -
tz_convert(self, tz, axis=0, level=None, copy=True)
Convert tz-aware axis to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to convert
-level : int, str, default None
-    If axis ia a MultiIndex, convert a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data

-Returns
--------

-Raises
-------
-TypeError
-    If the axis is tz-naive.
- -
tz_localize(self, tz, axis=0, level=None, copy=True, ambiguous='raise')
Localize tz-naive TimeSeries to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to localize
-level : int, str, default None
-    If axis ia a MultiIndex, localize a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data
-ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise'
-    - 'infer' will attempt to infer fall dst-transition hours based on
-      order
-    - bool-ndarray where True signifies a DST time, False designates
-      a non-DST time (note that this flag is only applicable for
-      ambiguous times)
-    - 'NaT' will return NaT where there are ambiguous times
-    - 'raise' will raise an AmbiguousTimeError if there are ambiguous
-      times
-infer_dst : boolean, default False (DEPRECATED)
-    Attempt to infer fall dst-transition hours based on order

-Returns
--------

-Raises
-------
-TypeError
-    If the TimeSeries is tz-aware and tz is not None.
- -
where(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is True and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The where method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``True`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``where`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.mask`
- -
xs(self, key, axis=0, level=None, drop_level=True)
Returns a cross-section (row(s) or column(s)) from the
-Series/DataFrame. Defaults to cross-section on the rows (axis=0).

-Parameters
-----------
-key : object
-    Some label contained in the index, or partially in a MultiIndex
-axis : int, default 0
-    Axis to retrieve cross-section on
-level : object, defaults to first n levels (n=1 or len(key))
-    In case of a key partially contained in a MultiIndex, indicate
-    which levels are used. Levels can be referred by label or position.
-drop_level : boolean, default True
-    If False, returns object with same levels as self.

-Examples
---------
->>> df
-   A  B  C
-a  4  5  2
-b  4  0  9
-c  9  7  3
->>> df.xs('a')
-A    4
-B    5
-C    2
-Name: a
->>> df.xs('C', axis=1)
-a    2
-b    9
-c    3
-Name: C

->>> df
-                    A  B  C  D
-first second third
-bar   one    1      4  1  8  9
-      two    1      7  5  5  0
-baz   one    1      6  6  8  0
-      three  2      5  3  5  3
->>> df.xs(('baz', 'three'))
-       A  B  C  D
-third
-2      5  3  5  3
->>> df.xs('one', level=1)
-             A  B  C  D
-first third
-bar   1      4  1  8  9
-baz   1      6  6  8  0
->>> df.xs(('baz', 2), level=[0, 'third'])
-        A  B  C  D
-second
-three   5  3  5  3

-Returns
--------
-xs : Series or DataFrame

-Notes
------
-xs is only for getting, not setting values.

-MultiIndex Slicers is a generic way to get/set values on any level or
-levels.  It is a superset of xs functionality, see
-:ref:`MultiIndex Slicers <advanced.mi_slicers>`
- -
-Data descriptors inherited from pandas.core.generic.NDFrame:
-
at
-
Fast label-based scalar accessor

-Similarly to ``loc``, ``at`` provides **label** based scalar lookups.
-You can also set using these indexers.
-
-
blocks
-
Internal property, property synonym for as_blocks()
-
-
iat
-
Fast integer location scalar accessor.

-Similarly to ``iloc``, ``iat`` provides **integer** based lookups.
-You can also set using these indexers.
-
-
iloc
-
Purely integer-location based indexing for selection by position.

-``.iloc[]`` is primarily integer position based (from ``0`` to
-``length-1`` of the axis), but may also be used with a boolean
-array.

-Allowed inputs are:

-- An integer, e.g. ``5``.
-- A list or array of integers, e.g. ``[4, 3, 0]``.
-- A slice object with ints, e.g. ``1:7``.
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.iloc`` will raise ``IndexError`` if a requested indexer is
-out-of-bounds, except *slice* indexers which allow out-of-bounds
-indexing (this conforms with python/numpy *slice* semantics).

-See more at :ref:`Selection by Position <indexing.integer>`
-
-
ix
-
A primarily label-location based indexer, with integer position
-fallback.

-``.ix[]`` supports mixed integer and label based access. It is
-primarily label based, but will fall back to integer positional
-access unless the corresponding axis is of integer type.

-``.ix`` is the most general indexer and will support any of the
-inputs in ``.loc`` and ``.iloc``. ``.ix`` also supports floating
-point label schemes. ``.ix`` is exceptionally useful when dealing
-with mixed positional and label based hierachical indexes.

-However, when an axis is integer based, ONLY label based access
-and not positional access is supported. Thus, in such cases, it's
-usually better to be explicit and use ``.iloc`` or ``.loc``.

-See more at :ref:`Advanced Indexing <advanced>`.
-
-
loc
-
Purely label-location based indexer for selection by label.

-``.loc[]`` is primarily label based, but may also be used with a
-boolean array.

-Allowed inputs are:

-- A single label, e.g. ``5`` or ``'a'``, (note that ``5`` is
-  interpreted as a *label* of the index, and **never** as an
-  integer position along the index).
-- A list or array of labels, e.g. ``['a', 'b', 'c']``.
-- A slice object with labels, e.g. ``'a':'f'`` (note that contrary
-  to usual python slices, **both** the start and the stop are included!).
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.loc`` will raise a ``KeyError`` when the items are not found.

-See more at :ref:`Selection by Label <indexing.label>`
-
-
-Data and other attributes inherited from pandas.core.generic.NDFrame:
-
is_copy = None
- -
-Methods inherited from pandas.core.base.PandasObject:
-
__dir__(self)
Provide method name lookup and completion
-Only provide 'public' methods
- -
__sizeof__(self)
Generates the total memory usage for a object that returns
-either a value or Series of values
- -
-Methods inherited from pandas.core.base.StringMixin:
-
__bytes__(self)
Return a string representation for a particular object.

-Invoked by bytes(obj) in py3 only.
-Yields a bytestring in both py2/py3.
- -
__repr__(self)
Return a string representation for a particular object.

-Yields Bytestring in Py2, Unicode String in py3.
- -
__str__(self)
Return a string representation for a particular Object

-Invoked by str(df) in both py2/py3.
-Yields Bytestring in Py2, Unicode String in py3.
- -

- - - - - - - -
 
-class FigureState(builtins.object)
   Encapsulates information about the current figure.
 
 Methods defined here:
-
__init__(self)
Initialize self.  See help(type(self)) for accurate signature.
- -
clear_lines(self)
- -
get_line(self, style, kwargs)
Gets the line object for a given style tuple.

-style: Matplotlib style string
-kwargs: dictionary of style options

-returns: maplotlib.lines.Lines2D
- -
make_line(self, style, kwargs)
- -
-Data descriptors defined here:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-

- - - - - - - -
 
-class MyDataFrame(pandas.core.frame.DataFrame)
   MyTimeFrame is a modified version of a Pandas DataFrame,
-with a few changes to make it more suited to our purpose.

-In particular, DataFrame provides two special variables called
-`dt` and `T` that cause problems if we try to use those names
-as state variables.

-So I added new definitions that override the special variables
-and make these names useable as row labels.
 
 
Method resolution order:
-
MyDataFrame
-
pandas.core.frame.DataFrame
-
pandas.core.generic.NDFrame
-
pandas.core.base.PandasObject
-
pandas.core.base.StringMixin
-
pandas.core.base.SelectionMixin
-
builtins.object
-
-
-Methods defined here:
-
__init__(self, *args, **kwargs)
Initialize self.  See help(type(self)) for accurate signature.
- -
-Data descriptors defined here:
-
T
-
Intercept the Series accessor object so we can use `T`
-as a row label and access it using dot notation.

-https://pandas.pydata.org/pandas-docs/stable/generated/
-pandas.DataFrame.T.html#pandas.DataFrame.T
-
-
dt
-
Intercept the Series accessor object so we can use `dt`
-as a row label and access it using dot notation.

-https://pandas.pydata.org/pandas-docs/stable/generated/
-pandas.DataFrame.dt.html
-
-
-Methods inherited from pandas.core.frame.DataFrame:
-
__add__(self, other, axis=None, level=None, fill_value=None)
Binary operator __add__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__and__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __and__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__div__ = __truediv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __truediv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__eq__(self, other)
Wrapper for comparison method __eq__
- -
__floordiv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __floordiv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__ge__(self, other)
Wrapper for comparison method __ge__
- -
__getitem__(self, key)
- -
__gt__(self, other)
Wrapper for comparison method __gt__
- -
__iadd__ = f(self, other)
- -
__imul__ = f(self, other)
- -
__ipow__ = f(self, other)
- -
__isub__ = f(self, other)
- -
__itruediv__ = f(self, other)
- -
__le__(self, other)
Wrapper for comparison method __le__
- -
__len__(self)
Returns length of info axis, but here we use the index
- -
__lt__(self, other)
Wrapper for comparison method __lt__
- -
__mod__(self, other, axis=None, level=None, fill_value=None)
Binary operator __mod__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__mul__(self, other, axis=None, level=None, fill_value=None)
Binary operator __mul__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__ne__(self, other)
Wrapper for comparison method __ne__
- -
__or__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __or__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__pow__(self, other, axis=None, level=None, fill_value=None)
Binary operator __pow__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__radd__(self, other, axis=None, level=None, fill_value=None)
Binary operator __radd__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rand__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __rand__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rdiv__ = __rtruediv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rtruediv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rfloordiv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rfloordiv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rmod__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rmod__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rmul__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rmul__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__ror__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __ror__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rpow__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rpow__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rsub__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rsub__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rtruediv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rtruediv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rxor__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __rxor__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__setitem__(self, key, value)
- -
__sub__(self, other, axis=None, level=None, fill_value=None)
Binary operator __sub__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__truediv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __truediv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__unicode__(self)
Return a string representation for a particular DataFrame

-Invoked by unicode(df) in py2 only. Yields a Unicode String in both
-py2/py3.
- -
__xor__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __xor__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
add(self, other, axis='columns', level=None, fill_value=None)
Addition of dataframe and other, element-wise (binary operator `add`).

-Equivalent to ``dataframe + other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.radd
- -
agg = aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a DataFrame or when passed to DataFrame.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : DataFrame

-Examples
---------

->>> df = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
-...                   index=pd.date_range('1/1/2000', periods=10))
->>> df.iloc[3:7] = np.nan

-Aggregate these functions across all columns

->>> df.agg(['sum', 'min'])
-            A         B         C
-sum -0.182253 -0.614014 -2.909534
-min -1.916563 -1.460076 -1.568297

-Different aggregations per column

->>> df.agg({'A' : ['sum', 'min'], 'B' : ['min', 'max']})
-            A         B
-max       NaN  1.514318
-min -1.916563 -1.460076
-sum -0.182253       NaN

-See also
---------
-pandas.DataFrame.apply
-pandas.DataFrame.transform
-pandas.DataFrame.groupby.aggregate
-pandas.DataFrame.resample.aggregate
-pandas.DataFrame.rolling.aggregate
- -
aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a DataFrame or when passed to DataFrame.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : DataFrame

-Examples
---------

->>> df = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
-...                   index=pd.date_range('1/1/2000', periods=10))
->>> df.iloc[3:7] = np.nan

-Aggregate these functions across all columns

->>> df.agg(['sum', 'min'])
-            A         B         C
-sum -0.182253 -0.614014 -2.909534
-min -1.916563 -1.460076 -1.568297

-Different aggregations per column

->>> df.agg({'A' : ['sum', 'min'], 'B' : ['min', 'max']})
-            A         B
-max       NaN  1.514318
-min -1.916563 -1.460076
-sum -0.182253       NaN

-See also
---------
-pandas.DataFrame.apply
-pandas.DataFrame.transform
-pandas.DataFrame.groupby.aggregate
-pandas.DataFrame.resample.aggregate
-pandas.DataFrame.rolling.aggregate
- -
align(self, other, join='outer', axis=None, level=None, copy=True, fill_value=None, method=None, limit=None, fill_axis=0, broadcast_axis=None)
Align two object on their axes with the
-specified join method for each axis Index

-Parameters
-----------
-other : DataFrame or Series
-join : {'outer', 'inner', 'left', 'right'}, default 'outer'
-axis : allowed axis of the other object, default None
-    Align on index (0), columns (1), or both (None)
-level : int or level name, default None
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-copy : boolean, default True
-    Always returns new objects. If copy=False and no reindexing is
-    required then original objects are returned.
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-method : str, default None
-limit : int, default None
-fill_axis : {0 or 'index', 1 or 'columns'}, default 0
-    Filling axis, method and limit
-broadcast_axis : {0 or 'index', 1 or 'columns'}, default None
-    Broadcast values along this axis, if aligning two objects of
-    different dimensions

-    .. versionadded:: 0.17.0

-Returns
--------
-(left, right) : (DataFrame, type of other)
-    Aligned objects
- -
all(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether all elements are True over requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-all : Series or DataFrame (if level specified)
- -
any(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether any element is True over requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-any : Series or DataFrame (if level specified)
- -
append(self, other, ignore_index=False, verify_integrity=False)
Append rows of `other` to the end of this frame, returning a new
-object. Columns not in this frame are added as new columns.

-Parameters
-----------
-other : DataFrame or Series/dict-like object, or list of these
-    The data to append.
-ignore_index : boolean, default False
-    If True, do not use the index labels.
-verify_integrity : boolean, default False
-    If True, raise ValueError on creating index with duplicates.

-Returns
--------
-appended : DataFrame

-Notes
------
-If a list of dict/series is passed and the keys are all contained in
-the DataFrame's index, the order of the columns in the resulting
-DataFrame will be unchanged.

-See also
---------
-pandas.concat : General function to concatenate DataFrameSeries
-    or Panel objects

-Examples
---------

->>> df = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))
->>> df
-   A  B
-0  1  2
-1  3  4
->>> df2 = pd.DataFrame([[5, 6], [7, 8]], columns=list('AB'))
->>> df.append(df2)
-   A  B
-0  1  2
-1  3  4
-0  5  6
-1  7  8

-With `ignore_index` set to True:

->>> df.append(df2, ignore_index=True)
-   A  B
-0  1  2
-1  3  4
-2  5  6
-3  7  8
- -
apply(self, func, axis=0, broadcast=False, raw=False, reduce=None, args=(), **kwds)
Applies function along input axis of DataFrame.

-Objects passed to functions are Series objects having index
-either the DataFrame's index (axis=0) or the columns (axis=1).
-Return type depends on whether passed function aggregates, or the
-reduce argument if the DataFrame is empty.

-Parameters
-----------
-func : function
-    Function to apply to each column/row
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    * 0 or 'index': apply function to each column
-    * 1 or 'columns': apply function to each row
-broadcast : boolean, default False
-    For aggregation functions, return object of same size with values
-    propagated
-raw : boolean, default False
-    If False, convert each row or column into a Series. If raw=True the
-    passed function will receive ndarray objects instead. If you are
-    just applying a NumPy reduction function this will achieve much
-    better performance
-reduce : boolean or None, default None
-    Try to apply reduction procedures. If the DataFrame is empty,
-    apply will use reduce to determine whether the result should be a
-    Series or a DataFrame. If reduce is None (the default), apply's
-    return value will be guessed by calling func an empty Series (note:
-    while guessing, exceptions raised by func will be ignored). If
-    reduce is True a Series will always be returned, and if False a
-    DataFrame will always be returned.
-args : tuple
-    Positional arguments to pass to function in addition to the
-    array/series
-Additional keyword arguments will be passed as keywords to the function

-Notes
------
-In the current implementation apply calls func twice on the
-first column/row to decide whether it can take a fast or slow
-code path. This can lead to unexpected behavior if func has
-side-effects, as they will take effect twice for the first
-column/row.

-Examples
---------
->>> df.apply(numpy.sqrt) # returns DataFrame
->>> df.apply(numpy.sum, axis=0) # equiv to df.sum(0)
->>> df.apply(numpy.sum, axis=1) # equiv to df.sum(1)

-See also
---------
-DataFrame.applymap: For elementwise operations
-DataFrame.aggregate: only perform aggregating type operations
-DataFrame.transform: only perform transformating type operations

-Returns
--------
-applied : Series or DataFrame
- -
applymap(self, func)
Apply a function to a DataFrame that is intended to operate
-elementwise, i.e. like doing map(func, series) for each series in the
-DataFrame

-Parameters
-----------
-func : function
-    Python function, returns a single value from a single value

-Examples
---------

->>> df = pd.DataFrame(np.random.randn(3, 3))
->>> df
-    0         1          2
-0  -0.029638  1.081563   1.280300
-1   0.647747  0.831136  -1.549481
-2   0.513416 -0.884417   0.195343
->>> df = df.applymap(lambda x: '%.2f' % x)
->>> df
-    0         1          2
-0  -0.03      1.08       1.28
-1   0.65      0.83      -1.55
-2   0.51     -0.88       0.20

-Returns
--------
-applied : DataFrame

-See also
---------
-DataFrame.apply : For operations on rows/columns
- -
assign(self, **kwargs)
Assign new columns to a DataFrame, returning a new object
-(a copy) with all the original columns in addition to the new ones.

-.. versionadded:: 0.16.0

-Parameters
-----------
-kwargs : keyword, value pairs
-    keywords are the column names. If the values are
-    callable, they are computed on the DataFrame and
-    assigned to the new columns. The callable must not
-    change input DataFrame (though pandas doesn't check it).
-    If the values are not callable, (e.g. a Series, scalar, or array),
-    they are simply assigned.

-Returns
--------
-df : DataFrame
-    A new DataFrame with the new columns in addition to
-    all the existing columns.

-Notes
------
-Since ``kwargs`` is a dictionary, the order of your
-arguments may not be preserved. To make things predicatable,
-the columns are inserted in alphabetical order, at the end of
-your DataFrame. Assigning multiple columns within the same
-``assign`` is possible, but you cannot reference other columns
-created within the same ``assign`` call.

-Examples
---------
->>> df = DataFrame({'A': range(1, 11), 'B': np.random.randn(10)})

-Where the value is a callable, evaluated on `df`:

->>> df.assign(ln_A = lambda x: np.log(x.A))
-    A         B      ln_A
-0   1  0.426905  0.000000
-1   2 -0.780949  0.693147
-2   3 -0.418711  1.098612
-3   4 -0.269708  1.386294
-4   5 -0.274002  1.609438
-5   6 -0.500792  1.791759
-6   7  1.649697  1.945910
-7   8 -1.495604  2.079442
-8   9  0.549296  2.197225
-9  10 -0.758542  2.302585

-Where the value already exists and is inserted:

->>> newcol = np.log(df['A'])
->>> df.assign(ln_A=newcol)
-    A         B      ln_A
-0   1  0.426905  0.000000
-1   2 -0.780949  0.693147
-2   3 -0.418711  1.098612
-3   4 -0.269708  1.386294
-4   5 -0.274002  1.609438
-5   6 -0.500792  1.791759
-6   7  1.649697  1.945910
-7   8 -1.495604  2.079442
-8   9  0.549296  2.197225
-9  10 -0.758542  2.302585
- -
boxplot(self, column=None, by=None, ax=None, fontsize=None, rot=0, grid=True, figsize=None, layout=None, return_type=None, **kwds)
Make a box plot from DataFrame column optionally grouped by some columns or
-other inputs

-Parameters
-----------
-data : the pandas object holding the data
-column : column name or list of names, or vector
-    Can be any valid input to groupby
-by : string or sequence
-    Column in the DataFrame to group by
-ax : Matplotlib axes object, optional
-fontsize : int or string
-rot : label rotation angle
-figsize : A tuple (width, height) in inches
-grid : Setting this to True will show the grid
-layout : tuple (optional)
-    (rows, columns) for the layout of the plot
-return_type : {None, 'axes', 'dict', 'both'}, default None
-    The kind of object to return. The default is ``axes``
-    'axes' returns the matplotlib axes the boxplot is drawn on;
-    'dict' returns a dictionary  whose values are the matplotlib
-    Lines of the boxplot;
-    'both' returns a namedtuple with the axes and dict.

-    When grouping with ``by``, a Series mapping columns to ``return_type``
-    is returned, unless ``return_type`` is None, in which case a NumPy
-    array of axes is returned with the same shape as ``layout``.
-    See the prose documentation for more.

-kwds : other plotting keyword arguments to be passed to matplotlib boxplot
-       function

-Returns
--------
-lines : dict
-ax : matplotlib Axes
-(ax, lines): namedtuple

-Notes
------
-Use ``return_type='dict'`` when you want to tweak the appearance
-of the lines after plotting. In this case a dict containing the Lines
-making up the boxes, caps, fliers, medians, and whiskers is returned.
- -
combine(self, other, func, fill_value=None, overwrite=True)
Add two DataFrame objects and do not propagate NaN values, so if for a
-(column, time) one frame is missing a value, it will default to the
-other frame's value (which might be NaN as well)

-Parameters
-----------
-other : DataFrame
-func : function
-fill_value : scalar value
-overwrite : boolean, default True
-    If True then overwrite values for common keys in the calling frame

-Returns
--------
-result : DataFrame
- -
combine_first(self, other)
Combine two DataFrame objects and default to non-null values in frame
-calling the method. Result index columns will be the union of the
-respective indexes and columns

-Parameters
-----------
-other : DataFrame

-Examples
---------
-a's values prioritized, use values from b to fill holes:

->>> a.combine_first(b)


-Returns
--------
-combined : DataFrame
- -
compound(self, axis=None, skipna=None, level=None)
Return the compound percentage of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-compounded : Series or DataFrame (if level specified)
- -
corr(self, method='pearson', min_periods=1)
Compute pairwise correlation of columns, excluding NA/null values

-Parameters
-----------
-method : {'pearson', 'kendall', 'spearman'}
-    * pearson : standard correlation coefficient
-    * kendall : Kendall Tau correlation coefficient
-    * spearman : Spearman rank correlation
-min_periods : int, optional
-    Minimum number of observations required per pair of columns
-    to have a valid result. Currently only available for pearson
-    and spearman correlation

-Returns
--------
-y : DataFrame
- -
corrwith(self, other, axis=0, drop=False)
Compute pairwise correlation between rows or columns of two DataFrame
-objects.

-Parameters
-----------
-other : DataFrame
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    0 or 'index' to compute column-wise, 1 or 'columns' for row-wise
-drop : boolean, default False
-    Drop missing indices from result, default returns union of all

-Returns
--------
-correls : Series
- -
count(self, axis=0, level=None, numeric_only=False)
Return Series with number of non-NA/null observations over requested
-axis. Works with non-floating point data as well (detects NaN and None)

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    0 or 'index' for row-wise, 1 or 'columns' for column-wise
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a DataFrame
-numeric_only : boolean, default False
-    Include only float, int, boolean data

-Returns
--------
-count : Series (or DataFrame if level specified)
- -
cov(self, min_periods=None)
Compute pairwise covariance of columns, excluding NA/null values

-Parameters
-----------
-min_periods : int, optional
-    Minimum number of observations required per pair of columns
-    to have a valid result.

-Returns
--------
-y : DataFrame

-Notes
------
-`y` contains the covariance matrix of the DataFrame's time series.
-The covariance is normalized by N-1 (unbiased estimator).
- -
cummax(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative max over requested axis.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummax : Series



-See also
---------
-pandas.core.window.Expanding.max : Similar functionality
-    but ignores ``NaN`` values.
- -
cummin(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative minimum over requested axis.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummin : Series



-See also
---------
-pandas.core.window.Expanding.min : Similar functionality
-    but ignores ``NaN`` values.
- -
cumprod(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative product over requested axis.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumprod : Series



-See also
---------
-pandas.core.window.Expanding.prod : Similar functionality
-    but ignores ``NaN`` values.
- -
cumsum(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative sum over requested axis.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumsum : Series



-See also
---------
-pandas.core.window.Expanding.sum : Similar functionality
-    but ignores ``NaN`` values.
- -
diff(self, periods=1, axis=0)
1st discrete difference of object

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming difference
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    Take difference over rows (0) or columns (1).

-    .. versionadded: 0.16.1

-Returns
--------
-diffed : DataFrame
- -
div = truediv(self, other, axis='columns', level=None, fill_value=None)
Floating division of dataframe and other, element-wise (binary operator `truediv`).

-Equivalent to ``dataframe / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rtruediv
- -
divide = truediv(self, other, axis='columns', level=None, fill_value=None)
Floating division of dataframe and other, element-wise (binary operator `truediv`).

-Equivalent to ``dataframe / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rtruediv
- -
dot(self, other)
Matrix multiplication with DataFrame or Series objects

-Parameters
-----------
-other : DataFrame or Series

-Returns
--------
-dot_product : DataFrame or Series
- -
drop_duplicates(self, subset=None, keep='first', inplace=False)
Return DataFrame with duplicate rows removed, optionally only
-considering certain columns

-Parameters
-----------
-subset : column label or sequence of labels, optional
-    Only consider certain columns for identifying duplicates, by
-    default use all of the columns
-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Drop duplicates except for the first occurrence.
-    - ``last`` : Drop duplicates except for the last occurrence.
-    - False : Drop all duplicates.
-inplace : boolean, default False
-    Whether to drop duplicates in place or to return a copy

-Returns
--------
-deduplicated : DataFrame
- -
dropna(self, axis=0, how='any', thresh=None, subset=None, inplace=False)
Return object with labels on given axis omitted where alternately any
-or all of the data are missing

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, or tuple/list thereof
-    Pass tuple or list to drop on multiple axes
-how : {'any', 'all'}
-    * any : if any NA values are present, drop that label
-    * all : if all values are NA, drop that label
-thresh : int, default None
-    int value : require that many non-NA values
-subset : array-like
-    Labels along other axis to consider, e.g. if you are dropping rows
-    these would be a list of columns to include
-inplace : boolean, default False
-    If True, do operation inplace and return None.

-Returns
--------
-dropped : DataFrame

-Examples
---------
->>> df = pd.DataFrame([[np.nan, 2, np.nan, 0], [3, 4, np.nan, 1],
-...                    [np.nan, np.nan, np.nan, 5]],
-...                   columns=list('ABCD'))
->>> df
-     A    B   C  D
-0  NaN  2.0 NaN  0
-1  3.0  4.0 NaN  1
-2  NaN  NaN NaN  5

-Drop the columns where all elements are nan:

->>> df.dropna(axis=1, how='all')
-     A    B  D
-0  NaN  2.0  0
-1  3.0  4.0  1
-2  NaN  NaN  5

-Drop the columns where any of the elements is nan

->>> df.dropna(axis=1, how='any')
-   D
-0  0
-1  1
-2  5

-Drop the rows where all of the elements are nan
-(there is no row to drop, so df stays the same):

->>> df.dropna(axis=0, how='all')
-     A    B   C  D
-0  NaN  2.0 NaN  0
-1  3.0  4.0 NaN  1
-2  NaN  NaN NaN  5

-Keep only the rows with at least 2 non-na values:

->>> df.dropna(thresh=2)
-     A    B   C  D
-0  NaN  2.0 NaN  0
-1  3.0  4.0 NaN  1
- -
duplicated(self, subset=None, keep='first')
Return boolean Series denoting duplicate rows, optionally only
-considering certain columns

-Parameters
-----------
-subset : column label or sequence of labels, optional
-    Only consider certain columns for identifying duplicates, by
-    default use all of the columns
-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Mark duplicates as ``True`` except for the
-      first occurrence.
-    - ``last`` : Mark duplicates as ``True`` except for the
-      last occurrence.
-    - False : Mark all duplicates as ``True``.

-Returns
--------
-duplicated : Series
- -
eq(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods eq
- -
eval(self, expr, inplace=None, **kwargs)
Evaluate an expression in the context of the calling DataFrame
-instance.

-Parameters
-----------
-expr : string
-    The expression string to evaluate.
-inplace : bool
-    If the expression contains an assignment, whether to return a new
-    DataFrame or mutate the existing.

-    WARNING: inplace=None currently falls back to to True, but
-    in a future version, will default to False.  Use inplace=True
-    explicitly rather than relying on the default.

-    .. versionadded:: 0.18.0

-kwargs : dict
-    See the documentation for :func:`~pandas.eval` for complete details
-    on the keyword arguments accepted by
-    :meth:`~pandas.DataFrame.query`.

-Returns
--------
-ret : ndarray, scalar, or pandas object

-See Also
---------
-pandas.DataFrame.query
-pandas.DataFrame.assign
-pandas.eval

-Notes
------
-For more details see the API documentation for :func:`~pandas.eval`.
-For detailed examples see :ref:`enhancing performance with eval
-<enhancingperf.eval>`.

-Examples
---------
->>> from numpy.random import randn
->>> from pandas import DataFrame
->>> df = DataFrame(randn(10, 2), columns=list('ab'))
->>> df.eval('a + b')
->>> df.eval('c = a + b')
- -
ewm(self, com=None, span=None, halflife=None, alpha=None, min_periods=0, freq=None, adjust=True, ignore_na=False, axis=0)
Provides exponential weighted functions

-.. versionadded:: 0.18.0

-Parameters
-----------
-com : float, optional
-    Specify decay in terms of center of mass,
-    :math:`\alpha = 1 / (1 + com),\text{ for } com \geq 0`
-span : float, optional
-    Specify decay in terms of span,
-    :math:`\alpha = 2 / (span + 1),\text{ for } span \geq 1`
-halflife : float, optional
-    Specify decay in terms of half-life,
-    :math:`\alpha = 1 - exp(log(0.5) / halflife),\text{ for } halflife > 0`
-alpha : float, optional
-    Specify smoothing factor :math:`\alpha` directly,
-    :math:`0 < \alpha \leq 1`

-    .. versionadded:: 0.18.0

-min_periods : int, default 0
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : None or string alias / date offset object, default=None (DEPRECATED)
-    Frequency to conform to before computing statistic
-adjust : boolean, default True
-    Divide by decaying adjustment factor in beginning periods to account
-    for imbalance in relative weightings (viewing EWMA as a moving average)
-ignore_na : boolean, default False
-    Ignore missing values when calculating weights;
-    specify True to reproduce pre-0.15.0 behavior

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.ewm(com=0.5).mean()
-          B
-0  0.000000
-1  0.750000
-2  1.615385
-3  1.615385
-4  3.670213

-Notes
------
-Exactly one of center of mass, span, half-life, and alpha must be provided.
-Allowed values and relationship between the parameters are specified in the
-parameter descriptions above; see the link at the end of this section for
-a detailed explanation.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-When adjust is True (default), weighted averages are calculated using
-weights (1-alpha)**(n-1), (1-alpha)**(n-2), ..., 1-alpha, 1.

-When adjust is False, weighted averages are calculated recursively as:
-   weighted_average[0] = arg[0];
-   weighted_average[i] = (1-alpha)*weighted_average[i-1] + alpha*arg[i].

-When ignore_na is False (default), weights are based on absolute positions.
-For example, the weights of x and y used in calculating the final weighted
-average of [x, None, y] are (1-alpha)**2 and 1 (if adjust is True), and
-(1-alpha)**2 and alpha (if adjust is False).

-When ignore_na is True (reproducing pre-0.15.0 behavior), weights are based
-on relative positions. For example, the weights of x and y used in
-calculating the final weighted average of [x, None, y] are 1-alpha and 1
-(if adjust is True), and 1-alpha and alpha (if adjust is False).

-More details can be found at
-http://pandas.pydata.org/pandas-docs/stable/computation.html#exponentially-weighted-windows
- -
expanding(self, min_periods=1, freq=None, center=False, axis=0)
Provides expanding transformations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-axis : int or string, default 0

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.expanding(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  3.0
-4  7.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).
- -
fillna(self, value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)
Fill NA/NaN values using the specified method

-Parameters
-----------
-value : scalar, dict, Series, or DataFrame
-    Value to use to fill holes (e.g. 0), alternately a
-    dict/Series/DataFrame of values specifying which value to use for
-    each index (for a Series) or column (for a DataFrame). (values not
-    in the dict/Series/DataFrame will not be filled). This value cannot
-    be a list.
-method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
-    Method to use for filling holes in reindexed Series
-    pad / ffill: propagate last valid observation forward to next valid
-    backfill / bfill: use NEXT valid observation to fill gap
-axis : {0 or 'index', 1 or 'columns'}
-inplace : boolean, default False
-    If True, fill in place. Note: this will modify any
-    other views on this object, (e.g. a no-copy slice for a column in a
-    DataFrame).
-limit : int, default None
-    If method is specified, this is the maximum number of consecutive
-    NaN values to forward/backward fill. In other words, if there is
-    a gap with more than this number of consecutive NaNs, it will only
-    be partially filled. If method is not specified, this is the
-    maximum number of entries along the entire axis where NaNs will be
-    filled. Must be greater than 0 if not None.
-downcast : dict, default is None
-    a dict of item->dtype of what to downcast if possible,
-    or the string 'infer' which will try to downcast to an appropriate
-    equal type (e.g. float64 to int64 if possible)

-See Also
---------
-reindex, asfreq

-Returns
--------
-filled : DataFrame
- -
first_valid_index(self)
Return label for first non-NA/null value
- -
floordiv(self, other, axis='columns', level=None, fill_value=None)
Integer division of dataframe and other, element-wise (binary operator `floordiv`).

-Equivalent to ``dataframe // other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rfloordiv
- -
ge(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods ge
- -
get_value(self, index, col, takeable=False)
Quickly retrieve single value at passed column and index

-Parameters
-----------
-index : row label
-col : column label
-takeable : interpret the index/col as indexers, default False

-Returns
--------
-value : scalar value
- -
gt(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods gt
- -
hist = hist_frame(data, column=None, by=None, grid=True, xlabelsize=None, xrot=None, ylabelsize=None, yrot=None, ax=None, sharex=False, sharey=False, figsize=None, layout=None, bins=10, **kwds)
Draw histogram of the DataFrame's series using matplotlib / pylab.

-Parameters
-----------
-data : DataFrame
-column : string or sequence
-    If passed, will be used to limit data to a subset of columns
-by : object, optional
-    If passed, then used to form histograms for separate groups
-grid : boolean, default True
-    Whether to show axis grid lines
-xlabelsize : int, default None
-    If specified changes the x-axis label size
-xrot : float, default None
-    rotation of x axis labels
-ylabelsize : int, default None
-    If specified changes the y-axis label size
-yrot : float, default None
-    rotation of y axis labels
-ax : matplotlib axes object, default None
-sharex : boolean, default True if ax is None else False
-    In case subplots=True, share x axis and set some x axis labels to
-    invisible; defaults to True if ax is None otherwise False if an ax
-    is passed in; Be aware, that passing in both an ax and sharex=True
-    will alter all x axis labels for all subplots in a figure!
-sharey : boolean, default False
-    In case subplots=True, share y axis and set some y axis labels to
-    invisible
-figsize : tuple
-    The size of the figure to create in inches by default
-layout : tuple, optional
-    Tuple of (rows, columns) for the layout of the histograms
-bins : integer, default 10
-    Number of histogram bins to be used
-kwds : other plotting keyword arguments
-    To be passed to hist function
- -
idxmax(self, axis=0, skipna=True)
Return index of first occurrence of maximum over requested axis.
-NA/null values are excluded.

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    0 or 'index' for row-wise, 1 or 'columns' for column-wise
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be first index.

-Returns
--------
-idxmax : Series

-Notes
------
-This method is the DataFrame version of ``ndarray.argmax``.

-See Also
---------
-Series.idxmax
- -
idxmin(self, axis=0, skipna=True)
Return index of first occurrence of minimum over requested axis.
-NA/null values are excluded.

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    0 or 'index' for row-wise, 1 or 'columns' for column-wise
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-idxmin : Series

-Notes
------
-This method is the DataFrame version of ``ndarray.argmin``.

-See Also
---------
-Series.idxmin
- -
info(self, verbose=None, buf=None, max_cols=None, memory_usage=None, null_counts=None)
Concise summary of a DataFrame.

-Parameters
-----------
-verbose : {None, True, False}, optional
-    Whether to print the full summary.
-    None follows the `display.max_info_columns` setting.
-    True or False overrides the `display.max_info_columns` setting.
-buf : writable buffer, defaults to sys.stdout
-max_cols : int, default None
-    Determines whether full summary or short summary is printed.
-    None follows the `display.max_info_columns` setting.
-memory_usage : boolean/string, default None
-    Specifies whether total memory usage of the DataFrame
-    elements (including index) should be displayed. None follows
-    the `display.memory_usage` setting. True or False overrides
-    the `display.memory_usage` setting. A value of 'deep' is equivalent
-    of True, with deep introspection. Memory usage is shown in
-    human-readable units (base-2 representation).
-null_counts : boolean, default None
-    Whether to show the non-null counts

-    - If None, then only show if the frame is smaller than
-      max_info_rows and max_info_columns.
-    - If True, always show counts.
-    - If False, never show counts.
- -
insert(self, loc, column, value, allow_duplicates=False)
Insert column into DataFrame at specified location.

-If `allow_duplicates` is False, raises Exception if column
-is already contained in the DataFrame.

-Parameters
-----------
-loc : int
-    Must have 0 <= loc <= len(columns)
-column : object
-value : scalar, Series, or array-like
- -
isin(self, values)
Return boolean DataFrame showing whether each element in the
-DataFrame is contained in values.

-Parameters
-----------
-values : iterable, SeriesDataFrame or dictionary
-    The result will only be true at a location if all the
-    labels match. If `values` is a Series, that's the index. If
-    `values` is a dictionary, the keys must be the column names,
-    which must match. If `values` is a DataFrame,
-    then both the index and column labels must match.

-Returns
--------

-DataFrame of booleans

-Examples
---------
-When ``values`` is a list:

->>> df = DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'f']})
->>> df.isin([1, 3, 12, 'a'])
-       A      B
-0   True   True
-1  False  False
-2   True  False

-When ``values`` is a dict:

->>> df = DataFrame({'A': [1, 2, 3], 'B': [1, 4, 7]})
->>> df.isin({'A': [1, 3], 'B': [4, 7, 12]})
-       A      B
-0   True  False  # Note that B didn't match the 1 here.
-1  False   True
-2   True   True

-When ``values`` is a Series or DataFrame:

->>> df = DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'f']})
->>> other = DataFrame({'A': [1, 3, 3, 2], 'B': ['e', 'f', 'f', 'e']})
->>> df.isin(other)
-       A      B
-0   True  False
-1  False  False  # Column A in `other` has a 3, but not at index 1.
-2   True   True
- -
items = iteritems(self)
Iterator over (column name, Series) pairs.

-See also
---------
-iterrows : Iterate over DataFrame rows as (index, Series) pairs.
-itertuples : Iterate over DataFrame rows as namedtuples of the values.
- -
iteritems(self)
Iterator over (column name, Series) pairs.

-See also
---------
-iterrows : Iterate over DataFrame rows as (index, Series) pairs.
-itertuples : Iterate over DataFrame rows as namedtuples of the values.
- -
iterrows(self)
Iterate over DataFrame rows as (index, Series) pairs.

-Notes
------

-1. Because ``iterrows`` returns a Series for each row,
-   it does **not** preserve dtypes across the rows (dtypes are
-   preserved across columns for DataFrames). For example,

-   >>> df = pd.DataFrame([[1, 1.5]], columns=['int', 'float'])
-   >>> row = next(df.iterrows())[1]
-   >>> row
-   int      1.0
-   float    1.5
-   Name: 0, dtype: float64
-   >>> print(row['int'].dtype)
-   float64
-   >>> print(df['int'].dtype)
-   int64

-   To preserve dtypes while iterating over the rows, it is better
-   to use :meth:`itertuples` which returns namedtuples of the values
-   and which is generally faster than ``iterrows``.

-2. You should **never modify** something you are iterating over.
-   This is not guaranteed to work in all cases. Depending on the
-   data types, the iterator returns a copy and not a view, and writing
-   to it will have no effect.

-Returns
--------
-it : generator
-    A generator that iterates over the rows of the frame.

-See also
---------
-itertuples : Iterate over DataFrame rows as namedtuples of the values.
-iteritems : Iterate over (column name, Series) pairs.
- -
itertuples(self, index=True, name='Pandas')
Iterate over DataFrame rows as namedtuples, with index value as first
-element of the tuple.

-Parameters
-----------
-index : boolean, default True
-    If True, return the index as the first element of the tuple.
-name : string, default "Pandas"
-    The name of the returned namedtuples or None to return regular
-    tuples.

-Notes
------
-The column names will be renamed to positional names if they are
-invalid Python identifiers, repeated, or start with an underscore.
-With a large number of columns (>255), regular tuples are returned.

-See also
---------
-iterrows : Iterate over DataFrame rows as (index, Series) pairs.
-iteritems : Iterate over (column name, Series) pairs.

-Examples
---------

->>> df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]},
-                      index=['a', 'b'])
->>> df
-   col1  col2
-a     1   0.1
-b     2   0.2
->>> for row in df.itertuples():
-...     print(row)
-...
-Pandas(Index='a', col1=1, col2=0.10000000000000001)
-Pandas(Index='b', col1=2, col2=0.20000000000000001)
- -
join(self, other, on=None, how='left', lsuffix='', rsuffix='', sort=False)
Join columns with other DataFrame either on index or on a key
-column. Efficiently Join multiple DataFrame objects by index at once by
-passing a list.

-Parameters
-----------
-other : DataFrameSeries with name field set, or list of DataFrame
-    Index should be similar to one of the columns in this one. If a
-    Series is passed, its name attribute must be set, and that will be
-    used as the column name in the resulting joined DataFrame
-on : column name, tuple/list of column names, or array-like
-    Column(s) in the caller to join on the index in other,
-    otherwise joins index-on-index. If multiples
-    columns given, the passed DataFrame must have a MultiIndex. Can
-    pass an array as the join key if not already contained in the
-    calling DataFrame. Like an Excel VLOOKUP operation
-how : {'left', 'right', 'outer', 'inner'}, default: 'left'
-    How to handle the operation of the two objects.

-    * left: use calling frame's index (or column if on is specified)
-    * right: use other frame's index
-    * outer: form union of calling frame's index (or column if on is
-      specified) with other frame's index, and sort it
-      lexicographically
-    * inner: form intersection of calling frame's index (or column if
-      on is specified) with other frame's index, preserving the order
-      of the calling's one
-lsuffix : string
-    Suffix to use from left frame's overlapping columns
-rsuffix : string
-    Suffix to use from right frame's overlapping columns
-sort : boolean, default False
-    Order result DataFrame lexicographically by the join key. If False,
-    the order of the join key depends on the join type (how keyword)

-Notes
------
-on, lsuffix, and rsuffix options are not supported when passing a list
-of DataFrame objects

-Examples
---------
->>> caller = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3', 'K4', 'K5'],
-...                        'A': ['A0', 'A1', 'A2', 'A3', 'A4', 'A5']})

->>> caller
-    A key
-0  A0  K0
-1  A1  K1
-2  A2  K2
-3  A3  K3
-4  A4  K4
-5  A5  K5

->>> other = pd.DataFrame({'key': ['K0', 'K1', 'K2'],
-...                       'B': ['B0', 'B1', 'B2']})

->>> other
-    B key
-0  B0  K0
-1  B1  K1
-2  B2  K2

-Join DataFrames using their indexes.

->>> caller.join(other, lsuffix='_caller', rsuffix='_other')

->>>     A key_caller    B key_other
-    0  A0         K0   B0        K0
-    1  A1         K1   B1        K1
-    2  A2         K2   B2        K2
-    3  A3         K3  NaN       NaN
-    4  A4         K4  NaN       NaN
-    5  A5         K5  NaN       NaN


-If we want to join using the key columns, we need to set key to be
-the index in both caller and other. The joined DataFrame will have
-key as its index.

->>> caller.set_index('key').join(other.set_index('key'))

->>>      A    B
-    key
-    K0   A0   B0
-    K1   A1   B1
-    K2   A2   B2
-    K3   A3  NaN
-    K4   A4  NaN
-    K5   A5  NaN

-Another option to join using the key columns is to use the on
-parameter. DataFrame.join always uses other's index but we can use any
-column in the caller. This method preserves the original caller's
-index in the result.

->>> caller.join(other.set_index('key'), on='key')

->>>     A key    B
-    0  A0  K0   B0
-    1  A1  K1   B1
-    2  A2  K2   B2
-    3  A3  K3  NaN
-    4  A4  K4  NaN
-    5  A5  K5  NaN


-See also
---------
-DataFrame.merge : For column(s)-on-columns(s) operations

-Returns
--------
-joined : DataFrame
- -
kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : Series or DataFrame (if level specified)
- -
kurtosis = kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : Series or DataFrame (if level specified)
- -
last_valid_index(self)
Return label for last non-NA/null value
- -
le(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods le
- -
lookup(self, row_labels, col_labels)
Label-based "fancy indexing" function for DataFrame.
-Given equal-length arrays of row and column labels, return an
-array of the values corresponding to each (row, col) pair.

-Parameters
-----------
-row_labels : sequence
-    The row labels to use for lookup
-col_labels : sequence
-    The column labels to use for lookup

-Notes
------
-Akin to::

-    result = []
-    for row, col in zip(row_labels, col_labels):
-        result.append(df.get_value(row, col))

-Examples
---------
-values : ndarray
-    The found values
- -
lt(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods lt
- -
mad(self, axis=None, skipna=None, level=None)
Return the mean absolute deviation of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mad : Series or DataFrame (if level specified)
- -
max(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the maximum of the values in the object.
-            If you want the *index* of the maximum, use ``idxmax``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmax``.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-max : Series or DataFrame (if level specified)
- -
mean(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the mean of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mean : Series or DataFrame (if level specified)
- -
median(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the median of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-median : Series or DataFrame (if level specified)
- -
melt(self, id_vars=None, value_vars=None, var_name=None, value_name='value', col_level=None)
"Unpivots" a DataFrame from wide format to long format, optionally
-leaving identifier variables set.

-This function is useful to massage a DataFrame into a format where one
-or more columns are identifier variables (`id_vars`), while all other
-columns, considered measured variables (`value_vars`), are "unpivoted" to
-the row axis, leaving just two non-identifier columns, 'variable' and
-'value'.

-.. versionadded:: 0.20.0

-Parameters
-----------
-frame : DataFrame
-id_vars : tuple, list, or ndarray, optional
-    Column(s) to use as identifier variables.
-value_vars : tuple, list, or ndarray, optional
-    Column(s) to unpivot. If not specified, uses all columns that
-    are not set as `id_vars`.
-var_name : scalar
-    Name to use for the 'variable' column. If None it uses
-    ``frame.columns.name`` or 'variable'.
-value_name : scalar, default 'value'
-    Name to use for the 'value' column.
-col_level : int or string, optional
-    If columns are a MultiIndex then use this level to melt.

-See also
---------
-melt
-pivot_table
-DataFrame.pivot

-Examples
---------
->>> import pandas as pd
->>> df = pd.DataFrame({'A': {0: 'a', 1: 'b', 2: 'c'},
-...                    'B': {0: 1, 1: 3, 2: 5},
-...                    'C': {0: 2, 1: 4, 2: 6}})
->>> df
-   A  B  C
-0  a  1  2
-1  b  3  4
-2  c  5  6

->>> df.melt(id_vars=['A'], value_vars=['B'])
-   A variable  value
-0  a        B      1
-1  b        B      3
-2  c        B      5

->>> df.melt(id_vars=['A'], value_vars=['B', 'C'])
-   A variable  value
-0  a        B      1
-1  b        B      3
-2  c        B      5
-3  a        C      2
-4  b        C      4
-5  c        C      6

-The names of 'variable' and 'value' columns can be customized:

->>> df.melt(id_vars=['A'], value_vars=['B'],
-...         var_name='myVarname', value_name='myValname')
-   A myVarname  myValname
-0  a         B          1
-1  b         B          3
-2  c         B          5

-If you have multi-index columns:

->>> df.columns = [list('ABC'), list('DEF')]
->>> df
-   A  B  C
-   D  E  F
-0  a  1  2
-1  b  3  4
-2  c  5  6

->>> df.melt(col_level=0, id_vars=['A'], value_vars=['B'])
-   A variable  value
-0  a        B      1
-1  b        B      3
-2  c        B      5

->>> df.melt(id_vars=[('A', 'D')], value_vars=[('B', 'E')])
-  (A, D) variable_0 variable_1  value
-0      a          B          E      1
-1      b          B          E      3
-2      c          B          E      5
- -
memory_usage(self, index=True, deep=False)
Memory usage of DataFrame columns.

-Parameters
-----------
-index : bool
-    Specifies whether to include memory usage of DataFrame's
-    index in returned Series. If `index=True` (default is False)
-    the first index of the Series is `Index`.
-deep : bool
-    Introspect the data deeply, interrogate
-    `object` dtypes for system-level memory consumption

-Returns
--------
-sizes : Series
-    A series with column names as index and memory usage of
-    columns with units of bytes.

-Notes
------
-Memory usage does not include memory consumed by elements that
-are not components of the array if deep=False

-See Also
---------
-numpy.ndarray.nbytes
- -
merge(self, right, how='inner', on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=False, suffixes=('_x', '_y'), copy=True, indicator=False)
Merge DataFrame objects by performing a database-style join operation by
-columns or indexes.

-If joining columns on columns, the DataFrame indexes *will be
-ignored*. Otherwise if joining indexes on indexes or indexes on a column or
-columns, the index will be passed on.

-Parameters
-----------
-right : DataFrame
-how : {'left', 'right', 'outer', 'inner'}, default 'inner'
-    * left: use only keys from left frame, similar to a SQL left outer join;
-      preserve key order
-    * right: use only keys from right frame, similar to a SQL right outer join;
-      preserve key order
-    * outer: use union of keys from both frames, similar to a SQL full outer
-      join; sort keys lexicographically
-    * inner: use intersection of keys from both frames, similar to a SQL inner
-      join; preserve the order of the left keys
-on : label or list
-    Field names to join on. Must be found in both DataFrames. If on is
-    None and not merging on indexes, then it merges on the intersection of
-    the columns by default.
-left_on : label or list, or array-like
-    Field names to join on in left DataFrame. Can be a vector or list of
-    vectors of the length of the DataFrame to use a particular vector as
-    the join key instead of columns
-right_on : label or list, or array-like
-    Field names to join on in right DataFrame or vector/list of vectors per
-    left_on docs
-left_index : boolean, default False
-    Use the index from the left DataFrame as the join key(s). If it is a
-    MultiIndex, the number of keys in the other DataFrame (either the index
-    or a number of columns) must match the number of levels
-right_index : boolean, default False
-    Use the index from the right DataFrame as the join key. Same caveats as
-    left_index
-sort : boolean, default False
-    Sort the join keys lexicographically in the result DataFrame. If False,
-    the order of the join keys depends on the join type (how keyword)
-suffixes : 2-length sequence (tuple, list, ...)
-    Suffix to apply to overlapping column names in the left and right
-    side, respectively
-copy : boolean, default True
-    If False, do not copy data unnecessarily
-indicator : boolean or string, default False
-    If True, adds a column to output DataFrame called "_merge" with
-    information on the source of each row.
-    If string, column with information on source of each row will be added to
-    output DataFrame, and column will be named value of string.
-    Information column is Categorical-type and takes on a value of "left_only"
-    for observations whose merge key only appears in 'left' DataFrame,
-    "right_only" for observations whose merge key only appears in 'right'
-    DataFrame, and "both" if the observation's merge key is found in both.

-    .. versionadded:: 0.17.0

-Examples
---------

->>> A              >>> B
-    lkey value         rkey value
-0   foo  1         0   foo  5
-1   bar  2         1   bar  6
-2   baz  3         2   qux  7
-3   foo  4         3   bar  8

->>> A.merge(B, left_on='lkey', right_on='rkey', how='outer')
-   lkey  value_x  rkey  value_y
-0  foo   1        foo   5
-1  foo   4        foo   5
-2  bar   2        bar   6
-3  bar   2        bar   8
-4  baz   3        NaN   NaN
-5  NaN   NaN      qux   7

-Returns
--------
-merged : DataFrame
-    The output type will the be same as 'left', if it is a subclass
-    of DataFrame.

-See also
---------
-merge_ordered
-merge_asof
- -
min(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the minimum of the values in the object.
-            If you want the *index* of the minimum, use ``idxmin``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmin``.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-min : Series or DataFrame (if level specified)
- -
mod(self, other, axis='columns', level=None, fill_value=None)
Modulo of dataframe and other, element-wise (binary operator `mod`).

-Equivalent to ``dataframe % other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rmod
- -
mode(self, axis=0, numeric_only=False)
Gets the mode(s) of each element along the axis selected. Adds a row
-for each mode per label, fills in gaps with nan.

-Note that there could be multiple values returned for the selected
-axis (when more than one item share the maximum frequency), which is
-the reason why a dataframe is returned. If you want to impute missing
-values with the mode in a dataframe ``df``, you can just do this:
-``df.fillna(df.mode().iloc[0])``

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    * 0 or 'index' : get mode of each column
-    * 1 or 'columns' : get mode of each row
-numeric_only : boolean, default False
-    if True, only apply to numeric columns

-Returns
--------
-modes : DataFrame (sorted)

-Examples
---------
->>> df = pd.DataFrame({'A': [1, 2, 1, 2, 1, 2, 3]})
->>> df.mode()
-   A
-0  1
-1  2
- -
mul(self, other, axis='columns', level=None, fill_value=None)
Multiplication of dataframe and other, element-wise (binary operator `mul`).

-Equivalent to ``dataframe * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rmul
- -
multiply = mul(self, other, axis='columns', level=None, fill_value=None)
Multiplication of dataframe and other, element-wise (binary operator `mul`).

-Equivalent to ``dataframe * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rmul
- -
ne(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods ne
- -
nlargest(self, n, columns, keep='first')
Get the rows of a DataFrame sorted by the `n` largest
-values of `columns`.

-.. versionadded:: 0.17.0

-Parameters
-----------
-n : int
-    Number of items to retrieve
-columns : list or str
-    Column name or names to order by
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-DataFrame

-Examples
---------
->>> df = DataFrame({'a': [1, 10, 8, 11, -1],
-...                 'b': list('abdce'),
-...                 'c': [1.0, 2.0, np.nan, 3.0, 4.0]})
->>> df.nlargest(3, 'a')
-    a  b   c
-3  11  c   3
-1  10  b   2
-2   8  d NaN
- -
nsmallest(self, n, columns, keep='first')
Get the rows of a DataFrame sorted by the `n` smallest
-values of `columns`.

-.. versionadded:: 0.17.0

-Parameters
-----------
-n : int
-    Number of items to retrieve
-columns : list or str
-    Column name or names to order by
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-DataFrame

-Examples
---------
->>> df = DataFrame({'a': [1, 10, 8, 11, -1],
-...                 'b': list('abdce'),
-...                 'c': [1.0, 2.0, np.nan, 3.0, 4.0]})
->>> df.nsmallest(3, 'a')
-   a  b   c
-4 -1  e   4
-0  1  a   1
-2  8  d NaN
- -
nunique(self, axis=0, dropna=True)
Return Series with number of distinct observations over requested
-axis.

-.. versionadded:: 0.20.0

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-dropna : boolean, default True
-    Don't include NaN in the counts.

-Returns
--------
-nunique : Series

-Examples
---------
->>> df = pd.DataFrame({'A': [1, 2, 3], 'B': [1, 1, 1]})
->>> df.nunique()
-A    3
-B    1

->>> df.nunique(axis=1)
-0    1
-1    2
-2    2
- -
pivot(self, index=None, columns=None, values=None)
Reshape data (produce a "pivot" table) based on column values. Uses
-unique values from index / columns to form axes of the resulting
-DataFrame.

-Parameters
-----------
-index : string or object, optional
-    Column name to use to make new frame's index. If None, uses
-    existing index.
-columns : string or object
-    Column name to use to make new frame's columns
-values : string or object, optional
-    Column name to use for populating new frame's values. If not
-    specified, all remaining columns will be used and the result will
-    have hierarchically indexed columns

-Returns
--------
-pivoted : DataFrame

-See also
---------
-DataFrame.pivot_table : generalization of pivot that can handle
-    duplicate values for one index/column pair
-DataFrame.unstack : pivot based on the index values instead of a
-    column

-Notes
------
-For finer-tuned control, see hierarchical indexing documentation along
-with the related stack/unstack methods

-Examples
---------

->>> df = pd.DataFrame({'foo': ['one','one','one','two','two','two'],
-                       'bar': ['A', 'B', 'C', 'A', 'B', 'C'],
-                       'baz': [1, 2, 3, 4, 5, 6]})
->>> df
-    foo   bar  baz
-0   one   A    1
-1   one   B    2
-2   one   C    3
-3   two   A    4
-4   two   B    5
-5   two   C    6

->>> df.pivot(index='foo', columns='bar', values='baz')
-     A   B   C
-one  1   2   3
-two  4   5   6

->>> df.pivot(index='foo', columns='bar')['baz']
-     A   B   C
-one  1   2   3
-two  4   5   6
- -
pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All')
Create a spreadsheet-style pivot table as a DataFrame. The levels in the
-pivot table will be stored in MultiIndex objects (hierarchical indexes) on
-the index and columns of the result DataFrame

-Parameters
-----------
-data : DataFrame
-values : column to aggregate, optional
-index : column, Grouper, array, or list of the previous
-    If an array is passed, it must be the same length as the data. The list
-    can contain any of the other types (except list).
-    Keys to group by on the pivot table index.  If an array is passed, it
-    is being used as the same manner as column values.
-columns : column, Grouper, array, or list of the previous
-    If an array is passed, it must be the same length as the data. The list
-    can contain any of the other types (except list).
-    Keys to group by on the pivot table column.  If an array is passed, it
-    is being used as the same manner as column values.
-aggfunc : function or list of functions, default numpy.mean
-    If list of functions passed, the resulting pivot table will have
-    hierarchical columns whose top level are the function names (inferred
-    from the function objects themselves)
-fill_value : scalar, default None
-    Value to replace missing values with
-margins : boolean, default False
-    Add all row / columns (e.g. for subtotal / grand totals)
-dropna : boolean, default True
-    Do not include columns whose entries are all NaN
-margins_name : string, default 'All'
-    Name of the row / column that will contain the totals
-    when margins is True.

-Examples
---------
->>> df
-   A   B   C      D
-0  foo one small  1
-1  foo one large  2
-2  foo one large  2
-3  foo two small  3
-4  foo two small  3
-5  bar one large  4
-6  bar one small  5
-7  bar two small  6
-8  bar two large  7

->>> table = pivot_table(df, values='D', index=['A', 'B'],
-...                     columns=['C'], aggfunc=np.sum)
->>> table
-          small  large
-foo  one  1      4
-     two  6      NaN
-bar  one  5      4
-     two  6      7

-Returns
--------
-table : DataFrame

-See also
---------
-DataFrame.pivot : pivot without aggregation that can handle
-    non-numeric data
- -
pow(self, other, axis='columns', level=None, fill_value=None)
Exponential power of dataframe and other, element-wise (binary operator `pow`).

-Equivalent to ``dataframe ** other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rpow
- -
prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : Series or DataFrame (if level specified)
- -
product = prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : Series or DataFrame (if level specified)
- -
quantile(self, q=0.5, axis=0, numeric_only=True, interpolation='linear')
Return values at the given quantile over requested axis, a la
-numpy.percentile.

-Parameters
-----------
-q : float or array-like, default 0.5 (50% quantile)
-    0 <= q <= 1, the quantile(s) to compute
-axis : {0, 1, 'index', 'columns'} (default 0)
-    0 or 'index' for row-wise, 1 or 'columns' for column-wise
-interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'}
-    .. versionadded:: 0.18.0

-    This optional parameter specifies the interpolation method to use,
-    when the desired quantile lies between two data points `i` and `j`:

-    * linear: `i + (j - i) * fraction`, where `fraction` is the
-      fractional part of the index surrounded by `i` and `j`.
-    * lower: `i`.
-    * higher: `j`.
-    * nearest: `i` or `j` whichever is nearest.
-    * midpoint: (`i` + `j`) / 2.

-Returns
--------
-quantiles : Series or DataFrame

-    - If ``q`` is an array, a DataFrame will be returned where the
-      index is ``q``, the columns are the columns of self, and the
-      values are the quantiles.
-    - If ``q`` is a float, a Series will be returned where the
-      index is the columns of self and the values are the quantiles.

-Examples
---------

->>> df = DataFrame(np.array([[1, 1], [2, 10], [3, 100], [4, 100]]),
-                   columns=['a', 'b'])
->>> df.quantile(.1)
-a    1.3
-b    3.7
-dtype: float64
->>> df.quantile([.1, .5])
-       a     b
-0.1  1.3   3.7
-0.5  2.5  55.0
- -
query(self, expr, inplace=False, **kwargs)
Query the columns of a frame with a boolean expression.

-.. versionadded:: 0.13

-Parameters
-----------
-expr : string
-    The query string to evaluate.  You can refer to variables
-    in the environment by prefixing them with an '@' character like
-    ``@a + b``.
-inplace : bool
-    Whether the query should modify the data in place or return
-    a modified copy

-    .. versionadded:: 0.18.0

-kwargs : dict
-    See the documentation for :func:`pandas.eval` for complete details
-    on the keyword arguments accepted by :meth:`DataFrame.query`.

-Returns
--------
-q : DataFrame

-Notes
------
-The result of the evaluation of this expression is first passed to
-:attr:`DataFrame.loc` and if that fails because of a
-multidimensional key (e.g., a DataFrame) then the result will be passed
-to :meth:`DataFrame.__getitem__`.

-This method uses the top-level :func:`pandas.eval` function to
-evaluate the passed query.

-The :meth:`~pandas.DataFrame.query` method uses a slightly
-modified Python syntax by default. For example, the ``&`` and ``|``
-(bitwise) operators have the precedence of their boolean cousins,
-:keyword:`and` and :keyword:`or`. This *is* syntactically valid Python,
-however the semantics are different.

-You can change the semantics of the expression by passing the keyword
-argument ``parser='python'``. This enforces the same semantics as
-evaluation in Python space. Likewise, you can pass ``engine='python'``
-to evaluate an expression using Python itself as a backend. This is not
-recommended as it is inefficient compared to using ``numexpr`` as the
-engine.

-The :attr:`DataFrame.index` and
-:attr:`DataFrame.columns` attributes of the
-:class:`~pandas.DataFrame` instance are placed in the query namespace
-by default, which allows you to treat both the index and columns of the
-frame as a column in the frame.
-The identifier ``index`` is used for the frame index; you can also
-use the name of the index to identify it in a query.

-For further details and examples see the ``query`` documentation in
-:ref:`indexing <indexing.query>`.

-See Also
---------
-pandas.eval
-DataFrame.eval

-Examples
---------
->>> from numpy.random import randn
->>> from pandas import DataFrame
->>> df = DataFrame(randn(10, 2), columns=list('ab'))
->>> df.query('a > b')
->>> df[df.a > df.b]  # same result as the previous expression
- -
radd(self, other, axis='columns', level=None, fill_value=None)
Addition of dataframe and other, element-wise (binary operator `radd`).

-Equivalent to ``other + dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.add
- -
rdiv = rtruediv(self, other, axis='columns', level=None, fill_value=None)
Floating division of dataframe and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.truediv
- -
reindex(self, index=None, columns=None, **kwargs)
Conform DataFrame to new index with optional filling logic, placing
-NA/NaN in locations having no value in the previous index. A new object
-is produced unless the new index is equivalent to the current one and
-copy=False

-Parameters
-----------
-index, columns : array-like, optional (can be specified in order, or as
-    keywords)
-    New labels / index to conform to. Preferably an Index object to
-    avoid duplicating data
-method : {None, 'backfill'/'bfill', 'pad'/'ffill', 'nearest'}, optional
-    method to use for filling holes in reindexed DataFrame.
-    Please note: this is only  applicable to DataFrames/Series with a
-    monotonically increasing/decreasing index.

-    * default: don't fill gaps
-    * pad / ffill: propagate last valid observation forward to next
-      valid
-    * backfill / bfill: use next valid observation to fill gap
-    * nearest: use nearest valid observations to fill gap

-copy : boolean, default True
-    Return a new object, even if the passed indexes are the same
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-limit : int, default None
-    Maximum number of consecutive elements to forward or backward fill
-tolerance : optional
-    Maximum distance between original and new labels for inexact
-    matches. The values of the index at the matching locations most
-    satisfy the equation ``abs(index[indexer] - target) <= tolerance``.

-    .. versionadded:: 0.17.0

-Examples
---------

-Create a dataframe with some fictional data.

->>> index = ['Firefox', 'Chrome', 'Safari', 'IE10', 'Konqueror']
->>> df = pd.DataFrame({
-...      'http_status': [200,200,404,404,301],
-...      'response_time': [0.04, 0.02, 0.07, 0.08, 1.0]},
-...       index=index)
->>> df
-           http_status  response_time
-Firefox            200           0.04
-Chrome             200           0.02
-Safari             404           0.07
-IE10               404           0.08
-Konqueror          301           1.00

-Create a new index and reindex the dataframe. By default
-values in the new index that do not have corresponding
-records in the dataframe are assigned ``NaN``.

->>> new_index= ['Safari', 'Iceweasel', 'Comodo Dragon', 'IE10',
-...             'Chrome']
->>> df.reindex(new_index)
-               http_status  response_time
-Safari               404.0           0.07
-Iceweasel              NaN            NaN
-Comodo Dragon          NaN            NaN
-IE10                 404.0           0.08
-Chrome               200.0           0.02

-We can fill in the missing values by passing a value to
-the keyword ``fill_value``. Because the index is not monotonically
-increasing or decreasing, we cannot use arguments to the keyword
-``method`` to fill the ``NaN`` values.

->>> df.reindex(new_index, fill_value=0)
-               http_status  response_time
-Safari                 404           0.07
-Iceweasel                0           0.00
-Comodo Dragon            0           0.00
-IE10                   404           0.08
-Chrome                 200           0.02

->>> df.reindex(new_index, fill_value='missing')
-              http_status response_time
-Safari                404          0.07
-Iceweasel         missing       missing
-Comodo Dragon     missing       missing
-IE10                  404          0.08
-Chrome                200          0.02

-To further illustrate the filling functionality in
-``reindex``, we will create a dataframe with a
-monotonically increasing index (for example, a sequence
-of dates).

->>> date_index = pd.date_range('1/1/2010', periods=6, freq='D')
->>> df2 = pd.DataFrame({"prices": [100, 101, np.nan, 100, 89, 88]},
-...                    index=date_index)
->>> df2
-            prices
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88

-Suppose we decide to expand the dataframe to cover a wider
-date range.

->>> date_index2 = pd.date_range('12/29/2009', periods=10, freq='D')
->>> df2.reindex(date_index2)
-            prices
-2009-12-29     NaN
-2009-12-30     NaN
-2009-12-31     NaN
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-The index entries that did not have a value in the original data frame
-(for example, '2009-12-29') are by default filled with ``NaN``.
-If desired, we can fill in the missing values using one of several
-options.

-For example, to backpropagate the last valid value to fill the ``NaN``
-values, pass ``bfill`` as an argument to the ``method`` keyword.

->>> df2.reindex(date_index2, method='bfill')
-            prices
-2009-12-29     100
-2009-12-30     100
-2009-12-31     100
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-Please note that the ``NaN`` value present in the original dataframe
-(at index value 2010-01-03) will not be filled by any of the
-value propagation schemes. This is because filling while reindexing
-does not look at dataframe values, but only compares the original and
-desired indexes. If you do want to fill in the ``NaN`` values present
-in the original dataframe, use the ``fillna()`` method.

-Returns
--------
-reindexed : DataFrame
- -
reindex_axis(self, labels, axis=0, method=None, level=None, copy=True, limit=None, fill_value=nan)
Conform input object to new index with optional
-filling logic, placing NA/NaN in locations having no value in the
-previous index. A new object is produced unless the new index is
-equivalent to the current one and copy=False

-Parameters
-----------
-labels : array-like
-    New labels / index to conform to. Preferably an Index object to
-    avoid duplicating data
-axis : {0 or 'index', 1 or 'columns'}
-method : {None, 'backfill'/'bfill', 'pad'/'ffill', 'nearest'}, optional
-    Method to use for filling holes in reindexed DataFrame:

-    * default: don't fill gaps
-    * pad / ffill: propagate last valid observation forward to next
-      valid
-    * backfill / bfill: use next valid observation to fill gap
-    * nearest: use nearest valid observations to fill gap

-copy : boolean, default True
-    Return a new object, even if the passed indexes are the same
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-limit : int, default None
-    Maximum number of consecutive elements to forward or backward fill
-tolerance : optional
-    Maximum distance between original and new labels for inexact
-    matches. The values of the index at the matching locations most
-    satisfy the equation ``abs(index[indexer] - target) <= tolerance``.

-    .. versionadded:: 0.17.0

-Examples
---------
->>> df.reindex_axis(['A', 'B', 'C'], axis=1)

-See Also
---------
-reindex, reindex_like

-Returns
--------
-reindexed : DataFrame
- -
rename(self, index=None, columns=None, **kwargs)
Alter axes input function or functions. Function / dict values must be
-unique (1-to-1). Labels not contained in a dict / Series will be left
-as-is. Extra labels listed don't throw an error. Alternatively, change
-``Series.name`` with a scalar value (Series only).

-Parameters
-----------
-index, columns : scalar, list-like, dict-like or function, optional
-    Scalar or list-like will alter the ``Series.name`` attribute,
-    and raise on DataFrame or Panel.
-    dict-like or functions are transformations to apply to
-    that axis' values
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False
-    Whether to return a new DataFrame. If True then value of copy is
-    ignored.
-level : int or level name, default None
-    In case of a MultiIndex, only rename labels in the specified
-    level.

-Returns
--------
-renamed : DataFrame (new object)

-See Also
---------
-pandas.NDFrame.rename_axis

-Examples
---------
->>> s = pd.Series([1, 2, 3])
->>> s
-0    1
-1    2
-2    3
-dtype: int64
->>> s.rename("my_name") # scalar, changes Series.name
-0    1
-1    2
-2    3
-Name: my_name, dtype: int64
->>> s.rename(lambda x: x ** 2)  # function, changes labels
-0    1
-1    2
-4    3
-dtype: int64
->>> s.rename({1: 3, 2: 5})  # mapping, changes labels
-0    1
-3    2
-5    3
-dtype: int64
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename(2)
-Traceback (most recent call last):
-...
-TypeError: 'int' object is not callable
->>> df.rename(index=str, columns={"A": "a", "B": "c"})
-   a  c
-0  1  4
-1  2  5
-2  3  6
->>> df.rename(index=str, columns={"A": "a", "C": "c"})
-   a  B
-0  1  4
-1  2  5
-2  3  6
- -
reorder_levels(self, order, axis=0)
Rearrange index levels using input order.
-May not drop or duplicate levels

-Parameters
-----------
-order : list of int or list of str
-    List representing new level order. Reference level by number
-    (position) or by key (label).
-axis : int
-    Where to reorder levels.

-Returns
--------
-type of caller (new object)
- -
reset_index(self, level=None, drop=False, inplace=False, col_level=0, col_fill='')
For DataFrame with multi-level index, return new DataFrame with
-labeling information in the columns under the index names, defaulting
-to 'level_0', 'level_1', etc. if any are None. For a standard index,
-the index name will be used (if set), otherwise a default 'index' or
-'level_0' (if 'index' is already taken) will be used.

-Parameters
-----------
-level : int, str, tuple, or list, default None
-    Only remove the given levels from the index. Removes all levels by
-    default
-drop : boolean, default False
-    Do not try to insert index into dataframe columns. This resets
-    the index to the default integer index.
-inplace : boolean, default False
-    Modify the DataFrame in place (do not create a new object)
-col_level : int or str, default 0
-    If the columns have multiple levels, determines which level the
-    labels are inserted into. By default it is inserted into the first
-    level.
-col_fill : object, default ''
-    If the columns have multiple levels, determines how the other
-    levels are named. If None then the index name is repeated.

-Returns
--------
-resetted : DataFrame
- -
rfloordiv(self, other, axis='columns', level=None, fill_value=None)
Integer division of dataframe and other, element-wise (binary operator `rfloordiv`).

-Equivalent to ``other // dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.floordiv
- -
rmod(self, other, axis='columns', level=None, fill_value=None)
Modulo of dataframe and other, element-wise (binary operator `rmod`).

-Equivalent to ``other % dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.mod
- -
rmul(self, other, axis='columns', level=None, fill_value=None)
Multiplication of dataframe and other, element-wise (binary operator `rmul`).

-Equivalent to ``other * dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.mul
- -
rolling(self, window, min_periods=None, freq=None, center=False, win_type=None, on=None, axis=0, closed=None)
Provides rolling window calculcations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-window : int, or offset
-    Size of the moving window. This is the number of observations used for
-    calculating the statistic. Each window will be a fixed size.

-    If its an offset then this will be the time period of each window. Each
-    window will be a variable sized based on the observations included in
-    the time-period. This is only valid for datetimelike indexes. This is
-    new in 0.19.0
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA). For a window that is specified by an offset,
-    this will default to 1.
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-win_type : string, default None
-    Provide a window type. See the notes below.
-on : string, optional
-    For a DataFrame, column on which to calculate
-    the rolling window, rather than the index
-closed : string, default None
-    Make the interval closed on the 'right', 'left', 'both' or
-    'neither' endpoints.
-    For offset-based windows, it defaults to 'right'.
-    For fixed windows, defaults to 'both'. Remaining cases not implemented
-    for fixed windows.

-    .. versionadded:: 0.20.0

-axis : int or string, default 0

-Returns
--------
-a Window or Rolling sub-classed for the particular operation

-Examples
---------

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]})
->>> df
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

-Rolling sum with a window length of 2, using the 'triang'
-window type.

->>> df.rolling(2, win_type='triang').sum()
-     B
-0  NaN
-1  1.0
-2  2.5
-3  NaN
-4  NaN

-Rolling sum with a window length of 2, min_periods defaults
-to the window length.

->>> df.rolling(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  NaN
-4  NaN

-Same as above, but explicity set the min_periods

->>> df.rolling(2, min_periods=1).sum()
-     B
-0  0.0
-1  1.0
-2  3.0
-3  2.0
-4  4.0

-A ragged (meaning not-a-regular frequency), time-indexed DataFrame

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]},
-....:                 index = [pd.Timestamp('20130101 09:00:00'),
-....:                          pd.Timestamp('20130101 09:00:02'),
-....:                          pd.Timestamp('20130101 09:00:03'),
-....:                          pd.Timestamp('20130101 09:00:05'),
-....:                          pd.Timestamp('20130101 09:00:06')])

->>> df
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  2.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0


-Contrasting to an integer rolling window, this will roll a variable
-length window corresponding to the time period.
-The default for min_periods is 1.

->>> df.rolling('2s').sum()
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  3.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-To learn more about the offsets & frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-The recognized win_types are:

-* ``boxcar``
-* ``triang``
-* ``blackman``
-* ``hamming``
-* ``bartlett``
-* ``parzen``
-* ``bohman``
-* ``blackmanharris``
-* ``nuttall``
-* ``barthann``
-* ``kaiser`` (needs beta)
-* ``gaussian`` (needs std)
-* ``general_gaussian`` (needs power, width)
-* ``slepian`` (needs width).
- -
round(self, decimals=0, *args, **kwargs)
Round a DataFrame to a variable number of decimal places.

-.. versionadded:: 0.17.0

-Parameters
-----------
-decimals : int, dict, Series
-    Number of decimal places to round each column to. If an int is
-    given, round each column to the same number of places.
-    Otherwise dict and Series round to variable numbers of places.
-    Column names should be in the keys if `decimals` is a
-    dict-like, or in the index if `decimals` is a Series. Any
-    columns not included in `decimals` will be left as is. Elements
-    of `decimals` which are not columns of the input will be
-    ignored.

-Examples
---------
->>> df = pd.DataFrame(np.random.random([3, 3]),
-...     columns=['A', 'B', 'C'], index=['first', 'second', 'third'])
->>> df
-               A         B         C
-first   0.028208  0.992815  0.173891
-second  0.038683  0.645646  0.577595
-third   0.877076  0.149370  0.491027
->>> df.round(2)
-           A     B     C
-first   0.03  0.99  0.17
-second  0.04  0.65  0.58
-third   0.88  0.15  0.49
->>> df.round({'A': 1, 'C': 2})
-          A         B     C
-first   0.0  0.992815  0.17
-second  0.0  0.645646  0.58
-third   0.9  0.149370  0.49
->>> decimals = pd.Series([1, 0, 2], index=['A', 'B', 'C'])
->>> df.round(decimals)
-          A  B     C
-first   0.0  1  0.17
-second  0.0  1  0.58
-third   0.9  0  0.49

-Returns
--------
-DataFrame object

-See Also
---------
-numpy.around
-Series.round
- -
rpow(self, other, axis='columns', level=None, fill_value=None)
Exponential power of dataframe and other, element-wise (binary operator `rpow`).

-Equivalent to ``other ** dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.pow
- -
rsub(self, other, axis='columns', level=None, fill_value=None)
Subtraction of dataframe and other, element-wise (binary operator `rsub`).

-Equivalent to ``other - dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.sub
- -
rtruediv(self, other, axis='columns', level=None, fill_value=None)
Floating division of dataframe and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.truediv
- -
select_dtypes(self, include=None, exclude=None)
Return a subset of a DataFrame including/excluding columns based on
-their ``dtype``.

-Parameters
-----------
-include, exclude : list-like
-    A list of dtypes or strings to be included/excluded. You must pass
-    in a non-empty sequence for at least one of these.

-Raises
-------
-ValueError
-    * If both of ``include`` and ``exclude`` are empty
-    * If ``include`` and ``exclude`` have overlapping elements
-    * If any kind of string dtype is passed in.
-TypeError
-    * If either of ``include`` or ``exclude`` is not a sequence

-Returns
--------
-subset : DataFrame
-    The subset of the frame including the dtypes in ``include`` and
-    excluding the dtypes in ``exclude``.

-Notes
------
-* To select all *numeric* types use the numpy dtype ``numpy.number``
-* To select strings you must use the ``object`` dtype, but note that
-  this will return *all* object dtype columns
-* See the `numpy dtype hierarchy
-  <http://docs.scipy.org/doc/numpy/reference/arrays.scalars.html>`__
-* To select datetimes, use np.datetime64, 'datetime' or 'datetime64'
-* To select timedeltas, use np.timedelta64, 'timedelta' or
-  'timedelta64'
-* To select Pandas categorical dtypes, use 'category'
-* To select Pandas datetimetz dtypes, use 'datetimetz' (new in 0.20.0),
-  or a 'datetime64[ns, tz]' string

-Examples
---------
->>> df = pd.DataFrame({'a': np.random.randn(6).astype('f4'),
-...                    'b': [True, False] * 3,
-...                    'c': [1.0, 2.0] * 3})
->>> df
-        a      b  c
-0  0.3962   True  1
-1  0.1459  False  2
-2  0.2623   True  1
-3  0.0764  False  2
-4 -0.9703   True  1
-5 -1.2094  False  2
->>> df.select_dtypes(include=['float64'])
-   c
-0  1
-1  2
-2  1
-3  2
-4  1
-5  2
->>> df.select_dtypes(exclude=['floating'])
-       b
-0   True
-1  False
-2   True
-3  False
-4   True
-5  False
- -
sem(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased standard error of the mean over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sem : Series or DataFrame (if level specified)
- -
set_index(self, keys, drop=True, append=False, inplace=False, verify_integrity=False)
Set the DataFrame index (row labels) using one or more existing
-columns. By default yields a new object.

-Parameters
-----------
-keys : column label or list of column labels / arrays
-drop : boolean, default True
-    Delete columns to be used as the new index
-append : boolean, default False
-    Whether to append columns to existing index
-inplace : boolean, default False
-    Modify the DataFrame in place (do not create a new object)
-verify_integrity : boolean, default False
-    Check the new index for duplicates. Otherwise defer the check until
-    necessary. Setting to False will improve the performance of this
-    method

-Examples
---------
->>> indexed_df = df.set_index(['A', 'B'])
->>> indexed_df2 = df.set_index(['A', [0, 1, 2, 0, 1, 2]])
->>> indexed_df3 = df.set_index([[0, 1, 2, 0, 1, 2]])

-Returns
--------
-dataframe : DataFrame
- -
set_value(self, index, col, value, takeable=False)
Put single value at passed column and index

-Parameters
-----------
-index : row label
-col : column label
-value : scalar value
-takeable : interpret the index/col as indexers, default False

-Returns
--------
-frame : DataFrame
-    If label pair is contained, will be reference to calling DataFrame,
-    otherwise a new object
- -
shift(self, periods=1, freq=None, axis=0)
Shift index by desired number of periods with an optional time freq

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, optional
-    Increment to use from the tseries module or time rule (e.g. 'EOM').
-    See Notes.
-axis : {0 or 'index', 1 or 'columns'}

-Notes
------
-If freq is specified then the index values are shifted but the data
-is not realigned. That is, use freq if you would like to extend the
-index when shifting and preserve the original data.

-Returns
--------
-shifted : DataFrame
- -
skew(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased skew over requested axis
-Normalized by N-1

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-skew : Series or DataFrame (if level specified)
- -
sort_index(self, axis=0, level=None, ascending=True, inplace=False, kind='quicksort', na_position='last', sort_remaining=True, by=None)
Sort object by labels (along an axis)

-Parameters
-----------
-axis : index, columns to direct sorting
-level : int or level name or list of ints or list of level names
-    if not None, sort on values in specified index level(s)
-ascending : boolean, default True
-    Sort ascending vs. descending
-inplace : bool, default False
-    if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end.
-     Not implemented for MultiIndex.
-sort_remaining : bool, default True
-    if true and sorting by level and index is multilevel, sort by other
-    levels too (in order) after sorting by specified level

-Returns
--------
-sorted_obj : DataFrame
- -
sort_values(self, by, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last')
Sort by the values along either axis

-.. versionadded:: 0.17.0

-Parameters
-----------
-by : str or list of str
-    Name or list of names which refer to the axis items.
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    Axis to direct sorting
-ascending : bool or list of bool, default True
-     Sort ascending vs. descending. Specify list for multiple sort
-     orders.  If this is a list of bools, must match the length of
-     the by.
-inplace : bool, default False
-     if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end

-Returns
--------
-sorted_obj : DataFrame
- -
sortlevel(self, level=0, axis=0, ascending=True, inplace=False, sort_remaining=True)
DEPRECATED: use :meth:`DataFrame.sort_index`

-Sort multilevel index by chosen axis and primary level. Data will be
-lexicographically sorted by the chosen level followed by the other
-levels (in order)

-Parameters
-----------
-level : int
-axis : {0 or 'index', 1 or 'columns'}, default 0
-ascending : boolean, default True
-inplace : boolean, default False
-    Sort the DataFrame without creating a new instance
-sort_remaining : boolean, default True
-    Sort by the other levels too.

-Returns
--------
-sorted : DataFrame

-See Also
---------
-DataFrame.sort_index(level=...)
- -
stack(self, level=-1, dropna=True)
Pivot a level of the (possibly hierarchical) column labels, returning a
-DataFrame (or Series in the case of an object with a single level of
-column labels) having a hierarchical index with a new inner-most level
-of row labels.
-The level involved will automatically get sorted.

-Parameters
-----------
-level : int, string, or list of these, default last level
-    Level(s) to stack, can pass level name
-dropna : boolean, default True
-    Whether to drop rows in the resulting Frame/Series with no valid
-    values

-Examples
-----------
->>> s
-     a   b
-one  1.  2.
-two  3.  4.

->>> s.stack()
-one a    1
-    b    2
-two a    3
-    b    4

-Returns
--------
-stacked : DataFrame or Series
- -
std(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return sample standard deviation over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-std : Series or DataFrame (if level specified)
- -
sub(self, other, axis='columns', level=None, fill_value=None)
Subtraction of dataframe and other, element-wise (binary operator `sub`).

-Equivalent to ``dataframe - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rsub
- -
subtract = sub(self, other, axis='columns', level=None, fill_value=None)
Subtraction of dataframe and other, element-wise (binary operator `sub`).

-Equivalent to ``dataframe - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rsub
- -
sum(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the sum of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sum : Series or DataFrame (if level specified)
- -
swaplevel(self, i=-2, j=-1, axis=0)
Swap levels i and j in a MultiIndex on a particular axis

-Parameters
-----------
-i, j : int, string (can be mixed)
-    Level of index to be swapped. Can pass level name as string.

-Returns
--------
-swapped : type of caller (new object)

-.. versionchanged:: 0.18.1

-   The indexes ``i`` and ``j`` are now optional, and default to
-   the two innermost levels of the index.
- -
to_csv(self, path_or_buf=None, sep=',', na_rep='', float_format=None, columns=None, header=True, index=True, index_label=None, mode='w', encoding=None, compression=None, quoting=None, quotechar='"', line_terminator='\n', chunksize=None, tupleize_cols=False, date_format=None, doublequote=True, escapechar=None, decimal='.')
Write DataFrame to a comma-separated values (csv) file

-Parameters
-----------
-path_or_buf : string or file handle, default None
-    File path or object, if None is provided the result is returned as
-    a string.
-sep : character, default ','
-    Field delimiter for the output file.
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-columns : sequence, optional
-    Columns to write
-header : boolean or list of string, default True
-    Write out column names. If a list of string is given it is assumed
-    to be aliases for the column names
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, or False, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.  If
-    False do not print fields for index names. Use index_label=False
-    for easier importing in R
-mode : str
-    Python write mode, default 'w'
-encoding : string, optional
-    A string representing the encoding to use in the output file,
-    defaults to 'ascii' on Python 2 and 'utf-8' on Python 3.
-compression : string, optional
-    a string representing the compression to use in the output file,
-    allowed values are 'gzip', 'bz2', 'xz',
-    only used when the first argument is a filename
-line_terminator : string, default ``'\n'``
-    The newline character or character sequence to use in the output
-    file
-quoting : optional constant from csv module
-    defaults to csv.QUOTE_MINIMAL. If you have set a `float_format`
-    then floats are converted to strings and thus csv.QUOTE_NONNUMERIC
-    will treat them as non-numeric
-quotechar : string (length 1), default '\"'
-    character used to quote fields
-doublequote : boolean, default True
-    Control quoting of `quotechar` inside a field
-escapechar : string (length 1), default None
-    character used to escape `sep` and `quotechar` when appropriate
-chunksize : int or None
-    rows to write at a time
-tupleize_cols : boolean, default False
-    write multi_index columns as a list of tuples (if True)
-    or new (expanded format) if False)
-date_format : string, default None
-    Format string for datetime objects
-decimal: string, default '.'
-    Character recognized as decimal separator. E.g. use ',' for
-    European data

-    .. versionadded:: 0.16.0
- -
to_dict(self, orient='dict')
Convert DataFrame to dictionary.

-Parameters
-----------
-orient : str {'dict', 'list', 'series', 'split', 'records', 'index'}
-    Determines the type of the values of the dictionary.

-    - dict (default) : dict like {column -> {index -> value}}
-    - list : dict like {column -> [values]}
-    - series : dict like {column -> Series(values)}
-    - split : dict like
-      {index -> [index], columns -> [columns], data -> [values]}
-    - records : list like
-      [{column -> value}, ... , {column -> value}]
-    - index : dict like {index -> {column -> value}}

-      .. versionadded:: 0.17.0

-    Abbreviations are allowed. `s` indicates `series` and `sp`
-    indicates `split`.

-Returns
--------
-result : dict like {column -> {index -> value}}
- -
to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='', float_format=None, columns=None, header=True, index=True, index_label=None, startrow=0, startcol=0, engine=None, merge_cells=True, encoding=None, inf_rep='inf', verbose=True, freeze_panes=None)
Write DataFrame to an excel sheet


-Parameters
-----------
-excel_writer : string or ExcelWriter object
-    File path or existing ExcelWriter
-sheet_name : string, default 'Sheet1'
-    Name of sheet which will contain DataFrame
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-columns : sequence, optional
-    Columns to write
-header : boolean or list of string, default True
-    Write out column names. If a list of string is given it is
-    assumed to be aliases for the column names
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.
-startrow :
-    upper left cell row to dump data frame
-startcol :
-    upper left cell column to dump data frame
-engine : string, default None
-    write engine to use - you can also set this via the options
-    ``io.excel.xlsx.writer``, ``io.excel.xls.writer``, and
-    ``io.excel.xlsm.writer``.
-merge_cells : boolean, default True
-    Write MultiIndex and Hierarchical Rows as merged cells.
-encoding: string, default None
-    encoding of the resulting excel file. Only necessary for xlwt,
-    other writers support unicode natively.
-inf_rep : string, default 'inf'
-    Representation for infinity (there is no native representation for
-    infinity in Excel)
-freeze_panes : tuple of integer (length 2), default None
-    Specifies the one-based bottommost row and rightmost column that
-    is to be frozen

-    .. versionadded:: 0.20.0

-Notes
------
-If passing an existing ExcelWriter object, then the sheet will be added
-to the existing workbook.  This can be used to save different
-DataFrames to one workbook:

->>> writer = pd.ExcelWriter('output.xlsx')
->>> df1.to_excel(writer,'Sheet1')
->>> df2.to_excel(writer,'Sheet2')
->>> writer.save()

-For compatibility with to_csv, to_excel serializes lists and dicts to
-strings before writing.
- -
to_feather(self, fname)
write out the binary feather-format for DataFrames

-.. versionadded:: 0.20.0

-Parameters
-----------
-fname : str
-    string file path
- -
to_gbq(self, destination_table, project_id, chunksize=10000, verbose=True, reauth=False, if_exists='fail', private_key=None)
Write a DataFrame to a Google BigQuery table.

-The main method a user calls to export pandas DataFrame contents to
-Google BigQuery table.

-Google BigQuery API Client Library v2 for Python is used.
-Documentation is available `here
-<https://developers.google.com/api-client-library/python/apis/bigquery/v2>`__

-Authentication to the Google BigQuery service is via OAuth 2.0.

-- If "private_key" is not provided:

-  By default "application default credentials" are used.

-  If default application credentials are not found or are restrictive,
-  user account credentials are used. In this case, you will be asked to
-  grant permissions for product name 'pandas GBQ'.

-- If "private_key" is provided:

-  Service account credentials will be used to authenticate.

-Parameters
-----------
-dataframe : DataFrame
-    DataFrame to be written
-destination_table : string
-    Name of table to be written, in the form 'dataset.tablename'
-project_id : str
-    Google BigQuery Account project ID.
-chunksize : int (default 10000)
-    Number of rows to be inserted in each chunk from the dataframe.
-verbose : boolean (default True)
-    Show percentage complete
-reauth : boolean (default False)
-    Force Google BigQuery to reauthenticate the user. This is useful
-    if multiple accounts are used.
-if_exists : {'fail', 'replace', 'append'}, default 'fail'
-    'fail': If table exists, do nothing.
-    'replace': If table exists, drop it, recreate it, and insert data.
-    'append': If table exists, insert data. Create if does not exist.
-private_key : str (optional)
-    Service account private key in JSON format. Can be file path
-    or string contents. This is useful for remote server
-    authentication (eg. jupyter iPython notebook on remote host)
- -
to_html(self, buf=None, columns=None, col_space=None, header=True, index=True, na_rep='NaN', formatters=None, float_format=None, sparsify=None, index_names=True, justify=None, bold_rows=True, classes=None, escape=True, max_rows=None, max_cols=None, show_dimensions=False, notebook=False, decimal='.', border=None)
Render a DataFrame as an HTML table.

-`to_html`-specific options:

-bold_rows : boolean, default True
-    Make the row labels bold in the output
-classes : str or list or tuple, default None
-    CSS class(es) to apply to the resulting html table
-escape : boolean, default True
-    Convert the characters <, >, and & to HTML-safe sequences.=
-max_rows : int, optional
-    Maximum number of rows to show before truncating. If None, show
-    all.
-max_cols : int, optional
-    Maximum number of columns to show before truncating. If None, show
-    all.
-decimal : string, default '.'
-    Character recognized as decimal separator, e.g. ',' in Europe

-    .. versionadded:: 0.18.0
-border : int
-    A ``border=border`` attribute is included in the opening
-    `<table>` tag. Default ``pd.options.html.border``.

-    .. versionadded:: 0.19.0

-Parameters
-----------
-buf : StringIO-like, optional
-    buffer to write to
-columns : sequence, optional
-    the subset of columns to write; default None writes all columns
-col_space : int, optional
-    the minimum width of each column
-header : bool, optional
-    whether to print column labels, default True
-index : bool, optional
-    whether to print index (row) labels, default True
-na_rep : string, optional
-    string representation of NAN to use, default 'NaN'
-formatters : list or dict of one-parameter functions, optional
-    formatter functions to apply to columns' elements by position or name,
-    default None. The result of each function must be a unicode string.
-    List must be of length equal to the number of columns.
-float_format : one-parameter function, optional
-    formatter function to apply to columns' elements if they are floats,
-    default None. The result of this function must be a unicode string.
-sparsify : bool, optional
-    Set to False for a DataFrame with a hierarchical index to print every
-    multiindex key at each row, default True
-index_names : bool, optional
-    Prints the names of the indexes, default True
-line_width : int, optional
-    Width to wrap a line in characters, default no wrap
-justify : {'left', 'right'}, default None
-    Left or right-justify the column labels. If None uses the option from
-    the print configuration (controlled by set_option), 'right' out
-    of the box.

-Returns
--------
-formatted : string (or unicode, depending on data and options)
- -
to_latex(self, buf=None, columns=None, col_space=None, header=True, index=True, na_rep='NaN', formatters=None, float_format=None, sparsify=None, index_names=True, bold_rows=True, column_format=None, longtable=None, escape=None, encoding=None, decimal='.', multicolumn=None, multicolumn_format=None, multirow=None)
Render a DataFrame to a tabular environment table. You can splice
-this into a LaTeX document. Requires \usepackage{booktabs}.

-`to_latex`-specific options:

-bold_rows : boolean, default True
-    Make the row labels bold in the output
-column_format : str, default None
-    The columns format as specified in `LaTeX table format
-    <https://en.wikibooks.org/wiki/LaTeX/Tables>`__ e.g 'rcl' for 3
-    columns
-longtable : boolean, default will be read from the pandas config module
-    Default: False.
-    Use a longtable environment instead of tabular. Requires adding
-    a \usepackage{longtable} to your LaTeX preamble.
-escape : boolean, default will be read from the pandas config module
-    Default: True.
-    When set to False prevents from escaping latex special
-    characters in column names.
-encoding : str, default None
-    A string representing the encoding to use in the output file,
-    defaults to 'ascii' on Python 2 and 'utf-8' on Python 3.
-decimal : string, default '.'
-    Character recognized as decimal separator, e.g. ',' in Europe.

-    .. versionadded:: 0.18.0

-multicolumn : boolean, default True
-    Use \multicolumn to enhance MultiIndex columns.
-    The default will be read from the config module.

-    .. versionadded:: 0.20.0

-multicolumn_format : str, default 'l'
-    The alignment for multicolumns, similar to `column_format`
-    The default will be read from the config module.

-    .. versionadded:: 0.20.0

-multirow : boolean, default False
-    Use \multirow to enhance MultiIndex rows.
-    Requires adding a \usepackage{multirow} to your LaTeX preamble.
-    Will print centered labels (instead of top-aligned)
-    across the contained rows, separating groups via clines.
-    The default will be read from the pandas config module.

-    .. versionadded:: 0.20.0


-Parameters
-----------
-buf : StringIO-like, optional
-    buffer to write to
-columns : sequence, optional
-    the subset of columns to write; default None writes all columns
-col_space : int, optional
-    the minimum width of each column
-header : bool, optional
-    Write out column names. If a list of string is given, it is assumed to be aliases for the column names.
-index : bool, optional
-    whether to print index (row) labels, default True
-na_rep : string, optional
-    string representation of NAN to use, default 'NaN'
-formatters : list or dict of one-parameter functions, optional
-    formatter functions to apply to columns' elements by position or name,
-    default None. The result of each function must be a unicode string.
-    List must be of length equal to the number of columns.
-float_format : one-parameter function, optional
-    formatter function to apply to columns' elements if they are floats,
-    default None. The result of this function must be a unicode string.
-sparsify : bool, optional
-    Set to False for a DataFrame with a hierarchical index to print every
-    multiindex key at each row, default True
-index_names : bool, optional
-    Prints the names of the indexes, default True
-line_width : int, optional
-    Width to wrap a line in characters, default no wrap

-Returns
--------
-formatted : string (or unicode, depending on data and options)
- -
to_panel(self)
Transform long (stacked) format (DataFrame) into wide (3D, Panel)
-format.

-Currently the index of the DataFrame must be a 2-level MultiIndex. This
-may be generalized later

-Returns
--------
-panel : Panel
- -
to_period(self, freq=None, axis=0, copy=True)
Convert DataFrame from DatetimeIndex to PeriodIndex with desired
-frequency (inferred from index if not passed)

-Parameters
-----------
-freq : string, default
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    The axis to convert (the index by default)
-copy : boolean, default True
-    If False then underlying input data is not copied

-Returns
--------
-ts : TimeSeries with PeriodIndex
- -
to_records(self, index=True, convert_datetime64=True)
Convert DataFrame to record array. Index will be put in the
-'index' field of the record array if requested

-Parameters
-----------
-index : boolean, default True
-    Include index in resulting record array, stored in 'index' field
-convert_datetime64 : boolean, default True
-    Whether to convert the index to datetime.datetime if it is a
-    DatetimeIndex

-Returns
--------
-y : recarray
- -
to_sparse(self, fill_value=None, kind='block')
Convert to SparseDataFrame

-Parameters
-----------
-fill_value : float, default NaN
-kind : {'block', 'integer'}

-Returns
--------
-y : SparseDataFrame
- -
to_stata(self, fname, convert_dates=None, write_index=True, encoding='latin-1', byteorder=None, time_stamp=None, data_label=None, variable_labels=None)
A class for writing Stata binary dta files from array-like objects

-Parameters
-----------
-fname : str or buffer
-    String path of file-like object
-convert_dates : dict
-    Dictionary mapping columns containing datetime types to stata
-    internal format to use when wirting the dates. Options are 'tc',
-    'td', 'tm', 'tw', 'th', 'tq', 'ty'. Column can be either an integer
-    or a name. Datetime columns that do not have a conversion type
-    specified will be converted to 'tc'. Raises NotImplementedError if
-    a datetime column has timezone information
-write_index : bool
-    Write the index to Stata dataset.
-encoding : str
-    Default is latin-1. Unicode is not supported
-byteorder : str
-    Can be ">", "<", "little", or "big". default is `sys.byteorder`
-time_stamp : datetime
-    A datetime to use as file creation date.  Default is the current
-    time.
-dataset_label : str
-    A label for the data set.  Must be 80 characters or smaller.
-variable_labels : dict
-    Dictionary containing columns as keys and variable labels as
-    values. Each label must be 80 characters or smaller.

-    .. versionadded:: 0.19.0

-Raises
-------
-NotImplementedError
-    * If datetimes contain timezone information
-    * Column dtype is not representable in Stata
-ValueError
-    * Columns listed in convert_dates are noth either datetime64[ns]
-      or datetime.datetime
-    * Column listed in convert_dates is not in DataFrame
-    * Categorical label contains more than 32,000 characters

-    .. versionadded:: 0.19.0

-Examples
---------
->>> writer = StataWriter('./data_file.dta', data)
->>> writer.write_file()

-Or with dates

->>> writer = StataWriter('./date_data_file.dta', data, {2 : 'tw'})
->>> writer.write_file()
- -
to_string(self, buf=None, columns=None, col_space=None, header=True, index=True, na_rep='NaN', formatters=None, float_format=None, sparsify=None, index_names=True, justify=None, line_width=None, max_rows=None, max_cols=None, show_dimensions=False)
Render a DataFrame to a console-friendly tabular output.

-Parameters
-----------
-buf : StringIO-like, optional
-    buffer to write to
-columns : sequence, optional
-    the subset of columns to write; default None writes all columns
-col_space : int, optional
-    the minimum width of each column
-header : bool, optional
-    Write out column names. If a list of string is given, it is assumed to be aliases for the column names
-index : bool, optional
-    whether to print index (row) labels, default True
-na_rep : string, optional
-    string representation of NAN to use, default 'NaN'
-formatters : list or dict of one-parameter functions, optional
-    formatter functions to apply to columns' elements by position or name,
-    default None. The result of each function must be a unicode string.
-    List must be of length equal to the number of columns.
-float_format : one-parameter function, optional
-    formatter function to apply to columns' elements if they are floats,
-    default None. The result of this function must be a unicode string.
-sparsify : bool, optional
-    Set to False for a DataFrame with a hierarchical index to print every
-    multiindex key at each row, default True
-index_names : bool, optional
-    Prints the names of the indexes, default True
-line_width : int, optional
-    Width to wrap a line in characters, default no wrap
-justify : {'left', 'right'}, default None
-    Left or right-justify the column labels. If None uses the option from
-    the print configuration (controlled by set_option), 'right' out
-    of the box.

-Returns
--------
-formatted : string (or unicode, depending on data and options)
- -
to_timestamp(self, freq=None, how='start', axis=0, copy=True)
Cast to DatetimeIndex of timestamps, at *beginning* of period

-Parameters
-----------
-freq : string, default frequency of PeriodIndex
-    Desired frequency
-how : {'s', 'e', 'start', 'end'}
-    Convention for converting period to timestamp; start of period
-    vs. end
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    The axis to convert (the index by default)
-copy : boolean, default True
-    If false then underlying input data is not copied

-Returns
--------
-df : DataFrame with DatetimeIndex
- -
transform(self, func, *args, **kwargs)
Call function producing a like-indexed NDFrame
-and return a NDFrame with the transformed values`

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    To apply to column

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Returns
--------
-transformed : NDFrame

-Examples
---------
->>> df = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
-...                   index=pd.date_range('1/1/2000', periods=10))
-df.iloc[3:7] = np.nan

->>> df.transform(lambda x: (x - x.mean()) / x.std())
-                   A         B         C
-2000-01-01  0.579457  1.236184  0.123424
-2000-01-02  0.370357 -0.605875 -1.231325
-2000-01-03  1.455756 -0.277446  0.288967
-2000-01-04       NaN       NaN       NaN
-2000-01-05       NaN       NaN       NaN
-2000-01-06       NaN       NaN       NaN
-2000-01-07       NaN       NaN       NaN
-2000-01-08 -0.498658  1.274522  1.642524
-2000-01-09 -0.540524 -1.012676 -0.828968
-2000-01-10 -1.366388 -0.614710  0.005378

-See also
---------
-pandas.NDFrame.aggregate
-pandas.NDFrame.apply
- -
transpose(self, *args, **kwargs)
Transpose index and columns
- -
truediv(self, other, axis='columns', level=None, fill_value=None)
Floating division of dataframe and other, element-wise (binary operator `truediv`).

-Equivalent to ``dataframe / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rtruediv
- -
unstack(self, level=-1, fill_value=None)
Pivot a level of the (necessarily hierarchical) index labels, returning
-a DataFrame having a new level of column labels whose inner-most level
-consists of the pivoted index labels. If the index is not a MultiIndex,
-the output will be a Series (the analogue of stack when the columns are
-not a MultiIndex).
-The level involved will automatically get sorted.

-Parameters
-----------
-level : int, string, or list of these, default -1 (last level)
-    Level(s) of index to unstack, can pass level name
-fill_value : replace NaN with this value if the unstack produces
-    missing values

-    .. versionadded: 0.18.0

-See also
---------
-DataFrame.pivot : Pivot a table based on column values.
-DataFrame.stack : Pivot a level of the column labels (inverse operation
-    from `unstack`).

-Examples
---------
->>> index = pd.MultiIndex.from_tuples([('one', 'a'), ('one', 'b'),
-...                                    ('two', 'a'), ('two', 'b')])
->>> s = pd.Series(np.arange(1.0, 5.0), index=index)
->>> s
-one  a   1.0
-     b   2.0
-two  a   3.0
-     b   4.0
-dtype: float64

->>> s.unstack(level=-1)
-     a   b
-one  1.0  2.0
-two  3.0  4.0

->>> s.unstack(level=0)
-   one  two
-a  1.0   3.0
-b  2.0   4.0

->>> df = s.unstack(level=0)
->>> df.unstack()
-one  a  1.0
-     b  2.0
-two  a  3.0
-     b  4.0
-dtype: float64

-Returns
--------
-unstacked : DataFrame or Series
- -
update(self, other, join='left', overwrite=True, filter_func=None, raise_conflict=False)
Modify DataFrame in place using non-NA values from passed
-DataFrame. Aligns on indices

-Parameters
-----------
-other : DataFrame, or object coercible into a DataFrame
-join : {'left'}, default 'left'
-overwrite : boolean, default True
-    If True then overwrite values for common keys in the calling frame
-filter_func : callable(1d-array) -> 1d-array<boolean>, default None
-    Can choose to replace values other than NA. Return True for values
-    that should be updated
-raise_conflict : boolean
-    If True, will raise an error if the DataFrame and other both
-    contain data in the same place.
- -
var(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased variance over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-var : Series or DataFrame (if level specified)
- -
-Class methods inherited from pandas.core.frame.DataFrame:
-
from_csv(path, header=0, sep=',', index_col=0, parse_dates=True, encoding=None, tupleize_cols=False, infer_datetime_format=False) from builtins.type
Read CSV file (DISCOURAGED, please use :func:`pandas.read_csv`
-instead).

-It is preferable to use the more powerful :func:`pandas.read_csv`
-for most general purposes, but ``from_csv`` makes for an easy
-roundtrip to and from a file (the exact counterpart of
-``to_csv``), especially with a DataFrame of time series data.

-This method only differs from the preferred :func:`pandas.read_csv`
-in some defaults:

-- `index_col` is ``0`` instead of ``None`` (take first column as index
-  by default)
-- `parse_dates` is ``True`` instead of ``False`` (try parsing the index
-  as datetime by default)

-So a ``pd.DataFrame.from_csv(path)`` can be replaced by
-``pd.read_csv(path, index_col=0, parse_dates=True)``.

-Parameters
-----------
-path : string file path or file handle / StringIO
-header : int, default 0
-    Row to use as header (skip prior rows)
-sep : string, default ','
-    Field delimiter
-index_col : int or sequence, default 0
-    Column to use for index. If a sequence is given, a MultiIndex
-    is used. Different default from read_table
-parse_dates : boolean, default True
-    Parse dates. Different default from read_table
-tupleize_cols : boolean, default False
-    write multi_index columns as a list of tuples (if True)
-    or new (expanded format) if False)
-infer_datetime_format: boolean, default False
-    If True and `parse_dates` is True for a column, try to infer the
-    datetime format based on the first datetime string. If the format
-    can be inferred, there often will be a large parsing speed-up.

-See also
---------
-pandas.read_csv

-Returns
--------
-y : DataFrame
- -
from_dict(data, orient='columns', dtype=None) from builtins.type
Construct DataFrame from dict of array-like or dicts

-Parameters
-----------
-data : dict
-    {field : array-like} or {field : dict}
-orient : {'columns', 'index'}, default 'columns'
-    The "orientation" of the data. If the keys of the passed dict
-    should be the columns of the resulting DataFrame, pass 'columns'
-    (default). Otherwise if the keys should be rows, pass 'index'.
-dtype : dtype, default None
-    Data type to force, otherwise infer

-Returns
--------
-DataFrame
- -
from_items(items, columns=None, orient='columns') from builtins.type
Convert (key, value) pairs to DataFrame. The keys will be the axis
-index (usually the columns, but depends on the specified
-orientation). The values should be arrays or Series.

-Parameters
-----------
-items : sequence of (key, value) pairs
-    Values should be arrays or Series.
-columns : sequence of column labels, optional
-    Must be passed if orient='index'.
-orient : {'columns', 'index'}, default 'columns'
-    The "orientation" of the data. If the keys of the
-    input correspond to column labels, pass 'columns'
-    (default). Otherwise if the keys correspond to the index,
-    pass 'index'.

-Returns
--------
-frame : DataFrame
- -
from_records(data, index=None, exclude=None, columns=None, coerce_float=False, nrows=None) from builtins.type
Convert structured or record ndarray to DataFrame

-Parameters
-----------
-data : ndarray (structured dtype), list of tuples, dict, or DataFrame
-index : string, list of fields, array-like
-    Field of array to use as the index, alternately a specific set of
-    input labels to use
-exclude : sequence, default None
-    Columns or fields to exclude
-columns : sequence, default None
-    Column names to use. If the passed data do not have names
-    associated with them, this argument provides names for the
-    columns. Otherwise this argument indicates the order of the columns
-    in the result (any names not found in the data will become all-NA
-    columns)
-coerce_float : boolean, default False
-    Attempt to convert values of non-string, non-numeric objects (like
-    decimal.Decimal) to floating point, useful for SQL result sets

-Returns
--------
-df : DataFrame
- -
-Data descriptors inherited from pandas.core.frame.DataFrame:
-
axes
-
Return a list with the row axis labels and column axis labels as the
-only members. They are returned in that order.
-
-
columns
-
-
index
-
-
shape
-
Return a tuple representing the dimensionality of the DataFrame.
-
-
style
-
Property returning a Styler object containing methods for
-building a styled HTML representation fo the DataFrame.

-See Also
---------
-pandas.io.formats.style.Styler
-
-
-Data and other attributes inherited from pandas.core.frame.DataFrame:
-
plot = <class 'pandas.plotting._core.FramePlotMethods'>
DataFrame plotting accessor and method

-Examples
---------
->>> df.plot.line()
->>> df.plot.scatter('x', 'y')
->>> df.plot.hexbin()

-These plotting methods can also be accessed by calling the accessor as a
-method with the ``kind`` argument:
-``df.plot(kind='line')`` is equivalent to ``df.plot.line()``
- -
-Methods inherited from pandas.core.generic.NDFrame:
-
__abs__(self)
- -
__array__(self, dtype=None)
- -
__array_wrap__(self, result, context=None)
- -
__bool__ = __nonzero__(self)
- -
__contains__(self, key)
True if the key is in the info axis
- -
__copy__(self, deep=True)
- -
__deepcopy__(self, memo=None)
- -
__delitem__(self, key)
Delete item
- -
__finalize__(self, other, method=None, **kwargs)
Propagate metadata from other to self.

-Parameters
-----------
-other : the object from which to get the attributes that we are going
-    to propagate
-method : optional, a passed method name ; possibly to take different
-    types of propagation actions based on this
- -
__getattr__(self, name)
After regular attribute access, try looking up the name
-This allows simpler access to columns for interactive use.
- -
__getstate__(self)
- -
__hash__(self)
Return hash(self).
- -
__invert__(self)
- -
__iter__(self)
Iterate over infor axis
- -
__neg__(self)
- -
__nonzero__(self)
- -
__round__(self, decimals=0)
- -
__setattr__(self, name, value)
After regular attribute access, try setting the name
-This allows simpler access to columns for interactive use.
- -
__setstate__(self, state)
- -
abs(self)
Return an object with absolute value taken--only applicable to objects
-that are all numeric.

-Returns
--------
-abs: type of caller
- -
add_prefix(self, prefix)
Concatenate prefix string with panel items names.

-Parameters
-----------
-prefix : string

-Returns
--------
-with_prefix : type of caller
- -
add_suffix(self, suffix)
Concatenate suffix string with panel items names.

-Parameters
-----------
-suffix : string

-Returns
--------
-with_suffix : type of caller
- -
as_blocks(self, copy=True)
Convert the frame to a dict of dtype -> Constructor Types that each has
-a homogeneous dtype.

-NOTE: the dtypes of the blocks WILL BE PRESERVED HERE (unlike in
-      as_matrix)

-Parameters
-----------
-copy : boolean, default True

-       .. versionadded: 0.16.1

-Returns
--------
-values : a dict of dtype -> Constructor Types
- -
as_matrix(self, columns=None)
Convert the frame to its Numpy-array representation.

-Parameters
-----------
-columns: list, optional, default:None
-    If None, return all columns, otherwise, returns specified columns.

-Returns
--------
-values : ndarray
-    If the caller is heterogeneous and contains booleans or objects,
-    the result will be of dtype=object. See Notes.


-Notes
------
-Return is NOT a Numpy-matrix, rather, a Numpy-array.

-The dtype will be a lower-common-denominator dtype (implicit
-upcasting); that is to say if the dtypes (even of numeric types)
-are mixed, the one that accommodates all will be chosen. Use this
-with care if you are not dealing with the blocks.

-e.g. If the dtypes are float16 and float32, dtype will be upcast to
-float32.  If dtypes are int32 and uint8, dtype will be upcase to
-int32. By numpy.find_common_type convention, mixing int64 and uint64
-will result in a flot64 dtype.

-This method is provided for backwards compatibility. Generally,
-it is recommended to use '.values'.

-See Also
---------
-pandas.DataFrame.values
- -
asfreq(self, freq, method=None, how=None, normalize=False, fill_value=None)
Convert TimeSeries to specified frequency.

-Optionally provide filling method to pad/backfill missing values.

-Returns the original data conformed to a new index with the specified
-frequency. ``resample`` is more appropriate if an operation, such as
-summarization, is necessary to represent the data at the new frequency.

-Parameters
-----------
-freq : DateOffset object, or string
-method : {'backfill'/'bfill', 'pad'/'ffill'}, default None
-    Method to use for filling holes in reindexed Series (note this
-    does not fill NaNs that already were present):

-    * 'pad' / 'ffill': propagate last valid observation forward to next
-      valid
-    * 'backfill' / 'bfill': use NEXT valid observation to fill
-how : {'start', 'end'}, default end
-    For PeriodIndex only, see PeriodIndex.asfreq
-normalize : bool, default False
-    Whether to reset output index to midnight
-fill_value: scalar, optional
-    Value to use for missing values, applied during upsampling (note
-    this does not fill NaNs that already were present).

-    .. versionadded:: 0.20.0

-Returns
--------
-converted : type of caller

-Examples
---------

-Start by creating a series with 4 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=4, freq='T')
->>> series = pd.Series([0.0, None, 2.0, 3.0], index=index)
->>> df = pd.DataFrame({'s':series})
->>> df
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:03:00    3.0

-Upsample the series into 30 second bins.

->>> df.asfreq(freq='30S')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    NaN
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``fill value``.

->>> df.asfreq(freq='30S', fill_value=9.0)
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    9.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    9.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    9.0
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``method``.

->>> df.asfreq(freq='30S', method='bfill')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    2.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    3.0
-2000-01-01 00:03:00    3.0

-See Also
---------
-reindex

-Notes
------
-To learn more about the frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
- -
asof(self, where, subset=None)
The last row without any NaN is taken (or the last row without
-NaN considering only the subset of columns in the case of a DataFrame)

-.. versionadded:: 0.19.0 For DataFrame

-If there is no good value, NaN is returned for a Series
-a Series of NaN values for a DataFrame

-Parameters
-----------
-where : date or array of dates
-subset : string or list of strings, default None
-   if not None use these columns for NaN propagation

-Notes
------
-Dates are assumed to be sorted
-Raises if this is not the case

-Returns
--------
-where is scalar

-  - value or NaN if input is Series
-  - Series if input is DataFrame

-where is Index: same shape object as input

-See Also
---------
-merge_asof
- -
astype(self, dtype, copy=True, errors='raise', **kwargs)
Cast object to input numpy.dtype
-Return a copy when copy = True (be really careful with this!)

-Parameters
-----------
-dtype : data type, or dict of column name -> data type
-    Use a numpy.dtype or Python type to cast entire pandas object to
-    the same type. Alternatively, use {col: dtype, ...}, where col is a
-    column label and dtype is a numpy.dtype or Python type to cast one
-    or more of the DataFrame's columns to column-specific types.
-errors : {'raise', 'ignore'}, default 'raise'.
-    Control raising of exceptions on invalid data for provided dtype.

-    - ``raise`` : allow exceptions to be raised
-    - ``ignore`` : suppress exceptions. On error return original object

-    .. versionadded:: 0.20.0

-raise_on_error : DEPRECATED use ``errors`` instead
-kwargs : keyword arguments to pass on to the constructor

-Returns
--------
-casted : type of caller
- -
at_time(self, time, asof=False)
Select values at particular time of day (e.g. 9:30AM).

-Parameters
-----------
-time : datetime.time or string

-Returns
--------
-values_at_time : type of caller
- -
between_time(self, start_time, end_time, include_start=True, include_end=True)
Select values between particular times of the day (e.g., 9:00-9:30 AM).

-Parameters
-----------
-start_time : datetime.time or string
-end_time : datetime.time or string
-include_start : boolean, default True
-include_end : boolean, default True

-Returns
--------
-values_between_time : type of caller
- -
bfill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='bfill') <DataFrame.fillna>`
- -
bool(self)
Return the bool of a single element PandasObject.

-This must be a boolean scalar value, either True or False.  Raise a
-ValueError if the PandasObject does not have exactly 1 element, or that
-element is not boolean
- -
clip(self, lower=None, upper=None, axis=None, *args, **kwargs)
Trim values at input threshold(s).

-Parameters
-----------
-lower : float or array_like, default None
-upper : float or array_like, default None
-axis : int or string axis name, optional
-    Align object with lower and upper along the given axis.

-Returns
--------
-clipped : Series

-Examples
---------
->>> df
-  0         1
-0  0.335232 -1.256177
-1 -1.367855  0.746646
-2  0.027753 -1.176076
-3  0.230930 -0.679613
-4  1.261967  0.570967
->>> df.clip(-1.0, 0.5)
-          0         1
-0  0.335232 -1.000000
-1 -1.000000  0.500000
-2  0.027753 -1.000000
-3  0.230930 -0.679613
-4  0.500000  0.500000
->>> t
-0   -0.3
-1   -0.2
-2   -0.1
-3    0.0
-4    0.1
-dtype: float64
->>> df.clip(t, t + 1, axis=0)
-          0         1
-0  0.335232 -0.300000
-1 -0.200000  0.746646
-2  0.027753 -0.100000
-3  0.230930  0.000000
-4  1.100000  0.570967
- -
clip_lower(self, threshold, axis=None)
Return copy of the input with values below given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
clip_upper(self, threshold, axis=None)
Return copy of input with values above given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
consolidate(self, inplace=False)
DEPRECATED: consolidate will be an internal implementation only.
- -
convert_objects(self, convert_dates=True, convert_numeric=False, convert_timedeltas=True, copy=True)
Deprecated.

-Attempt to infer better dtype for object columns

-Parameters
-----------
-convert_dates : boolean, default True
-    If True, convert to date where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-convert_numeric : boolean, default False
-    If True, attempt to coerce to numbers (including strings), with
-    unconvertible values becoming NaN.
-convert_timedeltas : boolean, default True
-    If True, convert to timedelta where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-copy : boolean, default True
-    If True, return a copy even if no copy is necessary (e.g. no
-    conversion was done). Note: This is meant for internal use, and
-    should not be confused with inplace.

-See Also
---------
-pandas.to_datetime : Convert argument to datetime.
-pandas.to_timedelta : Convert argument to timedelta.
-pandas.to_numeric : Return a fixed frequency timedelta index,
-    with day as the default.

-Returns
--------
-converted : same as input object
- -
copy(self, deep=True)
Make a copy of this objects data.

-Parameters
-----------
-deep : boolean or string, default True
-    Make a deep copy, including a copy of the data and the indices.
-    With ``deep=False`` neither the indices or the data are copied.

-    Note that when ``deep=True`` data is copied, actual python objects
-    will not be copied recursively, only the reference to the object.
-    This is in contrast to ``copy.deepcopy`` in the Standard Library,
-    which recursively copies object data.

-Returns
--------
-copy : type of caller
- -
describe(self, percentiles=None, include=None, exclude=None)
Generates descriptive statistics that summarize the central tendency,
-dispersion and shape of a dataset's distribution, excluding
-``NaN`` values.

-Analyzes both numeric and object series, as well
-as ``DataFrame`` column sets of mixed data types. The output
-will vary depending on what is provided. Refer to the notes
-below for more detail.

-Parameters
-----------
-percentiles : list-like of numbers, optional
-    The percentiles to include in the output. All should
-    fall between 0 and 1. The default is
-    ``[.25, .5, .75]``, which returns the 25th, 50th, and
-    75th percentiles.
-include : 'all', list-like of dtypes or None (default), optional
-    A white list of data types to include in the result. Ignored
-    for ``Series``. Here are the options:

-    - 'all' : All columns of the input will be included in the output.
-    - A list-like of dtypes : Limits the results to the
-      provided data types.
-      To limit the result to numeric types submit
-      ``numpy.number``. To limit it instead to categorical
-      objects submit the ``numpy.object`` data type. Strings
-      can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will include all numeric columns.
-exclude : list-like of dtypes or None (default), optional,
-    A black list of data types to omit from the result. Ignored
-    for ``Series``. Here are the options:

-    - A list-like of dtypes : Excludes the provided data types
-      from the result. To select numeric types submit
-      ``numpy.number``. To select categorical objects submit the data
-      type ``numpy.object``. Strings can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will exclude nothing.

-Returns
--------
-summary:  Series/DataFrame of summary statistics

-Notes
------
-For numeric data, the result's index will include ``count``,
-``mean``, ``std``, ``min``, ``max`` as well as lower, ``50`` and
-upper percentiles. By default the lower percentile is ``25`` and the
-upper percentile is ``75``. The ``50`` percentile is the
-same as the median.

-For object data (e.g. strings or timestamps), the result's index
-will include ``count``, ``unique``, ``top``, and ``freq``. The ``top``
-is the most common value. The ``freq`` is the most common value's
-frequency. Timestamps also include the ``first`` and ``last`` items.

-If multiple object values have the highest count, then the
-``count`` and ``top`` results will be arbitrarily chosen from
-among those with the highest count.

-For mixed data types provided via a ``DataFrame``, the default is to
-return only an analysis of numeric columns. If ``include='all'``
-is provided as an option, the result will include a union of
-attributes of each type.

-The `include` and `exclude` parameters can be used to limit
-which columns in a ``DataFrame`` are analyzed for the output.
-The parameters are ignored when analyzing a ``Series``.

-Examples
---------
-Describing a numeric ``Series``.

->>> s = pd.Series([1, 2, 3])
->>> s.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0

-Describing a categorical ``Series``.

->>> s = pd.Series(['a', 'a', 'b', 'c'])
->>> s.describe()
-count     4
-unique    3
-top       a
-freq      2
-dtype: object

-Describing a timestamp ``Series``.

->>> s = pd.Series([
-...   np.datetime64("2000-01-01"),
-...   np.datetime64("2010-01-01"),
-...   np.datetime64("2010-01-01")
-... ])
->>> s.describe()
-count                       3
-unique                      2
-top       2010-01-01 00:00:00
-freq                        2
-first     2000-01-01 00:00:00
-last      2010-01-01 00:00:00
-dtype: object

-Describing a ``DataFrame``. By default only numeric fields
-are returned.

->>> df = pd.DataFrame([[1, 'a'], [2, 'b'], [3, 'c']],
-...                   columns=['numeric', 'object'])
->>> df.describe()
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Describing all columns of a ``DataFrame`` regardless of data type.

->>> df.describe(include='all')
-        numeric object
-count       3.0      3
-unique      NaN      3
-top         NaN      b
-freq        NaN      1
-mean        2.0    NaN
-std         1.0    NaN
-min         1.0    NaN
-25%         1.5    NaN
-50%         2.0    NaN
-75%         2.5    NaN
-max         3.0    NaN

-Describing a column from a ``DataFrame`` by accessing it as
-an attribute.

->>> df.numeric.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0
-Name: numeric, dtype: float64

-Including only numeric columns in a ``DataFrame`` description.

->>> df.describe(include=[np.number])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Including only string columns in a ``DataFrame`` description.

->>> df.describe(include=[np.object])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding numeric columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.number])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding object columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.object])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-See Also
---------
-DataFrame.count
-DataFrame.max
-DataFrame.min
-DataFrame.mean
-DataFrame.std
-DataFrame.select_dtypes
- -
drop(self, labels, axis=0, level=None, inplace=False, errors='raise')
Return new object with labels in requested axis removed.

-Parameters
-----------
-labels : single label or list-like
-axis : int or axis name
-level : int or level name, default None
-    For MultiIndex
-inplace : bool, default False
-    If True, do operation inplace and return None.
-errors : {'ignore', 'raise'}, default 'raise'
-    If 'ignore', suppress error and existing labels are dropped.

-    .. versionadded:: 0.16.1

-Returns
--------
-dropped : type of caller
- -
equals(self, other)
Determines if two NDFrame objects contain the same elements. NaNs in
-the same location are considered equal.
- -
ffill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='ffill') <DataFrame.fillna>`
- -
filter(self, items=None, like=None, regex=None, axis=None)
Subset rows or columns of dataframe according to labels in
-the specified index.

-Note that this routine does not filter a dataframe on its
-contents. The filter is applied to the labels of the index.

-Parameters
-----------
-items : list-like
-    List of info axis to restrict to (must not all be present)
-like : string
-    Keep info axis where "arg in col == True"
-regex : string (regular expression)
-    Keep info axis with re.search(regex, col) == True
-axis : int or string axis name
-    The axis to filter on.  By default this is the info axis,
-    'index' for Series, 'columns' for DataFrame

-Returns
--------
-same type as input object

-Examples
---------
->>> df
-one  two  three
-mouse     1    2      3
-rabbit    4    5      6

->>> # select columns by name
->>> df.filter(items=['one', 'three'])
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select columns by regular expression
->>> df.filter(regex='e$', axis=1)
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select rows containing 'bbi'
->>> df.filter(like='bbi', axis=0)
-one  two  three
-rabbit    4    5      6

-See Also
---------
-pandas.DataFrame.select

-Notes
------
-The ``items``, ``like``, and ``regex`` parameters are
-enforced to be mutually exclusive.

-``axis`` defaults to the info axis that is used when indexing
-with ``[]``.
- -
first(self, offset)
Convenience method for subsetting initial periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.first('10D') -> First 10 days

-Returns
--------
-subset : type of caller
- -
get(self, key, default=None)
Get item from object for given key (DataFrame column, Panel slice,
-etc.). Returns default value if not found.

-Parameters
-----------
-key : object

-Returns
--------
-value : type of items contained in object
- -
get_dtype_counts(self)
Return the counts of dtypes in this object.
- -
get_ftype_counts(self)
Return the counts of ftypes in this object.
- -
get_values(self)
same as values (but handles sparseness conversions)
- -
groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, **kwargs)
Group series using mapper (dict or key function, apply given function
-to group, return result as series) or by a series of columns.

-Parameters
-----------
-by : mapping, function, str, or iterable
-    Used to determine the groups for the groupby.
-    If ``by`` is a function, it's called on each value of the object's
-    index. If a dict or Series is passed, the Series or dict VALUES
-    will be used to determine the groups (the Series' values are first
-    aligned; see ``.align()`` method). If an ndarray is passed, the
-    values are used as-is determine the groups. A str or list of strs
-    may be passed to group by the columns in ``self``
-axis : int, default 0
-level : int, level name, or sequence of such, default None
-    If the axis is a MultiIndex (hierarchical), group by a particular
-    level or levels
-as_index : boolean, default True
-    For aggregated output, return object with group labels as the
-    index. Only relevant for DataFrame input. as_index=False is
-    effectively "SQL-style" grouped output
-sort : boolean, default True
-    Sort group keys. Get better performance by turning this off.
-    Note this does not influence the order of observations within each
-    group.  groupby preserves the order of rows within each group.
-group_keys : boolean, default True
-    When calling apply, add group keys to index to identify pieces
-squeeze : boolean, default False
-    reduce the dimensionality of the return type if possible,
-    otherwise return a consistent type

-Examples
---------
-DataFrame results

->>> data.groupby(func, axis=0).mean()
->>> data.groupby(['col1', 'col2'])['col3'].mean()

-DataFrame with hierarchical index

->>> data.groupby(['col1', 'col2']).mean()

-Returns
--------
-GroupBy object
- -
head(self, n=5)
Returns first n rows
- -
interpolate(self, method='linear', axis=0, limit=None, inplace=False, limit_direction='forward', downcast=None, **kwargs)
Interpolate values according to different methods.

-Please note that only ``method='linear'`` is supported for
-DataFrames/Series with a MultiIndex.

-Parameters
-----------
-method : {'linear', 'time', 'index', 'values', 'nearest', 'zero',
-          'slinear', 'quadratic', 'cubic', 'barycentric', 'krogh',
-          'polynomial', 'spline', 'piecewise_polynomial',
-          'from_derivatives', 'pchip', 'akima'}

-    * 'linear': ignore the index and treat the values as equally
-      spaced. This is the only method supported on MultiIndexes.
-      default
-    * 'time': interpolation works on daily and higher resolution
-      data to interpolate given length of interval
-    * 'index', 'values': use the actual numerical values of the index
-    * 'nearest', 'zero', 'slinear', 'quadratic', 'cubic',
-      'barycentric', 'polynomial' is passed to
-      ``scipy.interpolate.interp1d``. Both 'polynomial' and 'spline'
-      require that you also specify an `order` (int),
-      e.g. df.interpolate(method='polynomial', order=4).
-      These use the actual numerical values of the index.
-    * 'krogh', 'piecewise_polynomial', 'spline', 'pchip' and 'akima'
-      are all wrappers around the scipy interpolation methods of
-      similar names. These use the actual numerical values of the
-      index. For more information on their behavior, see the
-      `scipy documentation
-      <http://docs.scipy.org/doc/scipy/reference/interpolate.html#univariate-interpolation>`__
-      and `tutorial documentation
-      <http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html>`__
-    * 'from_derivatives' refers to BPoly.from_derivatives which
-      replaces 'piecewise_polynomial' interpolation method in
-      scipy 0.18

-    .. versionadded:: 0.18.1

-       Added support for the 'akima' method
-       Added interpolate method 'from_derivatives' which replaces
-       'piecewise_polynomial' in scipy 0.18; backwards-compatible with
-       scipy < 0.18

-axis : {0, 1}, default 0
-    * 0: fill column-by-column
-    * 1: fill row-by-row
-limit : int, default None.
-    Maximum number of consecutive NaNs to fill. Must be greater than 0.
-limit_direction : {'forward', 'backward', 'both'}, default 'forward'
-    If limit is specified, consecutive NaNs will be filled in this
-    direction.

-    .. versionadded:: 0.17.0

-inplace : bool, default False
-    Update the NDFrame in place if possible.
-downcast : optional, 'infer' or None, defaults to None
-    Downcast dtypes if possible.
-kwargs : keyword arguments to pass on to the interpolating function.

-Returns
--------
-Series or DataFrame of same shape interpolated at the NaNs

-See Also
---------
-reindex, replace, fillna

-Examples
---------

-Filling in NaNs

->>> s = pd.Series([0, 1, np.nan, 3])
->>> s.interpolate()
-0    0
-1    1
-2    2
-3    3
-dtype: float64
- -
isnull(self)
Return a boolean same-sized object indicating if the values are null.

-See Also
---------
-notnull : boolean inverse of isnull
- -
keys(self)
Get the 'info axis' (see Indexing for more)

-This is index for Series, columns for DataFrame and major_axis for
-Panel.
- -
last(self, offset)
Convenience method for subsetting final periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.last('5M') -> Last 5 months

-Returns
--------
-subset : type of caller
- -
mask(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is False and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The mask method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``False`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``mask`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.where`
- -
notnull(self)
Return a boolean same-sized object indicating if the values are
-not null.

-See Also
---------
-isnull : boolean inverse of notnull
- -
pct_change(self, periods=1, fill_method='pad', limit=None, freq=None, **kwargs)
Percent change over given number of periods.

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming percent change
-fill_method : str, default 'pad'
-    How to handle NAs before computing percent changes
-limit : int, default None
-    The number of consecutive NAs to fill before stopping
-freq : DateOffset, timedelta, or offset alias string, optional
-    Increment to use from time series API (e.g. 'M' or BDay())

-Returns
--------
-chg : NDFrame

-Notes
------

-By default, the percentage change is calculated along the stat
-axis: 0, or ``Index``, for ``DataFrame`` and 1, or ``minor`` for
-``Panel``. You can change this with the ``axis`` keyword argument.
- -
pipe(self, func, *args, **kwargs)
Apply func(self, \*args, \*\*kwargs)

-.. versionadded:: 0.16.2

-Parameters
-----------
-func : function
-    function to apply to the NDFrame.
-    ``args``, and ``kwargs`` are passed into ``func``.
-    Alternatively a ``(callable, data_keyword)`` tuple where
-    ``data_keyword`` is a string indicating the keyword of
-    ``callable`` that expects the NDFrame.
-args : positional arguments passed into ``func``.
-kwargs : a dictionary of keyword arguments passed into ``func``.

-Returns
--------
-object : the return type of ``func``.

-Notes
------

-Use ``.pipe`` when chaining together functions that expect
-on Series or DataFrames. Instead of writing

->>> f(g(h(df), arg1=a), arg2=b, arg3=c)

-You can write

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe(f, arg2=b, arg3=c)
-... )

-If you have a function that takes the data as (say) the second
-argument, pass a tuple indicating which keyword expects the
-data. For example, suppose ``f`` takes its data as ``arg2``:

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe((f, 'arg2'), arg1=a, arg3=c)
-...  )

-See Also
---------
-pandas.DataFrame.apply
-pandas.DataFrame.applymap
-pandas.Series.map
- -
pop(self, item)
Return item and drop from frame. Raise KeyError if not found.
- -
rank(self, axis=0, method='average', numeric_only=None, na_option='keep', ascending=True, pct=False)
Compute numerical data ranks (1 through n) along axis. Equal values are
-assigned a rank that is the average of the ranks of those values

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    index to direct ranking
-method : {'average', 'min', 'max', 'first', 'dense'}
-    * average: average rank of group
-    * min: lowest rank in group
-    * max: highest rank in group
-    * first: ranks assigned in order they appear in the array
-    * dense: like 'min', but rank always increases by 1 between groups
-numeric_only : boolean, default None
-    Include only float, int, boolean data. Valid only for DataFrame or
-    Panel objects
-na_option : {'keep', 'top', 'bottom'}
-    * keep: leave NA values where they are
-    * top: smallest rank if ascending
-    * bottom: smallest rank if descending
-ascending : boolean, default True
-    False for ranks by high (1) to low (N)
-pct : boolean, default False
-    Computes percentage rank of data

-Returns
--------
-ranks : same type as caller
- -
reindex_like(self, other, method=None, copy=True, limit=None, tolerance=None)
Return an object with matching indices to myself.

-Parameters
-----------
-other : Object
-method : string or None
-copy : boolean, default True
-limit : int, default None
-    Maximum number of consecutive labels to fill for inexact matches.
-tolerance : optional
-    Maximum distance between labels of the other object and this
-    object for inexact matches.

-    .. versionadded:: 0.17.0

-Notes
------
-Like calling s.reindex(index=other.index, columns=other.columns,
-                       method=...)

-Returns
--------
-reindexed : same as input
- -
rename_axis(self, mapper, axis=0, copy=True, inplace=False)
Alter index and / or columns using input function or functions.
-A scalar or list-like for ``mapper`` will alter the ``Index.name``
-or ``MultiIndex.names`` attribute.
-A function or dict for ``mapper`` will alter the labels.
-Function / dict values must be unique (1-to-1). Labels not contained in
-a dict / Series will be left as-is.

-Parameters
-----------
-mapper : scalar, list-like, dict-like or function, optional
-axis : int or string, default 0
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False

-Returns
--------
-renamed : type of caller

-See Also
---------
-pandas.NDFrame.rename
-pandas.Index.rename

-Examples
---------
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename_axis("foo")  # scalar, alters df.index.name
-     A  B
-foo
-0    1  4
-1    2  5
-2    3  6
->>> df.rename_axis(lambda x: 2 * x)  # function: alters labels
-   A  B
-0  1  4
-2  2  5
-4  3  6
->>> df.rename_axis({"A": "ehh", "C": "see"}, axis="columns")  # mapping
-   ehh  B
-0    1  4
-1    2  5
-2    3  6
- -
replace(self, to_replace=None, value=None, inplace=False, limit=None, regex=False, method='pad', axis=None)
Replace values given in 'to_replace' with 'value'.

-Parameters
-----------
-to_replace : str, regex, list, dict, Series, numeric, or None

-    * str or regex:

-        - str: string exactly matching `to_replace` will be replaced
-          with `value`
-        - regex: regexs matching `to_replace` will be replaced with
-          `value`

-    * list of str, regex, or numeric:

-        - First, if `to_replace` and `value` are both lists, they
-          **must** be the same length.
-        - Second, if ``regex=True`` then all of the strings in **both**
-          lists will be interpreted as regexs otherwise they will match
-          directly. This doesn't matter much for `value` since there
-          are only a few possible substitution regexes you can use.
-        - str and regex rules apply as above.

-    * dict:

-        - Nested dictionaries, e.g., {'a': {'b': nan}}, are read as
-          follows: look in column 'a' for the value 'b' and replace it
-          with nan. You can nest regular expressions as well. Note that
-          column names (the top-level dictionary keys in a nested
-          dictionary) **cannot** be regular expressions.
-        - Keys map to column names and values map to substitution
-          values. You can treat this as a special case of passing two
-          lists except that you are specifying the column to search in.

-    * None:

-        - This means that the ``regex`` argument must be a string,
-          compiled regular expression, or list, dict, ndarray or Series
-          of such elements. If `value` is also ``None`` then this
-          **must** be a nested dictionary or ``Series``.

-    See the examples section for examples of each of these.
-value : scalar, dict, list, str, regex, default None
-    Value to use to fill holes (e.g. 0), alternately a dict of values
-    specifying which value to use for each column (columns not in the
-    dict will not be filled). Regular expressions, strings and lists or
-    dicts of such objects are also allowed.
-inplace : boolean, default False
-    If True, in place. Note: this will modify any
-    other views on this object (e.g. a column form a DataFrame).
-    Returns the caller if this is True.
-limit : int, default None
-    Maximum size gap to forward or backward fill
-regex : bool or same types as `to_replace`, default False
-    Whether to interpret `to_replace` and/or `value` as regular
-    expressions. If this is ``True`` then `to_replace` *must* be a
-    string. Otherwise, `to_replace` must be ``None`` because this
-    parameter will be interpreted as a regular expression or a list,
-    dict, or array of regular expressions.
-method : string, optional, {'pad', 'ffill', 'bfill'}
-    The method to use when for replacement, when ``to_replace`` is a
-    ``list``.

-See Also
---------
-NDFrame.reindex
-NDFrame.asfreq
-NDFrame.fillna

-Returns
--------
-filled : NDFrame

-Raises
-------
-AssertionError
-    * If `regex` is not a ``bool`` and `to_replace` is not ``None``.
-TypeError
-    * If `to_replace` is a ``dict`` and `value` is not a ``list``,
-      ``dict``, ``ndarray``, or ``Series``
-    * If `to_replace` is ``None`` and `regex` is not compilable into a
-      regular expression or is a list, dict, ndarray, or Series.
-ValueError
-    * If `to_replace` and `value` are ``list`` s or ``ndarray`` s, but
-      they are not the same length.

-Notes
------
-* Regex substitution is performed under the hood with ``re.sub``. The
-  rules for substitution for ``re.sub`` are the same.
-* Regular expressions will only substitute on strings, meaning you
-  cannot provide, for example, a regular expression matching floating
-  point numbers and expect the columns in your frame that have a
-  numeric dtype to be matched. However, if those floating point numbers
-  *are* strings, then you can do this.
-* This method has *a lot* of options. You are encouraged to experiment
-  and play with this method to gain intuition about how it works.
- -
resample(self, rule, how=None, axis=0, fill_method=None, closed=None, label=None, convention='start', kind=None, loffset=None, limit=None, base=0, on=None, level=None)
Convenience method for frequency conversion and resampling of time
-series.  Object must have a datetime-like index (DatetimeIndex,
-PeriodIndex, or TimedeltaIndex), or pass datetime-like values
-to the on or level keyword.

-Parameters
-----------
-rule : string
-    the offset string or object representing target conversion
-axis : int, optional, default 0
-closed : {'right', 'left'}
-    Which side of bin interval is closed
-label : {'right', 'left'}
-    Which bin edge label to label bucket with
-convention : {'start', 'end', 's', 'e'}
-loffset : timedelta
-    Adjust the resampled time labels
-base : int, default 0
-    For frequencies that evenly subdivide 1 day, the "origin" of the
-    aggregated intervals. For example, for '5min' frequency, base could
-    range from 0 through 4. Defaults to 0
-on : string, optional
-    For a DataFrame, column to use instead of index for resampling.
-    Column must be datetime-like.

-    .. versionadded:: 0.19.0

-level : string or int, optional
-    For a MultiIndex, level (name or number) to use for
-    resampling.  Level must be datetime-like.

-    .. versionadded:: 0.19.0

-Notes
------
-To learn more about the offset strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-Examples
---------

-Start by creating a series with 9 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=9, freq='T')
->>> series = pd.Series(range(9), index=index)
->>> series
-2000-01-01 00:00:00    0
-2000-01-01 00:01:00    1
-2000-01-01 00:02:00    2
-2000-01-01 00:03:00    3
-2000-01-01 00:04:00    4
-2000-01-01 00:05:00    5
-2000-01-01 00:06:00    6
-2000-01-01 00:07:00    7
-2000-01-01 00:08:00    8
-Freq: T, dtype: int64

-Downsample the series into 3 minute bins and sum the values
-of the timestamps falling into a bin.

->>> series.resample('3T').sum()
-2000-01-01 00:00:00     3
-2000-01-01 00:03:00    12
-2000-01-01 00:06:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but label each
-bin using the right edge instead of the left. Please note that the
-value in the bucket used as the label is not included in the bucket,
-which it labels. For example, in the original series the
-bucket ``2000-01-01 00:03:00`` contains the value 3, but the summed
-value in the resampled bucket with the label``2000-01-01 00:03:00``
-does not include 3 (if it did, the summed value would be 6, not 3).
-To include this value close the right side of the bin interval as
-illustrated in the example below this one.

->>> series.resample('3T', label='right').sum()
-2000-01-01 00:03:00     3
-2000-01-01 00:06:00    12
-2000-01-01 00:09:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but close the right
-side of the bin interval.

->>> series.resample('3T', label='right', closed='right').sum()
-2000-01-01 00:00:00     0
-2000-01-01 00:03:00     6
-2000-01-01 00:06:00    15
-2000-01-01 00:09:00    15
-Freq: 3T, dtype: int64

-Upsample the series into 30 second bins.

->>> series.resample('30S').asfreq()[0:5] #select first 5 rows
-2000-01-01 00:00:00   0.0
-2000-01-01 00:00:30   NaN
-2000-01-01 00:01:00   1.0
-2000-01-01 00:01:30   NaN
-2000-01-01 00:02:00   2.0
-Freq: 30S, dtype: float64

-Upsample the series into 30 second bins and fill the ``NaN``
-values using the ``pad`` method.

->>> series.resample('30S').pad()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    0
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    1
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Upsample the series into 30 second bins and fill the
-``NaN`` values using the ``bfill`` method.

->>> series.resample('30S').bfill()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    1
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    2
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Pass a custom function via ``apply``

->>> def custom_resampler(array_like):
-...     return np.sum(array_like)+5

->>> series.resample('3T').apply(custom_resampler)
-2000-01-01 00:00:00     8
-2000-01-01 00:03:00    17
-2000-01-01 00:06:00    26
-Freq: 3T, dtype: int64

-For DataFrame objects, the keyword ``on`` can be used to specify the
-column instead of the index for resampling.

->>> df = pd.DataFrame(data=9*[range(4)], columns=['a', 'b', 'c', 'd'])
->>> df['time'] = pd.date_range('1/1/2000', periods=9, freq='T')
->>> df.resample('3T', on='time').sum()
-                     a  b  c  d
-time
-2000-01-01 00:00:00  0  3  6  9
-2000-01-01 00:03:00  0  3  6  9
-2000-01-01 00:06:00  0  3  6  9

-For a DataFrame with MultiIndex, the keyword ``level`` can be used to
-specify on level the resampling needs to take place.

->>> time = pd.date_range('1/1/2000', periods=5, freq='T')
->>> df2 = pd.DataFrame(data=10*[range(4)],
-                       columns=['a', 'b', 'c', 'd'],
-                       index=pd.MultiIndex.from_product([time, [1, 2]])
-                       )
->>> df2.resample('3T', level=0).sum()
-                     a  b   c   d
-2000-01-01 00:00:00  0  6  12  18
-2000-01-01 00:03:00  0  4   8  12
- -
sample(self, n=None, frac=None, replace=False, weights=None, random_state=None, axis=None)
Returns a random sample of items from an axis of object.

-.. versionadded:: 0.16.1

-Parameters
-----------
-n : int, optional
-    Number of items from axis to return. Cannot be used with `frac`.
-    Default = 1 if `frac` = None.
-frac : float, optional
-    Fraction of axis items to return. Cannot be used with `n`.
-replace : boolean, optional
-    Sample with or without replacement. Default = False.
-weights : str or ndarray-like, optional
-    Default 'None' results in equal probability weighting.
-    If passed a Series, will align with target object on index. Index
-    values in weights not found in sampled object will be ignored and
-    index values in sampled object not in weights will be assigned
-    weights of zero.
-    If called on a DataFrame, will accept the name of a column
-    when axis = 0.
-    Unless weights are a Series, weights must be same length as axis
-    being sampled.
-    If weights do not sum to 1, they will be normalized to sum to 1.
-    Missing values in the weights column will be treated as zero.
-    inf and -inf values not allowed.
-random_state : int or numpy.random.RandomState, optional
-    Seed for the random number generator (if int), or numpy RandomState
-    object.
-axis : int or string, optional
-    Axis to sample. Accepts axis number or name. Default is stat axis
-    for given data type (0 for Series and DataFrames, 1 for Panels).

-Returns
--------
-A new object of same type as caller.

-Examples
---------

-Generate an example ``Series`` and ``DataFrame``:

->>> s = pd.Series(np.random.randn(50))
->>> s.head()
-0   -0.038497
-1    1.820773
-2   -0.972766
-3   -1.598270
-4   -1.095526
-dtype: float64
->>> df = pd.DataFrame(np.random.randn(50, 4), columns=list('ABCD'))
->>> df.head()
-          A         B         C         D
-0  0.016443 -2.318952 -0.566372 -1.028078
-1 -1.051921  0.438836  0.658280 -0.175797
-2 -1.243569 -0.364626 -0.215065  0.057736
-3  1.768216  0.404512 -0.385604 -1.457834
-4  1.072446 -1.137172  0.314194 -0.046661

-Next extract a random sample from both of these objects...

-3 random elements from the ``Series``:

->>> s.sample(n=3)
-27   -0.994689
-55   -1.049016
-67   -0.224565
-dtype: float64

-And a random 10% of the ``DataFrame`` with replacement:

->>> df.sample(frac=0.1, replace=True)
-           A         B         C         D
-35  1.981780  0.142106  1.817165 -0.290805
-49 -1.336199 -0.448634 -0.789640  0.217116
-40  0.823173 -0.078816  1.009536  1.015108
-15  1.421154 -0.055301 -1.922594 -0.019696
-6  -0.148339  0.832938  1.787600 -1.383767
- -
select(self, crit, axis=0)
Return data corresponding to axis labels matching criteria

-Parameters
-----------
-crit : function
-    To be called on each index (label). Should return True or False
-axis : int

-Returns
--------
-selection : type of caller
- -
set_axis(self, axis, labels)
public verson of axis assignment
- -
slice_shift(self, periods=1, axis=0)
Equivalent to `shift` without copying data. The shifted data will
-not include the dropped periods and the shifted axis will be smaller
-than the original.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative

-Notes
------
-While the `slice_shift` is faster than `shift`, you may pay for it
-later during alignment.

-Returns
--------
-shifted : same type as caller
- -
squeeze(self, axis=None)
Squeeze length 1 dimensions.

-Parameters
-----------
-axis : None, integer or string axis name, optional
-    The axis to squeeze if 1-sized.

-    .. versionadded:: 0.20.0

-Returns
--------
-scalar if 1-sized, else original object
- -
swapaxes(self, axis1, axis2, copy=True)
Interchange axes and swap values axes appropriately

-Returns
--------
-y : same as input
- -
tail(self, n=5)
Returns last n rows
- -
take(self, indices, axis=0, convert=True, is_copy=True, **kwargs)
Analogous to ndarray.take

-Parameters
-----------
-indices : list / array of ints
-axis : int, default 0
-convert : translate neg to pos indices (default)
-is_copy : mark the returned frame as a copy

-Returns
--------
-taken : type of caller
- -
to_clipboard(self, excel=None, sep=None, **kwargs)
Attempt to write text representation of object to the system clipboard
-This can be pasted into Excel, for example.

-Parameters
-----------
-excel : boolean, defaults to True
-        if True, use the provided separator, writing in a csv
-        format for allowing easy pasting into excel.
-        if False, write a string representation of the object
-        to the clipboard
-sep : optional, defaults to tab
-other keywords are passed to to_csv

-Notes
------
-Requirements for your platform
-  - Linux: xclip, or xsel (with gtk or PyQt4 modules)
-  - Windows: none
-  - OS X: none
- -
to_dense(self)
Return dense representation of NDFrame (as opposed to sparse)
- -
to_hdf(self, path_or_buf, key, **kwargs)
Write the contained data to an HDF5 file using HDFStore.

-Parameters
-----------
-path_or_buf : the path (string) or HDFStore object
-key : string
-    identifier for the group in the store
-mode : optional, {'a', 'w', 'r+'}, default 'a'

-  ``'w'``
-      Write; a new file is created (an existing file with the same
-      name would be deleted).
-  ``'a'``
-      Append; an existing file is opened for reading and writing,
-      and if the file does not exist it is created.
-  ``'r+'``
-      It is similar to ``'a'``, but the file must already exist.
-format : 'fixed(f)|table(t)', default is 'fixed'
-    fixed(f) : Fixed format
-               Fast writing/reading. Not-appendable, nor searchable
-    table(t) : Table format
-               Write as a PyTables Table structure which may perform
-               worse but allow more flexible operations like searching
-               / selecting subsets of the data
-append : boolean, default False
-    For Table formats, append the input data to the existing
-data_columns :  list of columns, or True, default None
-    List of columns to create as indexed data columns for on-disk
-    queries, or True to use all columns. By default only the axes
-    of the object are indexed. See `here
-    <http://pandas.pydata.org/pandas-docs/stable/io.html#query-via-data-columns>`__.

-    Applicable only to format='table'.
-complevel : int, 1-9, default 0
-    If a complib is specified compression will be applied
-    where possible
-complib : {'zlib', 'bzip2', 'lzo', 'blosc', None}, default None
-    If complevel is > 0 apply compression to objects written
-    in the store wherever possible
-fletcher32 : bool, default False
-    If applying compression use the fletcher32 checksum
-dropna : boolean, default False.
-    If true, ALL nan rows will not be written to store.
- -
to_json(self, path_or_buf=None, orient=None, date_format=None, double_precision=10, force_ascii=True, date_unit='ms', default_handler=None, lines=False)
Convert the object to a JSON string.

-Note NaN's and None will be converted to null and datetime objects
-will be converted to UNIX timestamps.

-Parameters
-----------
-path_or_buf : the path or buffer to write the result string
-    if this is None, return a StringIO of the converted string
-orient : string

-    * Series

-      - default is 'index'
-      - allowed values are: {'split','records','index'}

-    * DataFrame

-      - default is 'columns'
-      - allowed values are:
-        {'split','records','index','columns','values'}

-    * The format of the JSON string

-      - split : dict like
-        {index -> [index], columns -> [columns], data -> [values]}
-      - records : list like
-        [{column -> value}, ... , {column -> value}]
-      - index : dict like {index -> {column -> value}}
-      - columns : dict like {column -> {index -> value}}
-      - values : just the values array
-      - table : dict like {'schema': {schema}, 'data': {data}}
-        describing the data, and the data component is
-        like ``orient='records'``.

-        .. versionchanged:: 0.20.0

-date_format : {None, 'epoch', 'iso'}
-    Type of date conversion. `epoch` = epoch milliseconds,
-    `iso` = ISO8601. The default depends on the `orient`. For
-    `orient='table'`, the default is `'iso'`. For all other orients,
-    the default is `'epoch'`.
-double_precision : The number of decimal places to use when encoding
-    floating point values, default 10.
-force_ascii : force encoded string to be ASCII, default True.
-date_unit : string, default 'ms' (milliseconds)
-    The time unit to encode to, governs timestamp and ISO8601
-    precision.  One of 's', 'ms', 'us', 'ns' for second, millisecond,
-    microsecond, and nanosecond respectively.
-default_handler : callable, default None
-    Handler to call if object cannot otherwise be converted to a
-    suitable format for JSON. Should receive a single argument which is
-    the object to convert and return a serialisable object.
-lines : boolean, default False
-    If 'orient' is 'records' write out line delimited json format. Will
-    throw ValueError if incorrect 'orient' since others are not list
-    like.

-    .. versionadded:: 0.19.0

-Returns
--------
-same type as input object with filtered info axis

-See Also
---------
-pd.read_json

-Examples
---------

->>> df = pd.DataFrame([['a', 'b'], ['c', 'd']],
-...                   index=['row 1', 'row 2'],
-...                   columns=['col 1', 'col 2'])
->>> df.to_json(orient='split')
-'{"columns":["col 1","col 2"],
-  "index":["row 1","row 2"],
-  "data":[["a","b"],["c","d"]]}'

-Encoding/decoding a Dataframe using ``'index'`` formatted JSON:

->>> df.to_json(orient='index')
-'{"row 1":{"col 1":"a","col 2":"b"},"row 2":{"col 1":"c","col 2":"d"}}'

-Encoding/decoding a Dataframe using ``'records'`` formatted JSON.
-Note that index labels are not preserved with this encoding.

->>> df.to_json(orient='records')
-'[{"col 1":"a","col 2":"b"},{"col 1":"c","col 2":"d"}]'

-Encoding with Table Schema

->>> df.to_json(orient='table')
-'{"schema": {"fields": [{"name": "index", "type": "string"},
-                        {"name": "col 1", "type": "string"},
-                        {"name": "col 2", "type": "string"}],
-             "primaryKey": "index",
-             "pandas_version": "0.20.0"},
-  "data": [{"index": "row 1", "col 1": "a", "col 2": "b"},
-           {"index": "row 2", "col 1": "c", "col 2": "d"}]}'
- -
to_msgpack(self, path_or_buf=None, encoding='utf-8', **kwargs)
msgpack (serialize) object to input file path

-THIS IS AN EXPERIMENTAL LIBRARY and the storage format
-may not be stable until a future release.

-Parameters
-----------
-path : string File path, buffer-like, or None
-    if None, return generated string
-append : boolean whether to append to an existing msgpack
-    (default is False)
-compress : type of compressor (zlib or blosc), default to None (no
-    compression)
- -
to_pickle(self, path, compression='infer')
Pickle (serialize) object to input file path.

-Parameters
-----------
-path : string
-    File path
-compression : {'infer', 'gzip', 'bz2', 'xz', None}, default 'infer'
-    a string representing the compression to use in the output file

-    .. versionadded:: 0.20.0
- -
to_sql(self, name, con, flavor=None, schema=None, if_exists='fail', index=True, index_label=None, chunksize=None, dtype=None)
Write records stored in a DataFrame to a SQL database.

-Parameters
-----------
-name : string
-    Name of SQL table
-con : SQLAlchemy engine or DBAPI2 connection (legacy mode)
-    Using SQLAlchemy makes it possible to use any DB supported by that
-    library. If a DBAPI2 object, only sqlite3 is supported.
-flavor : 'sqlite', default None
-    DEPRECATED: this parameter will be removed in a future version,
-    as 'sqlite' is the only supported option if SQLAlchemy is not
-    installed.
-schema : string, default None
-    Specify the schema (if database flavor supports this). If None, use
-    default schema.
-if_exists : {'fail', 'replace', 'append'}, default 'fail'
-    - fail: If table exists, do nothing.
-    - replace: If table exists, drop it, recreate it, and insert data.
-    - append: If table exists, insert data. Create if does not exist.
-index : boolean, default True
-    Write DataFrame index as a column.
-index_label : string or sequence, default None
-    Column label for index column(s). If None is given (default) and
-    `index` is True, then the index names are used.
-    A sequence should be given if the DataFrame uses MultiIndex.
-chunksize : int, default None
-    If not None, then rows will be written in batches of this size at a
-    time.  If None, all rows will be written at once.
-dtype : dict of column name to SQL type, default None
-    Optional specifying the datatype for columns. The SQL type should
-    be a SQLAlchemy type, or a string for sqlite3 fallback connection.
- -
to_xarray(self)
Return an xarray object from the pandas object.

-Returns
--------
-a DataArray for a Series
-a Dataset for a DataFrame
-a DataArray for higher dims

-Examples
---------
->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)})
->>> df
-   A    B    C
-0  1  foo  4.0
-1  1  bar  5.0
-2  2  foo  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (index: 3)
-Coordinates:
-  * index    (index) int64 0 1 2
-Data variables:
-    A        (index) int64 1 1 2
-    B        (index) object 'foo' 'bar' 'foo'
-    C        (index) float64 4.0 5.0 6.0

->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)}
-                     ).set_index(['B','A'])
->>> df
-         C
-B   A
-foo 1  4.0
-bar 1  5.0
-foo 2  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (A: 2, B: 2)
-Coordinates:
-  * B        (B) object 'bar' 'foo'
-  * A        (A) int64 1 2
-Data variables:
-    C        (B, A) float64 5.0 nan 4.0 6.0

->>> p = pd.Panel(np.arange(24).reshape(4,3,2),
-                 items=list('ABCD'),
-                 major_axis=pd.date_range('20130101', periods=3),
-                 minor_axis=['first', 'second'])
->>> p
-<class 'pandas.core.panel.Panel'>
-Dimensions: 4 (items) x 3 (major_axis) x 2 (minor_axis)
-Items axis: A to D
-Major_axis axis: 2013-01-01 00:00:00 to 2013-01-03 00:00:00
-Minor_axis axis: first to second

->>> p.to_xarray()
-<xarray.DataArray (items: 4, major_axis: 3, minor_axis: 2)>
-array([[[ 0,  1],
-        [ 2,  3],
-        [ 4,  5]],
-       [[ 6,  7],
-        [ 8,  9],
-        [10, 11]],
-       [[12, 13],
-        [14, 15],
-        [16, 17]],
-       [[18, 19],
-        [20, 21],
-        [22, 23]]])
-Coordinates:
-  * items       (items) object 'A' 'B' 'C' 'D'
-  * major_axis  (major_axis) datetime64[ns] 2013-01-01 2013-01-02 2013-01-03  # noqa
-  * minor_axis  (minor_axis) object 'first' 'second'

-Notes
------
-See the `xarray docs <http://xarray.pydata.org/en/stable/>`__
- -
truncate(self, before=None, after=None, axis=None, copy=True)
Truncates a sorted NDFrame before and/or after some particular
-index value. If the axis contains only datetime values, before/after
-parameters are converted to datetime values.

-Parameters
-----------
-before : date
-    Truncate before index value
-after : date
-    Truncate after index value
-axis : the truncation axis, defaults to the stat axis
-copy : boolean, default is True,
-    return a copy of the truncated section

-Returns
--------
-truncated : type of caller
- -
tshift(self, periods=1, freq=None, axis=0)
Shift the time index, using the index's frequency if available.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, default None
-    Increment to use from the tseries module or time rule (e.g. 'EOM')
-axis : int or basestring
-    Corresponds to the axis that contains the Index

-Notes
------
-If freq is not specified then tries to use the freq or inferred_freq
-attributes of the index. If neither of those attributes exist, a
-ValueError is thrown

-Returns
--------
-shifted : NDFrame
- -
tz_convert(self, tz, axis=0, level=None, copy=True)
Convert tz-aware axis to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to convert
-level : int, str, default None
-    If axis ia a MultiIndex, convert a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data

-Returns
--------

-Raises
-------
-TypeError
-    If the axis is tz-naive.
- -
tz_localize(self, tz, axis=0, level=None, copy=True, ambiguous='raise')
Localize tz-naive TimeSeries to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to localize
-level : int, str, default None
-    If axis ia a MultiIndex, localize a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data
-ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise'
-    - 'infer' will attempt to infer fall dst-transition hours based on
-      order
-    - bool-ndarray where True signifies a DST time, False designates
-      a non-DST time (note that this flag is only applicable for
-      ambiguous times)
-    - 'NaT' will return NaT where there are ambiguous times
-    - 'raise' will raise an AmbiguousTimeError if there are ambiguous
-      times
-infer_dst : boolean, default False (DEPRECATED)
-    Attempt to infer fall dst-transition hours based on order

-Returns
--------

-Raises
-------
-TypeError
-    If the TimeSeries is tz-aware and tz is not None.
- -
where(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is True and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The where method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``True`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``where`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.mask`
- -
xs(self, key, axis=0, level=None, drop_level=True)
Returns a cross-section (row(s) or column(s)) from the
-Series/DataFrame. Defaults to cross-section on the rows (axis=0).

-Parameters
-----------
-key : object
-    Some label contained in the index, or partially in a MultiIndex
-axis : int, default 0
-    Axis to retrieve cross-section on
-level : object, defaults to first n levels (n=1 or len(key))
-    In case of a key partially contained in a MultiIndex, indicate
-    which levels are used. Levels can be referred by label or position.
-drop_level : boolean, default True
-    If False, returns object with same levels as self.

-Examples
---------
->>> df
-   A  B  C
-a  4  5  2
-b  4  0  9
-c  9  7  3
->>> df.xs('a')
-A    4
-B    5
-C    2
-Name: a
->>> df.xs('C', axis=1)
-a    2
-b    9
-c    3
-Name: C

->>> df
-                    A  B  C  D
-first second third
-bar   one    1      4  1  8  9
-      two    1      7  5  5  0
-baz   one    1      6  6  8  0
-      three  2      5  3  5  3
->>> df.xs(('baz', 'three'))
-       A  B  C  D
-third
-2      5  3  5  3
->>> df.xs('one', level=1)
-             A  B  C  D
-first third
-bar   1      4  1  8  9
-baz   1      6  6  8  0
->>> df.xs(('baz', 2), level=[0, 'third'])
-        A  B  C  D
-second
-three   5  3  5  3

-Returns
--------
-xs : Series or DataFrame

-Notes
------
-xs is only for getting, not setting values.

-MultiIndex Slicers is a generic way to get/set values on any level or
-levels.  It is a superset of xs functionality, see
-:ref:`MultiIndex Slicers <advanced.mi_slicers>`
- -
-Data descriptors inherited from pandas.core.generic.NDFrame:
-
at
-
Fast label-based scalar accessor

-Similarly to ``loc``, ``at`` provides **label** based scalar lookups.
-You can also set using these indexers.
-
-
blocks
-
Internal property, property synonym for as_blocks()
-
-
dtypes
-
Return the dtypes in this object.
-
-
empty
-
True if NDFrame is entirely empty [no items], meaning any of the
-axes are of length 0.

-Notes
------
-If NDFrame contains only NaNs, it is still not considered empty. See
-the example below.

-Examples
---------
-An example of an actual empty DataFrame. Notice the index is empty:

->>> df_empty = pd.DataFrame({'A' : []})
->>> df_empty
-Empty DataFrame
-Columns: [A]
-Index: []
->>> df_empty.empty
-True

-If we only have NaNs in our DataFrame, it is not considered empty! We
-will need to drop the NaNs to make the DataFrame empty:

->>> df = pd.DataFrame({'A' : [np.nan]})
->>> df
-    A
-0 NaN
->>> df.empty
-False
->>> df.dropna().empty
-True

-See also
---------
-pandas.Series.dropna
-pandas.DataFrame.dropna
-
-
ftypes
-
Return the ftypes (indication of sparse/dense and dtype)
-in this object.
-
-
iat
-
Fast integer location scalar accessor.

-Similarly to ``iloc``, ``iat`` provides **integer** based lookups.
-You can also set using these indexers.
-
-
iloc
-
Purely integer-location based indexing for selection by position.

-``.iloc[]`` is primarily integer position based (from ``0`` to
-``length-1`` of the axis), but may also be used with a boolean
-array.

-Allowed inputs are:

-- An integer, e.g. ``5``.
-- A list or array of integers, e.g. ``[4, 3, 0]``.
-- A slice object with ints, e.g. ``1:7``.
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.iloc`` will raise ``IndexError`` if a requested indexer is
-out-of-bounds, except *slice* indexers which allow out-of-bounds
-indexing (this conforms with python/numpy *slice* semantics).

-See more at :ref:`Selection by Position <indexing.integer>`
-
-
ix
-
A primarily label-location based indexer, with integer position
-fallback.

-``.ix[]`` supports mixed integer and label based access. It is
-primarily label based, but will fall back to integer positional
-access unless the corresponding axis is of integer type.

-``.ix`` is the most general indexer and will support any of the
-inputs in ``.loc`` and ``.iloc``. ``.ix`` also supports floating
-point label schemes. ``.ix`` is exceptionally useful when dealing
-with mixed positional and label based hierachical indexes.

-However, when an axis is integer based, ONLY label based access
-and not positional access is supported. Thus, in such cases, it's
-usually better to be explicit and use ``.iloc`` or ``.loc``.

-See more at :ref:`Advanced Indexing <advanced>`.
-
-
loc
-
Purely label-location based indexer for selection by label.

-``.loc[]`` is primarily label based, but may also be used with a
-boolean array.

-Allowed inputs are:

-- A single label, e.g. ``5`` or ``'a'``, (note that ``5`` is
-  interpreted as a *label* of the index, and **never** as an
-  integer position along the index).
-- A list or array of labels, e.g. ``['a', 'b', 'c']``.
-- A slice object with labels, e.g. ``'a':'f'`` (note that contrary
-  to usual python slices, **both** the start and the stop are included!).
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.loc`` will raise a ``KeyError`` when the items are not found.

-See more at :ref:`Selection by Label <indexing.label>`
-
-
ndim
-
Number of axes / array dimensions
-
-
size
-
number of elements in the NDFrame
-
-
values
-
Numpy representation of NDFrame

-Notes
------
-The dtype will be a lower-common-denominator dtype (implicit
-upcasting); that is to say if the dtypes (even of numeric types)
-are mixed, the one that accommodates all will be chosen. Use this
-with care if you are not dealing with the blocks.

-e.g. If the dtypes are float16 and float32, dtype will be upcast to
-float32.  If dtypes are int32 and uint8, dtype will be upcast to
-int32. By numpy.find_common_type convention, mixing int64 and uint64
-will result in a flot64 dtype.
-
-
-Data and other attributes inherited from pandas.core.generic.NDFrame:
-
is_copy = None
- -
-Methods inherited from pandas.core.base.PandasObject:
-
__dir__(self)
Provide method name lookup and completion
-Only provide 'public' methods
- -
__sizeof__(self)
Generates the total memory usage for a object that returns
-either a value or Series of values
- -
-Methods inherited from pandas.core.base.StringMixin:
-
__bytes__(self)
Return a string representation for a particular object.

-Invoked by bytes(obj) in py3 only.
-Yields a bytestring in both py2/py3.
- -
__repr__(self)
Return a string representation for a particular object.

-Yields Bytestring in Py2, Unicode String in py3.
- -
__str__(self)
Return a string representation for a particular Object

-Invoked by str(df) in both py2/py3.
-Yields Bytestring in Py2, Unicode String in py3.
- -
-Data descriptors inherited from pandas.core.base.StringMixin:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-

- - - - - - - -
 
-class MySeries(pandas.core.series.Series)
   One-dimensional ndarray with axis labels (including time series).

-Labels need not be unique but must be a hashable type. The object
-supports both integer- and label-based indexing and provides a host of
-methods for performing operations involving the index. Statistical
-methods from ndarray have been overridden to automatically exclude
-missing data (currently represented as NaN).

-Operations between Series (+, -, /, *, **) align values based on their
-associated index values-- they need not be the same length. The result
-index will be the sorted union of the two indexes.

-Parameters
-----------
-data : array-like, dict, or scalar value
-    Contains data stored in Series
-index : array-like or Index (1d)
-    Values must be hashable and have the same length as `data`.
-    Non-unique index values are allowed. Will default to
-    RangeIndex(len(data)) if not provided. If both a dict and index
-    sequence are used, the index will override the keys found in the
-    dict.
-dtype : numpy.dtype or None
-    If None, dtype will be inferred
-copy : boolean, default False
-    Copy input data
 
 
Method resolution order:
-
MySeries
-
pandas.core.series.Series
-
pandas.core.base.IndexOpsMixin
-
pandas.core.strings.StringAccessorMixin
-
pandas.core.generic.NDFrame
-
pandas.core.base.PandasObject
-
pandas.core.base.StringMixin
-
pandas.core.base.SelectionMixin
-
builtins.object
-
-
-Methods defined here:
-
__init__(self, *args, **kwargs)
Initialize a Series.

-Note: this cleans up a weird Series behavior, which is
-that Series() and Series([]) yield different results.
-See: https://github.com/pandas-dev/pandas/issues/16737
- -
set(self, **kwargs)
Uses keyword arguments to update the Series in place.

-Example: series.update(a=1, b=2)
- -
-Methods inherited from pandas.core.series.Series:
-
__add__ = wrapper(left, right, name='__add__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f2f0>)
- -
__and__ = wrapper(self, other)
- -
__array__(self, result=None)
the array interface, return my values
- -
__array_prepare__(self, result, context=None)
Gets called prior to a ufunc
- -
__array_wrap__(self, result, context=None)
Gets called after a ufunc
- -
__div__ = wrapper(left, right, name='__truediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f9d8>)
- -
__divmod__ = wrapper(left, right, name='__divmod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca343bf8>)
- -
__eq__ = wrapper(self, other, axis=None)
- -
__float__ = wrapper(self)
- -
__floordiv__ = wrapper(left, right, name='__floordiv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fb70>)
- -
__ge__ = wrapper(self, other, axis=None)
- -
__getitem__(self, key)
- -
__gt__ = wrapper(self, other, axis=None)
- -
__iadd__ = f(self, other)
- -
__imul__ = f(self, other)
- -
__int__ = wrapper(self)
- -
__ipow__ = f(self, other)
- -
__isub__ = f(self, other)
- -
__iter__(self)
provide iteration over the values of the Series
-box values if necessary
- -
__itruediv__ = f(self, other)
- -
__le__ = wrapper(self, other, axis=None)
- -
__len__(self)
return the length of the Series
- -
__long__ = wrapper(self)
- -
__lt__ = wrapper(self, other, axis=None)
- -
__mod__ = wrapper(left, right, name='__mod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fd08>)
- -
__mul__ = wrapper(left, right, name='__mul__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f840>)
- -
__ne__ = wrapper(self, other, axis=None)
- -
__or__ = wrapper(self, other)
- -
__pow__ = wrapper(left, right, name='__pow__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fea0>)
- -
__radd__ = wrapper(left, right, name='__radd__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f510>)
- -
__rand__ = wrapper(self, other)
- -
__rdiv__ = wrapper(left, right, name='__rtruediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342510>)
- -
__rfloordiv__ = wrapper(left, right, name='__rfloordiv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342730>)
- -
__rmod__ = wrapper(left, right, name='__rmod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342b70>)
- -
__rmul__ = wrapper(left, right, name='__rmul__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca3420d0>)
- -
__ror__ = wrapper(self, other)
- -
__rpow__ = wrapper(left, right, name='__rpow__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342950>)
- -
__rsub__ = wrapper(left, right, name='__rsub__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca3422f0>)
- -
__rtruediv__ = wrapper(left, right, name='__rtruediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342510>)
- -
__rxor__ = wrapper(self, other)
- -
__setitem__(self, key, value)
- -
__sub__ = wrapper(left, right, name='__sub__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f6a8>)
- -
__truediv__ = wrapper(left, right, name='__truediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f9d8>)
- -
__unicode__(self)
Return a string representation for a particular DataFrame

-Invoked by unicode(df) in py2 only. Yields a Unicode String in both
-py2/py3.
- -
__xor__ = wrapper(self, other)
- -
add(self, other, level=None, fill_value=None, axis=0)
Addition of series and other, element-wise (binary operator `add`).

-Equivalent to ``series + other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.radd
- -
agg = aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a Series or when passed to Series.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : Series

-Examples
---------

->>> s = Series(np.random.randn(10))

->>> s.agg('min')
--1.3018049988556679

->>> s.agg(['min', 'max'])
-min   -1.301805
-max    1.127688
-dtype: float64

-See also
---------
-pandas.Series.apply
-pandas.Series.transform
- -
aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a Series or when passed to Series.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : Series

-Examples
---------

->>> s = Series(np.random.randn(10))

->>> s.agg('min')
--1.3018049988556679

->>> s.agg(['min', 'max'])
-min   -1.301805
-max    1.127688
-dtype: float64

-See also
---------
-pandas.Series.apply
-pandas.Series.transform
- -
align(self, other, join='outer', axis=None, level=None, copy=True, fill_value=None, method=None, limit=None, fill_axis=0, broadcast_axis=None)
Align two object on their axes with the
-specified join method for each axis Index

-Parameters
-----------
-other : DataFrame or Series
-join : {'outer', 'inner', 'left', 'right'}, default 'outer'
-axis : allowed axis of the other object, default None
-    Align on index (0), columns (1), or both (None)
-level : int or level name, default None
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-copy : boolean, default True
-    Always returns new objects. If copy=False and no reindexing is
-    required then original objects are returned.
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-method : str, default None
-limit : int, default None
-fill_axis : {0, 'index'}, default 0
-    Filling axis, method and limit
-broadcast_axis : {0, 'index'}, default None
-    Broadcast values along this axis, if aligning two objects of
-    different dimensions

-    .. versionadded:: 0.17.0

-Returns
--------
-(left, right) : (Series, type of other)
-    Aligned objects
- -
all(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether all elements are True over requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-all : scalar or Series (if level specified)
- -
any(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether any element is True over requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-any : scalar or Series (if level specified)
- -
append(self, to_append, ignore_index=False, verify_integrity=False)
Concatenate two or more Series.

-Parameters
-----------
-to_append : Series or list/tuple of Series
-ignore_index : boolean, default False
-    If True, do not use the index labels.

-    .. versionadded: 0.19.0

-verify_integrity : boolean, default False
-    If True, raise Exception on creating index with duplicates

-Returns
--------
-appended : Series

-Examples
---------
->>> s1 = pd.Series([1, 2, 3])
->>> s2 = pd.Series([4, 5, 6])
->>> s3 = pd.Series([4, 5, 6], index=[3,4,5])
->>> s1.append(s2)
-0    1
-1    2
-2    3
-0    4
-1    5
-2    6
-dtype: int64

->>> s1.append(s3)
-0    1
-1    2
-2    3
-3    4
-4    5
-5    6
-dtype: int64

-With `ignore_index` set to True:

->>> s1.append(s2, ignore_index=True)
-0    1
-1    2
-2    3
-3    4
-4    5
-5    6
-dtype: int64

-With `verify_integrity` set to True:

->>> s1.append(s2, verify_integrity=True)
-Traceback (most recent call last):
-...
-ValueError: Indexes have overlapping values: [0, 1, 2]
- -
apply(self, func, convert_dtype=True, args=(), **kwds)
Invoke function on values of Series. Can be ufunc (a NumPy function
-that applies to the entire Series) or a Python function that only works
-on single values

-Parameters
-----------
-func : function
-convert_dtype : boolean, default True
-    Try to find better dtype for elementwise function results. If
-    False, leave as dtype=object
-args : tuple
-    Positional arguments to pass to function in addition to the value
-Additional keyword arguments will be passed as keywords to the function

-Returns
--------
-y : Series or DataFrame if func returns a Series

-See also
---------
-Series.map: For element-wise operations
-Series.agg: only perform aggregating type operations
-Series.transform: only perform transformating type operations

-Examples
---------

-Create a series with typical summer temperatures for each city.

->>> import pandas as pd
->>> import numpy as np
->>> series = pd.Series([20, 21, 12], index=['London',
-... 'New York','Helsinki'])
->>> series
-London      20
-New York    21
-Helsinki    12
-dtype: int64

-Square the values by defining a function and passing it as an
-argument to ``apply()``.

->>> def square(x):
-...     return x**2
->>> series.apply(square)
-London      400
-New York    441
-Helsinki    144
-dtype: int64

-Square the values by passing an anonymous function as an
-argument to ``apply()``.

->>> series.apply(lambda x: x**2)
-London      400
-New York    441
-Helsinki    144
-dtype: int64

-Define a custom function that needs additional positional
-arguments and pass these additional arguments using the
-``args`` keyword.

->>> def subtract_custom_value(x, custom_value):
-...     return x-custom_value

->>> series.apply(subtract_custom_value, args=(5,))
-London      15
-New York    16
-Helsinki     7
-dtype: int64

-Define a custom function that takes keyword arguments
-and pass these arguments to ``apply``.

->>> def add_custom_values(x, **kwargs):
-...     for month in kwargs:
-...         x+=kwargs[month]
-...         return x

->>> series.apply(add_custom_values, june=30, july=20, august=25)
-London      95
-New York    96
-Helsinki    87
-dtype: int64

-Use a function from the Numpy library.

->>> series.apply(np.log)
-London      2.995732
-New York    3.044522
-Helsinki    2.484907
-dtype: float64
- -
argmax = idxmax(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of maximum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmax : Index of maximum of values

-Notes
------
-This method is the Series version of ``ndarray.argmax``.

-See Also
---------
-DataFrame.idxmax
-numpy.ndarray.argmax
- -
argmin = idxmin(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of minimum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmin : Index of minimum of values

-Notes
------
-This method is the Series version of ``ndarray.argmin``.

-See Also
---------
-DataFrame.idxmin
-numpy.ndarray.argmin
- -
argsort(self, axis=0, kind='quicksort', order=None)
Overrides ndarray.argsort. Argsorts the value, omitting NA/null values,
-and places the result in the same locations as the non-NA values

-Parameters
-----------
-axis : int (can only be zero)
-kind : {'mergesort', 'quicksort', 'heapsort'}, default 'quicksort'
-    Choice of sorting algorithm. See np.sort for more
-    information. 'mergesort' is the only stable algorithm
-order : ignored

-Returns
--------
-argsorted : Series, with -1 indicated where nan values are present

-See also
---------
-numpy.ndarray.argsort
- -
autocorr(self, lag=1)
Lag-N autocorrelation

-Parameters
-----------
-lag : int, default 1
-    Number of lags to apply before performing autocorrelation.

-Returns
--------
-autocorr : float
- -
between(self, left, right, inclusive=True)
Return boolean Series equivalent to left <= series <= right. NA values
-will be treated as False

-Parameters
-----------
-left : scalar
-    Left boundary
-right : scalar
-    Right boundary

-Returns
--------
-is_between : Series
- -
combine(self, other, func, fill_value=nan)
Perform elementwise binary operation on two Series using given function
-with optional fill value when an index is missing from one Series or
-the other

-Parameters
-----------
-other : Series or scalar value
-func : function
-fill_value : scalar value

-Returns
--------
-result : Series
- -
combine_first(self, other)
Combine Series values, choosing the calling Series's values
-first. Result index will be the union of the two indexes

-Parameters
-----------
-other : Series

-Returns
--------
-y : Series
- -
compound(self, axis=None, skipna=None, level=None)
Return the compound percentage of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-compounded : scalar or Series (if level specified)
- -
compress(self, condition, *args, **kwargs)
Return selected slices of an array along given axis as a Series

-See also
---------
-numpy.ndarray.compress
- -
corr(self, other, method='pearson', min_periods=None)
Compute correlation with `other` Series, excluding missing values

-Parameters
-----------
-other : Series
-method : {'pearson', 'kendall', 'spearman'}
-    * pearson : standard correlation coefficient
-    * kendall : Kendall Tau correlation coefficient
-    * spearman : Spearman rank correlation
-min_periods : int, optional
-    Minimum number of observations needed to have a valid result


-Returns
--------
-correlation : float
- -
count(self, level=None)
Return number of non-NA/null observations in the Series

-Parameters
-----------
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a smaller Series

-Returns
--------
-nobs : int or Series (if level specified)
- -
cov(self, other, min_periods=None)
Compute covariance with Series, excluding missing values

-Parameters
-----------
-other : Series
-min_periods : int, optional
-    Minimum number of observations needed to have a valid result

-Returns
--------
-covariance : float

-Normalized by N-1 (unbiased estimator).
- -
cummax(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative max over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummax : scalar



-See also
---------
-pandas.core.window.Expanding.max : Similar functionality
-    but ignores ``NaN`` values.
- -
cummin(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative minimum over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummin : scalar



-See also
---------
-pandas.core.window.Expanding.min : Similar functionality
-    but ignores ``NaN`` values.
- -
cumprod(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative product over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumprod : scalar



-See also
---------
-pandas.core.window.Expanding.prod : Similar functionality
-    but ignores ``NaN`` values.
- -
cumsum(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative sum over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumsum : scalar



-See also
---------
-pandas.core.window.Expanding.sum : Similar functionality
-    but ignores ``NaN`` values.
- -
diff(self, periods=1)
1st discrete difference of object

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming difference

-Returns
--------
-diffed : Series
- -
div = truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
divide = truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
dot(self, other)
Matrix multiplication with DataFrame or inner-product with Series
-objects

-Parameters
-----------
-other : Series or DataFrame

-Returns
--------
-dot_product : scalar or Series
- -
drop_duplicates(self, keep='first', inplace=False)
Return Series with duplicate values removed

-Parameters
-----------

-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Drop duplicates except for the first occurrence.
-    - ``last`` : Drop duplicates except for the last occurrence.
-    - False : Drop all duplicates.
-inplace : boolean, default False
-If True, performs operation inplace and returns None.

-Returns
--------
-deduplicated : Series
- -
dropna(self, axis=0, inplace=False, **kwargs)
Return Series without null values

-Returns
--------
-valid : Series
-inplace : boolean, default False
-    Do operation in place.
- -
duplicated(self, keep='first')
Return boolean Series denoting duplicate values

-Parameters
-----------
-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Mark duplicates as ``True`` except for the first
-      occurrence.
-    - ``last`` : Mark duplicates as ``True`` except for the last
-      occurrence.
-    - False : Mark all duplicates as ``True``.

-Returns
--------
-duplicated : Series
- -
eq(self, other, level=None, fill_value=None, axis=0)
Equal to of series and other, element-wise (binary operator `eq`).

-Equivalent to ``series == other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
ewm(self, com=None, span=None, halflife=None, alpha=None, min_periods=0, freq=None, adjust=True, ignore_na=False, axis=0)
Provides exponential weighted functions

-.. versionadded:: 0.18.0

-Parameters
-----------
-com : float, optional
-    Specify decay in terms of center of mass,
-    :math:`\alpha = 1 / (1 + com),\text{ for } com \geq 0`
-span : float, optional
-    Specify decay in terms of span,
-    :math:`\alpha = 2 / (span + 1),\text{ for } span \geq 1`
-halflife : float, optional
-    Specify decay in terms of half-life,
-    :math:`\alpha = 1 - exp(log(0.5) / halflife),\text{ for } halflife > 0`
-alpha : float, optional
-    Specify smoothing factor :math:`\alpha` directly,
-    :math:`0 < \alpha \leq 1`

-    .. versionadded:: 0.18.0

-min_periods : int, default 0
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : None or string alias / date offset object, default=None (DEPRECATED)
-    Frequency to conform to before computing statistic
-adjust : boolean, default True
-    Divide by decaying adjustment factor in beginning periods to account
-    for imbalance in relative weightings (viewing EWMA as a moving average)
-ignore_na : boolean, default False
-    Ignore missing values when calculating weights;
-    specify True to reproduce pre-0.15.0 behavior

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.ewm(com=0.5).mean()
-          B
-0  0.000000
-1  0.750000
-2  1.615385
-3  1.615385
-4  3.670213

-Notes
------
-Exactly one of center of mass, span, half-life, and alpha must be provided.
-Allowed values and relationship between the parameters are specified in the
-parameter descriptions above; see the link at the end of this section for
-a detailed explanation.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-When adjust is True (default), weighted averages are calculated using
-weights (1-alpha)**(n-1), (1-alpha)**(n-2), ..., 1-alpha, 1.

-When adjust is False, weighted averages are calculated recursively as:
-   weighted_average[0] = arg[0];
-   weighted_average[i] = (1-alpha)*weighted_average[i-1] + alpha*arg[i].

-When ignore_na is False (default), weights are based on absolute positions.
-For example, the weights of x and y used in calculating the final weighted
-average of [x, None, y] are (1-alpha)**2 and 1 (if adjust is True), and
-(1-alpha)**2 and alpha (if adjust is False).

-When ignore_na is True (reproducing pre-0.15.0 behavior), weights are based
-on relative positions. For example, the weights of x and y used in
-calculating the final weighted average of [x, None, y] are 1-alpha and 1
-(if adjust is True), and 1-alpha and alpha (if adjust is False).

-More details can be found at
-http://pandas.pydata.org/pandas-docs/stable/computation.html#exponentially-weighted-windows
- -
expanding(self, min_periods=1, freq=None, center=False, axis=0)
Provides expanding transformations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-axis : int or string, default 0

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.expanding(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  3.0
-4  7.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).
- -
fillna(self, value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)
Fill NA/NaN values using the specified method

-Parameters
-----------
-value : scalar, dict, Series, or DataFrame
-    Value to use to fill holes (e.g. 0), alternately a
-    dict/Series/DataFrame of values specifying which value to use for
-    each index (for a Series) or column (for a DataFrame). (values not
-    in the dict/Series/DataFrame will not be filled). This value cannot
-    be a list.
-method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
-    Method to use for filling holes in reindexed Series
-    pad / ffill: propagate last valid observation forward to next valid
-    backfill / bfill: use NEXT valid observation to fill gap
-axis : {0, 'index'}
-inplace : boolean, default False
-    If True, fill in place. Note: this will modify any
-    other views on this object, (e.g. a no-copy slice for a column in a
-    DataFrame).
-limit : int, default None
-    If method is specified, this is the maximum number of consecutive
-    NaN values to forward/backward fill. In other words, if there is
-    a gap with more than this number of consecutive NaNs, it will only
-    be partially filled. If method is not specified, this is the
-    maximum number of entries along the entire axis where NaNs will be
-    filled. Must be greater than 0 if not None.
-downcast : dict, default is None
-    a dict of item->dtype of what to downcast if possible,
-    or the string 'infer' which will try to downcast to an appropriate
-    equal type (e.g. float64 to int64 if possible)

-See Also
---------
-reindex, asfreq

-Returns
--------
-filled : Series
- -
first_valid_index(self)
Return label for first non-NA/null value
- -
floordiv(self, other, level=None, fill_value=None, axis=0)
Integer division of series and other, element-wise (binary operator `floordiv`).

-Equivalent to ``series // other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rfloordiv
- -
ge(self, other, level=None, fill_value=None, axis=0)
Greater than or equal to of series and other, element-wise (binary operator `ge`).

-Equivalent to ``series >= other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
get_value(self, label, takeable=False)
Quickly retrieve single value at passed index label

-Parameters
-----------
-index : label
-takeable : interpret the index as indexers, default False

-Returns
--------
-value : scalar value
- -
get_values(self)
same as values (but handles sparseness conversions); is a view
- -
gt(self, other, level=None, fill_value=None, axis=0)
Greater than of series and other, element-wise (binary operator `gt`).

-Equivalent to ``series > other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
hist = hist_series(self, by=None, ax=None, grid=True, xlabelsize=None, xrot=None, ylabelsize=None, yrot=None, figsize=None, bins=10, **kwds)
Draw histogram of the input series using matplotlib

-Parameters
-----------
-by : object, optional
-    If passed, then used to form histograms for separate groups
-ax : matplotlib axis object
-    If not passed, uses gca()
-grid : boolean, default True
-    Whether to show axis grid lines
-xlabelsize : int, default None
-    If specified changes the x-axis label size
-xrot : float, default None
-    rotation of x axis labels
-ylabelsize : int, default None
-    If specified changes the y-axis label size
-yrot : float, default None
-    rotation of y axis labels
-figsize : tuple, default None
-    figure size in inches by default
-bins: integer, default 10
-    Number of histogram bins to be used
-kwds : keywords
-    To be passed to the actual plotting function

-Notes
------
-See matplotlib documentation online for more on this
- -
idxmax(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of maximum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmax : Index of maximum of values

-Notes
------
-This method is the Series version of ``ndarray.argmax``.

-See Also
---------
-DataFrame.idxmax
-numpy.ndarray.argmax
- -
idxmin(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of minimum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmin : Index of minimum of values

-Notes
------
-This method is the Series version of ``ndarray.argmin``.

-See Also
---------
-DataFrame.idxmin
-numpy.ndarray.argmin
- -
isin(self, values)
Return a boolean :class:`~pandas.Series` showing whether each element
-in the :class:`~pandas.Series` is exactly contained in the passed
-sequence of ``values``.

-Parameters
-----------
-values : set or list-like
-    The sequence of values to test. Passing in a single string will
-    raise a ``TypeError``. Instead, turn a single string into a
-    ``list`` of one element.

-    .. versionadded:: 0.18.1

-    Support for values as a set

-Returns
--------
-isin : Series (bool dtype)

-Raises
-------
-TypeError
-  * If ``values`` is a string

-See Also
---------
-pandas.DataFrame.isin

-Examples
---------

->>> s = pd.Series(list('abc'))
->>> s.isin(['a', 'c', 'e'])
-0     True
-1    False
-2     True
-dtype: bool

-Passing a single string as ``s.isin('a')`` will raise an error. Use
-a list of one element instead:

->>> s.isin(['a'])
-0     True
-1    False
-2    False
-dtype: bool
- -
items = iteritems(self)
Lazily iterate over (index, value) tuples
- -
iteritems(self)
Lazily iterate over (index, value) tuples
- -
keys(self)
Alias for index
- -
kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : scalar or Series (if level specified)
- -
kurtosis = kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : scalar or Series (if level specified)
- -
last_valid_index(self)
Return label for last non-NA/null value
- -
le(self, other, level=None, fill_value=None, axis=0)
Less than or equal to of series and other, element-wise (binary operator `le`).

-Equivalent to ``series <= other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
lt(self, other, level=None, fill_value=None, axis=0)
Less than of series and other, element-wise (binary operator `lt`).

-Equivalent to ``series < other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
mad(self, axis=None, skipna=None, level=None)
Return the mean absolute deviation of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mad : scalar or Series (if level specified)
- -
map(self, arg, na_action=None)
Map values of Series using input correspondence (which can be
-a dict, Series, or function)

-Parameters
-----------
-arg : function, dict, or Series
-na_action : {None, 'ignore'}
-    If 'ignore', propagate NA values, without passing them to the
-    mapping function

-Returns
--------
-y : Series
-    same index as caller

-Examples
---------

-Map inputs to outputs (both of type `Series`)

->>> x = pd.Series([1,2,3], index=['one', 'two', 'three'])
->>> x
-one      1
-two      2
-three    3
-dtype: int64

->>> y = pd.Series(['foo', 'bar', 'baz'], index=[1,2,3])
->>> y
-1    foo
-2    bar
-3    baz

->>> x.map(y)
-one   foo
-two   bar
-three baz

-If `arg` is a dictionary, return a new Series with values converted
-according to the dictionary's mapping:

->>> z = {1: 'A', 2: 'B', 3: 'C'}

->>> x.map(z)
-one   A
-two   B
-three C

-Use na_action to control whether NA values are affected by the mapping
-function.

->>> s = pd.Series([1, 2, 3, np.nan])

->>> s2 = s.map('this is a string {}'.format, na_action=None)
-0    this is a string 1.0
-1    this is a string 2.0
-2    this is a string 3.0
-3    this is a string nan
-dtype: object

->>> s3 = s.map('this is a string {}'.format, na_action='ignore')
-0    this is a string 1.0
-1    this is a string 2.0
-2    this is a string 3.0
-3                     NaN
-dtype: object

-See Also
---------
-Series.apply: For applying more complex functions on a Series
-DataFrame.apply: Apply a function row-/column-wise
-DataFrame.applymap: Apply a function elementwise on a whole DataFrame

-Notes
------
-When `arg` is a dictionary, values in Series that are not in the
-dictionary (as keys) are converted to ``NaN``. However, if the
-dictionary is a ``dict`` subclass that defines ``__missing__`` (i.e.
-provides a method for default values), then this default is used
-rather than ``NaN``:

->>> from collections import Counter
->>> counter = Counter()
->>> counter['bar'] += 1
->>> y.map(counter)
-1    0
-2    1
-3    0
-dtype: int64
- -
max(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the maximum of the values in the object.
-            If you want the *index* of the maximum, use ``idxmax``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmax``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-max : scalar or Series (if level specified)
- -
mean(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the mean of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mean : scalar or Series (if level specified)
- -
median(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the median of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-median : scalar or Series (if level specified)
- -
memory_usage(self, index=True, deep=False)
Memory usage of the Series

-Parameters
-----------
-index : bool
-    Specifies whether to include memory usage of Series index
-deep : bool
-    Introspect the data deeply, interrogate
-    `object` dtypes for system-level memory consumption

-Returns
--------
-scalar bytes of memory consumed

-Notes
------
-Memory usage does not include memory consumed by elements that
-are not components of the array if deep=False

-See Also
---------
-numpy.ndarray.nbytes
- -
min(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the minimum of the values in the object.
-            If you want the *index* of the minimum, use ``idxmin``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmin``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-min : scalar or Series (if level specified)
- -
mod(self, other, level=None, fill_value=None, axis=0)
Modulo of series and other, element-wise (binary operator `mod`).

-Equivalent to ``series % other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmod
- -
mode(self)
Return the mode(s) of the dataset.

-Always returns Series even if only one value is returned.

-Returns
--------
-modes : Series (sorted)
- -
mul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `mul`).

-Equivalent to ``series * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmul
- -
multiply = mul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `mul`).

-Equivalent to ``series * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmul
- -
ne(self, other, level=None, fill_value=None, axis=0)
Not equal to of series and other, element-wise (binary operator `ne`).

-Equivalent to ``series != other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
nlargest(self, n=5, keep='first')
Return the largest `n` elements.

-Parameters
-----------
-n : int
-    Return this many descending sorted values
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-top_n : Series
-    The n largest values in the Series, in sorted order

-Notes
------
-Faster than ``.sort_values(ascending=False).head(n)`` for small `n`
-relative to the size of the ``Series`` object.

-See Also
---------
-Series.nsmallest

-Examples
---------
->>> import pandas as pd
->>> import numpy as np
->>> s = pd.Series(np.random.randn(10**6))
->>> s.nlargest(10)  # only sorts up to the N requested
-219921    4.644710
-82124     4.608745
-421689    4.564644
-425277    4.447014
-718691    4.414137
-43154     4.403520
-283187    4.313922
-595519    4.273635
-503969    4.250236
-121637    4.240952
-dtype: float64
- -
nonzero(self)
Return the indices of the elements that are non-zero

-This method is equivalent to calling `numpy.nonzero` on the
-series data. For compatability with NumPy, the return value is
-the same (a tuple with an array of indices for each dimension),
-but it will always be a one-item tuple because series only have
-one dimension.

-Examples
---------
->>> s = pd.Series([0, 3, 0, 4])
->>> s.nonzero()
-(array([1, 3]),)
->>> s.iloc[s.nonzero()[0]]
-1    3
-3    4
-dtype: int64

-See Also
---------
-numpy.nonzero
- -
nsmallest(self, n=5, keep='first')
Return the smallest `n` elements.

-Parameters
-----------
-n : int
-    Return this many ascending sorted values
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-bottom_n : Series
-    The n smallest values in the Series, in sorted order

-Notes
------
-Faster than ``.sort_values().head(n)`` for small `n` relative to
-the size of the ``Series`` object.

-See Also
---------
-Series.nlargest

-Examples
---------
->>> import pandas as pd
->>> import numpy as np
->>> s = pd.Series(np.random.randn(10**6))
->>> s.nsmallest(10)  # only sorts up to the N requested
-288532   -4.954580
-732345   -4.835960
-64803    -4.812550
-446457   -4.609998
-501225   -4.483945
-669476   -4.472935
-973615   -4.401699
-621279   -4.355126
-773916   -4.347355
-359919   -4.331927
-dtype: float64
- -
pow(self, other, level=None, fill_value=None, axis=0)
Exponential power of series and other, element-wise (binary operator `pow`).

-Equivalent to ``series ** other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rpow
- -
prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : scalar or Series (if level specified)
- -
product = prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : scalar or Series (if level specified)
- -
ptp(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Returns the difference between the maximum value and the
-            minimum value in the object. This is the equivalent of the
-            ``numpy.ndarray`` method ``ptp``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-ptp : scalar or Series (if level specified)
- -
put(self, *args, **kwargs)
Applies the `put` method to its `values` attribute
-if it has one.

-See also
---------
-numpy.ndarray.put
- -
quantile(self, q=0.5, interpolation='linear')
Return value at the given quantile, a la numpy.percentile.

-Parameters
-----------
-q : float or array-like, default 0.5 (50% quantile)
-    0 <= q <= 1, the quantile(s) to compute
-interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'}
-    .. versionadded:: 0.18.0

-    This optional parameter specifies the interpolation method to use,
-    when the desired quantile lies between two data points `i` and `j`:

-        * linear: `i + (j - i) * fraction`, where `fraction` is the
-          fractional part of the index surrounded by `i` and `j`.
-        * lower: `i`.
-        * higher: `j`.
-        * nearest: `i` or `j` whichever is nearest.
-        * midpoint: (`i` + `j`) / 2.

-Returns
--------
-quantile : float or Series
-    if ``q`` is an array, a Series will be returned where the
-    index is ``q`` and the values are the quantiles.

-Examples
---------
->>> s = Series([1, 2, 3, 4])
->>> s.quantile(.5)
-2.5
->>> s.quantile([.25, .5, .75])
-0.25    1.75
-0.50    2.50
-0.75    3.25
-dtype: float64
- -
radd(self, other, level=None, fill_value=None, axis=0)
Addition of series and other, element-wise (binary operator `radd`).

-Equivalent to ``other + series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.add
- -
ravel(self, order='C')
Return the flattened underlying data as an ndarray

-See also
---------
-numpy.ndarray.ravel
- -
rdiv = rtruediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.truediv
- -
reindex(self, index=None, **kwargs)
Conform Series to new index with optional filling logic, placing
-NA/NaN in locations having no value in the previous index. A new object
-is produced unless the new index is equivalent to the current one and
-copy=False

-Parameters
-----------
-index : array-like, optional (can be specified in order, or as
-    keywords)
-    New labels / index to conform to. Preferably an Index object to
-    avoid duplicating data
-method : {None, 'backfill'/'bfill', 'pad'/'ffill', 'nearest'}, optional
-    method to use for filling holes in reindexed DataFrame.
-    Please note: this is only  applicable to DataFrames/Series with a
-    monotonically increasing/decreasing index.

-    * default: don't fill gaps
-    * pad / ffill: propagate last valid observation forward to next
-      valid
-    * backfill / bfill: use next valid observation to fill gap
-    * nearest: use nearest valid observations to fill gap

-copy : boolean, default True
-    Return a new object, even if the passed indexes are the same
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-limit : int, default None
-    Maximum number of consecutive elements to forward or backward fill
-tolerance : optional
-    Maximum distance between original and new labels for inexact
-    matches. The values of the index at the matching locations most
-    satisfy the equation ``abs(index[indexer] - target) <= tolerance``.

-    .. versionadded:: 0.17.0

-Examples
---------

-Create a dataframe with some fictional data.

->>> index = ['Firefox', 'Chrome', 'Safari', 'IE10', 'Konqueror']
->>> df = pd.DataFrame({
-...      'http_status': [200,200,404,404,301],
-...      'response_time': [0.04, 0.02, 0.07, 0.08, 1.0]},
-...       index=index)
->>> df
-           http_status  response_time
-Firefox            200           0.04
-Chrome             200           0.02
-Safari             404           0.07
-IE10               404           0.08
-Konqueror          301           1.00

-Create a new index and reindex the dataframe. By default
-values in the new index that do not have corresponding
-records in the dataframe are assigned ``NaN``.

->>> new_index= ['Safari', 'Iceweasel', 'Comodo Dragon', 'IE10',
-...             'Chrome']
->>> df.reindex(new_index)
-               http_status  response_time
-Safari               404.0           0.07
-Iceweasel              NaN            NaN
-Comodo Dragon          NaN            NaN
-IE10                 404.0           0.08
-Chrome               200.0           0.02

-We can fill in the missing values by passing a value to
-the keyword ``fill_value``. Because the index is not monotonically
-increasing or decreasing, we cannot use arguments to the keyword
-``method`` to fill the ``NaN`` values.

->>> df.reindex(new_index, fill_value=0)
-               http_status  response_time
-Safari                 404           0.07
-Iceweasel                0           0.00
-Comodo Dragon            0           0.00
-IE10                   404           0.08
-Chrome                 200           0.02

->>> df.reindex(new_index, fill_value='missing')
-              http_status response_time
-Safari                404          0.07
-Iceweasel         missing       missing
-Comodo Dragon     missing       missing
-IE10                  404          0.08
-Chrome                200          0.02

-To further illustrate the filling functionality in
-``reindex``, we will create a dataframe with a
-monotonically increasing index (for example, a sequence
-of dates).

->>> date_index = pd.date_range('1/1/2010', periods=6, freq='D')
->>> df2 = pd.DataFrame({"prices": [100, 101, np.nan, 100, 89, 88]},
-...                    index=date_index)
->>> df2
-            prices
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88

-Suppose we decide to expand the dataframe to cover a wider
-date range.

->>> date_index2 = pd.date_range('12/29/2009', periods=10, freq='D')
->>> df2.reindex(date_index2)
-            prices
-2009-12-29     NaN
-2009-12-30     NaN
-2009-12-31     NaN
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-The index entries that did not have a value in the original data frame
-(for example, '2009-12-29') are by default filled with ``NaN``.
-If desired, we can fill in the missing values using one of several
-options.

-For example, to backpropagate the last valid value to fill the ``NaN``
-values, pass ``bfill`` as an argument to the ``method`` keyword.

->>> df2.reindex(date_index2, method='bfill')
-            prices
-2009-12-29     100
-2009-12-30     100
-2009-12-31     100
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-Please note that the ``NaN`` value present in the original dataframe
-(at index value 2010-01-03) will not be filled by any of the
-value propagation schemes. This is because filling while reindexing
-does not look at dataframe values, but only compares the original and
-desired indexes. If you do want to fill in the ``NaN`` values present
-in the original dataframe, use the ``fillna()`` method.

-Returns
--------
-reindexed : Series
- -
reindex_axis(self, labels, axis=0, **kwargs)
for compatibility with higher dims
- -
rename(self, index=None, **kwargs)
Alter axes input function or functions. Function / dict values must be
-unique (1-to-1). Labels not contained in a dict / Series will be left
-as-is. Extra labels listed don't throw an error. Alternatively, change
-``Series.name`` with a scalar value (Series only).

-Parameters
-----------
-index : scalar, list-like, dict-like or function, optional
-    Scalar or list-like will alter the ``Series.name`` attribute,
-    and raise on DataFrame or Panel.
-    dict-like or functions are transformations to apply to
-    that axis' values
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False
-    Whether to return a new Series. If True then value of copy is
-    ignored.
-level : int or level name, default None
-    In case of a MultiIndex, only rename labels in the specified
-    level.

-Returns
--------
-renamed : Series (new object)

-See Also
---------
-pandas.NDFrame.rename_axis

-Examples
---------
->>> s = pd.Series([1, 2, 3])
->>> s
-0    1
-1    2
-2    3
-dtype: int64
->>> s.rename("my_name") # scalar, changes Series.name
-0    1
-1    2
-2    3
-Name: my_name, dtype: int64
->>> s.rename(lambda x: x ** 2)  # function, changes labels
-0    1
-1    2
-4    3
-dtype: int64
->>> s.rename({1: 3, 2: 5})  # mapping, changes labels
-0    1
-3    2
-5    3
-dtype: int64
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename(2)
-Traceback (most recent call last):
-...
-TypeError: 'int' object is not callable
->>> df.rename(index=str, columns={"A": "a", "B": "c"})
-   a  c
-0  1  4
-1  2  5
-2  3  6
->>> df.rename(index=str, columns={"A": "a", "C": "c"})
-   a  B
-0  1  4
-1  2  5
-2  3  6
- -
reorder_levels(self, order)
Rearrange index levels using input order. May not drop or duplicate
-levels

-Parameters
-----------
-order : list of int representing new level order.
-       (reference level by number or key)
-axis : where to reorder levels

-Returns
--------
-type of caller (new object)
- -
repeat(self, repeats, *args, **kwargs)
Repeat elements of an Series. Refer to `numpy.ndarray.repeat`
-for more information about the `repeats` argument.

-See also
---------
-numpy.ndarray.repeat
- -
reset_index(self, level=None, drop=False, name=None, inplace=False)
Analogous to the :meth:`pandas.DataFrame.reset_index` function, see
-docstring there.

-Parameters
-----------
-level : int, str, tuple, or list, default None
-    Only remove the given levels from the index. Removes all levels by
-    default
-drop : boolean, default False
-    Do not try to insert index into dataframe columns
-name : object, default None
-    The name of the column corresponding to the Series values
-inplace : boolean, default False
-    Modify the Series in place (do not create a new object)

-Returns
-----------
-resetted : DataFrame, or Series if drop == True
- -
reshape(self, *args, **kwargs)
DEPRECATED: calling this method will raise an error in a
-future release. Please call ``.values.reshape(...)`` instead.

-return an ndarray with the values shape
-if the specified shape matches exactly the current shape, then
-return self (for compat)

-See also
---------
-numpy.ndarray.reshape
- -
rfloordiv(self, other, level=None, fill_value=None, axis=0)
Integer division of series and other, element-wise (binary operator `rfloordiv`).

-Equivalent to ``other // series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.floordiv
- -
rmod(self, other, level=None, fill_value=None, axis=0)
Modulo of series and other, element-wise (binary operator `rmod`).

-Equivalent to ``other % series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.mod
- -
rmul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `rmul`).

-Equivalent to ``other * series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.mul
- -
rolling(self, window, min_periods=None, freq=None, center=False, win_type=None, on=None, axis=0, closed=None)
Provides rolling window calculcations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-window : int, or offset
-    Size of the moving window. This is the number of observations used for
-    calculating the statistic. Each window will be a fixed size.

-    If its an offset then this will be the time period of each window. Each
-    window will be a variable sized based on the observations included in
-    the time-period. This is only valid for datetimelike indexes. This is
-    new in 0.19.0
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA). For a window that is specified by an offset,
-    this will default to 1.
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-win_type : string, default None
-    Provide a window type. See the notes below.
-on : string, optional
-    For a DataFrame, column on which to calculate
-    the rolling window, rather than the index
-closed : string, default None
-    Make the interval closed on the 'right', 'left', 'both' or
-    'neither' endpoints.
-    For offset-based windows, it defaults to 'right'.
-    For fixed windows, defaults to 'both'. Remaining cases not implemented
-    for fixed windows.

-    .. versionadded:: 0.20.0

-axis : int or string, default 0

-Returns
--------
-a Window or Rolling sub-classed for the particular operation

-Examples
---------

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]})
->>> df
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

-Rolling sum with a window length of 2, using the 'triang'
-window type.

->>> df.rolling(2, win_type='triang').sum()
-     B
-0  NaN
-1  1.0
-2  2.5
-3  NaN
-4  NaN

-Rolling sum with a window length of 2, min_periods defaults
-to the window length.

->>> df.rolling(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  NaN
-4  NaN

-Same as above, but explicity set the min_periods

->>> df.rolling(2, min_periods=1).sum()
-     B
-0  0.0
-1  1.0
-2  3.0
-3  2.0
-4  4.0

-A ragged (meaning not-a-regular frequency), time-indexed DataFrame

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]},
-....:                 index = [pd.Timestamp('20130101 09:00:00'),
-....:                          pd.Timestamp('20130101 09:00:02'),
-....:                          pd.Timestamp('20130101 09:00:03'),
-....:                          pd.Timestamp('20130101 09:00:05'),
-....:                          pd.Timestamp('20130101 09:00:06')])

->>> df
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  2.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0


-Contrasting to an integer rolling window, this will roll a variable
-length window corresponding to the time period.
-The default for min_periods is 1.

->>> df.rolling('2s').sum()
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  3.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-To learn more about the offsets & frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-The recognized win_types are:

-* ``boxcar``
-* ``triang``
-* ``blackman``
-* ``hamming``
-* ``bartlett``
-* ``parzen``
-* ``bohman``
-* ``blackmanharris``
-* ``nuttall``
-* ``barthann``
-* ``kaiser`` (needs beta)
-* ``gaussian`` (needs std)
-* ``general_gaussian`` (needs power, width)
-* ``slepian`` (needs width).
- -
round(self, decimals=0, *args, **kwargs)
Round each value in a Series to the given number of decimals.

-Parameters
-----------
-decimals : int
-    Number of decimal places to round to (default: 0).
-    If decimals is negative, it specifies the number of
-    positions to the left of the decimal point.

-Returns
--------
-Series object

-See Also
---------
-numpy.around
-DataFrame.round
- -
rpow(self, other, level=None, fill_value=None, axis=0)
Exponential power of series and other, element-wise (binary operator `rpow`).

-Equivalent to ``other ** series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.pow
- -
rsub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `rsub`).

-Equivalent to ``other - series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.sub
- -
rtruediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.truediv
- -
searchsorted(self, value, side='left', sorter=None)
Find indices where elements should be inserted to maintain order.

-Find the indices into a sorted Series `self` such that, if the
-corresponding elements in `value` were inserted before the indices,
-the order of `self` would be preserved.

-Parameters
-----------
-value : array_like
-    Values to insert into `self`.
-side : {'left', 'right'}, optional
-    If 'left', the index of the first suitable location found is given.
-    If 'right', return the last such index.  If there is no suitable
-    index, return either 0 or N (where N is the length of `self`).
-sorter : 1-D array_like, optional
-    Optional array of integer indices that sort `self` into ascending
-    order. They are typically the result of ``np.argsort``.

-Returns
--------
-indices : array of ints
-    Array of insertion points with the same shape as `value`.

-See Also
---------
-numpy.searchsorted

-Notes
------
-Binary search is used to find the required insertion points.

-Examples
---------

->>> x = pd.Series([1, 2, 3])
->>> x
-0    1
-1    2
-2    3
-dtype: int64

->>> x.searchsorted(4)
-array([3])

->>> x.searchsorted([0, 4])
-array([0, 3])

->>> x.searchsorted([1, 3], side='left')
-array([0, 2])

->>> x.searchsorted([1, 3], side='right')
-array([1, 3])

->>> x = pd.Categorical(['apple', 'bread', 'bread', 'cheese', 'milk' ])
-[apple, bread, bread, cheese, milk]
-Categories (4, object): [apple < bread < cheese < milk]

->>> x.searchsorted('bread')
-array([1])     # Note: an array, not a scalar

->>> x.searchsorted(['bread'])
-array([1])

->>> x.searchsorted(['bread', 'eggs'])
-array([1, 4])

->>> x.searchsorted(['bread', 'eggs'], side='right')
-array([3, 4])    # eggs before milk
- -
sem(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased standard error of the mean over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sem : scalar or Series (if level specified)
- -
set_value(self, label, value, takeable=False)
Quickly set single value at passed label. If label is not contained, a
-new object is created with the label placed at the end of the result
-index

-Parameters
-----------
-label : object
-    Partial indexing with MultiIndex not allowed
-value : object
-    Scalar value
-takeable : interpret the index as indexers, default False

-Returns
--------
-series : Series
-    If label is contained, will be reference to calling Series,
-    otherwise a new object
- -
shift(self, periods=1, freq=None, axis=0)
Shift index by desired number of periods with an optional time freq

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, optional
-    Increment to use from the tseries module or time rule (e.g. 'EOM').
-    See Notes.
-axis : {0, 'index'}

-Notes
------
-If freq is specified then the index values are shifted but the data
-is not realigned. That is, use freq if you would like to extend the
-index when shifting and preserve the original data.

-Returns
--------
-shifted : Series
- -
skew(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased skew over requested axis
-Normalized by N-1

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-skew : scalar or Series (if level specified)
- -
sort_index(self, axis=0, level=None, ascending=True, inplace=False, kind='quicksort', na_position='last', sort_remaining=True)
Sort object by labels (along an axis)

-Parameters
-----------
-axis : index to direct sorting
-level : int or level name or list of ints or list of level names
-    if not None, sort on values in specified index level(s)
-ascending : boolean, default True
-    Sort ascending vs. descending
-inplace : bool, default False
-    if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end.
-     Not implemented for MultiIndex.
-sort_remaining : bool, default True
-    if true and sorting by level and index is multilevel, sort by other
-    levels too (in order) after sorting by specified level

-Returns
--------
-sorted_obj : Series
- -
sort_values(self, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last')
Sort by the values along either axis

-.. versionadded:: 0.17.0

-Parameters
-----------
-axis : {0, 'index'}, default 0
-    Axis to direct sorting
-ascending : bool or list of bool, default True
-     Sort ascending vs. descending. Specify list for multiple sort
-     orders.  If this is a list of bools, must match the length of
-     the by.
-inplace : bool, default False
-     if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end

-Returns
--------
-sorted_obj : Series
- -
sortlevel(self, level=0, ascending=True, sort_remaining=True)
DEPRECATED: use :meth:`Series.sort_index`

-Sort Series with MultiIndex by chosen level. Data will be
-lexicographically sorted by the chosen level followed by the other
-levels (in order)

-Parameters
-----------
-level : int or level name, default None
-ascending : bool, default True

-Returns
--------
-sorted : Series

-See Also
---------
-Series.sort_index(level=...)
- -
std(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return sample standard deviation over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-std : scalar or Series (if level specified)
- -
sub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `sub`).

-Equivalent to ``series - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rsub
- -
subtract = sub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `sub`).

-Equivalent to ``series - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rsub
- -
sum(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the sum of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sum : scalar or Series (if level specified)
- -
swaplevel(self, i=-2, j=-1, copy=True)
Swap levels i and j in a MultiIndex

-Parameters
-----------
-i, j : int, string (can be mixed)
-    Level of index to be swapped. Can pass level name as string.

-Returns
--------
-swapped : Series

-.. versionchanged:: 0.18.1

-   The indexes ``i`` and ``j`` are now optional, and default to
-   the two innermost levels of the index.
- -
take(self, indices, axis=0, convert=True, is_copy=False, **kwargs)
return Series corresponding to requested indices

-Parameters
-----------
-indices : list / array of ints
-convert : translate negative to positive indices (default)

-Returns
--------
-taken : Series

-See also
---------
-numpy.ndarray.take
- -
to_csv(self, path=None, index=True, sep=',', na_rep='', float_format=None, header=False, index_label=None, mode='w', encoding=None, date_format=None, decimal='.')
Write Series to a comma-separated values (csv) file

-Parameters
-----------
-path : string or file handle, default None
-    File path or object, if None is provided the result is returned as
-    a string.
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-header : boolean, default False
-    Write out series name
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.
-mode : Python write mode, default 'w'
-sep : character, default ","
-    Field delimiter for the output file.
-encoding : string, optional
-    a string representing the encoding to use if the contents are
-    non-ascii, for python versions prior to 3
-date_format: string, default None
-    Format string for datetime objects.
-decimal: string, default '.'
-    Character recognized as decimal separator. E.g. use ',' for
-    European data
- -
to_dict(self)
Convert Series to {label -> value} dict

-Returns
--------
-value_dict : dict
- -
to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='', float_format=None, columns=None, header=True, index=True, index_label=None, startrow=0, startcol=0, engine=None, merge_cells=True, encoding=None, inf_rep='inf', verbose=True)
Write Series to an excel sheet

-.. versionadded:: 0.20.0


-Parameters
-----------
-excel_writer : string or ExcelWriter object
-    File path or existing ExcelWriter
-sheet_name : string, default 'Sheet1'
-    Name of sheet which will contain DataFrame
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-columns : sequence, optional
-    Columns to write
-header : boolean or list of string, default True
-    Write out column names. If a list of string is given it is
-    assumed to be aliases for the column names
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.
-startrow :
-    upper left cell row to dump data frame
-startcol :
-    upper left cell column to dump data frame
-engine : string, default None
-    write engine to use - you can also set this via the options
-    ``io.excel.xlsx.writer``, ``io.excel.xls.writer``, and
-    ``io.excel.xlsm.writer``.
-merge_cells : boolean, default True
-    Write MultiIndex and Hierarchical Rows as merged cells.
-encoding: string, default None
-    encoding of the resulting excel file. Only necessary for xlwt,
-    other writers support unicode natively.
-inf_rep : string, default 'inf'
-    Representation for infinity (there is no native representation for
-    infinity in Excel)
-freeze_panes : tuple of integer (length 2), default None
-    Specifies the one-based bottommost row and rightmost column that
-    is to be frozen

-    .. versionadded:: 0.20.0

-Notes
------
-If passing an existing ExcelWriter object, then the sheet will be added
-to the existing workbook.  This can be used to save different
-DataFrames to one workbook:

->>> writer = pd.ExcelWriter('output.xlsx')
->>> df1.to_excel(writer,'Sheet1')
->>> df2.to_excel(writer,'Sheet2')
->>> writer.save()

-For compatibility with to_csv, to_excel serializes lists and dicts to
-strings before writing.
- -
to_frame(self, name=None)
Convert Series to DataFrame

-Parameters
-----------
-name : object, default None
-    The passed name should substitute for the series name (if it has
-    one).

-Returns
--------
-data_frame : DataFrame
- -
to_period(self, freq=None, copy=True)
Convert Series from DatetimeIndex to PeriodIndex with desired
-frequency (inferred from index if not passed)

-Parameters
-----------
-freq : string, default

-Returns
--------
-ts : Series with PeriodIndex
- -
to_sparse(self, kind='block', fill_value=None)
Convert Series to SparseSeries

-Parameters
-----------
-kind : {'block', 'integer'}
-fill_value : float, defaults to NaN (missing)

-Returns
--------
-sp : SparseSeries
- -
to_string(self, buf=None, na_rep='NaN', float_format=None, header=True, index=True, length=False, dtype=False, name=False, max_rows=None)
Render a string representation of the Series

-Parameters
-----------
-buf : StringIO-like, optional
-    buffer to write to
-na_rep : string, optional
-    string representation of NAN to use, default 'NaN'
-float_format : one-parameter function, optional
-    formatter function to apply to columns' elements if they are floats
-    default None
-header: boolean, default True
-    Add the Series header (index name)
-index : bool, optional
-    Add index (row) labels, default True
-length : boolean, default False
-    Add the Series length
-dtype : boolean, default False
-    Add the Series dtype
-name : boolean, default False
-    Add the Series name if not None
-max_rows : int, optional
-    Maximum number of rows to show before truncating. If None, show
-    all.

-Returns
--------
-formatted : string (if not buffer passed)
- -
to_timestamp(self, freq=None, how='start', copy=True)
Cast to datetimeindex of timestamps, at *beginning* of period

-Parameters
-----------
-freq : string, default frequency of PeriodIndex
-    Desired frequency
-how : {'s', 'e', 'start', 'end'}
-    Convention for converting period to timestamp; start of period
-    vs. end

-Returns
--------
-ts : Series with DatetimeIndex
- -
tolist(self)
Convert Series to a nested list
- -
transform(self, func, *args, **kwargs)
Call function producing a like-indexed NDFrame
-and return a NDFrame with the transformed values`

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    To apply to column

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Returns
--------
-transformed : NDFrame

-Examples
---------
->>> df = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
-...                   index=pd.date_range('1/1/2000', periods=10))
-df.iloc[3:7] = np.nan

->>> df.transform(lambda x: (x - x.mean()) / x.std())
-                   A         B         C
-2000-01-01  0.579457  1.236184  0.123424
-2000-01-02  0.370357 -0.605875 -1.231325
-2000-01-03  1.455756 -0.277446  0.288967
-2000-01-04       NaN       NaN       NaN
-2000-01-05       NaN       NaN       NaN
-2000-01-06       NaN       NaN       NaN
-2000-01-07       NaN       NaN       NaN
-2000-01-08 -0.498658  1.274522  1.642524
-2000-01-09 -0.540524 -1.012676 -0.828968
-2000-01-10 -1.366388 -0.614710  0.005378

-See also
---------
-pandas.NDFrame.aggregate
-pandas.NDFrame.apply
- -
truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
unique(self)
Return unique values in the object. Uniques are returned in order
-of appearance, this does NOT sort. Hash table-based unique.

-Parameters
-----------
-values : 1d array-like

-Returns
--------
-unique values.
-  - If the input is an Index, the return is an Index
-  - If the input is a Categorical dtype, the return is a Categorical
-  - If the input is a Series/ndarray, the return will be an ndarray

-See Also
---------
-unique
-Index.unique
-Series.unique
- -
unstack(self, level=-1, fill_value=None)
Unstack, a.k.a. pivot, Series with MultiIndex to produce DataFrame.
-The level involved will automatically get sorted.

-Parameters
-----------
-level : int, string, or list of these, default last level
-    Level(s) to unstack, can pass level name
-fill_value : replace NaN with this value if the unstack produces
-    missing values

-    .. versionadded: 0.18.0

-Examples
---------
->>> s = pd.Series([1, 2, 3, 4],
-...     index=pd.MultiIndex.from_product([['one', 'two'], ['a', 'b']]))
->>> s
-one  a    1
-     b    2
-two  a    3
-     b    4
-dtype: int64

->>> s.unstack(level=-1)
-     a  b
-one  1  2
-two  3  4

->>> s.unstack(level=0)
-   one  two
-a    1    3
-b    2    4

-Returns
--------
-unstacked : DataFrame
- -
update(self, other)
Modify Series in place using non-NA values from passed
-Series. Aligns on index

-Parameters
-----------
-other : Series
- -
valid lambda self, inplace=False, **kwargs
- -
var(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased variance over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-var : scalar or Series (if level specified)
- -
view(self, dtype=None)
- -
-Class methods inherited from pandas.core.series.Series:
-
from_array(arr, index=None, name=None, dtype=None, copy=False, fastpath=False) from builtins.type
- -
from_csv(path, sep=',', parse_dates=True, header=None, index_col=0, encoding=None, infer_datetime_format=False) from builtins.type
Read CSV file (DISCOURAGED, please use :func:`pandas.read_csv`
-instead).

-It is preferable to use the more powerful :func:`pandas.read_csv`
-for most general purposes, but ``from_csv`` makes for an easy
-roundtrip to and from a file (the exact counterpart of
-``to_csv``), especially with a time Series.

-This method only differs from :func:`pandas.read_csv` in some defaults:

-- `index_col` is ``0`` instead of ``None`` (take first column as index
-  by default)
-- `header` is ``None`` instead of ``0`` (the first row is not used as
-  the column names)
-- `parse_dates` is ``True`` instead of ``False`` (try parsing the index
-  as datetime by default)

-With :func:`pandas.read_csv`, the option ``squeeze=True`` can be used
-to return a Series like ``from_csv``.

-Parameters
-----------
-path : string file path or file handle / StringIO
-sep : string, default ','
-    Field delimiter
-parse_dates : boolean, default True
-    Parse dates. Different default from read_table
-header : int, default None
-    Row to use as header (skip prior rows)
-index_col : int or sequence, default 0
-    Column to use for index. If a sequence is given, a MultiIndex
-    is used. Different default from read_table
-encoding : string, optional
-    a string representing the encoding to use if the contents are
-    non-ascii, for python versions prior to 3
-infer_datetime_format: boolean, default False
-    If True and `parse_dates` is True for a column, try to infer the
-    datetime format based on the first datetime string. If the format
-    can be inferred, there often will be a large parsing speed-up.

-See also
---------
-pandas.read_csv

-Returns
--------
-y : Series
- -
-Data descriptors inherited from pandas.core.series.Series:
-
asobject
-
return object Series which contains boxed values

-*this is an internal non-public method*
-
-
axes
-
Return a list of the row axis labels
-
-
dtype
-
return the dtype object of the underlying data
-
-
dtypes
-
return the dtype object of the underlying data
-
-
ftype
-
return if the data is sparse|dense
-
-
ftypes
-
return if the data is sparse|dense
-
-
imag
-
-
index
-
-
name
-
-
real
-
-
values
-
Return Series as ndarray or ndarray-like
-depending on the dtype

-Returns
--------
-arr : numpy.ndarray or ndarray-like

-Examples
---------
->>> pd.Series([1, 2, 3]).values
-array([1, 2, 3])

->>> pd.Series(list('aabc')).values
-array(['a', 'a', 'b', 'c'], dtype=object)

->>> pd.Series(list('aabc')).astype('category').values
-[a, a, b, c]
-Categories (3, object): [a, b, c]

-Timezone aware datetime data is converted to UTC:

->>> pd.Series(pd.date_range('20130101', periods=3,
-...                         tz='US/Eastern')).values
-array(['2013-01-01T05:00:00.000000000',
-       '2013-01-02T05:00:00.000000000',
-       '2013-01-03T05:00:00.000000000'], dtype='datetime64[ns]')
-
-
-Data and other attributes inherited from pandas.core.series.Series:
-
cat = <class 'pandas.core.categorical.CategoricalAccessor'>
Accessor object for categorical properties of the Series values.

-Be aware that assigning to `categories` is a inplace operation, while all
-methods return new categorical data per default (but can be called with
-`inplace=True`).

-Examples
---------
->>> s.cat.categories
->>> s.cat.categories = list('abc')
->>> s.cat.rename_categories(list('cab'))
->>> s.cat.reorder_categories(list('cab'))
->>> s.cat.add_categories(['d','e'])
->>> s.cat.remove_categories(['d'])
->>> s.cat.remove_unused_categories()
->>> s.cat.set_categories(list('abcde'))
->>> s.cat.as_ordered()
->>> s.cat.as_unordered()
- -
dt = <class 'pandas.core.indexes.accessors.CombinedDatetimelikeProperties'>
Accessor object for datetimelike properties of the Series values.

-Examples
---------
->>> s.dt.hour
->>> s.dt.second
->>> s.dt.quarter

-Returns a Series indexed like the original Series.
-Raises TypeError if the Series does not contain datetimelike values.
- -
plot = <class 'pandas.plotting._core.SeriesPlotMethods'>
Series plotting accessor and method

-Examples
---------
->>> s.plot.line()
->>> s.plot.bar()
->>> s.plot.hist()

-Plotting methods can also be accessed by calling the accessor as a method
-with the ``kind`` argument:
-``s.plot(kind='line')`` is equivalent to ``s.plot.line()``
- -
-Methods inherited from pandas.core.base.IndexOpsMixin:
-
factorize(self, sort=False, na_sentinel=-1)
Encode the object as an enumerated type or categorical variable

-Parameters
-----------
-sort : boolean, default False
-    Sort by values
-na_sentinel: int, default -1
-    Value to mark "not found"

-Returns
--------
-labels : the indexer to the original array
-uniques : the unique Index
- -
item(self)
return the first element of the underlying data as a python
-scalar
- -
nunique(self, dropna=True)
Return number of unique elements in the object.

-Excludes NA values by default.

-Parameters
-----------
-dropna : boolean, default True
-    Don't include NaN in the count.

-Returns
--------
-nunique : int
- -
transpose(self, *args, **kwargs)
return the transpose, which is by definition self
- -
value_counts(self, normalize=False, sort=True, ascending=False, bins=None, dropna=True)
Returns object containing counts of unique values.

-The resulting object will be in descending order so that the
-first element is the most frequently-occurring element.
-Excludes NA values by default.

-Parameters
-----------
-normalize : boolean, default False
-    If True then the object returned will contain the relative
-    frequencies of the unique values.
-sort : boolean, default True
-    Sort by values
-ascending : boolean, default False
-    Sort in ascending order
-bins : integer, optional
-    Rather than count values, group them into half-open bins,
-    a convenience for pd.cut, only works with numeric data
-dropna : boolean, default True
-    Don't include counts of NaN.

-Returns
--------
-counts : Series
- -
-Data descriptors inherited from pandas.core.base.IndexOpsMixin:
-
T
-
return the transpose, which is by definition self
-
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-
base
-
return the base object if the memory of the underlying data is
-shared
-
-
data
-
return the data pointer of the underlying data
-
-
empty
-
-
flags
-
return the ndarray.flags for the underlying data
-
-
hasnans
-
-
is_monotonic
-
Return boolean if values in the object are
-monotonic_increasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic : boolean
-
-
is_monotonic_decreasing
-
Return boolean if values in the object are
-monotonic_decreasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic_decreasing : boolean
-
-
is_monotonic_increasing
-
Return boolean if values in the object are
-monotonic_increasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic : boolean
-
-
is_unique
-
Return boolean if values in the object are unique

-Returns
--------
-is_unique : boolean
-
-
itemsize
-
return the size of the dtype of the item of the underlying data
-
-
nbytes
-
return the number of bytes in the underlying data
-
-
ndim
-
return the number of dimensions of the underlying data,
-by definition 1
-
-
shape
-
return a tuple of the shape of the underlying data
-
-
size
-
return the number of elements in the underlying data
-
-
strides
-
return the strides of the underlying data
-
-
-Data and other attributes inherited from pandas.core.base.IndexOpsMixin:
-
__array_priority__ = 1000
- -
-Data and other attributes inherited from pandas.core.strings.StringAccessorMixin:
-
str = <class 'pandas.core.strings.StringMethods'>
Vectorized string functions for Series and Index. NAs stay NA unless
-handled otherwise by a particular method. Patterned after Python's string
-methods, with some inspiration from R's stringr package.

-Examples
---------
->>> s.str.split('_')
->>> s.str.replace('_', '')
- -
-Methods inherited from pandas.core.generic.NDFrame:
-
__abs__(self)
- -
__bool__ = __nonzero__(self)
- -
__contains__(self, key)
True if the key is in the info axis
- -
__copy__(self, deep=True)
- -
__deepcopy__(self, memo=None)
- -
__delitem__(self, key)
Delete item
- -
__finalize__(self, other, method=None, **kwargs)
Propagate metadata from other to self.

-Parameters
-----------
-other : the object from which to get the attributes that we are going
-    to propagate
-method : optional, a passed method name ; possibly to take different
-    types of propagation actions based on this
- -
__getattr__(self, name)
After regular attribute access, try looking up the name
-This allows simpler access to columns for interactive use.
- -
__getstate__(self)
- -
__hash__(self)
Return hash(self).
- -
__invert__(self)
- -
__neg__(self)
- -
__nonzero__(self)
- -
__round__(self, decimals=0)
- -
__setattr__(self, name, value)
After regular attribute access, try setting the name
-This allows simpler access to columns for interactive use.
- -
__setstate__(self, state)
- -
abs(self)
Return an object with absolute value taken--only applicable to objects
-that are all numeric.

-Returns
--------
-abs: type of caller
- -
add_prefix(self, prefix)
Concatenate prefix string with panel items names.

-Parameters
-----------
-prefix : string

-Returns
--------
-with_prefix : type of caller
- -
add_suffix(self, suffix)
Concatenate suffix string with panel items names.

-Parameters
-----------
-suffix : string

-Returns
--------
-with_suffix : type of caller
- -
as_blocks(self, copy=True)
Convert the frame to a dict of dtype -> Constructor Types that each has
-a homogeneous dtype.

-NOTE: the dtypes of the blocks WILL BE PRESERVED HERE (unlike in
-      as_matrix)

-Parameters
-----------
-copy : boolean, default True

-       .. versionadded: 0.16.1

-Returns
--------
-values : a dict of dtype -> Constructor Types
- -
as_matrix(self, columns=None)
Convert the frame to its Numpy-array representation.

-Parameters
-----------
-columns: list, optional, default:None
-    If None, return all columns, otherwise, returns specified columns.

-Returns
--------
-values : ndarray
-    If the caller is heterogeneous and contains booleans or objects,
-    the result will be of dtype=object. See Notes.


-Notes
------
-Return is NOT a Numpy-matrix, rather, a Numpy-array.

-The dtype will be a lower-common-denominator dtype (implicit
-upcasting); that is to say if the dtypes (even of numeric types)
-are mixed, the one that accommodates all will be chosen. Use this
-with care if you are not dealing with the blocks.

-e.g. If the dtypes are float16 and float32, dtype will be upcast to
-float32.  If dtypes are int32 and uint8, dtype will be upcase to
-int32. By numpy.find_common_type convention, mixing int64 and uint64
-will result in a flot64 dtype.

-This method is provided for backwards compatibility. Generally,
-it is recommended to use '.values'.

-See Also
---------
-pandas.DataFrame.values
- -
asfreq(self, freq, method=None, how=None, normalize=False, fill_value=None)
Convert TimeSeries to specified frequency.

-Optionally provide filling method to pad/backfill missing values.

-Returns the original data conformed to a new index with the specified
-frequency. ``resample`` is more appropriate if an operation, such as
-summarization, is necessary to represent the data at the new frequency.

-Parameters
-----------
-freq : DateOffset object, or string
-method : {'backfill'/'bfill', 'pad'/'ffill'}, default None
-    Method to use for filling holes in reindexed Series (note this
-    does not fill NaNs that already were present):

-    * 'pad' / 'ffill': propagate last valid observation forward to next
-      valid
-    * 'backfill' / 'bfill': use NEXT valid observation to fill
-how : {'start', 'end'}, default end
-    For PeriodIndex only, see PeriodIndex.asfreq
-normalize : bool, default False
-    Whether to reset output index to midnight
-fill_value: scalar, optional
-    Value to use for missing values, applied during upsampling (note
-    this does not fill NaNs that already were present).

-    .. versionadded:: 0.20.0

-Returns
--------
-converted : type of caller

-Examples
---------

-Start by creating a series with 4 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=4, freq='T')
->>> series = pd.Series([0.0, None, 2.0, 3.0], index=index)
->>> df = pd.DataFrame({'s':series})
->>> df
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:03:00    3.0

-Upsample the series into 30 second bins.

->>> df.asfreq(freq='30S')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    NaN
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``fill value``.

->>> df.asfreq(freq='30S', fill_value=9.0)
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    9.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    9.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    9.0
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``method``.

->>> df.asfreq(freq='30S', method='bfill')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    2.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    3.0
-2000-01-01 00:03:00    3.0

-See Also
---------
-reindex

-Notes
------
-To learn more about the frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
- -
asof(self, where, subset=None)
The last row without any NaN is taken (or the last row without
-NaN considering only the subset of columns in the case of a DataFrame)

-.. versionadded:: 0.19.0 For DataFrame

-If there is no good value, NaN is returned for a Series
-a Series of NaN values for a DataFrame

-Parameters
-----------
-where : date or array of dates
-subset : string or list of strings, default None
-   if not None use these columns for NaN propagation

-Notes
------
-Dates are assumed to be sorted
-Raises if this is not the case

-Returns
--------
-where is scalar

-  - value or NaN if input is Series
-  - Series if input is DataFrame

-where is Index: same shape object as input

-See Also
---------
-merge_asof
- -
astype(self, dtype, copy=True, errors='raise', **kwargs)
Cast object to input numpy.dtype
-Return a copy when copy = True (be really careful with this!)

-Parameters
-----------
-dtype : data type, or dict of column name -> data type
-    Use a numpy.dtype or Python type to cast entire pandas object to
-    the same type. Alternatively, use {col: dtype, ...}, where col is a
-    column label and dtype is a numpy.dtype or Python type to cast one
-    or more of the DataFrame's columns to column-specific types.
-errors : {'raise', 'ignore'}, default 'raise'.
-    Control raising of exceptions on invalid data for provided dtype.

-    - ``raise`` : allow exceptions to be raised
-    - ``ignore`` : suppress exceptions. On error return original object

-    .. versionadded:: 0.20.0

-raise_on_error : DEPRECATED use ``errors`` instead
-kwargs : keyword arguments to pass on to the constructor

-Returns
--------
-casted : type of caller
- -
at_time(self, time, asof=False)
Select values at particular time of day (e.g. 9:30AM).

-Parameters
-----------
-time : datetime.time or string

-Returns
--------
-values_at_time : type of caller
- -
between_time(self, start_time, end_time, include_start=True, include_end=True)
Select values between particular times of the day (e.g., 9:00-9:30 AM).

-Parameters
-----------
-start_time : datetime.time or string
-end_time : datetime.time or string
-include_start : boolean, default True
-include_end : boolean, default True

-Returns
--------
-values_between_time : type of caller
- -
bfill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='bfill') <DataFrame.fillna>`
- -
bool(self)
Return the bool of a single element PandasObject.

-This must be a boolean scalar value, either True or False.  Raise a
-ValueError if the PandasObject does not have exactly 1 element, or that
-element is not boolean
- -
clip(self, lower=None, upper=None, axis=None, *args, **kwargs)
Trim values at input threshold(s).

-Parameters
-----------
-lower : float or array_like, default None
-upper : float or array_like, default None
-axis : int or string axis name, optional
-    Align object with lower and upper along the given axis.

-Returns
--------
-clipped : Series

-Examples
---------
->>> df
-  0         1
-0  0.335232 -1.256177
-1 -1.367855  0.746646
-2  0.027753 -1.176076
-3  0.230930 -0.679613
-4  1.261967  0.570967
->>> df.clip(-1.0, 0.5)
-          0         1
-0  0.335232 -1.000000
-1 -1.000000  0.500000
-2  0.027753 -1.000000
-3  0.230930 -0.679613
-4  0.500000  0.500000
->>> t
-0   -0.3
-1   -0.2
-2   -0.1
-3    0.0
-4    0.1
-dtype: float64
->>> df.clip(t, t + 1, axis=0)
-          0         1
-0  0.335232 -0.300000
-1 -0.200000  0.746646
-2  0.027753 -0.100000
-3  0.230930  0.000000
-4  1.100000  0.570967
- -
clip_lower(self, threshold, axis=None)
Return copy of the input with values below given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
clip_upper(self, threshold, axis=None)
Return copy of input with values above given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
consolidate(self, inplace=False)
DEPRECATED: consolidate will be an internal implementation only.
- -
convert_objects(self, convert_dates=True, convert_numeric=False, convert_timedeltas=True, copy=True)
Deprecated.

-Attempt to infer better dtype for object columns

-Parameters
-----------
-convert_dates : boolean, default True
-    If True, convert to date where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-convert_numeric : boolean, default False
-    If True, attempt to coerce to numbers (including strings), with
-    unconvertible values becoming NaN.
-convert_timedeltas : boolean, default True
-    If True, convert to timedelta where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-copy : boolean, default True
-    If True, return a copy even if no copy is necessary (e.g. no
-    conversion was done). Note: This is meant for internal use, and
-    should not be confused with inplace.

-See Also
---------
-pandas.to_datetime : Convert argument to datetime.
-pandas.to_timedelta : Convert argument to timedelta.
-pandas.to_numeric : Return a fixed frequency timedelta index,
-    with day as the default.

-Returns
--------
-converted : same as input object
- -
copy(self, deep=True)
Make a copy of this objects data.

-Parameters
-----------
-deep : boolean or string, default True
-    Make a deep copy, including a copy of the data and the indices.
-    With ``deep=False`` neither the indices or the data are copied.

-    Note that when ``deep=True`` data is copied, actual python objects
-    will not be copied recursively, only the reference to the object.
-    This is in contrast to ``copy.deepcopy`` in the Standard Library,
-    which recursively copies object data.

-Returns
--------
-copy : type of caller
- -
describe(self, percentiles=None, include=None, exclude=None)
Generates descriptive statistics that summarize the central tendency,
-dispersion and shape of a dataset's distribution, excluding
-``NaN`` values.

-Analyzes both numeric and object series, as well
-as ``DataFrame`` column sets of mixed data types. The output
-will vary depending on what is provided. Refer to the notes
-below for more detail.

-Parameters
-----------
-percentiles : list-like of numbers, optional
-    The percentiles to include in the output. All should
-    fall between 0 and 1. The default is
-    ``[.25, .5, .75]``, which returns the 25th, 50th, and
-    75th percentiles.
-include : 'all', list-like of dtypes or None (default), optional
-    A white list of data types to include in the result. Ignored
-    for ``Series``. Here are the options:

-    - 'all' : All columns of the input will be included in the output.
-    - A list-like of dtypes : Limits the results to the
-      provided data types.
-      To limit the result to numeric types submit
-      ``numpy.number``. To limit it instead to categorical
-      objects submit the ``numpy.object`` data type. Strings
-      can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will include all numeric columns.
-exclude : list-like of dtypes or None (default), optional,
-    A black list of data types to omit from the result. Ignored
-    for ``Series``. Here are the options:

-    - A list-like of dtypes : Excludes the provided data types
-      from the result. To select numeric types submit
-      ``numpy.number``. To select categorical objects submit the data
-      type ``numpy.object``. Strings can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will exclude nothing.

-Returns
--------
-summary:  Series/DataFrame of summary statistics

-Notes
------
-For numeric data, the result's index will include ``count``,
-``mean``, ``std``, ``min``, ``max`` as well as lower, ``50`` and
-upper percentiles. By default the lower percentile is ``25`` and the
-upper percentile is ``75``. The ``50`` percentile is the
-same as the median.

-For object data (e.g. strings or timestamps), the result's index
-will include ``count``, ``unique``, ``top``, and ``freq``. The ``top``
-is the most common value. The ``freq`` is the most common value's
-frequency. Timestamps also include the ``first`` and ``last`` items.

-If multiple object values have the highest count, then the
-``count`` and ``top`` results will be arbitrarily chosen from
-among those with the highest count.

-For mixed data types provided via a ``DataFrame``, the default is to
-return only an analysis of numeric columns. If ``include='all'``
-is provided as an option, the result will include a union of
-attributes of each type.

-The `include` and `exclude` parameters can be used to limit
-which columns in a ``DataFrame`` are analyzed for the output.
-The parameters are ignored when analyzing a ``Series``.

-Examples
---------
-Describing a numeric ``Series``.

->>> s = pd.Series([1, 2, 3])
->>> s.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0

-Describing a categorical ``Series``.

->>> s = pd.Series(['a', 'a', 'b', 'c'])
->>> s.describe()
-count     4
-unique    3
-top       a
-freq      2
-dtype: object

-Describing a timestamp ``Series``.

->>> s = pd.Series([
-...   np.datetime64("2000-01-01"),
-...   np.datetime64("2010-01-01"),
-...   np.datetime64("2010-01-01")
-... ])
->>> s.describe()
-count                       3
-unique                      2
-top       2010-01-01 00:00:00
-freq                        2
-first     2000-01-01 00:00:00
-last      2010-01-01 00:00:00
-dtype: object

-Describing a ``DataFrame``. By default only numeric fields
-are returned.

->>> df = pd.DataFrame([[1, 'a'], [2, 'b'], [3, 'c']],
-...                   columns=['numeric', 'object'])
->>> df.describe()
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Describing all columns of a ``DataFrame`` regardless of data type.

->>> df.describe(include='all')
-        numeric object
-count       3.0      3
-unique      NaN      3
-top         NaN      b
-freq        NaN      1
-mean        2.0    NaN
-std         1.0    NaN
-min         1.0    NaN
-25%         1.5    NaN
-50%         2.0    NaN
-75%         2.5    NaN
-max         3.0    NaN

-Describing a column from a ``DataFrame`` by accessing it as
-an attribute.

->>> df.numeric.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0
-Name: numeric, dtype: float64

-Including only numeric columns in a ``DataFrame`` description.

->>> df.describe(include=[np.number])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Including only string columns in a ``DataFrame`` description.

->>> df.describe(include=[np.object])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding numeric columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.number])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding object columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.object])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-See Also
---------
-DataFrame.count
-DataFrame.max
-DataFrame.min
-DataFrame.mean
-DataFrame.std
-DataFrame.select_dtypes
- -
drop(self, labels, axis=0, level=None, inplace=False, errors='raise')
Return new object with labels in requested axis removed.

-Parameters
-----------
-labels : single label or list-like
-axis : int or axis name
-level : int or level name, default None
-    For MultiIndex
-inplace : bool, default False
-    If True, do operation inplace and return None.
-errors : {'ignore', 'raise'}, default 'raise'
-    If 'ignore', suppress error and existing labels are dropped.

-    .. versionadded:: 0.16.1

-Returns
--------
-dropped : type of caller
- -
equals(self, other)
Determines if two NDFrame objects contain the same elements. NaNs in
-the same location are considered equal.
- -
ffill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='ffill') <DataFrame.fillna>`
- -
filter(self, items=None, like=None, regex=None, axis=None)
Subset rows or columns of dataframe according to labels in
-the specified index.

-Note that this routine does not filter a dataframe on its
-contents. The filter is applied to the labels of the index.

-Parameters
-----------
-items : list-like
-    List of info axis to restrict to (must not all be present)
-like : string
-    Keep info axis where "arg in col == True"
-regex : string (regular expression)
-    Keep info axis with re.search(regex, col) == True
-axis : int or string axis name
-    The axis to filter on.  By default this is the info axis,
-    'index' for Series, 'columns' for DataFrame

-Returns
--------
-same type as input object

-Examples
---------
->>> df
-one  two  three
-mouse     1    2      3
-rabbit    4    5      6

->>> # select columns by name
->>> df.filter(items=['one', 'three'])
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select columns by regular expression
->>> df.filter(regex='e$', axis=1)
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select rows containing 'bbi'
->>> df.filter(like='bbi', axis=0)
-one  two  three
-rabbit    4    5      6

-See Also
---------
-pandas.DataFrame.select

-Notes
------
-The ``items``, ``like``, and ``regex`` parameters are
-enforced to be mutually exclusive.

-``axis`` defaults to the info axis that is used when indexing
-with ``[]``.
- -
first(self, offset)
Convenience method for subsetting initial periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.first('10D') -> First 10 days

-Returns
--------
-subset : type of caller
- -
get(self, key, default=None)
Get item from object for given key (DataFrame column, Panel slice,
-etc.). Returns default value if not found.

-Parameters
-----------
-key : object

-Returns
--------
-value : type of items contained in object
- -
get_dtype_counts(self)
Return the counts of dtypes in this object.
- -
get_ftype_counts(self)
Return the counts of ftypes in this object.
- -
groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, **kwargs)
Group series using mapper (dict or key function, apply given function
-to group, return result as series) or by a series of columns.

-Parameters
-----------
-by : mapping, function, str, or iterable
-    Used to determine the groups for the groupby.
-    If ``by`` is a function, it's called on each value of the object's
-    index. If a dict or Series is passed, the Series or dict VALUES
-    will be used to determine the groups (the Series' values are first
-    aligned; see ``.align()`` method). If an ndarray is passed, the
-    values are used as-is determine the groups. A str or list of strs
-    may be passed to group by the columns in ``self``
-axis : int, default 0
-level : int, level name, or sequence of such, default None
-    If the axis is a MultiIndex (hierarchical), group by a particular
-    level or levels
-as_index : boolean, default True
-    For aggregated output, return object with group labels as the
-    index. Only relevant for DataFrame input. as_index=False is
-    effectively "SQL-style" grouped output
-sort : boolean, default True
-    Sort group keys. Get better performance by turning this off.
-    Note this does not influence the order of observations within each
-    group.  groupby preserves the order of rows within each group.
-group_keys : boolean, default True
-    When calling apply, add group keys to index to identify pieces
-squeeze : boolean, default False
-    reduce the dimensionality of the return type if possible,
-    otherwise return a consistent type

-Examples
---------
-DataFrame results

->>> data.groupby(func, axis=0).mean()
->>> data.groupby(['col1', 'col2'])['col3'].mean()

-DataFrame with hierarchical index

->>> data.groupby(['col1', 'col2']).mean()

-Returns
--------
-GroupBy object
- -
head(self, n=5)
Returns first n rows
- -
interpolate(self, method='linear', axis=0, limit=None, inplace=False, limit_direction='forward', downcast=None, **kwargs)
Interpolate values according to different methods.

-Please note that only ``method='linear'`` is supported for
-DataFrames/Series with a MultiIndex.

-Parameters
-----------
-method : {'linear', 'time', 'index', 'values', 'nearest', 'zero',
-          'slinear', 'quadratic', 'cubic', 'barycentric', 'krogh',
-          'polynomial', 'spline', 'piecewise_polynomial',
-          'from_derivatives', 'pchip', 'akima'}

-    * 'linear': ignore the index and treat the values as equally
-      spaced. This is the only method supported on MultiIndexes.
-      default
-    * 'time': interpolation works on daily and higher resolution
-      data to interpolate given length of interval
-    * 'index', 'values': use the actual numerical values of the index
-    * 'nearest', 'zero', 'slinear', 'quadratic', 'cubic',
-      'barycentric', 'polynomial' is passed to
-      ``scipy.interpolate.interp1d``. Both 'polynomial' and 'spline'
-      require that you also specify an `order` (int),
-      e.g. df.interpolate(method='polynomial', order=4).
-      These use the actual numerical values of the index.
-    * 'krogh', 'piecewise_polynomial', 'spline', 'pchip' and 'akima'
-      are all wrappers around the scipy interpolation methods of
-      similar names. These use the actual numerical values of the
-      index. For more information on their behavior, see the
-      `scipy documentation
-      <http://docs.scipy.org/doc/scipy/reference/interpolate.html#univariate-interpolation>`__
-      and `tutorial documentation
-      <http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html>`__
-    * 'from_derivatives' refers to BPoly.from_derivatives which
-      replaces 'piecewise_polynomial' interpolation method in
-      scipy 0.18

-    .. versionadded:: 0.18.1

-       Added support for the 'akima' method
-       Added interpolate method 'from_derivatives' which replaces
-       'piecewise_polynomial' in scipy 0.18; backwards-compatible with
-       scipy < 0.18

-axis : {0, 1}, default 0
-    * 0: fill column-by-column
-    * 1: fill row-by-row
-limit : int, default None.
-    Maximum number of consecutive NaNs to fill. Must be greater than 0.
-limit_direction : {'forward', 'backward', 'both'}, default 'forward'
-    If limit is specified, consecutive NaNs will be filled in this
-    direction.

-    .. versionadded:: 0.17.0

-inplace : bool, default False
-    Update the NDFrame in place if possible.
-downcast : optional, 'infer' or None, defaults to None
-    Downcast dtypes if possible.
-kwargs : keyword arguments to pass on to the interpolating function.

-Returns
--------
-Series or DataFrame of same shape interpolated at the NaNs

-See Also
---------
-reindex, replace, fillna

-Examples
---------

-Filling in NaNs

->>> s = pd.Series([0, 1, np.nan, 3])
->>> s.interpolate()
-0    0
-1    1
-2    2
-3    3
-dtype: float64
- -
isnull(self)
Return a boolean same-sized object indicating if the values are null.

-See Also
---------
-notnull : boolean inverse of isnull
- -
last(self, offset)
Convenience method for subsetting final periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.last('5M') -> Last 5 months

-Returns
--------
-subset : type of caller
- -
mask(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is False and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The mask method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``False`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``mask`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.where`
- -
notnull(self)
Return a boolean same-sized object indicating if the values are
-not null.

-See Also
---------
-isnull : boolean inverse of notnull
- -
pct_change(self, periods=1, fill_method='pad', limit=None, freq=None, **kwargs)
Percent change over given number of periods.

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming percent change
-fill_method : str, default 'pad'
-    How to handle NAs before computing percent changes
-limit : int, default None
-    The number of consecutive NAs to fill before stopping
-freq : DateOffset, timedelta, or offset alias string, optional
-    Increment to use from time series API (e.g. 'M' or BDay())

-Returns
--------
-chg : NDFrame

-Notes
------

-By default, the percentage change is calculated along the stat
-axis: 0, or ``Index``, for ``DataFrame`` and 1, or ``minor`` for
-``Panel``. You can change this with the ``axis`` keyword argument.
- -
pipe(self, func, *args, **kwargs)
Apply func(self, \*args, \*\*kwargs)

-.. versionadded:: 0.16.2

-Parameters
-----------
-func : function
-    function to apply to the NDFrame.
-    ``args``, and ``kwargs`` are passed into ``func``.
-    Alternatively a ``(callable, data_keyword)`` tuple where
-    ``data_keyword`` is a string indicating the keyword of
-    ``callable`` that expects the NDFrame.
-args : positional arguments passed into ``func``.
-kwargs : a dictionary of keyword arguments passed into ``func``.

-Returns
--------
-object : the return type of ``func``.

-Notes
------

-Use ``.pipe`` when chaining together functions that expect
-on Series or DataFrames. Instead of writing

->>> f(g(h(df), arg1=a), arg2=b, arg3=c)

-You can write

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe(f, arg2=b, arg3=c)
-... )

-If you have a function that takes the data as (say) the second
-argument, pass a tuple indicating which keyword expects the
-data. For example, suppose ``f`` takes its data as ``arg2``:

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe((f, 'arg2'), arg1=a, arg3=c)
-...  )

-See Also
---------
-pandas.DataFrame.apply
-pandas.DataFrame.applymap
-pandas.Series.map
- -
pop(self, item)
Return item and drop from frame. Raise KeyError if not found.
- -
rank(self, axis=0, method='average', numeric_only=None, na_option='keep', ascending=True, pct=False)
Compute numerical data ranks (1 through n) along axis. Equal values are
-assigned a rank that is the average of the ranks of those values

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    index to direct ranking
-method : {'average', 'min', 'max', 'first', 'dense'}
-    * average: average rank of group
-    * min: lowest rank in group
-    * max: highest rank in group
-    * first: ranks assigned in order they appear in the array
-    * dense: like 'min', but rank always increases by 1 between groups
-numeric_only : boolean, default None
-    Include only float, int, boolean data. Valid only for DataFrame or
-    Panel objects
-na_option : {'keep', 'top', 'bottom'}
-    * keep: leave NA values where they are
-    * top: smallest rank if ascending
-    * bottom: smallest rank if descending
-ascending : boolean, default True
-    False for ranks by high (1) to low (N)
-pct : boolean, default False
-    Computes percentage rank of data

-Returns
--------
-ranks : same type as caller
- -
reindex_like(self, other, method=None, copy=True, limit=None, tolerance=None)
Return an object with matching indices to myself.

-Parameters
-----------
-other : Object
-method : string or None
-copy : boolean, default True
-limit : int, default None
-    Maximum number of consecutive labels to fill for inexact matches.
-tolerance : optional
-    Maximum distance between labels of the other object and this
-    object for inexact matches.

-    .. versionadded:: 0.17.0

-Notes
------
-Like calling s.reindex(index=other.index, columns=other.columns,
-                       method=...)

-Returns
--------
-reindexed : same as input
- -
rename_axis(self, mapper, axis=0, copy=True, inplace=False)
Alter index and / or columns using input function or functions.
-A scalar or list-like for ``mapper`` will alter the ``Index.name``
-or ``MultiIndex.names`` attribute.
-A function or dict for ``mapper`` will alter the labels.
-Function / dict values must be unique (1-to-1). Labels not contained in
-a dict / Series will be left as-is.

-Parameters
-----------
-mapper : scalar, list-like, dict-like or function, optional
-axis : int or string, default 0
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False

-Returns
--------
-renamed : type of caller

-See Also
---------
-pandas.NDFrame.rename
-pandas.Index.rename

-Examples
---------
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename_axis("foo")  # scalar, alters df.index.name
-     A  B
-foo
-0    1  4
-1    2  5
-2    3  6
->>> df.rename_axis(lambda x: 2 * x)  # function: alters labels
-   A  B
-0  1  4
-2  2  5
-4  3  6
->>> df.rename_axis({"A": "ehh", "C": "see"}, axis="columns")  # mapping
-   ehh  B
-0    1  4
-1    2  5
-2    3  6
- -
replace(self, to_replace=None, value=None, inplace=False, limit=None, regex=False, method='pad', axis=None)
Replace values given in 'to_replace' with 'value'.

-Parameters
-----------
-to_replace : str, regex, list, dict, Series, numeric, or None

-    * str or regex:

-        - str: string exactly matching `to_replace` will be replaced
-          with `value`
-        - regex: regexs matching `to_replace` will be replaced with
-          `value`

-    * list of str, regex, or numeric:

-        - First, if `to_replace` and `value` are both lists, they
-          **must** be the same length.
-        - Second, if ``regex=True`` then all of the strings in **both**
-          lists will be interpreted as regexs otherwise they will match
-          directly. This doesn't matter much for `value` since there
-          are only a few possible substitution regexes you can use.
-        - str and regex rules apply as above.

-    * dict:

-        - Nested dictionaries, e.g., {'a': {'b': nan}}, are read as
-          follows: look in column 'a' for the value 'b' and replace it
-          with nan. You can nest regular expressions as well. Note that
-          column names (the top-level dictionary keys in a nested
-          dictionary) **cannot** be regular expressions.
-        - Keys map to column names and values map to substitution
-          values. You can treat this as a special case of passing two
-          lists except that you are specifying the column to search in.

-    * None:

-        - This means that the ``regex`` argument must be a string,
-          compiled regular expression, or list, dict, ndarray or Series
-          of such elements. If `value` is also ``None`` then this
-          **must** be a nested dictionary or ``Series``.

-    See the examples section for examples of each of these.
-value : scalar, dict, list, str, regex, default None
-    Value to use to fill holes (e.g. 0), alternately a dict of values
-    specifying which value to use for each column (columns not in the
-    dict will not be filled). Regular expressions, strings and lists or
-    dicts of such objects are also allowed.
-inplace : boolean, default False
-    If True, in place. Note: this will modify any
-    other views on this object (e.g. a column form a DataFrame).
-    Returns the caller if this is True.
-limit : int, default None
-    Maximum size gap to forward or backward fill
-regex : bool or same types as `to_replace`, default False
-    Whether to interpret `to_replace` and/or `value` as regular
-    expressions. If this is ``True`` then `to_replace` *must* be a
-    string. Otherwise, `to_replace` must be ``None`` because this
-    parameter will be interpreted as a regular expression or a list,
-    dict, or array of regular expressions.
-method : string, optional, {'pad', 'ffill', 'bfill'}
-    The method to use when for replacement, when ``to_replace`` is a
-    ``list``.

-See Also
---------
-NDFrame.reindex
-NDFrame.asfreq
-NDFrame.fillna

-Returns
--------
-filled : NDFrame

-Raises
-------
-AssertionError
-    * If `regex` is not a ``bool`` and `to_replace` is not ``None``.
-TypeError
-    * If `to_replace` is a ``dict`` and `value` is not a ``list``,
-      ``dict``, ``ndarray``, or ``Series``
-    * If `to_replace` is ``None`` and `regex` is not compilable into a
-      regular expression or is a list, dict, ndarray, or Series.
-ValueError
-    * If `to_replace` and `value` are ``list`` s or ``ndarray`` s, but
-      they are not the same length.

-Notes
------
-* Regex substitution is performed under the hood with ``re.sub``. The
-  rules for substitution for ``re.sub`` are the same.
-* Regular expressions will only substitute on strings, meaning you
-  cannot provide, for example, a regular expression matching floating
-  point numbers and expect the columns in your frame that have a
-  numeric dtype to be matched. However, if those floating point numbers
-  *are* strings, then you can do this.
-* This method has *a lot* of options. You are encouraged to experiment
-  and play with this method to gain intuition about how it works.
- -
resample(self, rule, how=None, axis=0, fill_method=None, closed=None, label=None, convention='start', kind=None, loffset=None, limit=None, base=0, on=None, level=None)
Convenience method for frequency conversion and resampling of time
-series.  Object must have a datetime-like index (DatetimeIndex,
-PeriodIndex, or TimedeltaIndex), or pass datetime-like values
-to the on or level keyword.

-Parameters
-----------
-rule : string
-    the offset string or object representing target conversion
-axis : int, optional, default 0
-closed : {'right', 'left'}
-    Which side of bin interval is closed
-label : {'right', 'left'}
-    Which bin edge label to label bucket with
-convention : {'start', 'end', 's', 'e'}
-loffset : timedelta
-    Adjust the resampled time labels
-base : int, default 0
-    For frequencies that evenly subdivide 1 day, the "origin" of the
-    aggregated intervals. For example, for '5min' frequency, base could
-    range from 0 through 4. Defaults to 0
-on : string, optional
-    For a DataFrame, column to use instead of index for resampling.
-    Column must be datetime-like.

-    .. versionadded:: 0.19.0

-level : string or int, optional
-    For a MultiIndex, level (name or number) to use for
-    resampling.  Level must be datetime-like.

-    .. versionadded:: 0.19.0

-Notes
------
-To learn more about the offset strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-Examples
---------

-Start by creating a series with 9 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=9, freq='T')
->>> series = pd.Series(range(9), index=index)
->>> series
-2000-01-01 00:00:00    0
-2000-01-01 00:01:00    1
-2000-01-01 00:02:00    2
-2000-01-01 00:03:00    3
-2000-01-01 00:04:00    4
-2000-01-01 00:05:00    5
-2000-01-01 00:06:00    6
-2000-01-01 00:07:00    7
-2000-01-01 00:08:00    8
-Freq: T, dtype: int64

-Downsample the series into 3 minute bins and sum the values
-of the timestamps falling into a bin.

->>> series.resample('3T').sum()
-2000-01-01 00:00:00     3
-2000-01-01 00:03:00    12
-2000-01-01 00:06:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but label each
-bin using the right edge instead of the left. Please note that the
-value in the bucket used as the label is not included in the bucket,
-which it labels. For example, in the original series the
-bucket ``2000-01-01 00:03:00`` contains the value 3, but the summed
-value in the resampled bucket with the label``2000-01-01 00:03:00``
-does not include 3 (if it did, the summed value would be 6, not 3).
-To include this value close the right side of the bin interval as
-illustrated in the example below this one.

->>> series.resample('3T', label='right').sum()
-2000-01-01 00:03:00     3
-2000-01-01 00:06:00    12
-2000-01-01 00:09:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but close the right
-side of the bin interval.

->>> series.resample('3T', label='right', closed='right').sum()
-2000-01-01 00:00:00     0
-2000-01-01 00:03:00     6
-2000-01-01 00:06:00    15
-2000-01-01 00:09:00    15
-Freq: 3T, dtype: int64

-Upsample the series into 30 second bins.

->>> series.resample('30S').asfreq()[0:5] #select first 5 rows
-2000-01-01 00:00:00   0.0
-2000-01-01 00:00:30   NaN
-2000-01-01 00:01:00   1.0
-2000-01-01 00:01:30   NaN
-2000-01-01 00:02:00   2.0
-Freq: 30S, dtype: float64

-Upsample the series into 30 second bins and fill the ``NaN``
-values using the ``pad`` method.

->>> series.resample('30S').pad()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    0
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    1
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Upsample the series into 30 second bins and fill the
-``NaN`` values using the ``bfill`` method.

->>> series.resample('30S').bfill()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    1
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    2
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Pass a custom function via ``apply``

->>> def custom_resampler(array_like):
-...     return np.sum(array_like)+5

->>> series.resample('3T').apply(custom_resampler)
-2000-01-01 00:00:00     8
-2000-01-01 00:03:00    17
-2000-01-01 00:06:00    26
-Freq: 3T, dtype: int64

-For DataFrame objects, the keyword ``on`` can be used to specify the
-column instead of the index for resampling.

->>> df = pd.DataFrame(data=9*[range(4)], columns=['a', 'b', 'c', 'd'])
->>> df['time'] = pd.date_range('1/1/2000', periods=9, freq='T')
->>> df.resample('3T', on='time').sum()
-                     a  b  c  d
-time
-2000-01-01 00:00:00  0  3  6  9
-2000-01-01 00:03:00  0  3  6  9
-2000-01-01 00:06:00  0  3  6  9

-For a DataFrame with MultiIndex, the keyword ``level`` can be used to
-specify on level the resampling needs to take place.

->>> time = pd.date_range('1/1/2000', periods=5, freq='T')
->>> df2 = pd.DataFrame(data=10*[range(4)],
-                       columns=['a', 'b', 'c', 'd'],
-                       index=pd.MultiIndex.from_product([time, [1, 2]])
-                       )
->>> df2.resample('3T', level=0).sum()
-                     a  b   c   d
-2000-01-01 00:00:00  0  6  12  18
-2000-01-01 00:03:00  0  4   8  12
- -
sample(self, n=None, frac=None, replace=False, weights=None, random_state=None, axis=None)
Returns a random sample of items from an axis of object.

-.. versionadded:: 0.16.1

-Parameters
-----------
-n : int, optional
-    Number of items from axis to return. Cannot be used with `frac`.
-    Default = 1 if `frac` = None.
-frac : float, optional
-    Fraction of axis items to return. Cannot be used with `n`.
-replace : boolean, optional
-    Sample with or without replacement. Default = False.
-weights : str or ndarray-like, optional
-    Default 'None' results in equal probability weighting.
-    If passed a Series, will align with target object on index. Index
-    values in weights not found in sampled object will be ignored and
-    index values in sampled object not in weights will be assigned
-    weights of zero.
-    If called on a DataFrame, will accept the name of a column
-    when axis = 0.
-    Unless weights are a Series, weights must be same length as axis
-    being sampled.
-    If weights do not sum to 1, they will be normalized to sum to 1.
-    Missing values in the weights column will be treated as zero.
-    inf and -inf values not allowed.
-random_state : int or numpy.random.RandomState, optional
-    Seed for the random number generator (if int), or numpy RandomState
-    object.
-axis : int or string, optional
-    Axis to sample. Accepts axis number or name. Default is stat axis
-    for given data type (0 for Series and DataFrames, 1 for Panels).

-Returns
--------
-A new object of same type as caller.

-Examples
---------

-Generate an example ``Series`` and ``DataFrame``:

->>> s = pd.Series(np.random.randn(50))
->>> s.head()
-0   -0.038497
-1    1.820773
-2   -0.972766
-3   -1.598270
-4   -1.095526
-dtype: float64
->>> df = pd.DataFrame(np.random.randn(50, 4), columns=list('ABCD'))
->>> df.head()
-          A         B         C         D
-0  0.016443 -2.318952 -0.566372 -1.028078
-1 -1.051921  0.438836  0.658280 -0.175797
-2 -1.243569 -0.364626 -0.215065  0.057736
-3  1.768216  0.404512 -0.385604 -1.457834
-4  1.072446 -1.137172  0.314194 -0.046661

-Next extract a random sample from both of these objects...

-3 random elements from the ``Series``:

->>> s.sample(n=3)
-27   -0.994689
-55   -1.049016
-67   -0.224565
-dtype: float64

-And a random 10% of the ``DataFrame`` with replacement:

->>> df.sample(frac=0.1, replace=True)
-           A         B         C         D
-35  1.981780  0.142106  1.817165 -0.290805
-49 -1.336199 -0.448634 -0.789640  0.217116
-40  0.823173 -0.078816  1.009536  1.015108
-15  1.421154 -0.055301 -1.922594 -0.019696
-6  -0.148339  0.832938  1.787600 -1.383767
- -
select(self, crit, axis=0)
Return data corresponding to axis labels matching criteria

-Parameters
-----------
-crit : function
-    To be called on each index (label). Should return True or False
-axis : int

-Returns
--------
-selection : type of caller
- -
set_axis(self, axis, labels)
public verson of axis assignment
- -
slice_shift(self, periods=1, axis=0)
Equivalent to `shift` without copying data. The shifted data will
-not include the dropped periods and the shifted axis will be smaller
-than the original.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative

-Notes
------
-While the `slice_shift` is faster than `shift`, you may pay for it
-later during alignment.

-Returns
--------
-shifted : same type as caller
- -
squeeze(self, axis=None)
Squeeze length 1 dimensions.

-Parameters
-----------
-axis : None, integer or string axis name, optional
-    The axis to squeeze if 1-sized.

-    .. versionadded:: 0.20.0

-Returns
--------
-scalar if 1-sized, else original object
- -
swapaxes(self, axis1, axis2, copy=True)
Interchange axes and swap values axes appropriately

-Returns
--------
-y : same as input
- -
tail(self, n=5)
Returns last n rows
- -
to_clipboard(self, excel=None, sep=None, **kwargs)
Attempt to write text representation of object to the system clipboard
-This can be pasted into Excel, for example.

-Parameters
-----------
-excel : boolean, defaults to True
-        if True, use the provided separator, writing in a csv
-        format for allowing easy pasting into excel.
-        if False, write a string representation of the object
-        to the clipboard
-sep : optional, defaults to tab
-other keywords are passed to to_csv

-Notes
------
-Requirements for your platform
-  - Linux: xclip, or xsel (with gtk or PyQt4 modules)
-  - Windows: none
-  - OS X: none
- -
to_dense(self)
Return dense representation of NDFrame (as opposed to sparse)
- -
to_hdf(self, path_or_buf, key, **kwargs)
Write the contained data to an HDF5 file using HDFStore.

-Parameters
-----------
-path_or_buf : the path (string) or HDFStore object
-key : string
-    identifier for the group in the store
-mode : optional, {'a', 'w', 'r+'}, default 'a'

-  ``'w'``
-      Write; a new file is created (an existing file with the same
-      name would be deleted).
-  ``'a'``
-      Append; an existing file is opened for reading and writing,
-      and if the file does not exist it is created.
-  ``'r+'``
-      It is similar to ``'a'``, but the file must already exist.
-format : 'fixed(f)|table(t)', default is 'fixed'
-    fixed(f) : Fixed format
-               Fast writing/reading. Not-appendable, nor searchable
-    table(t) : Table format
-               Write as a PyTables Table structure which may perform
-               worse but allow more flexible operations like searching
-               / selecting subsets of the data
-append : boolean, default False
-    For Table formats, append the input data to the existing
-data_columns :  list of columns, or True, default None
-    List of columns to create as indexed data columns for on-disk
-    queries, or True to use all columns. By default only the axes
-    of the object are indexed. See `here
-    <http://pandas.pydata.org/pandas-docs/stable/io.html#query-via-data-columns>`__.

-    Applicable only to format='table'.
-complevel : int, 1-9, default 0
-    If a complib is specified compression will be applied
-    where possible
-complib : {'zlib', 'bzip2', 'lzo', 'blosc', None}, default None
-    If complevel is > 0 apply compression to objects written
-    in the store wherever possible
-fletcher32 : bool, default False
-    If applying compression use the fletcher32 checksum
-dropna : boolean, default False.
-    If true, ALL nan rows will not be written to store.
- -
to_json(self, path_or_buf=None, orient=None, date_format=None, double_precision=10, force_ascii=True, date_unit='ms', default_handler=None, lines=False)
Convert the object to a JSON string.

-Note NaN's and None will be converted to null and datetime objects
-will be converted to UNIX timestamps.

-Parameters
-----------
-path_or_buf : the path or buffer to write the result string
-    if this is None, return a StringIO of the converted string
-orient : string

-    * Series

-      - default is 'index'
-      - allowed values are: {'split','records','index'}

-    * DataFrame

-      - default is 'columns'
-      - allowed values are:
-        {'split','records','index','columns','values'}

-    * The format of the JSON string

-      - split : dict like
-        {index -> [index], columns -> [columns], data -> [values]}
-      - records : list like
-        [{column -> value}, ... , {column -> value}]
-      - index : dict like {index -> {column -> value}}
-      - columns : dict like {column -> {index -> value}}
-      - values : just the values array
-      - table : dict like {'schema': {schema}, 'data': {data}}
-        describing the data, and the data component is
-        like ``orient='records'``.

-        .. versionchanged:: 0.20.0

-date_format : {None, 'epoch', 'iso'}
-    Type of date conversion. `epoch` = epoch milliseconds,
-    `iso` = ISO8601. The default depends on the `orient`. For
-    `orient='table'`, the default is `'iso'`. For all other orients,
-    the default is `'epoch'`.
-double_precision : The number of decimal places to use when encoding
-    floating point values, default 10.
-force_ascii : force encoded string to be ASCII, default True.
-date_unit : string, default 'ms' (milliseconds)
-    The time unit to encode to, governs timestamp and ISO8601
-    precision.  One of 's', 'ms', 'us', 'ns' for second, millisecond,
-    microsecond, and nanosecond respectively.
-default_handler : callable, default None
-    Handler to call if object cannot otherwise be converted to a
-    suitable format for JSON. Should receive a single argument which is
-    the object to convert and return a serialisable object.
-lines : boolean, default False
-    If 'orient' is 'records' write out line delimited json format. Will
-    throw ValueError if incorrect 'orient' since others are not list
-    like.

-    .. versionadded:: 0.19.0

-Returns
--------
-same type as input object with filtered info axis

-See Also
---------
-pd.read_json

-Examples
---------

->>> df = pd.DataFrame([['a', 'b'], ['c', 'd']],
-...                   index=['row 1', 'row 2'],
-...                   columns=['col 1', 'col 2'])
->>> df.to_json(orient='split')
-'{"columns":["col 1","col 2"],
-  "index":["row 1","row 2"],
-  "data":[["a","b"],["c","d"]]}'

-Encoding/decoding a Dataframe using ``'index'`` formatted JSON:

->>> df.to_json(orient='index')
-'{"row 1":{"col 1":"a","col 2":"b"},"row 2":{"col 1":"c","col 2":"d"}}'

-Encoding/decoding a Dataframe using ``'records'`` formatted JSON.
-Note that index labels are not preserved with this encoding.

->>> df.to_json(orient='records')
-'[{"col 1":"a","col 2":"b"},{"col 1":"c","col 2":"d"}]'

-Encoding with Table Schema

->>> df.to_json(orient='table')
-'{"schema": {"fields": [{"name": "index", "type": "string"},
-                        {"name": "col 1", "type": "string"},
-                        {"name": "col 2", "type": "string"}],
-             "primaryKey": "index",
-             "pandas_version": "0.20.0"},
-  "data": [{"index": "row 1", "col 1": "a", "col 2": "b"},
-           {"index": "row 2", "col 1": "c", "col 2": "d"}]}'
- -
to_msgpack(self, path_or_buf=None, encoding='utf-8', **kwargs)
msgpack (serialize) object to input file path

-THIS IS AN EXPERIMENTAL LIBRARY and the storage format
-may not be stable until a future release.

-Parameters
-----------
-path : string File path, buffer-like, or None
-    if None, return generated string
-append : boolean whether to append to an existing msgpack
-    (default is False)
-compress : type of compressor (zlib or blosc), default to None (no
-    compression)
- -
to_pickle(self, path, compression='infer')
Pickle (serialize) object to input file path.

-Parameters
-----------
-path : string
-    File path
-compression : {'infer', 'gzip', 'bz2', 'xz', None}, default 'infer'
-    a string representing the compression to use in the output file

-    .. versionadded:: 0.20.0
- -
to_sql(self, name, con, flavor=None, schema=None, if_exists='fail', index=True, index_label=None, chunksize=None, dtype=None)
Write records stored in a DataFrame to a SQL database.

-Parameters
-----------
-name : string
-    Name of SQL table
-con : SQLAlchemy engine or DBAPI2 connection (legacy mode)
-    Using SQLAlchemy makes it possible to use any DB supported by that
-    library. If a DBAPI2 object, only sqlite3 is supported.
-flavor : 'sqlite', default None
-    DEPRECATED: this parameter will be removed in a future version,
-    as 'sqlite' is the only supported option if SQLAlchemy is not
-    installed.
-schema : string, default None
-    Specify the schema (if database flavor supports this). If None, use
-    default schema.
-if_exists : {'fail', 'replace', 'append'}, default 'fail'
-    - fail: If table exists, do nothing.
-    - replace: If table exists, drop it, recreate it, and insert data.
-    - append: If table exists, insert data. Create if does not exist.
-index : boolean, default True
-    Write DataFrame index as a column.
-index_label : string or sequence, default None
-    Column label for index column(s). If None is given (default) and
-    `index` is True, then the index names are used.
-    A sequence should be given if the DataFrame uses MultiIndex.
-chunksize : int, default None
-    If not None, then rows will be written in batches of this size at a
-    time.  If None, all rows will be written at once.
-dtype : dict of column name to SQL type, default None
-    Optional specifying the datatype for columns. The SQL type should
-    be a SQLAlchemy type, or a string for sqlite3 fallback connection.
- -
to_xarray(self)
Return an xarray object from the pandas object.

-Returns
--------
-a DataArray for a Series
-a Dataset for a DataFrame
-a DataArray for higher dims

-Examples
---------
->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)})
->>> df
-   A    B    C
-0  1  foo  4.0
-1  1  bar  5.0
-2  2  foo  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (index: 3)
-Coordinates:
-  * index    (index) int64 0 1 2
-Data variables:
-    A        (index) int64 1 1 2
-    B        (index) object 'foo' 'bar' 'foo'
-    C        (index) float64 4.0 5.0 6.0

->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)}
-                     ).set_index(['B','A'])
->>> df
-         C
-B   A
-foo 1  4.0
-bar 1  5.0
-foo 2  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (A: 2, B: 2)
-Coordinates:
-  * B        (B) object 'bar' 'foo'
-  * A        (A) int64 1 2
-Data variables:
-    C        (B, A) float64 5.0 nan 4.0 6.0

->>> p = pd.Panel(np.arange(24).reshape(4,3,2),
-                 items=list('ABCD'),
-                 major_axis=pd.date_range('20130101', periods=3),
-                 minor_axis=['first', 'second'])
->>> p
-<class 'pandas.core.panel.Panel'>
-Dimensions: 4 (items) x 3 (major_axis) x 2 (minor_axis)
-Items axis: A to D
-Major_axis axis: 2013-01-01 00:00:00 to 2013-01-03 00:00:00
-Minor_axis axis: first to second

->>> p.to_xarray()
-<xarray.DataArray (items: 4, major_axis: 3, minor_axis: 2)>
-array([[[ 0,  1],
-        [ 2,  3],
-        [ 4,  5]],
-       [[ 6,  7],
-        [ 8,  9],
-        [10, 11]],
-       [[12, 13],
-        [14, 15],
-        [16, 17]],
-       [[18, 19],
-        [20, 21],
-        [22, 23]]])
-Coordinates:
-  * items       (items) object 'A' 'B' 'C' 'D'
-  * major_axis  (major_axis) datetime64[ns] 2013-01-01 2013-01-02 2013-01-03  # noqa
-  * minor_axis  (minor_axis) object 'first' 'second'

-Notes
------
-See the `xarray docs <http://xarray.pydata.org/en/stable/>`__
- -
truncate(self, before=None, after=None, axis=None, copy=True)
Truncates a sorted NDFrame before and/or after some particular
-index value. If the axis contains only datetime values, before/after
-parameters are converted to datetime values.

-Parameters
-----------
-before : date
-    Truncate before index value
-after : date
-    Truncate after index value
-axis : the truncation axis, defaults to the stat axis
-copy : boolean, default is True,
-    return a copy of the truncated section

-Returns
--------
-truncated : type of caller
- -
tshift(self, periods=1, freq=None, axis=0)
Shift the time index, using the index's frequency if available.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, default None
-    Increment to use from the tseries module or time rule (e.g. 'EOM')
-axis : int or basestring
-    Corresponds to the axis that contains the Index

-Notes
------
-If freq is not specified then tries to use the freq or inferred_freq
-attributes of the index. If neither of those attributes exist, a
-ValueError is thrown

-Returns
--------
-shifted : NDFrame
- -
tz_convert(self, tz, axis=0, level=None, copy=True)
Convert tz-aware axis to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to convert
-level : int, str, default None
-    If axis ia a MultiIndex, convert a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data

-Returns
--------

-Raises
-------
-TypeError
-    If the axis is tz-naive.
- -
tz_localize(self, tz, axis=0, level=None, copy=True, ambiguous='raise')
Localize tz-naive TimeSeries to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to localize
-level : int, str, default None
-    If axis ia a MultiIndex, localize a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data
-ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise'
-    - 'infer' will attempt to infer fall dst-transition hours based on
-      order
-    - bool-ndarray where True signifies a DST time, False designates
-      a non-DST time (note that this flag is only applicable for
-      ambiguous times)
-    - 'NaT' will return NaT where there are ambiguous times
-    - 'raise' will raise an AmbiguousTimeError if there are ambiguous
-      times
-infer_dst : boolean, default False (DEPRECATED)
-    Attempt to infer fall dst-transition hours based on order

-Returns
--------

-Raises
-------
-TypeError
-    If the TimeSeries is tz-aware and tz is not None.
- -
where(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is True and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The where method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``True`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``where`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.mask`
- -
xs(self, key, axis=0, level=None, drop_level=True)
Returns a cross-section (row(s) or column(s)) from the
-Series/DataFrame. Defaults to cross-section on the rows (axis=0).

-Parameters
-----------
-key : object
-    Some label contained in the index, or partially in a MultiIndex
-axis : int, default 0
-    Axis to retrieve cross-section on
-level : object, defaults to first n levels (n=1 or len(key))
-    In case of a key partially contained in a MultiIndex, indicate
-    which levels are used. Levels can be referred by label or position.
-drop_level : boolean, default True
-    If False, returns object with same levels as self.

-Examples
---------
->>> df
-   A  B  C
-a  4  5  2
-b  4  0  9
-c  9  7  3
->>> df.xs('a')
-A    4
-B    5
-C    2
-Name: a
->>> df.xs('C', axis=1)
-a    2
-b    9
-c    3
-Name: C

->>> df
-                    A  B  C  D
-first second third
-bar   one    1      4  1  8  9
-      two    1      7  5  5  0
-baz   one    1      6  6  8  0
-      three  2      5  3  5  3
->>> df.xs(('baz', 'three'))
-       A  B  C  D
-third
-2      5  3  5  3
->>> df.xs('one', level=1)
-             A  B  C  D
-first third
-bar   1      4  1  8  9
-baz   1      6  6  8  0
->>> df.xs(('baz', 2), level=[0, 'third'])
-        A  B  C  D
-second
-three   5  3  5  3

-Returns
--------
-xs : Series or DataFrame

-Notes
------
-xs is only for getting, not setting values.

-MultiIndex Slicers is a generic way to get/set values on any level or
-levels.  It is a superset of xs functionality, see
-:ref:`MultiIndex Slicers <advanced.mi_slicers>`
- -
-Data descriptors inherited from pandas.core.generic.NDFrame:
-
at
-
Fast label-based scalar accessor

-Similarly to ``loc``, ``at`` provides **label** based scalar lookups.
-You can also set using these indexers.
-
-
blocks
-
Internal property, property synonym for as_blocks()
-
-
iat
-
Fast integer location scalar accessor.

-Similarly to ``iloc``, ``iat`` provides **integer** based lookups.
-You can also set using these indexers.
-
-
iloc
-
Purely integer-location based indexing for selection by position.

-``.iloc[]`` is primarily integer position based (from ``0`` to
-``length-1`` of the axis), but may also be used with a boolean
-array.

-Allowed inputs are:

-- An integer, e.g. ``5``.
-- A list or array of integers, e.g. ``[4, 3, 0]``.
-- A slice object with ints, e.g. ``1:7``.
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.iloc`` will raise ``IndexError`` if a requested indexer is
-out-of-bounds, except *slice* indexers which allow out-of-bounds
-indexing (this conforms with python/numpy *slice* semantics).

-See more at :ref:`Selection by Position <indexing.integer>`
-
-
ix
-
A primarily label-location based indexer, with integer position
-fallback.

-``.ix[]`` supports mixed integer and label based access. It is
-primarily label based, but will fall back to integer positional
-access unless the corresponding axis is of integer type.

-``.ix`` is the most general indexer and will support any of the
-inputs in ``.loc`` and ``.iloc``. ``.ix`` also supports floating
-point label schemes. ``.ix`` is exceptionally useful when dealing
-with mixed positional and label based hierachical indexes.

-However, when an axis is integer based, ONLY label based access
-and not positional access is supported. Thus, in such cases, it's
-usually better to be explicit and use ``.iloc`` or ``.loc``.

-See more at :ref:`Advanced Indexing <advanced>`.
-
-
loc
-
Purely label-location based indexer for selection by label.

-``.loc[]`` is primarily label based, but may also be used with a
-boolean array.

-Allowed inputs are:

-- A single label, e.g. ``5`` or ``'a'``, (note that ``5`` is
-  interpreted as a *label* of the index, and **never** as an
-  integer position along the index).
-- A list or array of labels, e.g. ``['a', 'b', 'c']``.
-- A slice object with labels, e.g. ``'a':'f'`` (note that contrary
-  to usual python slices, **both** the start and the stop are included!).
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.loc`` will raise a ``KeyError`` when the items are not found.

-See more at :ref:`Selection by Label <indexing.label>`
-
-
-Data and other attributes inherited from pandas.core.generic.NDFrame:
-
is_copy = None
- -
-Methods inherited from pandas.core.base.PandasObject:
-
__dir__(self)
Provide method name lookup and completion
-Only provide 'public' methods
- -
__sizeof__(self)
Generates the total memory usage for a object that returns
-either a value or Series of values
- -
-Methods inherited from pandas.core.base.StringMixin:
-
__bytes__(self)
Return a string representation for a particular object.

-Invoked by bytes(obj) in py3 only.
-Yields a bytestring in both py2/py3.
- -
__repr__(self)
Return a string representation for a particular object.

-Yields Bytestring in Py2, Unicode String in py3.
- -
__str__(self)
Return a string representation for a particular Object

-Invoked by str(df) in both py2/py3.
-Yields Bytestring in Py2, Unicode String in py3.
- -

- - - - - - - -
 
-class Simplot(builtins.object)
   Provides a simplified interface to matplotlib.
 
 Methods defined here:
-
__init__(self)
Initializes the instance variables.
- -
get_figure_state(self, figure=None)
Gets the state of the current figure.

-figure: Figure

-returns: FigureState object
- -
-Data descriptors defined here:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-

- - - - - - - -
 
-class State(System)
   One-dimensional ndarray with axis labels (including time series).

-Labels need not be unique but must be a hashable type. The object
-supports both integer- and label-based indexing and provides a host of
-methods for performing operations involving the index. Statistical
-methods from ndarray have been overridden to automatically exclude
-missing data (currently represented as NaN).

-Operations between Series (+, -, /, *, **) align values based on their
-associated index values-- they need not be the same length. The result
-index will be the sorted union of the two indexes.

-Parameters
-----------
-data : array-like, dict, or scalar value
-    Contains data stored in Series
-index : array-like or Index (1d)
-    Values must be hashable and have the same length as `data`.
-    Non-unique index values are allowed. Will default to
-    RangeIndex(len(data)) if not provided. If both a dict and index
-    sequence are used, the index will override the keys found in the
-    dict.
-dtype : numpy.dtype or None
-    If None, dtype will be inferred
-copy : boolean, default False
-    Copy input data
 
 
Method resolution order:
-
State
-
System
-
MySeries
-
pandas.core.series.Series
-
pandas.core.base.IndexOpsMixin
-
pandas.core.strings.StringAccessorMixin
-
pandas.core.generic.NDFrame
-
pandas.core.base.PandasObject
-
pandas.core.base.StringMixin
-
pandas.core.base.SelectionMixin
-
builtins.object
-
-
-Methods inherited from System:
-
__init__(self, *args, **kwargs)
Initialize the series.

-If there are no positional arguments, use kwargs.

-If there is one positional argument, copy it.

-More than one positional argument is an error.
- -
-Data descriptors inherited from System:
-
T
-
Intercept the Series accessor object so we can use `T`
-as a row label and access it using dot notation.

-https://pandas.pydata.org/pandas-docs/stable/generated/
-pandas.Series.T.html#pandas.Series.T
-
-
dt
-
Intercept the Series accessor object so we can use `dt`
-as a row label and access it using dot notation.

-https://pandas.pydata.org/pandas-docs/stable/generated/
-pandas.Series.dt.html
-
-
-Methods inherited from MySeries:
-
set(self, **kwargs)
Uses keyword arguments to update the Series in place.

-Example: series.update(a=1, b=2)
- -
-Methods inherited from pandas.core.series.Series:
-
__add__ = wrapper(left, right, name='__add__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f2f0>)
- -
__and__ = wrapper(self, other)
- -
__array__(self, result=None)
the array interface, return my values
- -
__array_prepare__(self, result, context=None)
Gets called prior to a ufunc
- -
__array_wrap__(self, result, context=None)
Gets called after a ufunc
- -
__div__ = wrapper(left, right, name='__truediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f9d8>)
- -
__divmod__ = wrapper(left, right, name='__divmod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca343bf8>)
- -
__eq__ = wrapper(self, other, axis=None)
- -
__float__ = wrapper(self)
- -
__floordiv__ = wrapper(left, right, name='__floordiv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fb70>)
- -
__ge__ = wrapper(self, other, axis=None)
- -
__getitem__(self, key)
- -
__gt__ = wrapper(self, other, axis=None)
- -
__iadd__ = f(self, other)
- -
__imul__ = f(self, other)
- -
__int__ = wrapper(self)
- -
__ipow__ = f(self, other)
- -
__isub__ = f(self, other)
- -
__iter__(self)
provide iteration over the values of the Series
-box values if necessary
- -
__itruediv__ = f(self, other)
- -
__le__ = wrapper(self, other, axis=None)
- -
__len__(self)
return the length of the Series
- -
__long__ = wrapper(self)
- -
__lt__ = wrapper(self, other, axis=None)
- -
__mod__ = wrapper(left, right, name='__mod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fd08>)
- -
__mul__ = wrapper(left, right, name='__mul__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f840>)
- -
__ne__ = wrapper(self, other, axis=None)
- -
__or__ = wrapper(self, other)
- -
__pow__ = wrapper(left, right, name='__pow__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fea0>)
- -
__radd__ = wrapper(left, right, name='__radd__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f510>)
- -
__rand__ = wrapper(self, other)
- -
__rdiv__ = wrapper(left, right, name='__rtruediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342510>)
- -
__rfloordiv__ = wrapper(left, right, name='__rfloordiv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342730>)
- -
__rmod__ = wrapper(left, right, name='__rmod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342b70>)
- -
__rmul__ = wrapper(left, right, name='__rmul__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca3420d0>)
- -
__ror__ = wrapper(self, other)
- -
__rpow__ = wrapper(left, right, name='__rpow__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342950>)
- -
__rsub__ = wrapper(left, right, name='__rsub__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca3422f0>)
- -
__rtruediv__ = wrapper(left, right, name='__rtruediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342510>)
- -
__rxor__ = wrapper(self, other)
- -
__setitem__(self, key, value)
- -
__sub__ = wrapper(left, right, name='__sub__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f6a8>)
- -
__truediv__ = wrapper(left, right, name='__truediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f9d8>)
- -
__unicode__(self)
Return a string representation for a particular DataFrame

-Invoked by unicode(df) in py2 only. Yields a Unicode String in both
-py2/py3.
- -
__xor__ = wrapper(self, other)
- -
add(self, other, level=None, fill_value=None, axis=0)
Addition of series and other, element-wise (binary operator `add`).

-Equivalent to ``series + other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.radd
- -
agg = aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a Series or when passed to Series.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : Series

-Examples
---------

->>> s = Series(np.random.randn(10))

->>> s.agg('min')
--1.3018049988556679

->>> s.agg(['min', 'max'])
-min   -1.301805
-max    1.127688
-dtype: float64

-See also
---------
-pandas.Series.apply
-pandas.Series.transform
- -
aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a Series or when passed to Series.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : Series

-Examples
---------

->>> s = Series(np.random.randn(10))

->>> s.agg('min')
--1.3018049988556679

->>> s.agg(['min', 'max'])
-min   -1.301805
-max    1.127688
-dtype: float64

-See also
---------
-pandas.Series.apply
-pandas.Series.transform
- -
align(self, other, join='outer', axis=None, level=None, copy=True, fill_value=None, method=None, limit=None, fill_axis=0, broadcast_axis=None)
Align two object on their axes with the
-specified join method for each axis Index

-Parameters
-----------
-other : DataFrame or Series
-join : {'outer', 'inner', 'left', 'right'}, default 'outer'
-axis : allowed axis of the other object, default None
-    Align on index (0), columns (1), or both (None)
-level : int or level name, default None
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-copy : boolean, default True
-    Always returns new objects. If copy=False and no reindexing is
-    required then original objects are returned.
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-method : str, default None
-limit : int, default None
-fill_axis : {0, 'index'}, default 0
-    Filling axis, method and limit
-broadcast_axis : {0, 'index'}, default None
-    Broadcast values along this axis, if aligning two objects of
-    different dimensions

-    .. versionadded:: 0.17.0

-Returns
--------
-(left, right) : (Series, type of other)
-    Aligned objects
- -
all(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether all elements are True over requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-all : scalar or Series (if level specified)
- -
any(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether any element is True over requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-any : scalar or Series (if level specified)
- -
append(self, to_append, ignore_index=False, verify_integrity=False)
Concatenate two or more Series.

-Parameters
-----------
-to_append : Series or list/tuple of Series
-ignore_index : boolean, default False
-    If True, do not use the index labels.

-    .. versionadded: 0.19.0

-verify_integrity : boolean, default False
-    If True, raise Exception on creating index with duplicates

-Returns
--------
-appended : Series

-Examples
---------
->>> s1 = pd.Series([1, 2, 3])
->>> s2 = pd.Series([4, 5, 6])
->>> s3 = pd.Series([4, 5, 6], index=[3,4,5])
->>> s1.append(s2)
-0    1
-1    2
-2    3
-0    4
-1    5
-2    6
-dtype: int64

->>> s1.append(s3)
-0    1
-1    2
-2    3
-3    4
-4    5
-5    6
-dtype: int64

-With `ignore_index` set to True:

->>> s1.append(s2, ignore_index=True)
-0    1
-1    2
-2    3
-3    4
-4    5
-5    6
-dtype: int64

-With `verify_integrity` set to True:

->>> s1.append(s2, verify_integrity=True)
-Traceback (most recent call last):
-...
-ValueError: Indexes have overlapping values: [0, 1, 2]
- -
apply(self, func, convert_dtype=True, args=(), **kwds)
Invoke function on values of Series. Can be ufunc (a NumPy function
-that applies to the entire Series) or a Python function that only works
-on single values

-Parameters
-----------
-func : function
-convert_dtype : boolean, default True
-    Try to find better dtype for elementwise function results. If
-    False, leave as dtype=object
-args : tuple
-    Positional arguments to pass to function in addition to the value
-Additional keyword arguments will be passed as keywords to the function

-Returns
--------
-y : Series or DataFrame if func returns a Series

-See also
---------
-Series.map: For element-wise operations
-Series.agg: only perform aggregating type operations
-Series.transform: only perform transformating type operations

-Examples
---------

-Create a series with typical summer temperatures for each city.

->>> import pandas as pd
->>> import numpy as np
->>> series = pd.Series([20, 21, 12], index=['London',
-... 'New York','Helsinki'])
->>> series
-London      20
-New York    21
-Helsinki    12
-dtype: int64

-Square the values by defining a function and passing it as an
-argument to ``apply()``.

->>> def square(x):
-...     return x**2
->>> series.apply(square)
-London      400
-New York    441
-Helsinki    144
-dtype: int64

-Square the values by passing an anonymous function as an
-argument to ``apply()``.

->>> series.apply(lambda x: x**2)
-London      400
-New York    441
-Helsinki    144
-dtype: int64

-Define a custom function that needs additional positional
-arguments and pass these additional arguments using the
-``args`` keyword.

->>> def subtract_custom_value(x, custom_value):
-...     return x-custom_value

->>> series.apply(subtract_custom_value, args=(5,))
-London      15
-New York    16
-Helsinki     7
-dtype: int64

-Define a custom function that takes keyword arguments
-and pass these arguments to ``apply``.

->>> def add_custom_values(x, **kwargs):
-...     for month in kwargs:
-...         x+=kwargs[month]
-...         return x

->>> series.apply(add_custom_values, june=30, july=20, august=25)
-London      95
-New York    96
-Helsinki    87
-dtype: int64

-Use a function from the Numpy library.

->>> series.apply(np.log)
-London      2.995732
-New York    3.044522
-Helsinki    2.484907
-dtype: float64
- -
argmax = idxmax(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of maximum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmax : Index of maximum of values

-Notes
------
-This method is the Series version of ``ndarray.argmax``.

-See Also
---------
-DataFrame.idxmax
-numpy.ndarray.argmax
- -
argmin = idxmin(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of minimum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmin : Index of minimum of values

-Notes
------
-This method is the Series version of ``ndarray.argmin``.

-See Also
---------
-DataFrame.idxmin
-numpy.ndarray.argmin
- -
argsort(self, axis=0, kind='quicksort', order=None)
Overrides ndarray.argsort. Argsorts the value, omitting NA/null values,
-and places the result in the same locations as the non-NA values

-Parameters
-----------
-axis : int (can only be zero)
-kind : {'mergesort', 'quicksort', 'heapsort'}, default 'quicksort'
-    Choice of sorting algorithm. See np.sort for more
-    information. 'mergesort' is the only stable algorithm
-order : ignored

-Returns
--------
-argsorted : Series, with -1 indicated where nan values are present

-See also
---------
-numpy.ndarray.argsort
- -
autocorr(self, lag=1)
Lag-N autocorrelation

-Parameters
-----------
-lag : int, default 1
-    Number of lags to apply before performing autocorrelation.

-Returns
--------
-autocorr : float
- -
between(self, left, right, inclusive=True)
Return boolean Series equivalent to left <= series <= right. NA values
-will be treated as False

-Parameters
-----------
-left : scalar
-    Left boundary
-right : scalar
-    Right boundary

-Returns
--------
-is_between : Series
- -
combine(self, other, func, fill_value=nan)
Perform elementwise binary operation on two Series using given function
-with optional fill value when an index is missing from one Series or
-the other

-Parameters
-----------
-other : Series or scalar value
-func : function
-fill_value : scalar value

-Returns
--------
-result : Series
- -
combine_first(self, other)
Combine Series values, choosing the calling Series's values
-first. Result index will be the union of the two indexes

-Parameters
-----------
-other : Series

-Returns
--------
-y : Series
- -
compound(self, axis=None, skipna=None, level=None)
Return the compound percentage of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-compounded : scalar or Series (if level specified)
- -
compress(self, condition, *args, **kwargs)
Return selected slices of an array along given axis as a Series

-See also
---------
-numpy.ndarray.compress
- -
corr(self, other, method='pearson', min_periods=None)
Compute correlation with `other` Series, excluding missing values

-Parameters
-----------
-other : Series
-method : {'pearson', 'kendall', 'spearman'}
-    * pearson : standard correlation coefficient
-    * kendall : Kendall Tau correlation coefficient
-    * spearman : Spearman rank correlation
-min_periods : int, optional
-    Minimum number of observations needed to have a valid result


-Returns
--------
-correlation : float
- -
count(self, level=None)
Return number of non-NA/null observations in the Series

-Parameters
-----------
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a smaller Series

-Returns
--------
-nobs : int or Series (if level specified)
- -
cov(self, other, min_periods=None)
Compute covariance with Series, excluding missing values

-Parameters
-----------
-other : Series
-min_periods : int, optional
-    Minimum number of observations needed to have a valid result

-Returns
--------
-covariance : float

-Normalized by N-1 (unbiased estimator).
- -
cummax(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative max over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummax : scalar



-See also
---------
-pandas.core.window.Expanding.max : Similar functionality
-    but ignores ``NaN`` values.
- -
cummin(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative minimum over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummin : scalar



-See also
---------
-pandas.core.window.Expanding.min : Similar functionality
-    but ignores ``NaN`` values.
- -
cumprod(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative product over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumprod : scalar



-See also
---------
-pandas.core.window.Expanding.prod : Similar functionality
-    but ignores ``NaN`` values.
- -
cumsum(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative sum over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumsum : scalar



-See also
---------
-pandas.core.window.Expanding.sum : Similar functionality
-    but ignores ``NaN`` values.
- -
diff(self, periods=1)
1st discrete difference of object

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming difference

-Returns
--------
-diffed : Series
- -
div = truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
divide = truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
dot(self, other)
Matrix multiplication with DataFrame or inner-product with Series
-objects

-Parameters
-----------
-other : Series or DataFrame

-Returns
--------
-dot_product : scalar or Series
- -
drop_duplicates(self, keep='first', inplace=False)
Return Series with duplicate values removed

-Parameters
-----------

-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Drop duplicates except for the first occurrence.
-    - ``last`` : Drop duplicates except for the last occurrence.
-    - False : Drop all duplicates.
-inplace : boolean, default False
-If True, performs operation inplace and returns None.

-Returns
--------
-deduplicated : Series
- -
dropna(self, axis=0, inplace=False, **kwargs)
Return Series without null values

-Returns
--------
-valid : Series
-inplace : boolean, default False
-    Do operation in place.
- -
duplicated(self, keep='first')
Return boolean Series denoting duplicate values

-Parameters
-----------
-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Mark duplicates as ``True`` except for the first
-      occurrence.
-    - ``last`` : Mark duplicates as ``True`` except for the last
-      occurrence.
-    - False : Mark all duplicates as ``True``.

-Returns
--------
-duplicated : Series
- -
eq(self, other, level=None, fill_value=None, axis=0)
Equal to of series and other, element-wise (binary operator `eq`).

-Equivalent to ``series == other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
ewm(self, com=None, span=None, halflife=None, alpha=None, min_periods=0, freq=None, adjust=True, ignore_na=False, axis=0)
Provides exponential weighted functions

-.. versionadded:: 0.18.0

-Parameters
-----------
-com : float, optional
-    Specify decay in terms of center of mass,
-    :math:`\alpha = 1 / (1 + com),\text{ for } com \geq 0`
-span : float, optional
-    Specify decay in terms of span,
-    :math:`\alpha = 2 / (span + 1),\text{ for } span \geq 1`
-halflife : float, optional
-    Specify decay in terms of half-life,
-    :math:`\alpha = 1 - exp(log(0.5) / halflife),\text{ for } halflife > 0`
-alpha : float, optional
-    Specify smoothing factor :math:`\alpha` directly,
-    :math:`0 < \alpha \leq 1`

-    .. versionadded:: 0.18.0

-min_periods : int, default 0
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : None or string alias / date offset object, default=None (DEPRECATED)
-    Frequency to conform to before computing statistic
-adjust : boolean, default True
-    Divide by decaying adjustment factor in beginning periods to account
-    for imbalance in relative weightings (viewing EWMA as a moving average)
-ignore_na : boolean, default False
-    Ignore missing values when calculating weights;
-    specify True to reproduce pre-0.15.0 behavior

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.ewm(com=0.5).mean()
-          B
-0  0.000000
-1  0.750000
-2  1.615385
-3  1.615385
-4  3.670213

-Notes
------
-Exactly one of center of mass, span, half-life, and alpha must be provided.
-Allowed values and relationship between the parameters are specified in the
-parameter descriptions above; see the link at the end of this section for
-a detailed explanation.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-When adjust is True (default), weighted averages are calculated using
-weights (1-alpha)**(n-1), (1-alpha)**(n-2), ..., 1-alpha, 1.

-When adjust is False, weighted averages are calculated recursively as:
-   weighted_average[0] = arg[0];
-   weighted_average[i] = (1-alpha)*weighted_average[i-1] + alpha*arg[i].

-When ignore_na is False (default), weights are based on absolute positions.
-For example, the weights of x and y used in calculating the final weighted
-average of [x, None, y] are (1-alpha)**2 and 1 (if adjust is True), and
-(1-alpha)**2 and alpha (if adjust is False).

-When ignore_na is True (reproducing pre-0.15.0 behavior), weights are based
-on relative positions. For example, the weights of x and y used in
-calculating the final weighted average of [x, None, y] are 1-alpha and 1
-(if adjust is True), and 1-alpha and alpha (if adjust is False).

-More details can be found at
-http://pandas.pydata.org/pandas-docs/stable/computation.html#exponentially-weighted-windows
- -
expanding(self, min_periods=1, freq=None, center=False, axis=0)
Provides expanding transformations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-axis : int or string, default 0

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.expanding(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  3.0
-4  7.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).
- -
fillna(self, value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)
Fill NA/NaN values using the specified method

-Parameters
-----------
-value : scalar, dict, Series, or DataFrame
-    Value to use to fill holes (e.g. 0), alternately a
-    dict/Series/DataFrame of values specifying which value to use for
-    each index (for a Series) or column (for a DataFrame). (values not
-    in the dict/Series/DataFrame will not be filled). This value cannot
-    be a list.
-method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
-    Method to use for filling holes in reindexed Series
-    pad / ffill: propagate last valid observation forward to next valid
-    backfill / bfill: use NEXT valid observation to fill gap
-axis : {0, 'index'}
-inplace : boolean, default False
-    If True, fill in place. Note: this will modify any
-    other views on this object, (e.g. a no-copy slice for a column in a
-    DataFrame).
-limit : int, default None
-    If method is specified, this is the maximum number of consecutive
-    NaN values to forward/backward fill. In other words, if there is
-    a gap with more than this number of consecutive NaNs, it will only
-    be partially filled. If method is not specified, this is the
-    maximum number of entries along the entire axis where NaNs will be
-    filled. Must be greater than 0 if not None.
-downcast : dict, default is None
-    a dict of item->dtype of what to downcast if possible,
-    or the string 'infer' which will try to downcast to an appropriate
-    equal type (e.g. float64 to int64 if possible)

-See Also
---------
-reindex, asfreq

-Returns
--------
-filled : Series
- -
first_valid_index(self)
Return label for first non-NA/null value
- -
floordiv(self, other, level=None, fill_value=None, axis=0)
Integer division of series and other, element-wise (binary operator `floordiv`).

-Equivalent to ``series // other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rfloordiv
- -
ge(self, other, level=None, fill_value=None, axis=0)
Greater than or equal to of series and other, element-wise (binary operator `ge`).

-Equivalent to ``series >= other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
get_value(self, label, takeable=False)
Quickly retrieve single value at passed index label

-Parameters
-----------
-index : label
-takeable : interpret the index as indexers, default False

-Returns
--------
-value : scalar value
- -
get_values(self)
same as values (but handles sparseness conversions); is a view
- -
gt(self, other, level=None, fill_value=None, axis=0)
Greater than of series and other, element-wise (binary operator `gt`).

-Equivalent to ``series > other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
hist = hist_series(self, by=None, ax=None, grid=True, xlabelsize=None, xrot=None, ylabelsize=None, yrot=None, figsize=None, bins=10, **kwds)
Draw histogram of the input series using matplotlib

-Parameters
-----------
-by : object, optional
-    If passed, then used to form histograms for separate groups
-ax : matplotlib axis object
-    If not passed, uses gca()
-grid : boolean, default True
-    Whether to show axis grid lines
-xlabelsize : int, default None
-    If specified changes the x-axis label size
-xrot : float, default None
-    rotation of x axis labels
-ylabelsize : int, default None
-    If specified changes the y-axis label size
-yrot : float, default None
-    rotation of y axis labels
-figsize : tuple, default None
-    figure size in inches by default
-bins: integer, default 10
-    Number of histogram bins to be used
-kwds : keywords
-    To be passed to the actual plotting function

-Notes
------
-See matplotlib documentation online for more on this
- -
idxmax(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of maximum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmax : Index of maximum of values

-Notes
------
-This method is the Series version of ``ndarray.argmax``.

-See Also
---------
-DataFrame.idxmax
-numpy.ndarray.argmax
- -
idxmin(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of minimum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmin : Index of minimum of values

-Notes
------
-This method is the Series version of ``ndarray.argmin``.

-See Also
---------
-DataFrame.idxmin
-numpy.ndarray.argmin
- -
isin(self, values)
Return a boolean :class:`~pandas.Series` showing whether each element
-in the :class:`~pandas.Series` is exactly contained in the passed
-sequence of ``values``.

-Parameters
-----------
-values : set or list-like
-    The sequence of values to test. Passing in a single string will
-    raise a ``TypeError``. Instead, turn a single string into a
-    ``list`` of one element.

-    .. versionadded:: 0.18.1

-    Support for values as a set

-Returns
--------
-isin : Series (bool dtype)

-Raises
-------
-TypeError
-  * If ``values`` is a string

-See Also
---------
-pandas.DataFrame.isin

-Examples
---------

->>> s = pd.Series(list('abc'))
->>> s.isin(['a', 'c', 'e'])
-0     True
-1    False
-2     True
-dtype: bool

-Passing a single string as ``s.isin('a')`` will raise an error. Use
-a list of one element instead:

->>> s.isin(['a'])
-0     True
-1    False
-2    False
-dtype: bool
- -
items = iteritems(self)
Lazily iterate over (index, value) tuples
- -
iteritems(self)
Lazily iterate over (index, value) tuples
- -
keys(self)
Alias for index
- -
kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : scalar or Series (if level specified)
- -
kurtosis = kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : scalar or Series (if level specified)
- -
last_valid_index(self)
Return label for last non-NA/null value
- -
le(self, other, level=None, fill_value=None, axis=0)
Less than or equal to of series and other, element-wise (binary operator `le`).

-Equivalent to ``series <= other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
lt(self, other, level=None, fill_value=None, axis=0)
Less than of series and other, element-wise (binary operator `lt`).

-Equivalent to ``series < other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
mad(self, axis=None, skipna=None, level=None)
Return the mean absolute deviation of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mad : scalar or Series (if level specified)
- -
map(self, arg, na_action=None)
Map values of Series using input correspondence (which can be
-a dict, Series, or function)

-Parameters
-----------
-arg : function, dict, or Series
-na_action : {None, 'ignore'}
-    If 'ignore', propagate NA values, without passing them to the
-    mapping function

-Returns
--------
-y : Series
-    same index as caller

-Examples
---------

-Map inputs to outputs (both of type `Series`)

->>> x = pd.Series([1,2,3], index=['one', 'two', 'three'])
->>> x
-one      1
-two      2
-three    3
-dtype: int64

->>> y = pd.Series(['foo', 'bar', 'baz'], index=[1,2,3])
->>> y
-1    foo
-2    bar
-3    baz

->>> x.map(y)
-one   foo
-two   bar
-three baz

-If `arg` is a dictionary, return a new Series with values converted
-according to the dictionary's mapping:

->>> z = {1: 'A', 2: 'B', 3: 'C'}

->>> x.map(z)
-one   A
-two   B
-three C

-Use na_action to control whether NA values are affected by the mapping
-function.

->>> s = pd.Series([1, 2, 3, np.nan])

->>> s2 = s.map('this is a string {}'.format, na_action=None)
-0    this is a string 1.0
-1    this is a string 2.0
-2    this is a string 3.0
-3    this is a string nan
-dtype: object

->>> s3 = s.map('this is a string {}'.format, na_action='ignore')
-0    this is a string 1.0
-1    this is a string 2.0
-2    this is a string 3.0
-3                     NaN
-dtype: object

-See Also
---------
-Series.apply: For applying more complex functions on a Series
-DataFrame.apply: Apply a function row-/column-wise
-DataFrame.applymap: Apply a function elementwise on a whole DataFrame

-Notes
------
-When `arg` is a dictionary, values in Series that are not in the
-dictionary (as keys) are converted to ``NaN``. However, if the
-dictionary is a ``dict`` subclass that defines ``__missing__`` (i.e.
-provides a method for default values), then this default is used
-rather than ``NaN``:

->>> from collections import Counter
->>> counter = Counter()
->>> counter['bar'] += 1
->>> y.map(counter)
-1    0
-2    1
-3    0
-dtype: int64
- -
max(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the maximum of the values in the object.
-            If you want the *index* of the maximum, use ``idxmax``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmax``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-max : scalar or Series (if level specified)
- -
mean(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the mean of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mean : scalar or Series (if level specified)
- -
median(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the median of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-median : scalar or Series (if level specified)
- -
memory_usage(self, index=True, deep=False)
Memory usage of the Series

-Parameters
-----------
-index : bool
-    Specifies whether to include memory usage of Series index
-deep : bool
-    Introspect the data deeply, interrogate
-    `object` dtypes for system-level memory consumption

-Returns
--------
-scalar bytes of memory consumed

-Notes
------
-Memory usage does not include memory consumed by elements that
-are not components of the array if deep=False

-See Also
---------
-numpy.ndarray.nbytes
- -
min(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the minimum of the values in the object.
-            If you want the *index* of the minimum, use ``idxmin``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmin``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-min : scalar or Series (if level specified)
- -
mod(self, other, level=None, fill_value=None, axis=0)
Modulo of series and other, element-wise (binary operator `mod`).

-Equivalent to ``series % other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmod
- -
mode(self)
Return the mode(s) of the dataset.

-Always returns Series even if only one value is returned.

-Returns
--------
-modes : Series (sorted)
- -
mul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `mul`).

-Equivalent to ``series * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmul
- -
multiply = mul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `mul`).

-Equivalent to ``series * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmul
- -
ne(self, other, level=None, fill_value=None, axis=0)
Not equal to of series and other, element-wise (binary operator `ne`).

-Equivalent to ``series != other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
nlargest(self, n=5, keep='first')
Return the largest `n` elements.

-Parameters
-----------
-n : int
-    Return this many descending sorted values
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-top_n : Series
-    The n largest values in the Series, in sorted order

-Notes
------
-Faster than ``.sort_values(ascending=False).head(n)`` for small `n`
-relative to the size of the ``Series`` object.

-See Also
---------
-Series.nsmallest

-Examples
---------
->>> import pandas as pd
->>> import numpy as np
->>> s = pd.Series(np.random.randn(10**6))
->>> s.nlargest(10)  # only sorts up to the N requested
-219921    4.644710
-82124     4.608745
-421689    4.564644
-425277    4.447014
-718691    4.414137
-43154     4.403520
-283187    4.313922
-595519    4.273635
-503969    4.250236
-121637    4.240952
-dtype: float64
- -
nonzero(self)
Return the indices of the elements that are non-zero

-This method is equivalent to calling `numpy.nonzero` on the
-series data. For compatability with NumPy, the return value is
-the same (a tuple with an array of indices for each dimension),
-but it will always be a one-item tuple because series only have
-one dimension.

-Examples
---------
->>> s = pd.Series([0, 3, 0, 4])
->>> s.nonzero()
-(array([1, 3]),)
->>> s.iloc[s.nonzero()[0]]
-1    3
-3    4
-dtype: int64

-See Also
---------
-numpy.nonzero
- -
nsmallest(self, n=5, keep='first')
Return the smallest `n` elements.

-Parameters
-----------
-n : int
-    Return this many ascending sorted values
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-bottom_n : Series
-    The n smallest values in the Series, in sorted order

-Notes
------
-Faster than ``.sort_values().head(n)`` for small `n` relative to
-the size of the ``Series`` object.

-See Also
---------
-Series.nlargest

-Examples
---------
->>> import pandas as pd
->>> import numpy as np
->>> s = pd.Series(np.random.randn(10**6))
->>> s.nsmallest(10)  # only sorts up to the N requested
-288532   -4.954580
-732345   -4.835960
-64803    -4.812550
-446457   -4.609998
-501225   -4.483945
-669476   -4.472935
-973615   -4.401699
-621279   -4.355126
-773916   -4.347355
-359919   -4.331927
-dtype: float64
- -
pow(self, other, level=None, fill_value=None, axis=0)
Exponential power of series and other, element-wise (binary operator `pow`).

-Equivalent to ``series ** other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rpow
- -
prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : scalar or Series (if level specified)
- -
product = prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : scalar or Series (if level specified)
- -
ptp(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Returns the difference between the maximum value and the
-            minimum value in the object. This is the equivalent of the
-            ``numpy.ndarray`` method ``ptp``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-ptp : scalar or Series (if level specified)
- -
put(self, *args, **kwargs)
Applies the `put` method to its `values` attribute
-if it has one.

-See also
---------
-numpy.ndarray.put
- -
quantile(self, q=0.5, interpolation='linear')
Return value at the given quantile, a la numpy.percentile.

-Parameters
-----------
-q : float or array-like, default 0.5 (50% quantile)
-    0 <= q <= 1, the quantile(s) to compute
-interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'}
-    .. versionadded:: 0.18.0

-    This optional parameter specifies the interpolation method to use,
-    when the desired quantile lies between two data points `i` and `j`:

-        * linear: `i + (j - i) * fraction`, where `fraction` is the
-          fractional part of the index surrounded by `i` and `j`.
-        * lower: `i`.
-        * higher: `j`.
-        * nearest: `i` or `j` whichever is nearest.
-        * midpoint: (`i` + `j`) / 2.

-Returns
--------
-quantile : float or Series
-    if ``q`` is an array, a Series will be returned where the
-    index is ``q`` and the values are the quantiles.

-Examples
---------
->>> s = Series([1, 2, 3, 4])
->>> s.quantile(.5)
-2.5
->>> s.quantile([.25, .5, .75])
-0.25    1.75
-0.50    2.50
-0.75    3.25
-dtype: float64
- -
radd(self, other, level=None, fill_value=None, axis=0)
Addition of series and other, element-wise (binary operator `radd`).

-Equivalent to ``other + series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.add
- -
ravel(self, order='C')
Return the flattened underlying data as an ndarray

-See also
---------
-numpy.ndarray.ravel
- -
rdiv = rtruediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.truediv
- -
reindex(self, index=None, **kwargs)
Conform Series to new index with optional filling logic, placing
-NA/NaN in locations having no value in the previous index. A new object
-is produced unless the new index is equivalent to the current one and
-copy=False

-Parameters
-----------
-index : array-like, optional (can be specified in order, or as
-    keywords)
-    New labels / index to conform to. Preferably an Index object to
-    avoid duplicating data
-method : {None, 'backfill'/'bfill', 'pad'/'ffill', 'nearest'}, optional
-    method to use for filling holes in reindexed DataFrame.
-    Please note: this is only  applicable to DataFrames/Series with a
-    monotonically increasing/decreasing index.

-    * default: don't fill gaps
-    * pad / ffill: propagate last valid observation forward to next
-      valid
-    * backfill / bfill: use next valid observation to fill gap
-    * nearest: use nearest valid observations to fill gap

-copy : boolean, default True
-    Return a new object, even if the passed indexes are the same
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-limit : int, default None
-    Maximum number of consecutive elements to forward or backward fill
-tolerance : optional
-    Maximum distance between original and new labels for inexact
-    matches. The values of the index at the matching locations most
-    satisfy the equation ``abs(index[indexer] - target) <= tolerance``.

-    .. versionadded:: 0.17.0

-Examples
---------

-Create a dataframe with some fictional data.

->>> index = ['Firefox', 'Chrome', 'Safari', 'IE10', 'Konqueror']
->>> df = pd.DataFrame({
-...      'http_status': [200,200,404,404,301],
-...      'response_time': [0.04, 0.02, 0.07, 0.08, 1.0]},
-...       index=index)
->>> df
-           http_status  response_time
-Firefox            200           0.04
-Chrome             200           0.02
-Safari             404           0.07
-IE10               404           0.08
-Konqueror          301           1.00

-Create a new index and reindex the dataframe. By default
-values in the new index that do not have corresponding
-records in the dataframe are assigned ``NaN``.

->>> new_index= ['Safari', 'Iceweasel', 'Comodo Dragon', 'IE10',
-...             'Chrome']
->>> df.reindex(new_index)
-               http_status  response_time
-Safari               404.0           0.07
-Iceweasel              NaN            NaN
-Comodo Dragon          NaN            NaN
-IE10                 404.0           0.08
-Chrome               200.0           0.02

-We can fill in the missing values by passing a value to
-the keyword ``fill_value``. Because the index is not monotonically
-increasing or decreasing, we cannot use arguments to the keyword
-``method`` to fill the ``NaN`` values.

->>> df.reindex(new_index, fill_value=0)
-               http_status  response_time
-Safari                 404           0.07
-Iceweasel                0           0.00
-Comodo Dragon            0           0.00
-IE10                   404           0.08
-Chrome                 200           0.02

->>> df.reindex(new_index, fill_value='missing')
-              http_status response_time
-Safari                404          0.07
-Iceweasel         missing       missing
-Comodo Dragon     missing       missing
-IE10                  404          0.08
-Chrome                200          0.02

-To further illustrate the filling functionality in
-``reindex``, we will create a dataframe with a
-monotonically increasing index (for example, a sequence
-of dates).

->>> date_index = pd.date_range('1/1/2010', periods=6, freq='D')
->>> df2 = pd.DataFrame({"prices": [100, 101, np.nan, 100, 89, 88]},
-...                    index=date_index)
->>> df2
-            prices
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88

-Suppose we decide to expand the dataframe to cover a wider
-date range.

->>> date_index2 = pd.date_range('12/29/2009', periods=10, freq='D')
->>> df2.reindex(date_index2)
-            prices
-2009-12-29     NaN
-2009-12-30     NaN
-2009-12-31     NaN
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-The index entries that did not have a value in the original data frame
-(for example, '2009-12-29') are by default filled with ``NaN``.
-If desired, we can fill in the missing values using one of several
-options.

-For example, to backpropagate the last valid value to fill the ``NaN``
-values, pass ``bfill`` as an argument to the ``method`` keyword.

->>> df2.reindex(date_index2, method='bfill')
-            prices
-2009-12-29     100
-2009-12-30     100
-2009-12-31     100
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-Please note that the ``NaN`` value present in the original dataframe
-(at index value 2010-01-03) will not be filled by any of the
-value propagation schemes. This is because filling while reindexing
-does not look at dataframe values, but only compares the original and
-desired indexes. If you do want to fill in the ``NaN`` values present
-in the original dataframe, use the ``fillna()`` method.

-Returns
--------
-reindexed : Series
- -
reindex_axis(self, labels, axis=0, **kwargs)
for compatibility with higher dims
- -
rename(self, index=None, **kwargs)
Alter axes input function or functions. Function / dict values must be
-unique (1-to-1). Labels not contained in a dict / Series will be left
-as-is. Extra labels listed don't throw an error. Alternatively, change
-``Series.name`` with a scalar value (Series only).

-Parameters
-----------
-index : scalar, list-like, dict-like or function, optional
-    Scalar or list-like will alter the ``Series.name`` attribute,
-    and raise on DataFrame or Panel.
-    dict-like or functions are transformations to apply to
-    that axis' values
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False
-    Whether to return a new Series. If True then value of copy is
-    ignored.
-level : int or level name, default None
-    In case of a MultiIndex, only rename labels in the specified
-    level.

-Returns
--------
-renamed : Series (new object)

-See Also
---------
-pandas.NDFrame.rename_axis

-Examples
---------
->>> s = pd.Series([1, 2, 3])
->>> s
-0    1
-1    2
-2    3
-dtype: int64
->>> s.rename("my_name") # scalar, changes Series.name
-0    1
-1    2
-2    3
-Name: my_name, dtype: int64
->>> s.rename(lambda x: x ** 2)  # function, changes labels
-0    1
-1    2
-4    3
-dtype: int64
->>> s.rename({1: 3, 2: 5})  # mapping, changes labels
-0    1
-3    2
-5    3
-dtype: int64
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename(2)
-Traceback (most recent call last):
-...
-TypeError: 'int' object is not callable
->>> df.rename(index=str, columns={"A": "a", "B": "c"})
-   a  c
-0  1  4
-1  2  5
-2  3  6
->>> df.rename(index=str, columns={"A": "a", "C": "c"})
-   a  B
-0  1  4
-1  2  5
-2  3  6
- -
reorder_levels(self, order)
Rearrange index levels using input order. May not drop or duplicate
-levels

-Parameters
-----------
-order : list of int representing new level order.
-       (reference level by number or key)
-axis : where to reorder levels

-Returns
--------
-type of caller (new object)
- -
repeat(self, repeats, *args, **kwargs)
Repeat elements of an Series. Refer to `numpy.ndarray.repeat`
-for more information about the `repeats` argument.

-See also
---------
-numpy.ndarray.repeat
- -
reset_index(self, level=None, drop=False, name=None, inplace=False)
Analogous to the :meth:`pandas.DataFrame.reset_index` function, see
-docstring there.

-Parameters
-----------
-level : int, str, tuple, or list, default None
-    Only remove the given levels from the index. Removes all levels by
-    default
-drop : boolean, default False
-    Do not try to insert index into dataframe columns
-name : object, default None
-    The name of the column corresponding to the Series values
-inplace : boolean, default False
-    Modify the Series in place (do not create a new object)

-Returns
-----------
-resetted : DataFrame, or Series if drop == True
- -
reshape(self, *args, **kwargs)
DEPRECATED: calling this method will raise an error in a
-future release. Please call ``.values.reshape(...)`` instead.

-return an ndarray with the values shape
-if the specified shape matches exactly the current shape, then
-return self (for compat)

-See also
---------
-numpy.ndarray.reshape
- -
rfloordiv(self, other, level=None, fill_value=None, axis=0)
Integer division of series and other, element-wise (binary operator `rfloordiv`).

-Equivalent to ``other // series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.floordiv
- -
rmod(self, other, level=None, fill_value=None, axis=0)
Modulo of series and other, element-wise (binary operator `rmod`).

-Equivalent to ``other % series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.mod
- -
rmul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `rmul`).

-Equivalent to ``other * series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.mul
- -
rolling(self, window, min_periods=None, freq=None, center=False, win_type=None, on=None, axis=0, closed=None)
Provides rolling window calculcations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-window : int, or offset
-    Size of the moving window. This is the number of observations used for
-    calculating the statistic. Each window will be a fixed size.

-    If its an offset then this will be the time period of each window. Each
-    window will be a variable sized based on the observations included in
-    the time-period. This is only valid for datetimelike indexes. This is
-    new in 0.19.0
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA). For a window that is specified by an offset,
-    this will default to 1.
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-win_type : string, default None
-    Provide a window type. See the notes below.
-on : string, optional
-    For a DataFrame, column on which to calculate
-    the rolling window, rather than the index
-closed : string, default None
-    Make the interval closed on the 'right', 'left', 'both' or
-    'neither' endpoints.
-    For offset-based windows, it defaults to 'right'.
-    For fixed windows, defaults to 'both'. Remaining cases not implemented
-    for fixed windows.

-    .. versionadded:: 0.20.0

-axis : int or string, default 0

-Returns
--------
-a Window or Rolling sub-classed for the particular operation

-Examples
---------

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]})
->>> df
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

-Rolling sum with a window length of 2, using the 'triang'
-window type.

->>> df.rolling(2, win_type='triang').sum()
-     B
-0  NaN
-1  1.0
-2  2.5
-3  NaN
-4  NaN

-Rolling sum with a window length of 2, min_periods defaults
-to the window length.

->>> df.rolling(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  NaN
-4  NaN

-Same as above, but explicity set the min_periods

->>> df.rolling(2, min_periods=1).sum()
-     B
-0  0.0
-1  1.0
-2  3.0
-3  2.0
-4  4.0

-A ragged (meaning not-a-regular frequency), time-indexed DataFrame

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]},
-....:                 index = [pd.Timestamp('20130101 09:00:00'),
-....:                          pd.Timestamp('20130101 09:00:02'),
-....:                          pd.Timestamp('20130101 09:00:03'),
-....:                          pd.Timestamp('20130101 09:00:05'),
-....:                          pd.Timestamp('20130101 09:00:06')])

->>> df
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  2.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0


-Contrasting to an integer rolling window, this will roll a variable
-length window corresponding to the time period.
-The default for min_periods is 1.

->>> df.rolling('2s').sum()
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  3.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-To learn more about the offsets & frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-The recognized win_types are:

-* ``boxcar``
-* ``triang``
-* ``blackman``
-* ``hamming``
-* ``bartlett``
-* ``parzen``
-* ``bohman``
-* ``blackmanharris``
-* ``nuttall``
-* ``barthann``
-* ``kaiser`` (needs beta)
-* ``gaussian`` (needs std)
-* ``general_gaussian`` (needs power, width)
-* ``slepian`` (needs width).
- -
round(self, decimals=0, *args, **kwargs)
Round each value in a Series to the given number of decimals.

-Parameters
-----------
-decimals : int
-    Number of decimal places to round to (default: 0).
-    If decimals is negative, it specifies the number of
-    positions to the left of the decimal point.

-Returns
--------
-Series object

-See Also
---------
-numpy.around
-DataFrame.round
- -
rpow(self, other, level=None, fill_value=None, axis=0)
Exponential power of series and other, element-wise (binary operator `rpow`).

-Equivalent to ``other ** series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.pow
- -
rsub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `rsub`).

-Equivalent to ``other - series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.sub
- -
rtruediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.truediv
- -
searchsorted(self, value, side='left', sorter=None)
Find indices where elements should be inserted to maintain order.

-Find the indices into a sorted Series `self` such that, if the
-corresponding elements in `value` were inserted before the indices,
-the order of `self` would be preserved.

-Parameters
-----------
-value : array_like
-    Values to insert into `self`.
-side : {'left', 'right'}, optional
-    If 'left', the index of the first suitable location found is given.
-    If 'right', return the last such index.  If there is no suitable
-    index, return either 0 or N (where N is the length of `self`).
-sorter : 1-D array_like, optional
-    Optional array of integer indices that sort `self` into ascending
-    order. They are typically the result of ``np.argsort``.

-Returns
--------
-indices : array of ints
-    Array of insertion points with the same shape as `value`.

-See Also
---------
-numpy.searchsorted

-Notes
------
-Binary search is used to find the required insertion points.

-Examples
---------

->>> x = pd.Series([1, 2, 3])
->>> x
-0    1
-1    2
-2    3
-dtype: int64

->>> x.searchsorted(4)
-array([3])

->>> x.searchsorted([0, 4])
-array([0, 3])

->>> x.searchsorted([1, 3], side='left')
-array([0, 2])

->>> x.searchsorted([1, 3], side='right')
-array([1, 3])

->>> x = pd.Categorical(['apple', 'bread', 'bread', 'cheese', 'milk' ])
-[apple, bread, bread, cheese, milk]
-Categories (4, object): [apple < bread < cheese < milk]

->>> x.searchsorted('bread')
-array([1])     # Note: an array, not a scalar

->>> x.searchsorted(['bread'])
-array([1])

->>> x.searchsorted(['bread', 'eggs'])
-array([1, 4])

->>> x.searchsorted(['bread', 'eggs'], side='right')
-array([3, 4])    # eggs before milk
- -
sem(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased standard error of the mean over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sem : scalar or Series (if level specified)
- -
set_value(self, label, value, takeable=False)
Quickly set single value at passed label. If label is not contained, a
-new object is created with the label placed at the end of the result
-index

-Parameters
-----------
-label : object
-    Partial indexing with MultiIndex not allowed
-value : object
-    Scalar value
-takeable : interpret the index as indexers, default False

-Returns
--------
-series : Series
-    If label is contained, will be reference to calling Series,
-    otherwise a new object
- -
shift(self, periods=1, freq=None, axis=0)
Shift index by desired number of periods with an optional time freq

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, optional
-    Increment to use from the tseries module or time rule (e.g. 'EOM').
-    See Notes.
-axis : {0, 'index'}

-Notes
------
-If freq is specified then the index values are shifted but the data
-is not realigned. That is, use freq if you would like to extend the
-index when shifting and preserve the original data.

-Returns
--------
-shifted : Series
- -
skew(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased skew over requested axis
-Normalized by N-1

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-skew : scalar or Series (if level specified)
- -
sort_index(self, axis=0, level=None, ascending=True, inplace=False, kind='quicksort', na_position='last', sort_remaining=True)
Sort object by labels (along an axis)

-Parameters
-----------
-axis : index to direct sorting
-level : int or level name or list of ints or list of level names
-    if not None, sort on values in specified index level(s)
-ascending : boolean, default True
-    Sort ascending vs. descending
-inplace : bool, default False
-    if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end.
-     Not implemented for MultiIndex.
-sort_remaining : bool, default True
-    if true and sorting by level and index is multilevel, sort by other
-    levels too (in order) after sorting by specified level

-Returns
--------
-sorted_obj : Series
- -
sort_values(self, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last')
Sort by the values along either axis

-.. versionadded:: 0.17.0

-Parameters
-----------
-axis : {0, 'index'}, default 0
-    Axis to direct sorting
-ascending : bool or list of bool, default True
-     Sort ascending vs. descending. Specify list for multiple sort
-     orders.  If this is a list of bools, must match the length of
-     the by.
-inplace : bool, default False
-     if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end

-Returns
--------
-sorted_obj : Series
- -
sortlevel(self, level=0, ascending=True, sort_remaining=True)
DEPRECATED: use :meth:`Series.sort_index`

-Sort Series with MultiIndex by chosen level. Data will be
-lexicographically sorted by the chosen level followed by the other
-levels (in order)

-Parameters
-----------
-level : int or level name, default None
-ascending : bool, default True

-Returns
--------
-sorted : Series

-See Also
---------
-Series.sort_index(level=...)
- -
std(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return sample standard deviation over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-std : scalar or Series (if level specified)
- -
sub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `sub`).

-Equivalent to ``series - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rsub
- -
subtract = sub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `sub`).

-Equivalent to ``series - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rsub
- -
sum(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the sum of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sum : scalar or Series (if level specified)
- -
swaplevel(self, i=-2, j=-1, copy=True)
Swap levels i and j in a MultiIndex

-Parameters
-----------
-i, j : int, string (can be mixed)
-    Level of index to be swapped. Can pass level name as string.

-Returns
--------
-swapped : Series

-.. versionchanged:: 0.18.1

-   The indexes ``i`` and ``j`` are now optional, and default to
-   the two innermost levels of the index.
- -
take(self, indices, axis=0, convert=True, is_copy=False, **kwargs)
return Series corresponding to requested indices

-Parameters
-----------
-indices : list / array of ints
-convert : translate negative to positive indices (default)

-Returns
--------
-taken : Series

-See also
---------
-numpy.ndarray.take
- -
to_csv(self, path=None, index=True, sep=',', na_rep='', float_format=None, header=False, index_label=None, mode='w', encoding=None, date_format=None, decimal='.')
Write Series to a comma-separated values (csv) file

-Parameters
-----------
-path : string or file handle, default None
-    File path or object, if None is provided the result is returned as
-    a string.
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-header : boolean, default False
-    Write out series name
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.
-mode : Python write mode, default 'w'
-sep : character, default ","
-    Field delimiter for the output file.
-encoding : string, optional
-    a string representing the encoding to use if the contents are
-    non-ascii, for python versions prior to 3
-date_format: string, default None
-    Format string for datetime objects.
-decimal: string, default '.'
-    Character recognized as decimal separator. E.g. use ',' for
-    European data
- -
to_dict(self)
Convert Series to {label -> value} dict

-Returns
--------
-value_dict : dict
- -
to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='', float_format=None, columns=None, header=True, index=True, index_label=None, startrow=0, startcol=0, engine=None, merge_cells=True, encoding=None, inf_rep='inf', verbose=True)
Write Series to an excel sheet

-.. versionadded:: 0.20.0


-Parameters
-----------
-excel_writer : string or ExcelWriter object
-    File path or existing ExcelWriter
-sheet_name : string, default 'Sheet1'
-    Name of sheet which will contain DataFrame
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-columns : sequence, optional
-    Columns to write
-header : boolean or list of string, default True
-    Write out column names. If a list of string is given it is
-    assumed to be aliases for the column names
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.
-startrow :
-    upper left cell row to dump data frame
-startcol :
-    upper left cell column to dump data frame
-engine : string, default None
-    write engine to use - you can also set this via the options
-    ``io.excel.xlsx.writer``, ``io.excel.xls.writer``, and
-    ``io.excel.xlsm.writer``.
-merge_cells : boolean, default True
-    Write MultiIndex and Hierarchical Rows as merged cells.
-encoding: string, default None
-    encoding of the resulting excel file. Only necessary for xlwt,
-    other writers support unicode natively.
-inf_rep : string, default 'inf'
-    Representation for infinity (there is no native representation for
-    infinity in Excel)
-freeze_panes : tuple of integer (length 2), default None
-    Specifies the one-based bottommost row and rightmost column that
-    is to be frozen

-    .. versionadded:: 0.20.0

-Notes
------
-If passing an existing ExcelWriter object, then the sheet will be added
-to the existing workbook.  This can be used to save different
-DataFrames to one workbook:

->>> writer = pd.ExcelWriter('output.xlsx')
->>> df1.to_excel(writer,'Sheet1')
->>> df2.to_excel(writer,'Sheet2')
->>> writer.save()

-For compatibility with to_csv, to_excel serializes lists and dicts to
-strings before writing.
- -
to_frame(self, name=None)
Convert Series to DataFrame

-Parameters
-----------
-name : object, default None
-    The passed name should substitute for the series name (if it has
-    one).

-Returns
--------
-data_frame : DataFrame
- -
to_period(self, freq=None, copy=True)
Convert Series from DatetimeIndex to PeriodIndex with desired
-frequency (inferred from index if not passed)

-Parameters
-----------
-freq : string, default

-Returns
--------
-ts : Series with PeriodIndex
- -
to_sparse(self, kind='block', fill_value=None)
Convert Series to SparseSeries

-Parameters
-----------
-kind : {'block', 'integer'}
-fill_value : float, defaults to NaN (missing)

-Returns
--------
-sp : SparseSeries
- -
to_string(self, buf=None, na_rep='NaN', float_format=None, header=True, index=True, length=False, dtype=False, name=False, max_rows=None)
Render a string representation of the Series

-Parameters
-----------
-buf : StringIO-like, optional
-    buffer to write to
-na_rep : string, optional
-    string representation of NAN to use, default 'NaN'
-float_format : one-parameter function, optional
-    formatter function to apply to columns' elements if they are floats
-    default None
-header: boolean, default True
-    Add the Series header (index name)
-index : bool, optional
-    Add index (row) labels, default True
-length : boolean, default False
-    Add the Series length
-dtype : boolean, default False
-    Add the Series dtype
-name : boolean, default False
-    Add the Series name if not None
-max_rows : int, optional
-    Maximum number of rows to show before truncating. If None, show
-    all.

-Returns
--------
-formatted : string (if not buffer passed)
- -
to_timestamp(self, freq=None, how='start', copy=True)
Cast to datetimeindex of timestamps, at *beginning* of period

-Parameters
-----------
-freq : string, default frequency of PeriodIndex
-    Desired frequency
-how : {'s', 'e', 'start', 'end'}
-    Convention for converting period to timestamp; start of period
-    vs. end

-Returns
--------
-ts : Series with DatetimeIndex
- -
tolist(self)
Convert Series to a nested list
- -
transform(self, func, *args, **kwargs)
Call function producing a like-indexed NDFrame
-and return a NDFrame with the transformed values`

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    To apply to column

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Returns
--------
-transformed : NDFrame

-Examples
---------
->>> df = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
-...                   index=pd.date_range('1/1/2000', periods=10))
-df.iloc[3:7] = np.nan

->>> df.transform(lambda x: (x - x.mean()) / x.std())
-                   A         B         C
-2000-01-01  0.579457  1.236184  0.123424
-2000-01-02  0.370357 -0.605875 -1.231325
-2000-01-03  1.455756 -0.277446  0.288967
-2000-01-04       NaN       NaN       NaN
-2000-01-05       NaN       NaN       NaN
-2000-01-06       NaN       NaN       NaN
-2000-01-07       NaN       NaN       NaN
-2000-01-08 -0.498658  1.274522  1.642524
-2000-01-09 -0.540524 -1.012676 -0.828968
-2000-01-10 -1.366388 -0.614710  0.005378

-See also
---------
-pandas.NDFrame.aggregate
-pandas.NDFrame.apply
- -
truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
unique(self)
Return unique values in the object. Uniques are returned in order
-of appearance, this does NOT sort. Hash table-based unique.

-Parameters
-----------
-values : 1d array-like

-Returns
--------
-unique values.
-  - If the input is an Index, the return is an Index
-  - If the input is a Categorical dtype, the return is a Categorical
-  - If the input is a Series/ndarray, the return will be an ndarray

-See Also
---------
-unique
-Index.unique
-Series.unique
- -
unstack(self, level=-1, fill_value=None)
Unstack, a.k.a. pivot, Series with MultiIndex to produce DataFrame.
-The level involved will automatically get sorted.

-Parameters
-----------
-level : int, string, or list of these, default last level
-    Level(s) to unstack, can pass level name
-fill_value : replace NaN with this value if the unstack produces
-    missing values

-    .. versionadded: 0.18.0

-Examples
---------
->>> s = pd.Series([1, 2, 3, 4],
-...     index=pd.MultiIndex.from_product([['one', 'two'], ['a', 'b']]))
->>> s
-one  a    1
-     b    2
-two  a    3
-     b    4
-dtype: int64

->>> s.unstack(level=-1)
-     a  b
-one  1  2
-two  3  4

->>> s.unstack(level=0)
-   one  two
-a    1    3
-b    2    4

-Returns
--------
-unstacked : DataFrame
- -
update(self, other)
Modify Series in place using non-NA values from passed
-Series. Aligns on index

-Parameters
-----------
-other : Series
- -
valid lambda self, inplace=False, **kwargs
- -
var(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased variance over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-var : scalar or Series (if level specified)
- -
view(self, dtype=None)
- -
-Class methods inherited from pandas.core.series.Series:
-
from_array(arr, index=None, name=None, dtype=None, copy=False, fastpath=False) from builtins.type
- -
from_csv(path, sep=',', parse_dates=True, header=None, index_col=0, encoding=None, infer_datetime_format=False) from builtins.type
Read CSV file (DISCOURAGED, please use :func:`pandas.read_csv`
-instead).

-It is preferable to use the more powerful :func:`pandas.read_csv`
-for most general purposes, but ``from_csv`` makes for an easy
-roundtrip to and from a file (the exact counterpart of
-``to_csv``), especially with a time Series.

-This method only differs from :func:`pandas.read_csv` in some defaults:

-- `index_col` is ``0`` instead of ``None`` (take first column as index
-  by default)
-- `header` is ``None`` instead of ``0`` (the first row is not used as
-  the column names)
-- `parse_dates` is ``True`` instead of ``False`` (try parsing the index
-  as datetime by default)

-With :func:`pandas.read_csv`, the option ``squeeze=True`` can be used
-to return a Series like ``from_csv``.

-Parameters
-----------
-path : string file path or file handle / StringIO
-sep : string, default ','
-    Field delimiter
-parse_dates : boolean, default True
-    Parse dates. Different default from read_table
-header : int, default None
-    Row to use as header (skip prior rows)
-index_col : int or sequence, default 0
-    Column to use for index. If a sequence is given, a MultiIndex
-    is used. Different default from read_table
-encoding : string, optional
-    a string representing the encoding to use if the contents are
-    non-ascii, for python versions prior to 3
-infer_datetime_format: boolean, default False
-    If True and `parse_dates` is True for a column, try to infer the
-    datetime format based on the first datetime string. If the format
-    can be inferred, there often will be a large parsing speed-up.

-See also
---------
-pandas.read_csv

-Returns
--------
-y : Series
- -
-Data descriptors inherited from pandas.core.series.Series:
-
asobject
-
return object Series which contains boxed values

-*this is an internal non-public method*
-
-
axes
-
Return a list of the row axis labels
-
-
dtype
-
return the dtype object of the underlying data
-
-
dtypes
-
return the dtype object of the underlying data
-
-
ftype
-
return if the data is sparse|dense
-
-
ftypes
-
return if the data is sparse|dense
-
-
imag
-
-
index
-
-
name
-
-
real
-
-
values
-
Return Series as ndarray or ndarray-like
-depending on the dtype

-Returns
--------
-arr : numpy.ndarray or ndarray-like

-Examples
---------
->>> pd.Series([1, 2, 3]).values
-array([1, 2, 3])

->>> pd.Series(list('aabc')).values
-array(['a', 'a', 'b', 'c'], dtype=object)

->>> pd.Series(list('aabc')).astype('category').values
-[a, a, b, c]
-Categories (3, object): [a, b, c]

-Timezone aware datetime data is converted to UTC:

->>> pd.Series(pd.date_range('20130101', periods=3,
-...                         tz='US/Eastern')).values
-array(['2013-01-01T05:00:00.000000000',
-       '2013-01-02T05:00:00.000000000',
-       '2013-01-03T05:00:00.000000000'], dtype='datetime64[ns]')
-
-
-Data and other attributes inherited from pandas.core.series.Series:
-
cat = <class 'pandas.core.categorical.CategoricalAccessor'>
Accessor object for categorical properties of the Series values.

-Be aware that assigning to `categories` is a inplace operation, while all
-methods return new categorical data per default (but can be called with
-`inplace=True`).

-Examples
---------
->>> s.cat.categories
->>> s.cat.categories = list('abc')
->>> s.cat.rename_categories(list('cab'))
->>> s.cat.reorder_categories(list('cab'))
->>> s.cat.add_categories(['d','e'])
->>> s.cat.remove_categories(['d'])
->>> s.cat.remove_unused_categories()
->>> s.cat.set_categories(list('abcde'))
->>> s.cat.as_ordered()
->>> s.cat.as_unordered()
- -
plot = <class 'pandas.plotting._core.SeriesPlotMethods'>
Series plotting accessor and method

-Examples
---------
->>> s.plot.line()
->>> s.plot.bar()
->>> s.plot.hist()

-Plotting methods can also be accessed by calling the accessor as a method
-with the ``kind`` argument:
-``s.plot(kind='line')`` is equivalent to ``s.plot.line()``
- -
-Methods inherited from pandas.core.base.IndexOpsMixin:
-
factorize(self, sort=False, na_sentinel=-1)
Encode the object as an enumerated type or categorical variable

-Parameters
-----------
-sort : boolean, default False
-    Sort by values
-na_sentinel: int, default -1
-    Value to mark "not found"

-Returns
--------
-labels : the indexer to the original array
-uniques : the unique Index
- -
item(self)
return the first element of the underlying data as a python
-scalar
- -
nunique(self, dropna=True)
Return number of unique elements in the object.

-Excludes NA values by default.

-Parameters
-----------
-dropna : boolean, default True
-    Don't include NaN in the count.

-Returns
--------
-nunique : int
- -
transpose(self, *args, **kwargs)
return the transpose, which is by definition self
- -
value_counts(self, normalize=False, sort=True, ascending=False, bins=None, dropna=True)
Returns object containing counts of unique values.

-The resulting object will be in descending order so that the
-first element is the most frequently-occurring element.
-Excludes NA values by default.

-Parameters
-----------
-normalize : boolean, default False
-    If True then the object returned will contain the relative
-    frequencies of the unique values.
-sort : boolean, default True
-    Sort by values
-ascending : boolean, default False
-    Sort in ascending order
-bins : integer, optional
-    Rather than count values, group them into half-open bins,
-    a convenience for pd.cut, only works with numeric data
-dropna : boolean, default True
-    Don't include counts of NaN.

-Returns
--------
-counts : Series
- -
-Data descriptors inherited from pandas.core.base.IndexOpsMixin:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-
base
-
return the base object if the memory of the underlying data is
-shared
-
-
data
-
return the data pointer of the underlying data
-
-
empty
-
-
flags
-
return the ndarray.flags for the underlying data
-
-
hasnans
-
-
is_monotonic
-
Return boolean if values in the object are
-monotonic_increasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic : boolean
-
-
is_monotonic_decreasing
-
Return boolean if values in the object are
-monotonic_decreasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic_decreasing : boolean
-
-
is_monotonic_increasing
-
Return boolean if values in the object are
-monotonic_increasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic : boolean
-
-
is_unique
-
Return boolean if values in the object are unique

-Returns
--------
-is_unique : boolean
-
-
itemsize
-
return the size of the dtype of the item of the underlying data
-
-
nbytes
-
return the number of bytes in the underlying data
-
-
ndim
-
return the number of dimensions of the underlying data,
-by definition 1
-
-
shape
-
return a tuple of the shape of the underlying data
-
-
size
-
return the number of elements in the underlying data
-
-
strides
-
return the strides of the underlying data
-
-
-Data and other attributes inherited from pandas.core.base.IndexOpsMixin:
-
__array_priority__ = 1000
- -
-Data and other attributes inherited from pandas.core.strings.StringAccessorMixin:
-
str = <class 'pandas.core.strings.StringMethods'>
Vectorized string functions for Series and Index. NAs stay NA unless
-handled otherwise by a particular method. Patterned after Python's string
-methods, with some inspiration from R's stringr package.

-Examples
---------
->>> s.str.split('_')
->>> s.str.replace('_', '')
- -
-Methods inherited from pandas.core.generic.NDFrame:
-
__abs__(self)
- -
__bool__ = __nonzero__(self)
- -
__contains__(self, key)
True if the key is in the info axis
- -
__copy__(self, deep=True)
- -
__deepcopy__(self, memo=None)
- -
__delitem__(self, key)
Delete item
- -
__finalize__(self, other, method=None, **kwargs)
Propagate metadata from other to self.

-Parameters
-----------
-other : the object from which to get the attributes that we are going
-    to propagate
-method : optional, a passed method name ; possibly to take different
-    types of propagation actions based on this
- -
__getattr__(self, name)
After regular attribute access, try looking up the name
-This allows simpler access to columns for interactive use.
- -
__getstate__(self)
- -
__hash__(self)
Return hash(self).
- -
__invert__(self)
- -
__neg__(self)
- -
__nonzero__(self)
- -
__round__(self, decimals=0)
- -
__setattr__(self, name, value)
After regular attribute access, try setting the name
-This allows simpler access to columns for interactive use.
- -
__setstate__(self, state)
- -
abs(self)
Return an object with absolute value taken--only applicable to objects
-that are all numeric.

-Returns
--------
-abs: type of caller
- -
add_prefix(self, prefix)
Concatenate prefix string with panel items names.

-Parameters
-----------
-prefix : string

-Returns
--------
-with_prefix : type of caller
- -
add_suffix(self, suffix)
Concatenate suffix string with panel items names.

-Parameters
-----------
-suffix : string

-Returns
--------
-with_suffix : type of caller
- -
as_blocks(self, copy=True)
Convert the frame to a dict of dtype -> Constructor Types that each has
-a homogeneous dtype.

-NOTE: the dtypes of the blocks WILL BE PRESERVED HERE (unlike in
-      as_matrix)

-Parameters
-----------
-copy : boolean, default True

-       .. versionadded: 0.16.1

-Returns
--------
-values : a dict of dtype -> Constructor Types
- -
as_matrix(self, columns=None)
Convert the frame to its Numpy-array representation.

-Parameters
-----------
-columns: list, optional, default:None
-    If None, return all columns, otherwise, returns specified columns.

-Returns
--------
-values : ndarray
-    If the caller is heterogeneous and contains booleans or objects,
-    the result will be of dtype=object. See Notes.


-Notes
------
-Return is NOT a Numpy-matrix, rather, a Numpy-array.

-The dtype will be a lower-common-denominator dtype (implicit
-upcasting); that is to say if the dtypes (even of numeric types)
-are mixed, the one that accommodates all will be chosen. Use this
-with care if you are not dealing with the blocks.

-e.g. If the dtypes are float16 and float32, dtype will be upcast to
-float32.  If dtypes are int32 and uint8, dtype will be upcase to
-int32. By numpy.find_common_type convention, mixing int64 and uint64
-will result in a flot64 dtype.

-This method is provided for backwards compatibility. Generally,
-it is recommended to use '.values'.

-See Also
---------
-pandas.DataFrame.values
- -
asfreq(self, freq, method=None, how=None, normalize=False, fill_value=None)
Convert TimeSeries to specified frequency.

-Optionally provide filling method to pad/backfill missing values.

-Returns the original data conformed to a new index with the specified
-frequency. ``resample`` is more appropriate if an operation, such as
-summarization, is necessary to represent the data at the new frequency.

-Parameters
-----------
-freq : DateOffset object, or string
-method : {'backfill'/'bfill', 'pad'/'ffill'}, default None
-    Method to use for filling holes in reindexed Series (note this
-    does not fill NaNs that already were present):

-    * 'pad' / 'ffill': propagate last valid observation forward to next
-      valid
-    * 'backfill' / 'bfill': use NEXT valid observation to fill
-how : {'start', 'end'}, default end
-    For PeriodIndex only, see PeriodIndex.asfreq
-normalize : bool, default False
-    Whether to reset output index to midnight
-fill_value: scalar, optional
-    Value to use for missing values, applied during upsampling (note
-    this does not fill NaNs that already were present).

-    .. versionadded:: 0.20.0

-Returns
--------
-converted : type of caller

-Examples
---------

-Start by creating a series with 4 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=4, freq='T')
->>> series = pd.Series([0.0, None, 2.0, 3.0], index=index)
->>> df = pd.DataFrame({'s':series})
->>> df
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:03:00    3.0

-Upsample the series into 30 second bins.

->>> df.asfreq(freq='30S')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    NaN
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``fill value``.

->>> df.asfreq(freq='30S', fill_value=9.0)
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    9.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    9.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    9.0
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``method``.

->>> df.asfreq(freq='30S', method='bfill')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    2.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    3.0
-2000-01-01 00:03:00    3.0

-See Also
---------
-reindex

-Notes
------
-To learn more about the frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
- -
asof(self, where, subset=None)
The last row without any NaN is taken (or the last row without
-NaN considering only the subset of columns in the case of a DataFrame)

-.. versionadded:: 0.19.0 For DataFrame

-If there is no good value, NaN is returned for a Series
-a Series of NaN values for a DataFrame

-Parameters
-----------
-where : date or array of dates
-subset : string or list of strings, default None
-   if not None use these columns for NaN propagation

-Notes
------
-Dates are assumed to be sorted
-Raises if this is not the case

-Returns
--------
-where is scalar

-  - value or NaN if input is Series
-  - Series if input is DataFrame

-where is Index: same shape object as input

-See Also
---------
-merge_asof
- -
astype(self, dtype, copy=True, errors='raise', **kwargs)
Cast object to input numpy.dtype
-Return a copy when copy = True (be really careful with this!)

-Parameters
-----------
-dtype : data type, or dict of column name -> data type
-    Use a numpy.dtype or Python type to cast entire pandas object to
-    the same type. Alternatively, use {col: dtype, ...}, where col is a
-    column label and dtype is a numpy.dtype or Python type to cast one
-    or more of the DataFrame's columns to column-specific types.
-errors : {'raise', 'ignore'}, default 'raise'.
-    Control raising of exceptions on invalid data for provided dtype.

-    - ``raise`` : allow exceptions to be raised
-    - ``ignore`` : suppress exceptions. On error return original object

-    .. versionadded:: 0.20.0

-raise_on_error : DEPRECATED use ``errors`` instead
-kwargs : keyword arguments to pass on to the constructor

-Returns
--------
-casted : type of caller
- -
at_time(self, time, asof=False)
Select values at particular time of day (e.g. 9:30AM).

-Parameters
-----------
-time : datetime.time or string

-Returns
--------
-values_at_time : type of caller
- -
between_time(self, start_time, end_time, include_start=True, include_end=True)
Select values between particular times of the day (e.g., 9:00-9:30 AM).

-Parameters
-----------
-start_time : datetime.time or string
-end_time : datetime.time or string
-include_start : boolean, default True
-include_end : boolean, default True

-Returns
--------
-values_between_time : type of caller
- -
bfill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='bfill') <DataFrame.fillna>`
- -
bool(self)
Return the bool of a single element PandasObject.

-This must be a boolean scalar value, either True or False.  Raise a
-ValueError if the PandasObject does not have exactly 1 element, or that
-element is not boolean
- -
clip(self, lower=None, upper=None, axis=None, *args, **kwargs)
Trim values at input threshold(s).

-Parameters
-----------
-lower : float or array_like, default None
-upper : float or array_like, default None
-axis : int or string axis name, optional
-    Align object with lower and upper along the given axis.

-Returns
--------
-clipped : Series

-Examples
---------
->>> df
-  0         1
-0  0.335232 -1.256177
-1 -1.367855  0.746646
-2  0.027753 -1.176076
-3  0.230930 -0.679613
-4  1.261967  0.570967
->>> df.clip(-1.0, 0.5)
-          0         1
-0  0.335232 -1.000000
-1 -1.000000  0.500000
-2  0.027753 -1.000000
-3  0.230930 -0.679613
-4  0.500000  0.500000
->>> t
-0   -0.3
-1   -0.2
-2   -0.1
-3    0.0
-4    0.1
-dtype: float64
->>> df.clip(t, t + 1, axis=0)
-          0         1
-0  0.335232 -0.300000
-1 -0.200000  0.746646
-2  0.027753 -0.100000
-3  0.230930  0.000000
-4  1.100000  0.570967
- -
clip_lower(self, threshold, axis=None)
Return copy of the input with values below given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
clip_upper(self, threshold, axis=None)
Return copy of input with values above given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
consolidate(self, inplace=False)
DEPRECATED: consolidate will be an internal implementation only.
- -
convert_objects(self, convert_dates=True, convert_numeric=False, convert_timedeltas=True, copy=True)
Deprecated.

-Attempt to infer better dtype for object columns

-Parameters
-----------
-convert_dates : boolean, default True
-    If True, convert to date where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-convert_numeric : boolean, default False
-    If True, attempt to coerce to numbers (including strings), with
-    unconvertible values becoming NaN.
-convert_timedeltas : boolean, default True
-    If True, convert to timedelta where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-copy : boolean, default True
-    If True, return a copy even if no copy is necessary (e.g. no
-    conversion was done). Note: This is meant for internal use, and
-    should not be confused with inplace.

-See Also
---------
-pandas.to_datetime : Convert argument to datetime.
-pandas.to_timedelta : Convert argument to timedelta.
-pandas.to_numeric : Return a fixed frequency timedelta index,
-    with day as the default.

-Returns
--------
-converted : same as input object
- -
copy(self, deep=True)
Make a copy of this objects data.

-Parameters
-----------
-deep : boolean or string, default True
-    Make a deep copy, including a copy of the data and the indices.
-    With ``deep=False`` neither the indices or the data are copied.

-    Note that when ``deep=True`` data is copied, actual python objects
-    will not be copied recursively, only the reference to the object.
-    This is in contrast to ``copy.deepcopy`` in the Standard Library,
-    which recursively copies object data.

-Returns
--------
-copy : type of caller
- -
describe(self, percentiles=None, include=None, exclude=None)
Generates descriptive statistics that summarize the central tendency,
-dispersion and shape of a dataset's distribution, excluding
-``NaN`` values.

-Analyzes both numeric and object series, as well
-as ``DataFrame`` column sets of mixed data types. The output
-will vary depending on what is provided. Refer to the notes
-below for more detail.

-Parameters
-----------
-percentiles : list-like of numbers, optional
-    The percentiles to include in the output. All should
-    fall between 0 and 1. The default is
-    ``[.25, .5, .75]``, which returns the 25th, 50th, and
-    75th percentiles.
-include : 'all', list-like of dtypes or None (default), optional
-    A white list of data types to include in the result. Ignored
-    for ``Series``. Here are the options:

-    - 'all' : All columns of the input will be included in the output.
-    - A list-like of dtypes : Limits the results to the
-      provided data types.
-      To limit the result to numeric types submit
-      ``numpy.number``. To limit it instead to categorical
-      objects submit the ``numpy.object`` data type. Strings
-      can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will include all numeric columns.
-exclude : list-like of dtypes or None (default), optional,
-    A black list of data types to omit from the result. Ignored
-    for ``Series``. Here are the options:

-    - A list-like of dtypes : Excludes the provided data types
-      from the result. To select numeric types submit
-      ``numpy.number``. To select categorical objects submit the data
-      type ``numpy.object``. Strings can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will exclude nothing.

-Returns
--------
-summary:  Series/DataFrame of summary statistics

-Notes
------
-For numeric data, the result's index will include ``count``,
-``mean``, ``std``, ``min``, ``max`` as well as lower, ``50`` and
-upper percentiles. By default the lower percentile is ``25`` and the
-upper percentile is ``75``. The ``50`` percentile is the
-same as the median.

-For object data (e.g. strings or timestamps), the result's index
-will include ``count``, ``unique``, ``top``, and ``freq``. The ``top``
-is the most common value. The ``freq`` is the most common value's
-frequency. Timestamps also include the ``first`` and ``last`` items.

-If multiple object values have the highest count, then the
-``count`` and ``top`` results will be arbitrarily chosen from
-among those with the highest count.

-For mixed data types provided via a ``DataFrame``, the default is to
-return only an analysis of numeric columns. If ``include='all'``
-is provided as an option, the result will include a union of
-attributes of each type.

-The `include` and `exclude` parameters can be used to limit
-which columns in a ``DataFrame`` are analyzed for the output.
-The parameters are ignored when analyzing a ``Series``.

-Examples
---------
-Describing a numeric ``Series``.

->>> s = pd.Series([1, 2, 3])
->>> s.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0

-Describing a categorical ``Series``.

->>> s = pd.Series(['a', 'a', 'b', 'c'])
->>> s.describe()
-count     4
-unique    3
-top       a
-freq      2
-dtype: object

-Describing a timestamp ``Series``.

->>> s = pd.Series([
-...   np.datetime64("2000-01-01"),
-...   np.datetime64("2010-01-01"),
-...   np.datetime64("2010-01-01")
-... ])
->>> s.describe()
-count                       3
-unique                      2
-top       2010-01-01 00:00:00
-freq                        2
-first     2000-01-01 00:00:00
-last      2010-01-01 00:00:00
-dtype: object

-Describing a ``DataFrame``. By default only numeric fields
-are returned.

->>> df = pd.DataFrame([[1, 'a'], [2, 'b'], [3, 'c']],
-...                   columns=['numeric', 'object'])
->>> df.describe()
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Describing all columns of a ``DataFrame`` regardless of data type.

->>> df.describe(include='all')
-        numeric object
-count       3.0      3
-unique      NaN      3
-top         NaN      b
-freq        NaN      1
-mean        2.0    NaN
-std         1.0    NaN
-min         1.0    NaN
-25%         1.5    NaN
-50%         2.0    NaN
-75%         2.5    NaN
-max         3.0    NaN

-Describing a column from a ``DataFrame`` by accessing it as
-an attribute.

->>> df.numeric.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0
-Name: numeric, dtype: float64

-Including only numeric columns in a ``DataFrame`` description.

->>> df.describe(include=[np.number])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Including only string columns in a ``DataFrame`` description.

->>> df.describe(include=[np.object])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding numeric columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.number])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding object columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.object])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-See Also
---------
-DataFrame.count
-DataFrame.max
-DataFrame.min
-DataFrame.mean
-DataFrame.std
-DataFrame.select_dtypes
- -
drop(self, labels, axis=0, level=None, inplace=False, errors='raise')
Return new object with labels in requested axis removed.

-Parameters
-----------
-labels : single label or list-like
-axis : int or axis name
-level : int or level name, default None
-    For MultiIndex
-inplace : bool, default False
-    If True, do operation inplace and return None.
-errors : {'ignore', 'raise'}, default 'raise'
-    If 'ignore', suppress error and existing labels are dropped.

-    .. versionadded:: 0.16.1

-Returns
--------
-dropped : type of caller
- -
equals(self, other)
Determines if two NDFrame objects contain the same elements. NaNs in
-the same location are considered equal.
- -
ffill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='ffill') <DataFrame.fillna>`
- -
filter(self, items=None, like=None, regex=None, axis=None)
Subset rows or columns of dataframe according to labels in
-the specified index.

-Note that this routine does not filter a dataframe on its
-contents. The filter is applied to the labels of the index.

-Parameters
-----------
-items : list-like
-    List of info axis to restrict to (must not all be present)
-like : string
-    Keep info axis where "arg in col == True"
-regex : string (regular expression)
-    Keep info axis with re.search(regex, col) == True
-axis : int or string axis name
-    The axis to filter on.  By default this is the info axis,
-    'index' for Series, 'columns' for DataFrame

-Returns
--------
-same type as input object

-Examples
---------
->>> df
-one  two  three
-mouse     1    2      3
-rabbit    4    5      6

->>> # select columns by name
->>> df.filter(items=['one', 'three'])
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select columns by regular expression
->>> df.filter(regex='e$', axis=1)
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select rows containing 'bbi'
->>> df.filter(like='bbi', axis=0)
-one  two  three
-rabbit    4    5      6

-See Also
---------
-pandas.DataFrame.select

-Notes
------
-The ``items``, ``like``, and ``regex`` parameters are
-enforced to be mutually exclusive.

-``axis`` defaults to the info axis that is used when indexing
-with ``[]``.
- -
first(self, offset)
Convenience method for subsetting initial periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.first('10D') -> First 10 days

-Returns
--------
-subset : type of caller
- -
get(self, key, default=None)
Get item from object for given key (DataFrame column, Panel slice,
-etc.). Returns default value if not found.

-Parameters
-----------
-key : object

-Returns
--------
-value : type of items contained in object
- -
get_dtype_counts(self)
Return the counts of dtypes in this object.
- -
get_ftype_counts(self)
Return the counts of ftypes in this object.
- -
groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, **kwargs)
Group series using mapper (dict or key function, apply given function
-to group, return result as series) or by a series of columns.

-Parameters
-----------
-by : mapping, function, str, or iterable
-    Used to determine the groups for the groupby.
-    If ``by`` is a function, it's called on each value of the object's
-    index. If a dict or Series is passed, the Series or dict VALUES
-    will be used to determine the groups (the Series' values are first
-    aligned; see ``.align()`` method). If an ndarray is passed, the
-    values are used as-is determine the groups. A str or list of strs
-    may be passed to group by the columns in ``self``
-axis : int, default 0
-level : int, level name, or sequence of such, default None
-    If the axis is a MultiIndex (hierarchical), group by a particular
-    level or levels
-as_index : boolean, default True
-    For aggregated output, return object with group labels as the
-    index. Only relevant for DataFrame input. as_index=False is
-    effectively "SQL-style" grouped output
-sort : boolean, default True
-    Sort group keys. Get better performance by turning this off.
-    Note this does not influence the order of observations within each
-    group.  groupby preserves the order of rows within each group.
-group_keys : boolean, default True
-    When calling apply, add group keys to index to identify pieces
-squeeze : boolean, default False
-    reduce the dimensionality of the return type if possible,
-    otherwise return a consistent type

-Examples
---------
-DataFrame results

->>> data.groupby(func, axis=0).mean()
->>> data.groupby(['col1', 'col2'])['col3'].mean()

-DataFrame with hierarchical index

->>> data.groupby(['col1', 'col2']).mean()

-Returns
--------
-GroupBy object
- -
head(self, n=5)
Returns first n rows
- -
interpolate(self, method='linear', axis=0, limit=None, inplace=False, limit_direction='forward', downcast=None, **kwargs)
Interpolate values according to different methods.

-Please note that only ``method='linear'`` is supported for
-DataFrames/Series with a MultiIndex.

-Parameters
-----------
-method : {'linear', 'time', 'index', 'values', 'nearest', 'zero',
-          'slinear', 'quadratic', 'cubic', 'barycentric', 'krogh',
-          'polynomial', 'spline', 'piecewise_polynomial',
-          'from_derivatives', 'pchip', 'akima'}

-    * 'linear': ignore the index and treat the values as equally
-      spaced. This is the only method supported on MultiIndexes.
-      default
-    * 'time': interpolation works on daily and higher resolution
-      data to interpolate given length of interval
-    * 'index', 'values': use the actual numerical values of the index
-    * 'nearest', 'zero', 'slinear', 'quadratic', 'cubic',
-      'barycentric', 'polynomial' is passed to
-      ``scipy.interpolate.interp1d``. Both 'polynomial' and 'spline'
-      require that you also specify an `order` (int),
-      e.g. df.interpolate(method='polynomial', order=4).
-      These use the actual numerical values of the index.
-    * 'krogh', 'piecewise_polynomial', 'spline', 'pchip' and 'akima'
-      are all wrappers around the scipy interpolation methods of
-      similar names. These use the actual numerical values of the
-      index. For more information on their behavior, see the
-      `scipy documentation
-      <http://docs.scipy.org/doc/scipy/reference/interpolate.html#univariate-interpolation>`__
-      and `tutorial documentation
-      <http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html>`__
-    * 'from_derivatives' refers to BPoly.from_derivatives which
-      replaces 'piecewise_polynomial' interpolation method in
-      scipy 0.18

-    .. versionadded:: 0.18.1

-       Added support for the 'akima' method
-       Added interpolate method 'from_derivatives' which replaces
-       'piecewise_polynomial' in scipy 0.18; backwards-compatible with
-       scipy < 0.18

-axis : {0, 1}, default 0
-    * 0: fill column-by-column
-    * 1: fill row-by-row
-limit : int, default None.
-    Maximum number of consecutive NaNs to fill. Must be greater than 0.
-limit_direction : {'forward', 'backward', 'both'}, default 'forward'
-    If limit is specified, consecutive NaNs will be filled in this
-    direction.

-    .. versionadded:: 0.17.0

-inplace : bool, default False
-    Update the NDFrame in place if possible.
-downcast : optional, 'infer' or None, defaults to None
-    Downcast dtypes if possible.
-kwargs : keyword arguments to pass on to the interpolating function.

-Returns
--------
-Series or DataFrame of same shape interpolated at the NaNs

-See Also
---------
-reindex, replace, fillna

-Examples
---------

-Filling in NaNs

->>> s = pd.Series([0, 1, np.nan, 3])
->>> s.interpolate()
-0    0
-1    1
-2    2
-3    3
-dtype: float64
- -
isnull(self)
Return a boolean same-sized object indicating if the values are null.

-See Also
---------
-notnull : boolean inverse of isnull
- -
last(self, offset)
Convenience method for subsetting final periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.last('5M') -> Last 5 months

-Returns
--------
-subset : type of caller
- -
mask(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is False and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The mask method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``False`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``mask`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.where`
- -
notnull(self)
Return a boolean same-sized object indicating if the values are
-not null.

-See Also
---------
-isnull : boolean inverse of notnull
- -
pct_change(self, periods=1, fill_method='pad', limit=None, freq=None, **kwargs)
Percent change over given number of periods.

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming percent change
-fill_method : str, default 'pad'
-    How to handle NAs before computing percent changes
-limit : int, default None
-    The number of consecutive NAs to fill before stopping
-freq : DateOffset, timedelta, or offset alias string, optional
-    Increment to use from time series API (e.g. 'M' or BDay())

-Returns
--------
-chg : NDFrame

-Notes
------

-By default, the percentage change is calculated along the stat
-axis: 0, or ``Index``, for ``DataFrame`` and 1, or ``minor`` for
-``Panel``. You can change this with the ``axis`` keyword argument.
- -
pipe(self, func, *args, **kwargs)
Apply func(self, \*args, \*\*kwargs)

-.. versionadded:: 0.16.2

-Parameters
-----------
-func : function
-    function to apply to the NDFrame.
-    ``args``, and ``kwargs`` are passed into ``func``.
-    Alternatively a ``(callable, data_keyword)`` tuple where
-    ``data_keyword`` is a string indicating the keyword of
-    ``callable`` that expects the NDFrame.
-args : positional arguments passed into ``func``.
-kwargs : a dictionary of keyword arguments passed into ``func``.

-Returns
--------
-object : the return type of ``func``.

-Notes
------

-Use ``.pipe`` when chaining together functions that expect
-on Series or DataFrames. Instead of writing

->>> f(g(h(df), arg1=a), arg2=b, arg3=c)

-You can write

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe(f, arg2=b, arg3=c)
-... )

-If you have a function that takes the data as (say) the second
-argument, pass a tuple indicating which keyword expects the
-data. For example, suppose ``f`` takes its data as ``arg2``:

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe((f, 'arg2'), arg1=a, arg3=c)
-...  )

-See Also
---------
-pandas.DataFrame.apply
-pandas.DataFrame.applymap
-pandas.Series.map
- -
pop(self, item)
Return item and drop from frame. Raise KeyError if not found.
- -
rank(self, axis=0, method='average', numeric_only=None, na_option='keep', ascending=True, pct=False)
Compute numerical data ranks (1 through n) along axis. Equal values are
-assigned a rank that is the average of the ranks of those values

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    index to direct ranking
-method : {'average', 'min', 'max', 'first', 'dense'}
-    * average: average rank of group
-    * min: lowest rank in group
-    * max: highest rank in group
-    * first: ranks assigned in order they appear in the array
-    * dense: like 'min', but rank always increases by 1 between groups
-numeric_only : boolean, default None
-    Include only float, int, boolean data. Valid only for DataFrame or
-    Panel objects
-na_option : {'keep', 'top', 'bottom'}
-    * keep: leave NA values where they are
-    * top: smallest rank if ascending
-    * bottom: smallest rank if descending
-ascending : boolean, default True
-    False for ranks by high (1) to low (N)
-pct : boolean, default False
-    Computes percentage rank of data

-Returns
--------
-ranks : same type as caller
- -
reindex_like(self, other, method=None, copy=True, limit=None, tolerance=None)
Return an object with matching indices to myself.

-Parameters
-----------
-other : Object
-method : string or None
-copy : boolean, default True
-limit : int, default None
-    Maximum number of consecutive labels to fill for inexact matches.
-tolerance : optional
-    Maximum distance between labels of the other object and this
-    object for inexact matches.

-    .. versionadded:: 0.17.0

-Notes
------
-Like calling s.reindex(index=other.index, columns=other.columns,
-                       method=...)

-Returns
--------
-reindexed : same as input
- -
rename_axis(self, mapper, axis=0, copy=True, inplace=False)
Alter index and / or columns using input function or functions.
-A scalar or list-like for ``mapper`` will alter the ``Index.name``
-or ``MultiIndex.names`` attribute.
-A function or dict for ``mapper`` will alter the labels.
-Function / dict values must be unique (1-to-1). Labels not contained in
-a dict / Series will be left as-is.

-Parameters
-----------
-mapper : scalar, list-like, dict-like or function, optional
-axis : int or string, default 0
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False

-Returns
--------
-renamed : type of caller

-See Also
---------
-pandas.NDFrame.rename
-pandas.Index.rename

-Examples
---------
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename_axis("foo")  # scalar, alters df.index.name
-     A  B
-foo
-0    1  4
-1    2  5
-2    3  6
->>> df.rename_axis(lambda x: 2 * x)  # function: alters labels
-   A  B
-0  1  4
-2  2  5
-4  3  6
->>> df.rename_axis({"A": "ehh", "C": "see"}, axis="columns")  # mapping
-   ehh  B
-0    1  4
-1    2  5
-2    3  6
- -
replace(self, to_replace=None, value=None, inplace=False, limit=None, regex=False, method='pad', axis=None)
Replace values given in 'to_replace' with 'value'.

-Parameters
-----------
-to_replace : str, regex, list, dict, Series, numeric, or None

-    * str or regex:

-        - str: string exactly matching `to_replace` will be replaced
-          with `value`
-        - regex: regexs matching `to_replace` will be replaced with
-          `value`

-    * list of str, regex, or numeric:

-        - First, if `to_replace` and `value` are both lists, they
-          **must** be the same length.
-        - Second, if ``regex=True`` then all of the strings in **both**
-          lists will be interpreted as regexs otherwise they will match
-          directly. This doesn't matter much for `value` since there
-          are only a few possible substitution regexes you can use.
-        - str and regex rules apply as above.

-    * dict:

-        - Nested dictionaries, e.g., {'a': {'b': nan}}, are read as
-          follows: look in column 'a' for the value 'b' and replace it
-          with nan. You can nest regular expressions as well. Note that
-          column names (the top-level dictionary keys in a nested
-          dictionary) **cannot** be regular expressions.
-        - Keys map to column names and values map to substitution
-          values. You can treat this as a special case of passing two
-          lists except that you are specifying the column to search in.

-    * None:

-        - This means that the ``regex`` argument must be a string,
-          compiled regular expression, or list, dict, ndarray or Series
-          of such elements. If `value` is also ``None`` then this
-          **must** be a nested dictionary or ``Series``.

-    See the examples section for examples of each of these.
-value : scalar, dict, list, str, regex, default None
-    Value to use to fill holes (e.g. 0), alternately a dict of values
-    specifying which value to use for each column (columns not in the
-    dict will not be filled). Regular expressions, strings and lists or
-    dicts of such objects are also allowed.
-inplace : boolean, default False
-    If True, in place. Note: this will modify any
-    other views on this object (e.g. a column form a DataFrame).
-    Returns the caller if this is True.
-limit : int, default None
-    Maximum size gap to forward or backward fill
-regex : bool or same types as `to_replace`, default False
-    Whether to interpret `to_replace` and/or `value` as regular
-    expressions. If this is ``True`` then `to_replace` *must* be a
-    string. Otherwise, `to_replace` must be ``None`` because this
-    parameter will be interpreted as a regular expression or a list,
-    dict, or array of regular expressions.
-method : string, optional, {'pad', 'ffill', 'bfill'}
-    The method to use when for replacement, when ``to_replace`` is a
-    ``list``.

-See Also
---------
-NDFrame.reindex
-NDFrame.asfreq
-NDFrame.fillna

-Returns
--------
-filled : NDFrame

-Raises
-------
-AssertionError
-    * If `regex` is not a ``bool`` and `to_replace` is not ``None``.
-TypeError
-    * If `to_replace` is a ``dict`` and `value` is not a ``list``,
-      ``dict``, ``ndarray``, or ``Series``
-    * If `to_replace` is ``None`` and `regex` is not compilable into a
-      regular expression or is a list, dict, ndarray, or Series.
-ValueError
-    * If `to_replace` and `value` are ``list`` s or ``ndarray`` s, but
-      they are not the same length.

-Notes
------
-* Regex substitution is performed under the hood with ``re.sub``. The
-  rules for substitution for ``re.sub`` are the same.
-* Regular expressions will only substitute on strings, meaning you
-  cannot provide, for example, a regular expression matching floating
-  point numbers and expect the columns in your frame that have a
-  numeric dtype to be matched. However, if those floating point numbers
-  *are* strings, then you can do this.
-* This method has *a lot* of options. You are encouraged to experiment
-  and play with this method to gain intuition about how it works.
- -
resample(self, rule, how=None, axis=0, fill_method=None, closed=None, label=None, convention='start', kind=None, loffset=None, limit=None, base=0, on=None, level=None)
Convenience method for frequency conversion and resampling of time
-series.  Object must have a datetime-like index (DatetimeIndex,
-PeriodIndex, or TimedeltaIndex), or pass datetime-like values
-to the on or level keyword.

-Parameters
-----------
-rule : string
-    the offset string or object representing target conversion
-axis : int, optional, default 0
-closed : {'right', 'left'}
-    Which side of bin interval is closed
-label : {'right', 'left'}
-    Which bin edge label to label bucket with
-convention : {'start', 'end', 's', 'e'}
-loffset : timedelta
-    Adjust the resampled time labels
-base : int, default 0
-    For frequencies that evenly subdivide 1 day, the "origin" of the
-    aggregated intervals. For example, for '5min' frequency, base could
-    range from 0 through 4. Defaults to 0
-on : string, optional
-    For a DataFrame, column to use instead of index for resampling.
-    Column must be datetime-like.

-    .. versionadded:: 0.19.0

-level : string or int, optional
-    For a MultiIndex, level (name or number) to use for
-    resampling.  Level must be datetime-like.

-    .. versionadded:: 0.19.0

-Notes
------
-To learn more about the offset strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-Examples
---------

-Start by creating a series with 9 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=9, freq='T')
->>> series = pd.Series(range(9), index=index)
->>> series
-2000-01-01 00:00:00    0
-2000-01-01 00:01:00    1
-2000-01-01 00:02:00    2
-2000-01-01 00:03:00    3
-2000-01-01 00:04:00    4
-2000-01-01 00:05:00    5
-2000-01-01 00:06:00    6
-2000-01-01 00:07:00    7
-2000-01-01 00:08:00    8
-Freq: T, dtype: int64

-Downsample the series into 3 minute bins and sum the values
-of the timestamps falling into a bin.

->>> series.resample('3T').sum()
-2000-01-01 00:00:00     3
-2000-01-01 00:03:00    12
-2000-01-01 00:06:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but label each
-bin using the right edge instead of the left. Please note that the
-value in the bucket used as the label is not included in the bucket,
-which it labels. For example, in the original series the
-bucket ``2000-01-01 00:03:00`` contains the value 3, but the summed
-value in the resampled bucket with the label``2000-01-01 00:03:00``
-does not include 3 (if it did, the summed value would be 6, not 3).
-To include this value close the right side of the bin interval as
-illustrated in the example below this one.

->>> series.resample('3T', label='right').sum()
-2000-01-01 00:03:00     3
-2000-01-01 00:06:00    12
-2000-01-01 00:09:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but close the right
-side of the bin interval.

->>> series.resample('3T', label='right', closed='right').sum()
-2000-01-01 00:00:00     0
-2000-01-01 00:03:00     6
-2000-01-01 00:06:00    15
-2000-01-01 00:09:00    15
-Freq: 3T, dtype: int64

-Upsample the series into 30 second bins.

->>> series.resample('30S').asfreq()[0:5] #select first 5 rows
-2000-01-01 00:00:00   0.0
-2000-01-01 00:00:30   NaN
-2000-01-01 00:01:00   1.0
-2000-01-01 00:01:30   NaN
-2000-01-01 00:02:00   2.0
-Freq: 30S, dtype: float64

-Upsample the series into 30 second bins and fill the ``NaN``
-values using the ``pad`` method.

->>> series.resample('30S').pad()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    0
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    1
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Upsample the series into 30 second bins and fill the
-``NaN`` values using the ``bfill`` method.

->>> series.resample('30S').bfill()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    1
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    2
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Pass a custom function via ``apply``

->>> def custom_resampler(array_like):
-...     return np.sum(array_like)+5

->>> series.resample('3T').apply(custom_resampler)
-2000-01-01 00:00:00     8
-2000-01-01 00:03:00    17
-2000-01-01 00:06:00    26
-Freq: 3T, dtype: int64

-For DataFrame objects, the keyword ``on`` can be used to specify the
-column instead of the index for resampling.

->>> df = pd.DataFrame(data=9*[range(4)], columns=['a', 'b', 'c', 'd'])
->>> df['time'] = pd.date_range('1/1/2000', periods=9, freq='T')
->>> df.resample('3T', on='time').sum()
-                     a  b  c  d
-time
-2000-01-01 00:00:00  0  3  6  9
-2000-01-01 00:03:00  0  3  6  9
-2000-01-01 00:06:00  0  3  6  9

-For a DataFrame with MultiIndex, the keyword ``level`` can be used to
-specify on level the resampling needs to take place.

->>> time = pd.date_range('1/1/2000', periods=5, freq='T')
->>> df2 = pd.DataFrame(data=10*[range(4)],
-                       columns=['a', 'b', 'c', 'd'],
-                       index=pd.MultiIndex.from_product([time, [1, 2]])
-                       )
->>> df2.resample('3T', level=0).sum()
-                     a  b   c   d
-2000-01-01 00:00:00  0  6  12  18
-2000-01-01 00:03:00  0  4   8  12
- -
sample(self, n=None, frac=None, replace=False, weights=None, random_state=None, axis=None)
Returns a random sample of items from an axis of object.

-.. versionadded:: 0.16.1

-Parameters
-----------
-n : int, optional
-    Number of items from axis to return. Cannot be used with `frac`.
-    Default = 1 if `frac` = None.
-frac : float, optional
-    Fraction of axis items to return. Cannot be used with `n`.
-replace : boolean, optional
-    Sample with or without replacement. Default = False.
-weights : str or ndarray-like, optional
-    Default 'None' results in equal probability weighting.
-    If passed a Series, will align with target object on index. Index
-    values in weights not found in sampled object will be ignored and
-    index values in sampled object not in weights will be assigned
-    weights of zero.
-    If called on a DataFrame, will accept the name of a column
-    when axis = 0.
-    Unless weights are a Series, weights must be same length as axis
-    being sampled.
-    If weights do not sum to 1, they will be normalized to sum to 1.
-    Missing values in the weights column will be treated as zero.
-    inf and -inf values not allowed.
-random_state : int or numpy.random.RandomState, optional
-    Seed for the random number generator (if int), or numpy RandomState
-    object.
-axis : int or string, optional
-    Axis to sample. Accepts axis number or name. Default is stat axis
-    for given data type (0 for Series and DataFrames, 1 for Panels).

-Returns
--------
-A new object of same type as caller.

-Examples
---------

-Generate an example ``Series`` and ``DataFrame``:

->>> s = pd.Series(np.random.randn(50))
->>> s.head()
-0   -0.038497
-1    1.820773
-2   -0.972766
-3   -1.598270
-4   -1.095526
-dtype: float64
->>> df = pd.DataFrame(np.random.randn(50, 4), columns=list('ABCD'))
->>> df.head()
-          A         B         C         D
-0  0.016443 -2.318952 -0.566372 -1.028078
-1 -1.051921  0.438836  0.658280 -0.175797
-2 -1.243569 -0.364626 -0.215065  0.057736
-3  1.768216  0.404512 -0.385604 -1.457834
-4  1.072446 -1.137172  0.314194 -0.046661

-Next extract a random sample from both of these objects...

-3 random elements from the ``Series``:

->>> s.sample(n=3)
-27   -0.994689
-55   -1.049016
-67   -0.224565
-dtype: float64

-And a random 10% of the ``DataFrame`` with replacement:

->>> df.sample(frac=0.1, replace=True)
-           A         B         C         D
-35  1.981780  0.142106  1.817165 -0.290805
-49 -1.336199 -0.448634 -0.789640  0.217116
-40  0.823173 -0.078816  1.009536  1.015108
-15  1.421154 -0.055301 -1.922594 -0.019696
-6  -0.148339  0.832938  1.787600 -1.383767
- -
select(self, crit, axis=0)
Return data corresponding to axis labels matching criteria

-Parameters
-----------
-crit : function
-    To be called on each index (label). Should return True or False
-axis : int

-Returns
--------
-selection : type of caller
- -
set_axis(self, axis, labels)
public verson of axis assignment
- -
slice_shift(self, periods=1, axis=0)
Equivalent to `shift` without copying data. The shifted data will
-not include the dropped periods and the shifted axis will be smaller
-than the original.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative

-Notes
------
-While the `slice_shift` is faster than `shift`, you may pay for it
-later during alignment.

-Returns
--------
-shifted : same type as caller
- -
squeeze(self, axis=None)
Squeeze length 1 dimensions.

-Parameters
-----------
-axis : None, integer or string axis name, optional
-    The axis to squeeze if 1-sized.

-    .. versionadded:: 0.20.0

-Returns
--------
-scalar if 1-sized, else original object
- -
swapaxes(self, axis1, axis2, copy=True)
Interchange axes and swap values axes appropriately

-Returns
--------
-y : same as input
- -
tail(self, n=5)
Returns last n rows
- -
to_clipboard(self, excel=None, sep=None, **kwargs)
Attempt to write text representation of object to the system clipboard
-This can be pasted into Excel, for example.

-Parameters
-----------
-excel : boolean, defaults to True
-        if True, use the provided separator, writing in a csv
-        format for allowing easy pasting into excel.
-        if False, write a string representation of the object
-        to the clipboard
-sep : optional, defaults to tab
-other keywords are passed to to_csv

-Notes
------
-Requirements for your platform
-  - Linux: xclip, or xsel (with gtk or PyQt4 modules)
-  - Windows: none
-  - OS X: none
- -
to_dense(self)
Return dense representation of NDFrame (as opposed to sparse)
- -
to_hdf(self, path_or_buf, key, **kwargs)
Write the contained data to an HDF5 file using HDFStore.

-Parameters
-----------
-path_or_buf : the path (string) or HDFStore object
-key : string
-    identifier for the group in the store
-mode : optional, {'a', 'w', 'r+'}, default 'a'

-  ``'w'``
-      Write; a new file is created (an existing file with the same
-      name would be deleted).
-  ``'a'``
-      Append; an existing file is opened for reading and writing,
-      and if the file does not exist it is created.
-  ``'r+'``
-      It is similar to ``'a'``, but the file must already exist.
-format : 'fixed(f)|table(t)', default is 'fixed'
-    fixed(f) : Fixed format
-               Fast writing/reading. Not-appendable, nor searchable
-    table(t) : Table format
-               Write as a PyTables Table structure which may perform
-               worse but allow more flexible operations like searching
-               / selecting subsets of the data
-append : boolean, default False
-    For Table formats, append the input data to the existing
-data_columns :  list of columns, or True, default None
-    List of columns to create as indexed data columns for on-disk
-    queries, or True to use all columns. By default only the axes
-    of the object are indexed. See `here
-    <http://pandas.pydata.org/pandas-docs/stable/io.html#query-via-data-columns>`__.

-    Applicable only to format='table'.
-complevel : int, 1-9, default 0
-    If a complib is specified compression will be applied
-    where possible
-complib : {'zlib', 'bzip2', 'lzo', 'blosc', None}, default None
-    If complevel is > 0 apply compression to objects written
-    in the store wherever possible
-fletcher32 : bool, default False
-    If applying compression use the fletcher32 checksum
-dropna : boolean, default False.
-    If true, ALL nan rows will not be written to store.
- -
to_json(self, path_or_buf=None, orient=None, date_format=None, double_precision=10, force_ascii=True, date_unit='ms', default_handler=None, lines=False)
Convert the object to a JSON string.

-Note NaN's and None will be converted to null and datetime objects
-will be converted to UNIX timestamps.

-Parameters
-----------
-path_or_buf : the path or buffer to write the result string
-    if this is None, return a StringIO of the converted string
-orient : string

-    * Series

-      - default is 'index'
-      - allowed values are: {'split','records','index'}

-    * DataFrame

-      - default is 'columns'
-      - allowed values are:
-        {'split','records','index','columns','values'}

-    * The format of the JSON string

-      - split : dict like
-        {index -> [index], columns -> [columns], data -> [values]}
-      - records : list like
-        [{column -> value}, ... , {column -> value}]
-      - index : dict like {index -> {column -> value}}
-      - columns : dict like {column -> {index -> value}}
-      - values : just the values array
-      - table : dict like {'schema': {schema}, 'data': {data}}
-        describing the data, and the data component is
-        like ``orient='records'``.

-        .. versionchanged:: 0.20.0

-date_format : {None, 'epoch', 'iso'}
-    Type of date conversion. `epoch` = epoch milliseconds,
-    `iso` = ISO8601. The default depends on the `orient`. For
-    `orient='table'`, the default is `'iso'`. For all other orients,
-    the default is `'epoch'`.
-double_precision : The number of decimal places to use when encoding
-    floating point values, default 10.
-force_ascii : force encoded string to be ASCII, default True.
-date_unit : string, default 'ms' (milliseconds)
-    The time unit to encode to, governs timestamp and ISO8601
-    precision.  One of 's', 'ms', 'us', 'ns' for second, millisecond,
-    microsecond, and nanosecond respectively.
-default_handler : callable, default None
-    Handler to call if object cannot otherwise be converted to a
-    suitable format for JSON. Should receive a single argument which is
-    the object to convert and return a serialisable object.
-lines : boolean, default False
-    If 'orient' is 'records' write out line delimited json format. Will
-    throw ValueError if incorrect 'orient' since others are not list
-    like.

-    .. versionadded:: 0.19.0

-Returns
--------
-same type as input object with filtered info axis

-See Also
---------
-pd.read_json

-Examples
---------

->>> df = pd.DataFrame([['a', 'b'], ['c', 'd']],
-...                   index=['row 1', 'row 2'],
-...                   columns=['col 1', 'col 2'])
->>> df.to_json(orient='split')
-'{"columns":["col 1","col 2"],
-  "index":["row 1","row 2"],
-  "data":[["a","b"],["c","d"]]}'

-Encoding/decoding a Dataframe using ``'index'`` formatted JSON:

->>> df.to_json(orient='index')
-'{"row 1":{"col 1":"a","col 2":"b"},"row 2":{"col 1":"c","col 2":"d"}}'

-Encoding/decoding a Dataframe using ``'records'`` formatted JSON.
-Note that index labels are not preserved with this encoding.

->>> df.to_json(orient='records')
-'[{"col 1":"a","col 2":"b"},{"col 1":"c","col 2":"d"}]'

-Encoding with Table Schema

->>> df.to_json(orient='table')
-'{"schema": {"fields": [{"name": "index", "type": "string"},
-                        {"name": "col 1", "type": "string"},
-                        {"name": "col 2", "type": "string"}],
-             "primaryKey": "index",
-             "pandas_version": "0.20.0"},
-  "data": [{"index": "row 1", "col 1": "a", "col 2": "b"},
-           {"index": "row 2", "col 1": "c", "col 2": "d"}]}'
- -
to_msgpack(self, path_or_buf=None, encoding='utf-8', **kwargs)
msgpack (serialize) object to input file path

-THIS IS AN EXPERIMENTAL LIBRARY and the storage format
-may not be stable until a future release.

-Parameters
-----------
-path : string File path, buffer-like, or None
-    if None, return generated string
-append : boolean whether to append to an existing msgpack
-    (default is False)
-compress : type of compressor (zlib or blosc), default to None (no
-    compression)
- -
to_pickle(self, path, compression='infer')
Pickle (serialize) object to input file path.

-Parameters
-----------
-path : string
-    File path
-compression : {'infer', 'gzip', 'bz2', 'xz', None}, default 'infer'
-    a string representing the compression to use in the output file

-    .. versionadded:: 0.20.0
- -
to_sql(self, name, con, flavor=None, schema=None, if_exists='fail', index=True, index_label=None, chunksize=None, dtype=None)
Write records stored in a DataFrame to a SQL database.

-Parameters
-----------
-name : string
-    Name of SQL table
-con : SQLAlchemy engine or DBAPI2 connection (legacy mode)
-    Using SQLAlchemy makes it possible to use any DB supported by that
-    library. If a DBAPI2 object, only sqlite3 is supported.
-flavor : 'sqlite', default None
-    DEPRECATED: this parameter will be removed in a future version,
-    as 'sqlite' is the only supported option if SQLAlchemy is not
-    installed.
-schema : string, default None
-    Specify the schema (if database flavor supports this). If None, use
-    default schema.
-if_exists : {'fail', 'replace', 'append'}, default 'fail'
-    - fail: If table exists, do nothing.
-    - replace: If table exists, drop it, recreate it, and insert data.
-    - append: If table exists, insert data. Create if does not exist.
-index : boolean, default True
-    Write DataFrame index as a column.
-index_label : string or sequence, default None
-    Column label for index column(s). If None is given (default) and
-    `index` is True, then the index names are used.
-    A sequence should be given if the DataFrame uses MultiIndex.
-chunksize : int, default None
-    If not None, then rows will be written in batches of this size at a
-    time.  If None, all rows will be written at once.
-dtype : dict of column name to SQL type, default None
-    Optional specifying the datatype for columns. The SQL type should
-    be a SQLAlchemy type, or a string for sqlite3 fallback connection.
- -
to_xarray(self)
Return an xarray object from the pandas object.

-Returns
--------
-a DataArray for a Series
-a Dataset for a DataFrame
-a DataArray for higher dims

-Examples
---------
->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)})
->>> df
-   A    B    C
-0  1  foo  4.0
-1  1  bar  5.0
-2  2  foo  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (index: 3)
-Coordinates:
-  * index    (index) int64 0 1 2
-Data variables:
-    A        (index) int64 1 1 2
-    B        (index) object 'foo' 'bar' 'foo'
-    C        (index) float64 4.0 5.0 6.0

->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)}
-                     ).set_index(['B','A'])
->>> df
-         C
-B   A
-foo 1  4.0
-bar 1  5.0
-foo 2  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (A: 2, B: 2)
-Coordinates:
-  * B        (B) object 'bar' 'foo'
-  * A        (A) int64 1 2
-Data variables:
-    C        (B, A) float64 5.0 nan 4.0 6.0

->>> p = pd.Panel(np.arange(24).reshape(4,3,2),
-                 items=list('ABCD'),
-                 major_axis=pd.date_range('20130101', periods=3),
-                 minor_axis=['first', 'second'])
->>> p
-<class 'pandas.core.panel.Panel'>
-Dimensions: 4 (items) x 3 (major_axis) x 2 (minor_axis)
-Items axis: A to D
-Major_axis axis: 2013-01-01 00:00:00 to 2013-01-03 00:00:00
-Minor_axis axis: first to second

->>> p.to_xarray()
-<xarray.DataArray (items: 4, major_axis: 3, minor_axis: 2)>
-array([[[ 0,  1],
-        [ 2,  3],
-        [ 4,  5]],
-       [[ 6,  7],
-        [ 8,  9],
-        [10, 11]],
-       [[12, 13],
-        [14, 15],
-        [16, 17]],
-       [[18, 19],
-        [20, 21],
-        [22, 23]]])
-Coordinates:
-  * items       (items) object 'A' 'B' 'C' 'D'
-  * major_axis  (major_axis) datetime64[ns] 2013-01-01 2013-01-02 2013-01-03  # noqa
-  * minor_axis  (minor_axis) object 'first' 'second'

-Notes
------
-See the `xarray docs <http://xarray.pydata.org/en/stable/>`__
- -
truncate(self, before=None, after=None, axis=None, copy=True)
Truncates a sorted NDFrame before and/or after some particular
-index value. If the axis contains only datetime values, before/after
-parameters are converted to datetime values.

-Parameters
-----------
-before : date
-    Truncate before index value
-after : date
-    Truncate after index value
-axis : the truncation axis, defaults to the stat axis
-copy : boolean, default is True,
-    return a copy of the truncated section

-Returns
--------
-truncated : type of caller
- -
tshift(self, periods=1, freq=None, axis=0)
Shift the time index, using the index's frequency if available.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, default None
-    Increment to use from the tseries module or time rule (e.g. 'EOM')
-axis : int or basestring
-    Corresponds to the axis that contains the Index

-Notes
------
-If freq is not specified then tries to use the freq or inferred_freq
-attributes of the index. If neither of those attributes exist, a
-ValueError is thrown

-Returns
--------
-shifted : NDFrame
- -
tz_convert(self, tz, axis=0, level=None, copy=True)
Convert tz-aware axis to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to convert
-level : int, str, default None
-    If axis ia a MultiIndex, convert a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data

-Returns
--------

-Raises
-------
-TypeError
-    If the axis is tz-naive.
- -
tz_localize(self, tz, axis=0, level=None, copy=True, ambiguous='raise')
Localize tz-naive TimeSeries to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to localize
-level : int, str, default None
-    If axis ia a MultiIndex, localize a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data
-ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise'
-    - 'infer' will attempt to infer fall dst-transition hours based on
-      order
-    - bool-ndarray where True signifies a DST time, False designates
-      a non-DST time (note that this flag is only applicable for
-      ambiguous times)
-    - 'NaT' will return NaT where there are ambiguous times
-    - 'raise' will raise an AmbiguousTimeError if there are ambiguous
-      times
-infer_dst : boolean, default False (DEPRECATED)
-    Attempt to infer fall dst-transition hours based on order

-Returns
--------

-Raises
-------
-TypeError
-    If the TimeSeries is tz-aware and tz is not None.
- -
where(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is True and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The where method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``True`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``where`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.mask`
- -
xs(self, key, axis=0, level=None, drop_level=True)
Returns a cross-section (row(s) or column(s)) from the
-Series/DataFrame. Defaults to cross-section on the rows (axis=0).

-Parameters
-----------
-key : object
-    Some label contained in the index, or partially in a MultiIndex
-axis : int, default 0
-    Axis to retrieve cross-section on
-level : object, defaults to first n levels (n=1 or len(key))
-    In case of a key partially contained in a MultiIndex, indicate
-    which levels are used. Levels can be referred by label or position.
-drop_level : boolean, default True
-    If False, returns object with same levels as self.

-Examples
---------
->>> df
-   A  B  C
-a  4  5  2
-b  4  0  9
-c  9  7  3
->>> df.xs('a')
-A    4
-B    5
-C    2
-Name: a
->>> df.xs('C', axis=1)
-a    2
-b    9
-c    3
-Name: C

->>> df
-                    A  B  C  D
-first second third
-bar   one    1      4  1  8  9
-      two    1      7  5  5  0
-baz   one    1      6  6  8  0
-      three  2      5  3  5  3
->>> df.xs(('baz', 'three'))
-       A  B  C  D
-third
-2      5  3  5  3
->>> df.xs('one', level=1)
-             A  B  C  D
-first third
-bar   1      4  1  8  9
-baz   1      6  6  8  0
->>> df.xs(('baz', 2), level=[0, 'third'])
-        A  B  C  D
-second
-three   5  3  5  3

-Returns
--------
-xs : Series or DataFrame

-Notes
------
-xs is only for getting, not setting values.

-MultiIndex Slicers is a generic way to get/set values on any level or
-levels.  It is a superset of xs functionality, see
-:ref:`MultiIndex Slicers <advanced.mi_slicers>`
- -
-Data descriptors inherited from pandas.core.generic.NDFrame:
-
at
-
Fast label-based scalar accessor

-Similarly to ``loc``, ``at`` provides **label** based scalar lookups.
-You can also set using these indexers.
-
-
blocks
-
Internal property, property synonym for as_blocks()
-
-
iat
-
Fast integer location scalar accessor.

-Similarly to ``iloc``, ``iat`` provides **integer** based lookups.
-You can also set using these indexers.
-
-
iloc
-
Purely integer-location based indexing for selection by position.

-``.iloc[]`` is primarily integer position based (from ``0`` to
-``length-1`` of the axis), but may also be used with a boolean
-array.

-Allowed inputs are:

-- An integer, e.g. ``5``.
-- A list or array of integers, e.g. ``[4, 3, 0]``.
-- A slice object with ints, e.g. ``1:7``.
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.iloc`` will raise ``IndexError`` if a requested indexer is
-out-of-bounds, except *slice* indexers which allow out-of-bounds
-indexing (this conforms with python/numpy *slice* semantics).

-See more at :ref:`Selection by Position <indexing.integer>`
-
-
ix
-
A primarily label-location based indexer, with integer position
-fallback.

-``.ix[]`` supports mixed integer and label based access. It is
-primarily label based, but will fall back to integer positional
-access unless the corresponding axis is of integer type.

-``.ix`` is the most general indexer and will support any of the
-inputs in ``.loc`` and ``.iloc``. ``.ix`` also supports floating
-point label schemes. ``.ix`` is exceptionally useful when dealing
-with mixed positional and label based hierachical indexes.

-However, when an axis is integer based, ONLY label based access
-and not positional access is supported. Thus, in such cases, it's
-usually better to be explicit and use ``.iloc`` or ``.loc``.

-See more at :ref:`Advanced Indexing <advanced>`.
-
-
loc
-
Purely label-location based indexer for selection by label.

-``.loc[]`` is primarily label based, but may also be used with a
-boolean array.

-Allowed inputs are:

-- A single label, e.g. ``5`` or ``'a'``, (note that ``5`` is
-  interpreted as a *label* of the index, and **never** as an
-  integer position along the index).
-- A list or array of labels, e.g. ``['a', 'b', 'c']``.
-- A slice object with labels, e.g. ``'a':'f'`` (note that contrary
-  to usual python slices, **both** the start and the stop are included!).
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.loc`` will raise a ``KeyError`` when the items are not found.

-See more at :ref:`Selection by Label <indexing.label>`
-
-
-Data and other attributes inherited from pandas.core.generic.NDFrame:
-
is_copy = None
- -
-Methods inherited from pandas.core.base.PandasObject:
-
__dir__(self)
Provide method name lookup and completion
-Only provide 'public' methods
- -
__sizeof__(self)
Generates the total memory usage for a object that returns
-either a value or Series of values
- -
-Methods inherited from pandas.core.base.StringMixin:
-
__bytes__(self)
Return a string representation for a particular object.

-Invoked by bytes(obj) in py3 only.
-Yields a bytestring in both py2/py3.
- -
__repr__(self)
Return a string representation for a particular object.

-Yields Bytestring in Py2, Unicode String in py3.
- -
__str__(self)
Return a string representation for a particular Object

-Invoked by str(df) in both py2/py3.
-Yields Bytestring in Py2, Unicode String in py3.
- -

- - - - - -
 
-class SubPlots(builtins.object)
    Methods defined here:
-
__init__(self, fig, axes_seq)
Initialize self.  See help(type(self)) for accurate signature.
- -
current_axes()
- -
next_axes(self)
# TODO: consider making SubPlots iterable
- -
-Data descriptors defined here:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-

- - - - - - - -
 
-class SweepFrame(MyDataFrame)
   MyTimeFrame is a modified version of a Pandas DataFrame,
-with a few changes to make it more suited to our purpose.

-In particular, DataFrame provides two special variables called
-`dt` and `T` that cause problems if we try to use those names
-as state variables.

-So I added new definitions that override the special variables
-and make these names useable as row labels.
 
 
Method resolution order:
-
SweepFrame
-
MyDataFrame
-
pandas.core.frame.DataFrame
-
pandas.core.generic.NDFrame
-
pandas.core.base.PandasObject
-
pandas.core.base.StringMixin
-
pandas.core.base.SelectionMixin
-
builtins.object
-
-
-Methods inherited from MyDataFrame:
-
__init__(self, *args, **kwargs)
Initialize self.  See help(type(self)) for accurate signature.
- -
-Data descriptors inherited from MyDataFrame:
-
T
-
Intercept the Series accessor object so we can use `T`
-as a row label and access it using dot notation.

-https://pandas.pydata.org/pandas-docs/stable/generated/
-pandas.DataFrame.T.html#pandas.DataFrame.T
-
-
dt
-
Intercept the Series accessor object so we can use `dt`
-as a row label and access it using dot notation.

-https://pandas.pydata.org/pandas-docs/stable/generated/
-pandas.DataFrame.dt.html
-
-
-Methods inherited from pandas.core.frame.DataFrame:
-
__add__(self, other, axis=None, level=None, fill_value=None)
Binary operator __add__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__and__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __and__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__div__ = __truediv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __truediv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__eq__(self, other)
Wrapper for comparison method __eq__
- -
__floordiv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __floordiv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__ge__(self, other)
Wrapper for comparison method __ge__
- -
__getitem__(self, key)
- -
__gt__(self, other)
Wrapper for comparison method __gt__
- -
__iadd__ = f(self, other)
- -
__imul__ = f(self, other)
- -
__ipow__ = f(self, other)
- -
__isub__ = f(self, other)
- -
__itruediv__ = f(self, other)
- -
__le__(self, other)
Wrapper for comparison method __le__
- -
__len__(self)
Returns length of info axis, but here we use the index
- -
__lt__(self, other)
Wrapper for comparison method __lt__
- -
__mod__(self, other, axis=None, level=None, fill_value=None)
Binary operator __mod__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__mul__(self, other, axis=None, level=None, fill_value=None)
Binary operator __mul__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__ne__(self, other)
Wrapper for comparison method __ne__
- -
__or__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __or__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__pow__(self, other, axis=None, level=None, fill_value=None)
Binary operator __pow__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__radd__(self, other, axis=None, level=None, fill_value=None)
Binary operator __radd__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rand__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __rand__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rdiv__ = __rtruediv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rtruediv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rfloordiv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rfloordiv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rmod__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rmod__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rmul__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rmul__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__ror__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __ror__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rpow__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rpow__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rsub__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rsub__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rtruediv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rtruediv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rxor__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __rxor__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__setitem__(self, key, value)
- -
__sub__(self, other, axis=None, level=None, fill_value=None)
Binary operator __sub__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__truediv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __truediv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__unicode__(self)
Return a string representation for a particular DataFrame

-Invoked by unicode(df) in py2 only. Yields a Unicode String in both
-py2/py3.
- -
__xor__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __xor__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
add(self, other, axis='columns', level=None, fill_value=None)
Addition of dataframe and other, element-wise (binary operator `add`).

-Equivalent to ``dataframe + other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.radd
- -
agg = aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a DataFrame or when passed to DataFrame.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : DataFrame

-Examples
---------

->>> df = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
-...                   index=pd.date_range('1/1/2000', periods=10))
->>> df.iloc[3:7] = np.nan

-Aggregate these functions across all columns

->>> df.agg(['sum', 'min'])
-            A         B         C
-sum -0.182253 -0.614014 -2.909534
-min -1.916563 -1.460076 -1.568297

-Different aggregations per column

->>> df.agg({'A' : ['sum', 'min'], 'B' : ['min', 'max']})
-            A         B
-max       NaN  1.514318
-min -1.916563 -1.460076
-sum -0.182253       NaN

-See also
---------
-pandas.DataFrame.apply
-pandas.DataFrame.transform
-pandas.DataFrame.groupby.aggregate
-pandas.DataFrame.resample.aggregate
-pandas.DataFrame.rolling.aggregate
- -
aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a DataFrame or when passed to DataFrame.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : DataFrame

-Examples
---------

->>> df = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
-...                   index=pd.date_range('1/1/2000', periods=10))
->>> df.iloc[3:7] = np.nan

-Aggregate these functions across all columns

->>> df.agg(['sum', 'min'])
-            A         B         C
-sum -0.182253 -0.614014 -2.909534
-min -1.916563 -1.460076 -1.568297

-Different aggregations per column

->>> df.agg({'A' : ['sum', 'min'], 'B' : ['min', 'max']})
-            A         B
-max       NaN  1.514318
-min -1.916563 -1.460076
-sum -0.182253       NaN

-See also
---------
-pandas.DataFrame.apply
-pandas.DataFrame.transform
-pandas.DataFrame.groupby.aggregate
-pandas.DataFrame.resample.aggregate
-pandas.DataFrame.rolling.aggregate
- -
align(self, other, join='outer', axis=None, level=None, copy=True, fill_value=None, method=None, limit=None, fill_axis=0, broadcast_axis=None)
Align two object on their axes with the
-specified join method for each axis Index

-Parameters
-----------
-other : DataFrame or Series
-join : {'outer', 'inner', 'left', 'right'}, default 'outer'
-axis : allowed axis of the other object, default None
-    Align on index (0), columns (1), or both (None)
-level : int or level name, default None
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-copy : boolean, default True
-    Always returns new objects. If copy=False and no reindexing is
-    required then original objects are returned.
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-method : str, default None
-limit : int, default None
-fill_axis : {0 or 'index', 1 or 'columns'}, default 0
-    Filling axis, method and limit
-broadcast_axis : {0 or 'index', 1 or 'columns'}, default None
-    Broadcast values along this axis, if aligning two objects of
-    different dimensions

-    .. versionadded:: 0.17.0

-Returns
--------
-(left, right) : (DataFrame, type of other)
-    Aligned objects
- -
all(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether all elements are True over requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-all : Series or DataFrame (if level specified)
- -
any(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether any element is True over requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-any : Series or DataFrame (if level specified)
- -
append(self, other, ignore_index=False, verify_integrity=False)
Append rows of `other` to the end of this frame, returning a new
-object. Columns not in this frame are added as new columns.

-Parameters
-----------
-other : DataFrame or Series/dict-like object, or list of these
-    The data to append.
-ignore_index : boolean, default False
-    If True, do not use the index labels.
-verify_integrity : boolean, default False
-    If True, raise ValueError on creating index with duplicates.

-Returns
--------
-appended : DataFrame

-Notes
------
-If a list of dict/series is passed and the keys are all contained in
-the DataFrame's index, the order of the columns in the resulting
-DataFrame will be unchanged.

-See also
---------
-pandas.concat : General function to concatenate DataFrameSeries
-    or Panel objects

-Examples
---------

->>> df = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))
->>> df
-   A  B
-0  1  2
-1  3  4
->>> df2 = pd.DataFrame([[5, 6], [7, 8]], columns=list('AB'))
->>> df.append(df2)
-   A  B
-0  1  2
-1  3  4
-0  5  6
-1  7  8

-With `ignore_index` set to True:

->>> df.append(df2, ignore_index=True)
-   A  B
-0  1  2
-1  3  4
-2  5  6
-3  7  8
- -
apply(self, func, axis=0, broadcast=False, raw=False, reduce=None, args=(), **kwds)
Applies function along input axis of DataFrame.

-Objects passed to functions are Series objects having index
-either the DataFrame's index (axis=0) or the columns (axis=1).
-Return type depends on whether passed function aggregates, or the
-reduce argument if the DataFrame is empty.

-Parameters
-----------
-func : function
-    Function to apply to each column/row
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    * 0 or 'index': apply function to each column
-    * 1 or 'columns': apply function to each row
-broadcast : boolean, default False
-    For aggregation functions, return object of same size with values
-    propagated
-raw : boolean, default False
-    If False, convert each row or column into a Series. If raw=True the
-    passed function will receive ndarray objects instead. If you are
-    just applying a NumPy reduction function this will achieve much
-    better performance
-reduce : boolean or None, default None
-    Try to apply reduction procedures. If the DataFrame is empty,
-    apply will use reduce to determine whether the result should be a
-    Series or a DataFrame. If reduce is None (the default), apply's
-    return value will be guessed by calling func an empty Series (note:
-    while guessing, exceptions raised by func will be ignored). If
-    reduce is True a Series will always be returned, and if False a
-    DataFrame will always be returned.
-args : tuple
-    Positional arguments to pass to function in addition to the
-    array/series
-Additional keyword arguments will be passed as keywords to the function

-Notes
------
-In the current implementation apply calls func twice on the
-first column/row to decide whether it can take a fast or slow
-code path. This can lead to unexpected behavior if func has
-side-effects, as they will take effect twice for the first
-column/row.

-Examples
---------
->>> df.apply(numpy.sqrt) # returns DataFrame
->>> df.apply(numpy.sum, axis=0) # equiv to df.sum(0)
->>> df.apply(numpy.sum, axis=1) # equiv to df.sum(1)

-See also
---------
-DataFrame.applymap: For elementwise operations
-DataFrame.aggregate: only perform aggregating type operations
-DataFrame.transform: only perform transformating type operations

-Returns
--------
-applied : Series or DataFrame
- -
applymap(self, func)
Apply a function to a DataFrame that is intended to operate
-elementwise, i.e. like doing map(func, series) for each series in the
-DataFrame

-Parameters
-----------
-func : function
-    Python function, returns a single value from a single value

-Examples
---------

->>> df = pd.DataFrame(np.random.randn(3, 3))
->>> df
-    0         1          2
-0  -0.029638  1.081563   1.280300
-1   0.647747  0.831136  -1.549481
-2   0.513416 -0.884417   0.195343
->>> df = df.applymap(lambda x: '%.2f' % x)
->>> df
-    0         1          2
-0  -0.03      1.08       1.28
-1   0.65      0.83      -1.55
-2   0.51     -0.88       0.20

-Returns
--------
-applied : DataFrame

-See also
---------
-DataFrame.apply : For operations on rows/columns
- -
assign(self, **kwargs)
Assign new columns to a DataFrame, returning a new object
-(a copy) with all the original columns in addition to the new ones.

-.. versionadded:: 0.16.0

-Parameters
-----------
-kwargs : keyword, value pairs
-    keywords are the column names. If the values are
-    callable, they are computed on the DataFrame and
-    assigned to the new columns. The callable must not
-    change input DataFrame (though pandas doesn't check it).
-    If the values are not callable, (e.g. a Series, scalar, or array),
-    they are simply assigned.

-Returns
--------
-df : DataFrame
-    A new DataFrame with the new columns in addition to
-    all the existing columns.

-Notes
------
-Since ``kwargs`` is a dictionary, the order of your
-arguments may not be preserved. To make things predicatable,
-the columns are inserted in alphabetical order, at the end of
-your DataFrame. Assigning multiple columns within the same
-``assign`` is possible, but you cannot reference other columns
-created within the same ``assign`` call.

-Examples
---------
->>> df = DataFrame({'A': range(1, 11), 'B': np.random.randn(10)})

-Where the value is a callable, evaluated on `df`:

->>> df.assign(ln_A = lambda x: np.log(x.A))
-    A         B      ln_A
-0   1  0.426905  0.000000
-1   2 -0.780949  0.693147
-2   3 -0.418711  1.098612
-3   4 -0.269708  1.386294
-4   5 -0.274002  1.609438
-5   6 -0.500792  1.791759
-6   7  1.649697  1.945910
-7   8 -1.495604  2.079442
-8   9  0.549296  2.197225
-9  10 -0.758542  2.302585

-Where the value already exists and is inserted:

->>> newcol = np.log(df['A'])
->>> df.assign(ln_A=newcol)
-    A         B      ln_A
-0   1  0.426905  0.000000
-1   2 -0.780949  0.693147
-2   3 -0.418711  1.098612
-3   4 -0.269708  1.386294
-4   5 -0.274002  1.609438
-5   6 -0.500792  1.791759
-6   7  1.649697  1.945910
-7   8 -1.495604  2.079442
-8   9  0.549296  2.197225
-9  10 -0.758542  2.302585
- -
boxplot(self, column=None, by=None, ax=None, fontsize=None, rot=0, grid=True, figsize=None, layout=None, return_type=None, **kwds)
Make a box plot from DataFrame column optionally grouped by some columns or
-other inputs

-Parameters
-----------
-data : the pandas object holding the data
-column : column name or list of names, or vector
-    Can be any valid input to groupby
-by : string or sequence
-    Column in the DataFrame to group by
-ax : Matplotlib axes object, optional
-fontsize : int or string
-rot : label rotation angle
-figsize : A tuple (width, height) in inches
-grid : Setting this to True will show the grid
-layout : tuple (optional)
-    (rows, columns) for the layout of the plot
-return_type : {None, 'axes', 'dict', 'both'}, default None
-    The kind of object to return. The default is ``axes``
-    'axes' returns the matplotlib axes the boxplot is drawn on;
-    'dict' returns a dictionary  whose values are the matplotlib
-    Lines of the boxplot;
-    'both' returns a namedtuple with the axes and dict.

-    When grouping with ``by``, a Series mapping columns to ``return_type``
-    is returned, unless ``return_type`` is None, in which case a NumPy
-    array of axes is returned with the same shape as ``layout``.
-    See the prose documentation for more.

-kwds : other plotting keyword arguments to be passed to matplotlib boxplot
-       function

-Returns
--------
-lines : dict
-ax : matplotlib Axes
-(ax, lines): namedtuple

-Notes
------
-Use ``return_type='dict'`` when you want to tweak the appearance
-of the lines after plotting. In this case a dict containing the Lines
-making up the boxes, caps, fliers, medians, and whiskers is returned.
- -
combine(self, other, func, fill_value=None, overwrite=True)
Add two DataFrame objects and do not propagate NaN values, so if for a
-(column, time) one frame is missing a value, it will default to the
-other frame's value (which might be NaN as well)

-Parameters
-----------
-other : DataFrame
-func : function
-fill_value : scalar value
-overwrite : boolean, default True
-    If True then overwrite values for common keys in the calling frame

-Returns
--------
-result : DataFrame
- -
combine_first(self, other)
Combine two DataFrame objects and default to non-null values in frame
-calling the method. Result index columns will be the union of the
-respective indexes and columns

-Parameters
-----------
-other : DataFrame

-Examples
---------
-a's values prioritized, use values from b to fill holes:

->>> a.combine_first(b)


-Returns
--------
-combined : DataFrame
- -
compound(self, axis=None, skipna=None, level=None)
Return the compound percentage of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-compounded : Series or DataFrame (if level specified)
- -
corr(self, method='pearson', min_periods=1)
Compute pairwise correlation of columns, excluding NA/null values

-Parameters
-----------
-method : {'pearson', 'kendall', 'spearman'}
-    * pearson : standard correlation coefficient
-    * kendall : Kendall Tau correlation coefficient
-    * spearman : Spearman rank correlation
-min_periods : int, optional
-    Minimum number of observations required per pair of columns
-    to have a valid result. Currently only available for pearson
-    and spearman correlation

-Returns
--------
-y : DataFrame
- -
corrwith(self, other, axis=0, drop=False)
Compute pairwise correlation between rows or columns of two DataFrame
-objects.

-Parameters
-----------
-other : DataFrame
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    0 or 'index' to compute column-wise, 1 or 'columns' for row-wise
-drop : boolean, default False
-    Drop missing indices from result, default returns union of all

-Returns
--------
-correls : Series
- -
count(self, axis=0, level=None, numeric_only=False)
Return Series with number of non-NA/null observations over requested
-axis. Works with non-floating point data as well (detects NaN and None)

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    0 or 'index' for row-wise, 1 or 'columns' for column-wise
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a DataFrame
-numeric_only : boolean, default False
-    Include only float, int, boolean data

-Returns
--------
-count : Series (or DataFrame if level specified)
- -
cov(self, min_periods=None)
Compute pairwise covariance of columns, excluding NA/null values

-Parameters
-----------
-min_periods : int, optional
-    Minimum number of observations required per pair of columns
-    to have a valid result.

-Returns
--------
-y : DataFrame

-Notes
------
-`y` contains the covariance matrix of the DataFrame's time series.
-The covariance is normalized by N-1 (unbiased estimator).
- -
cummax(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative max over requested axis.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummax : Series



-See also
---------
-pandas.core.window.Expanding.max : Similar functionality
-    but ignores ``NaN`` values.
- -
cummin(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative minimum over requested axis.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummin : Series



-See also
---------
-pandas.core.window.Expanding.min : Similar functionality
-    but ignores ``NaN`` values.
- -
cumprod(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative product over requested axis.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumprod : Series



-See also
---------
-pandas.core.window.Expanding.prod : Similar functionality
-    but ignores ``NaN`` values.
- -
cumsum(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative sum over requested axis.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumsum : Series



-See also
---------
-pandas.core.window.Expanding.sum : Similar functionality
-    but ignores ``NaN`` values.
- -
diff(self, periods=1, axis=0)
1st discrete difference of object

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming difference
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    Take difference over rows (0) or columns (1).

-    .. versionadded: 0.16.1

-Returns
--------
-diffed : DataFrame
- -
div = truediv(self, other, axis='columns', level=None, fill_value=None)
Floating division of dataframe and other, element-wise (binary operator `truediv`).

-Equivalent to ``dataframe / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rtruediv
- -
divide = truediv(self, other, axis='columns', level=None, fill_value=None)
Floating division of dataframe and other, element-wise (binary operator `truediv`).

-Equivalent to ``dataframe / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rtruediv
- -
dot(self, other)
Matrix multiplication with DataFrame or Series objects

-Parameters
-----------
-other : DataFrame or Series

-Returns
--------
-dot_product : DataFrame or Series
- -
drop_duplicates(self, subset=None, keep='first', inplace=False)
Return DataFrame with duplicate rows removed, optionally only
-considering certain columns

-Parameters
-----------
-subset : column label or sequence of labels, optional
-    Only consider certain columns for identifying duplicates, by
-    default use all of the columns
-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Drop duplicates except for the first occurrence.
-    - ``last`` : Drop duplicates except for the last occurrence.
-    - False : Drop all duplicates.
-inplace : boolean, default False
-    Whether to drop duplicates in place or to return a copy

-Returns
--------
-deduplicated : DataFrame
- -
dropna(self, axis=0, how='any', thresh=None, subset=None, inplace=False)
Return object with labels on given axis omitted where alternately any
-or all of the data are missing

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, or tuple/list thereof
-    Pass tuple or list to drop on multiple axes
-how : {'any', 'all'}
-    * any : if any NA values are present, drop that label
-    * all : if all values are NA, drop that label
-thresh : int, default None
-    int value : require that many non-NA values
-subset : array-like
-    Labels along other axis to consider, e.g. if you are dropping rows
-    these would be a list of columns to include
-inplace : boolean, default False
-    If True, do operation inplace and return None.

-Returns
--------
-dropped : DataFrame

-Examples
---------
->>> df = pd.DataFrame([[np.nan, 2, np.nan, 0], [3, 4, np.nan, 1],
-...                    [np.nan, np.nan, np.nan, 5]],
-...                   columns=list('ABCD'))
->>> df
-     A    B   C  D
-0  NaN  2.0 NaN  0
-1  3.0  4.0 NaN  1
-2  NaN  NaN NaN  5

-Drop the columns where all elements are nan:

->>> df.dropna(axis=1, how='all')
-     A    B  D
-0  NaN  2.0  0
-1  3.0  4.0  1
-2  NaN  NaN  5

-Drop the columns where any of the elements is nan

->>> df.dropna(axis=1, how='any')
-   D
-0  0
-1  1
-2  5

-Drop the rows where all of the elements are nan
-(there is no row to drop, so df stays the same):

->>> df.dropna(axis=0, how='all')
-     A    B   C  D
-0  NaN  2.0 NaN  0
-1  3.0  4.0 NaN  1
-2  NaN  NaN NaN  5

-Keep only the rows with at least 2 non-na values:

->>> df.dropna(thresh=2)
-     A    B   C  D
-0  NaN  2.0 NaN  0
-1  3.0  4.0 NaN  1
- -
duplicated(self, subset=None, keep='first')
Return boolean Series denoting duplicate rows, optionally only
-considering certain columns

-Parameters
-----------
-subset : column label or sequence of labels, optional
-    Only consider certain columns for identifying duplicates, by
-    default use all of the columns
-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Mark duplicates as ``True`` except for the
-      first occurrence.
-    - ``last`` : Mark duplicates as ``True`` except for the
-      last occurrence.
-    - False : Mark all duplicates as ``True``.

-Returns
--------
-duplicated : Series
- -
eq(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods eq
- -
eval(self, expr, inplace=None, **kwargs)
Evaluate an expression in the context of the calling DataFrame
-instance.

-Parameters
-----------
-expr : string
-    The expression string to evaluate.
-inplace : bool
-    If the expression contains an assignment, whether to return a new
-    DataFrame or mutate the existing.

-    WARNING: inplace=None currently falls back to to True, but
-    in a future version, will default to False.  Use inplace=True
-    explicitly rather than relying on the default.

-    .. versionadded:: 0.18.0

-kwargs : dict
-    See the documentation for :func:`~pandas.eval` for complete details
-    on the keyword arguments accepted by
-    :meth:`~pandas.DataFrame.query`.

-Returns
--------
-ret : ndarray, scalar, or pandas object

-See Also
---------
-pandas.DataFrame.query
-pandas.DataFrame.assign
-pandas.eval

-Notes
------
-For more details see the API documentation for :func:`~pandas.eval`.
-For detailed examples see :ref:`enhancing performance with eval
-<enhancingperf.eval>`.

-Examples
---------
->>> from numpy.random import randn
->>> from pandas import DataFrame
->>> df = DataFrame(randn(10, 2), columns=list('ab'))
->>> df.eval('a + b')
->>> df.eval('c = a + b')
- -
ewm(self, com=None, span=None, halflife=None, alpha=None, min_periods=0, freq=None, adjust=True, ignore_na=False, axis=0)
Provides exponential weighted functions

-.. versionadded:: 0.18.0

-Parameters
-----------
-com : float, optional
-    Specify decay in terms of center of mass,
-    :math:`\alpha = 1 / (1 + com),\text{ for } com \geq 0`
-span : float, optional
-    Specify decay in terms of span,
-    :math:`\alpha = 2 / (span + 1),\text{ for } span \geq 1`
-halflife : float, optional
-    Specify decay in terms of half-life,
-    :math:`\alpha = 1 - exp(log(0.5) / halflife),\text{ for } halflife > 0`
-alpha : float, optional
-    Specify smoothing factor :math:`\alpha` directly,
-    :math:`0 < \alpha \leq 1`

-    .. versionadded:: 0.18.0

-min_periods : int, default 0
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : None or string alias / date offset object, default=None (DEPRECATED)
-    Frequency to conform to before computing statistic
-adjust : boolean, default True
-    Divide by decaying adjustment factor in beginning periods to account
-    for imbalance in relative weightings (viewing EWMA as a moving average)
-ignore_na : boolean, default False
-    Ignore missing values when calculating weights;
-    specify True to reproduce pre-0.15.0 behavior

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.ewm(com=0.5).mean()
-          B
-0  0.000000
-1  0.750000
-2  1.615385
-3  1.615385
-4  3.670213

-Notes
------
-Exactly one of center of mass, span, half-life, and alpha must be provided.
-Allowed values and relationship between the parameters are specified in the
-parameter descriptions above; see the link at the end of this section for
-a detailed explanation.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-When adjust is True (default), weighted averages are calculated using
-weights (1-alpha)**(n-1), (1-alpha)**(n-2), ..., 1-alpha, 1.

-When adjust is False, weighted averages are calculated recursively as:
-   weighted_average[0] = arg[0];
-   weighted_average[i] = (1-alpha)*weighted_average[i-1] + alpha*arg[i].

-When ignore_na is False (default), weights are based on absolute positions.
-For example, the weights of x and y used in calculating the final weighted
-average of [x, None, y] are (1-alpha)**2 and 1 (if adjust is True), and
-(1-alpha)**2 and alpha (if adjust is False).

-When ignore_na is True (reproducing pre-0.15.0 behavior), weights are based
-on relative positions. For example, the weights of x and y used in
-calculating the final weighted average of [x, None, y] are 1-alpha and 1
-(if adjust is True), and 1-alpha and alpha (if adjust is False).

-More details can be found at
-http://pandas.pydata.org/pandas-docs/stable/computation.html#exponentially-weighted-windows
- -
expanding(self, min_periods=1, freq=None, center=False, axis=0)
Provides expanding transformations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-axis : int or string, default 0

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.expanding(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  3.0
-4  7.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).
- -
fillna(self, value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)
Fill NA/NaN values using the specified method

-Parameters
-----------
-value : scalar, dict, Series, or DataFrame
-    Value to use to fill holes (e.g. 0), alternately a
-    dict/Series/DataFrame of values specifying which value to use for
-    each index (for a Series) or column (for a DataFrame). (values not
-    in the dict/Series/DataFrame will not be filled). This value cannot
-    be a list.
-method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
-    Method to use for filling holes in reindexed Series
-    pad / ffill: propagate last valid observation forward to next valid
-    backfill / bfill: use NEXT valid observation to fill gap
-axis : {0 or 'index', 1 or 'columns'}
-inplace : boolean, default False
-    If True, fill in place. Note: this will modify any
-    other views on this object, (e.g. a no-copy slice for a column in a
-    DataFrame).
-limit : int, default None
-    If method is specified, this is the maximum number of consecutive
-    NaN values to forward/backward fill. In other words, if there is
-    a gap with more than this number of consecutive NaNs, it will only
-    be partially filled. If method is not specified, this is the
-    maximum number of entries along the entire axis where NaNs will be
-    filled. Must be greater than 0 if not None.
-downcast : dict, default is None
-    a dict of item->dtype of what to downcast if possible,
-    or the string 'infer' which will try to downcast to an appropriate
-    equal type (e.g. float64 to int64 if possible)

-See Also
---------
-reindex, asfreq

-Returns
--------
-filled : DataFrame
- -
first_valid_index(self)
Return label for first non-NA/null value
- -
floordiv(self, other, axis='columns', level=None, fill_value=None)
Integer division of dataframe and other, element-wise (binary operator `floordiv`).

-Equivalent to ``dataframe // other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rfloordiv
- -
ge(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods ge
- -
get_value(self, index, col, takeable=False)
Quickly retrieve single value at passed column and index

-Parameters
-----------
-index : row label
-col : column label
-takeable : interpret the index/col as indexers, default False

-Returns
--------
-value : scalar value
- -
gt(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods gt
- -
hist = hist_frame(data, column=None, by=None, grid=True, xlabelsize=None, xrot=None, ylabelsize=None, yrot=None, ax=None, sharex=False, sharey=False, figsize=None, layout=None, bins=10, **kwds)
Draw histogram of the DataFrame's series using matplotlib / pylab.

-Parameters
-----------
-data : DataFrame
-column : string or sequence
-    If passed, will be used to limit data to a subset of columns
-by : object, optional
-    If passed, then used to form histograms for separate groups
-grid : boolean, default True
-    Whether to show axis grid lines
-xlabelsize : int, default None
-    If specified changes the x-axis label size
-xrot : float, default None
-    rotation of x axis labels
-ylabelsize : int, default None
-    If specified changes the y-axis label size
-yrot : float, default None
-    rotation of y axis labels
-ax : matplotlib axes object, default None
-sharex : boolean, default True if ax is None else False
-    In case subplots=True, share x axis and set some x axis labels to
-    invisible; defaults to True if ax is None otherwise False if an ax
-    is passed in; Be aware, that passing in both an ax and sharex=True
-    will alter all x axis labels for all subplots in a figure!
-sharey : boolean, default False
-    In case subplots=True, share y axis and set some y axis labels to
-    invisible
-figsize : tuple
-    The size of the figure to create in inches by default
-layout : tuple, optional
-    Tuple of (rows, columns) for the layout of the histograms
-bins : integer, default 10
-    Number of histogram bins to be used
-kwds : other plotting keyword arguments
-    To be passed to hist function
- -
idxmax(self, axis=0, skipna=True)
Return index of first occurrence of maximum over requested axis.
-NA/null values are excluded.

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    0 or 'index' for row-wise, 1 or 'columns' for column-wise
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be first index.

-Returns
--------
-idxmax : Series

-Notes
------
-This method is the DataFrame version of ``ndarray.argmax``.

-See Also
---------
-Series.idxmax
- -
idxmin(self, axis=0, skipna=True)
Return index of first occurrence of minimum over requested axis.
-NA/null values are excluded.

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    0 or 'index' for row-wise, 1 or 'columns' for column-wise
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-idxmin : Series

-Notes
------
-This method is the DataFrame version of ``ndarray.argmin``.

-See Also
---------
-Series.idxmin
- -
info(self, verbose=None, buf=None, max_cols=None, memory_usage=None, null_counts=None)
Concise summary of a DataFrame.

-Parameters
-----------
-verbose : {None, True, False}, optional
-    Whether to print the full summary.
-    None follows the `display.max_info_columns` setting.
-    True or False overrides the `display.max_info_columns` setting.
-buf : writable buffer, defaults to sys.stdout
-max_cols : int, default None
-    Determines whether full summary or short summary is printed.
-    None follows the `display.max_info_columns` setting.
-memory_usage : boolean/string, default None
-    Specifies whether total memory usage of the DataFrame
-    elements (including index) should be displayed. None follows
-    the `display.memory_usage` setting. True or False overrides
-    the `display.memory_usage` setting. A value of 'deep' is equivalent
-    of True, with deep introspection. Memory usage is shown in
-    human-readable units (base-2 representation).
-null_counts : boolean, default None
-    Whether to show the non-null counts

-    - If None, then only show if the frame is smaller than
-      max_info_rows and max_info_columns.
-    - If True, always show counts.
-    - If False, never show counts.
- -
insert(self, loc, column, value, allow_duplicates=False)
Insert column into DataFrame at specified location.

-If `allow_duplicates` is False, raises Exception if column
-is already contained in the DataFrame.

-Parameters
-----------
-loc : int
-    Must have 0 <= loc <= len(columns)
-column : object
-value : scalar, Series, or array-like
- -
isin(self, values)
Return boolean DataFrame showing whether each element in the
-DataFrame is contained in values.

-Parameters
-----------
-values : iterable, SeriesDataFrame or dictionary
-    The result will only be true at a location if all the
-    labels match. If `values` is a Series, that's the index. If
-    `values` is a dictionary, the keys must be the column names,
-    which must match. If `values` is a DataFrame,
-    then both the index and column labels must match.

-Returns
--------

-DataFrame of booleans

-Examples
---------
-When ``values`` is a list:

->>> df = DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'f']})
->>> df.isin([1, 3, 12, 'a'])
-       A      B
-0   True   True
-1  False  False
-2   True  False

-When ``values`` is a dict:

->>> df = DataFrame({'A': [1, 2, 3], 'B': [1, 4, 7]})
->>> df.isin({'A': [1, 3], 'B': [4, 7, 12]})
-       A      B
-0   True  False  # Note that B didn't match the 1 here.
-1  False   True
-2   True   True

-When ``values`` is a Series or DataFrame:

->>> df = DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'f']})
->>> other = DataFrame({'A': [1, 3, 3, 2], 'B': ['e', 'f', 'f', 'e']})
->>> df.isin(other)
-       A      B
-0   True  False
-1  False  False  # Column A in `other` has a 3, but not at index 1.
-2   True   True
- -
items = iteritems(self)
Iterator over (column name, Series) pairs.

-See also
---------
-iterrows : Iterate over DataFrame rows as (index, Series) pairs.
-itertuples : Iterate over DataFrame rows as namedtuples of the values.
- -
iteritems(self)
Iterator over (column name, Series) pairs.

-See also
---------
-iterrows : Iterate over DataFrame rows as (index, Series) pairs.
-itertuples : Iterate over DataFrame rows as namedtuples of the values.
- -
iterrows(self)
Iterate over DataFrame rows as (index, Series) pairs.

-Notes
------

-1. Because ``iterrows`` returns a Series for each row,
-   it does **not** preserve dtypes across the rows (dtypes are
-   preserved across columns for DataFrames). For example,

-   >>> df = pd.DataFrame([[1, 1.5]], columns=['int', 'float'])
-   >>> row = next(df.iterrows())[1]
-   >>> row
-   int      1.0
-   float    1.5
-   Name: 0, dtype: float64
-   >>> print(row['int'].dtype)
-   float64
-   >>> print(df['int'].dtype)
-   int64

-   To preserve dtypes while iterating over the rows, it is better
-   to use :meth:`itertuples` which returns namedtuples of the values
-   and which is generally faster than ``iterrows``.

-2. You should **never modify** something you are iterating over.
-   This is not guaranteed to work in all cases. Depending on the
-   data types, the iterator returns a copy and not a view, and writing
-   to it will have no effect.

-Returns
--------
-it : generator
-    A generator that iterates over the rows of the frame.

-See also
---------
-itertuples : Iterate over DataFrame rows as namedtuples of the values.
-iteritems : Iterate over (column name, Series) pairs.
- -
itertuples(self, index=True, name='Pandas')
Iterate over DataFrame rows as namedtuples, with index value as first
-element of the tuple.

-Parameters
-----------
-index : boolean, default True
-    If True, return the index as the first element of the tuple.
-name : string, default "Pandas"
-    The name of the returned namedtuples or None to return regular
-    tuples.

-Notes
------
-The column names will be renamed to positional names if they are
-invalid Python identifiers, repeated, or start with an underscore.
-With a large number of columns (>255), regular tuples are returned.

-See also
---------
-iterrows : Iterate over DataFrame rows as (index, Series) pairs.
-iteritems : Iterate over (column name, Series) pairs.

-Examples
---------

->>> df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]},
-                      index=['a', 'b'])
->>> df
-   col1  col2
-a     1   0.1
-b     2   0.2
->>> for row in df.itertuples():
-...     print(row)
-...
-Pandas(Index='a', col1=1, col2=0.10000000000000001)
-Pandas(Index='b', col1=2, col2=0.20000000000000001)
- -
join(self, other, on=None, how='left', lsuffix='', rsuffix='', sort=False)
Join columns with other DataFrame either on index or on a key
-column. Efficiently Join multiple DataFrame objects by index at once by
-passing a list.

-Parameters
-----------
-other : DataFrameSeries with name field set, or list of DataFrame
-    Index should be similar to one of the columns in this one. If a
-    Series is passed, its name attribute must be set, and that will be
-    used as the column name in the resulting joined DataFrame
-on : column name, tuple/list of column names, or array-like
-    Column(s) in the caller to join on the index in other,
-    otherwise joins index-on-index. If multiples
-    columns given, the passed DataFrame must have a MultiIndex. Can
-    pass an array as the join key if not already contained in the
-    calling DataFrame. Like an Excel VLOOKUP operation
-how : {'left', 'right', 'outer', 'inner'}, default: 'left'
-    How to handle the operation of the two objects.

-    * left: use calling frame's index (or column if on is specified)
-    * right: use other frame's index
-    * outer: form union of calling frame's index (or column if on is
-      specified) with other frame's index, and sort it
-      lexicographically
-    * inner: form intersection of calling frame's index (or column if
-      on is specified) with other frame's index, preserving the order
-      of the calling's one
-lsuffix : string
-    Suffix to use from left frame's overlapping columns
-rsuffix : string
-    Suffix to use from right frame's overlapping columns
-sort : boolean, default False
-    Order result DataFrame lexicographically by the join key. If False,
-    the order of the join key depends on the join type (how keyword)

-Notes
------
-on, lsuffix, and rsuffix options are not supported when passing a list
-of DataFrame objects

-Examples
---------
->>> caller = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3', 'K4', 'K5'],
-...                        'A': ['A0', 'A1', 'A2', 'A3', 'A4', 'A5']})

->>> caller
-    A key
-0  A0  K0
-1  A1  K1
-2  A2  K2
-3  A3  K3
-4  A4  K4
-5  A5  K5

->>> other = pd.DataFrame({'key': ['K0', 'K1', 'K2'],
-...                       'B': ['B0', 'B1', 'B2']})

->>> other
-    B key
-0  B0  K0
-1  B1  K1
-2  B2  K2

-Join DataFrames using their indexes.

->>> caller.join(other, lsuffix='_caller', rsuffix='_other')

->>>     A key_caller    B key_other
-    0  A0         K0   B0        K0
-    1  A1         K1   B1        K1
-    2  A2         K2   B2        K2
-    3  A3         K3  NaN       NaN
-    4  A4         K4  NaN       NaN
-    5  A5         K5  NaN       NaN


-If we want to join using the key columns, we need to set key to be
-the index in both caller and other. The joined DataFrame will have
-key as its index.

->>> caller.set_index('key').join(other.set_index('key'))

->>>      A    B
-    key
-    K0   A0   B0
-    K1   A1   B1
-    K2   A2   B2
-    K3   A3  NaN
-    K4   A4  NaN
-    K5   A5  NaN

-Another option to join using the key columns is to use the on
-parameter. DataFrame.join always uses other's index but we can use any
-column in the caller. This method preserves the original caller's
-index in the result.

->>> caller.join(other.set_index('key'), on='key')

->>>     A key    B
-    0  A0  K0   B0
-    1  A1  K1   B1
-    2  A2  K2   B2
-    3  A3  K3  NaN
-    4  A4  K4  NaN
-    5  A5  K5  NaN


-See also
---------
-DataFrame.merge : For column(s)-on-columns(s) operations

-Returns
--------
-joined : DataFrame
- -
kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : Series or DataFrame (if level specified)
- -
kurtosis = kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : Series or DataFrame (if level specified)
- -
last_valid_index(self)
Return label for last non-NA/null value
- -
le(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods le
- -
lookup(self, row_labels, col_labels)
Label-based "fancy indexing" function for DataFrame.
-Given equal-length arrays of row and column labels, return an
-array of the values corresponding to each (row, col) pair.

-Parameters
-----------
-row_labels : sequence
-    The row labels to use for lookup
-col_labels : sequence
-    The column labels to use for lookup

-Notes
------
-Akin to::

-    result = []
-    for row, col in zip(row_labels, col_labels):
-        result.append(df.get_value(row, col))

-Examples
---------
-values : ndarray
-    The found values
- -
lt(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods lt
- -
mad(self, axis=None, skipna=None, level=None)
Return the mean absolute deviation of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mad : Series or DataFrame (if level specified)
- -
max(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the maximum of the values in the object.
-            If you want the *index* of the maximum, use ``idxmax``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmax``.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-max : Series or DataFrame (if level specified)
- -
mean(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the mean of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mean : Series or DataFrame (if level specified)
- -
median(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the median of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-median : Series or DataFrame (if level specified)
- -
melt(self, id_vars=None, value_vars=None, var_name=None, value_name='value', col_level=None)
"Unpivots" a DataFrame from wide format to long format, optionally
-leaving identifier variables set.

-This function is useful to massage a DataFrame into a format where one
-or more columns are identifier variables (`id_vars`), while all other
-columns, considered measured variables (`value_vars`), are "unpivoted" to
-the row axis, leaving just two non-identifier columns, 'variable' and
-'value'.

-.. versionadded:: 0.20.0

-Parameters
-----------
-frame : DataFrame
-id_vars : tuple, list, or ndarray, optional
-    Column(s) to use as identifier variables.
-value_vars : tuple, list, or ndarray, optional
-    Column(s) to unpivot. If not specified, uses all columns that
-    are not set as `id_vars`.
-var_name : scalar
-    Name to use for the 'variable' column. If None it uses
-    ``frame.columns.name`` or 'variable'.
-value_name : scalar, default 'value'
-    Name to use for the 'value' column.
-col_level : int or string, optional
-    If columns are a MultiIndex then use this level to melt.

-See also
---------
-melt
-pivot_table
-DataFrame.pivot

-Examples
---------
->>> import pandas as pd
->>> df = pd.DataFrame({'A': {0: 'a', 1: 'b', 2: 'c'},
-...                    'B': {0: 1, 1: 3, 2: 5},
-...                    'C': {0: 2, 1: 4, 2: 6}})
->>> df
-   A  B  C
-0  a  1  2
-1  b  3  4
-2  c  5  6

->>> df.melt(id_vars=['A'], value_vars=['B'])
-   A variable  value
-0  a        B      1
-1  b        B      3
-2  c        B      5

->>> df.melt(id_vars=['A'], value_vars=['B', 'C'])
-   A variable  value
-0  a        B      1
-1  b        B      3
-2  c        B      5
-3  a        C      2
-4  b        C      4
-5  c        C      6

-The names of 'variable' and 'value' columns can be customized:

->>> df.melt(id_vars=['A'], value_vars=['B'],
-...         var_name='myVarname', value_name='myValname')
-   A myVarname  myValname
-0  a         B          1
-1  b         B          3
-2  c         B          5

-If you have multi-index columns:

->>> df.columns = [list('ABC'), list('DEF')]
->>> df
-   A  B  C
-   D  E  F
-0  a  1  2
-1  b  3  4
-2  c  5  6

->>> df.melt(col_level=0, id_vars=['A'], value_vars=['B'])
-   A variable  value
-0  a        B      1
-1  b        B      3
-2  c        B      5

->>> df.melt(id_vars=[('A', 'D')], value_vars=[('B', 'E')])
-  (A, D) variable_0 variable_1  value
-0      a          B          E      1
-1      b          B          E      3
-2      c          B          E      5
- -
memory_usage(self, index=True, deep=False)
Memory usage of DataFrame columns.

-Parameters
-----------
-index : bool
-    Specifies whether to include memory usage of DataFrame's
-    index in returned Series. If `index=True` (default is False)
-    the first index of the Series is `Index`.
-deep : bool
-    Introspect the data deeply, interrogate
-    `object` dtypes for system-level memory consumption

-Returns
--------
-sizes : Series
-    A series with column names as index and memory usage of
-    columns with units of bytes.

-Notes
------
-Memory usage does not include memory consumed by elements that
-are not components of the array if deep=False

-See Also
---------
-numpy.ndarray.nbytes
- -
merge(self, right, how='inner', on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=False, suffixes=('_x', '_y'), copy=True, indicator=False)
Merge DataFrame objects by performing a database-style join operation by
-columns or indexes.

-If joining columns on columns, the DataFrame indexes *will be
-ignored*. Otherwise if joining indexes on indexes or indexes on a column or
-columns, the index will be passed on.

-Parameters
-----------
-right : DataFrame
-how : {'left', 'right', 'outer', 'inner'}, default 'inner'
-    * left: use only keys from left frame, similar to a SQL left outer join;
-      preserve key order
-    * right: use only keys from right frame, similar to a SQL right outer join;
-      preserve key order
-    * outer: use union of keys from both frames, similar to a SQL full outer
-      join; sort keys lexicographically
-    * inner: use intersection of keys from both frames, similar to a SQL inner
-      join; preserve the order of the left keys
-on : label or list
-    Field names to join on. Must be found in both DataFrames. If on is
-    None and not merging on indexes, then it merges on the intersection of
-    the columns by default.
-left_on : label or list, or array-like
-    Field names to join on in left DataFrame. Can be a vector or list of
-    vectors of the length of the DataFrame to use a particular vector as
-    the join key instead of columns
-right_on : label or list, or array-like
-    Field names to join on in right DataFrame or vector/list of vectors per
-    left_on docs
-left_index : boolean, default False
-    Use the index from the left DataFrame as the join key(s). If it is a
-    MultiIndex, the number of keys in the other DataFrame (either the index
-    or a number of columns) must match the number of levels
-right_index : boolean, default False
-    Use the index from the right DataFrame as the join key. Same caveats as
-    left_index
-sort : boolean, default False
-    Sort the join keys lexicographically in the result DataFrame. If False,
-    the order of the join keys depends on the join type (how keyword)
-suffixes : 2-length sequence (tuple, list, ...)
-    Suffix to apply to overlapping column names in the left and right
-    side, respectively
-copy : boolean, default True
-    If False, do not copy data unnecessarily
-indicator : boolean or string, default False
-    If True, adds a column to output DataFrame called "_merge" with
-    information on the source of each row.
-    If string, column with information on source of each row will be added to
-    output DataFrame, and column will be named value of string.
-    Information column is Categorical-type and takes on a value of "left_only"
-    for observations whose merge key only appears in 'left' DataFrame,
-    "right_only" for observations whose merge key only appears in 'right'
-    DataFrame, and "both" if the observation's merge key is found in both.

-    .. versionadded:: 0.17.0

-Examples
---------

->>> A              >>> B
-    lkey value         rkey value
-0   foo  1         0   foo  5
-1   bar  2         1   bar  6
-2   baz  3         2   qux  7
-3   foo  4         3   bar  8

->>> A.merge(B, left_on='lkey', right_on='rkey', how='outer')
-   lkey  value_x  rkey  value_y
-0  foo   1        foo   5
-1  foo   4        foo   5
-2  bar   2        bar   6
-3  bar   2        bar   8
-4  baz   3        NaN   NaN
-5  NaN   NaN      qux   7

-Returns
--------
-merged : DataFrame
-    The output type will the be same as 'left', if it is a subclass
-    of DataFrame.

-See also
---------
-merge_ordered
-merge_asof
- -
min(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the minimum of the values in the object.
-            If you want the *index* of the minimum, use ``idxmin``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmin``.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-min : Series or DataFrame (if level specified)
- -
mod(self, other, axis='columns', level=None, fill_value=None)
Modulo of dataframe and other, element-wise (binary operator `mod`).

-Equivalent to ``dataframe % other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rmod
- -
mode(self, axis=0, numeric_only=False)
Gets the mode(s) of each element along the axis selected. Adds a row
-for each mode per label, fills in gaps with nan.

-Note that there could be multiple values returned for the selected
-axis (when more than one item share the maximum frequency), which is
-the reason why a dataframe is returned. If you want to impute missing
-values with the mode in a dataframe ``df``, you can just do this:
-``df.fillna(df.mode().iloc[0])``

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    * 0 or 'index' : get mode of each column
-    * 1 or 'columns' : get mode of each row
-numeric_only : boolean, default False
-    if True, only apply to numeric columns

-Returns
--------
-modes : DataFrame (sorted)

-Examples
---------
->>> df = pd.DataFrame({'A': [1, 2, 1, 2, 1, 2, 3]})
->>> df.mode()
-   A
-0  1
-1  2
- -
mul(self, other, axis='columns', level=None, fill_value=None)
Multiplication of dataframe and other, element-wise (binary operator `mul`).

-Equivalent to ``dataframe * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rmul
- -
multiply = mul(self, other, axis='columns', level=None, fill_value=None)
Multiplication of dataframe and other, element-wise (binary operator `mul`).

-Equivalent to ``dataframe * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rmul
- -
ne(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods ne
- -
nlargest(self, n, columns, keep='first')
Get the rows of a DataFrame sorted by the `n` largest
-values of `columns`.

-.. versionadded:: 0.17.0

-Parameters
-----------
-n : int
-    Number of items to retrieve
-columns : list or str
-    Column name or names to order by
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-DataFrame

-Examples
---------
->>> df = DataFrame({'a': [1, 10, 8, 11, -1],
-...                 'b': list('abdce'),
-...                 'c': [1.0, 2.0, np.nan, 3.0, 4.0]})
->>> df.nlargest(3, 'a')
-    a  b   c
-3  11  c   3
-1  10  b   2
-2   8  d NaN
- -
nsmallest(self, n, columns, keep='first')
Get the rows of a DataFrame sorted by the `n` smallest
-values of `columns`.

-.. versionadded:: 0.17.0

-Parameters
-----------
-n : int
-    Number of items to retrieve
-columns : list or str
-    Column name or names to order by
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-DataFrame

-Examples
---------
->>> df = DataFrame({'a': [1, 10, 8, 11, -1],
-...                 'b': list('abdce'),
-...                 'c': [1.0, 2.0, np.nan, 3.0, 4.0]})
->>> df.nsmallest(3, 'a')
-   a  b   c
-4 -1  e   4
-0  1  a   1
-2  8  d NaN
- -
nunique(self, axis=0, dropna=True)
Return Series with number of distinct observations over requested
-axis.

-.. versionadded:: 0.20.0

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-dropna : boolean, default True
-    Don't include NaN in the counts.

-Returns
--------
-nunique : Series

-Examples
---------
->>> df = pd.DataFrame({'A': [1, 2, 3], 'B': [1, 1, 1]})
->>> df.nunique()
-A    3
-B    1

->>> df.nunique(axis=1)
-0    1
-1    2
-2    2
- -
pivot(self, index=None, columns=None, values=None)
Reshape data (produce a "pivot" table) based on column values. Uses
-unique values from index / columns to form axes of the resulting
-DataFrame.

-Parameters
-----------
-index : string or object, optional
-    Column name to use to make new frame's index. If None, uses
-    existing index.
-columns : string or object
-    Column name to use to make new frame's columns
-values : string or object, optional
-    Column name to use for populating new frame's values. If not
-    specified, all remaining columns will be used and the result will
-    have hierarchically indexed columns

-Returns
--------
-pivoted : DataFrame

-See also
---------
-DataFrame.pivot_table : generalization of pivot that can handle
-    duplicate values for one index/column pair
-DataFrame.unstack : pivot based on the index values instead of a
-    column

-Notes
------
-For finer-tuned control, see hierarchical indexing documentation along
-with the related stack/unstack methods

-Examples
---------

->>> df = pd.DataFrame({'foo': ['one','one','one','two','two','two'],
-                       'bar': ['A', 'B', 'C', 'A', 'B', 'C'],
-                       'baz': [1, 2, 3, 4, 5, 6]})
->>> df
-    foo   bar  baz
-0   one   A    1
-1   one   B    2
-2   one   C    3
-3   two   A    4
-4   two   B    5
-5   two   C    6

->>> df.pivot(index='foo', columns='bar', values='baz')
-     A   B   C
-one  1   2   3
-two  4   5   6

->>> df.pivot(index='foo', columns='bar')['baz']
-     A   B   C
-one  1   2   3
-two  4   5   6
- -
pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All')
Create a spreadsheet-style pivot table as a DataFrame. The levels in the
-pivot table will be stored in MultiIndex objects (hierarchical indexes) on
-the index and columns of the result DataFrame

-Parameters
-----------
-data : DataFrame
-values : column to aggregate, optional
-index : column, Grouper, array, or list of the previous
-    If an array is passed, it must be the same length as the data. The list
-    can contain any of the other types (except list).
-    Keys to group by on the pivot table index.  If an array is passed, it
-    is being used as the same manner as column values.
-columns : column, Grouper, array, or list of the previous
-    If an array is passed, it must be the same length as the data. The list
-    can contain any of the other types (except list).
-    Keys to group by on the pivot table column.  If an array is passed, it
-    is being used as the same manner as column values.
-aggfunc : function or list of functions, default numpy.mean
-    If list of functions passed, the resulting pivot table will have
-    hierarchical columns whose top level are the function names (inferred
-    from the function objects themselves)
-fill_value : scalar, default None
-    Value to replace missing values with
-margins : boolean, default False
-    Add all row / columns (e.g. for subtotal / grand totals)
-dropna : boolean, default True
-    Do not include columns whose entries are all NaN
-margins_name : string, default 'All'
-    Name of the row / column that will contain the totals
-    when margins is True.

-Examples
---------
->>> df
-   A   B   C      D
-0  foo one small  1
-1  foo one large  2
-2  foo one large  2
-3  foo two small  3
-4  foo two small  3
-5  bar one large  4
-6  bar one small  5
-7  bar two small  6
-8  bar two large  7

->>> table = pivot_table(df, values='D', index=['A', 'B'],
-...                     columns=['C'], aggfunc=np.sum)
->>> table
-          small  large
-foo  one  1      4
-     two  6      NaN
-bar  one  5      4
-     two  6      7

-Returns
--------
-table : DataFrame

-See also
---------
-DataFrame.pivot : pivot without aggregation that can handle
-    non-numeric data
- -
pow(self, other, axis='columns', level=None, fill_value=None)
Exponential power of dataframe and other, element-wise (binary operator `pow`).

-Equivalent to ``dataframe ** other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rpow
- -
prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : Series or DataFrame (if level specified)
- -
product = prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : Series or DataFrame (if level specified)
- -
quantile(self, q=0.5, axis=0, numeric_only=True, interpolation='linear')
Return values at the given quantile over requested axis, a la
-numpy.percentile.

-Parameters
-----------
-q : float or array-like, default 0.5 (50% quantile)
-    0 <= q <= 1, the quantile(s) to compute
-axis : {0, 1, 'index', 'columns'} (default 0)
-    0 or 'index' for row-wise, 1 or 'columns' for column-wise
-interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'}
-    .. versionadded:: 0.18.0

-    This optional parameter specifies the interpolation method to use,
-    when the desired quantile lies between two data points `i` and `j`:

-    * linear: `i + (j - i) * fraction`, where `fraction` is the
-      fractional part of the index surrounded by `i` and `j`.
-    * lower: `i`.
-    * higher: `j`.
-    * nearest: `i` or `j` whichever is nearest.
-    * midpoint: (`i` + `j`) / 2.

-Returns
--------
-quantiles : Series or DataFrame

-    - If ``q`` is an array, a DataFrame will be returned where the
-      index is ``q``, the columns are the columns of self, and the
-      values are the quantiles.
-    - If ``q`` is a float, a Series will be returned where the
-      index is the columns of self and the values are the quantiles.

-Examples
---------

->>> df = DataFrame(np.array([[1, 1], [2, 10], [3, 100], [4, 100]]),
-                   columns=['a', 'b'])
->>> df.quantile(.1)
-a    1.3
-b    3.7
-dtype: float64
->>> df.quantile([.1, .5])
-       a     b
-0.1  1.3   3.7
-0.5  2.5  55.0
- -
query(self, expr, inplace=False, **kwargs)
Query the columns of a frame with a boolean expression.

-.. versionadded:: 0.13

-Parameters
-----------
-expr : string
-    The query string to evaluate.  You can refer to variables
-    in the environment by prefixing them with an '@' character like
-    ``@a + b``.
-inplace : bool
-    Whether the query should modify the data in place or return
-    a modified copy

-    .. versionadded:: 0.18.0

-kwargs : dict
-    See the documentation for :func:`pandas.eval` for complete details
-    on the keyword arguments accepted by :meth:`DataFrame.query`.

-Returns
--------
-q : DataFrame

-Notes
------
-The result of the evaluation of this expression is first passed to
-:attr:`DataFrame.loc` and if that fails because of a
-multidimensional key (e.g., a DataFrame) then the result will be passed
-to :meth:`DataFrame.__getitem__`.

-This method uses the top-level :func:`pandas.eval` function to
-evaluate the passed query.

-The :meth:`~pandas.DataFrame.query` method uses a slightly
-modified Python syntax by default. For example, the ``&`` and ``|``
-(bitwise) operators have the precedence of their boolean cousins,
-:keyword:`and` and :keyword:`or`. This *is* syntactically valid Python,
-however the semantics are different.

-You can change the semantics of the expression by passing the keyword
-argument ``parser='python'``. This enforces the same semantics as
-evaluation in Python space. Likewise, you can pass ``engine='python'``
-to evaluate an expression using Python itself as a backend. This is not
-recommended as it is inefficient compared to using ``numexpr`` as the
-engine.

-The :attr:`DataFrame.index` and
-:attr:`DataFrame.columns` attributes of the
-:class:`~pandas.DataFrame` instance are placed in the query namespace
-by default, which allows you to treat both the index and columns of the
-frame as a column in the frame.
-The identifier ``index`` is used for the frame index; you can also
-use the name of the index to identify it in a query.

-For further details and examples see the ``query`` documentation in
-:ref:`indexing <indexing.query>`.

-See Also
---------
-pandas.eval
-DataFrame.eval

-Examples
---------
->>> from numpy.random import randn
->>> from pandas import DataFrame
->>> df = DataFrame(randn(10, 2), columns=list('ab'))
->>> df.query('a > b')
->>> df[df.a > df.b]  # same result as the previous expression
- -
radd(self, other, axis='columns', level=None, fill_value=None)
Addition of dataframe and other, element-wise (binary operator `radd`).

-Equivalent to ``other + dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.add
- -
rdiv = rtruediv(self, other, axis='columns', level=None, fill_value=None)
Floating division of dataframe and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.truediv
- -
reindex(self, index=None, columns=None, **kwargs)
Conform DataFrame to new index with optional filling logic, placing
-NA/NaN in locations having no value in the previous index. A new object
-is produced unless the new index is equivalent to the current one and
-copy=False

-Parameters
-----------
-index, columns : array-like, optional (can be specified in order, or as
-    keywords)
-    New labels / index to conform to. Preferably an Index object to
-    avoid duplicating data
-method : {None, 'backfill'/'bfill', 'pad'/'ffill', 'nearest'}, optional
-    method to use for filling holes in reindexed DataFrame.
-    Please note: this is only  applicable to DataFrames/Series with a
-    monotonically increasing/decreasing index.

-    * default: don't fill gaps
-    * pad / ffill: propagate last valid observation forward to next
-      valid
-    * backfill / bfill: use next valid observation to fill gap
-    * nearest: use nearest valid observations to fill gap

-copy : boolean, default True
-    Return a new object, even if the passed indexes are the same
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-limit : int, default None
-    Maximum number of consecutive elements to forward or backward fill
-tolerance : optional
-    Maximum distance between original and new labels for inexact
-    matches. The values of the index at the matching locations most
-    satisfy the equation ``abs(index[indexer] - target) <= tolerance``.

-    .. versionadded:: 0.17.0

-Examples
---------

-Create a dataframe with some fictional data.

->>> index = ['Firefox', 'Chrome', 'Safari', 'IE10', 'Konqueror']
->>> df = pd.DataFrame({
-...      'http_status': [200,200,404,404,301],
-...      'response_time': [0.04, 0.02, 0.07, 0.08, 1.0]},
-...       index=index)
->>> df
-           http_status  response_time
-Firefox            200           0.04
-Chrome             200           0.02
-Safari             404           0.07
-IE10               404           0.08
-Konqueror          301           1.00

-Create a new index and reindex the dataframe. By default
-values in the new index that do not have corresponding
-records in the dataframe are assigned ``NaN``.

->>> new_index= ['Safari', 'Iceweasel', 'Comodo Dragon', 'IE10',
-...             'Chrome']
->>> df.reindex(new_index)
-               http_status  response_time
-Safari               404.0           0.07
-Iceweasel              NaN            NaN
-Comodo Dragon          NaN            NaN
-IE10                 404.0           0.08
-Chrome               200.0           0.02

-We can fill in the missing values by passing a value to
-the keyword ``fill_value``. Because the index is not monotonically
-increasing or decreasing, we cannot use arguments to the keyword
-``method`` to fill the ``NaN`` values.

->>> df.reindex(new_index, fill_value=0)
-               http_status  response_time
-Safari                 404           0.07
-Iceweasel                0           0.00
-Comodo Dragon            0           0.00
-IE10                   404           0.08
-Chrome                 200           0.02

->>> df.reindex(new_index, fill_value='missing')
-              http_status response_time
-Safari                404          0.07
-Iceweasel         missing       missing
-Comodo Dragon     missing       missing
-IE10                  404          0.08
-Chrome                200          0.02

-To further illustrate the filling functionality in
-``reindex``, we will create a dataframe with a
-monotonically increasing index (for example, a sequence
-of dates).

->>> date_index = pd.date_range('1/1/2010', periods=6, freq='D')
->>> df2 = pd.DataFrame({"prices": [100, 101, np.nan, 100, 89, 88]},
-...                    index=date_index)
->>> df2
-            prices
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88

-Suppose we decide to expand the dataframe to cover a wider
-date range.

->>> date_index2 = pd.date_range('12/29/2009', periods=10, freq='D')
->>> df2.reindex(date_index2)
-            prices
-2009-12-29     NaN
-2009-12-30     NaN
-2009-12-31     NaN
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-The index entries that did not have a value in the original data frame
-(for example, '2009-12-29') are by default filled with ``NaN``.
-If desired, we can fill in the missing values using one of several
-options.

-For example, to backpropagate the last valid value to fill the ``NaN``
-values, pass ``bfill`` as an argument to the ``method`` keyword.

->>> df2.reindex(date_index2, method='bfill')
-            prices
-2009-12-29     100
-2009-12-30     100
-2009-12-31     100
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-Please note that the ``NaN`` value present in the original dataframe
-(at index value 2010-01-03) will not be filled by any of the
-value propagation schemes. This is because filling while reindexing
-does not look at dataframe values, but only compares the original and
-desired indexes. If you do want to fill in the ``NaN`` values present
-in the original dataframe, use the ``fillna()`` method.

-Returns
--------
-reindexed : DataFrame
- -
reindex_axis(self, labels, axis=0, method=None, level=None, copy=True, limit=None, fill_value=nan)
Conform input object to new index with optional
-filling logic, placing NA/NaN in locations having no value in the
-previous index. A new object is produced unless the new index is
-equivalent to the current one and copy=False

-Parameters
-----------
-labels : array-like
-    New labels / index to conform to. Preferably an Index object to
-    avoid duplicating data
-axis : {0 or 'index', 1 or 'columns'}
-method : {None, 'backfill'/'bfill', 'pad'/'ffill', 'nearest'}, optional
-    Method to use for filling holes in reindexed DataFrame:

-    * default: don't fill gaps
-    * pad / ffill: propagate last valid observation forward to next
-      valid
-    * backfill / bfill: use next valid observation to fill gap
-    * nearest: use nearest valid observations to fill gap

-copy : boolean, default True
-    Return a new object, even if the passed indexes are the same
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-limit : int, default None
-    Maximum number of consecutive elements to forward or backward fill
-tolerance : optional
-    Maximum distance between original and new labels for inexact
-    matches. The values of the index at the matching locations most
-    satisfy the equation ``abs(index[indexer] - target) <= tolerance``.

-    .. versionadded:: 0.17.0

-Examples
---------
->>> df.reindex_axis(['A', 'B', 'C'], axis=1)

-See Also
---------
-reindex, reindex_like

-Returns
--------
-reindexed : DataFrame
- -
rename(self, index=None, columns=None, **kwargs)
Alter axes input function or functions. Function / dict values must be
-unique (1-to-1). Labels not contained in a dict / Series will be left
-as-is. Extra labels listed don't throw an error. Alternatively, change
-``Series.name`` with a scalar value (Series only).

-Parameters
-----------
-index, columns : scalar, list-like, dict-like or function, optional
-    Scalar or list-like will alter the ``Series.name`` attribute,
-    and raise on DataFrame or Panel.
-    dict-like or functions are transformations to apply to
-    that axis' values
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False
-    Whether to return a new DataFrame. If True then value of copy is
-    ignored.
-level : int or level name, default None
-    In case of a MultiIndex, only rename labels in the specified
-    level.

-Returns
--------
-renamed : DataFrame (new object)

-See Also
---------
-pandas.NDFrame.rename_axis

-Examples
---------
->>> s = pd.Series([1, 2, 3])
->>> s
-0    1
-1    2
-2    3
-dtype: int64
->>> s.rename("my_name") # scalar, changes Series.name
-0    1
-1    2
-2    3
-Name: my_name, dtype: int64
->>> s.rename(lambda x: x ** 2)  # function, changes labels
-0    1
-1    2
-4    3
-dtype: int64
->>> s.rename({1: 3, 2: 5})  # mapping, changes labels
-0    1
-3    2
-5    3
-dtype: int64
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename(2)
-Traceback (most recent call last):
-...
-TypeError: 'int' object is not callable
->>> df.rename(index=str, columns={"A": "a", "B": "c"})
-   a  c
-0  1  4
-1  2  5
-2  3  6
->>> df.rename(index=str, columns={"A": "a", "C": "c"})
-   a  B
-0  1  4
-1  2  5
-2  3  6
- -
reorder_levels(self, order, axis=0)
Rearrange index levels using input order.
-May not drop or duplicate levels

-Parameters
-----------
-order : list of int or list of str
-    List representing new level order. Reference level by number
-    (position) or by key (label).
-axis : int
-    Where to reorder levels.

-Returns
--------
-type of caller (new object)
- -
reset_index(self, level=None, drop=False, inplace=False, col_level=0, col_fill='')
For DataFrame with multi-level index, return new DataFrame with
-labeling information in the columns under the index names, defaulting
-to 'level_0', 'level_1', etc. if any are None. For a standard index,
-the index name will be used (if set), otherwise a default 'index' or
-'level_0' (if 'index' is already taken) will be used.

-Parameters
-----------
-level : int, str, tuple, or list, default None
-    Only remove the given levels from the index. Removes all levels by
-    default
-drop : boolean, default False
-    Do not try to insert index into dataframe columns. This resets
-    the index to the default integer index.
-inplace : boolean, default False
-    Modify the DataFrame in place (do not create a new object)
-col_level : int or str, default 0
-    If the columns have multiple levels, determines which level the
-    labels are inserted into. By default it is inserted into the first
-    level.
-col_fill : object, default ''
-    If the columns have multiple levels, determines how the other
-    levels are named. If None then the index name is repeated.

-Returns
--------
-resetted : DataFrame
- -
rfloordiv(self, other, axis='columns', level=None, fill_value=None)
Integer division of dataframe and other, element-wise (binary operator `rfloordiv`).

-Equivalent to ``other // dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.floordiv
- -
rmod(self, other, axis='columns', level=None, fill_value=None)
Modulo of dataframe and other, element-wise (binary operator `rmod`).

-Equivalent to ``other % dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.mod
- -
rmul(self, other, axis='columns', level=None, fill_value=None)
Multiplication of dataframe and other, element-wise (binary operator `rmul`).

-Equivalent to ``other * dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.mul
- -
rolling(self, window, min_periods=None, freq=None, center=False, win_type=None, on=None, axis=0, closed=None)
Provides rolling window calculcations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-window : int, or offset
-    Size of the moving window. This is the number of observations used for
-    calculating the statistic. Each window will be a fixed size.

-    If its an offset then this will be the time period of each window. Each
-    window will be a variable sized based on the observations included in
-    the time-period. This is only valid for datetimelike indexes. This is
-    new in 0.19.0
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA). For a window that is specified by an offset,
-    this will default to 1.
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-win_type : string, default None
-    Provide a window type. See the notes below.
-on : string, optional
-    For a DataFrame, column on which to calculate
-    the rolling window, rather than the index
-closed : string, default None
-    Make the interval closed on the 'right', 'left', 'both' or
-    'neither' endpoints.
-    For offset-based windows, it defaults to 'right'.
-    For fixed windows, defaults to 'both'. Remaining cases not implemented
-    for fixed windows.

-    .. versionadded:: 0.20.0

-axis : int or string, default 0

-Returns
--------
-a Window or Rolling sub-classed for the particular operation

-Examples
---------

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]})
->>> df
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

-Rolling sum with a window length of 2, using the 'triang'
-window type.

->>> df.rolling(2, win_type='triang').sum()
-     B
-0  NaN
-1  1.0
-2  2.5
-3  NaN
-4  NaN

-Rolling sum with a window length of 2, min_periods defaults
-to the window length.

->>> df.rolling(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  NaN
-4  NaN

-Same as above, but explicity set the min_periods

->>> df.rolling(2, min_periods=1).sum()
-     B
-0  0.0
-1  1.0
-2  3.0
-3  2.0
-4  4.0

-A ragged (meaning not-a-regular frequency), time-indexed DataFrame

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]},
-....:                 index = [pd.Timestamp('20130101 09:00:00'),
-....:                          pd.Timestamp('20130101 09:00:02'),
-....:                          pd.Timestamp('20130101 09:00:03'),
-....:                          pd.Timestamp('20130101 09:00:05'),
-....:                          pd.Timestamp('20130101 09:00:06')])

->>> df
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  2.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0


-Contrasting to an integer rolling window, this will roll a variable
-length window corresponding to the time period.
-The default for min_periods is 1.

->>> df.rolling('2s').sum()
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  3.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-To learn more about the offsets & frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-The recognized win_types are:

-* ``boxcar``
-* ``triang``
-* ``blackman``
-* ``hamming``
-* ``bartlett``
-* ``parzen``
-* ``bohman``
-* ``blackmanharris``
-* ``nuttall``
-* ``barthann``
-* ``kaiser`` (needs beta)
-* ``gaussian`` (needs std)
-* ``general_gaussian`` (needs power, width)
-* ``slepian`` (needs width).
- -
round(self, decimals=0, *args, **kwargs)
Round a DataFrame to a variable number of decimal places.

-.. versionadded:: 0.17.0

-Parameters
-----------
-decimals : int, dict, Series
-    Number of decimal places to round each column to. If an int is
-    given, round each column to the same number of places.
-    Otherwise dict and Series round to variable numbers of places.
-    Column names should be in the keys if `decimals` is a
-    dict-like, or in the index if `decimals` is a Series. Any
-    columns not included in `decimals` will be left as is. Elements
-    of `decimals` which are not columns of the input will be
-    ignored.

-Examples
---------
->>> df = pd.DataFrame(np.random.random([3, 3]),
-...     columns=['A', 'B', 'C'], index=['first', 'second', 'third'])
->>> df
-               A         B         C
-first   0.028208  0.992815  0.173891
-second  0.038683  0.645646  0.577595
-third   0.877076  0.149370  0.491027
->>> df.round(2)
-           A     B     C
-first   0.03  0.99  0.17
-second  0.04  0.65  0.58
-third   0.88  0.15  0.49
->>> df.round({'A': 1, 'C': 2})
-          A         B     C
-first   0.0  0.992815  0.17
-second  0.0  0.645646  0.58
-third   0.9  0.149370  0.49
->>> decimals = pd.Series([1, 0, 2], index=['A', 'B', 'C'])
->>> df.round(decimals)
-          A  B     C
-first   0.0  1  0.17
-second  0.0  1  0.58
-third   0.9  0  0.49

-Returns
--------
-DataFrame object

-See Also
---------
-numpy.around
-Series.round
- -
rpow(self, other, axis='columns', level=None, fill_value=None)
Exponential power of dataframe and other, element-wise (binary operator `rpow`).

-Equivalent to ``other ** dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.pow
- -
rsub(self, other, axis='columns', level=None, fill_value=None)
Subtraction of dataframe and other, element-wise (binary operator `rsub`).

-Equivalent to ``other - dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.sub
- -
rtruediv(self, other, axis='columns', level=None, fill_value=None)
Floating division of dataframe and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.truediv
- -
select_dtypes(self, include=None, exclude=None)
Return a subset of a DataFrame including/excluding columns based on
-their ``dtype``.

-Parameters
-----------
-include, exclude : list-like
-    A list of dtypes or strings to be included/excluded. You must pass
-    in a non-empty sequence for at least one of these.

-Raises
-------
-ValueError
-    * If both of ``include`` and ``exclude`` are empty
-    * If ``include`` and ``exclude`` have overlapping elements
-    * If any kind of string dtype is passed in.
-TypeError
-    * If either of ``include`` or ``exclude`` is not a sequence

-Returns
--------
-subset : DataFrame
-    The subset of the frame including the dtypes in ``include`` and
-    excluding the dtypes in ``exclude``.

-Notes
------
-* To select all *numeric* types use the numpy dtype ``numpy.number``
-* To select strings you must use the ``object`` dtype, but note that
-  this will return *all* object dtype columns
-* See the `numpy dtype hierarchy
-  <http://docs.scipy.org/doc/numpy/reference/arrays.scalars.html>`__
-* To select datetimes, use np.datetime64, 'datetime' or 'datetime64'
-* To select timedeltas, use np.timedelta64, 'timedelta' or
-  'timedelta64'
-* To select Pandas categorical dtypes, use 'category'
-* To select Pandas datetimetz dtypes, use 'datetimetz' (new in 0.20.0),
-  or a 'datetime64[ns, tz]' string

-Examples
---------
->>> df = pd.DataFrame({'a': np.random.randn(6).astype('f4'),
-...                    'b': [True, False] * 3,
-...                    'c': [1.0, 2.0] * 3})
->>> df
-        a      b  c
-0  0.3962   True  1
-1  0.1459  False  2
-2  0.2623   True  1
-3  0.0764  False  2
-4 -0.9703   True  1
-5 -1.2094  False  2
->>> df.select_dtypes(include=['float64'])
-   c
-0  1
-1  2
-2  1
-3  2
-4  1
-5  2
->>> df.select_dtypes(exclude=['floating'])
-       b
-0   True
-1  False
-2   True
-3  False
-4   True
-5  False
- -
sem(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased standard error of the mean over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sem : Series or DataFrame (if level specified)
- -
set_index(self, keys, drop=True, append=False, inplace=False, verify_integrity=False)
Set the DataFrame index (row labels) using one or more existing
-columns. By default yields a new object.

-Parameters
-----------
-keys : column label or list of column labels / arrays
-drop : boolean, default True
-    Delete columns to be used as the new index
-append : boolean, default False
-    Whether to append columns to existing index
-inplace : boolean, default False
-    Modify the DataFrame in place (do not create a new object)
-verify_integrity : boolean, default False
-    Check the new index for duplicates. Otherwise defer the check until
-    necessary. Setting to False will improve the performance of this
-    method

-Examples
---------
->>> indexed_df = df.set_index(['A', 'B'])
->>> indexed_df2 = df.set_index(['A', [0, 1, 2, 0, 1, 2]])
->>> indexed_df3 = df.set_index([[0, 1, 2, 0, 1, 2]])

-Returns
--------
-dataframe : DataFrame
- -
set_value(self, index, col, value, takeable=False)
Put single value at passed column and index

-Parameters
-----------
-index : row label
-col : column label
-value : scalar value
-takeable : interpret the index/col as indexers, default False

-Returns
--------
-frame : DataFrame
-    If label pair is contained, will be reference to calling DataFrame,
-    otherwise a new object
- -
shift(self, periods=1, freq=None, axis=0)
Shift index by desired number of periods with an optional time freq

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, optional
-    Increment to use from the tseries module or time rule (e.g. 'EOM').
-    See Notes.
-axis : {0 or 'index', 1 or 'columns'}

-Notes
------
-If freq is specified then the index values are shifted but the data
-is not realigned. That is, use freq if you would like to extend the
-index when shifting and preserve the original data.

-Returns
--------
-shifted : DataFrame
- -
skew(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased skew over requested axis
-Normalized by N-1

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-skew : Series or DataFrame (if level specified)
- -
sort_index(self, axis=0, level=None, ascending=True, inplace=False, kind='quicksort', na_position='last', sort_remaining=True, by=None)
Sort object by labels (along an axis)

-Parameters
-----------
-axis : index, columns to direct sorting
-level : int or level name or list of ints or list of level names
-    if not None, sort on values in specified index level(s)
-ascending : boolean, default True
-    Sort ascending vs. descending
-inplace : bool, default False
-    if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end.
-     Not implemented for MultiIndex.
-sort_remaining : bool, default True
-    if true and sorting by level and index is multilevel, sort by other
-    levels too (in order) after sorting by specified level

-Returns
--------
-sorted_obj : DataFrame
- -
sort_values(self, by, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last')
Sort by the values along either axis

-.. versionadded:: 0.17.0

-Parameters
-----------
-by : str or list of str
-    Name or list of names which refer to the axis items.
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    Axis to direct sorting
-ascending : bool or list of bool, default True
-     Sort ascending vs. descending. Specify list for multiple sort
-     orders.  If this is a list of bools, must match the length of
-     the by.
-inplace : bool, default False
-     if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end

-Returns
--------
-sorted_obj : DataFrame
- -
sortlevel(self, level=0, axis=0, ascending=True, inplace=False, sort_remaining=True)
DEPRECATED: use :meth:`DataFrame.sort_index`

-Sort multilevel index by chosen axis and primary level. Data will be
-lexicographically sorted by the chosen level followed by the other
-levels (in order)

-Parameters
-----------
-level : int
-axis : {0 or 'index', 1 or 'columns'}, default 0
-ascending : boolean, default True
-inplace : boolean, default False
-    Sort the DataFrame without creating a new instance
-sort_remaining : boolean, default True
-    Sort by the other levels too.

-Returns
--------
-sorted : DataFrame

-See Also
---------
-DataFrame.sort_index(level=...)
- -
stack(self, level=-1, dropna=True)
Pivot a level of the (possibly hierarchical) column labels, returning a
-DataFrame (or Series in the case of an object with a single level of
-column labels) having a hierarchical index with a new inner-most level
-of row labels.
-The level involved will automatically get sorted.

-Parameters
-----------
-level : int, string, or list of these, default last level
-    Level(s) to stack, can pass level name
-dropna : boolean, default True
-    Whether to drop rows in the resulting Frame/Series with no valid
-    values

-Examples
-----------
->>> s
-     a   b
-one  1.  2.
-two  3.  4.

->>> s.stack()
-one a    1
-    b    2
-two a    3
-    b    4

-Returns
--------
-stacked : DataFrame or Series
- -
std(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return sample standard deviation over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-std : Series or DataFrame (if level specified)
- -
sub(self, other, axis='columns', level=None, fill_value=None)
Subtraction of dataframe and other, element-wise (binary operator `sub`).

-Equivalent to ``dataframe - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rsub
- -
subtract = sub(self, other, axis='columns', level=None, fill_value=None)
Subtraction of dataframe and other, element-wise (binary operator `sub`).

-Equivalent to ``dataframe - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rsub
- -
sum(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the sum of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sum : Series or DataFrame (if level specified)
- -
swaplevel(self, i=-2, j=-1, axis=0)
Swap levels i and j in a MultiIndex on a particular axis

-Parameters
-----------
-i, j : int, string (can be mixed)
-    Level of index to be swapped. Can pass level name as string.

-Returns
--------
-swapped : type of caller (new object)

-.. versionchanged:: 0.18.1

-   The indexes ``i`` and ``j`` are now optional, and default to
-   the two innermost levels of the index.
- -
to_csv(self, path_or_buf=None, sep=',', na_rep='', float_format=None, columns=None, header=True, index=True, index_label=None, mode='w', encoding=None, compression=None, quoting=None, quotechar='"', line_terminator='\n', chunksize=None, tupleize_cols=False, date_format=None, doublequote=True, escapechar=None, decimal='.')
Write DataFrame to a comma-separated values (csv) file

-Parameters
-----------
-path_or_buf : string or file handle, default None
-    File path or object, if None is provided the result is returned as
-    a string.
-sep : character, default ','
-    Field delimiter for the output file.
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-columns : sequence, optional
-    Columns to write
-header : boolean or list of string, default True
-    Write out column names. If a list of string is given it is assumed
-    to be aliases for the column names
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, or False, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.  If
-    False do not print fields for index names. Use index_label=False
-    for easier importing in R
-mode : str
-    Python write mode, default 'w'
-encoding : string, optional
-    A string representing the encoding to use in the output file,
-    defaults to 'ascii' on Python 2 and 'utf-8' on Python 3.
-compression : string, optional
-    a string representing the compression to use in the output file,
-    allowed values are 'gzip', 'bz2', 'xz',
-    only used when the first argument is a filename
-line_terminator : string, default ``'\n'``
-    The newline character or character sequence to use in the output
-    file
-quoting : optional constant from csv module
-    defaults to csv.QUOTE_MINIMAL. If you have set a `float_format`
-    then floats are converted to strings and thus csv.QUOTE_NONNUMERIC
-    will treat them as non-numeric
-quotechar : string (length 1), default '\"'
-    character used to quote fields
-doublequote : boolean, default True
-    Control quoting of `quotechar` inside a field
-escapechar : string (length 1), default None
-    character used to escape `sep` and `quotechar` when appropriate
-chunksize : int or None
-    rows to write at a time
-tupleize_cols : boolean, default False
-    write multi_index columns as a list of tuples (if True)
-    or new (expanded format) if False)
-date_format : string, default None
-    Format string for datetime objects
-decimal: string, default '.'
-    Character recognized as decimal separator. E.g. use ',' for
-    European data

-    .. versionadded:: 0.16.0
- -
to_dict(self, orient='dict')
Convert DataFrame to dictionary.

-Parameters
-----------
-orient : str {'dict', 'list', 'series', 'split', 'records', 'index'}
-    Determines the type of the values of the dictionary.

-    - dict (default) : dict like {column -> {index -> value}}
-    - list : dict like {column -> [values]}
-    - series : dict like {column -> Series(values)}
-    - split : dict like
-      {index -> [index], columns -> [columns], data -> [values]}
-    - records : list like
-      [{column -> value}, ... , {column -> value}]
-    - index : dict like {index -> {column -> value}}

-      .. versionadded:: 0.17.0

-    Abbreviations are allowed. `s` indicates `series` and `sp`
-    indicates `split`.

-Returns
--------
-result : dict like {column -> {index -> value}}
- -
to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='', float_format=None, columns=None, header=True, index=True, index_label=None, startrow=0, startcol=0, engine=None, merge_cells=True, encoding=None, inf_rep='inf', verbose=True, freeze_panes=None)
Write DataFrame to an excel sheet


-Parameters
-----------
-excel_writer : string or ExcelWriter object
-    File path or existing ExcelWriter
-sheet_name : string, default 'Sheet1'
-    Name of sheet which will contain DataFrame
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-columns : sequence, optional
-    Columns to write
-header : boolean or list of string, default True
-    Write out column names. If a list of string is given it is
-    assumed to be aliases for the column names
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.
-startrow :
-    upper left cell row to dump data frame
-startcol :
-    upper left cell column to dump data frame
-engine : string, default None
-    write engine to use - you can also set this via the options
-    ``io.excel.xlsx.writer``, ``io.excel.xls.writer``, and
-    ``io.excel.xlsm.writer``.
-merge_cells : boolean, default True
-    Write MultiIndex and Hierarchical Rows as merged cells.
-encoding: string, default None
-    encoding of the resulting excel file. Only necessary for xlwt,
-    other writers support unicode natively.
-inf_rep : string, default 'inf'
-    Representation for infinity (there is no native representation for
-    infinity in Excel)
-freeze_panes : tuple of integer (length 2), default None
-    Specifies the one-based bottommost row and rightmost column that
-    is to be frozen

-    .. versionadded:: 0.20.0

-Notes
------
-If passing an existing ExcelWriter object, then the sheet will be added
-to the existing workbook.  This can be used to save different
-DataFrames to one workbook:

->>> writer = pd.ExcelWriter('output.xlsx')
->>> df1.to_excel(writer,'Sheet1')
->>> df2.to_excel(writer,'Sheet2')
->>> writer.save()

-For compatibility with to_csv, to_excel serializes lists and dicts to
-strings before writing.
- -
to_feather(self, fname)
write out the binary feather-format for DataFrames

-.. versionadded:: 0.20.0

-Parameters
-----------
-fname : str
-    string file path
- -
to_gbq(self, destination_table, project_id, chunksize=10000, verbose=True, reauth=False, if_exists='fail', private_key=None)
Write a DataFrame to a Google BigQuery table.

-The main method a user calls to export pandas DataFrame contents to
-Google BigQuery table.

-Google BigQuery API Client Library v2 for Python is used.
-Documentation is available `here
-<https://developers.google.com/api-client-library/python/apis/bigquery/v2>`__

-Authentication to the Google BigQuery service is via OAuth 2.0.

-- If "private_key" is not provided:

-  By default "application default credentials" are used.

-  If default application credentials are not found or are restrictive,
-  user account credentials are used. In this case, you will be asked to
-  grant permissions for product name 'pandas GBQ'.

-- If "private_key" is provided:

-  Service account credentials will be used to authenticate.

-Parameters
-----------
-dataframe : DataFrame
-    DataFrame to be written
-destination_table : string
-    Name of table to be written, in the form 'dataset.tablename'
-project_id : str
-    Google BigQuery Account project ID.
-chunksize : int (default 10000)
-    Number of rows to be inserted in each chunk from the dataframe.
-verbose : boolean (default True)
-    Show percentage complete
-reauth : boolean (default False)
-    Force Google BigQuery to reauthenticate the user. This is useful
-    if multiple accounts are used.
-if_exists : {'fail', 'replace', 'append'}, default 'fail'
-    'fail': If table exists, do nothing.
-    'replace': If table exists, drop it, recreate it, and insert data.
-    'append': If table exists, insert data. Create if does not exist.
-private_key : str (optional)
-    Service account private key in JSON format. Can be file path
-    or string contents. This is useful for remote server
-    authentication (eg. jupyter iPython notebook on remote host)
- -
to_html(self, buf=None, columns=None, col_space=None, header=True, index=True, na_rep='NaN', formatters=None, float_format=None, sparsify=None, index_names=True, justify=None, bold_rows=True, classes=None, escape=True, max_rows=None, max_cols=None, show_dimensions=False, notebook=False, decimal='.', border=None)
Render a DataFrame as an HTML table.

-`to_html`-specific options:

-bold_rows : boolean, default True
-    Make the row labels bold in the output
-classes : str or list or tuple, default None
-    CSS class(es) to apply to the resulting html table
-escape : boolean, default True
-    Convert the characters <, >, and & to HTML-safe sequences.=
-max_rows : int, optional
-    Maximum number of rows to show before truncating. If None, show
-    all.
-max_cols : int, optional
-    Maximum number of columns to show before truncating. If None, show
-    all.
-decimal : string, default '.'
-    Character recognized as decimal separator, e.g. ',' in Europe

-    .. versionadded:: 0.18.0
-border : int
-    A ``border=border`` attribute is included in the opening
-    `<table>` tag. Default ``pd.options.html.border``.

-    .. versionadded:: 0.19.0

-Parameters
-----------
-buf : StringIO-like, optional
-    buffer to write to
-columns : sequence, optional
-    the subset of columns to write; default None writes all columns
-col_space : int, optional
-    the minimum width of each column
-header : bool, optional
-    whether to print column labels, default True
-index : bool, optional
-    whether to print index (row) labels, default True
-na_rep : string, optional
-    string representation of NAN to use, default 'NaN'
-formatters : list or dict of one-parameter functions, optional
-    formatter functions to apply to columns' elements by position or name,
-    default None. The result of each function must be a unicode string.
-    List must be of length equal to the number of columns.
-float_format : one-parameter function, optional
-    formatter function to apply to columns' elements if they are floats,
-    default None. The result of this function must be a unicode string.
-sparsify : bool, optional
-    Set to False for a DataFrame with a hierarchical index to print every
-    multiindex key at each row, default True
-index_names : bool, optional
-    Prints the names of the indexes, default True
-line_width : int, optional
-    Width to wrap a line in characters, default no wrap
-justify : {'left', 'right'}, default None
-    Left or right-justify the column labels. If None uses the option from
-    the print configuration (controlled by set_option), 'right' out
-    of the box.

-Returns
--------
-formatted : string (or unicode, depending on data and options)
- -
to_latex(self, buf=None, columns=None, col_space=None, header=True, index=True, na_rep='NaN', formatters=None, float_format=None, sparsify=None, index_names=True, bold_rows=True, column_format=None, longtable=None, escape=None, encoding=None, decimal='.', multicolumn=None, multicolumn_format=None, multirow=None)
Render a DataFrame to a tabular environment table. You can splice
-this into a LaTeX document. Requires \usepackage{booktabs}.

-`to_latex`-specific options:

-bold_rows : boolean, default True
-    Make the row labels bold in the output
-column_format : str, default None
-    The columns format as specified in `LaTeX table format
-    <https://en.wikibooks.org/wiki/LaTeX/Tables>`__ e.g 'rcl' for 3
-    columns
-longtable : boolean, default will be read from the pandas config module
-    Default: False.
-    Use a longtable environment instead of tabular. Requires adding
-    a \usepackage{longtable} to your LaTeX preamble.
-escape : boolean, default will be read from the pandas config module
-    Default: True.
-    When set to False prevents from escaping latex special
-    characters in column names.
-encoding : str, default None
-    A string representing the encoding to use in the output file,
-    defaults to 'ascii' on Python 2 and 'utf-8' on Python 3.
-decimal : string, default '.'
-    Character recognized as decimal separator, e.g. ',' in Europe.

-    .. versionadded:: 0.18.0

-multicolumn : boolean, default True
-    Use \multicolumn to enhance MultiIndex columns.
-    The default will be read from the config module.

-    .. versionadded:: 0.20.0

-multicolumn_format : str, default 'l'
-    The alignment for multicolumns, similar to `column_format`
-    The default will be read from the config module.

-    .. versionadded:: 0.20.0

-multirow : boolean, default False
-    Use \multirow to enhance MultiIndex rows.
-    Requires adding a \usepackage{multirow} to your LaTeX preamble.
-    Will print centered labels (instead of top-aligned)
-    across the contained rows, separating groups via clines.
-    The default will be read from the pandas config module.

-    .. versionadded:: 0.20.0


-Parameters
-----------
-buf : StringIO-like, optional
-    buffer to write to
-columns : sequence, optional
-    the subset of columns to write; default None writes all columns
-col_space : int, optional
-    the minimum width of each column
-header : bool, optional
-    Write out column names. If a list of string is given, it is assumed to be aliases for the column names.
-index : bool, optional
-    whether to print index (row) labels, default True
-na_rep : string, optional
-    string representation of NAN to use, default 'NaN'
-formatters : list or dict of one-parameter functions, optional
-    formatter functions to apply to columns' elements by position or name,
-    default None. The result of each function must be a unicode string.
-    List must be of length equal to the number of columns.
-float_format : one-parameter function, optional
-    formatter function to apply to columns' elements if they are floats,
-    default None. The result of this function must be a unicode string.
-sparsify : bool, optional
-    Set to False for a DataFrame with a hierarchical index to print every
-    multiindex key at each row, default True
-index_names : bool, optional
-    Prints the names of the indexes, default True
-line_width : int, optional
-    Width to wrap a line in characters, default no wrap

-Returns
--------
-formatted : string (or unicode, depending on data and options)
- -
to_panel(self)
Transform long (stacked) format (DataFrame) into wide (3D, Panel)
-format.

-Currently the index of the DataFrame must be a 2-level MultiIndex. This
-may be generalized later

-Returns
--------
-panel : Panel
- -
to_period(self, freq=None, axis=0, copy=True)
Convert DataFrame from DatetimeIndex to PeriodIndex with desired
-frequency (inferred from index if not passed)

-Parameters
-----------
-freq : string, default
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    The axis to convert (the index by default)
-copy : boolean, default True
-    If False then underlying input data is not copied

-Returns
--------
-ts : TimeSeries with PeriodIndex
- -
to_records(self, index=True, convert_datetime64=True)
Convert DataFrame to record array. Index will be put in the
-'index' field of the record array if requested

-Parameters
-----------
-index : boolean, default True
-    Include index in resulting record array, stored in 'index' field
-convert_datetime64 : boolean, default True
-    Whether to convert the index to datetime.datetime if it is a
-    DatetimeIndex

-Returns
--------
-y : recarray
- -
to_sparse(self, fill_value=None, kind='block')
Convert to SparseDataFrame

-Parameters
-----------
-fill_value : float, default NaN
-kind : {'block', 'integer'}

-Returns
--------
-y : SparseDataFrame
- -
to_stata(self, fname, convert_dates=None, write_index=True, encoding='latin-1', byteorder=None, time_stamp=None, data_label=None, variable_labels=None)
A class for writing Stata binary dta files from array-like objects

-Parameters
-----------
-fname : str or buffer
-    String path of file-like object
-convert_dates : dict
-    Dictionary mapping columns containing datetime types to stata
-    internal format to use when wirting the dates. Options are 'tc',
-    'td', 'tm', 'tw', 'th', 'tq', 'ty'. Column can be either an integer
-    or a name. Datetime columns that do not have a conversion type
-    specified will be converted to 'tc'. Raises NotImplementedError if
-    a datetime column has timezone information
-write_index : bool
-    Write the index to Stata dataset.
-encoding : str
-    Default is latin-1. Unicode is not supported
-byteorder : str
-    Can be ">", "<", "little", or "big". default is `sys.byteorder`
-time_stamp : datetime
-    A datetime to use as file creation date.  Default is the current
-    time.
-dataset_label : str
-    A label for the data set.  Must be 80 characters or smaller.
-variable_labels : dict
-    Dictionary containing columns as keys and variable labels as
-    values. Each label must be 80 characters or smaller.

-    .. versionadded:: 0.19.0

-Raises
-------
-NotImplementedError
-    * If datetimes contain timezone information
-    * Column dtype is not representable in Stata
-ValueError
-    * Columns listed in convert_dates are noth either datetime64[ns]
-      or datetime.datetime
-    * Column listed in convert_dates is not in DataFrame
-    * Categorical label contains more than 32,000 characters

-    .. versionadded:: 0.19.0

-Examples
---------
->>> writer = StataWriter('./data_file.dta', data)
->>> writer.write_file()

-Or with dates

->>> writer = StataWriter('./date_data_file.dta', data, {2 : 'tw'})
->>> writer.write_file()
- -
to_string(self, buf=None, columns=None, col_space=None, header=True, index=True, na_rep='NaN', formatters=None, float_format=None, sparsify=None, index_names=True, justify=None, line_width=None, max_rows=None, max_cols=None, show_dimensions=False)
Render a DataFrame to a console-friendly tabular output.

-Parameters
-----------
-buf : StringIO-like, optional
-    buffer to write to
-columns : sequence, optional
-    the subset of columns to write; default None writes all columns
-col_space : int, optional
-    the minimum width of each column
-header : bool, optional
-    Write out column names. If a list of string is given, it is assumed to be aliases for the column names
-index : bool, optional
-    whether to print index (row) labels, default True
-na_rep : string, optional
-    string representation of NAN to use, default 'NaN'
-formatters : list or dict of one-parameter functions, optional
-    formatter functions to apply to columns' elements by position or name,
-    default None. The result of each function must be a unicode string.
-    List must be of length equal to the number of columns.
-float_format : one-parameter function, optional
-    formatter function to apply to columns' elements if they are floats,
-    default None. The result of this function must be a unicode string.
-sparsify : bool, optional
-    Set to False for a DataFrame with a hierarchical index to print every
-    multiindex key at each row, default True
-index_names : bool, optional
-    Prints the names of the indexes, default True
-line_width : int, optional
-    Width to wrap a line in characters, default no wrap
-justify : {'left', 'right'}, default None
-    Left or right-justify the column labels. If None uses the option from
-    the print configuration (controlled by set_option), 'right' out
-    of the box.

-Returns
--------
-formatted : string (or unicode, depending on data and options)
- -
to_timestamp(self, freq=None, how='start', axis=0, copy=True)
Cast to DatetimeIndex of timestamps, at *beginning* of period

-Parameters
-----------
-freq : string, default frequency of PeriodIndex
-    Desired frequency
-how : {'s', 'e', 'start', 'end'}
-    Convention for converting period to timestamp; start of period
-    vs. end
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    The axis to convert (the index by default)
-copy : boolean, default True
-    If false then underlying input data is not copied

-Returns
--------
-df : DataFrame with DatetimeIndex
- -
transform(self, func, *args, **kwargs)
Call function producing a like-indexed NDFrame
-and return a NDFrame with the transformed values`

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    To apply to column

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Returns
--------
-transformed : NDFrame

-Examples
---------
->>> df = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
-...                   index=pd.date_range('1/1/2000', periods=10))
-df.iloc[3:7] = np.nan

->>> df.transform(lambda x: (x - x.mean()) / x.std())
-                   A         B         C
-2000-01-01  0.579457  1.236184  0.123424
-2000-01-02  0.370357 -0.605875 -1.231325
-2000-01-03  1.455756 -0.277446  0.288967
-2000-01-04       NaN       NaN       NaN
-2000-01-05       NaN       NaN       NaN
-2000-01-06       NaN       NaN       NaN
-2000-01-07       NaN       NaN       NaN
-2000-01-08 -0.498658  1.274522  1.642524
-2000-01-09 -0.540524 -1.012676 -0.828968
-2000-01-10 -1.366388 -0.614710  0.005378

-See also
---------
-pandas.NDFrame.aggregate
-pandas.NDFrame.apply
- -
transpose(self, *args, **kwargs)
Transpose index and columns
- -
truediv(self, other, axis='columns', level=None, fill_value=None)
Floating division of dataframe and other, element-wise (binary operator `truediv`).

-Equivalent to ``dataframe / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rtruediv
- -
unstack(self, level=-1, fill_value=None)
Pivot a level of the (necessarily hierarchical) index labels, returning
-a DataFrame having a new level of column labels whose inner-most level
-consists of the pivoted index labels. If the index is not a MultiIndex,
-the output will be a Series (the analogue of stack when the columns are
-not a MultiIndex).
-The level involved will automatically get sorted.

-Parameters
-----------
-level : int, string, or list of these, default -1 (last level)
-    Level(s) of index to unstack, can pass level name
-fill_value : replace NaN with this value if the unstack produces
-    missing values

-    .. versionadded: 0.18.0

-See also
---------
-DataFrame.pivot : Pivot a table based on column values.
-DataFrame.stack : Pivot a level of the column labels (inverse operation
-    from `unstack`).

-Examples
---------
->>> index = pd.MultiIndex.from_tuples([('one', 'a'), ('one', 'b'),
-...                                    ('two', 'a'), ('two', 'b')])
->>> s = pd.Series(np.arange(1.0, 5.0), index=index)
->>> s
-one  a   1.0
-     b   2.0
-two  a   3.0
-     b   4.0
-dtype: float64

->>> s.unstack(level=-1)
-     a   b
-one  1.0  2.0
-two  3.0  4.0

->>> s.unstack(level=0)
-   one  two
-a  1.0   3.0
-b  2.0   4.0

->>> df = s.unstack(level=0)
->>> df.unstack()
-one  a  1.0
-     b  2.0
-two  a  3.0
-     b  4.0
-dtype: float64

-Returns
--------
-unstacked : DataFrame or Series
- -
update(self, other, join='left', overwrite=True, filter_func=None, raise_conflict=False)
Modify DataFrame in place using non-NA values from passed
-DataFrame. Aligns on indices

-Parameters
-----------
-other : DataFrame, or object coercible into a DataFrame
-join : {'left'}, default 'left'
-overwrite : boolean, default True
-    If True then overwrite values for common keys in the calling frame
-filter_func : callable(1d-array) -> 1d-array<boolean>, default None
-    Can choose to replace values other than NA. Return True for values
-    that should be updated
-raise_conflict : boolean
-    If True, will raise an error if the DataFrame and other both
-    contain data in the same place.
- -
var(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased variance over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-var : Series or DataFrame (if level specified)
- -
-Class methods inherited from pandas.core.frame.DataFrame:
-
from_csv(path, header=0, sep=',', index_col=0, parse_dates=True, encoding=None, tupleize_cols=False, infer_datetime_format=False) from builtins.type
Read CSV file (DISCOURAGED, please use :func:`pandas.read_csv`
-instead).

-It is preferable to use the more powerful :func:`pandas.read_csv`
-for most general purposes, but ``from_csv`` makes for an easy
-roundtrip to and from a file (the exact counterpart of
-``to_csv``), especially with a DataFrame of time series data.

-This method only differs from the preferred :func:`pandas.read_csv`
-in some defaults:

-- `index_col` is ``0`` instead of ``None`` (take first column as index
-  by default)
-- `parse_dates` is ``True`` instead of ``False`` (try parsing the index
-  as datetime by default)

-So a ``pd.DataFrame.from_csv(path)`` can be replaced by
-``pd.read_csv(path, index_col=0, parse_dates=True)``.

-Parameters
-----------
-path : string file path or file handle / StringIO
-header : int, default 0
-    Row to use as header (skip prior rows)
-sep : string, default ','
-    Field delimiter
-index_col : int or sequence, default 0
-    Column to use for index. If a sequence is given, a MultiIndex
-    is used. Different default from read_table
-parse_dates : boolean, default True
-    Parse dates. Different default from read_table
-tupleize_cols : boolean, default False
-    write multi_index columns as a list of tuples (if True)
-    or new (expanded format) if False)
-infer_datetime_format: boolean, default False
-    If True and `parse_dates` is True for a column, try to infer the
-    datetime format based on the first datetime string. If the format
-    can be inferred, there often will be a large parsing speed-up.

-See also
---------
-pandas.read_csv

-Returns
--------
-y : DataFrame
- -
from_dict(data, orient='columns', dtype=None) from builtins.type
Construct DataFrame from dict of array-like or dicts

-Parameters
-----------
-data : dict
-    {field : array-like} or {field : dict}
-orient : {'columns', 'index'}, default 'columns'
-    The "orientation" of the data. If the keys of the passed dict
-    should be the columns of the resulting DataFrame, pass 'columns'
-    (default). Otherwise if the keys should be rows, pass 'index'.
-dtype : dtype, default None
-    Data type to force, otherwise infer

-Returns
--------
-DataFrame
- -
from_items(items, columns=None, orient='columns') from builtins.type
Convert (key, value) pairs to DataFrame. The keys will be the axis
-index (usually the columns, but depends on the specified
-orientation). The values should be arrays or Series.

-Parameters
-----------
-items : sequence of (key, value) pairs
-    Values should be arrays or Series.
-columns : sequence of column labels, optional
-    Must be passed if orient='index'.
-orient : {'columns', 'index'}, default 'columns'
-    The "orientation" of the data. If the keys of the
-    input correspond to column labels, pass 'columns'
-    (default). Otherwise if the keys correspond to the index,
-    pass 'index'.

-Returns
--------
-frame : DataFrame
- -
from_records(data, index=None, exclude=None, columns=None, coerce_float=False, nrows=None) from builtins.type
Convert structured or record ndarray to DataFrame

-Parameters
-----------
-data : ndarray (structured dtype), list of tuples, dict, or DataFrame
-index : string, list of fields, array-like
-    Field of array to use as the index, alternately a specific set of
-    input labels to use
-exclude : sequence, default None
-    Columns or fields to exclude
-columns : sequence, default None
-    Column names to use. If the passed data do not have names
-    associated with them, this argument provides names for the
-    columns. Otherwise this argument indicates the order of the columns
-    in the result (any names not found in the data will become all-NA
-    columns)
-coerce_float : boolean, default False
-    Attempt to convert values of non-string, non-numeric objects (like
-    decimal.Decimal) to floating point, useful for SQL result sets

-Returns
--------
-df : DataFrame
- -
-Data descriptors inherited from pandas.core.frame.DataFrame:
-
axes
-
Return a list with the row axis labels and column axis labels as the
-only members. They are returned in that order.
-
-
columns
-
-
index
-
-
shape
-
Return a tuple representing the dimensionality of the DataFrame.
-
-
style
-
Property returning a Styler object containing methods for
-building a styled HTML representation fo the DataFrame.

-See Also
---------
-pandas.io.formats.style.Styler
-
-
-Data and other attributes inherited from pandas.core.frame.DataFrame:
-
plot = <class 'pandas.plotting._core.FramePlotMethods'>
DataFrame plotting accessor and method

-Examples
---------
->>> df.plot.line()
->>> df.plot.scatter('x', 'y')
->>> df.plot.hexbin()

-These plotting methods can also be accessed by calling the accessor as a
-method with the ``kind`` argument:
-``df.plot(kind='line')`` is equivalent to ``df.plot.line()``
- -
-Methods inherited from pandas.core.generic.NDFrame:
-
__abs__(self)
- -
__array__(self, dtype=None)
- -
__array_wrap__(self, result, context=None)
- -
__bool__ = __nonzero__(self)
- -
__contains__(self, key)
True if the key is in the info axis
- -
__copy__(self, deep=True)
- -
__deepcopy__(self, memo=None)
- -
__delitem__(self, key)
Delete item
- -
__finalize__(self, other, method=None, **kwargs)
Propagate metadata from other to self.

-Parameters
-----------
-other : the object from which to get the attributes that we are going
-    to propagate
-method : optional, a passed method name ; possibly to take different
-    types of propagation actions based on this
- -
__getattr__(self, name)
After regular attribute access, try looking up the name
-This allows simpler access to columns for interactive use.
- -
__getstate__(self)
- -
__hash__(self)
Return hash(self).
- -
__invert__(self)
- -
__iter__(self)
Iterate over infor axis
- -
__neg__(self)
- -
__nonzero__(self)
- -
__round__(self, decimals=0)
- -
__setattr__(self, name, value)
After regular attribute access, try setting the name
-This allows simpler access to columns for interactive use.
- -
__setstate__(self, state)
- -
abs(self)
Return an object with absolute value taken--only applicable to objects
-that are all numeric.

-Returns
--------
-abs: type of caller
- -
add_prefix(self, prefix)
Concatenate prefix string with panel items names.

-Parameters
-----------
-prefix : string

-Returns
--------
-with_prefix : type of caller
- -
add_suffix(self, suffix)
Concatenate suffix string with panel items names.

-Parameters
-----------
-suffix : string

-Returns
--------
-with_suffix : type of caller
- -
as_blocks(self, copy=True)
Convert the frame to a dict of dtype -> Constructor Types that each has
-a homogeneous dtype.

-NOTE: the dtypes of the blocks WILL BE PRESERVED HERE (unlike in
-      as_matrix)

-Parameters
-----------
-copy : boolean, default True

-       .. versionadded: 0.16.1

-Returns
--------
-values : a dict of dtype -> Constructor Types
- -
as_matrix(self, columns=None)
Convert the frame to its Numpy-array representation.

-Parameters
-----------
-columns: list, optional, default:None
-    If None, return all columns, otherwise, returns specified columns.

-Returns
--------
-values : ndarray
-    If the caller is heterogeneous and contains booleans or objects,
-    the result will be of dtype=object. See Notes.


-Notes
------
-Return is NOT a Numpy-matrix, rather, a Numpy-array.

-The dtype will be a lower-common-denominator dtype (implicit
-upcasting); that is to say if the dtypes (even of numeric types)
-are mixed, the one that accommodates all will be chosen. Use this
-with care if you are not dealing with the blocks.

-e.g. If the dtypes are float16 and float32, dtype will be upcast to
-float32.  If dtypes are int32 and uint8, dtype will be upcase to
-int32. By numpy.find_common_type convention, mixing int64 and uint64
-will result in a flot64 dtype.

-This method is provided for backwards compatibility. Generally,
-it is recommended to use '.values'.

-See Also
---------
-pandas.DataFrame.values
- -
asfreq(self, freq, method=None, how=None, normalize=False, fill_value=None)
Convert TimeSeries to specified frequency.

-Optionally provide filling method to pad/backfill missing values.

-Returns the original data conformed to a new index with the specified
-frequency. ``resample`` is more appropriate if an operation, such as
-summarization, is necessary to represent the data at the new frequency.

-Parameters
-----------
-freq : DateOffset object, or string
-method : {'backfill'/'bfill', 'pad'/'ffill'}, default None
-    Method to use for filling holes in reindexed Series (note this
-    does not fill NaNs that already were present):

-    * 'pad' / 'ffill': propagate last valid observation forward to next
-      valid
-    * 'backfill' / 'bfill': use NEXT valid observation to fill
-how : {'start', 'end'}, default end
-    For PeriodIndex only, see PeriodIndex.asfreq
-normalize : bool, default False
-    Whether to reset output index to midnight
-fill_value: scalar, optional
-    Value to use for missing values, applied during upsampling (note
-    this does not fill NaNs that already were present).

-    .. versionadded:: 0.20.0

-Returns
--------
-converted : type of caller

-Examples
---------

-Start by creating a series with 4 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=4, freq='T')
->>> series = pd.Series([0.0, None, 2.0, 3.0], index=index)
->>> df = pd.DataFrame({'s':series})
->>> df
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:03:00    3.0

-Upsample the series into 30 second bins.

->>> df.asfreq(freq='30S')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    NaN
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``fill value``.

->>> df.asfreq(freq='30S', fill_value=9.0)
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    9.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    9.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    9.0
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``method``.

->>> df.asfreq(freq='30S', method='bfill')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    2.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    3.0
-2000-01-01 00:03:00    3.0

-See Also
---------
-reindex

-Notes
------
-To learn more about the frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
- -
asof(self, where, subset=None)
The last row without any NaN is taken (or the last row without
-NaN considering only the subset of columns in the case of a DataFrame)

-.. versionadded:: 0.19.0 For DataFrame

-If there is no good value, NaN is returned for a Series
-a Series of NaN values for a DataFrame

-Parameters
-----------
-where : date or array of dates
-subset : string or list of strings, default None
-   if not None use these columns for NaN propagation

-Notes
------
-Dates are assumed to be sorted
-Raises if this is not the case

-Returns
--------
-where is scalar

-  - value or NaN if input is Series
-  - Series if input is DataFrame

-where is Index: same shape object as input

-See Also
---------
-merge_asof
- -
astype(self, dtype, copy=True, errors='raise', **kwargs)
Cast object to input numpy.dtype
-Return a copy when copy = True (be really careful with this!)

-Parameters
-----------
-dtype : data type, or dict of column name -> data type
-    Use a numpy.dtype or Python type to cast entire pandas object to
-    the same type. Alternatively, use {col: dtype, ...}, where col is a
-    column label and dtype is a numpy.dtype or Python type to cast one
-    or more of the DataFrame's columns to column-specific types.
-errors : {'raise', 'ignore'}, default 'raise'.
-    Control raising of exceptions on invalid data for provided dtype.

-    - ``raise`` : allow exceptions to be raised
-    - ``ignore`` : suppress exceptions. On error return original object

-    .. versionadded:: 0.20.0

-raise_on_error : DEPRECATED use ``errors`` instead
-kwargs : keyword arguments to pass on to the constructor

-Returns
--------
-casted : type of caller
- -
at_time(self, time, asof=False)
Select values at particular time of day (e.g. 9:30AM).

-Parameters
-----------
-time : datetime.time or string

-Returns
--------
-values_at_time : type of caller
- -
between_time(self, start_time, end_time, include_start=True, include_end=True)
Select values between particular times of the day (e.g., 9:00-9:30 AM).

-Parameters
-----------
-start_time : datetime.time or string
-end_time : datetime.time or string
-include_start : boolean, default True
-include_end : boolean, default True

-Returns
--------
-values_between_time : type of caller
- -
bfill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='bfill') <DataFrame.fillna>`
- -
bool(self)
Return the bool of a single element PandasObject.

-This must be a boolean scalar value, either True or False.  Raise a
-ValueError if the PandasObject does not have exactly 1 element, or that
-element is not boolean
- -
clip(self, lower=None, upper=None, axis=None, *args, **kwargs)
Trim values at input threshold(s).

-Parameters
-----------
-lower : float or array_like, default None
-upper : float or array_like, default None
-axis : int or string axis name, optional
-    Align object with lower and upper along the given axis.

-Returns
--------
-clipped : Series

-Examples
---------
->>> df
-  0         1
-0  0.335232 -1.256177
-1 -1.367855  0.746646
-2  0.027753 -1.176076
-3  0.230930 -0.679613
-4  1.261967  0.570967
->>> df.clip(-1.0, 0.5)
-          0         1
-0  0.335232 -1.000000
-1 -1.000000  0.500000
-2  0.027753 -1.000000
-3  0.230930 -0.679613
-4  0.500000  0.500000
->>> t
-0   -0.3
-1   -0.2
-2   -0.1
-3    0.0
-4    0.1
-dtype: float64
->>> df.clip(t, t + 1, axis=0)
-          0         1
-0  0.335232 -0.300000
-1 -0.200000  0.746646
-2  0.027753 -0.100000
-3  0.230930  0.000000
-4  1.100000  0.570967
- -
clip_lower(self, threshold, axis=None)
Return copy of the input with values below given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
clip_upper(self, threshold, axis=None)
Return copy of input with values above given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
consolidate(self, inplace=False)
DEPRECATED: consolidate will be an internal implementation only.
- -
convert_objects(self, convert_dates=True, convert_numeric=False, convert_timedeltas=True, copy=True)
Deprecated.

-Attempt to infer better dtype for object columns

-Parameters
-----------
-convert_dates : boolean, default True
-    If True, convert to date where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-convert_numeric : boolean, default False
-    If True, attempt to coerce to numbers (including strings), with
-    unconvertible values becoming NaN.
-convert_timedeltas : boolean, default True
-    If True, convert to timedelta where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-copy : boolean, default True
-    If True, return a copy even if no copy is necessary (e.g. no
-    conversion was done). Note: This is meant for internal use, and
-    should not be confused with inplace.

-See Also
---------
-pandas.to_datetime : Convert argument to datetime.
-pandas.to_timedelta : Convert argument to timedelta.
-pandas.to_numeric : Return a fixed frequency timedelta index,
-    with day as the default.

-Returns
--------
-converted : same as input object
- -
copy(self, deep=True)
Make a copy of this objects data.

-Parameters
-----------
-deep : boolean or string, default True
-    Make a deep copy, including a copy of the data and the indices.
-    With ``deep=False`` neither the indices or the data are copied.

-    Note that when ``deep=True`` data is copied, actual python objects
-    will not be copied recursively, only the reference to the object.
-    This is in contrast to ``copy.deepcopy`` in the Standard Library,
-    which recursively copies object data.

-Returns
--------
-copy : type of caller
- -
describe(self, percentiles=None, include=None, exclude=None)
Generates descriptive statistics that summarize the central tendency,
-dispersion and shape of a dataset's distribution, excluding
-``NaN`` values.

-Analyzes both numeric and object series, as well
-as ``DataFrame`` column sets of mixed data types. The output
-will vary depending on what is provided. Refer to the notes
-below for more detail.

-Parameters
-----------
-percentiles : list-like of numbers, optional
-    The percentiles to include in the output. All should
-    fall between 0 and 1. The default is
-    ``[.25, .5, .75]``, which returns the 25th, 50th, and
-    75th percentiles.
-include : 'all', list-like of dtypes or None (default), optional
-    A white list of data types to include in the result. Ignored
-    for ``Series``. Here are the options:

-    - 'all' : All columns of the input will be included in the output.
-    - A list-like of dtypes : Limits the results to the
-      provided data types.
-      To limit the result to numeric types submit
-      ``numpy.number``. To limit it instead to categorical
-      objects submit the ``numpy.object`` data type. Strings
-      can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will include all numeric columns.
-exclude : list-like of dtypes or None (default), optional,
-    A black list of data types to omit from the result. Ignored
-    for ``Series``. Here are the options:

-    - A list-like of dtypes : Excludes the provided data types
-      from the result. To select numeric types submit
-      ``numpy.number``. To select categorical objects submit the data
-      type ``numpy.object``. Strings can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will exclude nothing.

-Returns
--------
-summary:  Series/DataFrame of summary statistics

-Notes
------
-For numeric data, the result's index will include ``count``,
-``mean``, ``std``, ``min``, ``max`` as well as lower, ``50`` and
-upper percentiles. By default the lower percentile is ``25`` and the
-upper percentile is ``75``. The ``50`` percentile is the
-same as the median.

-For object data (e.g. strings or timestamps), the result's index
-will include ``count``, ``unique``, ``top``, and ``freq``. The ``top``
-is the most common value. The ``freq`` is the most common value's
-frequency. Timestamps also include the ``first`` and ``last`` items.

-If multiple object values have the highest count, then the
-``count`` and ``top`` results will be arbitrarily chosen from
-among those with the highest count.

-For mixed data types provided via a ``DataFrame``, the default is to
-return only an analysis of numeric columns. If ``include='all'``
-is provided as an option, the result will include a union of
-attributes of each type.

-The `include` and `exclude` parameters can be used to limit
-which columns in a ``DataFrame`` are analyzed for the output.
-The parameters are ignored when analyzing a ``Series``.

-Examples
---------
-Describing a numeric ``Series``.

->>> s = pd.Series([1, 2, 3])
->>> s.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0

-Describing a categorical ``Series``.

->>> s = pd.Series(['a', 'a', 'b', 'c'])
->>> s.describe()
-count     4
-unique    3
-top       a
-freq      2
-dtype: object

-Describing a timestamp ``Series``.

->>> s = pd.Series([
-...   np.datetime64("2000-01-01"),
-...   np.datetime64("2010-01-01"),
-...   np.datetime64("2010-01-01")
-... ])
->>> s.describe()
-count                       3
-unique                      2
-top       2010-01-01 00:00:00
-freq                        2
-first     2000-01-01 00:00:00
-last      2010-01-01 00:00:00
-dtype: object

-Describing a ``DataFrame``. By default only numeric fields
-are returned.

->>> df = pd.DataFrame([[1, 'a'], [2, 'b'], [3, 'c']],
-...                   columns=['numeric', 'object'])
->>> df.describe()
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Describing all columns of a ``DataFrame`` regardless of data type.

->>> df.describe(include='all')
-        numeric object
-count       3.0      3
-unique      NaN      3
-top         NaN      b
-freq        NaN      1
-mean        2.0    NaN
-std         1.0    NaN
-min         1.0    NaN
-25%         1.5    NaN
-50%         2.0    NaN
-75%         2.5    NaN
-max         3.0    NaN

-Describing a column from a ``DataFrame`` by accessing it as
-an attribute.

->>> df.numeric.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0
-Name: numeric, dtype: float64

-Including only numeric columns in a ``DataFrame`` description.

->>> df.describe(include=[np.number])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Including only string columns in a ``DataFrame`` description.

->>> df.describe(include=[np.object])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding numeric columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.number])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding object columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.object])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-See Also
---------
-DataFrame.count
-DataFrame.max
-DataFrame.min
-DataFrame.mean
-DataFrame.std
-DataFrame.select_dtypes
- -
drop(self, labels, axis=0, level=None, inplace=False, errors='raise')
Return new object with labels in requested axis removed.

-Parameters
-----------
-labels : single label or list-like
-axis : int or axis name
-level : int or level name, default None
-    For MultiIndex
-inplace : bool, default False
-    If True, do operation inplace and return None.
-errors : {'ignore', 'raise'}, default 'raise'
-    If 'ignore', suppress error and existing labels are dropped.

-    .. versionadded:: 0.16.1

-Returns
--------
-dropped : type of caller
- -
equals(self, other)
Determines if two NDFrame objects contain the same elements. NaNs in
-the same location are considered equal.
- -
ffill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='ffill') <DataFrame.fillna>`
- -
filter(self, items=None, like=None, regex=None, axis=None)
Subset rows or columns of dataframe according to labels in
-the specified index.

-Note that this routine does not filter a dataframe on its
-contents. The filter is applied to the labels of the index.

-Parameters
-----------
-items : list-like
-    List of info axis to restrict to (must not all be present)
-like : string
-    Keep info axis where "arg in col == True"
-regex : string (regular expression)
-    Keep info axis with re.search(regex, col) == True
-axis : int or string axis name
-    The axis to filter on.  By default this is the info axis,
-    'index' for Series, 'columns' for DataFrame

-Returns
--------
-same type as input object

-Examples
---------
->>> df
-one  two  three
-mouse     1    2      3
-rabbit    4    5      6

->>> # select columns by name
->>> df.filter(items=['one', 'three'])
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select columns by regular expression
->>> df.filter(regex='e$', axis=1)
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select rows containing 'bbi'
->>> df.filter(like='bbi', axis=0)
-one  two  three
-rabbit    4    5      6

-See Also
---------
-pandas.DataFrame.select

-Notes
------
-The ``items``, ``like``, and ``regex`` parameters are
-enforced to be mutually exclusive.

-``axis`` defaults to the info axis that is used when indexing
-with ``[]``.
- -
first(self, offset)
Convenience method for subsetting initial periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.first('10D') -> First 10 days

-Returns
--------
-subset : type of caller
- -
get(self, key, default=None)
Get item from object for given key (DataFrame column, Panel slice,
-etc.). Returns default value if not found.

-Parameters
-----------
-key : object

-Returns
--------
-value : type of items contained in object
- -
get_dtype_counts(self)
Return the counts of dtypes in this object.
- -
get_ftype_counts(self)
Return the counts of ftypes in this object.
- -
get_values(self)
same as values (but handles sparseness conversions)
- -
groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, **kwargs)
Group series using mapper (dict or key function, apply given function
-to group, return result as series) or by a series of columns.

-Parameters
-----------
-by : mapping, function, str, or iterable
-    Used to determine the groups for the groupby.
-    If ``by`` is a function, it's called on each value of the object's
-    index. If a dict or Series is passed, the Series or dict VALUES
-    will be used to determine the groups (the Series' values are first
-    aligned; see ``.align()`` method). If an ndarray is passed, the
-    values are used as-is determine the groups. A str or list of strs
-    may be passed to group by the columns in ``self``
-axis : int, default 0
-level : int, level name, or sequence of such, default None
-    If the axis is a MultiIndex (hierarchical), group by a particular
-    level or levels
-as_index : boolean, default True
-    For aggregated output, return object with group labels as the
-    index. Only relevant for DataFrame input. as_index=False is
-    effectively "SQL-style" grouped output
-sort : boolean, default True
-    Sort group keys. Get better performance by turning this off.
-    Note this does not influence the order of observations within each
-    group.  groupby preserves the order of rows within each group.
-group_keys : boolean, default True
-    When calling apply, add group keys to index to identify pieces
-squeeze : boolean, default False
-    reduce the dimensionality of the return type if possible,
-    otherwise return a consistent type

-Examples
---------
-DataFrame results

->>> data.groupby(func, axis=0).mean()
->>> data.groupby(['col1', 'col2'])['col3'].mean()

-DataFrame with hierarchical index

->>> data.groupby(['col1', 'col2']).mean()

-Returns
--------
-GroupBy object
- -
head(self, n=5)
Returns first n rows
- -
interpolate(self, method='linear', axis=0, limit=None, inplace=False, limit_direction='forward', downcast=None, **kwargs)
Interpolate values according to different methods.

-Please note that only ``method='linear'`` is supported for
-DataFrames/Series with a MultiIndex.

-Parameters
-----------
-method : {'linear', 'time', 'index', 'values', 'nearest', 'zero',
-          'slinear', 'quadratic', 'cubic', 'barycentric', 'krogh',
-          'polynomial', 'spline', 'piecewise_polynomial',
-          'from_derivatives', 'pchip', 'akima'}

-    * 'linear': ignore the index and treat the values as equally
-      spaced. This is the only method supported on MultiIndexes.
-      default
-    * 'time': interpolation works on daily and higher resolution
-      data to interpolate given length of interval
-    * 'index', 'values': use the actual numerical values of the index
-    * 'nearest', 'zero', 'slinear', 'quadratic', 'cubic',
-      'barycentric', 'polynomial' is passed to
-      ``scipy.interpolate.interp1d``. Both 'polynomial' and 'spline'
-      require that you also specify an `order` (int),
-      e.g. df.interpolate(method='polynomial', order=4).
-      These use the actual numerical values of the index.
-    * 'krogh', 'piecewise_polynomial', 'spline', 'pchip' and 'akima'
-      are all wrappers around the scipy interpolation methods of
-      similar names. These use the actual numerical values of the
-      index. For more information on their behavior, see the
-      `scipy documentation
-      <http://docs.scipy.org/doc/scipy/reference/interpolate.html#univariate-interpolation>`__
-      and `tutorial documentation
-      <http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html>`__
-    * 'from_derivatives' refers to BPoly.from_derivatives which
-      replaces 'piecewise_polynomial' interpolation method in
-      scipy 0.18

-    .. versionadded:: 0.18.1

-       Added support for the 'akima' method
-       Added interpolate method 'from_derivatives' which replaces
-       'piecewise_polynomial' in scipy 0.18; backwards-compatible with
-       scipy < 0.18

-axis : {0, 1}, default 0
-    * 0: fill column-by-column
-    * 1: fill row-by-row
-limit : int, default None.
-    Maximum number of consecutive NaNs to fill. Must be greater than 0.
-limit_direction : {'forward', 'backward', 'both'}, default 'forward'
-    If limit is specified, consecutive NaNs will be filled in this
-    direction.

-    .. versionadded:: 0.17.0

-inplace : bool, default False
-    Update the NDFrame in place if possible.
-downcast : optional, 'infer' or None, defaults to None
-    Downcast dtypes if possible.
-kwargs : keyword arguments to pass on to the interpolating function.

-Returns
--------
-Series or DataFrame of same shape interpolated at the NaNs

-See Also
---------
-reindex, replace, fillna

-Examples
---------

-Filling in NaNs

->>> s = pd.Series([0, 1, np.nan, 3])
->>> s.interpolate()
-0    0
-1    1
-2    2
-3    3
-dtype: float64
- -
isnull(self)
Return a boolean same-sized object indicating if the values are null.

-See Also
---------
-notnull : boolean inverse of isnull
- -
keys(self)
Get the 'info axis' (see Indexing for more)

-This is index for Series, columns for DataFrame and major_axis for
-Panel.
- -
last(self, offset)
Convenience method for subsetting final periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.last('5M') -> Last 5 months

-Returns
--------
-subset : type of caller
- -
mask(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is False and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The mask method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``False`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``mask`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.where`
- -
notnull(self)
Return a boolean same-sized object indicating if the values are
-not null.

-See Also
---------
-isnull : boolean inverse of notnull
- -
pct_change(self, periods=1, fill_method='pad', limit=None, freq=None, **kwargs)
Percent change over given number of periods.

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming percent change
-fill_method : str, default 'pad'
-    How to handle NAs before computing percent changes
-limit : int, default None
-    The number of consecutive NAs to fill before stopping
-freq : DateOffset, timedelta, or offset alias string, optional
-    Increment to use from time series API (e.g. 'M' or BDay())

-Returns
--------
-chg : NDFrame

-Notes
------

-By default, the percentage change is calculated along the stat
-axis: 0, or ``Index``, for ``DataFrame`` and 1, or ``minor`` for
-``Panel``. You can change this with the ``axis`` keyword argument.
- -
pipe(self, func, *args, **kwargs)
Apply func(self, \*args, \*\*kwargs)

-.. versionadded:: 0.16.2

-Parameters
-----------
-func : function
-    function to apply to the NDFrame.
-    ``args``, and ``kwargs`` are passed into ``func``.
-    Alternatively a ``(callable, data_keyword)`` tuple where
-    ``data_keyword`` is a string indicating the keyword of
-    ``callable`` that expects the NDFrame.
-args : positional arguments passed into ``func``.
-kwargs : a dictionary of keyword arguments passed into ``func``.

-Returns
--------
-object : the return type of ``func``.

-Notes
------

-Use ``.pipe`` when chaining together functions that expect
-on Series or DataFrames. Instead of writing

->>> f(g(h(df), arg1=a), arg2=b, arg3=c)

-You can write

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe(f, arg2=b, arg3=c)
-... )

-If you have a function that takes the data as (say) the second
-argument, pass a tuple indicating which keyword expects the
-data. For example, suppose ``f`` takes its data as ``arg2``:

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe((f, 'arg2'), arg1=a, arg3=c)
-...  )

-See Also
---------
-pandas.DataFrame.apply
-pandas.DataFrame.applymap
-pandas.Series.map
- -
pop(self, item)
Return item and drop from frame. Raise KeyError if not found.
- -
rank(self, axis=0, method='average', numeric_only=None, na_option='keep', ascending=True, pct=False)
Compute numerical data ranks (1 through n) along axis. Equal values are
-assigned a rank that is the average of the ranks of those values

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    index to direct ranking
-method : {'average', 'min', 'max', 'first', 'dense'}
-    * average: average rank of group
-    * min: lowest rank in group
-    * max: highest rank in group
-    * first: ranks assigned in order they appear in the array
-    * dense: like 'min', but rank always increases by 1 between groups
-numeric_only : boolean, default None
-    Include only float, int, boolean data. Valid only for DataFrame or
-    Panel objects
-na_option : {'keep', 'top', 'bottom'}
-    * keep: leave NA values where they are
-    * top: smallest rank if ascending
-    * bottom: smallest rank if descending
-ascending : boolean, default True
-    False for ranks by high (1) to low (N)
-pct : boolean, default False
-    Computes percentage rank of data

-Returns
--------
-ranks : same type as caller
- -
reindex_like(self, other, method=None, copy=True, limit=None, tolerance=None)
Return an object with matching indices to myself.

-Parameters
-----------
-other : Object
-method : string or None
-copy : boolean, default True
-limit : int, default None
-    Maximum number of consecutive labels to fill for inexact matches.
-tolerance : optional
-    Maximum distance between labels of the other object and this
-    object for inexact matches.

-    .. versionadded:: 0.17.0

-Notes
------
-Like calling s.reindex(index=other.index, columns=other.columns,
-                       method=...)

-Returns
--------
-reindexed : same as input
- -
rename_axis(self, mapper, axis=0, copy=True, inplace=False)
Alter index and / or columns using input function or functions.
-A scalar or list-like for ``mapper`` will alter the ``Index.name``
-or ``MultiIndex.names`` attribute.
-A function or dict for ``mapper`` will alter the labels.
-Function / dict values must be unique (1-to-1). Labels not contained in
-a dict / Series will be left as-is.

-Parameters
-----------
-mapper : scalar, list-like, dict-like or function, optional
-axis : int or string, default 0
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False

-Returns
--------
-renamed : type of caller

-See Also
---------
-pandas.NDFrame.rename
-pandas.Index.rename

-Examples
---------
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename_axis("foo")  # scalar, alters df.index.name
-     A  B
-foo
-0    1  4
-1    2  5
-2    3  6
->>> df.rename_axis(lambda x: 2 * x)  # function: alters labels
-   A  B
-0  1  4
-2  2  5
-4  3  6
->>> df.rename_axis({"A": "ehh", "C": "see"}, axis="columns")  # mapping
-   ehh  B
-0    1  4
-1    2  5
-2    3  6
- -
replace(self, to_replace=None, value=None, inplace=False, limit=None, regex=False, method='pad', axis=None)
Replace values given in 'to_replace' with 'value'.

-Parameters
-----------
-to_replace : str, regex, list, dict, Series, numeric, or None

-    * str or regex:

-        - str: string exactly matching `to_replace` will be replaced
-          with `value`
-        - regex: regexs matching `to_replace` will be replaced with
-          `value`

-    * list of str, regex, or numeric:

-        - First, if `to_replace` and `value` are both lists, they
-          **must** be the same length.
-        - Second, if ``regex=True`` then all of the strings in **both**
-          lists will be interpreted as regexs otherwise they will match
-          directly. This doesn't matter much for `value` since there
-          are only a few possible substitution regexes you can use.
-        - str and regex rules apply as above.

-    * dict:

-        - Nested dictionaries, e.g., {'a': {'b': nan}}, are read as
-          follows: look in column 'a' for the value 'b' and replace it
-          with nan. You can nest regular expressions as well. Note that
-          column names (the top-level dictionary keys in a nested
-          dictionary) **cannot** be regular expressions.
-        - Keys map to column names and values map to substitution
-          values. You can treat this as a special case of passing two
-          lists except that you are specifying the column to search in.

-    * None:

-        - This means that the ``regex`` argument must be a string,
-          compiled regular expression, or list, dict, ndarray or Series
-          of such elements. If `value` is also ``None`` then this
-          **must** be a nested dictionary or ``Series``.

-    See the examples section for examples of each of these.
-value : scalar, dict, list, str, regex, default None
-    Value to use to fill holes (e.g. 0), alternately a dict of values
-    specifying which value to use for each column (columns not in the
-    dict will not be filled). Regular expressions, strings and lists or
-    dicts of such objects are also allowed.
-inplace : boolean, default False
-    If True, in place. Note: this will modify any
-    other views on this object (e.g. a column form a DataFrame).
-    Returns the caller if this is True.
-limit : int, default None
-    Maximum size gap to forward or backward fill
-regex : bool or same types as `to_replace`, default False
-    Whether to interpret `to_replace` and/or `value` as regular
-    expressions. If this is ``True`` then `to_replace` *must* be a
-    string. Otherwise, `to_replace` must be ``None`` because this
-    parameter will be interpreted as a regular expression or a list,
-    dict, or array of regular expressions.
-method : string, optional, {'pad', 'ffill', 'bfill'}
-    The method to use when for replacement, when ``to_replace`` is a
-    ``list``.

-See Also
---------
-NDFrame.reindex
-NDFrame.asfreq
-NDFrame.fillna

-Returns
--------
-filled : NDFrame

-Raises
-------
-AssertionError
-    * If `regex` is not a ``bool`` and `to_replace` is not ``None``.
-TypeError
-    * If `to_replace` is a ``dict`` and `value` is not a ``list``,
-      ``dict``, ``ndarray``, or ``Series``
-    * If `to_replace` is ``None`` and `regex` is not compilable into a
-      regular expression or is a list, dict, ndarray, or Series.
-ValueError
-    * If `to_replace` and `value` are ``list`` s or ``ndarray`` s, but
-      they are not the same length.

-Notes
------
-* Regex substitution is performed under the hood with ``re.sub``. The
-  rules for substitution for ``re.sub`` are the same.
-* Regular expressions will only substitute on strings, meaning you
-  cannot provide, for example, a regular expression matching floating
-  point numbers and expect the columns in your frame that have a
-  numeric dtype to be matched. However, if those floating point numbers
-  *are* strings, then you can do this.
-* This method has *a lot* of options. You are encouraged to experiment
-  and play with this method to gain intuition about how it works.
- -
resample(self, rule, how=None, axis=0, fill_method=None, closed=None, label=None, convention='start', kind=None, loffset=None, limit=None, base=0, on=None, level=None)
Convenience method for frequency conversion and resampling of time
-series.  Object must have a datetime-like index (DatetimeIndex,
-PeriodIndex, or TimedeltaIndex), or pass datetime-like values
-to the on or level keyword.

-Parameters
-----------
-rule : string
-    the offset string or object representing target conversion
-axis : int, optional, default 0
-closed : {'right', 'left'}
-    Which side of bin interval is closed
-label : {'right', 'left'}
-    Which bin edge label to label bucket with
-convention : {'start', 'end', 's', 'e'}
-loffset : timedelta
-    Adjust the resampled time labels
-base : int, default 0
-    For frequencies that evenly subdivide 1 day, the "origin" of the
-    aggregated intervals. For example, for '5min' frequency, base could
-    range from 0 through 4. Defaults to 0
-on : string, optional
-    For a DataFrame, column to use instead of index for resampling.
-    Column must be datetime-like.

-    .. versionadded:: 0.19.0

-level : string or int, optional
-    For a MultiIndex, level (name or number) to use for
-    resampling.  Level must be datetime-like.

-    .. versionadded:: 0.19.0

-Notes
------
-To learn more about the offset strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-Examples
---------

-Start by creating a series with 9 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=9, freq='T')
->>> series = pd.Series(range(9), index=index)
->>> series
-2000-01-01 00:00:00    0
-2000-01-01 00:01:00    1
-2000-01-01 00:02:00    2
-2000-01-01 00:03:00    3
-2000-01-01 00:04:00    4
-2000-01-01 00:05:00    5
-2000-01-01 00:06:00    6
-2000-01-01 00:07:00    7
-2000-01-01 00:08:00    8
-Freq: T, dtype: int64

-Downsample the series into 3 minute bins and sum the values
-of the timestamps falling into a bin.

->>> series.resample('3T').sum()
-2000-01-01 00:00:00     3
-2000-01-01 00:03:00    12
-2000-01-01 00:06:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but label each
-bin using the right edge instead of the left. Please note that the
-value in the bucket used as the label is not included in the bucket,
-which it labels. For example, in the original series the
-bucket ``2000-01-01 00:03:00`` contains the value 3, but the summed
-value in the resampled bucket with the label``2000-01-01 00:03:00``
-does not include 3 (if it did, the summed value would be 6, not 3).
-To include this value close the right side of the bin interval as
-illustrated in the example below this one.

->>> series.resample('3T', label='right').sum()
-2000-01-01 00:03:00     3
-2000-01-01 00:06:00    12
-2000-01-01 00:09:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but close the right
-side of the bin interval.

->>> series.resample('3T', label='right', closed='right').sum()
-2000-01-01 00:00:00     0
-2000-01-01 00:03:00     6
-2000-01-01 00:06:00    15
-2000-01-01 00:09:00    15
-Freq: 3T, dtype: int64

-Upsample the series into 30 second bins.

->>> series.resample('30S').asfreq()[0:5] #select first 5 rows
-2000-01-01 00:00:00   0.0
-2000-01-01 00:00:30   NaN
-2000-01-01 00:01:00   1.0
-2000-01-01 00:01:30   NaN
-2000-01-01 00:02:00   2.0
-Freq: 30S, dtype: float64

-Upsample the series into 30 second bins and fill the ``NaN``
-values using the ``pad`` method.

->>> series.resample('30S').pad()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    0
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    1
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Upsample the series into 30 second bins and fill the
-``NaN`` values using the ``bfill`` method.

->>> series.resample('30S').bfill()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    1
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    2
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Pass a custom function via ``apply``

->>> def custom_resampler(array_like):
-...     return np.sum(array_like)+5

->>> series.resample('3T').apply(custom_resampler)
-2000-01-01 00:00:00     8
-2000-01-01 00:03:00    17
-2000-01-01 00:06:00    26
-Freq: 3T, dtype: int64

-For DataFrame objects, the keyword ``on`` can be used to specify the
-column instead of the index for resampling.

->>> df = pd.DataFrame(data=9*[range(4)], columns=['a', 'b', 'c', 'd'])
->>> df['time'] = pd.date_range('1/1/2000', periods=9, freq='T')
->>> df.resample('3T', on='time').sum()
-                     a  b  c  d
-time
-2000-01-01 00:00:00  0  3  6  9
-2000-01-01 00:03:00  0  3  6  9
-2000-01-01 00:06:00  0  3  6  9

-For a DataFrame with MultiIndex, the keyword ``level`` can be used to
-specify on level the resampling needs to take place.

->>> time = pd.date_range('1/1/2000', periods=5, freq='T')
->>> df2 = pd.DataFrame(data=10*[range(4)],
-                       columns=['a', 'b', 'c', 'd'],
-                       index=pd.MultiIndex.from_product([time, [1, 2]])
-                       )
->>> df2.resample('3T', level=0).sum()
-                     a  b   c   d
-2000-01-01 00:00:00  0  6  12  18
-2000-01-01 00:03:00  0  4   8  12
- -
sample(self, n=None, frac=None, replace=False, weights=None, random_state=None, axis=None)
Returns a random sample of items from an axis of object.

-.. versionadded:: 0.16.1

-Parameters
-----------
-n : int, optional
-    Number of items from axis to return. Cannot be used with `frac`.
-    Default = 1 if `frac` = None.
-frac : float, optional
-    Fraction of axis items to return. Cannot be used with `n`.
-replace : boolean, optional
-    Sample with or without replacement. Default = False.
-weights : str or ndarray-like, optional
-    Default 'None' results in equal probability weighting.
-    If passed a Series, will align with target object on index. Index
-    values in weights not found in sampled object will be ignored and
-    index values in sampled object not in weights will be assigned
-    weights of zero.
-    If called on a DataFrame, will accept the name of a column
-    when axis = 0.
-    Unless weights are a Series, weights must be same length as axis
-    being sampled.
-    If weights do not sum to 1, they will be normalized to sum to 1.
-    Missing values in the weights column will be treated as zero.
-    inf and -inf values not allowed.
-random_state : int or numpy.random.RandomState, optional
-    Seed for the random number generator (if int), or numpy RandomState
-    object.
-axis : int or string, optional
-    Axis to sample. Accepts axis number or name. Default is stat axis
-    for given data type (0 for Series and DataFrames, 1 for Panels).

-Returns
--------
-A new object of same type as caller.

-Examples
---------

-Generate an example ``Series`` and ``DataFrame``:

->>> s = pd.Series(np.random.randn(50))
->>> s.head()
-0   -0.038497
-1    1.820773
-2   -0.972766
-3   -1.598270
-4   -1.095526
-dtype: float64
->>> df = pd.DataFrame(np.random.randn(50, 4), columns=list('ABCD'))
->>> df.head()
-          A         B         C         D
-0  0.016443 -2.318952 -0.566372 -1.028078
-1 -1.051921  0.438836  0.658280 -0.175797
-2 -1.243569 -0.364626 -0.215065  0.057736
-3  1.768216  0.404512 -0.385604 -1.457834
-4  1.072446 -1.137172  0.314194 -0.046661

-Next extract a random sample from both of these objects...

-3 random elements from the ``Series``:

->>> s.sample(n=3)
-27   -0.994689
-55   -1.049016
-67   -0.224565
-dtype: float64

-And a random 10% of the ``DataFrame`` with replacement:

->>> df.sample(frac=0.1, replace=True)
-           A         B         C         D
-35  1.981780  0.142106  1.817165 -0.290805
-49 -1.336199 -0.448634 -0.789640  0.217116
-40  0.823173 -0.078816  1.009536  1.015108
-15  1.421154 -0.055301 -1.922594 -0.019696
-6  -0.148339  0.832938  1.787600 -1.383767
- -
select(self, crit, axis=0)
Return data corresponding to axis labels matching criteria

-Parameters
-----------
-crit : function
-    To be called on each index (label). Should return True or False
-axis : int

-Returns
--------
-selection : type of caller
- -
set_axis(self, axis, labels)
public verson of axis assignment
- -
slice_shift(self, periods=1, axis=0)
Equivalent to `shift` without copying data. The shifted data will
-not include the dropped periods and the shifted axis will be smaller
-than the original.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative

-Notes
------
-While the `slice_shift` is faster than `shift`, you may pay for it
-later during alignment.

-Returns
--------
-shifted : same type as caller
- -
squeeze(self, axis=None)
Squeeze length 1 dimensions.

-Parameters
-----------
-axis : None, integer or string axis name, optional
-    The axis to squeeze if 1-sized.

-    .. versionadded:: 0.20.0

-Returns
--------
-scalar if 1-sized, else original object
- -
swapaxes(self, axis1, axis2, copy=True)
Interchange axes and swap values axes appropriately

-Returns
--------
-y : same as input
- -
tail(self, n=5)
Returns last n rows
- -
take(self, indices, axis=0, convert=True, is_copy=True, **kwargs)
Analogous to ndarray.take

-Parameters
-----------
-indices : list / array of ints
-axis : int, default 0
-convert : translate neg to pos indices (default)
-is_copy : mark the returned frame as a copy

-Returns
--------
-taken : type of caller
- -
to_clipboard(self, excel=None, sep=None, **kwargs)
Attempt to write text representation of object to the system clipboard
-This can be pasted into Excel, for example.

-Parameters
-----------
-excel : boolean, defaults to True
-        if True, use the provided separator, writing in a csv
-        format for allowing easy pasting into excel.
-        if False, write a string representation of the object
-        to the clipboard
-sep : optional, defaults to tab
-other keywords are passed to to_csv

-Notes
------
-Requirements for your platform
-  - Linux: xclip, or xsel (with gtk or PyQt4 modules)
-  - Windows: none
-  - OS X: none
- -
to_dense(self)
Return dense representation of NDFrame (as opposed to sparse)
- -
to_hdf(self, path_or_buf, key, **kwargs)
Write the contained data to an HDF5 file using HDFStore.

-Parameters
-----------
-path_or_buf : the path (string) or HDFStore object
-key : string
-    identifier for the group in the store
-mode : optional, {'a', 'w', 'r+'}, default 'a'

-  ``'w'``
-      Write; a new file is created (an existing file with the same
-      name would be deleted).
-  ``'a'``
-      Append; an existing file is opened for reading and writing,
-      and if the file does not exist it is created.
-  ``'r+'``
-      It is similar to ``'a'``, but the file must already exist.
-format : 'fixed(f)|table(t)', default is 'fixed'
-    fixed(f) : Fixed format
-               Fast writing/reading. Not-appendable, nor searchable
-    table(t) : Table format
-               Write as a PyTables Table structure which may perform
-               worse but allow more flexible operations like searching
-               / selecting subsets of the data
-append : boolean, default False
-    For Table formats, append the input data to the existing
-data_columns :  list of columns, or True, default None
-    List of columns to create as indexed data columns for on-disk
-    queries, or True to use all columns. By default only the axes
-    of the object are indexed. See `here
-    <http://pandas.pydata.org/pandas-docs/stable/io.html#query-via-data-columns>`__.

-    Applicable only to format='table'.
-complevel : int, 1-9, default 0
-    If a complib is specified compression will be applied
-    where possible
-complib : {'zlib', 'bzip2', 'lzo', 'blosc', None}, default None
-    If complevel is > 0 apply compression to objects written
-    in the store wherever possible
-fletcher32 : bool, default False
-    If applying compression use the fletcher32 checksum
-dropna : boolean, default False.
-    If true, ALL nan rows will not be written to store.
- -
to_json(self, path_or_buf=None, orient=None, date_format=None, double_precision=10, force_ascii=True, date_unit='ms', default_handler=None, lines=False)
Convert the object to a JSON string.

-Note NaN's and None will be converted to null and datetime objects
-will be converted to UNIX timestamps.

-Parameters
-----------
-path_or_buf : the path or buffer to write the result string
-    if this is None, return a StringIO of the converted string
-orient : string

-    * Series

-      - default is 'index'
-      - allowed values are: {'split','records','index'}

-    * DataFrame

-      - default is 'columns'
-      - allowed values are:
-        {'split','records','index','columns','values'}

-    * The format of the JSON string

-      - split : dict like
-        {index -> [index], columns -> [columns], data -> [values]}
-      - records : list like
-        [{column -> value}, ... , {column -> value}]
-      - index : dict like {index -> {column -> value}}
-      - columns : dict like {column -> {index -> value}}
-      - values : just the values array
-      - table : dict like {'schema': {schema}, 'data': {data}}
-        describing the data, and the data component is
-        like ``orient='records'``.

-        .. versionchanged:: 0.20.0

-date_format : {None, 'epoch', 'iso'}
-    Type of date conversion. `epoch` = epoch milliseconds,
-    `iso` = ISO8601. The default depends on the `orient`. For
-    `orient='table'`, the default is `'iso'`. For all other orients,
-    the default is `'epoch'`.
-double_precision : The number of decimal places to use when encoding
-    floating point values, default 10.
-force_ascii : force encoded string to be ASCII, default True.
-date_unit : string, default 'ms' (milliseconds)
-    The time unit to encode to, governs timestamp and ISO8601
-    precision.  One of 's', 'ms', 'us', 'ns' for second, millisecond,
-    microsecond, and nanosecond respectively.
-default_handler : callable, default None
-    Handler to call if object cannot otherwise be converted to a
-    suitable format for JSON. Should receive a single argument which is
-    the object to convert and return a serialisable object.
-lines : boolean, default False
-    If 'orient' is 'records' write out line delimited json format. Will
-    throw ValueError if incorrect 'orient' since others are not list
-    like.

-    .. versionadded:: 0.19.0

-Returns
--------
-same type as input object with filtered info axis

-See Also
---------
-pd.read_json

-Examples
---------

->>> df = pd.DataFrame([['a', 'b'], ['c', 'd']],
-...                   index=['row 1', 'row 2'],
-...                   columns=['col 1', 'col 2'])
->>> df.to_json(orient='split')
-'{"columns":["col 1","col 2"],
-  "index":["row 1","row 2"],
-  "data":[["a","b"],["c","d"]]}'

-Encoding/decoding a Dataframe using ``'index'`` formatted JSON:

->>> df.to_json(orient='index')
-'{"row 1":{"col 1":"a","col 2":"b"},"row 2":{"col 1":"c","col 2":"d"}}'

-Encoding/decoding a Dataframe using ``'records'`` formatted JSON.
-Note that index labels are not preserved with this encoding.

->>> df.to_json(orient='records')
-'[{"col 1":"a","col 2":"b"},{"col 1":"c","col 2":"d"}]'

-Encoding with Table Schema

->>> df.to_json(orient='table')
-'{"schema": {"fields": [{"name": "index", "type": "string"},
-                        {"name": "col 1", "type": "string"},
-                        {"name": "col 2", "type": "string"}],
-             "primaryKey": "index",
-             "pandas_version": "0.20.0"},
-  "data": [{"index": "row 1", "col 1": "a", "col 2": "b"},
-           {"index": "row 2", "col 1": "c", "col 2": "d"}]}'
- -
to_msgpack(self, path_or_buf=None, encoding='utf-8', **kwargs)
msgpack (serialize) object to input file path

-THIS IS AN EXPERIMENTAL LIBRARY and the storage format
-may not be stable until a future release.

-Parameters
-----------
-path : string File path, buffer-like, or None
-    if None, return generated string
-append : boolean whether to append to an existing msgpack
-    (default is False)
-compress : type of compressor (zlib or blosc), default to None (no
-    compression)
- -
to_pickle(self, path, compression='infer')
Pickle (serialize) object to input file path.

-Parameters
-----------
-path : string
-    File path
-compression : {'infer', 'gzip', 'bz2', 'xz', None}, default 'infer'
-    a string representing the compression to use in the output file

-    .. versionadded:: 0.20.0
- -
to_sql(self, name, con, flavor=None, schema=None, if_exists='fail', index=True, index_label=None, chunksize=None, dtype=None)
Write records stored in a DataFrame to a SQL database.

-Parameters
-----------
-name : string
-    Name of SQL table
-con : SQLAlchemy engine or DBAPI2 connection (legacy mode)
-    Using SQLAlchemy makes it possible to use any DB supported by that
-    library. If a DBAPI2 object, only sqlite3 is supported.
-flavor : 'sqlite', default None
-    DEPRECATED: this parameter will be removed in a future version,
-    as 'sqlite' is the only supported option if SQLAlchemy is not
-    installed.
-schema : string, default None
-    Specify the schema (if database flavor supports this). If None, use
-    default schema.
-if_exists : {'fail', 'replace', 'append'}, default 'fail'
-    - fail: If table exists, do nothing.
-    - replace: If table exists, drop it, recreate it, and insert data.
-    - append: If table exists, insert data. Create if does not exist.
-index : boolean, default True
-    Write DataFrame index as a column.
-index_label : string or sequence, default None
-    Column label for index column(s). If None is given (default) and
-    `index` is True, then the index names are used.
-    A sequence should be given if the DataFrame uses MultiIndex.
-chunksize : int, default None
-    If not None, then rows will be written in batches of this size at a
-    time.  If None, all rows will be written at once.
-dtype : dict of column name to SQL type, default None
-    Optional specifying the datatype for columns. The SQL type should
-    be a SQLAlchemy type, or a string for sqlite3 fallback connection.
- -
to_xarray(self)
Return an xarray object from the pandas object.

-Returns
--------
-a DataArray for a Series
-a Dataset for a DataFrame
-a DataArray for higher dims

-Examples
---------
->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)})
->>> df
-   A    B    C
-0  1  foo  4.0
-1  1  bar  5.0
-2  2  foo  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (index: 3)
-Coordinates:
-  * index    (index) int64 0 1 2
-Data variables:
-    A        (index) int64 1 1 2
-    B        (index) object 'foo' 'bar' 'foo'
-    C        (index) float64 4.0 5.0 6.0

->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)}
-                     ).set_index(['B','A'])
->>> df
-         C
-B   A
-foo 1  4.0
-bar 1  5.0
-foo 2  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (A: 2, B: 2)
-Coordinates:
-  * B        (B) object 'bar' 'foo'
-  * A        (A) int64 1 2
-Data variables:
-    C        (B, A) float64 5.0 nan 4.0 6.0

->>> p = pd.Panel(np.arange(24).reshape(4,3,2),
-                 items=list('ABCD'),
-                 major_axis=pd.date_range('20130101', periods=3),
-                 minor_axis=['first', 'second'])
->>> p
-<class 'pandas.core.panel.Panel'>
-Dimensions: 4 (items) x 3 (major_axis) x 2 (minor_axis)
-Items axis: A to D
-Major_axis axis: 2013-01-01 00:00:00 to 2013-01-03 00:00:00
-Minor_axis axis: first to second

->>> p.to_xarray()
-<xarray.DataArray (items: 4, major_axis: 3, minor_axis: 2)>
-array([[[ 0,  1],
-        [ 2,  3],
-        [ 4,  5]],
-       [[ 6,  7],
-        [ 8,  9],
-        [10, 11]],
-       [[12, 13],
-        [14, 15],
-        [16, 17]],
-       [[18, 19],
-        [20, 21],
-        [22, 23]]])
-Coordinates:
-  * items       (items) object 'A' 'B' 'C' 'D'
-  * major_axis  (major_axis) datetime64[ns] 2013-01-01 2013-01-02 2013-01-03  # noqa
-  * minor_axis  (minor_axis) object 'first' 'second'

-Notes
------
-See the `xarray docs <http://xarray.pydata.org/en/stable/>`__
- -
truncate(self, before=None, after=None, axis=None, copy=True)
Truncates a sorted NDFrame before and/or after some particular
-index value. If the axis contains only datetime values, before/after
-parameters are converted to datetime values.

-Parameters
-----------
-before : date
-    Truncate before index value
-after : date
-    Truncate after index value
-axis : the truncation axis, defaults to the stat axis
-copy : boolean, default is True,
-    return a copy of the truncated section

-Returns
--------
-truncated : type of caller
- -
tshift(self, periods=1, freq=None, axis=0)
Shift the time index, using the index's frequency if available.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, default None
-    Increment to use from the tseries module or time rule (e.g. 'EOM')
-axis : int or basestring
-    Corresponds to the axis that contains the Index

-Notes
------
-If freq is not specified then tries to use the freq or inferred_freq
-attributes of the index. If neither of those attributes exist, a
-ValueError is thrown

-Returns
--------
-shifted : NDFrame
- -
tz_convert(self, tz, axis=0, level=None, copy=True)
Convert tz-aware axis to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to convert
-level : int, str, default None
-    If axis ia a MultiIndex, convert a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data

-Returns
--------

-Raises
-------
-TypeError
-    If the axis is tz-naive.
- -
tz_localize(self, tz, axis=0, level=None, copy=True, ambiguous='raise')
Localize tz-naive TimeSeries to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to localize
-level : int, str, default None
-    If axis ia a MultiIndex, localize a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data
-ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise'
-    - 'infer' will attempt to infer fall dst-transition hours based on
-      order
-    - bool-ndarray where True signifies a DST time, False designates
-      a non-DST time (note that this flag is only applicable for
-      ambiguous times)
-    - 'NaT' will return NaT where there are ambiguous times
-    - 'raise' will raise an AmbiguousTimeError if there are ambiguous
-      times
-infer_dst : boolean, default False (DEPRECATED)
-    Attempt to infer fall dst-transition hours based on order

-Returns
--------

-Raises
-------
-TypeError
-    If the TimeSeries is tz-aware and tz is not None.
- -
where(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is True and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The where method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``True`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``where`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.mask`
- -
xs(self, key, axis=0, level=None, drop_level=True)
Returns a cross-section (row(s) or column(s)) from the
-Series/DataFrame. Defaults to cross-section on the rows (axis=0).

-Parameters
-----------
-key : object
-    Some label contained in the index, or partially in a MultiIndex
-axis : int, default 0
-    Axis to retrieve cross-section on
-level : object, defaults to first n levels (n=1 or len(key))
-    In case of a key partially contained in a MultiIndex, indicate
-    which levels are used. Levels can be referred by label or position.
-drop_level : boolean, default True
-    If False, returns object with same levels as self.

-Examples
---------
->>> df
-   A  B  C
-a  4  5  2
-b  4  0  9
-c  9  7  3
->>> df.xs('a')
-A    4
-B    5
-C    2
-Name: a
->>> df.xs('C', axis=1)
-a    2
-b    9
-c    3
-Name: C

->>> df
-                    A  B  C  D
-first second third
-bar   one    1      4  1  8  9
-      two    1      7  5  5  0
-baz   one    1      6  6  8  0
-      three  2      5  3  5  3
->>> df.xs(('baz', 'three'))
-       A  B  C  D
-third
-2      5  3  5  3
->>> df.xs('one', level=1)
-             A  B  C  D
-first third
-bar   1      4  1  8  9
-baz   1      6  6  8  0
->>> df.xs(('baz', 2), level=[0, 'third'])
-        A  B  C  D
-second
-three   5  3  5  3

-Returns
--------
-xs : Series or DataFrame

-Notes
------
-xs is only for getting, not setting values.

-MultiIndex Slicers is a generic way to get/set values on any level or
-levels.  It is a superset of xs functionality, see
-:ref:`MultiIndex Slicers <advanced.mi_slicers>`
- -
-Data descriptors inherited from pandas.core.generic.NDFrame:
-
at
-
Fast label-based scalar accessor

-Similarly to ``loc``, ``at`` provides **label** based scalar lookups.
-You can also set using these indexers.
-
-
blocks
-
Internal property, property synonym for as_blocks()
-
-
dtypes
-
Return the dtypes in this object.
-
-
empty
-
True if NDFrame is entirely empty [no items], meaning any of the
-axes are of length 0.

-Notes
------
-If NDFrame contains only NaNs, it is still not considered empty. See
-the example below.

-Examples
---------
-An example of an actual empty DataFrame. Notice the index is empty:

->>> df_empty = pd.DataFrame({'A' : []})
->>> df_empty
-Empty DataFrame
-Columns: [A]
-Index: []
->>> df_empty.empty
-True

-If we only have NaNs in our DataFrame, it is not considered empty! We
-will need to drop the NaNs to make the DataFrame empty:

->>> df = pd.DataFrame({'A' : [np.nan]})
->>> df
-    A
-0 NaN
->>> df.empty
-False
->>> df.dropna().empty
-True

-See also
---------
-pandas.Series.dropna
-pandas.DataFrame.dropna
-
-
ftypes
-
Return the ftypes (indication of sparse/dense and dtype)
-in this object.
-
-
iat
-
Fast integer location scalar accessor.

-Similarly to ``iloc``, ``iat`` provides **integer** based lookups.
-You can also set using these indexers.
-
-
iloc
-
Purely integer-location based indexing for selection by position.

-``.iloc[]`` is primarily integer position based (from ``0`` to
-``length-1`` of the axis), but may also be used with a boolean
-array.

-Allowed inputs are:

-- An integer, e.g. ``5``.
-- A list or array of integers, e.g. ``[4, 3, 0]``.
-- A slice object with ints, e.g. ``1:7``.
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.iloc`` will raise ``IndexError`` if a requested indexer is
-out-of-bounds, except *slice* indexers which allow out-of-bounds
-indexing (this conforms with python/numpy *slice* semantics).

-See more at :ref:`Selection by Position <indexing.integer>`
-
-
ix
-
A primarily label-location based indexer, with integer position
-fallback.

-``.ix[]`` supports mixed integer and label based access. It is
-primarily label based, but will fall back to integer positional
-access unless the corresponding axis is of integer type.

-``.ix`` is the most general indexer and will support any of the
-inputs in ``.loc`` and ``.iloc``. ``.ix`` also supports floating
-point label schemes. ``.ix`` is exceptionally useful when dealing
-with mixed positional and label based hierachical indexes.

-However, when an axis is integer based, ONLY label based access
-and not positional access is supported. Thus, in such cases, it's
-usually better to be explicit and use ``.iloc`` or ``.loc``.

-See more at :ref:`Advanced Indexing <advanced>`.
-
-
loc
-
Purely label-location based indexer for selection by label.

-``.loc[]`` is primarily label based, but may also be used with a
-boolean array.

-Allowed inputs are:

-- A single label, e.g. ``5`` or ``'a'``, (note that ``5`` is
-  interpreted as a *label* of the index, and **never** as an
-  integer position along the index).
-- A list or array of labels, e.g. ``['a', 'b', 'c']``.
-- A slice object with labels, e.g. ``'a':'f'`` (note that contrary
-  to usual python slices, **both** the start and the stop are included!).
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.loc`` will raise a ``KeyError`` when the items are not found.

-See more at :ref:`Selection by Label <indexing.label>`
-
-
ndim
-
Number of axes / array dimensions
-
-
size
-
number of elements in the NDFrame
-
-
values
-
Numpy representation of NDFrame

-Notes
------
-The dtype will be a lower-common-denominator dtype (implicit
-upcasting); that is to say if the dtypes (even of numeric types)
-are mixed, the one that accommodates all will be chosen. Use this
-with care if you are not dealing with the blocks.

-e.g. If the dtypes are float16 and float32, dtype will be upcast to
-float32.  If dtypes are int32 and uint8, dtype will be upcast to
-int32. By numpy.find_common_type convention, mixing int64 and uint64
-will result in a flot64 dtype.
-
-
-Data and other attributes inherited from pandas.core.generic.NDFrame:
-
is_copy = None
- -
-Methods inherited from pandas.core.base.PandasObject:
-
__dir__(self)
Provide method name lookup and completion
-Only provide 'public' methods
- -
__sizeof__(self)
Generates the total memory usage for a object that returns
-either a value or Series of values
- -
-Methods inherited from pandas.core.base.StringMixin:
-
__bytes__(self)
Return a string representation for a particular object.

-Invoked by bytes(obj) in py3 only.
-Yields a bytestring in both py2/py3.
- -
__repr__(self)
Return a string representation for a particular object.

-Yields Bytestring in Py2, Unicode String in py3.
- -
__str__(self)
Return a string representation for a particular Object

-Invoked by str(df) in both py2/py3.
-Yields Bytestring in Py2, Unicode String in py3.
- -
-Data descriptors inherited from pandas.core.base.StringMixin:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-

- - - - - - - -
 
-class SweepSeries(MySeries)
   Represents a mapping from parameter values to metrics.
 
 
Method resolution order:
-
SweepSeries
-
MySeries
-
pandas.core.series.Series
-
pandas.core.base.IndexOpsMixin
-
pandas.core.strings.StringAccessorMixin
-
pandas.core.generic.NDFrame
-
pandas.core.base.PandasObject
-
pandas.core.base.StringMixin
-
pandas.core.base.SelectionMixin
-
builtins.object
-
-
-Methods inherited from MySeries:
-
__init__(self, *args, **kwargs)
Initialize a Series.

-Note: this cleans up a weird Series behavior, which is
-that Series() and Series([]) yield different results.
-See: https://github.com/pandas-dev/pandas/issues/16737
- -
set(self, **kwargs)
Uses keyword arguments to update the Series in place.

-Example: series.update(a=1, b=2)
- -
-Methods inherited from pandas.core.series.Series:
-
__add__ = wrapper(left, right, name='__add__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f2f0>)
- -
__and__ = wrapper(self, other)
- -
__array__(self, result=None)
the array interface, return my values
- -
__array_prepare__(self, result, context=None)
Gets called prior to a ufunc
- -
__array_wrap__(self, result, context=None)
Gets called after a ufunc
- -
__div__ = wrapper(left, right, name='__truediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f9d8>)
- -
__divmod__ = wrapper(left, right, name='__divmod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca343bf8>)
- -
__eq__ = wrapper(self, other, axis=None)
- -
__float__ = wrapper(self)
- -
__floordiv__ = wrapper(left, right, name='__floordiv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fb70>)
- -
__ge__ = wrapper(self, other, axis=None)
- -
__getitem__(self, key)
- -
__gt__ = wrapper(self, other, axis=None)
- -
__iadd__ = f(self, other)
- -
__imul__ = f(self, other)
- -
__int__ = wrapper(self)
- -
__ipow__ = f(self, other)
- -
__isub__ = f(self, other)
- -
__iter__(self)
provide iteration over the values of the Series
-box values if necessary
- -
__itruediv__ = f(self, other)
- -
__le__ = wrapper(self, other, axis=None)
- -
__len__(self)
return the length of the Series
- -
__long__ = wrapper(self)
- -
__lt__ = wrapper(self, other, axis=None)
- -
__mod__ = wrapper(left, right, name='__mod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fd08>)
- -
__mul__ = wrapper(left, right, name='__mul__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f840>)
- -
__ne__ = wrapper(self, other, axis=None)
- -
__or__ = wrapper(self, other)
- -
__pow__ = wrapper(left, right, name='__pow__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fea0>)
- -
__radd__ = wrapper(left, right, name='__radd__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f510>)
- -
__rand__ = wrapper(self, other)
- -
__rdiv__ = wrapper(left, right, name='__rtruediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342510>)
- -
__rfloordiv__ = wrapper(left, right, name='__rfloordiv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342730>)
- -
__rmod__ = wrapper(left, right, name='__rmod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342b70>)
- -
__rmul__ = wrapper(left, right, name='__rmul__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca3420d0>)
- -
__ror__ = wrapper(self, other)
- -
__rpow__ = wrapper(left, right, name='__rpow__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342950>)
- -
__rsub__ = wrapper(left, right, name='__rsub__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca3422f0>)
- -
__rtruediv__ = wrapper(left, right, name='__rtruediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342510>)
- -
__rxor__ = wrapper(self, other)
- -
__setitem__(self, key, value)
- -
__sub__ = wrapper(left, right, name='__sub__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f6a8>)
- -
__truediv__ = wrapper(left, right, name='__truediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f9d8>)
- -
__unicode__(self)
Return a string representation for a particular DataFrame

-Invoked by unicode(df) in py2 only. Yields a Unicode String in both
-py2/py3.
- -
__xor__ = wrapper(self, other)
- -
add(self, other, level=None, fill_value=None, axis=0)
Addition of series and other, element-wise (binary operator `add`).

-Equivalent to ``series + other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.radd
- -
agg = aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a Series or when passed to Series.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : Series

-Examples
---------

->>> s = Series(np.random.randn(10))

->>> s.agg('min')
--1.3018049988556679

->>> s.agg(['min', 'max'])
-min   -1.301805
-max    1.127688
-dtype: float64

-See also
---------
-pandas.Series.apply
-pandas.Series.transform
- -
aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a Series or when passed to Series.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : Series

-Examples
---------

->>> s = Series(np.random.randn(10))

->>> s.agg('min')
--1.3018049988556679

->>> s.agg(['min', 'max'])
-min   -1.301805
-max    1.127688
-dtype: float64

-See also
---------
-pandas.Series.apply
-pandas.Series.transform
- -
align(self, other, join='outer', axis=None, level=None, copy=True, fill_value=None, method=None, limit=None, fill_axis=0, broadcast_axis=None)
Align two object on their axes with the
-specified join method for each axis Index

-Parameters
-----------
-other : DataFrame or Series
-join : {'outer', 'inner', 'left', 'right'}, default 'outer'
-axis : allowed axis of the other object, default None
-    Align on index (0), columns (1), or both (None)
-level : int or level name, default None
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-copy : boolean, default True
-    Always returns new objects. If copy=False and no reindexing is
-    required then original objects are returned.
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-method : str, default None
-limit : int, default None
-fill_axis : {0, 'index'}, default 0
-    Filling axis, method and limit
-broadcast_axis : {0, 'index'}, default None
-    Broadcast values along this axis, if aligning two objects of
-    different dimensions

-    .. versionadded:: 0.17.0

-Returns
--------
-(left, right) : (Series, type of other)
-    Aligned objects
- -
all(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether all elements are True over requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-all : scalar or Series (if level specified)
- -
any(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether any element is True over requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-any : scalar or Series (if level specified)
- -
append(self, to_append, ignore_index=False, verify_integrity=False)
Concatenate two or more Series.

-Parameters
-----------
-to_append : Series or list/tuple of Series
-ignore_index : boolean, default False
-    If True, do not use the index labels.

-    .. versionadded: 0.19.0

-verify_integrity : boolean, default False
-    If True, raise Exception on creating index with duplicates

-Returns
--------
-appended : Series

-Examples
---------
->>> s1 = pd.Series([1, 2, 3])
->>> s2 = pd.Series([4, 5, 6])
->>> s3 = pd.Series([4, 5, 6], index=[3,4,5])
->>> s1.append(s2)
-0    1
-1    2
-2    3
-0    4
-1    5
-2    6
-dtype: int64

->>> s1.append(s3)
-0    1
-1    2
-2    3
-3    4
-4    5
-5    6
-dtype: int64

-With `ignore_index` set to True:

->>> s1.append(s2, ignore_index=True)
-0    1
-1    2
-2    3
-3    4
-4    5
-5    6
-dtype: int64

-With `verify_integrity` set to True:

->>> s1.append(s2, verify_integrity=True)
-Traceback (most recent call last):
-...
-ValueError: Indexes have overlapping values: [0, 1, 2]
- -
apply(self, func, convert_dtype=True, args=(), **kwds)
Invoke function on values of Series. Can be ufunc (a NumPy function
-that applies to the entire Series) or a Python function that only works
-on single values

-Parameters
-----------
-func : function
-convert_dtype : boolean, default True
-    Try to find better dtype for elementwise function results. If
-    False, leave as dtype=object
-args : tuple
-    Positional arguments to pass to function in addition to the value
-Additional keyword arguments will be passed as keywords to the function

-Returns
--------
-y : Series or DataFrame if func returns a Series

-See also
---------
-Series.map: For element-wise operations
-Series.agg: only perform aggregating type operations
-Series.transform: only perform transformating type operations

-Examples
---------

-Create a series with typical summer temperatures for each city.

->>> import pandas as pd
->>> import numpy as np
->>> series = pd.Series([20, 21, 12], index=['London',
-... 'New York','Helsinki'])
->>> series
-London      20
-New York    21
-Helsinki    12
-dtype: int64

-Square the values by defining a function and passing it as an
-argument to ``apply()``.

->>> def square(x):
-...     return x**2
->>> series.apply(square)
-London      400
-New York    441
-Helsinki    144
-dtype: int64

-Square the values by passing an anonymous function as an
-argument to ``apply()``.

->>> series.apply(lambda x: x**2)
-London      400
-New York    441
-Helsinki    144
-dtype: int64

-Define a custom function that needs additional positional
-arguments and pass these additional arguments using the
-``args`` keyword.

->>> def subtract_custom_value(x, custom_value):
-...     return x-custom_value

->>> series.apply(subtract_custom_value, args=(5,))
-London      15
-New York    16
-Helsinki     7
-dtype: int64

-Define a custom function that takes keyword arguments
-and pass these arguments to ``apply``.

->>> def add_custom_values(x, **kwargs):
-...     for month in kwargs:
-...         x+=kwargs[month]
-...         return x

->>> series.apply(add_custom_values, june=30, july=20, august=25)
-London      95
-New York    96
-Helsinki    87
-dtype: int64

-Use a function from the Numpy library.

->>> series.apply(np.log)
-London      2.995732
-New York    3.044522
-Helsinki    2.484907
-dtype: float64
- -
argmax = idxmax(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of maximum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmax : Index of maximum of values

-Notes
------
-This method is the Series version of ``ndarray.argmax``.

-See Also
---------
-DataFrame.idxmax
-numpy.ndarray.argmax
- -
argmin = idxmin(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of minimum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmin : Index of minimum of values

-Notes
------
-This method is the Series version of ``ndarray.argmin``.

-See Also
---------
-DataFrame.idxmin
-numpy.ndarray.argmin
- -
argsort(self, axis=0, kind='quicksort', order=None)
Overrides ndarray.argsort. Argsorts the value, omitting NA/null values,
-and places the result in the same locations as the non-NA values

-Parameters
-----------
-axis : int (can only be zero)
-kind : {'mergesort', 'quicksort', 'heapsort'}, default 'quicksort'
-    Choice of sorting algorithm. See np.sort for more
-    information. 'mergesort' is the only stable algorithm
-order : ignored

-Returns
--------
-argsorted : Series, with -1 indicated where nan values are present

-See also
---------
-numpy.ndarray.argsort
- -
autocorr(self, lag=1)
Lag-N autocorrelation

-Parameters
-----------
-lag : int, default 1
-    Number of lags to apply before performing autocorrelation.

-Returns
--------
-autocorr : float
- -
between(self, left, right, inclusive=True)
Return boolean Series equivalent to left <= series <= right. NA values
-will be treated as False

-Parameters
-----------
-left : scalar
-    Left boundary
-right : scalar
-    Right boundary

-Returns
--------
-is_between : Series
- -
combine(self, other, func, fill_value=nan)
Perform elementwise binary operation on two Series using given function
-with optional fill value when an index is missing from one Series or
-the other

-Parameters
-----------
-other : Series or scalar value
-func : function
-fill_value : scalar value

-Returns
--------
-result : Series
- -
combine_first(self, other)
Combine Series values, choosing the calling Series's values
-first. Result index will be the union of the two indexes

-Parameters
-----------
-other : Series

-Returns
--------
-y : Series
- -
compound(self, axis=None, skipna=None, level=None)
Return the compound percentage of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-compounded : scalar or Series (if level specified)
- -
compress(self, condition, *args, **kwargs)
Return selected slices of an array along given axis as a Series

-See also
---------
-numpy.ndarray.compress
- -
corr(self, other, method='pearson', min_periods=None)
Compute correlation with `other` Series, excluding missing values

-Parameters
-----------
-other : Series
-method : {'pearson', 'kendall', 'spearman'}
-    * pearson : standard correlation coefficient
-    * kendall : Kendall Tau correlation coefficient
-    * spearman : Spearman rank correlation
-min_periods : int, optional
-    Minimum number of observations needed to have a valid result


-Returns
--------
-correlation : float
- -
count(self, level=None)
Return number of non-NA/null observations in the Series

-Parameters
-----------
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a smaller Series

-Returns
--------
-nobs : int or Series (if level specified)
- -
cov(self, other, min_periods=None)
Compute covariance with Series, excluding missing values

-Parameters
-----------
-other : Series
-min_periods : int, optional
-    Minimum number of observations needed to have a valid result

-Returns
--------
-covariance : float

-Normalized by N-1 (unbiased estimator).
- -
cummax(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative max over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummax : scalar



-See also
---------
-pandas.core.window.Expanding.max : Similar functionality
-    but ignores ``NaN`` values.
- -
cummin(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative minimum over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummin : scalar



-See also
---------
-pandas.core.window.Expanding.min : Similar functionality
-    but ignores ``NaN`` values.
- -
cumprod(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative product over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumprod : scalar



-See also
---------
-pandas.core.window.Expanding.prod : Similar functionality
-    but ignores ``NaN`` values.
- -
cumsum(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative sum over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumsum : scalar



-See also
---------
-pandas.core.window.Expanding.sum : Similar functionality
-    but ignores ``NaN`` values.
- -
diff(self, periods=1)
1st discrete difference of object

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming difference

-Returns
--------
-diffed : Series
- -
div = truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
divide = truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
dot(self, other)
Matrix multiplication with DataFrame or inner-product with Series
-objects

-Parameters
-----------
-other : Series or DataFrame

-Returns
--------
-dot_product : scalar or Series
- -
drop_duplicates(self, keep='first', inplace=False)
Return Series with duplicate values removed

-Parameters
-----------

-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Drop duplicates except for the first occurrence.
-    - ``last`` : Drop duplicates except for the last occurrence.
-    - False : Drop all duplicates.
-inplace : boolean, default False
-If True, performs operation inplace and returns None.

-Returns
--------
-deduplicated : Series
- -
dropna(self, axis=0, inplace=False, **kwargs)
Return Series without null values

-Returns
--------
-valid : Series
-inplace : boolean, default False
-    Do operation in place.
- -
duplicated(self, keep='first')
Return boolean Series denoting duplicate values

-Parameters
-----------
-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Mark duplicates as ``True`` except for the first
-      occurrence.
-    - ``last`` : Mark duplicates as ``True`` except for the last
-      occurrence.
-    - False : Mark all duplicates as ``True``.

-Returns
--------
-duplicated : Series
- -
eq(self, other, level=None, fill_value=None, axis=0)
Equal to of series and other, element-wise (binary operator `eq`).

-Equivalent to ``series == other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
ewm(self, com=None, span=None, halflife=None, alpha=None, min_periods=0, freq=None, adjust=True, ignore_na=False, axis=0)
Provides exponential weighted functions

-.. versionadded:: 0.18.0

-Parameters
-----------
-com : float, optional
-    Specify decay in terms of center of mass,
-    :math:`\alpha = 1 / (1 + com),\text{ for } com \geq 0`
-span : float, optional
-    Specify decay in terms of span,
-    :math:`\alpha = 2 / (span + 1),\text{ for } span \geq 1`
-halflife : float, optional
-    Specify decay in terms of half-life,
-    :math:`\alpha = 1 - exp(log(0.5) / halflife),\text{ for } halflife > 0`
-alpha : float, optional
-    Specify smoothing factor :math:`\alpha` directly,
-    :math:`0 < \alpha \leq 1`

-    .. versionadded:: 0.18.0

-min_periods : int, default 0
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : None or string alias / date offset object, default=None (DEPRECATED)
-    Frequency to conform to before computing statistic
-adjust : boolean, default True
-    Divide by decaying adjustment factor in beginning periods to account
-    for imbalance in relative weightings (viewing EWMA as a moving average)
-ignore_na : boolean, default False
-    Ignore missing values when calculating weights;
-    specify True to reproduce pre-0.15.0 behavior

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.ewm(com=0.5).mean()
-          B
-0  0.000000
-1  0.750000
-2  1.615385
-3  1.615385
-4  3.670213

-Notes
------
-Exactly one of center of mass, span, half-life, and alpha must be provided.
-Allowed values and relationship between the parameters are specified in the
-parameter descriptions above; see the link at the end of this section for
-a detailed explanation.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-When adjust is True (default), weighted averages are calculated using
-weights (1-alpha)**(n-1), (1-alpha)**(n-2), ..., 1-alpha, 1.

-When adjust is False, weighted averages are calculated recursively as:
-   weighted_average[0] = arg[0];
-   weighted_average[i] = (1-alpha)*weighted_average[i-1] + alpha*arg[i].

-When ignore_na is False (default), weights are based on absolute positions.
-For example, the weights of x and y used in calculating the final weighted
-average of [x, None, y] are (1-alpha)**2 and 1 (if adjust is True), and
-(1-alpha)**2 and alpha (if adjust is False).

-When ignore_na is True (reproducing pre-0.15.0 behavior), weights are based
-on relative positions. For example, the weights of x and y used in
-calculating the final weighted average of [x, None, y] are 1-alpha and 1
-(if adjust is True), and 1-alpha and alpha (if adjust is False).

-More details can be found at
-http://pandas.pydata.org/pandas-docs/stable/computation.html#exponentially-weighted-windows
- -
expanding(self, min_periods=1, freq=None, center=False, axis=0)
Provides expanding transformations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-axis : int or string, default 0

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.expanding(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  3.0
-4  7.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).
- -
fillna(self, value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)
Fill NA/NaN values using the specified method

-Parameters
-----------
-value : scalar, dict, Series, or DataFrame
-    Value to use to fill holes (e.g. 0), alternately a
-    dict/Series/DataFrame of values specifying which value to use for
-    each index (for a Series) or column (for a DataFrame). (values not
-    in the dict/Series/DataFrame will not be filled). This value cannot
-    be a list.
-method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
-    Method to use for filling holes in reindexed Series
-    pad / ffill: propagate last valid observation forward to next valid
-    backfill / bfill: use NEXT valid observation to fill gap
-axis : {0, 'index'}
-inplace : boolean, default False
-    If True, fill in place. Note: this will modify any
-    other views on this object, (e.g. a no-copy slice for a column in a
-    DataFrame).
-limit : int, default None
-    If method is specified, this is the maximum number of consecutive
-    NaN values to forward/backward fill. In other words, if there is
-    a gap with more than this number of consecutive NaNs, it will only
-    be partially filled. If method is not specified, this is the
-    maximum number of entries along the entire axis where NaNs will be
-    filled. Must be greater than 0 if not None.
-downcast : dict, default is None
-    a dict of item->dtype of what to downcast if possible,
-    or the string 'infer' which will try to downcast to an appropriate
-    equal type (e.g. float64 to int64 if possible)

-See Also
---------
-reindex, asfreq

-Returns
--------
-filled : Series
- -
first_valid_index(self)
Return label for first non-NA/null value
- -
floordiv(self, other, level=None, fill_value=None, axis=0)
Integer division of series and other, element-wise (binary operator `floordiv`).

-Equivalent to ``series // other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rfloordiv
- -
ge(self, other, level=None, fill_value=None, axis=0)
Greater than or equal to of series and other, element-wise (binary operator `ge`).

-Equivalent to ``series >= other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
get_value(self, label, takeable=False)
Quickly retrieve single value at passed index label

-Parameters
-----------
-index : label
-takeable : interpret the index as indexers, default False

-Returns
--------
-value : scalar value
- -
get_values(self)
same as values (but handles sparseness conversions); is a view
- -
gt(self, other, level=None, fill_value=None, axis=0)
Greater than of series and other, element-wise (binary operator `gt`).

-Equivalent to ``series > other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
hist = hist_series(self, by=None, ax=None, grid=True, xlabelsize=None, xrot=None, ylabelsize=None, yrot=None, figsize=None, bins=10, **kwds)
Draw histogram of the input series using matplotlib

-Parameters
-----------
-by : object, optional
-    If passed, then used to form histograms for separate groups
-ax : matplotlib axis object
-    If not passed, uses gca()
-grid : boolean, default True
-    Whether to show axis grid lines
-xlabelsize : int, default None
-    If specified changes the x-axis label size
-xrot : float, default None
-    rotation of x axis labels
-ylabelsize : int, default None
-    If specified changes the y-axis label size
-yrot : float, default None
-    rotation of y axis labels
-figsize : tuple, default None
-    figure size in inches by default
-bins: integer, default 10
-    Number of histogram bins to be used
-kwds : keywords
-    To be passed to the actual plotting function

-Notes
------
-See matplotlib documentation online for more on this
- -
idxmax(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of maximum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmax : Index of maximum of values

-Notes
------
-This method is the Series version of ``ndarray.argmax``.

-See Also
---------
-DataFrame.idxmax
-numpy.ndarray.argmax
- -
idxmin(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of minimum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmin : Index of minimum of values

-Notes
------
-This method is the Series version of ``ndarray.argmin``.

-See Also
---------
-DataFrame.idxmin
-numpy.ndarray.argmin
- -
isin(self, values)
Return a boolean :class:`~pandas.Series` showing whether each element
-in the :class:`~pandas.Series` is exactly contained in the passed
-sequence of ``values``.

-Parameters
-----------
-values : set or list-like
-    The sequence of values to test. Passing in a single string will
-    raise a ``TypeError``. Instead, turn a single string into a
-    ``list`` of one element.

-    .. versionadded:: 0.18.1

-    Support for values as a set

-Returns
--------
-isin : Series (bool dtype)

-Raises
-------
-TypeError
-  * If ``values`` is a string

-See Also
---------
-pandas.DataFrame.isin

-Examples
---------

->>> s = pd.Series(list('abc'))
->>> s.isin(['a', 'c', 'e'])
-0     True
-1    False
-2     True
-dtype: bool

-Passing a single string as ``s.isin('a')`` will raise an error. Use
-a list of one element instead:

->>> s.isin(['a'])
-0     True
-1    False
-2    False
-dtype: bool
- -
items = iteritems(self)
Lazily iterate over (index, value) tuples
- -
iteritems(self)
Lazily iterate over (index, value) tuples
- -
keys(self)
Alias for index
- -
kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : scalar or Series (if level specified)
- -
kurtosis = kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : scalar or Series (if level specified)
- -
last_valid_index(self)
Return label for last non-NA/null value
- -
le(self, other, level=None, fill_value=None, axis=0)
Less than or equal to of series and other, element-wise (binary operator `le`).

-Equivalent to ``series <= other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
lt(self, other, level=None, fill_value=None, axis=0)
Less than of series and other, element-wise (binary operator `lt`).

-Equivalent to ``series < other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
mad(self, axis=None, skipna=None, level=None)
Return the mean absolute deviation of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mad : scalar or Series (if level specified)
- -
map(self, arg, na_action=None)
Map values of Series using input correspondence (which can be
-a dict, Series, or function)

-Parameters
-----------
-arg : function, dict, or Series
-na_action : {None, 'ignore'}
-    If 'ignore', propagate NA values, without passing them to the
-    mapping function

-Returns
--------
-y : Series
-    same index as caller

-Examples
---------

-Map inputs to outputs (both of type `Series`)

->>> x = pd.Series([1,2,3], index=['one', 'two', 'three'])
->>> x
-one      1
-two      2
-three    3
-dtype: int64

->>> y = pd.Series(['foo', 'bar', 'baz'], index=[1,2,3])
->>> y
-1    foo
-2    bar
-3    baz

->>> x.map(y)
-one   foo
-two   bar
-three baz

-If `arg` is a dictionary, return a new Series with values converted
-according to the dictionary's mapping:

->>> z = {1: 'A', 2: 'B', 3: 'C'}

->>> x.map(z)
-one   A
-two   B
-three C

-Use na_action to control whether NA values are affected by the mapping
-function.

->>> s = pd.Series([1, 2, 3, np.nan])

->>> s2 = s.map('this is a string {}'.format, na_action=None)
-0    this is a string 1.0
-1    this is a string 2.0
-2    this is a string 3.0
-3    this is a string nan
-dtype: object

->>> s3 = s.map('this is a string {}'.format, na_action='ignore')
-0    this is a string 1.0
-1    this is a string 2.0
-2    this is a string 3.0
-3                     NaN
-dtype: object

-See Also
---------
-Series.apply: For applying more complex functions on a Series
-DataFrame.apply: Apply a function row-/column-wise
-DataFrame.applymap: Apply a function elementwise on a whole DataFrame

-Notes
------
-When `arg` is a dictionary, values in Series that are not in the
-dictionary (as keys) are converted to ``NaN``. However, if the
-dictionary is a ``dict`` subclass that defines ``__missing__`` (i.e.
-provides a method for default values), then this default is used
-rather than ``NaN``:

->>> from collections import Counter
->>> counter = Counter()
->>> counter['bar'] += 1
->>> y.map(counter)
-1    0
-2    1
-3    0
-dtype: int64
- -
max(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the maximum of the values in the object.
-            If you want the *index* of the maximum, use ``idxmax``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmax``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-max : scalar or Series (if level specified)
- -
mean(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the mean of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mean : scalar or Series (if level specified)
- -
median(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the median of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-median : scalar or Series (if level specified)
- -
memory_usage(self, index=True, deep=False)
Memory usage of the Series

-Parameters
-----------
-index : bool
-    Specifies whether to include memory usage of Series index
-deep : bool
-    Introspect the data deeply, interrogate
-    `object` dtypes for system-level memory consumption

-Returns
--------
-scalar bytes of memory consumed

-Notes
------
-Memory usage does not include memory consumed by elements that
-are not components of the array if deep=False

-See Also
---------
-numpy.ndarray.nbytes
- -
min(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the minimum of the values in the object.
-            If you want the *index* of the minimum, use ``idxmin``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmin``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-min : scalar or Series (if level specified)
- -
mod(self, other, level=None, fill_value=None, axis=0)
Modulo of series and other, element-wise (binary operator `mod`).

-Equivalent to ``series % other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmod
- -
mode(self)
Return the mode(s) of the dataset.

-Always returns Series even if only one value is returned.

-Returns
--------
-modes : Series (sorted)
- -
mul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `mul`).

-Equivalent to ``series * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmul
- -
multiply = mul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `mul`).

-Equivalent to ``series * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmul
- -
ne(self, other, level=None, fill_value=None, axis=0)
Not equal to of series and other, element-wise (binary operator `ne`).

-Equivalent to ``series != other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
nlargest(self, n=5, keep='first')
Return the largest `n` elements.

-Parameters
-----------
-n : int
-    Return this many descending sorted values
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-top_n : Series
-    The n largest values in the Series, in sorted order

-Notes
------
-Faster than ``.sort_values(ascending=False).head(n)`` for small `n`
-relative to the size of the ``Series`` object.

-See Also
---------
-Series.nsmallest

-Examples
---------
->>> import pandas as pd
->>> import numpy as np
->>> s = pd.Series(np.random.randn(10**6))
->>> s.nlargest(10)  # only sorts up to the N requested
-219921    4.644710
-82124     4.608745
-421689    4.564644
-425277    4.447014
-718691    4.414137
-43154     4.403520
-283187    4.313922
-595519    4.273635
-503969    4.250236
-121637    4.240952
-dtype: float64
- -
nonzero(self)
Return the indices of the elements that are non-zero

-This method is equivalent to calling `numpy.nonzero` on the
-series data. For compatability with NumPy, the return value is
-the same (a tuple with an array of indices for each dimension),
-but it will always be a one-item tuple because series only have
-one dimension.

-Examples
---------
->>> s = pd.Series([0, 3, 0, 4])
->>> s.nonzero()
-(array([1, 3]),)
->>> s.iloc[s.nonzero()[0]]
-1    3
-3    4
-dtype: int64

-See Also
---------
-numpy.nonzero
- -
nsmallest(self, n=5, keep='first')
Return the smallest `n` elements.

-Parameters
-----------
-n : int
-    Return this many ascending sorted values
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-bottom_n : Series
-    The n smallest values in the Series, in sorted order

-Notes
------
-Faster than ``.sort_values().head(n)`` for small `n` relative to
-the size of the ``Series`` object.

-See Also
---------
-Series.nlargest

-Examples
---------
->>> import pandas as pd
->>> import numpy as np
->>> s = pd.Series(np.random.randn(10**6))
->>> s.nsmallest(10)  # only sorts up to the N requested
-288532   -4.954580
-732345   -4.835960
-64803    -4.812550
-446457   -4.609998
-501225   -4.483945
-669476   -4.472935
-973615   -4.401699
-621279   -4.355126
-773916   -4.347355
-359919   -4.331927
-dtype: float64
- -
pow(self, other, level=None, fill_value=None, axis=0)
Exponential power of series and other, element-wise (binary operator `pow`).

-Equivalent to ``series ** other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rpow
- -
prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : scalar or Series (if level specified)
- -
product = prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : scalar or Series (if level specified)
- -
ptp(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Returns the difference between the maximum value and the
-            minimum value in the object. This is the equivalent of the
-            ``numpy.ndarray`` method ``ptp``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-ptp : scalar or Series (if level specified)
- -
put(self, *args, **kwargs)
Applies the `put` method to its `values` attribute
-if it has one.

-See also
---------
-numpy.ndarray.put
- -
quantile(self, q=0.5, interpolation='linear')
Return value at the given quantile, a la numpy.percentile.

-Parameters
-----------
-q : float or array-like, default 0.5 (50% quantile)
-    0 <= q <= 1, the quantile(s) to compute
-interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'}
-    .. versionadded:: 0.18.0

-    This optional parameter specifies the interpolation method to use,
-    when the desired quantile lies between two data points `i` and `j`:

-        * linear: `i + (j - i) * fraction`, where `fraction` is the
-          fractional part of the index surrounded by `i` and `j`.
-        * lower: `i`.
-        * higher: `j`.
-        * nearest: `i` or `j` whichever is nearest.
-        * midpoint: (`i` + `j`) / 2.

-Returns
--------
-quantile : float or Series
-    if ``q`` is an array, a Series will be returned where the
-    index is ``q`` and the values are the quantiles.

-Examples
---------
->>> s = Series([1, 2, 3, 4])
->>> s.quantile(.5)
-2.5
->>> s.quantile([.25, .5, .75])
-0.25    1.75
-0.50    2.50
-0.75    3.25
-dtype: float64
- -
radd(self, other, level=None, fill_value=None, axis=0)
Addition of series and other, element-wise (binary operator `radd`).

-Equivalent to ``other + series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.add
- -
ravel(self, order='C')
Return the flattened underlying data as an ndarray

-See also
---------
-numpy.ndarray.ravel
- -
rdiv = rtruediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.truediv
- -
reindex(self, index=None, **kwargs)
Conform Series to new index with optional filling logic, placing
-NA/NaN in locations having no value in the previous index. A new object
-is produced unless the new index is equivalent to the current one and
-copy=False

-Parameters
-----------
-index : array-like, optional (can be specified in order, or as
-    keywords)
-    New labels / index to conform to. Preferably an Index object to
-    avoid duplicating data
-method : {None, 'backfill'/'bfill', 'pad'/'ffill', 'nearest'}, optional
-    method to use for filling holes in reindexed DataFrame.
-    Please note: this is only  applicable to DataFrames/Series with a
-    monotonically increasing/decreasing index.

-    * default: don't fill gaps
-    * pad / ffill: propagate last valid observation forward to next
-      valid
-    * backfill / bfill: use next valid observation to fill gap
-    * nearest: use nearest valid observations to fill gap

-copy : boolean, default True
-    Return a new object, even if the passed indexes are the same
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-limit : int, default None
-    Maximum number of consecutive elements to forward or backward fill
-tolerance : optional
-    Maximum distance between original and new labels for inexact
-    matches. The values of the index at the matching locations most
-    satisfy the equation ``abs(index[indexer] - target) <= tolerance``.

-    .. versionadded:: 0.17.0

-Examples
---------

-Create a dataframe with some fictional data.

->>> index = ['Firefox', 'Chrome', 'Safari', 'IE10', 'Konqueror']
->>> df = pd.DataFrame({
-...      'http_status': [200,200,404,404,301],
-...      'response_time': [0.04, 0.02, 0.07, 0.08, 1.0]},
-...       index=index)
->>> df
-           http_status  response_time
-Firefox            200           0.04
-Chrome             200           0.02
-Safari             404           0.07
-IE10               404           0.08
-Konqueror          301           1.00

-Create a new index and reindex the dataframe. By default
-values in the new index that do not have corresponding
-records in the dataframe are assigned ``NaN``.

->>> new_index= ['Safari', 'Iceweasel', 'Comodo Dragon', 'IE10',
-...             'Chrome']
->>> df.reindex(new_index)
-               http_status  response_time
-Safari               404.0           0.07
-Iceweasel              NaN            NaN
-Comodo Dragon          NaN            NaN
-IE10                 404.0           0.08
-Chrome               200.0           0.02

-We can fill in the missing values by passing a value to
-the keyword ``fill_value``. Because the index is not monotonically
-increasing or decreasing, we cannot use arguments to the keyword
-``method`` to fill the ``NaN`` values.

->>> df.reindex(new_index, fill_value=0)
-               http_status  response_time
-Safari                 404           0.07
-Iceweasel                0           0.00
-Comodo Dragon            0           0.00
-IE10                   404           0.08
-Chrome                 200           0.02

->>> df.reindex(new_index, fill_value='missing')
-              http_status response_time
-Safari                404          0.07
-Iceweasel         missing       missing
-Comodo Dragon     missing       missing
-IE10                  404          0.08
-Chrome                200          0.02

-To further illustrate the filling functionality in
-``reindex``, we will create a dataframe with a
-monotonically increasing index (for example, a sequence
-of dates).

->>> date_index = pd.date_range('1/1/2010', periods=6, freq='D')
->>> df2 = pd.DataFrame({"prices": [100, 101, np.nan, 100, 89, 88]},
-...                    index=date_index)
->>> df2
-            prices
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88

-Suppose we decide to expand the dataframe to cover a wider
-date range.

->>> date_index2 = pd.date_range('12/29/2009', periods=10, freq='D')
->>> df2.reindex(date_index2)
-            prices
-2009-12-29     NaN
-2009-12-30     NaN
-2009-12-31     NaN
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-The index entries that did not have a value in the original data frame
-(for example, '2009-12-29') are by default filled with ``NaN``.
-If desired, we can fill in the missing values using one of several
-options.

-For example, to backpropagate the last valid value to fill the ``NaN``
-values, pass ``bfill`` as an argument to the ``method`` keyword.

->>> df2.reindex(date_index2, method='bfill')
-            prices
-2009-12-29     100
-2009-12-30     100
-2009-12-31     100
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-Please note that the ``NaN`` value present in the original dataframe
-(at index value 2010-01-03) will not be filled by any of the
-value propagation schemes. This is because filling while reindexing
-does not look at dataframe values, but only compares the original and
-desired indexes. If you do want to fill in the ``NaN`` values present
-in the original dataframe, use the ``fillna()`` method.

-Returns
--------
-reindexed : Series
- -
reindex_axis(self, labels, axis=0, **kwargs)
for compatibility with higher dims
- -
rename(self, index=None, **kwargs)
Alter axes input function or functions. Function / dict values must be
-unique (1-to-1). Labels not contained in a dict / Series will be left
-as-is. Extra labels listed don't throw an error. Alternatively, change
-``Series.name`` with a scalar value (Series only).

-Parameters
-----------
-index : scalar, list-like, dict-like or function, optional
-    Scalar or list-like will alter the ``Series.name`` attribute,
-    and raise on DataFrame or Panel.
-    dict-like or functions are transformations to apply to
-    that axis' values
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False
-    Whether to return a new Series. If True then value of copy is
-    ignored.
-level : int or level name, default None
-    In case of a MultiIndex, only rename labels in the specified
-    level.

-Returns
--------
-renamed : Series (new object)

-See Also
---------
-pandas.NDFrame.rename_axis

-Examples
---------
->>> s = pd.Series([1, 2, 3])
->>> s
-0    1
-1    2
-2    3
-dtype: int64
->>> s.rename("my_name") # scalar, changes Series.name
-0    1
-1    2
-2    3
-Name: my_name, dtype: int64
->>> s.rename(lambda x: x ** 2)  # function, changes labels
-0    1
-1    2
-4    3
-dtype: int64
->>> s.rename({1: 3, 2: 5})  # mapping, changes labels
-0    1
-3    2
-5    3
-dtype: int64
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename(2)
-Traceback (most recent call last):
-...
-TypeError: 'int' object is not callable
->>> df.rename(index=str, columns={"A": "a", "B": "c"})
-   a  c
-0  1  4
-1  2  5
-2  3  6
->>> df.rename(index=str, columns={"A": "a", "C": "c"})
-   a  B
-0  1  4
-1  2  5
-2  3  6
- -
reorder_levels(self, order)
Rearrange index levels using input order. May not drop or duplicate
-levels

-Parameters
-----------
-order : list of int representing new level order.
-       (reference level by number or key)
-axis : where to reorder levels

-Returns
--------
-type of caller (new object)
- -
repeat(self, repeats, *args, **kwargs)
Repeat elements of an Series. Refer to `numpy.ndarray.repeat`
-for more information about the `repeats` argument.

-See also
---------
-numpy.ndarray.repeat
- -
reset_index(self, level=None, drop=False, name=None, inplace=False)
Analogous to the :meth:`pandas.DataFrame.reset_index` function, see
-docstring there.

-Parameters
-----------
-level : int, str, tuple, or list, default None
-    Only remove the given levels from the index. Removes all levels by
-    default
-drop : boolean, default False
-    Do not try to insert index into dataframe columns
-name : object, default None
-    The name of the column corresponding to the Series values
-inplace : boolean, default False
-    Modify the Series in place (do not create a new object)

-Returns
-----------
-resetted : DataFrame, or Series if drop == True
- -
reshape(self, *args, **kwargs)
DEPRECATED: calling this method will raise an error in a
-future release. Please call ``.values.reshape(...)`` instead.

-return an ndarray with the values shape
-if the specified shape matches exactly the current shape, then
-return self (for compat)

-See also
---------
-numpy.ndarray.reshape
- -
rfloordiv(self, other, level=None, fill_value=None, axis=0)
Integer division of series and other, element-wise (binary operator `rfloordiv`).

-Equivalent to ``other // series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.floordiv
- -
rmod(self, other, level=None, fill_value=None, axis=0)
Modulo of series and other, element-wise (binary operator `rmod`).

-Equivalent to ``other % series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.mod
- -
rmul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `rmul`).

-Equivalent to ``other * series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.mul
- -
rolling(self, window, min_periods=None, freq=None, center=False, win_type=None, on=None, axis=0, closed=None)
Provides rolling window calculcations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-window : int, or offset
-    Size of the moving window. This is the number of observations used for
-    calculating the statistic. Each window will be a fixed size.

-    If its an offset then this will be the time period of each window. Each
-    window will be a variable sized based on the observations included in
-    the time-period. This is only valid for datetimelike indexes. This is
-    new in 0.19.0
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA). For a window that is specified by an offset,
-    this will default to 1.
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-win_type : string, default None
-    Provide a window type. See the notes below.
-on : string, optional
-    For a DataFrame, column on which to calculate
-    the rolling window, rather than the index
-closed : string, default None
-    Make the interval closed on the 'right', 'left', 'both' or
-    'neither' endpoints.
-    For offset-based windows, it defaults to 'right'.
-    For fixed windows, defaults to 'both'. Remaining cases not implemented
-    for fixed windows.

-    .. versionadded:: 0.20.0

-axis : int or string, default 0

-Returns
--------
-a Window or Rolling sub-classed for the particular operation

-Examples
---------

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]})
->>> df
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

-Rolling sum with a window length of 2, using the 'triang'
-window type.

->>> df.rolling(2, win_type='triang').sum()
-     B
-0  NaN
-1  1.0
-2  2.5
-3  NaN
-4  NaN

-Rolling sum with a window length of 2, min_periods defaults
-to the window length.

->>> df.rolling(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  NaN
-4  NaN

-Same as above, but explicity set the min_periods

->>> df.rolling(2, min_periods=1).sum()
-     B
-0  0.0
-1  1.0
-2  3.0
-3  2.0
-4  4.0

-A ragged (meaning not-a-regular frequency), time-indexed DataFrame

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]},
-....:                 index = [pd.Timestamp('20130101 09:00:00'),
-....:                          pd.Timestamp('20130101 09:00:02'),
-....:                          pd.Timestamp('20130101 09:00:03'),
-....:                          pd.Timestamp('20130101 09:00:05'),
-....:                          pd.Timestamp('20130101 09:00:06')])

->>> df
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  2.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0


-Contrasting to an integer rolling window, this will roll a variable
-length window corresponding to the time period.
-The default for min_periods is 1.

->>> df.rolling('2s').sum()
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  3.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-To learn more about the offsets & frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-The recognized win_types are:

-* ``boxcar``
-* ``triang``
-* ``blackman``
-* ``hamming``
-* ``bartlett``
-* ``parzen``
-* ``bohman``
-* ``blackmanharris``
-* ``nuttall``
-* ``barthann``
-* ``kaiser`` (needs beta)
-* ``gaussian`` (needs std)
-* ``general_gaussian`` (needs power, width)
-* ``slepian`` (needs width).
- -
round(self, decimals=0, *args, **kwargs)
Round each value in a Series to the given number of decimals.

-Parameters
-----------
-decimals : int
-    Number of decimal places to round to (default: 0).
-    If decimals is negative, it specifies the number of
-    positions to the left of the decimal point.

-Returns
--------
-Series object

-See Also
---------
-numpy.around
-DataFrame.round
- -
rpow(self, other, level=None, fill_value=None, axis=0)
Exponential power of series and other, element-wise (binary operator `rpow`).

-Equivalent to ``other ** series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.pow
- -
rsub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `rsub`).

-Equivalent to ``other - series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.sub
- -
rtruediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.truediv
- -
searchsorted(self, value, side='left', sorter=None)
Find indices where elements should be inserted to maintain order.

-Find the indices into a sorted Series `self` such that, if the
-corresponding elements in `value` were inserted before the indices,
-the order of `self` would be preserved.

-Parameters
-----------
-value : array_like
-    Values to insert into `self`.
-side : {'left', 'right'}, optional
-    If 'left', the index of the first suitable location found is given.
-    If 'right', return the last such index.  If there is no suitable
-    index, return either 0 or N (where N is the length of `self`).
-sorter : 1-D array_like, optional
-    Optional array of integer indices that sort `self` into ascending
-    order. They are typically the result of ``np.argsort``.

-Returns
--------
-indices : array of ints
-    Array of insertion points with the same shape as `value`.

-See Also
---------
-numpy.searchsorted

-Notes
------
-Binary search is used to find the required insertion points.

-Examples
---------

->>> x = pd.Series([1, 2, 3])
->>> x
-0    1
-1    2
-2    3
-dtype: int64

->>> x.searchsorted(4)
-array([3])

->>> x.searchsorted([0, 4])
-array([0, 3])

->>> x.searchsorted([1, 3], side='left')
-array([0, 2])

->>> x.searchsorted([1, 3], side='right')
-array([1, 3])

->>> x = pd.Categorical(['apple', 'bread', 'bread', 'cheese', 'milk' ])
-[apple, bread, bread, cheese, milk]
-Categories (4, object): [apple < bread < cheese < milk]

->>> x.searchsorted('bread')
-array([1])     # Note: an array, not a scalar

->>> x.searchsorted(['bread'])
-array([1])

->>> x.searchsorted(['bread', 'eggs'])
-array([1, 4])

->>> x.searchsorted(['bread', 'eggs'], side='right')
-array([3, 4])    # eggs before milk
- -
sem(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased standard error of the mean over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sem : scalar or Series (if level specified)
- -
set_value(self, label, value, takeable=False)
Quickly set single value at passed label. If label is not contained, a
-new object is created with the label placed at the end of the result
-index

-Parameters
-----------
-label : object
-    Partial indexing with MultiIndex not allowed
-value : object
-    Scalar value
-takeable : interpret the index as indexers, default False

-Returns
--------
-series : Series
-    If label is contained, will be reference to calling Series,
-    otherwise a new object
- -
shift(self, periods=1, freq=None, axis=0)
Shift index by desired number of periods with an optional time freq

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, optional
-    Increment to use from the tseries module or time rule (e.g. 'EOM').
-    See Notes.
-axis : {0, 'index'}

-Notes
------
-If freq is specified then the index values are shifted but the data
-is not realigned. That is, use freq if you would like to extend the
-index when shifting and preserve the original data.

-Returns
--------
-shifted : Series
- -
skew(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased skew over requested axis
-Normalized by N-1

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-skew : scalar or Series (if level specified)
- -
sort_index(self, axis=0, level=None, ascending=True, inplace=False, kind='quicksort', na_position='last', sort_remaining=True)
Sort object by labels (along an axis)

-Parameters
-----------
-axis : index to direct sorting
-level : int or level name or list of ints or list of level names
-    if not None, sort on values in specified index level(s)
-ascending : boolean, default True
-    Sort ascending vs. descending
-inplace : bool, default False
-    if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end.
-     Not implemented for MultiIndex.
-sort_remaining : bool, default True
-    if true and sorting by level and index is multilevel, sort by other
-    levels too (in order) after sorting by specified level

-Returns
--------
-sorted_obj : Series
- -
sort_values(self, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last')
Sort by the values along either axis

-.. versionadded:: 0.17.0

-Parameters
-----------
-axis : {0, 'index'}, default 0
-    Axis to direct sorting
-ascending : bool or list of bool, default True
-     Sort ascending vs. descending. Specify list for multiple sort
-     orders.  If this is a list of bools, must match the length of
-     the by.
-inplace : bool, default False
-     if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end

-Returns
--------
-sorted_obj : Series
- -
sortlevel(self, level=0, ascending=True, sort_remaining=True)
DEPRECATED: use :meth:`Series.sort_index`

-Sort Series with MultiIndex by chosen level. Data will be
-lexicographically sorted by the chosen level followed by the other
-levels (in order)

-Parameters
-----------
-level : int or level name, default None
-ascending : bool, default True

-Returns
--------
-sorted : Series

-See Also
---------
-Series.sort_index(level=...)
- -
std(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return sample standard deviation over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-std : scalar or Series (if level specified)
- -
sub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `sub`).

-Equivalent to ``series - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rsub
- -
subtract = sub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `sub`).

-Equivalent to ``series - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rsub
- -
sum(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the sum of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sum : scalar or Series (if level specified)
- -
swaplevel(self, i=-2, j=-1, copy=True)
Swap levels i and j in a MultiIndex

-Parameters
-----------
-i, j : int, string (can be mixed)
-    Level of index to be swapped. Can pass level name as string.

-Returns
--------
-swapped : Series

-.. versionchanged:: 0.18.1

-   The indexes ``i`` and ``j`` are now optional, and default to
-   the two innermost levels of the index.
- -
take(self, indices, axis=0, convert=True, is_copy=False, **kwargs)
return Series corresponding to requested indices

-Parameters
-----------
-indices : list / array of ints
-convert : translate negative to positive indices (default)

-Returns
--------
-taken : Series

-See also
---------
-numpy.ndarray.take
- -
to_csv(self, path=None, index=True, sep=',', na_rep='', float_format=None, header=False, index_label=None, mode='w', encoding=None, date_format=None, decimal='.')
Write Series to a comma-separated values (csv) file

-Parameters
-----------
-path : string or file handle, default None
-    File path or object, if None is provided the result is returned as
-    a string.
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-header : boolean, default False
-    Write out series name
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.
-mode : Python write mode, default 'w'
-sep : character, default ","
-    Field delimiter for the output file.
-encoding : string, optional
-    a string representing the encoding to use if the contents are
-    non-ascii, for python versions prior to 3
-date_format: string, default None
-    Format string for datetime objects.
-decimal: string, default '.'
-    Character recognized as decimal separator. E.g. use ',' for
-    European data
- -
to_dict(self)
Convert Series to {label -> value} dict

-Returns
--------
-value_dict : dict
- -
to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='', float_format=None, columns=None, header=True, index=True, index_label=None, startrow=0, startcol=0, engine=None, merge_cells=True, encoding=None, inf_rep='inf', verbose=True)
Write Series to an excel sheet

-.. versionadded:: 0.20.0


-Parameters
-----------
-excel_writer : string or ExcelWriter object
-    File path or existing ExcelWriter
-sheet_name : string, default 'Sheet1'
-    Name of sheet which will contain DataFrame
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-columns : sequence, optional
-    Columns to write
-header : boolean or list of string, default True
-    Write out column names. If a list of string is given it is
-    assumed to be aliases for the column names
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.
-startrow :
-    upper left cell row to dump data frame
-startcol :
-    upper left cell column to dump data frame
-engine : string, default None
-    write engine to use - you can also set this via the options
-    ``io.excel.xlsx.writer``, ``io.excel.xls.writer``, and
-    ``io.excel.xlsm.writer``.
-merge_cells : boolean, default True
-    Write MultiIndex and Hierarchical Rows as merged cells.
-encoding: string, default None
-    encoding of the resulting excel file. Only necessary for xlwt,
-    other writers support unicode natively.
-inf_rep : string, default 'inf'
-    Representation for infinity (there is no native representation for
-    infinity in Excel)
-freeze_panes : tuple of integer (length 2), default None
-    Specifies the one-based bottommost row and rightmost column that
-    is to be frozen

-    .. versionadded:: 0.20.0

-Notes
------
-If passing an existing ExcelWriter object, then the sheet will be added
-to the existing workbook.  This can be used to save different
-DataFrames to one workbook:

->>> writer = pd.ExcelWriter('output.xlsx')
->>> df1.to_excel(writer,'Sheet1')
->>> df2.to_excel(writer,'Sheet2')
->>> writer.save()

-For compatibility with to_csv, to_excel serializes lists and dicts to
-strings before writing.
- -
to_frame(self, name=None)
Convert Series to DataFrame

-Parameters
-----------
-name : object, default None
-    The passed name should substitute for the series name (if it has
-    one).

-Returns
--------
-data_frame : DataFrame
- -
to_period(self, freq=None, copy=True)
Convert Series from DatetimeIndex to PeriodIndex with desired
-frequency (inferred from index if not passed)

-Parameters
-----------
-freq : string, default

-Returns
--------
-ts : Series with PeriodIndex
- -
to_sparse(self, kind='block', fill_value=None)
Convert Series to SparseSeries

-Parameters
-----------
-kind : {'block', 'integer'}
-fill_value : float, defaults to NaN (missing)

-Returns
--------
-sp : SparseSeries
- -
to_string(self, buf=None, na_rep='NaN', float_format=None, header=True, index=True, length=False, dtype=False, name=False, max_rows=None)
Render a string representation of the Series

-Parameters
-----------
-buf : StringIO-like, optional
-    buffer to write to
-na_rep : string, optional
-    string representation of NAN to use, default 'NaN'
-float_format : one-parameter function, optional
-    formatter function to apply to columns' elements if they are floats
-    default None
-header: boolean, default True
-    Add the Series header (index name)
-index : bool, optional
-    Add index (row) labels, default True
-length : boolean, default False
-    Add the Series length
-dtype : boolean, default False
-    Add the Series dtype
-name : boolean, default False
-    Add the Series name if not None
-max_rows : int, optional
-    Maximum number of rows to show before truncating. If None, show
-    all.

-Returns
--------
-formatted : string (if not buffer passed)
- -
to_timestamp(self, freq=None, how='start', copy=True)
Cast to datetimeindex of timestamps, at *beginning* of period

-Parameters
-----------
-freq : string, default frequency of PeriodIndex
-    Desired frequency
-how : {'s', 'e', 'start', 'end'}
-    Convention for converting period to timestamp; start of period
-    vs. end

-Returns
--------
-ts : Series with DatetimeIndex
- -
tolist(self)
Convert Series to a nested list
- -
transform(self, func, *args, **kwargs)
Call function producing a like-indexed NDFrame
-and return a NDFrame with the transformed values`

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    To apply to column

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Returns
--------
-transformed : NDFrame

-Examples
---------
->>> df = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
-...                   index=pd.date_range('1/1/2000', periods=10))
-df.iloc[3:7] = np.nan

->>> df.transform(lambda x: (x - x.mean()) / x.std())
-                   A         B         C
-2000-01-01  0.579457  1.236184  0.123424
-2000-01-02  0.370357 -0.605875 -1.231325
-2000-01-03  1.455756 -0.277446  0.288967
-2000-01-04       NaN       NaN       NaN
-2000-01-05       NaN       NaN       NaN
-2000-01-06       NaN       NaN       NaN
-2000-01-07       NaN       NaN       NaN
-2000-01-08 -0.498658  1.274522  1.642524
-2000-01-09 -0.540524 -1.012676 -0.828968
-2000-01-10 -1.366388 -0.614710  0.005378

-See also
---------
-pandas.NDFrame.aggregate
-pandas.NDFrame.apply
- -
truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
unique(self)
Return unique values in the object. Uniques are returned in order
-of appearance, this does NOT sort. Hash table-based unique.

-Parameters
-----------
-values : 1d array-like

-Returns
--------
-unique values.
-  - If the input is an Index, the return is an Index
-  - If the input is a Categorical dtype, the return is a Categorical
-  - If the input is a Series/ndarray, the return will be an ndarray

-See Also
---------
-unique
-Index.unique
-Series.unique
- -
unstack(self, level=-1, fill_value=None)
Unstack, a.k.a. pivot, Series with MultiIndex to produce DataFrame.
-The level involved will automatically get sorted.

-Parameters
-----------
-level : int, string, or list of these, default last level
-    Level(s) to unstack, can pass level name
-fill_value : replace NaN with this value if the unstack produces
-    missing values

-    .. versionadded: 0.18.0

-Examples
---------
->>> s = pd.Series([1, 2, 3, 4],
-...     index=pd.MultiIndex.from_product([['one', 'two'], ['a', 'b']]))
->>> s
-one  a    1
-     b    2
-two  a    3
-     b    4
-dtype: int64

->>> s.unstack(level=-1)
-     a  b
-one  1  2
-two  3  4

->>> s.unstack(level=0)
-   one  two
-a    1    3
-b    2    4

-Returns
--------
-unstacked : DataFrame
- -
update(self, other)
Modify Series in place using non-NA values from passed
-Series. Aligns on index

-Parameters
-----------
-other : Series
- -
valid lambda self, inplace=False, **kwargs
- -
var(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased variance over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-var : scalar or Series (if level specified)
- -
view(self, dtype=None)
- -
-Class methods inherited from pandas.core.series.Series:
-
from_array(arr, index=None, name=None, dtype=None, copy=False, fastpath=False) from builtins.type
- -
from_csv(path, sep=',', parse_dates=True, header=None, index_col=0, encoding=None, infer_datetime_format=False) from builtins.type
Read CSV file (DISCOURAGED, please use :func:`pandas.read_csv`
-instead).

-It is preferable to use the more powerful :func:`pandas.read_csv`
-for most general purposes, but ``from_csv`` makes for an easy
-roundtrip to and from a file (the exact counterpart of
-``to_csv``), especially with a time Series.

-This method only differs from :func:`pandas.read_csv` in some defaults:

-- `index_col` is ``0`` instead of ``None`` (take first column as index
-  by default)
-- `header` is ``None`` instead of ``0`` (the first row is not used as
-  the column names)
-- `parse_dates` is ``True`` instead of ``False`` (try parsing the index
-  as datetime by default)

-With :func:`pandas.read_csv`, the option ``squeeze=True`` can be used
-to return a Series like ``from_csv``.

-Parameters
-----------
-path : string file path or file handle / StringIO
-sep : string, default ','
-    Field delimiter
-parse_dates : boolean, default True
-    Parse dates. Different default from read_table
-header : int, default None
-    Row to use as header (skip prior rows)
-index_col : int or sequence, default 0
-    Column to use for index. If a sequence is given, a MultiIndex
-    is used. Different default from read_table
-encoding : string, optional
-    a string representing the encoding to use if the contents are
-    non-ascii, for python versions prior to 3
-infer_datetime_format: boolean, default False
-    If True and `parse_dates` is True for a column, try to infer the
-    datetime format based on the first datetime string. If the format
-    can be inferred, there often will be a large parsing speed-up.

-See also
---------
-pandas.read_csv

-Returns
--------
-y : Series
- -
-Data descriptors inherited from pandas.core.series.Series:
-
asobject
-
return object Series which contains boxed values

-*this is an internal non-public method*
-
-
axes
-
Return a list of the row axis labels
-
-
dtype
-
return the dtype object of the underlying data
-
-
dtypes
-
return the dtype object of the underlying data
-
-
ftype
-
return if the data is sparse|dense
-
-
ftypes
-
return if the data is sparse|dense
-
-
imag
-
-
index
-
-
name
-
-
real
-
-
values
-
Return Series as ndarray or ndarray-like
-depending on the dtype

-Returns
--------
-arr : numpy.ndarray or ndarray-like

-Examples
---------
->>> pd.Series([1, 2, 3]).values
-array([1, 2, 3])

->>> pd.Series(list('aabc')).values
-array(['a', 'a', 'b', 'c'], dtype=object)

->>> pd.Series(list('aabc')).astype('category').values
-[a, a, b, c]
-Categories (3, object): [a, b, c]

-Timezone aware datetime data is converted to UTC:

->>> pd.Series(pd.date_range('20130101', periods=3,
-...                         tz='US/Eastern')).values
-array(['2013-01-01T05:00:00.000000000',
-       '2013-01-02T05:00:00.000000000',
-       '2013-01-03T05:00:00.000000000'], dtype='datetime64[ns]')
-
-
-Data and other attributes inherited from pandas.core.series.Series:
-
cat = <class 'pandas.core.categorical.CategoricalAccessor'>
Accessor object for categorical properties of the Series values.

-Be aware that assigning to `categories` is a inplace operation, while all
-methods return new categorical data per default (but can be called with
-`inplace=True`).

-Examples
---------
->>> s.cat.categories
->>> s.cat.categories = list('abc')
->>> s.cat.rename_categories(list('cab'))
->>> s.cat.reorder_categories(list('cab'))
->>> s.cat.add_categories(['d','e'])
->>> s.cat.remove_categories(['d'])
->>> s.cat.remove_unused_categories()
->>> s.cat.set_categories(list('abcde'))
->>> s.cat.as_ordered()
->>> s.cat.as_unordered()
- -
dt = <class 'pandas.core.indexes.accessors.CombinedDatetimelikeProperties'>
Accessor object for datetimelike properties of the Series values.

-Examples
---------
->>> s.dt.hour
->>> s.dt.second
->>> s.dt.quarter

-Returns a Series indexed like the original Series.
-Raises TypeError if the Series does not contain datetimelike values.
- -
plot = <class 'pandas.plotting._core.SeriesPlotMethods'>
Series plotting accessor and method

-Examples
---------
->>> s.plot.line()
->>> s.plot.bar()
->>> s.plot.hist()

-Plotting methods can also be accessed by calling the accessor as a method
-with the ``kind`` argument:
-``s.plot(kind='line')`` is equivalent to ``s.plot.line()``
- -
-Methods inherited from pandas.core.base.IndexOpsMixin:
-
factorize(self, sort=False, na_sentinel=-1)
Encode the object as an enumerated type or categorical variable

-Parameters
-----------
-sort : boolean, default False
-    Sort by values
-na_sentinel: int, default -1
-    Value to mark "not found"

-Returns
--------
-labels : the indexer to the original array
-uniques : the unique Index
- -
item(self)
return the first element of the underlying data as a python
-scalar
- -
nunique(self, dropna=True)
Return number of unique elements in the object.

-Excludes NA values by default.

-Parameters
-----------
-dropna : boolean, default True
-    Don't include NaN in the count.

-Returns
--------
-nunique : int
- -
transpose(self, *args, **kwargs)
return the transpose, which is by definition self
- -
value_counts(self, normalize=False, sort=True, ascending=False, bins=None, dropna=True)
Returns object containing counts of unique values.

-The resulting object will be in descending order so that the
-first element is the most frequently-occurring element.
-Excludes NA values by default.

-Parameters
-----------
-normalize : boolean, default False
-    If True then the object returned will contain the relative
-    frequencies of the unique values.
-sort : boolean, default True
-    Sort by values
-ascending : boolean, default False
-    Sort in ascending order
-bins : integer, optional
-    Rather than count values, group them into half-open bins,
-    a convenience for pd.cut, only works with numeric data
-dropna : boolean, default True
-    Don't include counts of NaN.

-Returns
--------
-counts : Series
- -
-Data descriptors inherited from pandas.core.base.IndexOpsMixin:
-
T
-
return the transpose, which is by definition self
-
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-
base
-
return the base object if the memory of the underlying data is
-shared
-
-
data
-
return the data pointer of the underlying data
-
-
empty
-
-
flags
-
return the ndarray.flags for the underlying data
-
-
hasnans
-
-
is_monotonic
-
Return boolean if values in the object are
-monotonic_increasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic : boolean
-
-
is_monotonic_decreasing
-
Return boolean if values in the object are
-monotonic_decreasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic_decreasing : boolean
-
-
is_monotonic_increasing
-
Return boolean if values in the object are
-monotonic_increasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic : boolean
-
-
is_unique
-
Return boolean if values in the object are unique

-Returns
--------
-is_unique : boolean
-
-
itemsize
-
return the size of the dtype of the item of the underlying data
-
-
nbytes
-
return the number of bytes in the underlying data
-
-
ndim
-
return the number of dimensions of the underlying data,
-by definition 1
-
-
shape
-
return a tuple of the shape of the underlying data
-
-
size
-
return the number of elements in the underlying data
-
-
strides
-
return the strides of the underlying data
-
-
-Data and other attributes inherited from pandas.core.base.IndexOpsMixin:
-
__array_priority__ = 1000
- -
-Data and other attributes inherited from pandas.core.strings.StringAccessorMixin:
-
str = <class 'pandas.core.strings.StringMethods'>
Vectorized string functions for Series and Index. NAs stay NA unless
-handled otherwise by a particular method. Patterned after Python's string
-methods, with some inspiration from R's stringr package.

-Examples
---------
->>> s.str.split('_')
->>> s.str.replace('_', '')
- -
-Methods inherited from pandas.core.generic.NDFrame:
-
__abs__(self)
- -
__bool__ = __nonzero__(self)
- -
__contains__(self, key)
True if the key is in the info axis
- -
__copy__(self, deep=True)
- -
__deepcopy__(self, memo=None)
- -
__delitem__(self, key)
Delete item
- -
__finalize__(self, other, method=None, **kwargs)
Propagate metadata from other to self.

-Parameters
-----------
-other : the object from which to get the attributes that we are going
-    to propagate
-method : optional, a passed method name ; possibly to take different
-    types of propagation actions based on this
- -
__getattr__(self, name)
After regular attribute access, try looking up the name
-This allows simpler access to columns for interactive use.
- -
__getstate__(self)
- -
__hash__(self)
Return hash(self).
- -
__invert__(self)
- -
__neg__(self)
- -
__nonzero__(self)
- -
__round__(self, decimals=0)
- -
__setattr__(self, name, value)
After regular attribute access, try setting the name
-This allows simpler access to columns for interactive use.
- -
__setstate__(self, state)
- -
abs(self)
Return an object with absolute value taken--only applicable to objects
-that are all numeric.

-Returns
--------
-abs: type of caller
- -
add_prefix(self, prefix)
Concatenate prefix string with panel items names.

-Parameters
-----------
-prefix : string

-Returns
--------
-with_prefix : type of caller
- -
add_suffix(self, suffix)
Concatenate suffix string with panel items names.

-Parameters
-----------
-suffix : string

-Returns
--------
-with_suffix : type of caller
- -
as_blocks(self, copy=True)
Convert the frame to a dict of dtype -> Constructor Types that each has
-a homogeneous dtype.

-NOTE: the dtypes of the blocks WILL BE PRESERVED HERE (unlike in
-      as_matrix)

-Parameters
-----------
-copy : boolean, default True

-       .. versionadded: 0.16.1

-Returns
--------
-values : a dict of dtype -> Constructor Types
- -
as_matrix(self, columns=None)
Convert the frame to its Numpy-array representation.

-Parameters
-----------
-columns: list, optional, default:None
-    If None, return all columns, otherwise, returns specified columns.

-Returns
--------
-values : ndarray
-    If the caller is heterogeneous and contains booleans or objects,
-    the result will be of dtype=object. See Notes.


-Notes
------
-Return is NOT a Numpy-matrix, rather, a Numpy-array.

-The dtype will be a lower-common-denominator dtype (implicit
-upcasting); that is to say if the dtypes (even of numeric types)
-are mixed, the one that accommodates all will be chosen. Use this
-with care if you are not dealing with the blocks.

-e.g. If the dtypes are float16 and float32, dtype will be upcast to
-float32.  If dtypes are int32 and uint8, dtype will be upcase to
-int32. By numpy.find_common_type convention, mixing int64 and uint64
-will result in a flot64 dtype.

-This method is provided for backwards compatibility. Generally,
-it is recommended to use '.values'.

-See Also
---------
-pandas.DataFrame.values
- -
asfreq(self, freq, method=None, how=None, normalize=False, fill_value=None)
Convert TimeSeries to specified frequency.

-Optionally provide filling method to pad/backfill missing values.

-Returns the original data conformed to a new index with the specified
-frequency. ``resample`` is more appropriate if an operation, such as
-summarization, is necessary to represent the data at the new frequency.

-Parameters
-----------
-freq : DateOffset object, or string
-method : {'backfill'/'bfill', 'pad'/'ffill'}, default None
-    Method to use for filling holes in reindexed Series (note this
-    does not fill NaNs that already were present):

-    * 'pad' / 'ffill': propagate last valid observation forward to next
-      valid
-    * 'backfill' / 'bfill': use NEXT valid observation to fill
-how : {'start', 'end'}, default end
-    For PeriodIndex only, see PeriodIndex.asfreq
-normalize : bool, default False
-    Whether to reset output index to midnight
-fill_value: scalar, optional
-    Value to use for missing values, applied during upsampling (note
-    this does not fill NaNs that already were present).

-    .. versionadded:: 0.20.0

-Returns
--------
-converted : type of caller

-Examples
---------

-Start by creating a series with 4 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=4, freq='T')
->>> series = pd.Series([0.0, None, 2.0, 3.0], index=index)
->>> df = pd.DataFrame({'s':series})
->>> df
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:03:00    3.0

-Upsample the series into 30 second bins.

->>> df.asfreq(freq='30S')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    NaN
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``fill value``.

->>> df.asfreq(freq='30S', fill_value=9.0)
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    9.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    9.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    9.0
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``method``.

->>> df.asfreq(freq='30S', method='bfill')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    2.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    3.0
-2000-01-01 00:03:00    3.0

-See Also
---------
-reindex

-Notes
------
-To learn more about the frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
- -
asof(self, where, subset=None)
The last row without any NaN is taken (or the last row without
-NaN considering only the subset of columns in the case of a DataFrame)

-.. versionadded:: 0.19.0 For DataFrame

-If there is no good value, NaN is returned for a Series
-a Series of NaN values for a DataFrame

-Parameters
-----------
-where : date or array of dates
-subset : string or list of strings, default None
-   if not None use these columns for NaN propagation

-Notes
------
-Dates are assumed to be sorted
-Raises if this is not the case

-Returns
--------
-where is scalar

-  - value or NaN if input is Series
-  - Series if input is DataFrame

-where is Index: same shape object as input

-See Also
---------
-merge_asof
- -
astype(self, dtype, copy=True, errors='raise', **kwargs)
Cast object to input numpy.dtype
-Return a copy when copy = True (be really careful with this!)

-Parameters
-----------
-dtype : data type, or dict of column name -> data type
-    Use a numpy.dtype or Python type to cast entire pandas object to
-    the same type. Alternatively, use {col: dtype, ...}, where col is a
-    column label and dtype is a numpy.dtype or Python type to cast one
-    or more of the DataFrame's columns to column-specific types.
-errors : {'raise', 'ignore'}, default 'raise'.
-    Control raising of exceptions on invalid data for provided dtype.

-    - ``raise`` : allow exceptions to be raised
-    - ``ignore`` : suppress exceptions. On error return original object

-    .. versionadded:: 0.20.0

-raise_on_error : DEPRECATED use ``errors`` instead
-kwargs : keyword arguments to pass on to the constructor

-Returns
--------
-casted : type of caller
- -
at_time(self, time, asof=False)
Select values at particular time of day (e.g. 9:30AM).

-Parameters
-----------
-time : datetime.time or string

-Returns
--------
-values_at_time : type of caller
- -
between_time(self, start_time, end_time, include_start=True, include_end=True)
Select values between particular times of the day (e.g., 9:00-9:30 AM).

-Parameters
-----------
-start_time : datetime.time or string
-end_time : datetime.time or string
-include_start : boolean, default True
-include_end : boolean, default True

-Returns
--------
-values_between_time : type of caller
- -
bfill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='bfill') <DataFrame.fillna>`
- -
bool(self)
Return the bool of a single element PandasObject.

-This must be a boolean scalar value, either True or False.  Raise a
-ValueError if the PandasObject does not have exactly 1 element, or that
-element is not boolean
- -
clip(self, lower=None, upper=None, axis=None, *args, **kwargs)
Trim values at input threshold(s).

-Parameters
-----------
-lower : float or array_like, default None
-upper : float or array_like, default None
-axis : int or string axis name, optional
-    Align object with lower and upper along the given axis.

-Returns
--------
-clipped : Series

-Examples
---------
->>> df
-  0         1
-0  0.335232 -1.256177
-1 -1.367855  0.746646
-2  0.027753 -1.176076
-3  0.230930 -0.679613
-4  1.261967  0.570967
->>> df.clip(-1.0, 0.5)
-          0         1
-0  0.335232 -1.000000
-1 -1.000000  0.500000
-2  0.027753 -1.000000
-3  0.230930 -0.679613
-4  0.500000  0.500000
->>> t
-0   -0.3
-1   -0.2
-2   -0.1
-3    0.0
-4    0.1
-dtype: float64
->>> df.clip(t, t + 1, axis=0)
-          0         1
-0  0.335232 -0.300000
-1 -0.200000  0.746646
-2  0.027753 -0.100000
-3  0.230930  0.000000
-4  1.100000  0.570967
- -
clip_lower(self, threshold, axis=None)
Return copy of the input with values below given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
clip_upper(self, threshold, axis=None)
Return copy of input with values above given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
consolidate(self, inplace=False)
DEPRECATED: consolidate will be an internal implementation only.
- -
convert_objects(self, convert_dates=True, convert_numeric=False, convert_timedeltas=True, copy=True)
Deprecated.

-Attempt to infer better dtype for object columns

-Parameters
-----------
-convert_dates : boolean, default True
-    If True, convert to date where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-convert_numeric : boolean, default False
-    If True, attempt to coerce to numbers (including strings), with
-    unconvertible values becoming NaN.
-convert_timedeltas : boolean, default True
-    If True, convert to timedelta where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-copy : boolean, default True
-    If True, return a copy even if no copy is necessary (e.g. no
-    conversion was done). Note: This is meant for internal use, and
-    should not be confused with inplace.

-See Also
---------
-pandas.to_datetime : Convert argument to datetime.
-pandas.to_timedelta : Convert argument to timedelta.
-pandas.to_numeric : Return a fixed frequency timedelta index,
-    with day as the default.

-Returns
--------
-converted : same as input object
- -
copy(self, deep=True)
Make a copy of this objects data.

-Parameters
-----------
-deep : boolean or string, default True
-    Make a deep copy, including a copy of the data and the indices.
-    With ``deep=False`` neither the indices or the data are copied.

-    Note that when ``deep=True`` data is copied, actual python objects
-    will not be copied recursively, only the reference to the object.
-    This is in contrast to ``copy.deepcopy`` in the Standard Library,
-    which recursively copies object data.

-Returns
--------
-copy : type of caller
- -
describe(self, percentiles=None, include=None, exclude=None)
Generates descriptive statistics that summarize the central tendency,
-dispersion and shape of a dataset's distribution, excluding
-``NaN`` values.

-Analyzes both numeric and object series, as well
-as ``DataFrame`` column sets of mixed data types. The output
-will vary depending on what is provided. Refer to the notes
-below for more detail.

-Parameters
-----------
-percentiles : list-like of numbers, optional
-    The percentiles to include in the output. All should
-    fall between 0 and 1. The default is
-    ``[.25, .5, .75]``, which returns the 25th, 50th, and
-    75th percentiles.
-include : 'all', list-like of dtypes or None (default), optional
-    A white list of data types to include in the result. Ignored
-    for ``Series``. Here are the options:

-    - 'all' : All columns of the input will be included in the output.
-    - A list-like of dtypes : Limits the results to the
-      provided data types.
-      To limit the result to numeric types submit
-      ``numpy.number``. To limit it instead to categorical
-      objects submit the ``numpy.object`` data type. Strings
-      can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will include all numeric columns.
-exclude : list-like of dtypes or None (default), optional,
-    A black list of data types to omit from the result. Ignored
-    for ``Series``. Here are the options:

-    - A list-like of dtypes : Excludes the provided data types
-      from the result. To select numeric types submit
-      ``numpy.number``. To select categorical objects submit the data
-      type ``numpy.object``. Strings can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will exclude nothing.

-Returns
--------
-summary:  Series/DataFrame of summary statistics

-Notes
------
-For numeric data, the result's index will include ``count``,
-``mean``, ``std``, ``min``, ``max`` as well as lower, ``50`` and
-upper percentiles. By default the lower percentile is ``25`` and the
-upper percentile is ``75``. The ``50`` percentile is the
-same as the median.

-For object data (e.g. strings or timestamps), the result's index
-will include ``count``, ``unique``, ``top``, and ``freq``. The ``top``
-is the most common value. The ``freq`` is the most common value's
-frequency. Timestamps also include the ``first`` and ``last`` items.

-If multiple object values have the highest count, then the
-``count`` and ``top`` results will be arbitrarily chosen from
-among those with the highest count.

-For mixed data types provided via a ``DataFrame``, the default is to
-return only an analysis of numeric columns. If ``include='all'``
-is provided as an option, the result will include a union of
-attributes of each type.

-The `include` and `exclude` parameters can be used to limit
-which columns in a ``DataFrame`` are analyzed for the output.
-The parameters are ignored when analyzing a ``Series``.

-Examples
---------
-Describing a numeric ``Series``.

->>> s = pd.Series([1, 2, 3])
->>> s.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0

-Describing a categorical ``Series``.

->>> s = pd.Series(['a', 'a', 'b', 'c'])
->>> s.describe()
-count     4
-unique    3
-top       a
-freq      2
-dtype: object

-Describing a timestamp ``Series``.

->>> s = pd.Series([
-...   np.datetime64("2000-01-01"),
-...   np.datetime64("2010-01-01"),
-...   np.datetime64("2010-01-01")
-... ])
->>> s.describe()
-count                       3
-unique                      2
-top       2010-01-01 00:00:00
-freq                        2
-first     2000-01-01 00:00:00
-last      2010-01-01 00:00:00
-dtype: object

-Describing a ``DataFrame``. By default only numeric fields
-are returned.

->>> df = pd.DataFrame([[1, 'a'], [2, 'b'], [3, 'c']],
-...                   columns=['numeric', 'object'])
->>> df.describe()
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Describing all columns of a ``DataFrame`` regardless of data type.

->>> df.describe(include='all')
-        numeric object
-count       3.0      3
-unique      NaN      3
-top         NaN      b
-freq        NaN      1
-mean        2.0    NaN
-std         1.0    NaN
-min         1.0    NaN
-25%         1.5    NaN
-50%         2.0    NaN
-75%         2.5    NaN
-max         3.0    NaN

-Describing a column from a ``DataFrame`` by accessing it as
-an attribute.

->>> df.numeric.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0
-Name: numeric, dtype: float64

-Including only numeric columns in a ``DataFrame`` description.

->>> df.describe(include=[np.number])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Including only string columns in a ``DataFrame`` description.

->>> df.describe(include=[np.object])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding numeric columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.number])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding object columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.object])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-See Also
---------
-DataFrame.count
-DataFrame.max
-DataFrame.min
-DataFrame.mean
-DataFrame.std
-DataFrame.select_dtypes
- -
drop(self, labels, axis=0, level=None, inplace=False, errors='raise')
Return new object with labels in requested axis removed.

-Parameters
-----------
-labels : single label or list-like
-axis : int or axis name
-level : int or level name, default None
-    For MultiIndex
-inplace : bool, default False
-    If True, do operation inplace and return None.
-errors : {'ignore', 'raise'}, default 'raise'
-    If 'ignore', suppress error and existing labels are dropped.

-    .. versionadded:: 0.16.1

-Returns
--------
-dropped : type of caller
- -
equals(self, other)
Determines if two NDFrame objects contain the same elements. NaNs in
-the same location are considered equal.
- -
ffill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='ffill') <DataFrame.fillna>`
- -
filter(self, items=None, like=None, regex=None, axis=None)
Subset rows or columns of dataframe according to labels in
-the specified index.

-Note that this routine does not filter a dataframe on its
-contents. The filter is applied to the labels of the index.

-Parameters
-----------
-items : list-like
-    List of info axis to restrict to (must not all be present)
-like : string
-    Keep info axis where "arg in col == True"
-regex : string (regular expression)
-    Keep info axis with re.search(regex, col) == True
-axis : int or string axis name
-    The axis to filter on.  By default this is the info axis,
-    'index' for Series, 'columns' for DataFrame

-Returns
--------
-same type as input object

-Examples
---------
->>> df
-one  two  three
-mouse     1    2      3
-rabbit    4    5      6

->>> # select columns by name
->>> df.filter(items=['one', 'three'])
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select columns by regular expression
->>> df.filter(regex='e$', axis=1)
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select rows containing 'bbi'
->>> df.filter(like='bbi', axis=0)
-one  two  three
-rabbit    4    5      6

-See Also
---------
-pandas.DataFrame.select

-Notes
------
-The ``items``, ``like``, and ``regex`` parameters are
-enforced to be mutually exclusive.

-``axis`` defaults to the info axis that is used when indexing
-with ``[]``.
- -
first(self, offset)
Convenience method for subsetting initial periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.first('10D') -> First 10 days

-Returns
--------
-subset : type of caller
- -
get(self, key, default=None)
Get item from object for given key (DataFrame column, Panel slice,
-etc.). Returns default value if not found.

-Parameters
-----------
-key : object

-Returns
--------
-value : type of items contained in object
- -
get_dtype_counts(self)
Return the counts of dtypes in this object.
- -
get_ftype_counts(self)
Return the counts of ftypes in this object.
- -
groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, **kwargs)
Group series using mapper (dict or key function, apply given function
-to group, return result as series) or by a series of columns.

-Parameters
-----------
-by : mapping, function, str, or iterable
-    Used to determine the groups for the groupby.
-    If ``by`` is a function, it's called on each value of the object's
-    index. If a dict or Series is passed, the Series or dict VALUES
-    will be used to determine the groups (the Series' values are first
-    aligned; see ``.align()`` method). If an ndarray is passed, the
-    values are used as-is determine the groups. A str or list of strs
-    may be passed to group by the columns in ``self``
-axis : int, default 0
-level : int, level name, or sequence of such, default None
-    If the axis is a MultiIndex (hierarchical), group by a particular
-    level or levels
-as_index : boolean, default True
-    For aggregated output, return object with group labels as the
-    index. Only relevant for DataFrame input. as_index=False is
-    effectively "SQL-style" grouped output
-sort : boolean, default True
-    Sort group keys. Get better performance by turning this off.
-    Note this does not influence the order of observations within each
-    group.  groupby preserves the order of rows within each group.
-group_keys : boolean, default True
-    When calling apply, add group keys to index to identify pieces
-squeeze : boolean, default False
-    reduce the dimensionality of the return type if possible,
-    otherwise return a consistent type

-Examples
---------
-DataFrame results

->>> data.groupby(func, axis=0).mean()
->>> data.groupby(['col1', 'col2'])['col3'].mean()

-DataFrame with hierarchical index

->>> data.groupby(['col1', 'col2']).mean()

-Returns
--------
-GroupBy object
- -
head(self, n=5)
Returns first n rows
- -
interpolate(self, method='linear', axis=0, limit=None, inplace=False, limit_direction='forward', downcast=None, **kwargs)
Interpolate values according to different methods.

-Please note that only ``method='linear'`` is supported for
-DataFrames/Series with a MultiIndex.

-Parameters
-----------
-method : {'linear', 'time', 'index', 'values', 'nearest', 'zero',
-          'slinear', 'quadratic', 'cubic', 'barycentric', 'krogh',
-          'polynomial', 'spline', 'piecewise_polynomial',
-          'from_derivatives', 'pchip', 'akima'}

-    * 'linear': ignore the index and treat the values as equally
-      spaced. This is the only method supported on MultiIndexes.
-      default
-    * 'time': interpolation works on daily and higher resolution
-      data to interpolate given length of interval
-    * 'index', 'values': use the actual numerical values of the index
-    * 'nearest', 'zero', 'slinear', 'quadratic', 'cubic',
-      'barycentric', 'polynomial' is passed to
-      ``scipy.interpolate.interp1d``. Both 'polynomial' and 'spline'
-      require that you also specify an `order` (int),
-      e.g. df.interpolate(method='polynomial', order=4).
-      These use the actual numerical values of the index.
-    * 'krogh', 'piecewise_polynomial', 'spline', 'pchip' and 'akima'
-      are all wrappers around the scipy interpolation methods of
-      similar names. These use the actual numerical values of the
-      index. For more information on their behavior, see the
-      `scipy documentation
-      <http://docs.scipy.org/doc/scipy/reference/interpolate.html#univariate-interpolation>`__
-      and `tutorial documentation
-      <http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html>`__
-    * 'from_derivatives' refers to BPoly.from_derivatives which
-      replaces 'piecewise_polynomial' interpolation method in
-      scipy 0.18

-    .. versionadded:: 0.18.1

-       Added support for the 'akima' method
-       Added interpolate method 'from_derivatives' which replaces
-       'piecewise_polynomial' in scipy 0.18; backwards-compatible with
-       scipy < 0.18

-axis : {0, 1}, default 0
-    * 0: fill column-by-column
-    * 1: fill row-by-row
-limit : int, default None.
-    Maximum number of consecutive NaNs to fill. Must be greater than 0.
-limit_direction : {'forward', 'backward', 'both'}, default 'forward'
-    If limit is specified, consecutive NaNs will be filled in this
-    direction.

-    .. versionadded:: 0.17.0

-inplace : bool, default False
-    Update the NDFrame in place if possible.
-downcast : optional, 'infer' or None, defaults to None
-    Downcast dtypes if possible.
-kwargs : keyword arguments to pass on to the interpolating function.

-Returns
--------
-Series or DataFrame of same shape interpolated at the NaNs

-See Also
---------
-reindex, replace, fillna

-Examples
---------

-Filling in NaNs

->>> s = pd.Series([0, 1, np.nan, 3])
->>> s.interpolate()
-0    0
-1    1
-2    2
-3    3
-dtype: float64
- -
isnull(self)
Return a boolean same-sized object indicating if the values are null.

-See Also
---------
-notnull : boolean inverse of isnull
- -
last(self, offset)
Convenience method for subsetting final periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.last('5M') -> Last 5 months

-Returns
--------
-subset : type of caller
- -
mask(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is False and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The mask method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``False`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``mask`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.where`
- -
notnull(self)
Return a boolean same-sized object indicating if the values are
-not null.

-See Also
---------
-isnull : boolean inverse of notnull
- -
pct_change(self, periods=1, fill_method='pad', limit=None, freq=None, **kwargs)
Percent change over given number of periods.

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming percent change
-fill_method : str, default 'pad'
-    How to handle NAs before computing percent changes
-limit : int, default None
-    The number of consecutive NAs to fill before stopping
-freq : DateOffset, timedelta, or offset alias string, optional
-    Increment to use from time series API (e.g. 'M' or BDay())

-Returns
--------
-chg : NDFrame

-Notes
------

-By default, the percentage change is calculated along the stat
-axis: 0, or ``Index``, for ``DataFrame`` and 1, or ``minor`` for
-``Panel``. You can change this with the ``axis`` keyword argument.
- -
pipe(self, func, *args, **kwargs)
Apply func(self, \*args, \*\*kwargs)

-.. versionadded:: 0.16.2

-Parameters
-----------
-func : function
-    function to apply to the NDFrame.
-    ``args``, and ``kwargs`` are passed into ``func``.
-    Alternatively a ``(callable, data_keyword)`` tuple where
-    ``data_keyword`` is a string indicating the keyword of
-    ``callable`` that expects the NDFrame.
-args : positional arguments passed into ``func``.
-kwargs : a dictionary of keyword arguments passed into ``func``.

-Returns
--------
-object : the return type of ``func``.

-Notes
------

-Use ``.pipe`` when chaining together functions that expect
-on Series or DataFrames. Instead of writing

->>> f(g(h(df), arg1=a), arg2=b, arg3=c)

-You can write

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe(f, arg2=b, arg3=c)
-... )

-If you have a function that takes the data as (say) the second
-argument, pass a tuple indicating which keyword expects the
-data. For example, suppose ``f`` takes its data as ``arg2``:

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe((f, 'arg2'), arg1=a, arg3=c)
-...  )

-See Also
---------
-pandas.DataFrame.apply
-pandas.DataFrame.applymap
-pandas.Series.map
- -
pop(self, item)
Return item and drop from frame. Raise KeyError if not found.
- -
rank(self, axis=0, method='average', numeric_only=None, na_option='keep', ascending=True, pct=False)
Compute numerical data ranks (1 through n) along axis. Equal values are
-assigned a rank that is the average of the ranks of those values

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    index to direct ranking
-method : {'average', 'min', 'max', 'first', 'dense'}
-    * average: average rank of group
-    * min: lowest rank in group
-    * max: highest rank in group
-    * first: ranks assigned in order they appear in the array
-    * dense: like 'min', but rank always increases by 1 between groups
-numeric_only : boolean, default None
-    Include only float, int, boolean data. Valid only for DataFrame or
-    Panel objects
-na_option : {'keep', 'top', 'bottom'}
-    * keep: leave NA values where they are
-    * top: smallest rank if ascending
-    * bottom: smallest rank if descending
-ascending : boolean, default True
-    False for ranks by high (1) to low (N)
-pct : boolean, default False
-    Computes percentage rank of data

-Returns
--------
-ranks : same type as caller
- -
reindex_like(self, other, method=None, copy=True, limit=None, tolerance=None)
Return an object with matching indices to myself.

-Parameters
-----------
-other : Object
-method : string or None
-copy : boolean, default True
-limit : int, default None
-    Maximum number of consecutive labels to fill for inexact matches.
-tolerance : optional
-    Maximum distance between labels of the other object and this
-    object for inexact matches.

-    .. versionadded:: 0.17.0

-Notes
------
-Like calling s.reindex(index=other.index, columns=other.columns,
-                       method=...)

-Returns
--------
-reindexed : same as input
- -
rename_axis(self, mapper, axis=0, copy=True, inplace=False)
Alter index and / or columns using input function or functions.
-A scalar or list-like for ``mapper`` will alter the ``Index.name``
-or ``MultiIndex.names`` attribute.
-A function or dict for ``mapper`` will alter the labels.
-Function / dict values must be unique (1-to-1). Labels not contained in
-a dict / Series will be left as-is.

-Parameters
-----------
-mapper : scalar, list-like, dict-like or function, optional
-axis : int or string, default 0
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False

-Returns
--------
-renamed : type of caller

-See Also
---------
-pandas.NDFrame.rename
-pandas.Index.rename

-Examples
---------
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename_axis("foo")  # scalar, alters df.index.name
-     A  B
-foo
-0    1  4
-1    2  5
-2    3  6
->>> df.rename_axis(lambda x: 2 * x)  # function: alters labels
-   A  B
-0  1  4
-2  2  5
-4  3  6
->>> df.rename_axis({"A": "ehh", "C": "see"}, axis="columns")  # mapping
-   ehh  B
-0    1  4
-1    2  5
-2    3  6
- -
replace(self, to_replace=None, value=None, inplace=False, limit=None, regex=False, method='pad', axis=None)
Replace values given in 'to_replace' with 'value'.

-Parameters
-----------
-to_replace : str, regex, list, dict, Series, numeric, or None

-    * str or regex:

-        - str: string exactly matching `to_replace` will be replaced
-          with `value`
-        - regex: regexs matching `to_replace` will be replaced with
-          `value`

-    * list of str, regex, or numeric:

-        - First, if `to_replace` and `value` are both lists, they
-          **must** be the same length.
-        - Second, if ``regex=True`` then all of the strings in **both**
-          lists will be interpreted as regexs otherwise they will match
-          directly. This doesn't matter much for `value` since there
-          are only a few possible substitution regexes you can use.
-        - str and regex rules apply as above.

-    * dict:

-        - Nested dictionaries, e.g., {'a': {'b': nan}}, are read as
-          follows: look in column 'a' for the value 'b' and replace it
-          with nan. You can nest regular expressions as well. Note that
-          column names (the top-level dictionary keys in a nested
-          dictionary) **cannot** be regular expressions.
-        - Keys map to column names and values map to substitution
-          values. You can treat this as a special case of passing two
-          lists except that you are specifying the column to search in.

-    * None:

-        - This means that the ``regex`` argument must be a string,
-          compiled regular expression, or list, dict, ndarray or Series
-          of such elements. If `value` is also ``None`` then this
-          **must** be a nested dictionary or ``Series``.

-    See the examples section for examples of each of these.
-value : scalar, dict, list, str, regex, default None
-    Value to use to fill holes (e.g. 0), alternately a dict of values
-    specifying which value to use for each column (columns not in the
-    dict will not be filled). Regular expressions, strings and lists or
-    dicts of such objects are also allowed.
-inplace : boolean, default False
-    If True, in place. Note: this will modify any
-    other views on this object (e.g. a column form a DataFrame).
-    Returns the caller if this is True.
-limit : int, default None
-    Maximum size gap to forward or backward fill
-regex : bool or same types as `to_replace`, default False
-    Whether to interpret `to_replace` and/or `value` as regular
-    expressions. If this is ``True`` then `to_replace` *must* be a
-    string. Otherwise, `to_replace` must be ``None`` because this
-    parameter will be interpreted as a regular expression or a list,
-    dict, or array of regular expressions.
-method : string, optional, {'pad', 'ffill', 'bfill'}
-    The method to use when for replacement, when ``to_replace`` is a
-    ``list``.

-See Also
---------
-NDFrame.reindex
-NDFrame.asfreq
-NDFrame.fillna

-Returns
--------
-filled : NDFrame

-Raises
-------
-AssertionError
-    * If `regex` is not a ``bool`` and `to_replace` is not ``None``.
-TypeError
-    * If `to_replace` is a ``dict`` and `value` is not a ``list``,
-      ``dict``, ``ndarray``, or ``Series``
-    * If `to_replace` is ``None`` and `regex` is not compilable into a
-      regular expression or is a list, dict, ndarray, or Series.
-ValueError
-    * If `to_replace` and `value` are ``list`` s or ``ndarray`` s, but
-      they are not the same length.

-Notes
------
-* Regex substitution is performed under the hood with ``re.sub``. The
-  rules for substitution for ``re.sub`` are the same.
-* Regular expressions will only substitute on strings, meaning you
-  cannot provide, for example, a regular expression matching floating
-  point numbers and expect the columns in your frame that have a
-  numeric dtype to be matched. However, if those floating point numbers
-  *are* strings, then you can do this.
-* This method has *a lot* of options. You are encouraged to experiment
-  and play with this method to gain intuition about how it works.
- -
resample(self, rule, how=None, axis=0, fill_method=None, closed=None, label=None, convention='start', kind=None, loffset=None, limit=None, base=0, on=None, level=None)
Convenience method for frequency conversion and resampling of time
-series.  Object must have a datetime-like index (DatetimeIndex,
-PeriodIndex, or TimedeltaIndex), or pass datetime-like values
-to the on or level keyword.

-Parameters
-----------
-rule : string
-    the offset string or object representing target conversion
-axis : int, optional, default 0
-closed : {'right', 'left'}
-    Which side of bin interval is closed
-label : {'right', 'left'}
-    Which bin edge label to label bucket with
-convention : {'start', 'end', 's', 'e'}
-loffset : timedelta
-    Adjust the resampled time labels
-base : int, default 0
-    For frequencies that evenly subdivide 1 day, the "origin" of the
-    aggregated intervals. For example, for '5min' frequency, base could
-    range from 0 through 4. Defaults to 0
-on : string, optional
-    For a DataFrame, column to use instead of index for resampling.
-    Column must be datetime-like.

-    .. versionadded:: 0.19.0

-level : string or int, optional
-    For a MultiIndex, level (name or number) to use for
-    resampling.  Level must be datetime-like.

-    .. versionadded:: 0.19.0

-Notes
------
-To learn more about the offset strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-Examples
---------

-Start by creating a series with 9 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=9, freq='T')
->>> series = pd.Series(range(9), index=index)
->>> series
-2000-01-01 00:00:00    0
-2000-01-01 00:01:00    1
-2000-01-01 00:02:00    2
-2000-01-01 00:03:00    3
-2000-01-01 00:04:00    4
-2000-01-01 00:05:00    5
-2000-01-01 00:06:00    6
-2000-01-01 00:07:00    7
-2000-01-01 00:08:00    8
-Freq: T, dtype: int64

-Downsample the series into 3 minute bins and sum the values
-of the timestamps falling into a bin.

->>> series.resample('3T').sum()
-2000-01-01 00:00:00     3
-2000-01-01 00:03:00    12
-2000-01-01 00:06:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but label each
-bin using the right edge instead of the left. Please note that the
-value in the bucket used as the label is not included in the bucket,
-which it labels. For example, in the original series the
-bucket ``2000-01-01 00:03:00`` contains the value 3, but the summed
-value in the resampled bucket with the label``2000-01-01 00:03:00``
-does not include 3 (if it did, the summed value would be 6, not 3).
-To include this value close the right side of the bin interval as
-illustrated in the example below this one.

->>> series.resample('3T', label='right').sum()
-2000-01-01 00:03:00     3
-2000-01-01 00:06:00    12
-2000-01-01 00:09:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but close the right
-side of the bin interval.

->>> series.resample('3T', label='right', closed='right').sum()
-2000-01-01 00:00:00     0
-2000-01-01 00:03:00     6
-2000-01-01 00:06:00    15
-2000-01-01 00:09:00    15
-Freq: 3T, dtype: int64

-Upsample the series into 30 second bins.

->>> series.resample('30S').asfreq()[0:5] #select first 5 rows
-2000-01-01 00:00:00   0.0
-2000-01-01 00:00:30   NaN
-2000-01-01 00:01:00   1.0
-2000-01-01 00:01:30   NaN
-2000-01-01 00:02:00   2.0
-Freq: 30S, dtype: float64

-Upsample the series into 30 second bins and fill the ``NaN``
-values using the ``pad`` method.

->>> series.resample('30S').pad()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    0
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    1
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Upsample the series into 30 second bins and fill the
-``NaN`` values using the ``bfill`` method.

->>> series.resample('30S').bfill()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    1
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    2
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Pass a custom function via ``apply``

->>> def custom_resampler(array_like):
-...     return np.sum(array_like)+5

->>> series.resample('3T').apply(custom_resampler)
-2000-01-01 00:00:00     8
-2000-01-01 00:03:00    17
-2000-01-01 00:06:00    26
-Freq: 3T, dtype: int64

-For DataFrame objects, the keyword ``on`` can be used to specify the
-column instead of the index for resampling.

->>> df = pd.DataFrame(data=9*[range(4)], columns=['a', 'b', 'c', 'd'])
->>> df['time'] = pd.date_range('1/1/2000', periods=9, freq='T')
->>> df.resample('3T', on='time').sum()
-                     a  b  c  d
-time
-2000-01-01 00:00:00  0  3  6  9
-2000-01-01 00:03:00  0  3  6  9
-2000-01-01 00:06:00  0  3  6  9

-For a DataFrame with MultiIndex, the keyword ``level`` can be used to
-specify on level the resampling needs to take place.

->>> time = pd.date_range('1/1/2000', periods=5, freq='T')
->>> df2 = pd.DataFrame(data=10*[range(4)],
-                       columns=['a', 'b', 'c', 'd'],
-                       index=pd.MultiIndex.from_product([time, [1, 2]])
-                       )
->>> df2.resample('3T', level=0).sum()
-                     a  b   c   d
-2000-01-01 00:00:00  0  6  12  18
-2000-01-01 00:03:00  0  4   8  12
- -
sample(self, n=None, frac=None, replace=False, weights=None, random_state=None, axis=None)
Returns a random sample of items from an axis of object.

-.. versionadded:: 0.16.1

-Parameters
-----------
-n : int, optional
-    Number of items from axis to return. Cannot be used with `frac`.
-    Default = 1 if `frac` = None.
-frac : float, optional
-    Fraction of axis items to return. Cannot be used with `n`.
-replace : boolean, optional
-    Sample with or without replacement. Default = False.
-weights : str or ndarray-like, optional
-    Default 'None' results in equal probability weighting.
-    If passed a Series, will align with target object on index. Index
-    values in weights not found in sampled object will be ignored and
-    index values in sampled object not in weights will be assigned
-    weights of zero.
-    If called on a DataFrame, will accept the name of a column
-    when axis = 0.
-    Unless weights are a Series, weights must be same length as axis
-    being sampled.
-    If weights do not sum to 1, they will be normalized to sum to 1.
-    Missing values in the weights column will be treated as zero.
-    inf and -inf values not allowed.
-random_state : int or numpy.random.RandomState, optional
-    Seed for the random number generator (if int), or numpy RandomState
-    object.
-axis : int or string, optional
-    Axis to sample. Accepts axis number or name. Default is stat axis
-    for given data type (0 for Series and DataFrames, 1 for Panels).

-Returns
--------
-A new object of same type as caller.

-Examples
---------

-Generate an example ``Series`` and ``DataFrame``:

->>> s = pd.Series(np.random.randn(50))
->>> s.head()
-0   -0.038497
-1    1.820773
-2   -0.972766
-3   -1.598270
-4   -1.095526
-dtype: float64
->>> df = pd.DataFrame(np.random.randn(50, 4), columns=list('ABCD'))
->>> df.head()
-          A         B         C         D
-0  0.016443 -2.318952 -0.566372 -1.028078
-1 -1.051921  0.438836  0.658280 -0.175797
-2 -1.243569 -0.364626 -0.215065  0.057736
-3  1.768216  0.404512 -0.385604 -1.457834
-4  1.072446 -1.137172  0.314194 -0.046661

-Next extract a random sample from both of these objects...

-3 random elements from the ``Series``:

->>> s.sample(n=3)
-27   -0.994689
-55   -1.049016
-67   -0.224565
-dtype: float64

-And a random 10% of the ``DataFrame`` with replacement:

->>> df.sample(frac=0.1, replace=True)
-           A         B         C         D
-35  1.981780  0.142106  1.817165 -0.290805
-49 -1.336199 -0.448634 -0.789640  0.217116
-40  0.823173 -0.078816  1.009536  1.015108
-15  1.421154 -0.055301 -1.922594 -0.019696
-6  -0.148339  0.832938  1.787600 -1.383767
- -
select(self, crit, axis=0)
Return data corresponding to axis labels matching criteria

-Parameters
-----------
-crit : function
-    To be called on each index (label). Should return True or False
-axis : int

-Returns
--------
-selection : type of caller
- -
set_axis(self, axis, labels)
public verson of axis assignment
- -
slice_shift(self, periods=1, axis=0)
Equivalent to `shift` without copying data. The shifted data will
-not include the dropped periods and the shifted axis will be smaller
-than the original.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative

-Notes
------
-While the `slice_shift` is faster than `shift`, you may pay for it
-later during alignment.

-Returns
--------
-shifted : same type as caller
- -
squeeze(self, axis=None)
Squeeze length 1 dimensions.

-Parameters
-----------
-axis : None, integer or string axis name, optional
-    The axis to squeeze if 1-sized.

-    .. versionadded:: 0.20.0

-Returns
--------
-scalar if 1-sized, else original object
- -
swapaxes(self, axis1, axis2, copy=True)
Interchange axes and swap values axes appropriately

-Returns
--------
-y : same as input
- -
tail(self, n=5)
Returns last n rows
- -
to_clipboard(self, excel=None, sep=None, **kwargs)
Attempt to write text representation of object to the system clipboard
-This can be pasted into Excel, for example.

-Parameters
-----------
-excel : boolean, defaults to True
-        if True, use the provided separator, writing in a csv
-        format for allowing easy pasting into excel.
-        if False, write a string representation of the object
-        to the clipboard
-sep : optional, defaults to tab
-other keywords are passed to to_csv

-Notes
------
-Requirements for your platform
-  - Linux: xclip, or xsel (with gtk or PyQt4 modules)
-  - Windows: none
-  - OS X: none
- -
to_dense(self)
Return dense representation of NDFrame (as opposed to sparse)
- -
to_hdf(self, path_or_buf, key, **kwargs)
Write the contained data to an HDF5 file using HDFStore.

-Parameters
-----------
-path_or_buf : the path (string) or HDFStore object
-key : string
-    identifier for the group in the store
-mode : optional, {'a', 'w', 'r+'}, default 'a'

-  ``'w'``
-      Write; a new file is created (an existing file with the same
-      name would be deleted).
-  ``'a'``
-      Append; an existing file is opened for reading and writing,
-      and if the file does not exist it is created.
-  ``'r+'``
-      It is similar to ``'a'``, but the file must already exist.
-format : 'fixed(f)|table(t)', default is 'fixed'
-    fixed(f) : Fixed format
-               Fast writing/reading. Not-appendable, nor searchable
-    table(t) : Table format
-               Write as a PyTables Table structure which may perform
-               worse but allow more flexible operations like searching
-               / selecting subsets of the data
-append : boolean, default False
-    For Table formats, append the input data to the existing
-data_columns :  list of columns, or True, default None
-    List of columns to create as indexed data columns for on-disk
-    queries, or True to use all columns. By default only the axes
-    of the object are indexed. See `here
-    <http://pandas.pydata.org/pandas-docs/stable/io.html#query-via-data-columns>`__.

-    Applicable only to format='table'.
-complevel : int, 1-9, default 0
-    If a complib is specified compression will be applied
-    where possible
-complib : {'zlib', 'bzip2', 'lzo', 'blosc', None}, default None
-    If complevel is > 0 apply compression to objects written
-    in the store wherever possible
-fletcher32 : bool, default False
-    If applying compression use the fletcher32 checksum
-dropna : boolean, default False.
-    If true, ALL nan rows will not be written to store.
- -
to_json(self, path_or_buf=None, orient=None, date_format=None, double_precision=10, force_ascii=True, date_unit='ms', default_handler=None, lines=False)
Convert the object to a JSON string.

-Note NaN's and None will be converted to null and datetime objects
-will be converted to UNIX timestamps.

-Parameters
-----------
-path_or_buf : the path or buffer to write the result string
-    if this is None, return a StringIO of the converted string
-orient : string

-    * Series

-      - default is 'index'
-      - allowed values are: {'split','records','index'}

-    * DataFrame

-      - default is 'columns'
-      - allowed values are:
-        {'split','records','index','columns','values'}

-    * The format of the JSON string

-      - split : dict like
-        {index -> [index], columns -> [columns], data -> [values]}
-      - records : list like
-        [{column -> value}, ... , {column -> value}]
-      - index : dict like {index -> {column -> value}}
-      - columns : dict like {column -> {index -> value}}
-      - values : just the values array
-      - table : dict like {'schema': {schema}, 'data': {data}}
-        describing the data, and the data component is
-        like ``orient='records'``.

-        .. versionchanged:: 0.20.0

-date_format : {None, 'epoch', 'iso'}
-    Type of date conversion. `epoch` = epoch milliseconds,
-    `iso` = ISO8601. The default depends on the `orient`. For
-    `orient='table'`, the default is `'iso'`. For all other orients,
-    the default is `'epoch'`.
-double_precision : The number of decimal places to use when encoding
-    floating point values, default 10.
-force_ascii : force encoded string to be ASCII, default True.
-date_unit : string, default 'ms' (milliseconds)
-    The time unit to encode to, governs timestamp and ISO8601
-    precision.  One of 's', 'ms', 'us', 'ns' for second, millisecond,
-    microsecond, and nanosecond respectively.
-default_handler : callable, default None
-    Handler to call if object cannot otherwise be converted to a
-    suitable format for JSON. Should receive a single argument which is
-    the object to convert and return a serialisable object.
-lines : boolean, default False
-    If 'orient' is 'records' write out line delimited json format. Will
-    throw ValueError if incorrect 'orient' since others are not list
-    like.

-    .. versionadded:: 0.19.0

-Returns
--------
-same type as input object with filtered info axis

-See Also
---------
-pd.read_json

-Examples
---------

->>> df = pd.DataFrame([['a', 'b'], ['c', 'd']],
-...                   index=['row 1', 'row 2'],
-...                   columns=['col 1', 'col 2'])
->>> df.to_json(orient='split')
-'{"columns":["col 1","col 2"],
-  "index":["row 1","row 2"],
-  "data":[["a","b"],["c","d"]]}'

-Encoding/decoding a Dataframe using ``'index'`` formatted JSON:

->>> df.to_json(orient='index')
-'{"row 1":{"col 1":"a","col 2":"b"},"row 2":{"col 1":"c","col 2":"d"}}'

-Encoding/decoding a Dataframe using ``'records'`` formatted JSON.
-Note that index labels are not preserved with this encoding.

->>> df.to_json(orient='records')
-'[{"col 1":"a","col 2":"b"},{"col 1":"c","col 2":"d"}]'

-Encoding with Table Schema

->>> df.to_json(orient='table')
-'{"schema": {"fields": [{"name": "index", "type": "string"},
-                        {"name": "col 1", "type": "string"},
-                        {"name": "col 2", "type": "string"}],
-             "primaryKey": "index",
-             "pandas_version": "0.20.0"},
-  "data": [{"index": "row 1", "col 1": "a", "col 2": "b"},
-           {"index": "row 2", "col 1": "c", "col 2": "d"}]}'
- -
to_msgpack(self, path_or_buf=None, encoding='utf-8', **kwargs)
msgpack (serialize) object to input file path

-THIS IS AN EXPERIMENTAL LIBRARY and the storage format
-may not be stable until a future release.

-Parameters
-----------
-path : string File path, buffer-like, or None
-    if None, return generated string
-append : boolean whether to append to an existing msgpack
-    (default is False)
-compress : type of compressor (zlib or blosc), default to None (no
-    compression)
- -
to_pickle(self, path, compression='infer')
Pickle (serialize) object to input file path.

-Parameters
-----------
-path : string
-    File path
-compression : {'infer', 'gzip', 'bz2', 'xz', None}, default 'infer'
-    a string representing the compression to use in the output file

-    .. versionadded:: 0.20.0
- -
to_sql(self, name, con, flavor=None, schema=None, if_exists='fail', index=True, index_label=None, chunksize=None, dtype=None)
Write records stored in a DataFrame to a SQL database.

-Parameters
-----------
-name : string
-    Name of SQL table
-con : SQLAlchemy engine or DBAPI2 connection (legacy mode)
-    Using SQLAlchemy makes it possible to use any DB supported by that
-    library. If a DBAPI2 object, only sqlite3 is supported.
-flavor : 'sqlite', default None
-    DEPRECATED: this parameter will be removed in a future version,
-    as 'sqlite' is the only supported option if SQLAlchemy is not
-    installed.
-schema : string, default None
-    Specify the schema (if database flavor supports this). If None, use
-    default schema.
-if_exists : {'fail', 'replace', 'append'}, default 'fail'
-    - fail: If table exists, do nothing.
-    - replace: If table exists, drop it, recreate it, and insert data.
-    - append: If table exists, insert data. Create if does not exist.
-index : boolean, default True
-    Write DataFrame index as a column.
-index_label : string or sequence, default None
-    Column label for index column(s). If None is given (default) and
-    `index` is True, then the index names are used.
-    A sequence should be given if the DataFrame uses MultiIndex.
-chunksize : int, default None
-    If not None, then rows will be written in batches of this size at a
-    time.  If None, all rows will be written at once.
-dtype : dict of column name to SQL type, default None
-    Optional specifying the datatype for columns. The SQL type should
-    be a SQLAlchemy type, or a string for sqlite3 fallback connection.
- -
to_xarray(self)
Return an xarray object from the pandas object.

-Returns
--------
-a DataArray for a Series
-a Dataset for a DataFrame
-a DataArray for higher dims

-Examples
---------
->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)})
->>> df
-   A    B    C
-0  1  foo  4.0
-1  1  bar  5.0
-2  2  foo  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (index: 3)
-Coordinates:
-  * index    (index) int64 0 1 2
-Data variables:
-    A        (index) int64 1 1 2
-    B        (index) object 'foo' 'bar' 'foo'
-    C        (index) float64 4.0 5.0 6.0

->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)}
-                     ).set_index(['B','A'])
->>> df
-         C
-B   A
-foo 1  4.0
-bar 1  5.0
-foo 2  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (A: 2, B: 2)
-Coordinates:
-  * B        (B) object 'bar' 'foo'
-  * A        (A) int64 1 2
-Data variables:
-    C        (B, A) float64 5.0 nan 4.0 6.0

->>> p = pd.Panel(np.arange(24).reshape(4,3,2),
-                 items=list('ABCD'),
-                 major_axis=pd.date_range('20130101', periods=3),
-                 minor_axis=['first', 'second'])
->>> p
-<class 'pandas.core.panel.Panel'>
-Dimensions: 4 (items) x 3 (major_axis) x 2 (minor_axis)
-Items axis: A to D
-Major_axis axis: 2013-01-01 00:00:00 to 2013-01-03 00:00:00
-Minor_axis axis: first to second

->>> p.to_xarray()
-<xarray.DataArray (items: 4, major_axis: 3, minor_axis: 2)>
-array([[[ 0,  1],
-        [ 2,  3],
-        [ 4,  5]],
-       [[ 6,  7],
-        [ 8,  9],
-        [10, 11]],
-       [[12, 13],
-        [14, 15],
-        [16, 17]],
-       [[18, 19],
-        [20, 21],
-        [22, 23]]])
-Coordinates:
-  * items       (items) object 'A' 'B' 'C' 'D'
-  * major_axis  (major_axis) datetime64[ns] 2013-01-01 2013-01-02 2013-01-03  # noqa
-  * minor_axis  (minor_axis) object 'first' 'second'

-Notes
------
-See the `xarray docs <http://xarray.pydata.org/en/stable/>`__
- -
truncate(self, before=None, after=None, axis=None, copy=True)
Truncates a sorted NDFrame before and/or after some particular
-index value. If the axis contains only datetime values, before/after
-parameters are converted to datetime values.

-Parameters
-----------
-before : date
-    Truncate before index value
-after : date
-    Truncate after index value
-axis : the truncation axis, defaults to the stat axis
-copy : boolean, default is True,
-    return a copy of the truncated section

-Returns
--------
-truncated : type of caller
- -
tshift(self, periods=1, freq=None, axis=0)
Shift the time index, using the index's frequency if available.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, default None
-    Increment to use from the tseries module or time rule (e.g. 'EOM')
-axis : int or basestring
-    Corresponds to the axis that contains the Index

-Notes
------
-If freq is not specified then tries to use the freq or inferred_freq
-attributes of the index. If neither of those attributes exist, a
-ValueError is thrown

-Returns
--------
-shifted : NDFrame
- -
tz_convert(self, tz, axis=0, level=None, copy=True)
Convert tz-aware axis to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to convert
-level : int, str, default None
-    If axis ia a MultiIndex, convert a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data

-Returns
--------

-Raises
-------
-TypeError
-    If the axis is tz-naive.
- -
tz_localize(self, tz, axis=0, level=None, copy=True, ambiguous='raise')
Localize tz-naive TimeSeries to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to localize
-level : int, str, default None
-    If axis ia a MultiIndex, localize a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data
-ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise'
-    - 'infer' will attempt to infer fall dst-transition hours based on
-      order
-    - bool-ndarray where True signifies a DST time, False designates
-      a non-DST time (note that this flag is only applicable for
-      ambiguous times)
-    - 'NaT' will return NaT where there are ambiguous times
-    - 'raise' will raise an AmbiguousTimeError if there are ambiguous
-      times
-infer_dst : boolean, default False (DEPRECATED)
-    Attempt to infer fall dst-transition hours based on order

-Returns
--------

-Raises
-------
-TypeError
-    If the TimeSeries is tz-aware and tz is not None.
- -
where(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is True and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The where method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``True`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``where`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.mask`
- -
xs(self, key, axis=0, level=None, drop_level=True)
Returns a cross-section (row(s) or column(s)) from the
-Series/DataFrame. Defaults to cross-section on the rows (axis=0).

-Parameters
-----------
-key : object
-    Some label contained in the index, or partially in a MultiIndex
-axis : int, default 0
-    Axis to retrieve cross-section on
-level : object, defaults to first n levels (n=1 or len(key))
-    In case of a key partially contained in a MultiIndex, indicate
-    which levels are used. Levels can be referred by label or position.
-drop_level : boolean, default True
-    If False, returns object with same levels as self.

-Examples
---------
->>> df
-   A  B  C
-a  4  5  2
-b  4  0  9
-c  9  7  3
->>> df.xs('a')
-A    4
-B    5
-C    2
-Name: a
->>> df.xs('C', axis=1)
-a    2
-b    9
-c    3
-Name: C

->>> df
-                    A  B  C  D
-first second third
-bar   one    1      4  1  8  9
-      two    1      7  5  5  0
-baz   one    1      6  6  8  0
-      three  2      5  3  5  3
->>> df.xs(('baz', 'three'))
-       A  B  C  D
-third
-2      5  3  5  3
->>> df.xs('one', level=1)
-             A  B  C  D
-first third
-bar   1      4  1  8  9
-baz   1      6  6  8  0
->>> df.xs(('baz', 2), level=[0, 'third'])
-        A  B  C  D
-second
-three   5  3  5  3

-Returns
--------
-xs : Series or DataFrame

-Notes
------
-xs is only for getting, not setting values.

-MultiIndex Slicers is a generic way to get/set values on any level or
-levels.  It is a superset of xs functionality, see
-:ref:`MultiIndex Slicers <advanced.mi_slicers>`
- -
-Data descriptors inherited from pandas.core.generic.NDFrame:
-
at
-
Fast label-based scalar accessor

-Similarly to ``loc``, ``at`` provides **label** based scalar lookups.
-You can also set using these indexers.
-
-
blocks
-
Internal property, property synonym for as_blocks()
-
-
iat
-
Fast integer location scalar accessor.

-Similarly to ``iloc``, ``iat`` provides **integer** based lookups.
-You can also set using these indexers.
-
-
iloc
-
Purely integer-location based indexing for selection by position.

-``.iloc[]`` is primarily integer position based (from ``0`` to
-``length-1`` of the axis), but may also be used with a boolean
-array.

-Allowed inputs are:

-- An integer, e.g. ``5``.
-- A list or array of integers, e.g. ``[4, 3, 0]``.
-- A slice object with ints, e.g. ``1:7``.
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.iloc`` will raise ``IndexError`` if a requested indexer is
-out-of-bounds, except *slice* indexers which allow out-of-bounds
-indexing (this conforms with python/numpy *slice* semantics).

-See more at :ref:`Selection by Position <indexing.integer>`
-
-
ix
-
A primarily label-location based indexer, with integer position
-fallback.

-``.ix[]`` supports mixed integer and label based access. It is
-primarily label based, but will fall back to integer positional
-access unless the corresponding axis is of integer type.

-``.ix`` is the most general indexer and will support any of the
-inputs in ``.loc`` and ``.iloc``. ``.ix`` also supports floating
-point label schemes. ``.ix`` is exceptionally useful when dealing
-with mixed positional and label based hierachical indexes.

-However, when an axis is integer based, ONLY label based access
-and not positional access is supported. Thus, in such cases, it's
-usually better to be explicit and use ``.iloc`` or ``.loc``.

-See more at :ref:`Advanced Indexing <advanced>`.
-
-
loc
-
Purely label-location based indexer for selection by label.

-``.loc[]`` is primarily label based, but may also be used with a
-boolean array.

-Allowed inputs are:

-- A single label, e.g. ``5`` or ``'a'``, (note that ``5`` is
-  interpreted as a *label* of the index, and **never** as an
-  integer position along the index).
-- A list or array of labels, e.g. ``['a', 'b', 'c']``.
-- A slice object with labels, e.g. ``'a':'f'`` (note that contrary
-  to usual python slices, **both** the start and the stop are included!).
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.loc`` will raise a ``KeyError`` when the items are not found.

-See more at :ref:`Selection by Label <indexing.label>`
-
-
-Data and other attributes inherited from pandas.core.generic.NDFrame:
-
is_copy = None
- -
-Methods inherited from pandas.core.base.PandasObject:
-
__dir__(self)
Provide method name lookup and completion
-Only provide 'public' methods
- -
__sizeof__(self)
Generates the total memory usage for a object that returns
-either a value or Series of values
- -
-Methods inherited from pandas.core.base.StringMixin:
-
__bytes__(self)
Return a string representation for a particular object.

-Invoked by bytes(obj) in py3 only.
-Yields a bytestring in both py2/py3.
- -
__repr__(self)
Return a string representation for a particular object.

-Yields Bytestring in Py2, Unicode String in py3.
- -
__str__(self)
Return a string representation for a particular Object

-Invoked by str(df) in both py2/py3.
-Yields Bytestring in Py2, Unicode String in py3.
- -

- - - - - - - -
 
-class System(MySeries)
   One-dimensional ndarray with axis labels (including time series).

-Labels need not be unique but must be a hashable type. The object
-supports both integer- and label-based indexing and provides a host of
-methods for performing operations involving the index. Statistical
-methods from ndarray have been overridden to automatically exclude
-missing data (currently represented as NaN).

-Operations between Series (+, -, /, *, **) align values based on their
-associated index values-- they need not be the same length. The result
-index will be the sorted union of the two indexes.

-Parameters
-----------
-data : array-like, dict, or scalar value
-    Contains data stored in Series
-index : array-like or Index (1d)
-    Values must be hashable and have the same length as `data`.
-    Non-unique index values are allowed. Will default to
-    RangeIndex(len(data)) if not provided. If both a dict and index
-    sequence are used, the index will override the keys found in the
-    dict.
-dtype : numpy.dtype or None
-    If None, dtype will be inferred
-copy : boolean, default False
-    Copy input data
 
 
Method resolution order:
-
System
-
MySeries
-
pandas.core.series.Series
-
pandas.core.base.IndexOpsMixin
-
pandas.core.strings.StringAccessorMixin
-
pandas.core.generic.NDFrame
-
pandas.core.base.PandasObject
-
pandas.core.base.StringMixin
-
pandas.core.base.SelectionMixin
-
builtins.object
-
-
-Methods defined here:
-
__init__(self, *args, **kwargs)
Initialize the series.

-If there are no positional arguments, use kwargs.

-If there is one positional argument, copy it.

-More than one positional argument is an error.
- -
-Data descriptors defined here:
-
T
-
Intercept the Series accessor object so we can use `T`
-as a row label and access it using dot notation.

-https://pandas.pydata.org/pandas-docs/stable/generated/
-pandas.Series.T.html#pandas.Series.T
-
-
dt
-
Intercept the Series accessor object so we can use `dt`
-as a row label and access it using dot notation.

-https://pandas.pydata.org/pandas-docs/stable/generated/
-pandas.Series.dt.html
-
-
-Methods inherited from MySeries:
-
set(self, **kwargs)
Uses keyword arguments to update the Series in place.

-Example: series.update(a=1, b=2)
- -
-Methods inherited from pandas.core.series.Series:
-
__add__ = wrapper(left, right, name='__add__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f2f0>)
- -
__and__ = wrapper(self, other)
- -
__array__(self, result=None)
the array interface, return my values
- -
__array_prepare__(self, result, context=None)
Gets called prior to a ufunc
- -
__array_wrap__(self, result, context=None)
Gets called after a ufunc
- -
__div__ = wrapper(left, right, name='__truediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f9d8>)
- -
__divmod__ = wrapper(left, right, name='__divmod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca343bf8>)
- -
__eq__ = wrapper(self, other, axis=None)
- -
__float__ = wrapper(self)
- -
__floordiv__ = wrapper(left, right, name='__floordiv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fb70>)
- -
__ge__ = wrapper(self, other, axis=None)
- -
__getitem__(self, key)
- -
__gt__ = wrapper(self, other, axis=None)
- -
__iadd__ = f(self, other)
- -
__imul__ = f(self, other)
- -
__int__ = wrapper(self)
- -
__ipow__ = f(self, other)
- -
__isub__ = f(self, other)
- -
__iter__(self)
provide iteration over the values of the Series
-box values if necessary
- -
__itruediv__ = f(self, other)
- -
__le__ = wrapper(self, other, axis=None)
- -
__len__(self)
return the length of the Series
- -
__long__ = wrapper(self)
- -
__lt__ = wrapper(self, other, axis=None)
- -
__mod__ = wrapper(left, right, name='__mod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fd08>)
- -
__mul__ = wrapper(left, right, name='__mul__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f840>)
- -
__ne__ = wrapper(self, other, axis=None)
- -
__or__ = wrapper(self, other)
- -
__pow__ = wrapper(left, right, name='__pow__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fea0>)
- -
__radd__ = wrapper(left, right, name='__radd__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f510>)
- -
__rand__ = wrapper(self, other)
- -
__rdiv__ = wrapper(left, right, name='__rtruediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342510>)
- -
__rfloordiv__ = wrapper(left, right, name='__rfloordiv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342730>)
- -
__rmod__ = wrapper(left, right, name='__rmod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342b70>)
- -
__rmul__ = wrapper(left, right, name='__rmul__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca3420d0>)
- -
__ror__ = wrapper(self, other)
- -
__rpow__ = wrapper(left, right, name='__rpow__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342950>)
- -
__rsub__ = wrapper(left, right, name='__rsub__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca3422f0>)
- -
__rtruediv__ = wrapper(left, right, name='__rtruediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342510>)
- -
__rxor__ = wrapper(self, other)
- -
__setitem__(self, key, value)
- -
__sub__ = wrapper(left, right, name='__sub__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f6a8>)
- -
__truediv__ = wrapper(left, right, name='__truediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f9d8>)
- -
__unicode__(self)
Return a string representation for a particular DataFrame

-Invoked by unicode(df) in py2 only. Yields a Unicode String in both
-py2/py3.
- -
__xor__ = wrapper(self, other)
- -
add(self, other, level=None, fill_value=None, axis=0)
Addition of series and other, element-wise (binary operator `add`).

-Equivalent to ``series + other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.radd
- -
agg = aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a Series or when passed to Series.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : Series

-Examples
---------

->>> s = Series(np.random.randn(10))

->>> s.agg('min')
--1.3018049988556679

->>> s.agg(['min', 'max'])
-min   -1.301805
-max    1.127688
-dtype: float64

-See also
---------
-pandas.Series.apply
-pandas.Series.transform
- -
aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a Series or when passed to Series.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : Series

-Examples
---------

->>> s = Series(np.random.randn(10))

->>> s.agg('min')
--1.3018049988556679

->>> s.agg(['min', 'max'])
-min   -1.301805
-max    1.127688
-dtype: float64

-See also
---------
-pandas.Series.apply
-pandas.Series.transform
- -
align(self, other, join='outer', axis=None, level=None, copy=True, fill_value=None, method=None, limit=None, fill_axis=0, broadcast_axis=None)
Align two object on their axes with the
-specified join method for each axis Index

-Parameters
-----------
-other : DataFrame or Series
-join : {'outer', 'inner', 'left', 'right'}, default 'outer'
-axis : allowed axis of the other object, default None
-    Align on index (0), columns (1), or both (None)
-level : int or level name, default None
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-copy : boolean, default True
-    Always returns new objects. If copy=False and no reindexing is
-    required then original objects are returned.
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-method : str, default None
-limit : int, default None
-fill_axis : {0, 'index'}, default 0
-    Filling axis, method and limit
-broadcast_axis : {0, 'index'}, default None
-    Broadcast values along this axis, if aligning two objects of
-    different dimensions

-    .. versionadded:: 0.17.0

-Returns
--------
-(left, right) : (Series, type of other)
-    Aligned objects
- -
all(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether all elements are True over requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-all : scalar or Series (if level specified)
- -
any(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether any element is True over requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-any : scalar or Series (if level specified)
- -
append(self, to_append, ignore_index=False, verify_integrity=False)
Concatenate two or more Series.

-Parameters
-----------
-to_append : Series or list/tuple of Series
-ignore_index : boolean, default False
-    If True, do not use the index labels.

-    .. versionadded: 0.19.0

-verify_integrity : boolean, default False
-    If True, raise Exception on creating index with duplicates

-Returns
--------
-appended : Series

-Examples
---------
->>> s1 = pd.Series([1, 2, 3])
->>> s2 = pd.Series([4, 5, 6])
->>> s3 = pd.Series([4, 5, 6], index=[3,4,5])
->>> s1.append(s2)
-0    1
-1    2
-2    3
-0    4
-1    5
-2    6
-dtype: int64

->>> s1.append(s3)
-0    1
-1    2
-2    3
-3    4
-4    5
-5    6
-dtype: int64

-With `ignore_index` set to True:

->>> s1.append(s2, ignore_index=True)
-0    1
-1    2
-2    3
-3    4
-4    5
-5    6
-dtype: int64

-With `verify_integrity` set to True:

->>> s1.append(s2, verify_integrity=True)
-Traceback (most recent call last):
-...
-ValueError: Indexes have overlapping values: [0, 1, 2]
- -
apply(self, func, convert_dtype=True, args=(), **kwds)
Invoke function on values of Series. Can be ufunc (a NumPy function
-that applies to the entire Series) or a Python function that only works
-on single values

-Parameters
-----------
-func : function
-convert_dtype : boolean, default True
-    Try to find better dtype for elementwise function results. If
-    False, leave as dtype=object
-args : tuple
-    Positional arguments to pass to function in addition to the value
-Additional keyword arguments will be passed as keywords to the function

-Returns
--------
-y : Series or DataFrame if func returns a Series

-See also
---------
-Series.map: For element-wise operations
-Series.agg: only perform aggregating type operations
-Series.transform: only perform transformating type operations

-Examples
---------

-Create a series with typical summer temperatures for each city.

->>> import pandas as pd
->>> import numpy as np
->>> series = pd.Series([20, 21, 12], index=['London',
-... 'New York','Helsinki'])
->>> series
-London      20
-New York    21
-Helsinki    12
-dtype: int64

-Square the values by defining a function and passing it as an
-argument to ``apply()``.

->>> def square(x):
-...     return x**2
->>> series.apply(square)
-London      400
-New York    441
-Helsinki    144
-dtype: int64

-Square the values by passing an anonymous function as an
-argument to ``apply()``.

->>> series.apply(lambda x: x**2)
-London      400
-New York    441
-Helsinki    144
-dtype: int64

-Define a custom function that needs additional positional
-arguments and pass these additional arguments using the
-``args`` keyword.

->>> def subtract_custom_value(x, custom_value):
-...     return x-custom_value

->>> series.apply(subtract_custom_value, args=(5,))
-London      15
-New York    16
-Helsinki     7
-dtype: int64

-Define a custom function that takes keyword arguments
-and pass these arguments to ``apply``.

->>> def add_custom_values(x, **kwargs):
-...     for month in kwargs:
-...         x+=kwargs[month]
-...         return x

->>> series.apply(add_custom_values, june=30, july=20, august=25)
-London      95
-New York    96
-Helsinki    87
-dtype: int64

-Use a function from the Numpy library.

->>> series.apply(np.log)
-London      2.995732
-New York    3.044522
-Helsinki    2.484907
-dtype: float64
- -
argmax = idxmax(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of maximum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmax : Index of maximum of values

-Notes
------
-This method is the Series version of ``ndarray.argmax``.

-See Also
---------
-DataFrame.idxmax
-numpy.ndarray.argmax
- -
argmin = idxmin(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of minimum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmin : Index of minimum of values

-Notes
------
-This method is the Series version of ``ndarray.argmin``.

-See Also
---------
-DataFrame.idxmin
-numpy.ndarray.argmin
- -
argsort(self, axis=0, kind='quicksort', order=None)
Overrides ndarray.argsort. Argsorts the value, omitting NA/null values,
-and places the result in the same locations as the non-NA values

-Parameters
-----------
-axis : int (can only be zero)
-kind : {'mergesort', 'quicksort', 'heapsort'}, default 'quicksort'
-    Choice of sorting algorithm. See np.sort for more
-    information. 'mergesort' is the only stable algorithm
-order : ignored

-Returns
--------
-argsorted : Series, with -1 indicated where nan values are present

-See also
---------
-numpy.ndarray.argsort
- -
autocorr(self, lag=1)
Lag-N autocorrelation

-Parameters
-----------
-lag : int, default 1
-    Number of lags to apply before performing autocorrelation.

-Returns
--------
-autocorr : float
- -
between(self, left, right, inclusive=True)
Return boolean Series equivalent to left <= series <= right. NA values
-will be treated as False

-Parameters
-----------
-left : scalar
-    Left boundary
-right : scalar
-    Right boundary

-Returns
--------
-is_between : Series
- -
combine(self, other, func, fill_value=nan)
Perform elementwise binary operation on two Series using given function
-with optional fill value when an index is missing from one Series or
-the other

-Parameters
-----------
-other : Series or scalar value
-func : function
-fill_value : scalar value

-Returns
--------
-result : Series
- -
combine_first(self, other)
Combine Series values, choosing the calling Series's values
-first. Result index will be the union of the two indexes

-Parameters
-----------
-other : Series

-Returns
--------
-y : Series
- -
compound(self, axis=None, skipna=None, level=None)
Return the compound percentage of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-compounded : scalar or Series (if level specified)
- -
compress(self, condition, *args, **kwargs)
Return selected slices of an array along given axis as a Series

-See also
---------
-numpy.ndarray.compress
- -
corr(self, other, method='pearson', min_periods=None)
Compute correlation with `other` Series, excluding missing values

-Parameters
-----------
-other : Series
-method : {'pearson', 'kendall', 'spearman'}
-    * pearson : standard correlation coefficient
-    * kendall : Kendall Tau correlation coefficient
-    * spearman : Spearman rank correlation
-min_periods : int, optional
-    Minimum number of observations needed to have a valid result


-Returns
--------
-correlation : float
- -
count(self, level=None)
Return number of non-NA/null observations in the Series

-Parameters
-----------
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a smaller Series

-Returns
--------
-nobs : int or Series (if level specified)
- -
cov(self, other, min_periods=None)
Compute covariance with Series, excluding missing values

-Parameters
-----------
-other : Series
-min_periods : int, optional
-    Minimum number of observations needed to have a valid result

-Returns
--------
-covariance : float

-Normalized by N-1 (unbiased estimator).
- -
cummax(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative max over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummax : scalar



-See also
---------
-pandas.core.window.Expanding.max : Similar functionality
-    but ignores ``NaN`` values.
- -
cummin(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative minimum over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummin : scalar



-See also
---------
-pandas.core.window.Expanding.min : Similar functionality
-    but ignores ``NaN`` values.
- -
cumprod(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative product over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumprod : scalar



-See also
---------
-pandas.core.window.Expanding.prod : Similar functionality
-    but ignores ``NaN`` values.
- -
cumsum(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative sum over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumsum : scalar



-See also
---------
-pandas.core.window.Expanding.sum : Similar functionality
-    but ignores ``NaN`` values.
- -
diff(self, periods=1)
1st discrete difference of object

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming difference

-Returns
--------
-diffed : Series
- -
div = truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
divide = truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
dot(self, other)
Matrix multiplication with DataFrame or inner-product with Series
-objects

-Parameters
-----------
-other : Series or DataFrame

-Returns
--------
-dot_product : scalar or Series
- -
drop_duplicates(self, keep='first', inplace=False)
Return Series with duplicate values removed

-Parameters
-----------

-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Drop duplicates except for the first occurrence.
-    - ``last`` : Drop duplicates except for the last occurrence.
-    - False : Drop all duplicates.
-inplace : boolean, default False
-If True, performs operation inplace and returns None.

-Returns
--------
-deduplicated : Series
- -
dropna(self, axis=0, inplace=False, **kwargs)
Return Series without null values

-Returns
--------
-valid : Series
-inplace : boolean, default False
-    Do operation in place.
- -
duplicated(self, keep='first')
Return boolean Series denoting duplicate values

-Parameters
-----------
-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Mark duplicates as ``True`` except for the first
-      occurrence.
-    - ``last`` : Mark duplicates as ``True`` except for the last
-      occurrence.
-    - False : Mark all duplicates as ``True``.

-Returns
--------
-duplicated : Series
- -
eq(self, other, level=None, fill_value=None, axis=0)
Equal to of series and other, element-wise (binary operator `eq`).

-Equivalent to ``series == other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
ewm(self, com=None, span=None, halflife=None, alpha=None, min_periods=0, freq=None, adjust=True, ignore_na=False, axis=0)
Provides exponential weighted functions

-.. versionadded:: 0.18.0

-Parameters
-----------
-com : float, optional
-    Specify decay in terms of center of mass,
-    :math:`\alpha = 1 / (1 + com),\text{ for } com \geq 0`
-span : float, optional
-    Specify decay in terms of span,
-    :math:`\alpha = 2 / (span + 1),\text{ for } span \geq 1`
-halflife : float, optional
-    Specify decay in terms of half-life,
-    :math:`\alpha = 1 - exp(log(0.5) / halflife),\text{ for } halflife > 0`
-alpha : float, optional
-    Specify smoothing factor :math:`\alpha` directly,
-    :math:`0 < \alpha \leq 1`

-    .. versionadded:: 0.18.0

-min_periods : int, default 0
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : None or string alias / date offset object, default=None (DEPRECATED)
-    Frequency to conform to before computing statistic
-adjust : boolean, default True
-    Divide by decaying adjustment factor in beginning periods to account
-    for imbalance in relative weightings (viewing EWMA as a moving average)
-ignore_na : boolean, default False
-    Ignore missing values when calculating weights;
-    specify True to reproduce pre-0.15.0 behavior

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.ewm(com=0.5).mean()
-          B
-0  0.000000
-1  0.750000
-2  1.615385
-3  1.615385
-4  3.670213

-Notes
------
-Exactly one of center of mass, span, half-life, and alpha must be provided.
-Allowed values and relationship between the parameters are specified in the
-parameter descriptions above; see the link at the end of this section for
-a detailed explanation.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-When adjust is True (default), weighted averages are calculated using
-weights (1-alpha)**(n-1), (1-alpha)**(n-2), ..., 1-alpha, 1.

-When adjust is False, weighted averages are calculated recursively as:
-   weighted_average[0] = arg[0];
-   weighted_average[i] = (1-alpha)*weighted_average[i-1] + alpha*arg[i].

-When ignore_na is False (default), weights are based on absolute positions.
-For example, the weights of x and y used in calculating the final weighted
-average of [x, None, y] are (1-alpha)**2 and 1 (if adjust is True), and
-(1-alpha)**2 and alpha (if adjust is False).

-When ignore_na is True (reproducing pre-0.15.0 behavior), weights are based
-on relative positions. For example, the weights of x and y used in
-calculating the final weighted average of [x, None, y] are 1-alpha and 1
-(if adjust is True), and 1-alpha and alpha (if adjust is False).

-More details can be found at
-http://pandas.pydata.org/pandas-docs/stable/computation.html#exponentially-weighted-windows
- -
expanding(self, min_periods=1, freq=None, center=False, axis=0)
Provides expanding transformations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-axis : int or string, default 0

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.expanding(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  3.0
-4  7.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).
- -
fillna(self, value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)
Fill NA/NaN values using the specified method

-Parameters
-----------
-value : scalar, dict, Series, or DataFrame
-    Value to use to fill holes (e.g. 0), alternately a
-    dict/Series/DataFrame of values specifying which value to use for
-    each index (for a Series) or column (for a DataFrame). (values not
-    in the dict/Series/DataFrame will not be filled). This value cannot
-    be a list.
-method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
-    Method to use for filling holes in reindexed Series
-    pad / ffill: propagate last valid observation forward to next valid
-    backfill / bfill: use NEXT valid observation to fill gap
-axis : {0, 'index'}
-inplace : boolean, default False
-    If True, fill in place. Note: this will modify any
-    other views on this object, (e.g. a no-copy slice for a column in a
-    DataFrame).
-limit : int, default None
-    If method is specified, this is the maximum number of consecutive
-    NaN values to forward/backward fill. In other words, if there is
-    a gap with more than this number of consecutive NaNs, it will only
-    be partially filled. If method is not specified, this is the
-    maximum number of entries along the entire axis where NaNs will be
-    filled. Must be greater than 0 if not None.
-downcast : dict, default is None
-    a dict of item->dtype of what to downcast if possible,
-    or the string 'infer' which will try to downcast to an appropriate
-    equal type (e.g. float64 to int64 if possible)

-See Also
---------
-reindex, asfreq

-Returns
--------
-filled : Series
- -
first_valid_index(self)
Return label for first non-NA/null value
- -
floordiv(self, other, level=None, fill_value=None, axis=0)
Integer division of series and other, element-wise (binary operator `floordiv`).

-Equivalent to ``series // other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rfloordiv
- -
ge(self, other, level=None, fill_value=None, axis=0)
Greater than or equal to of series and other, element-wise (binary operator `ge`).

-Equivalent to ``series >= other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
get_value(self, label, takeable=False)
Quickly retrieve single value at passed index label

-Parameters
-----------
-index : label
-takeable : interpret the index as indexers, default False

-Returns
--------
-value : scalar value
- -
get_values(self)
same as values (but handles sparseness conversions); is a view
- -
gt(self, other, level=None, fill_value=None, axis=0)
Greater than of series and other, element-wise (binary operator `gt`).

-Equivalent to ``series > other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
hist = hist_series(self, by=None, ax=None, grid=True, xlabelsize=None, xrot=None, ylabelsize=None, yrot=None, figsize=None, bins=10, **kwds)
Draw histogram of the input series using matplotlib

-Parameters
-----------
-by : object, optional
-    If passed, then used to form histograms for separate groups
-ax : matplotlib axis object
-    If not passed, uses gca()
-grid : boolean, default True
-    Whether to show axis grid lines
-xlabelsize : int, default None
-    If specified changes the x-axis label size
-xrot : float, default None
-    rotation of x axis labels
-ylabelsize : int, default None
-    If specified changes the y-axis label size
-yrot : float, default None
-    rotation of y axis labels
-figsize : tuple, default None
-    figure size in inches by default
-bins: integer, default 10
-    Number of histogram bins to be used
-kwds : keywords
-    To be passed to the actual plotting function

-Notes
------
-See matplotlib documentation online for more on this
- -
idxmax(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of maximum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmax : Index of maximum of values

-Notes
------
-This method is the Series version of ``ndarray.argmax``.

-See Also
---------
-DataFrame.idxmax
-numpy.ndarray.argmax
- -
idxmin(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of minimum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmin : Index of minimum of values

-Notes
------
-This method is the Series version of ``ndarray.argmin``.

-See Also
---------
-DataFrame.idxmin
-numpy.ndarray.argmin
- -
isin(self, values)
Return a boolean :class:`~pandas.Series` showing whether each element
-in the :class:`~pandas.Series` is exactly contained in the passed
-sequence of ``values``.

-Parameters
-----------
-values : set or list-like
-    The sequence of values to test. Passing in a single string will
-    raise a ``TypeError``. Instead, turn a single string into a
-    ``list`` of one element.

-    .. versionadded:: 0.18.1

-    Support for values as a set

-Returns
--------
-isin : Series (bool dtype)

-Raises
-------
-TypeError
-  * If ``values`` is a string

-See Also
---------
-pandas.DataFrame.isin

-Examples
---------

->>> s = pd.Series(list('abc'))
->>> s.isin(['a', 'c', 'e'])
-0     True
-1    False
-2     True
-dtype: bool

-Passing a single string as ``s.isin('a')`` will raise an error. Use
-a list of one element instead:

->>> s.isin(['a'])
-0     True
-1    False
-2    False
-dtype: bool
- -
items = iteritems(self)
Lazily iterate over (index, value) tuples
- -
iteritems(self)
Lazily iterate over (index, value) tuples
- -
keys(self)
Alias for index
- -
kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : scalar or Series (if level specified)
- -
kurtosis = kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : scalar or Series (if level specified)
- -
last_valid_index(self)
Return label for last non-NA/null value
- -
le(self, other, level=None, fill_value=None, axis=0)
Less than or equal to of series and other, element-wise (binary operator `le`).

-Equivalent to ``series <= other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
lt(self, other, level=None, fill_value=None, axis=0)
Less than of series and other, element-wise (binary operator `lt`).

-Equivalent to ``series < other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
mad(self, axis=None, skipna=None, level=None)
Return the mean absolute deviation of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mad : scalar or Series (if level specified)
- -
map(self, arg, na_action=None)
Map values of Series using input correspondence (which can be
-a dict, Series, or function)

-Parameters
-----------
-arg : function, dict, or Series
-na_action : {None, 'ignore'}
-    If 'ignore', propagate NA values, without passing them to the
-    mapping function

-Returns
--------
-y : Series
-    same index as caller

-Examples
---------

-Map inputs to outputs (both of type `Series`)

->>> x = pd.Series([1,2,3], index=['one', 'two', 'three'])
->>> x
-one      1
-two      2
-three    3
-dtype: int64

->>> y = pd.Series(['foo', 'bar', 'baz'], index=[1,2,3])
->>> y
-1    foo
-2    bar
-3    baz

->>> x.map(y)
-one   foo
-two   bar
-three baz

-If `arg` is a dictionary, return a new Series with values converted
-according to the dictionary's mapping:

->>> z = {1: 'A', 2: 'B', 3: 'C'}

->>> x.map(z)
-one   A
-two   B
-three C

-Use na_action to control whether NA values are affected by the mapping
-function.

->>> s = pd.Series([1, 2, 3, np.nan])

->>> s2 = s.map('this is a string {}'.format, na_action=None)
-0    this is a string 1.0
-1    this is a string 2.0
-2    this is a string 3.0
-3    this is a string nan
-dtype: object

->>> s3 = s.map('this is a string {}'.format, na_action='ignore')
-0    this is a string 1.0
-1    this is a string 2.0
-2    this is a string 3.0
-3                     NaN
-dtype: object

-See Also
---------
-Series.apply: For applying more complex functions on a Series
-DataFrame.apply: Apply a function row-/column-wise
-DataFrame.applymap: Apply a function elementwise on a whole DataFrame

-Notes
------
-When `arg` is a dictionary, values in Series that are not in the
-dictionary (as keys) are converted to ``NaN``. However, if the
-dictionary is a ``dict`` subclass that defines ``__missing__`` (i.e.
-provides a method for default values), then this default is used
-rather than ``NaN``:

->>> from collections import Counter
->>> counter = Counter()
->>> counter['bar'] += 1
->>> y.map(counter)
-1    0
-2    1
-3    0
-dtype: int64
- -
max(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the maximum of the values in the object.
-            If you want the *index* of the maximum, use ``idxmax``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmax``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-max : scalar or Series (if level specified)
- -
mean(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the mean of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mean : scalar or Series (if level specified)
- -
median(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the median of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-median : scalar or Series (if level specified)
- -
memory_usage(self, index=True, deep=False)
Memory usage of the Series

-Parameters
-----------
-index : bool
-    Specifies whether to include memory usage of Series index
-deep : bool
-    Introspect the data deeply, interrogate
-    `object` dtypes for system-level memory consumption

-Returns
--------
-scalar bytes of memory consumed

-Notes
------
-Memory usage does not include memory consumed by elements that
-are not components of the array if deep=False

-See Also
---------
-numpy.ndarray.nbytes
- -
min(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the minimum of the values in the object.
-            If you want the *index* of the minimum, use ``idxmin``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmin``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-min : scalar or Series (if level specified)
- -
mod(self, other, level=None, fill_value=None, axis=0)
Modulo of series and other, element-wise (binary operator `mod`).

-Equivalent to ``series % other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmod
- -
mode(self)
Return the mode(s) of the dataset.

-Always returns Series even if only one value is returned.

-Returns
--------
-modes : Series (sorted)
- -
mul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `mul`).

-Equivalent to ``series * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmul
- -
multiply = mul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `mul`).

-Equivalent to ``series * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmul
- -
ne(self, other, level=None, fill_value=None, axis=0)
Not equal to of series and other, element-wise (binary operator `ne`).

-Equivalent to ``series != other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
nlargest(self, n=5, keep='first')
Return the largest `n` elements.

-Parameters
-----------
-n : int
-    Return this many descending sorted values
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-top_n : Series
-    The n largest values in the Series, in sorted order

-Notes
------
-Faster than ``.sort_values(ascending=False).head(n)`` for small `n`
-relative to the size of the ``Series`` object.

-See Also
---------
-Series.nsmallest

-Examples
---------
->>> import pandas as pd
->>> import numpy as np
->>> s = pd.Series(np.random.randn(10**6))
->>> s.nlargest(10)  # only sorts up to the N requested
-219921    4.644710
-82124     4.608745
-421689    4.564644
-425277    4.447014
-718691    4.414137
-43154     4.403520
-283187    4.313922
-595519    4.273635
-503969    4.250236
-121637    4.240952
-dtype: float64
- -
nonzero(self)
Return the indices of the elements that are non-zero

-This method is equivalent to calling `numpy.nonzero` on the
-series data. For compatability with NumPy, the return value is
-the same (a tuple with an array of indices for each dimension),
-but it will always be a one-item tuple because series only have
-one dimension.

-Examples
---------
->>> s = pd.Series([0, 3, 0, 4])
->>> s.nonzero()
-(array([1, 3]),)
->>> s.iloc[s.nonzero()[0]]
-1    3
-3    4
-dtype: int64

-See Also
---------
-numpy.nonzero
- -
nsmallest(self, n=5, keep='first')
Return the smallest `n` elements.

-Parameters
-----------
-n : int
-    Return this many ascending sorted values
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-bottom_n : Series
-    The n smallest values in the Series, in sorted order

-Notes
------
-Faster than ``.sort_values().head(n)`` for small `n` relative to
-the size of the ``Series`` object.

-See Also
---------
-Series.nlargest

-Examples
---------
->>> import pandas as pd
->>> import numpy as np
->>> s = pd.Series(np.random.randn(10**6))
->>> s.nsmallest(10)  # only sorts up to the N requested
-288532   -4.954580
-732345   -4.835960
-64803    -4.812550
-446457   -4.609998
-501225   -4.483945
-669476   -4.472935
-973615   -4.401699
-621279   -4.355126
-773916   -4.347355
-359919   -4.331927
-dtype: float64
- -
pow(self, other, level=None, fill_value=None, axis=0)
Exponential power of series and other, element-wise (binary operator `pow`).

-Equivalent to ``series ** other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rpow
- -
prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : scalar or Series (if level specified)
- -
product = prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : scalar or Series (if level specified)
- -
ptp(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Returns the difference between the maximum value and the
-            minimum value in the object. This is the equivalent of the
-            ``numpy.ndarray`` method ``ptp``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-ptp : scalar or Series (if level specified)
- -
put(self, *args, **kwargs)
Applies the `put` method to its `values` attribute
-if it has one.

-See also
---------
-numpy.ndarray.put
- -
quantile(self, q=0.5, interpolation='linear')
Return value at the given quantile, a la numpy.percentile.

-Parameters
-----------
-q : float or array-like, default 0.5 (50% quantile)
-    0 <= q <= 1, the quantile(s) to compute
-interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'}
-    .. versionadded:: 0.18.0

-    This optional parameter specifies the interpolation method to use,
-    when the desired quantile lies between two data points `i` and `j`:

-        * linear: `i + (j - i) * fraction`, where `fraction` is the
-          fractional part of the index surrounded by `i` and `j`.
-        * lower: `i`.
-        * higher: `j`.
-        * nearest: `i` or `j` whichever is nearest.
-        * midpoint: (`i` + `j`) / 2.

-Returns
--------
-quantile : float or Series
-    if ``q`` is an array, a Series will be returned where the
-    index is ``q`` and the values are the quantiles.

-Examples
---------
->>> s = Series([1, 2, 3, 4])
->>> s.quantile(.5)
-2.5
->>> s.quantile([.25, .5, .75])
-0.25    1.75
-0.50    2.50
-0.75    3.25
-dtype: float64
- -
radd(self, other, level=None, fill_value=None, axis=0)
Addition of series and other, element-wise (binary operator `radd`).

-Equivalent to ``other + series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.add
- -
ravel(self, order='C')
Return the flattened underlying data as an ndarray

-See also
---------
-numpy.ndarray.ravel
- -
rdiv = rtruediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.truediv
- -
reindex(self, index=None, **kwargs)
Conform Series to new index with optional filling logic, placing
-NA/NaN in locations having no value in the previous index. A new object
-is produced unless the new index is equivalent to the current one and
-copy=False

-Parameters
-----------
-index : array-like, optional (can be specified in order, or as
-    keywords)
-    New labels / index to conform to. Preferably an Index object to
-    avoid duplicating data
-method : {None, 'backfill'/'bfill', 'pad'/'ffill', 'nearest'}, optional
-    method to use for filling holes in reindexed DataFrame.
-    Please note: this is only  applicable to DataFrames/Series with a
-    monotonically increasing/decreasing index.

-    * default: don't fill gaps
-    * pad / ffill: propagate last valid observation forward to next
-      valid
-    * backfill / bfill: use next valid observation to fill gap
-    * nearest: use nearest valid observations to fill gap

-copy : boolean, default True
-    Return a new object, even if the passed indexes are the same
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-limit : int, default None
-    Maximum number of consecutive elements to forward or backward fill
-tolerance : optional
-    Maximum distance between original and new labels for inexact
-    matches. The values of the index at the matching locations most
-    satisfy the equation ``abs(index[indexer] - target) <= tolerance``.

-    .. versionadded:: 0.17.0

-Examples
---------

-Create a dataframe with some fictional data.

->>> index = ['Firefox', 'Chrome', 'Safari', 'IE10', 'Konqueror']
->>> df = pd.DataFrame({
-...      'http_status': [200,200,404,404,301],
-...      'response_time': [0.04, 0.02, 0.07, 0.08, 1.0]},
-...       index=index)
->>> df
-           http_status  response_time
-Firefox            200           0.04
-Chrome             200           0.02
-Safari             404           0.07
-IE10               404           0.08
-Konqueror          301           1.00

-Create a new index and reindex the dataframe. By default
-values in the new index that do not have corresponding
-records in the dataframe are assigned ``NaN``.

->>> new_index= ['Safari', 'Iceweasel', 'Comodo Dragon', 'IE10',
-...             'Chrome']
->>> df.reindex(new_index)
-               http_status  response_time
-Safari               404.0           0.07
-Iceweasel              NaN            NaN
-Comodo Dragon          NaN            NaN
-IE10                 404.0           0.08
-Chrome               200.0           0.02

-We can fill in the missing values by passing a value to
-the keyword ``fill_value``. Because the index is not monotonically
-increasing or decreasing, we cannot use arguments to the keyword
-``method`` to fill the ``NaN`` values.

->>> df.reindex(new_index, fill_value=0)
-               http_status  response_time
-Safari                 404           0.07
-Iceweasel                0           0.00
-Comodo Dragon            0           0.00
-IE10                   404           0.08
-Chrome                 200           0.02

->>> df.reindex(new_index, fill_value='missing')
-              http_status response_time
-Safari                404          0.07
-Iceweasel         missing       missing
-Comodo Dragon     missing       missing
-IE10                  404          0.08
-Chrome                200          0.02

-To further illustrate the filling functionality in
-``reindex``, we will create a dataframe with a
-monotonically increasing index (for example, a sequence
-of dates).

->>> date_index = pd.date_range('1/1/2010', periods=6, freq='D')
->>> df2 = pd.DataFrame({"prices": [100, 101, np.nan, 100, 89, 88]},
-...                    index=date_index)
->>> df2
-            prices
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88

-Suppose we decide to expand the dataframe to cover a wider
-date range.

->>> date_index2 = pd.date_range('12/29/2009', periods=10, freq='D')
->>> df2.reindex(date_index2)
-            prices
-2009-12-29     NaN
-2009-12-30     NaN
-2009-12-31     NaN
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-The index entries that did not have a value in the original data frame
-(for example, '2009-12-29') are by default filled with ``NaN``.
-If desired, we can fill in the missing values using one of several
-options.

-For example, to backpropagate the last valid value to fill the ``NaN``
-values, pass ``bfill`` as an argument to the ``method`` keyword.

->>> df2.reindex(date_index2, method='bfill')
-            prices
-2009-12-29     100
-2009-12-30     100
-2009-12-31     100
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-Please note that the ``NaN`` value present in the original dataframe
-(at index value 2010-01-03) will not be filled by any of the
-value propagation schemes. This is because filling while reindexing
-does not look at dataframe values, but only compares the original and
-desired indexes. If you do want to fill in the ``NaN`` values present
-in the original dataframe, use the ``fillna()`` method.

-Returns
--------
-reindexed : Series
- -
reindex_axis(self, labels, axis=0, **kwargs)
for compatibility with higher dims
- -
rename(self, index=None, **kwargs)
Alter axes input function or functions. Function / dict values must be
-unique (1-to-1). Labels not contained in a dict / Series will be left
-as-is. Extra labels listed don't throw an error. Alternatively, change
-``Series.name`` with a scalar value (Series only).

-Parameters
-----------
-index : scalar, list-like, dict-like or function, optional
-    Scalar or list-like will alter the ``Series.name`` attribute,
-    and raise on DataFrame or Panel.
-    dict-like or functions are transformations to apply to
-    that axis' values
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False
-    Whether to return a new Series. If True then value of copy is
-    ignored.
-level : int or level name, default None
-    In case of a MultiIndex, only rename labels in the specified
-    level.

-Returns
--------
-renamed : Series (new object)

-See Also
---------
-pandas.NDFrame.rename_axis

-Examples
---------
->>> s = pd.Series([1, 2, 3])
->>> s
-0    1
-1    2
-2    3
-dtype: int64
->>> s.rename("my_name") # scalar, changes Series.name
-0    1
-1    2
-2    3
-Name: my_name, dtype: int64
->>> s.rename(lambda x: x ** 2)  # function, changes labels
-0    1
-1    2
-4    3
-dtype: int64
->>> s.rename({1: 3, 2: 5})  # mapping, changes labels
-0    1
-3    2
-5    3
-dtype: int64
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename(2)
-Traceback (most recent call last):
-...
-TypeError: 'int' object is not callable
->>> df.rename(index=str, columns={"A": "a", "B": "c"})
-   a  c
-0  1  4
-1  2  5
-2  3  6
->>> df.rename(index=str, columns={"A": "a", "C": "c"})
-   a  B
-0  1  4
-1  2  5
-2  3  6
- -
reorder_levels(self, order)
Rearrange index levels using input order. May not drop or duplicate
-levels

-Parameters
-----------
-order : list of int representing new level order.
-       (reference level by number or key)
-axis : where to reorder levels

-Returns
--------
-type of caller (new object)
- -
repeat(self, repeats, *args, **kwargs)
Repeat elements of an Series. Refer to `numpy.ndarray.repeat`
-for more information about the `repeats` argument.

-See also
---------
-numpy.ndarray.repeat
- -
reset_index(self, level=None, drop=False, name=None, inplace=False)
Analogous to the :meth:`pandas.DataFrame.reset_index` function, see
-docstring there.

-Parameters
-----------
-level : int, str, tuple, or list, default None
-    Only remove the given levels from the index. Removes all levels by
-    default
-drop : boolean, default False
-    Do not try to insert index into dataframe columns
-name : object, default None
-    The name of the column corresponding to the Series values
-inplace : boolean, default False
-    Modify the Series in place (do not create a new object)

-Returns
-----------
-resetted : DataFrame, or Series if drop == True
- -
reshape(self, *args, **kwargs)
DEPRECATED: calling this method will raise an error in a
-future release. Please call ``.values.reshape(...)`` instead.

-return an ndarray with the values shape
-if the specified shape matches exactly the current shape, then
-return self (for compat)

-See also
---------
-numpy.ndarray.reshape
- -
rfloordiv(self, other, level=None, fill_value=None, axis=0)
Integer division of series and other, element-wise (binary operator `rfloordiv`).

-Equivalent to ``other // series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.floordiv
- -
rmod(self, other, level=None, fill_value=None, axis=0)
Modulo of series and other, element-wise (binary operator `rmod`).

-Equivalent to ``other % series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.mod
- -
rmul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `rmul`).

-Equivalent to ``other * series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.mul
- -
rolling(self, window, min_periods=None, freq=None, center=False, win_type=None, on=None, axis=0, closed=None)
Provides rolling window calculcations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-window : int, or offset
-    Size of the moving window. This is the number of observations used for
-    calculating the statistic. Each window will be a fixed size.

-    If its an offset then this will be the time period of each window. Each
-    window will be a variable sized based on the observations included in
-    the time-period. This is only valid for datetimelike indexes. This is
-    new in 0.19.0
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA). For a window that is specified by an offset,
-    this will default to 1.
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-win_type : string, default None
-    Provide a window type. See the notes below.
-on : string, optional
-    For a DataFrame, column on which to calculate
-    the rolling window, rather than the index
-closed : string, default None
-    Make the interval closed on the 'right', 'left', 'both' or
-    'neither' endpoints.
-    For offset-based windows, it defaults to 'right'.
-    For fixed windows, defaults to 'both'. Remaining cases not implemented
-    for fixed windows.

-    .. versionadded:: 0.20.0

-axis : int or string, default 0

-Returns
--------
-a Window or Rolling sub-classed for the particular operation

-Examples
---------

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]})
->>> df
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

-Rolling sum with a window length of 2, using the 'triang'
-window type.

->>> df.rolling(2, win_type='triang').sum()
-     B
-0  NaN
-1  1.0
-2  2.5
-3  NaN
-4  NaN

-Rolling sum with a window length of 2, min_periods defaults
-to the window length.

->>> df.rolling(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  NaN
-4  NaN

-Same as above, but explicity set the min_periods

->>> df.rolling(2, min_periods=1).sum()
-     B
-0  0.0
-1  1.0
-2  3.0
-3  2.0
-4  4.0

-A ragged (meaning not-a-regular frequency), time-indexed DataFrame

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]},
-....:                 index = [pd.Timestamp('20130101 09:00:00'),
-....:                          pd.Timestamp('20130101 09:00:02'),
-....:                          pd.Timestamp('20130101 09:00:03'),
-....:                          pd.Timestamp('20130101 09:00:05'),
-....:                          pd.Timestamp('20130101 09:00:06')])

->>> df
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  2.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0


-Contrasting to an integer rolling window, this will roll a variable
-length window corresponding to the time period.
-The default for min_periods is 1.

->>> df.rolling('2s').sum()
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  3.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-To learn more about the offsets & frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-The recognized win_types are:

-* ``boxcar``
-* ``triang``
-* ``blackman``
-* ``hamming``
-* ``bartlett``
-* ``parzen``
-* ``bohman``
-* ``blackmanharris``
-* ``nuttall``
-* ``barthann``
-* ``kaiser`` (needs beta)
-* ``gaussian`` (needs std)
-* ``general_gaussian`` (needs power, width)
-* ``slepian`` (needs width).
- -
round(self, decimals=0, *args, **kwargs)
Round each value in a Series to the given number of decimals.

-Parameters
-----------
-decimals : int
-    Number of decimal places to round to (default: 0).
-    If decimals is negative, it specifies the number of
-    positions to the left of the decimal point.

-Returns
--------
-Series object

-See Also
---------
-numpy.around
-DataFrame.round
- -
rpow(self, other, level=None, fill_value=None, axis=0)
Exponential power of series and other, element-wise (binary operator `rpow`).

-Equivalent to ``other ** series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.pow
- -
rsub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `rsub`).

-Equivalent to ``other - series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.sub
- -
rtruediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.truediv
- -
searchsorted(self, value, side='left', sorter=None)
Find indices where elements should be inserted to maintain order.

-Find the indices into a sorted Series `self` such that, if the
-corresponding elements in `value` were inserted before the indices,
-the order of `self` would be preserved.

-Parameters
-----------
-value : array_like
-    Values to insert into `self`.
-side : {'left', 'right'}, optional
-    If 'left', the index of the first suitable location found is given.
-    If 'right', return the last such index.  If there is no suitable
-    index, return either 0 or N (where N is the length of `self`).
-sorter : 1-D array_like, optional
-    Optional array of integer indices that sort `self` into ascending
-    order. They are typically the result of ``np.argsort``.

-Returns
--------
-indices : array of ints
-    Array of insertion points with the same shape as `value`.

-See Also
---------
-numpy.searchsorted

-Notes
------
-Binary search is used to find the required insertion points.

-Examples
---------

->>> x = pd.Series([1, 2, 3])
->>> x
-0    1
-1    2
-2    3
-dtype: int64

->>> x.searchsorted(4)
-array([3])

->>> x.searchsorted([0, 4])
-array([0, 3])

->>> x.searchsorted([1, 3], side='left')
-array([0, 2])

->>> x.searchsorted([1, 3], side='right')
-array([1, 3])

->>> x = pd.Categorical(['apple', 'bread', 'bread', 'cheese', 'milk' ])
-[apple, bread, bread, cheese, milk]
-Categories (4, object): [apple < bread < cheese < milk]

->>> x.searchsorted('bread')
-array([1])     # Note: an array, not a scalar

->>> x.searchsorted(['bread'])
-array([1])

->>> x.searchsorted(['bread', 'eggs'])
-array([1, 4])

->>> x.searchsorted(['bread', 'eggs'], side='right')
-array([3, 4])    # eggs before milk
- -
sem(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased standard error of the mean over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sem : scalar or Series (if level specified)
- -
set_value(self, label, value, takeable=False)
Quickly set single value at passed label. If label is not contained, a
-new object is created with the label placed at the end of the result
-index

-Parameters
-----------
-label : object
-    Partial indexing with MultiIndex not allowed
-value : object
-    Scalar value
-takeable : interpret the index as indexers, default False

-Returns
--------
-series : Series
-    If label is contained, will be reference to calling Series,
-    otherwise a new object
- -
shift(self, periods=1, freq=None, axis=0)
Shift index by desired number of periods with an optional time freq

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, optional
-    Increment to use from the tseries module or time rule (e.g. 'EOM').
-    See Notes.
-axis : {0, 'index'}

-Notes
------
-If freq is specified then the index values are shifted but the data
-is not realigned. That is, use freq if you would like to extend the
-index when shifting and preserve the original data.

-Returns
--------
-shifted : Series
- -
skew(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased skew over requested axis
-Normalized by N-1

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-skew : scalar or Series (if level specified)
- -
sort_index(self, axis=0, level=None, ascending=True, inplace=False, kind='quicksort', na_position='last', sort_remaining=True)
Sort object by labels (along an axis)

-Parameters
-----------
-axis : index to direct sorting
-level : int or level name or list of ints or list of level names
-    if not None, sort on values in specified index level(s)
-ascending : boolean, default True
-    Sort ascending vs. descending
-inplace : bool, default False
-    if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end.
-     Not implemented for MultiIndex.
-sort_remaining : bool, default True
-    if true and sorting by level and index is multilevel, sort by other
-    levels too (in order) after sorting by specified level

-Returns
--------
-sorted_obj : Series
- -
sort_values(self, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last')
Sort by the values along either axis

-.. versionadded:: 0.17.0

-Parameters
-----------
-axis : {0, 'index'}, default 0
-    Axis to direct sorting
-ascending : bool or list of bool, default True
-     Sort ascending vs. descending. Specify list for multiple sort
-     orders.  If this is a list of bools, must match the length of
-     the by.
-inplace : bool, default False
-     if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end

-Returns
--------
-sorted_obj : Series
- -
sortlevel(self, level=0, ascending=True, sort_remaining=True)
DEPRECATED: use :meth:`Series.sort_index`

-Sort Series with MultiIndex by chosen level. Data will be
-lexicographically sorted by the chosen level followed by the other
-levels (in order)

-Parameters
-----------
-level : int or level name, default None
-ascending : bool, default True

-Returns
--------
-sorted : Series

-See Also
---------
-Series.sort_index(level=...)
- -
std(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return sample standard deviation over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-std : scalar or Series (if level specified)
- -
sub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `sub`).

-Equivalent to ``series - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rsub
- -
subtract = sub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `sub`).

-Equivalent to ``series - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rsub
- -
sum(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the sum of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sum : scalar or Series (if level specified)
- -
swaplevel(self, i=-2, j=-1, copy=True)
Swap levels i and j in a MultiIndex

-Parameters
-----------
-i, j : int, string (can be mixed)
-    Level of index to be swapped. Can pass level name as string.

-Returns
--------
-swapped : Series

-.. versionchanged:: 0.18.1

-   The indexes ``i`` and ``j`` are now optional, and default to
-   the two innermost levels of the index.
- -
take(self, indices, axis=0, convert=True, is_copy=False, **kwargs)
return Series corresponding to requested indices

-Parameters
-----------
-indices : list / array of ints
-convert : translate negative to positive indices (default)

-Returns
--------
-taken : Series

-See also
---------
-numpy.ndarray.take
- -
to_csv(self, path=None, index=True, sep=',', na_rep='', float_format=None, header=False, index_label=None, mode='w', encoding=None, date_format=None, decimal='.')
Write Series to a comma-separated values (csv) file

-Parameters
-----------
-path : string or file handle, default None
-    File path or object, if None is provided the result is returned as
-    a string.
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-header : boolean, default False
-    Write out series name
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.
-mode : Python write mode, default 'w'
-sep : character, default ","
-    Field delimiter for the output file.
-encoding : string, optional
-    a string representing the encoding to use if the contents are
-    non-ascii, for python versions prior to 3
-date_format: string, default None
-    Format string for datetime objects.
-decimal: string, default '.'
-    Character recognized as decimal separator. E.g. use ',' for
-    European data
- -
to_dict(self)
Convert Series to {label -> value} dict

-Returns
--------
-value_dict : dict
- -
to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='', float_format=None, columns=None, header=True, index=True, index_label=None, startrow=0, startcol=0, engine=None, merge_cells=True, encoding=None, inf_rep='inf', verbose=True)
Write Series to an excel sheet

-.. versionadded:: 0.20.0


-Parameters
-----------
-excel_writer : string or ExcelWriter object
-    File path or existing ExcelWriter
-sheet_name : string, default 'Sheet1'
-    Name of sheet which will contain DataFrame
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-columns : sequence, optional
-    Columns to write
-header : boolean or list of string, default True
-    Write out column names. If a list of string is given it is
-    assumed to be aliases for the column names
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.
-startrow :
-    upper left cell row to dump data frame
-startcol :
-    upper left cell column to dump data frame
-engine : string, default None
-    write engine to use - you can also set this via the options
-    ``io.excel.xlsx.writer``, ``io.excel.xls.writer``, and
-    ``io.excel.xlsm.writer``.
-merge_cells : boolean, default True
-    Write MultiIndex and Hierarchical Rows as merged cells.
-encoding: string, default None
-    encoding of the resulting excel file. Only necessary for xlwt,
-    other writers support unicode natively.
-inf_rep : string, default 'inf'
-    Representation for infinity (there is no native representation for
-    infinity in Excel)
-freeze_panes : tuple of integer (length 2), default None
-    Specifies the one-based bottommost row and rightmost column that
-    is to be frozen

-    .. versionadded:: 0.20.0

-Notes
------
-If passing an existing ExcelWriter object, then the sheet will be added
-to the existing workbook.  This can be used to save different
-DataFrames to one workbook:

->>> writer = pd.ExcelWriter('output.xlsx')
->>> df1.to_excel(writer,'Sheet1')
->>> df2.to_excel(writer,'Sheet2')
->>> writer.save()

-For compatibility with to_csv, to_excel serializes lists and dicts to
-strings before writing.
- -
to_frame(self, name=None)
Convert Series to DataFrame

-Parameters
-----------
-name : object, default None
-    The passed name should substitute for the series name (if it has
-    one).

-Returns
--------
-data_frame : DataFrame
- -
to_period(self, freq=None, copy=True)
Convert Series from DatetimeIndex to PeriodIndex with desired
-frequency (inferred from index if not passed)

-Parameters
-----------
-freq : string, default

-Returns
--------
-ts : Series with PeriodIndex
- -
to_sparse(self, kind='block', fill_value=None)
Convert Series to SparseSeries

-Parameters
-----------
-kind : {'block', 'integer'}
-fill_value : float, defaults to NaN (missing)

-Returns
--------
-sp : SparseSeries
- -
to_string(self, buf=None, na_rep='NaN', float_format=None, header=True, index=True, length=False, dtype=False, name=False, max_rows=None)
Render a string representation of the Series

-Parameters
-----------
-buf : StringIO-like, optional
-    buffer to write to
-na_rep : string, optional
-    string representation of NAN to use, default 'NaN'
-float_format : one-parameter function, optional
-    formatter function to apply to columns' elements if they are floats
-    default None
-header: boolean, default True
-    Add the Series header (index name)
-index : bool, optional
-    Add index (row) labels, default True
-length : boolean, default False
-    Add the Series length
-dtype : boolean, default False
-    Add the Series dtype
-name : boolean, default False
-    Add the Series name if not None
-max_rows : int, optional
-    Maximum number of rows to show before truncating. If None, show
-    all.

-Returns
--------
-formatted : string (if not buffer passed)
- -
to_timestamp(self, freq=None, how='start', copy=True)
Cast to datetimeindex of timestamps, at *beginning* of period

-Parameters
-----------
-freq : string, default frequency of PeriodIndex
-    Desired frequency
-how : {'s', 'e', 'start', 'end'}
-    Convention for converting period to timestamp; start of period
-    vs. end

-Returns
--------
-ts : Series with DatetimeIndex
- -
tolist(self)
Convert Series to a nested list
- -
transform(self, func, *args, **kwargs)
Call function producing a like-indexed NDFrame
-and return a NDFrame with the transformed values`

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    To apply to column

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Returns
--------
-transformed : NDFrame

-Examples
---------
->>> df = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
-...                   index=pd.date_range('1/1/2000', periods=10))
-df.iloc[3:7] = np.nan

->>> df.transform(lambda x: (x - x.mean()) / x.std())
-                   A         B         C
-2000-01-01  0.579457  1.236184  0.123424
-2000-01-02  0.370357 -0.605875 -1.231325
-2000-01-03  1.455756 -0.277446  0.288967
-2000-01-04       NaN       NaN       NaN
-2000-01-05       NaN       NaN       NaN
-2000-01-06       NaN       NaN       NaN
-2000-01-07       NaN       NaN       NaN
-2000-01-08 -0.498658  1.274522  1.642524
-2000-01-09 -0.540524 -1.012676 -0.828968
-2000-01-10 -1.366388 -0.614710  0.005378

-See also
---------
-pandas.NDFrame.aggregate
-pandas.NDFrame.apply
- -
truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
unique(self)
Return unique values in the object. Uniques are returned in order
-of appearance, this does NOT sort. Hash table-based unique.

-Parameters
-----------
-values : 1d array-like

-Returns
--------
-unique values.
-  - If the input is an Index, the return is an Index
-  - If the input is a Categorical dtype, the return is a Categorical
-  - If the input is a Series/ndarray, the return will be an ndarray

-See Also
---------
-unique
-Index.unique
-Series.unique
- -
unstack(self, level=-1, fill_value=None)
Unstack, a.k.a. pivot, Series with MultiIndex to produce DataFrame.
-The level involved will automatically get sorted.

-Parameters
-----------
-level : int, string, or list of these, default last level
-    Level(s) to unstack, can pass level name
-fill_value : replace NaN with this value if the unstack produces
-    missing values

-    .. versionadded: 0.18.0

-Examples
---------
->>> s = pd.Series([1, 2, 3, 4],
-...     index=pd.MultiIndex.from_product([['one', 'two'], ['a', 'b']]))
->>> s
-one  a    1
-     b    2
-two  a    3
-     b    4
-dtype: int64

->>> s.unstack(level=-1)
-     a  b
-one  1  2
-two  3  4

->>> s.unstack(level=0)
-   one  two
-a    1    3
-b    2    4

-Returns
--------
-unstacked : DataFrame
- -
update(self, other)
Modify Series in place using non-NA values from passed
-Series. Aligns on index

-Parameters
-----------
-other : Series
- -
valid lambda self, inplace=False, **kwargs
- -
var(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased variance over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-var : scalar or Series (if level specified)
- -
view(self, dtype=None)
- -
-Class methods inherited from pandas.core.series.Series:
-
from_array(arr, index=None, name=None, dtype=None, copy=False, fastpath=False) from builtins.type
- -
from_csv(path, sep=',', parse_dates=True, header=None, index_col=0, encoding=None, infer_datetime_format=False) from builtins.type
Read CSV file (DISCOURAGED, please use :func:`pandas.read_csv`
-instead).

-It is preferable to use the more powerful :func:`pandas.read_csv`
-for most general purposes, but ``from_csv`` makes for an easy
-roundtrip to and from a file (the exact counterpart of
-``to_csv``), especially with a time Series.

-This method only differs from :func:`pandas.read_csv` in some defaults:

-- `index_col` is ``0`` instead of ``None`` (take first column as index
-  by default)
-- `header` is ``None`` instead of ``0`` (the first row is not used as
-  the column names)
-- `parse_dates` is ``True`` instead of ``False`` (try parsing the index
-  as datetime by default)

-With :func:`pandas.read_csv`, the option ``squeeze=True`` can be used
-to return a Series like ``from_csv``.

-Parameters
-----------
-path : string file path or file handle / StringIO
-sep : string, default ','
-    Field delimiter
-parse_dates : boolean, default True
-    Parse dates. Different default from read_table
-header : int, default None
-    Row to use as header (skip prior rows)
-index_col : int or sequence, default 0
-    Column to use for index. If a sequence is given, a MultiIndex
-    is used. Different default from read_table
-encoding : string, optional
-    a string representing the encoding to use if the contents are
-    non-ascii, for python versions prior to 3
-infer_datetime_format: boolean, default False
-    If True and `parse_dates` is True for a column, try to infer the
-    datetime format based on the first datetime string. If the format
-    can be inferred, there often will be a large parsing speed-up.

-See also
---------
-pandas.read_csv

-Returns
--------
-y : Series
- -
-Data descriptors inherited from pandas.core.series.Series:
-
asobject
-
return object Series which contains boxed values

-*this is an internal non-public method*
-
-
axes
-
Return a list of the row axis labels
-
-
dtype
-
return the dtype object of the underlying data
-
-
dtypes
-
return the dtype object of the underlying data
-
-
ftype
-
return if the data is sparse|dense
-
-
ftypes
-
return if the data is sparse|dense
-
-
imag
-
-
index
-
-
name
-
-
real
-
-
values
-
Return Series as ndarray or ndarray-like
-depending on the dtype

-Returns
--------
-arr : numpy.ndarray or ndarray-like

-Examples
---------
->>> pd.Series([1, 2, 3]).values
-array([1, 2, 3])

->>> pd.Series(list('aabc')).values
-array(['a', 'a', 'b', 'c'], dtype=object)

->>> pd.Series(list('aabc')).astype('category').values
-[a, a, b, c]
-Categories (3, object): [a, b, c]

-Timezone aware datetime data is converted to UTC:

->>> pd.Series(pd.date_range('20130101', periods=3,
-...                         tz='US/Eastern')).values
-array(['2013-01-01T05:00:00.000000000',
-       '2013-01-02T05:00:00.000000000',
-       '2013-01-03T05:00:00.000000000'], dtype='datetime64[ns]')
-
-
-Data and other attributes inherited from pandas.core.series.Series:
-
cat = <class 'pandas.core.categorical.CategoricalAccessor'>
Accessor object for categorical properties of the Series values.

-Be aware that assigning to `categories` is a inplace operation, while all
-methods return new categorical data per default (but can be called with
-`inplace=True`).

-Examples
---------
->>> s.cat.categories
->>> s.cat.categories = list('abc')
->>> s.cat.rename_categories(list('cab'))
->>> s.cat.reorder_categories(list('cab'))
->>> s.cat.add_categories(['d','e'])
->>> s.cat.remove_categories(['d'])
->>> s.cat.remove_unused_categories()
->>> s.cat.set_categories(list('abcde'))
->>> s.cat.as_ordered()
->>> s.cat.as_unordered()
- -
plot = <class 'pandas.plotting._core.SeriesPlotMethods'>
Series plotting accessor and method

-Examples
---------
->>> s.plot.line()
->>> s.plot.bar()
->>> s.plot.hist()

-Plotting methods can also be accessed by calling the accessor as a method
-with the ``kind`` argument:
-``s.plot(kind='line')`` is equivalent to ``s.plot.line()``
- -
-Methods inherited from pandas.core.base.IndexOpsMixin:
-
factorize(self, sort=False, na_sentinel=-1)
Encode the object as an enumerated type or categorical variable

-Parameters
-----------
-sort : boolean, default False
-    Sort by values
-na_sentinel: int, default -1
-    Value to mark "not found"

-Returns
--------
-labels : the indexer to the original array
-uniques : the unique Index
- -
item(self)
return the first element of the underlying data as a python
-scalar
- -
nunique(self, dropna=True)
Return number of unique elements in the object.

-Excludes NA values by default.

-Parameters
-----------
-dropna : boolean, default True
-    Don't include NaN in the count.

-Returns
--------
-nunique : int
- -
transpose(self, *args, **kwargs)
return the transpose, which is by definition self
- -
value_counts(self, normalize=False, sort=True, ascending=False, bins=None, dropna=True)
Returns object containing counts of unique values.

-The resulting object will be in descending order so that the
-first element is the most frequently-occurring element.
-Excludes NA values by default.

-Parameters
-----------
-normalize : boolean, default False
-    If True then the object returned will contain the relative
-    frequencies of the unique values.
-sort : boolean, default True
-    Sort by values
-ascending : boolean, default False
-    Sort in ascending order
-bins : integer, optional
-    Rather than count values, group them into half-open bins,
-    a convenience for pd.cut, only works with numeric data
-dropna : boolean, default True
-    Don't include counts of NaN.

-Returns
--------
-counts : Series
- -
-Data descriptors inherited from pandas.core.base.IndexOpsMixin:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-
base
-
return the base object if the memory of the underlying data is
-shared
-
-
data
-
return the data pointer of the underlying data
-
-
empty
-
-
flags
-
return the ndarray.flags for the underlying data
-
-
hasnans
-
-
is_monotonic
-
Return boolean if values in the object are
-monotonic_increasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic : boolean
-
-
is_monotonic_decreasing
-
Return boolean if values in the object are
-monotonic_decreasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic_decreasing : boolean
-
-
is_monotonic_increasing
-
Return boolean if values in the object are
-monotonic_increasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic : boolean
-
-
is_unique
-
Return boolean if values in the object are unique

-Returns
--------
-is_unique : boolean
-
-
itemsize
-
return the size of the dtype of the item of the underlying data
-
-
nbytes
-
return the number of bytes in the underlying data
-
-
ndim
-
return the number of dimensions of the underlying data,
-by definition 1
-
-
shape
-
return a tuple of the shape of the underlying data
-
-
size
-
return the number of elements in the underlying data
-
-
strides
-
return the strides of the underlying data
-
-
-Data and other attributes inherited from pandas.core.base.IndexOpsMixin:
-
__array_priority__ = 1000
- -
-Data and other attributes inherited from pandas.core.strings.StringAccessorMixin:
-
str = <class 'pandas.core.strings.StringMethods'>
Vectorized string functions for Series and Index. NAs stay NA unless
-handled otherwise by a particular method. Patterned after Python's string
-methods, with some inspiration from R's stringr package.

-Examples
---------
->>> s.str.split('_')
->>> s.str.replace('_', '')
- -
-Methods inherited from pandas.core.generic.NDFrame:
-
__abs__(self)
- -
__bool__ = __nonzero__(self)
- -
__contains__(self, key)
True if the key is in the info axis
- -
__copy__(self, deep=True)
- -
__deepcopy__(self, memo=None)
- -
__delitem__(self, key)
Delete item
- -
__finalize__(self, other, method=None, **kwargs)
Propagate metadata from other to self.

-Parameters
-----------
-other : the object from which to get the attributes that we are going
-    to propagate
-method : optional, a passed method name ; possibly to take different
-    types of propagation actions based on this
- -
__getattr__(self, name)
After regular attribute access, try looking up the name
-This allows simpler access to columns for interactive use.
- -
__getstate__(self)
- -
__hash__(self)
Return hash(self).
- -
__invert__(self)
- -
__neg__(self)
- -
__nonzero__(self)
- -
__round__(self, decimals=0)
- -
__setattr__(self, name, value)
After regular attribute access, try setting the name
-This allows simpler access to columns for interactive use.
- -
__setstate__(self, state)
- -
abs(self)
Return an object with absolute value taken--only applicable to objects
-that are all numeric.

-Returns
--------
-abs: type of caller
- -
add_prefix(self, prefix)
Concatenate prefix string with panel items names.

-Parameters
-----------
-prefix : string

-Returns
--------
-with_prefix : type of caller
- -
add_suffix(self, suffix)
Concatenate suffix string with panel items names.

-Parameters
-----------
-suffix : string

-Returns
--------
-with_suffix : type of caller
- -
as_blocks(self, copy=True)
Convert the frame to a dict of dtype -> Constructor Types that each has
-a homogeneous dtype.

-NOTE: the dtypes of the blocks WILL BE PRESERVED HERE (unlike in
-      as_matrix)

-Parameters
-----------
-copy : boolean, default True

-       .. versionadded: 0.16.1

-Returns
--------
-values : a dict of dtype -> Constructor Types
- -
as_matrix(self, columns=None)
Convert the frame to its Numpy-array representation.

-Parameters
-----------
-columns: list, optional, default:None
-    If None, return all columns, otherwise, returns specified columns.

-Returns
--------
-values : ndarray
-    If the caller is heterogeneous and contains booleans or objects,
-    the result will be of dtype=object. See Notes.


-Notes
------
-Return is NOT a Numpy-matrix, rather, a Numpy-array.

-The dtype will be a lower-common-denominator dtype (implicit
-upcasting); that is to say if the dtypes (even of numeric types)
-are mixed, the one that accommodates all will be chosen. Use this
-with care if you are not dealing with the blocks.

-e.g. If the dtypes are float16 and float32, dtype will be upcast to
-float32.  If dtypes are int32 and uint8, dtype will be upcase to
-int32. By numpy.find_common_type convention, mixing int64 and uint64
-will result in a flot64 dtype.

-This method is provided for backwards compatibility. Generally,
-it is recommended to use '.values'.

-See Also
---------
-pandas.DataFrame.values
- -
asfreq(self, freq, method=None, how=None, normalize=False, fill_value=None)
Convert TimeSeries to specified frequency.

-Optionally provide filling method to pad/backfill missing values.

-Returns the original data conformed to a new index with the specified
-frequency. ``resample`` is more appropriate if an operation, such as
-summarization, is necessary to represent the data at the new frequency.

-Parameters
-----------
-freq : DateOffset object, or string
-method : {'backfill'/'bfill', 'pad'/'ffill'}, default None
-    Method to use for filling holes in reindexed Series (note this
-    does not fill NaNs that already were present):

-    * 'pad' / 'ffill': propagate last valid observation forward to next
-      valid
-    * 'backfill' / 'bfill': use NEXT valid observation to fill
-how : {'start', 'end'}, default end
-    For PeriodIndex only, see PeriodIndex.asfreq
-normalize : bool, default False
-    Whether to reset output index to midnight
-fill_value: scalar, optional
-    Value to use for missing values, applied during upsampling (note
-    this does not fill NaNs that already were present).

-    .. versionadded:: 0.20.0

-Returns
--------
-converted : type of caller

-Examples
---------

-Start by creating a series with 4 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=4, freq='T')
->>> series = pd.Series([0.0, None, 2.0, 3.0], index=index)
->>> df = pd.DataFrame({'s':series})
->>> df
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:03:00    3.0

-Upsample the series into 30 second bins.

->>> df.asfreq(freq='30S')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    NaN
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``fill value``.

->>> df.asfreq(freq='30S', fill_value=9.0)
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    9.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    9.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    9.0
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``method``.

->>> df.asfreq(freq='30S', method='bfill')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    2.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    3.0
-2000-01-01 00:03:00    3.0

-See Also
---------
-reindex

-Notes
------
-To learn more about the frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
- -
asof(self, where, subset=None)
The last row without any NaN is taken (or the last row without
-NaN considering only the subset of columns in the case of a DataFrame)

-.. versionadded:: 0.19.0 For DataFrame

-If there is no good value, NaN is returned for a Series
-a Series of NaN values for a DataFrame

-Parameters
-----------
-where : date or array of dates
-subset : string or list of strings, default None
-   if not None use these columns for NaN propagation

-Notes
------
-Dates are assumed to be sorted
-Raises if this is not the case

-Returns
--------
-where is scalar

-  - value or NaN if input is Series
-  - Series if input is DataFrame

-where is Index: same shape object as input

-See Also
---------
-merge_asof
- -
astype(self, dtype, copy=True, errors='raise', **kwargs)
Cast object to input numpy.dtype
-Return a copy when copy = True (be really careful with this!)

-Parameters
-----------
-dtype : data type, or dict of column name -> data type
-    Use a numpy.dtype or Python type to cast entire pandas object to
-    the same type. Alternatively, use {col: dtype, ...}, where col is a
-    column label and dtype is a numpy.dtype or Python type to cast one
-    or more of the DataFrame's columns to column-specific types.
-errors : {'raise', 'ignore'}, default 'raise'.
-    Control raising of exceptions on invalid data for provided dtype.

-    - ``raise`` : allow exceptions to be raised
-    - ``ignore`` : suppress exceptions. On error return original object

-    .. versionadded:: 0.20.0

-raise_on_error : DEPRECATED use ``errors`` instead
-kwargs : keyword arguments to pass on to the constructor

-Returns
--------
-casted : type of caller
- -
at_time(self, time, asof=False)
Select values at particular time of day (e.g. 9:30AM).

-Parameters
-----------
-time : datetime.time or string

-Returns
--------
-values_at_time : type of caller
- -
between_time(self, start_time, end_time, include_start=True, include_end=True)
Select values between particular times of the day (e.g., 9:00-9:30 AM).

-Parameters
-----------
-start_time : datetime.time or string
-end_time : datetime.time or string
-include_start : boolean, default True
-include_end : boolean, default True

-Returns
--------
-values_between_time : type of caller
- -
bfill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='bfill') <DataFrame.fillna>`
- -
bool(self)
Return the bool of a single element PandasObject.

-This must be a boolean scalar value, either True or False.  Raise a
-ValueError if the PandasObject does not have exactly 1 element, or that
-element is not boolean
- -
clip(self, lower=None, upper=None, axis=None, *args, **kwargs)
Trim values at input threshold(s).

-Parameters
-----------
-lower : float or array_like, default None
-upper : float or array_like, default None
-axis : int or string axis name, optional
-    Align object with lower and upper along the given axis.

-Returns
--------
-clipped : Series

-Examples
---------
->>> df
-  0         1
-0  0.335232 -1.256177
-1 -1.367855  0.746646
-2  0.027753 -1.176076
-3  0.230930 -0.679613
-4  1.261967  0.570967
->>> df.clip(-1.0, 0.5)
-          0         1
-0  0.335232 -1.000000
-1 -1.000000  0.500000
-2  0.027753 -1.000000
-3  0.230930 -0.679613
-4  0.500000  0.500000
->>> t
-0   -0.3
-1   -0.2
-2   -0.1
-3    0.0
-4    0.1
-dtype: float64
->>> df.clip(t, t + 1, axis=0)
-          0         1
-0  0.335232 -0.300000
-1 -0.200000  0.746646
-2  0.027753 -0.100000
-3  0.230930  0.000000
-4  1.100000  0.570967
- -
clip_lower(self, threshold, axis=None)
Return copy of the input with values below given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
clip_upper(self, threshold, axis=None)
Return copy of input with values above given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
consolidate(self, inplace=False)
DEPRECATED: consolidate will be an internal implementation only.
- -
convert_objects(self, convert_dates=True, convert_numeric=False, convert_timedeltas=True, copy=True)
Deprecated.

-Attempt to infer better dtype for object columns

-Parameters
-----------
-convert_dates : boolean, default True
-    If True, convert to date where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-convert_numeric : boolean, default False
-    If True, attempt to coerce to numbers (including strings), with
-    unconvertible values becoming NaN.
-convert_timedeltas : boolean, default True
-    If True, convert to timedelta where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-copy : boolean, default True
-    If True, return a copy even if no copy is necessary (e.g. no
-    conversion was done). Note: This is meant for internal use, and
-    should not be confused with inplace.

-See Also
---------
-pandas.to_datetime : Convert argument to datetime.
-pandas.to_timedelta : Convert argument to timedelta.
-pandas.to_numeric : Return a fixed frequency timedelta index,
-    with day as the default.

-Returns
--------
-converted : same as input object
- -
copy(self, deep=True)
Make a copy of this objects data.

-Parameters
-----------
-deep : boolean or string, default True
-    Make a deep copy, including a copy of the data and the indices.
-    With ``deep=False`` neither the indices or the data are copied.

-    Note that when ``deep=True`` data is copied, actual python objects
-    will not be copied recursively, only the reference to the object.
-    This is in contrast to ``copy.deepcopy`` in the Standard Library,
-    which recursively copies object data.

-Returns
--------
-copy : type of caller
- -
describe(self, percentiles=None, include=None, exclude=None)
Generates descriptive statistics that summarize the central tendency,
-dispersion and shape of a dataset's distribution, excluding
-``NaN`` values.

-Analyzes both numeric and object series, as well
-as ``DataFrame`` column sets of mixed data types. The output
-will vary depending on what is provided. Refer to the notes
-below for more detail.

-Parameters
-----------
-percentiles : list-like of numbers, optional
-    The percentiles to include in the output. All should
-    fall between 0 and 1. The default is
-    ``[.25, .5, .75]``, which returns the 25th, 50th, and
-    75th percentiles.
-include : 'all', list-like of dtypes or None (default), optional
-    A white list of data types to include in the result. Ignored
-    for ``Series``. Here are the options:

-    - 'all' : All columns of the input will be included in the output.
-    - A list-like of dtypes : Limits the results to the
-      provided data types.
-      To limit the result to numeric types submit
-      ``numpy.number``. To limit it instead to categorical
-      objects submit the ``numpy.object`` data type. Strings
-      can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will include all numeric columns.
-exclude : list-like of dtypes or None (default), optional,
-    A black list of data types to omit from the result. Ignored
-    for ``Series``. Here are the options:

-    - A list-like of dtypes : Excludes the provided data types
-      from the result. To select numeric types submit
-      ``numpy.number``. To select categorical objects submit the data
-      type ``numpy.object``. Strings can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will exclude nothing.

-Returns
--------
-summary:  Series/DataFrame of summary statistics

-Notes
------
-For numeric data, the result's index will include ``count``,
-``mean``, ``std``, ``min``, ``max`` as well as lower, ``50`` and
-upper percentiles. By default the lower percentile is ``25`` and the
-upper percentile is ``75``. The ``50`` percentile is the
-same as the median.

-For object data (e.g. strings or timestamps), the result's index
-will include ``count``, ``unique``, ``top``, and ``freq``. The ``top``
-is the most common value. The ``freq`` is the most common value's
-frequency. Timestamps also include the ``first`` and ``last`` items.

-If multiple object values have the highest count, then the
-``count`` and ``top`` results will be arbitrarily chosen from
-among those with the highest count.

-For mixed data types provided via a ``DataFrame``, the default is to
-return only an analysis of numeric columns. If ``include='all'``
-is provided as an option, the result will include a union of
-attributes of each type.

-The `include` and `exclude` parameters can be used to limit
-which columns in a ``DataFrame`` are analyzed for the output.
-The parameters are ignored when analyzing a ``Series``.

-Examples
---------
-Describing a numeric ``Series``.

->>> s = pd.Series([1, 2, 3])
->>> s.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0

-Describing a categorical ``Series``.

->>> s = pd.Series(['a', 'a', 'b', 'c'])
->>> s.describe()
-count     4
-unique    3
-top       a
-freq      2
-dtype: object

-Describing a timestamp ``Series``.

->>> s = pd.Series([
-...   np.datetime64("2000-01-01"),
-...   np.datetime64("2010-01-01"),
-...   np.datetime64("2010-01-01")
-... ])
->>> s.describe()
-count                       3
-unique                      2
-top       2010-01-01 00:00:00
-freq                        2
-first     2000-01-01 00:00:00
-last      2010-01-01 00:00:00
-dtype: object

-Describing a ``DataFrame``. By default only numeric fields
-are returned.

->>> df = pd.DataFrame([[1, 'a'], [2, 'b'], [3, 'c']],
-...                   columns=['numeric', 'object'])
->>> df.describe()
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Describing all columns of a ``DataFrame`` regardless of data type.

->>> df.describe(include='all')
-        numeric object
-count       3.0      3
-unique      NaN      3
-top         NaN      b
-freq        NaN      1
-mean        2.0    NaN
-std         1.0    NaN
-min         1.0    NaN
-25%         1.5    NaN
-50%         2.0    NaN
-75%         2.5    NaN
-max         3.0    NaN

-Describing a column from a ``DataFrame`` by accessing it as
-an attribute.

->>> df.numeric.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0
-Name: numeric, dtype: float64

-Including only numeric columns in a ``DataFrame`` description.

->>> df.describe(include=[np.number])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Including only string columns in a ``DataFrame`` description.

->>> df.describe(include=[np.object])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding numeric columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.number])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding object columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.object])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-See Also
---------
-DataFrame.count
-DataFrame.max
-DataFrame.min
-DataFrame.mean
-DataFrame.std
-DataFrame.select_dtypes
- -
drop(self, labels, axis=0, level=None, inplace=False, errors='raise')
Return new object with labels in requested axis removed.

-Parameters
-----------
-labels : single label or list-like
-axis : int or axis name
-level : int or level name, default None
-    For MultiIndex
-inplace : bool, default False
-    If True, do operation inplace and return None.
-errors : {'ignore', 'raise'}, default 'raise'
-    If 'ignore', suppress error and existing labels are dropped.

-    .. versionadded:: 0.16.1

-Returns
--------
-dropped : type of caller
- -
equals(self, other)
Determines if two NDFrame objects contain the same elements. NaNs in
-the same location are considered equal.
- -
ffill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='ffill') <DataFrame.fillna>`
- -
filter(self, items=None, like=None, regex=None, axis=None)
Subset rows or columns of dataframe according to labels in
-the specified index.

-Note that this routine does not filter a dataframe on its
-contents. The filter is applied to the labels of the index.

-Parameters
-----------
-items : list-like
-    List of info axis to restrict to (must not all be present)
-like : string
-    Keep info axis where "arg in col == True"
-regex : string (regular expression)
-    Keep info axis with re.search(regex, col) == True
-axis : int or string axis name
-    The axis to filter on.  By default this is the info axis,
-    'index' for Series, 'columns' for DataFrame

-Returns
--------
-same type as input object

-Examples
---------
->>> df
-one  two  three
-mouse     1    2      3
-rabbit    4    5      6

->>> # select columns by name
->>> df.filter(items=['one', 'three'])
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select columns by regular expression
->>> df.filter(regex='e$', axis=1)
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select rows containing 'bbi'
->>> df.filter(like='bbi', axis=0)
-one  two  three
-rabbit    4    5      6

-See Also
---------
-pandas.DataFrame.select

-Notes
------
-The ``items``, ``like``, and ``regex`` parameters are
-enforced to be mutually exclusive.

-``axis`` defaults to the info axis that is used when indexing
-with ``[]``.
- -
first(self, offset)
Convenience method for subsetting initial periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.first('10D') -> First 10 days

-Returns
--------
-subset : type of caller
- -
get(self, key, default=None)
Get item from object for given key (DataFrame column, Panel slice,
-etc.). Returns default value if not found.

-Parameters
-----------
-key : object

-Returns
--------
-value : type of items contained in object
- -
get_dtype_counts(self)
Return the counts of dtypes in this object.
- -
get_ftype_counts(self)
Return the counts of ftypes in this object.
- -
groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, **kwargs)
Group series using mapper (dict or key function, apply given function
-to group, return result as series) or by a series of columns.

-Parameters
-----------
-by : mapping, function, str, or iterable
-    Used to determine the groups for the groupby.
-    If ``by`` is a function, it's called on each value of the object's
-    index. If a dict or Series is passed, the Series or dict VALUES
-    will be used to determine the groups (the Series' values are first
-    aligned; see ``.align()`` method). If an ndarray is passed, the
-    values are used as-is determine the groups. A str or list of strs
-    may be passed to group by the columns in ``self``
-axis : int, default 0
-level : int, level name, or sequence of such, default None
-    If the axis is a MultiIndex (hierarchical), group by a particular
-    level or levels
-as_index : boolean, default True
-    For aggregated output, return object with group labels as the
-    index. Only relevant for DataFrame input. as_index=False is
-    effectively "SQL-style" grouped output
-sort : boolean, default True
-    Sort group keys. Get better performance by turning this off.
-    Note this does not influence the order of observations within each
-    group.  groupby preserves the order of rows within each group.
-group_keys : boolean, default True
-    When calling apply, add group keys to index to identify pieces
-squeeze : boolean, default False
-    reduce the dimensionality of the return type if possible,
-    otherwise return a consistent type

-Examples
---------
-DataFrame results

->>> data.groupby(func, axis=0).mean()
->>> data.groupby(['col1', 'col2'])['col3'].mean()

-DataFrame with hierarchical index

->>> data.groupby(['col1', 'col2']).mean()

-Returns
--------
-GroupBy object
- -
head(self, n=5)
Returns first n rows
- -
interpolate(self, method='linear', axis=0, limit=None, inplace=False, limit_direction='forward', downcast=None, **kwargs)
Interpolate values according to different methods.

-Please note that only ``method='linear'`` is supported for
-DataFrames/Series with a MultiIndex.

-Parameters
-----------
-method : {'linear', 'time', 'index', 'values', 'nearest', 'zero',
-          'slinear', 'quadratic', 'cubic', 'barycentric', 'krogh',
-          'polynomial', 'spline', 'piecewise_polynomial',
-          'from_derivatives', 'pchip', 'akima'}

-    * 'linear': ignore the index and treat the values as equally
-      spaced. This is the only method supported on MultiIndexes.
-      default
-    * 'time': interpolation works on daily and higher resolution
-      data to interpolate given length of interval
-    * 'index', 'values': use the actual numerical values of the index
-    * 'nearest', 'zero', 'slinear', 'quadratic', 'cubic',
-      'barycentric', 'polynomial' is passed to
-      ``scipy.interpolate.interp1d``. Both 'polynomial' and 'spline'
-      require that you also specify an `order` (int),
-      e.g. df.interpolate(method='polynomial', order=4).
-      These use the actual numerical values of the index.
-    * 'krogh', 'piecewise_polynomial', 'spline', 'pchip' and 'akima'
-      are all wrappers around the scipy interpolation methods of
-      similar names. These use the actual numerical values of the
-      index. For more information on their behavior, see the
-      `scipy documentation
-      <http://docs.scipy.org/doc/scipy/reference/interpolate.html#univariate-interpolation>`__
-      and `tutorial documentation
-      <http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html>`__
-    * 'from_derivatives' refers to BPoly.from_derivatives which
-      replaces 'piecewise_polynomial' interpolation method in
-      scipy 0.18

-    .. versionadded:: 0.18.1

-       Added support for the 'akima' method
-       Added interpolate method 'from_derivatives' which replaces
-       'piecewise_polynomial' in scipy 0.18; backwards-compatible with
-       scipy < 0.18

-axis : {0, 1}, default 0
-    * 0: fill column-by-column
-    * 1: fill row-by-row
-limit : int, default None.
-    Maximum number of consecutive NaNs to fill. Must be greater than 0.
-limit_direction : {'forward', 'backward', 'both'}, default 'forward'
-    If limit is specified, consecutive NaNs will be filled in this
-    direction.

-    .. versionadded:: 0.17.0

-inplace : bool, default False
-    Update the NDFrame in place if possible.
-downcast : optional, 'infer' or None, defaults to None
-    Downcast dtypes if possible.
-kwargs : keyword arguments to pass on to the interpolating function.

-Returns
--------
-Series or DataFrame of same shape interpolated at the NaNs

-See Also
---------
-reindex, replace, fillna

-Examples
---------

-Filling in NaNs

->>> s = pd.Series([0, 1, np.nan, 3])
->>> s.interpolate()
-0    0
-1    1
-2    2
-3    3
-dtype: float64
- -
isnull(self)
Return a boolean same-sized object indicating if the values are null.

-See Also
---------
-notnull : boolean inverse of isnull
- -
last(self, offset)
Convenience method for subsetting final periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.last('5M') -> Last 5 months

-Returns
--------
-subset : type of caller
- -
mask(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is False and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The mask method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``False`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``mask`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.where`
- -
notnull(self)
Return a boolean same-sized object indicating if the values are
-not null.

-See Also
---------
-isnull : boolean inverse of notnull
- -
pct_change(self, periods=1, fill_method='pad', limit=None, freq=None, **kwargs)
Percent change over given number of periods.

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming percent change
-fill_method : str, default 'pad'
-    How to handle NAs before computing percent changes
-limit : int, default None
-    The number of consecutive NAs to fill before stopping
-freq : DateOffset, timedelta, or offset alias string, optional
-    Increment to use from time series API (e.g. 'M' or BDay())

-Returns
--------
-chg : NDFrame

-Notes
------

-By default, the percentage change is calculated along the stat
-axis: 0, or ``Index``, for ``DataFrame`` and 1, or ``minor`` for
-``Panel``. You can change this with the ``axis`` keyword argument.
- -
pipe(self, func, *args, **kwargs)
Apply func(self, \*args, \*\*kwargs)

-.. versionadded:: 0.16.2

-Parameters
-----------
-func : function
-    function to apply to the NDFrame.
-    ``args``, and ``kwargs`` are passed into ``func``.
-    Alternatively a ``(callable, data_keyword)`` tuple where
-    ``data_keyword`` is a string indicating the keyword of
-    ``callable`` that expects the NDFrame.
-args : positional arguments passed into ``func``.
-kwargs : a dictionary of keyword arguments passed into ``func``.

-Returns
--------
-object : the return type of ``func``.

-Notes
------

-Use ``.pipe`` when chaining together functions that expect
-on Series or DataFrames. Instead of writing

->>> f(g(h(df), arg1=a), arg2=b, arg3=c)

-You can write

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe(f, arg2=b, arg3=c)
-... )

-If you have a function that takes the data as (say) the second
-argument, pass a tuple indicating which keyword expects the
-data. For example, suppose ``f`` takes its data as ``arg2``:

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe((f, 'arg2'), arg1=a, arg3=c)
-...  )

-See Also
---------
-pandas.DataFrame.apply
-pandas.DataFrame.applymap
-pandas.Series.map
- -
pop(self, item)
Return item and drop from frame. Raise KeyError if not found.
- -
rank(self, axis=0, method='average', numeric_only=None, na_option='keep', ascending=True, pct=False)
Compute numerical data ranks (1 through n) along axis. Equal values are
-assigned a rank that is the average of the ranks of those values

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    index to direct ranking
-method : {'average', 'min', 'max', 'first', 'dense'}
-    * average: average rank of group
-    * min: lowest rank in group
-    * max: highest rank in group
-    * first: ranks assigned in order they appear in the array
-    * dense: like 'min', but rank always increases by 1 between groups
-numeric_only : boolean, default None
-    Include only float, int, boolean data. Valid only for DataFrame or
-    Panel objects
-na_option : {'keep', 'top', 'bottom'}
-    * keep: leave NA values where they are
-    * top: smallest rank if ascending
-    * bottom: smallest rank if descending
-ascending : boolean, default True
-    False for ranks by high (1) to low (N)
-pct : boolean, default False
-    Computes percentage rank of data

-Returns
--------
-ranks : same type as caller
- -
reindex_like(self, other, method=None, copy=True, limit=None, tolerance=None)
Return an object with matching indices to myself.

-Parameters
-----------
-other : Object
-method : string or None
-copy : boolean, default True
-limit : int, default None
-    Maximum number of consecutive labels to fill for inexact matches.
-tolerance : optional
-    Maximum distance between labels of the other object and this
-    object for inexact matches.

-    .. versionadded:: 0.17.0

-Notes
------
-Like calling s.reindex(index=other.index, columns=other.columns,
-                       method=...)

-Returns
--------
-reindexed : same as input
- -
rename_axis(self, mapper, axis=0, copy=True, inplace=False)
Alter index and / or columns using input function or functions.
-A scalar or list-like for ``mapper`` will alter the ``Index.name``
-or ``MultiIndex.names`` attribute.
-A function or dict for ``mapper`` will alter the labels.
-Function / dict values must be unique (1-to-1). Labels not contained in
-a dict / Series will be left as-is.

-Parameters
-----------
-mapper : scalar, list-like, dict-like or function, optional
-axis : int or string, default 0
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False

-Returns
--------
-renamed : type of caller

-See Also
---------
-pandas.NDFrame.rename
-pandas.Index.rename

-Examples
---------
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename_axis("foo")  # scalar, alters df.index.name
-     A  B
-foo
-0    1  4
-1    2  5
-2    3  6
->>> df.rename_axis(lambda x: 2 * x)  # function: alters labels
-   A  B
-0  1  4
-2  2  5
-4  3  6
->>> df.rename_axis({"A": "ehh", "C": "see"}, axis="columns")  # mapping
-   ehh  B
-0    1  4
-1    2  5
-2    3  6
- -
replace(self, to_replace=None, value=None, inplace=False, limit=None, regex=False, method='pad', axis=None)
Replace values given in 'to_replace' with 'value'.

-Parameters
-----------
-to_replace : str, regex, list, dict, Series, numeric, or None

-    * str or regex:

-        - str: string exactly matching `to_replace` will be replaced
-          with `value`
-        - regex: regexs matching `to_replace` will be replaced with
-          `value`

-    * list of str, regex, or numeric:

-        - First, if `to_replace` and `value` are both lists, they
-          **must** be the same length.
-        - Second, if ``regex=True`` then all of the strings in **both**
-          lists will be interpreted as regexs otherwise they will match
-          directly. This doesn't matter much for `value` since there
-          are only a few possible substitution regexes you can use.
-        - str and regex rules apply as above.

-    * dict:

-        - Nested dictionaries, e.g., {'a': {'b': nan}}, are read as
-          follows: look in column 'a' for the value 'b' and replace it
-          with nan. You can nest regular expressions as well. Note that
-          column names (the top-level dictionary keys in a nested
-          dictionary) **cannot** be regular expressions.
-        - Keys map to column names and values map to substitution
-          values. You can treat this as a special case of passing two
-          lists except that you are specifying the column to search in.

-    * None:

-        - This means that the ``regex`` argument must be a string,
-          compiled regular expression, or list, dict, ndarray or Series
-          of such elements. If `value` is also ``None`` then this
-          **must** be a nested dictionary or ``Series``.

-    See the examples section for examples of each of these.
-value : scalar, dict, list, str, regex, default None
-    Value to use to fill holes (e.g. 0), alternately a dict of values
-    specifying which value to use for each column (columns not in the
-    dict will not be filled). Regular expressions, strings and lists or
-    dicts of such objects are also allowed.
-inplace : boolean, default False
-    If True, in place. Note: this will modify any
-    other views on this object (e.g. a column form a DataFrame).
-    Returns the caller if this is True.
-limit : int, default None
-    Maximum size gap to forward or backward fill
-regex : bool or same types as `to_replace`, default False
-    Whether to interpret `to_replace` and/or `value` as regular
-    expressions. If this is ``True`` then `to_replace` *must* be a
-    string. Otherwise, `to_replace` must be ``None`` because this
-    parameter will be interpreted as a regular expression or a list,
-    dict, or array of regular expressions.
-method : string, optional, {'pad', 'ffill', 'bfill'}
-    The method to use when for replacement, when ``to_replace`` is a
-    ``list``.

-See Also
---------
-NDFrame.reindex
-NDFrame.asfreq
-NDFrame.fillna

-Returns
--------
-filled : NDFrame

-Raises
-------
-AssertionError
-    * If `regex` is not a ``bool`` and `to_replace` is not ``None``.
-TypeError
-    * If `to_replace` is a ``dict`` and `value` is not a ``list``,
-      ``dict``, ``ndarray``, or ``Series``
-    * If `to_replace` is ``None`` and `regex` is not compilable into a
-      regular expression or is a list, dict, ndarray, or Series.
-ValueError
-    * If `to_replace` and `value` are ``list`` s or ``ndarray`` s, but
-      they are not the same length.

-Notes
------
-* Regex substitution is performed under the hood with ``re.sub``. The
-  rules for substitution for ``re.sub`` are the same.
-* Regular expressions will only substitute on strings, meaning you
-  cannot provide, for example, a regular expression matching floating
-  point numbers and expect the columns in your frame that have a
-  numeric dtype to be matched. However, if those floating point numbers
-  *are* strings, then you can do this.
-* This method has *a lot* of options. You are encouraged to experiment
-  and play with this method to gain intuition about how it works.
- -
resample(self, rule, how=None, axis=0, fill_method=None, closed=None, label=None, convention='start', kind=None, loffset=None, limit=None, base=0, on=None, level=None)
Convenience method for frequency conversion and resampling of time
-series.  Object must have a datetime-like index (DatetimeIndex,
-PeriodIndex, or TimedeltaIndex), or pass datetime-like values
-to the on or level keyword.

-Parameters
-----------
-rule : string
-    the offset string or object representing target conversion
-axis : int, optional, default 0
-closed : {'right', 'left'}
-    Which side of bin interval is closed
-label : {'right', 'left'}
-    Which bin edge label to label bucket with
-convention : {'start', 'end', 's', 'e'}
-loffset : timedelta
-    Adjust the resampled time labels
-base : int, default 0
-    For frequencies that evenly subdivide 1 day, the "origin" of the
-    aggregated intervals. For example, for '5min' frequency, base could
-    range from 0 through 4. Defaults to 0
-on : string, optional
-    For a DataFrame, column to use instead of index for resampling.
-    Column must be datetime-like.

-    .. versionadded:: 0.19.0

-level : string or int, optional
-    For a MultiIndex, level (name or number) to use for
-    resampling.  Level must be datetime-like.

-    .. versionadded:: 0.19.0

-Notes
------
-To learn more about the offset strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-Examples
---------

-Start by creating a series with 9 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=9, freq='T')
->>> series = pd.Series(range(9), index=index)
->>> series
-2000-01-01 00:00:00    0
-2000-01-01 00:01:00    1
-2000-01-01 00:02:00    2
-2000-01-01 00:03:00    3
-2000-01-01 00:04:00    4
-2000-01-01 00:05:00    5
-2000-01-01 00:06:00    6
-2000-01-01 00:07:00    7
-2000-01-01 00:08:00    8
-Freq: T, dtype: int64

-Downsample the series into 3 minute bins and sum the values
-of the timestamps falling into a bin.

->>> series.resample('3T').sum()
-2000-01-01 00:00:00     3
-2000-01-01 00:03:00    12
-2000-01-01 00:06:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but label each
-bin using the right edge instead of the left. Please note that the
-value in the bucket used as the label is not included in the bucket,
-which it labels. For example, in the original series the
-bucket ``2000-01-01 00:03:00`` contains the value 3, but the summed
-value in the resampled bucket with the label``2000-01-01 00:03:00``
-does not include 3 (if it did, the summed value would be 6, not 3).
-To include this value close the right side of the bin interval as
-illustrated in the example below this one.

->>> series.resample('3T', label='right').sum()
-2000-01-01 00:03:00     3
-2000-01-01 00:06:00    12
-2000-01-01 00:09:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but close the right
-side of the bin interval.

->>> series.resample('3T', label='right', closed='right').sum()
-2000-01-01 00:00:00     0
-2000-01-01 00:03:00     6
-2000-01-01 00:06:00    15
-2000-01-01 00:09:00    15
-Freq: 3T, dtype: int64

-Upsample the series into 30 second bins.

->>> series.resample('30S').asfreq()[0:5] #select first 5 rows
-2000-01-01 00:00:00   0.0
-2000-01-01 00:00:30   NaN
-2000-01-01 00:01:00   1.0
-2000-01-01 00:01:30   NaN
-2000-01-01 00:02:00   2.0
-Freq: 30S, dtype: float64

-Upsample the series into 30 second bins and fill the ``NaN``
-values using the ``pad`` method.

->>> series.resample('30S').pad()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    0
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    1
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Upsample the series into 30 second bins and fill the
-``NaN`` values using the ``bfill`` method.

->>> series.resample('30S').bfill()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    1
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    2
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Pass a custom function via ``apply``

->>> def custom_resampler(array_like):
-...     return np.sum(array_like)+5

->>> series.resample('3T').apply(custom_resampler)
-2000-01-01 00:00:00     8
-2000-01-01 00:03:00    17
-2000-01-01 00:06:00    26
-Freq: 3T, dtype: int64

-For DataFrame objects, the keyword ``on`` can be used to specify the
-column instead of the index for resampling.

->>> df = pd.DataFrame(data=9*[range(4)], columns=['a', 'b', 'c', 'd'])
->>> df['time'] = pd.date_range('1/1/2000', periods=9, freq='T')
->>> df.resample('3T', on='time').sum()
-                     a  b  c  d
-time
-2000-01-01 00:00:00  0  3  6  9
-2000-01-01 00:03:00  0  3  6  9
-2000-01-01 00:06:00  0  3  6  9

-For a DataFrame with MultiIndex, the keyword ``level`` can be used to
-specify on level the resampling needs to take place.

->>> time = pd.date_range('1/1/2000', periods=5, freq='T')
->>> df2 = pd.DataFrame(data=10*[range(4)],
-                       columns=['a', 'b', 'c', 'd'],
-                       index=pd.MultiIndex.from_product([time, [1, 2]])
-                       )
->>> df2.resample('3T', level=0).sum()
-                     a  b   c   d
-2000-01-01 00:00:00  0  6  12  18
-2000-01-01 00:03:00  0  4   8  12
- -
sample(self, n=None, frac=None, replace=False, weights=None, random_state=None, axis=None)
Returns a random sample of items from an axis of object.

-.. versionadded:: 0.16.1

-Parameters
-----------
-n : int, optional
-    Number of items from axis to return. Cannot be used with `frac`.
-    Default = 1 if `frac` = None.
-frac : float, optional
-    Fraction of axis items to return. Cannot be used with `n`.
-replace : boolean, optional
-    Sample with or without replacement. Default = False.
-weights : str or ndarray-like, optional
-    Default 'None' results in equal probability weighting.
-    If passed a Series, will align with target object on index. Index
-    values in weights not found in sampled object will be ignored and
-    index values in sampled object not in weights will be assigned
-    weights of zero.
-    If called on a DataFrame, will accept the name of a column
-    when axis = 0.
-    Unless weights are a Series, weights must be same length as axis
-    being sampled.
-    If weights do not sum to 1, they will be normalized to sum to 1.
-    Missing values in the weights column will be treated as zero.
-    inf and -inf values not allowed.
-random_state : int or numpy.random.RandomState, optional
-    Seed for the random number generator (if int), or numpy RandomState
-    object.
-axis : int or string, optional
-    Axis to sample. Accepts axis number or name. Default is stat axis
-    for given data type (0 for Series and DataFrames, 1 for Panels).

-Returns
--------
-A new object of same type as caller.

-Examples
---------

-Generate an example ``Series`` and ``DataFrame``:

->>> s = pd.Series(np.random.randn(50))
->>> s.head()
-0   -0.038497
-1    1.820773
-2   -0.972766
-3   -1.598270
-4   -1.095526
-dtype: float64
->>> df = pd.DataFrame(np.random.randn(50, 4), columns=list('ABCD'))
->>> df.head()
-          A         B         C         D
-0  0.016443 -2.318952 -0.566372 -1.028078
-1 -1.051921  0.438836  0.658280 -0.175797
-2 -1.243569 -0.364626 -0.215065  0.057736
-3  1.768216  0.404512 -0.385604 -1.457834
-4  1.072446 -1.137172  0.314194 -0.046661

-Next extract a random sample from both of these objects...

-3 random elements from the ``Series``:

->>> s.sample(n=3)
-27   -0.994689
-55   -1.049016
-67   -0.224565
-dtype: float64

-And a random 10% of the ``DataFrame`` with replacement:

->>> df.sample(frac=0.1, replace=True)
-           A         B         C         D
-35  1.981780  0.142106  1.817165 -0.290805
-49 -1.336199 -0.448634 -0.789640  0.217116
-40  0.823173 -0.078816  1.009536  1.015108
-15  1.421154 -0.055301 -1.922594 -0.019696
-6  -0.148339  0.832938  1.787600 -1.383767
- -
select(self, crit, axis=0)
Return data corresponding to axis labels matching criteria

-Parameters
-----------
-crit : function
-    To be called on each index (label). Should return True or False
-axis : int

-Returns
--------
-selection : type of caller
- -
set_axis(self, axis, labels)
public verson of axis assignment
- -
slice_shift(self, periods=1, axis=0)
Equivalent to `shift` without copying data. The shifted data will
-not include the dropped periods and the shifted axis will be smaller
-than the original.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative

-Notes
------
-While the `slice_shift` is faster than `shift`, you may pay for it
-later during alignment.

-Returns
--------
-shifted : same type as caller
- -
squeeze(self, axis=None)
Squeeze length 1 dimensions.

-Parameters
-----------
-axis : None, integer or string axis name, optional
-    The axis to squeeze if 1-sized.

-    .. versionadded:: 0.20.0

-Returns
--------
-scalar if 1-sized, else original object
- -
swapaxes(self, axis1, axis2, copy=True)
Interchange axes and swap values axes appropriately

-Returns
--------
-y : same as input
- -
tail(self, n=5)
Returns last n rows
- -
to_clipboard(self, excel=None, sep=None, **kwargs)
Attempt to write text representation of object to the system clipboard
-This can be pasted into Excel, for example.

-Parameters
-----------
-excel : boolean, defaults to True
-        if True, use the provided separator, writing in a csv
-        format for allowing easy pasting into excel.
-        if False, write a string representation of the object
-        to the clipboard
-sep : optional, defaults to tab
-other keywords are passed to to_csv

-Notes
------
-Requirements for your platform
-  - Linux: xclip, or xsel (with gtk or PyQt4 modules)
-  - Windows: none
-  - OS X: none
- -
to_dense(self)
Return dense representation of NDFrame (as opposed to sparse)
- -
to_hdf(self, path_or_buf, key, **kwargs)
Write the contained data to an HDF5 file using HDFStore.

-Parameters
-----------
-path_or_buf : the path (string) or HDFStore object
-key : string
-    identifier for the group in the store
-mode : optional, {'a', 'w', 'r+'}, default 'a'

-  ``'w'``
-      Write; a new file is created (an existing file with the same
-      name would be deleted).
-  ``'a'``
-      Append; an existing file is opened for reading and writing,
-      and if the file does not exist it is created.
-  ``'r+'``
-      It is similar to ``'a'``, but the file must already exist.
-format : 'fixed(f)|table(t)', default is 'fixed'
-    fixed(f) : Fixed format
-               Fast writing/reading. Not-appendable, nor searchable
-    table(t) : Table format
-               Write as a PyTables Table structure which may perform
-               worse but allow more flexible operations like searching
-               / selecting subsets of the data
-append : boolean, default False
-    For Table formats, append the input data to the existing
-data_columns :  list of columns, or True, default None
-    List of columns to create as indexed data columns for on-disk
-    queries, or True to use all columns. By default only the axes
-    of the object are indexed. See `here
-    <http://pandas.pydata.org/pandas-docs/stable/io.html#query-via-data-columns>`__.

-    Applicable only to format='table'.
-complevel : int, 1-9, default 0
-    If a complib is specified compression will be applied
-    where possible
-complib : {'zlib', 'bzip2', 'lzo', 'blosc', None}, default None
-    If complevel is > 0 apply compression to objects written
-    in the store wherever possible
-fletcher32 : bool, default False
-    If applying compression use the fletcher32 checksum
-dropna : boolean, default False.
-    If true, ALL nan rows will not be written to store.
- -
to_json(self, path_or_buf=None, orient=None, date_format=None, double_precision=10, force_ascii=True, date_unit='ms', default_handler=None, lines=False)
Convert the object to a JSON string.

-Note NaN's and None will be converted to null and datetime objects
-will be converted to UNIX timestamps.

-Parameters
-----------
-path_or_buf : the path or buffer to write the result string
-    if this is None, return a StringIO of the converted string
-orient : string

-    * Series

-      - default is 'index'
-      - allowed values are: {'split','records','index'}

-    * DataFrame

-      - default is 'columns'
-      - allowed values are:
-        {'split','records','index','columns','values'}

-    * The format of the JSON string

-      - split : dict like
-        {index -> [index], columns -> [columns], data -> [values]}
-      - records : list like
-        [{column -> value}, ... , {column -> value}]
-      - index : dict like {index -> {column -> value}}
-      - columns : dict like {column -> {index -> value}}
-      - values : just the values array
-      - table : dict like {'schema': {schema}, 'data': {data}}
-        describing the data, and the data component is
-        like ``orient='records'``.

-        .. versionchanged:: 0.20.0

-date_format : {None, 'epoch', 'iso'}
-    Type of date conversion. `epoch` = epoch milliseconds,
-    `iso` = ISO8601. The default depends on the `orient`. For
-    `orient='table'`, the default is `'iso'`. For all other orients,
-    the default is `'epoch'`.
-double_precision : The number of decimal places to use when encoding
-    floating point values, default 10.
-force_ascii : force encoded string to be ASCII, default True.
-date_unit : string, default 'ms' (milliseconds)
-    The time unit to encode to, governs timestamp and ISO8601
-    precision.  One of 's', 'ms', 'us', 'ns' for second, millisecond,
-    microsecond, and nanosecond respectively.
-default_handler : callable, default None
-    Handler to call if object cannot otherwise be converted to a
-    suitable format for JSON. Should receive a single argument which is
-    the object to convert and return a serialisable object.
-lines : boolean, default False
-    If 'orient' is 'records' write out line delimited json format. Will
-    throw ValueError if incorrect 'orient' since others are not list
-    like.

-    .. versionadded:: 0.19.0

-Returns
--------
-same type as input object with filtered info axis

-See Also
---------
-pd.read_json

-Examples
---------

->>> df = pd.DataFrame([['a', 'b'], ['c', 'd']],
-...                   index=['row 1', 'row 2'],
-...                   columns=['col 1', 'col 2'])
->>> df.to_json(orient='split')
-'{"columns":["col 1","col 2"],
-  "index":["row 1","row 2"],
-  "data":[["a","b"],["c","d"]]}'

-Encoding/decoding a Dataframe using ``'index'`` formatted JSON:

->>> df.to_json(orient='index')
-'{"row 1":{"col 1":"a","col 2":"b"},"row 2":{"col 1":"c","col 2":"d"}}'

-Encoding/decoding a Dataframe using ``'records'`` formatted JSON.
-Note that index labels are not preserved with this encoding.

->>> df.to_json(orient='records')
-'[{"col 1":"a","col 2":"b"},{"col 1":"c","col 2":"d"}]'

-Encoding with Table Schema

->>> df.to_json(orient='table')
-'{"schema": {"fields": [{"name": "index", "type": "string"},
-                        {"name": "col 1", "type": "string"},
-                        {"name": "col 2", "type": "string"}],
-             "primaryKey": "index",
-             "pandas_version": "0.20.0"},
-  "data": [{"index": "row 1", "col 1": "a", "col 2": "b"},
-           {"index": "row 2", "col 1": "c", "col 2": "d"}]}'
- -
to_msgpack(self, path_or_buf=None, encoding='utf-8', **kwargs)
msgpack (serialize) object to input file path

-THIS IS AN EXPERIMENTAL LIBRARY and the storage format
-may not be stable until a future release.

-Parameters
-----------
-path : string File path, buffer-like, or None
-    if None, return generated string
-append : boolean whether to append to an existing msgpack
-    (default is False)
-compress : type of compressor (zlib or blosc), default to None (no
-    compression)
- -
to_pickle(self, path, compression='infer')
Pickle (serialize) object to input file path.

-Parameters
-----------
-path : string
-    File path
-compression : {'infer', 'gzip', 'bz2', 'xz', None}, default 'infer'
-    a string representing the compression to use in the output file

-    .. versionadded:: 0.20.0
- -
to_sql(self, name, con, flavor=None, schema=None, if_exists='fail', index=True, index_label=None, chunksize=None, dtype=None)
Write records stored in a DataFrame to a SQL database.

-Parameters
-----------
-name : string
-    Name of SQL table
-con : SQLAlchemy engine or DBAPI2 connection (legacy mode)
-    Using SQLAlchemy makes it possible to use any DB supported by that
-    library. If a DBAPI2 object, only sqlite3 is supported.
-flavor : 'sqlite', default None
-    DEPRECATED: this parameter will be removed in a future version,
-    as 'sqlite' is the only supported option if SQLAlchemy is not
-    installed.
-schema : string, default None
-    Specify the schema (if database flavor supports this). If None, use
-    default schema.
-if_exists : {'fail', 'replace', 'append'}, default 'fail'
-    - fail: If table exists, do nothing.
-    - replace: If table exists, drop it, recreate it, and insert data.
-    - append: If table exists, insert data. Create if does not exist.
-index : boolean, default True
-    Write DataFrame index as a column.
-index_label : string or sequence, default None
-    Column label for index column(s). If None is given (default) and
-    `index` is True, then the index names are used.
-    A sequence should be given if the DataFrame uses MultiIndex.
-chunksize : int, default None
-    If not None, then rows will be written in batches of this size at a
-    time.  If None, all rows will be written at once.
-dtype : dict of column name to SQL type, default None
-    Optional specifying the datatype for columns. The SQL type should
-    be a SQLAlchemy type, or a string for sqlite3 fallback connection.
- -
to_xarray(self)
Return an xarray object from the pandas object.

-Returns
--------
-a DataArray for a Series
-a Dataset for a DataFrame
-a DataArray for higher dims

-Examples
---------
->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)})
->>> df
-   A    B    C
-0  1  foo  4.0
-1  1  bar  5.0
-2  2  foo  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (index: 3)
-Coordinates:
-  * index    (index) int64 0 1 2
-Data variables:
-    A        (index) int64 1 1 2
-    B        (index) object 'foo' 'bar' 'foo'
-    C        (index) float64 4.0 5.0 6.0

->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)}
-                     ).set_index(['B','A'])
->>> df
-         C
-B   A
-foo 1  4.0
-bar 1  5.0
-foo 2  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (A: 2, B: 2)
-Coordinates:
-  * B        (B) object 'bar' 'foo'
-  * A        (A) int64 1 2
-Data variables:
-    C        (B, A) float64 5.0 nan 4.0 6.0

->>> p = pd.Panel(np.arange(24).reshape(4,3,2),
-                 items=list('ABCD'),
-                 major_axis=pd.date_range('20130101', periods=3),
-                 minor_axis=['first', 'second'])
->>> p
-<class 'pandas.core.panel.Panel'>
-Dimensions: 4 (items) x 3 (major_axis) x 2 (minor_axis)
-Items axis: A to D
-Major_axis axis: 2013-01-01 00:00:00 to 2013-01-03 00:00:00
-Minor_axis axis: first to second

->>> p.to_xarray()
-<xarray.DataArray (items: 4, major_axis: 3, minor_axis: 2)>
-array([[[ 0,  1],
-        [ 2,  3],
-        [ 4,  5]],
-       [[ 6,  7],
-        [ 8,  9],
-        [10, 11]],
-       [[12, 13],
-        [14, 15],
-        [16, 17]],
-       [[18, 19],
-        [20, 21],
-        [22, 23]]])
-Coordinates:
-  * items       (items) object 'A' 'B' 'C' 'D'
-  * major_axis  (major_axis) datetime64[ns] 2013-01-01 2013-01-02 2013-01-03  # noqa
-  * minor_axis  (minor_axis) object 'first' 'second'

-Notes
------
-See the `xarray docs <http://xarray.pydata.org/en/stable/>`__
- -
truncate(self, before=None, after=None, axis=None, copy=True)
Truncates a sorted NDFrame before and/or after some particular
-index value. If the axis contains only datetime values, before/after
-parameters are converted to datetime values.

-Parameters
-----------
-before : date
-    Truncate before index value
-after : date
-    Truncate after index value
-axis : the truncation axis, defaults to the stat axis
-copy : boolean, default is True,
-    return a copy of the truncated section

-Returns
--------
-truncated : type of caller
- -
tshift(self, periods=1, freq=None, axis=0)
Shift the time index, using the index's frequency if available.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, default None
-    Increment to use from the tseries module or time rule (e.g. 'EOM')
-axis : int or basestring
-    Corresponds to the axis that contains the Index

-Notes
------
-If freq is not specified then tries to use the freq or inferred_freq
-attributes of the index. If neither of those attributes exist, a
-ValueError is thrown

-Returns
--------
-shifted : NDFrame
- -
tz_convert(self, tz, axis=0, level=None, copy=True)
Convert tz-aware axis to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to convert
-level : int, str, default None
-    If axis ia a MultiIndex, convert a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data

-Returns
--------

-Raises
-------
-TypeError
-    If the axis is tz-naive.
- -
tz_localize(self, tz, axis=0, level=None, copy=True, ambiguous='raise')
Localize tz-naive TimeSeries to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to localize
-level : int, str, default None
-    If axis ia a MultiIndex, localize a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data
-ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise'
-    - 'infer' will attempt to infer fall dst-transition hours based on
-      order
-    - bool-ndarray where True signifies a DST time, False designates
-      a non-DST time (note that this flag is only applicable for
-      ambiguous times)
-    - 'NaT' will return NaT where there are ambiguous times
-    - 'raise' will raise an AmbiguousTimeError if there are ambiguous
-      times
-infer_dst : boolean, default False (DEPRECATED)
-    Attempt to infer fall dst-transition hours based on order

-Returns
--------

-Raises
-------
-TypeError
-    If the TimeSeries is tz-aware and tz is not None.
- -
where(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is True and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The where method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``True`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``where`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.mask`
- -
xs(self, key, axis=0, level=None, drop_level=True)
Returns a cross-section (row(s) or column(s)) from the
-Series/DataFrame. Defaults to cross-section on the rows (axis=0).

-Parameters
-----------
-key : object
-    Some label contained in the index, or partially in a MultiIndex
-axis : int, default 0
-    Axis to retrieve cross-section on
-level : object, defaults to first n levels (n=1 or len(key))
-    In case of a key partially contained in a MultiIndex, indicate
-    which levels are used. Levels can be referred by label or position.
-drop_level : boolean, default True
-    If False, returns object with same levels as self.

-Examples
---------
->>> df
-   A  B  C
-a  4  5  2
-b  4  0  9
-c  9  7  3
->>> df.xs('a')
-A    4
-B    5
-C    2
-Name: a
->>> df.xs('C', axis=1)
-a    2
-b    9
-c    3
-Name: C

->>> df
-                    A  B  C  D
-first second third
-bar   one    1      4  1  8  9
-      two    1      7  5  5  0
-baz   one    1      6  6  8  0
-      three  2      5  3  5  3
->>> df.xs(('baz', 'three'))
-       A  B  C  D
-third
-2      5  3  5  3
->>> df.xs('one', level=1)
-             A  B  C  D
-first third
-bar   1      4  1  8  9
-baz   1      6  6  8  0
->>> df.xs(('baz', 2), level=[0, 'third'])
-        A  B  C  D
-second
-three   5  3  5  3

-Returns
--------
-xs : Series or DataFrame

-Notes
------
-xs is only for getting, not setting values.

-MultiIndex Slicers is a generic way to get/set values on any level or
-levels.  It is a superset of xs functionality, see
-:ref:`MultiIndex Slicers <advanced.mi_slicers>`
- -
-Data descriptors inherited from pandas.core.generic.NDFrame:
-
at
-
Fast label-based scalar accessor

-Similarly to ``loc``, ``at`` provides **label** based scalar lookups.
-You can also set using these indexers.
-
-
blocks
-
Internal property, property synonym for as_blocks()
-
-
iat
-
Fast integer location scalar accessor.

-Similarly to ``iloc``, ``iat`` provides **integer** based lookups.
-You can also set using these indexers.
-
-
iloc
-
Purely integer-location based indexing for selection by position.

-``.iloc[]`` is primarily integer position based (from ``0`` to
-``length-1`` of the axis), but may also be used with a boolean
-array.

-Allowed inputs are:

-- An integer, e.g. ``5``.
-- A list or array of integers, e.g. ``[4, 3, 0]``.
-- A slice object with ints, e.g. ``1:7``.
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.iloc`` will raise ``IndexError`` if a requested indexer is
-out-of-bounds, except *slice* indexers which allow out-of-bounds
-indexing (this conforms with python/numpy *slice* semantics).

-See more at :ref:`Selection by Position <indexing.integer>`
-
-
ix
-
A primarily label-location based indexer, with integer position
-fallback.

-``.ix[]`` supports mixed integer and label based access. It is
-primarily label based, but will fall back to integer positional
-access unless the corresponding axis is of integer type.

-``.ix`` is the most general indexer and will support any of the
-inputs in ``.loc`` and ``.iloc``. ``.ix`` also supports floating
-point label schemes. ``.ix`` is exceptionally useful when dealing
-with mixed positional and label based hierachical indexes.

-However, when an axis is integer based, ONLY label based access
-and not positional access is supported. Thus, in such cases, it's
-usually better to be explicit and use ``.iloc`` or ``.loc``.

-See more at :ref:`Advanced Indexing <advanced>`.
-
-
loc
-
Purely label-location based indexer for selection by label.

-``.loc[]`` is primarily label based, but may also be used with a
-boolean array.

-Allowed inputs are:

-- A single label, e.g. ``5`` or ``'a'``, (note that ``5`` is
-  interpreted as a *label* of the index, and **never** as an
-  integer position along the index).
-- A list or array of labels, e.g. ``['a', 'b', 'c']``.
-- A slice object with labels, e.g. ``'a':'f'`` (note that contrary
-  to usual python slices, **both** the start and the stop are included!).
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.loc`` will raise a ``KeyError`` when the items are not found.

-See more at :ref:`Selection by Label <indexing.label>`
-
-
-Data and other attributes inherited from pandas.core.generic.NDFrame:
-
is_copy = None
- -
-Methods inherited from pandas.core.base.PandasObject:
-
__dir__(self)
Provide method name lookup and completion
-Only provide 'public' methods
- -
__sizeof__(self)
Generates the total memory usage for a object that returns
-either a value or Series of values
- -
-Methods inherited from pandas.core.base.StringMixin:
-
__bytes__(self)
Return a string representation for a particular object.

-Invoked by bytes(obj) in py3 only.
-Yields a bytestring in both py2/py3.
- -
__repr__(self)
Return a string representation for a particular object.

-Yields Bytestring in Py2, Unicode String in py3.
- -
__str__(self)
Return a string representation for a particular Object

-Invoked by str(df) in both py2/py3.
-Yields Bytestring in Py2, Unicode String in py3.
- -

- - - - - - - -
 
-class TimeFrame(MyDataFrame)
   MyTimeFrame is a modified version of a Pandas DataFrame,
-with a few changes to make it more suited to our purpose.

-In particular, DataFrame provides two special variables called
-`dt` and `T` that cause problems if we try to use those names
-as state variables.

-So I added new definitions that override the special variables
-and make these names useable as row labels.
 
 
Method resolution order:
-
TimeFrame
-
MyDataFrame
-
pandas.core.frame.DataFrame
-
pandas.core.generic.NDFrame
-
pandas.core.base.PandasObject
-
pandas.core.base.StringMixin
-
pandas.core.base.SelectionMixin
-
builtins.object
-
-
-Methods inherited from MyDataFrame:
-
__init__(self, *args, **kwargs)
Initialize self.  See help(type(self)) for accurate signature.
- -
-Data descriptors inherited from MyDataFrame:
-
T
-
Intercept the Series accessor object so we can use `T`
-as a row label and access it using dot notation.

-https://pandas.pydata.org/pandas-docs/stable/generated/
-pandas.DataFrame.T.html#pandas.DataFrame.T
-
-
dt
-
Intercept the Series accessor object so we can use `dt`
-as a row label and access it using dot notation.

-https://pandas.pydata.org/pandas-docs/stable/generated/
-pandas.DataFrame.dt.html
-
-
-Methods inherited from pandas.core.frame.DataFrame:
-
__add__(self, other, axis=None, level=None, fill_value=None)
Binary operator __add__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__and__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __and__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__div__ = __truediv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __truediv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__eq__(self, other)
Wrapper for comparison method __eq__
- -
__floordiv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __floordiv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__ge__(self, other)
Wrapper for comparison method __ge__
- -
__getitem__(self, key)
- -
__gt__(self, other)
Wrapper for comparison method __gt__
- -
__iadd__ = f(self, other)
- -
__imul__ = f(self, other)
- -
__ipow__ = f(self, other)
- -
__isub__ = f(self, other)
- -
__itruediv__ = f(self, other)
- -
__le__(self, other)
Wrapper for comparison method __le__
- -
__len__(self)
Returns length of info axis, but here we use the index
- -
__lt__(self, other)
Wrapper for comparison method __lt__
- -
__mod__(self, other, axis=None, level=None, fill_value=None)
Binary operator __mod__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__mul__(self, other, axis=None, level=None, fill_value=None)
Binary operator __mul__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__ne__(self, other)
Wrapper for comparison method __ne__
- -
__or__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __or__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__pow__(self, other, axis=None, level=None, fill_value=None)
Binary operator __pow__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__radd__(self, other, axis=None, level=None, fill_value=None)
Binary operator __radd__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rand__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __rand__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rdiv__ = __rtruediv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rtruediv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rfloordiv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rfloordiv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rmod__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rmod__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rmul__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rmul__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__ror__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __ror__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rpow__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rpow__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rsub__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rsub__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rtruediv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __rtruediv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__rxor__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __rxor__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__setitem__(self, key, value)
- -
__sub__(self, other, axis=None, level=None, fill_value=None)
Binary operator __sub__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__truediv__(self, other, axis=None, level=None, fill_value=None)
Binary operator __truediv__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
__unicode__(self)
Return a string representation for a particular DataFrame

-Invoked by unicode(df) in py2 only. Yields a Unicode String in both
-py2/py3.
- -
__xor__(self, other, axis='columns', level=None, fill_value=None)
Binary operator __xor__ with support to substitute a fill_value for missing data in
-one of the inputs

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame locations are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame
- -
add(self, other, axis='columns', level=None, fill_value=None)
Addition of dataframe and other, element-wise (binary operator `add`).

-Equivalent to ``dataframe + other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.radd
- -
agg = aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a DataFrame or when passed to DataFrame.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : DataFrame

-Examples
---------

->>> df = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
-...                   index=pd.date_range('1/1/2000', periods=10))
->>> df.iloc[3:7] = np.nan

-Aggregate these functions across all columns

->>> df.agg(['sum', 'min'])
-            A         B         C
-sum -0.182253 -0.614014 -2.909534
-min -1.916563 -1.460076 -1.568297

-Different aggregations per column

->>> df.agg({'A' : ['sum', 'min'], 'B' : ['min', 'max']})
-            A         B
-max       NaN  1.514318
-min -1.916563 -1.460076
-sum -0.182253       NaN

-See also
---------
-pandas.DataFrame.apply
-pandas.DataFrame.transform
-pandas.DataFrame.groupby.aggregate
-pandas.DataFrame.resample.aggregate
-pandas.DataFrame.rolling.aggregate
- -
aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a DataFrame or when passed to DataFrame.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : DataFrame

-Examples
---------

->>> df = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
-...                   index=pd.date_range('1/1/2000', periods=10))
->>> df.iloc[3:7] = np.nan

-Aggregate these functions across all columns

->>> df.agg(['sum', 'min'])
-            A         B         C
-sum -0.182253 -0.614014 -2.909534
-min -1.916563 -1.460076 -1.568297

-Different aggregations per column

->>> df.agg({'A' : ['sum', 'min'], 'B' : ['min', 'max']})
-            A         B
-max       NaN  1.514318
-min -1.916563 -1.460076
-sum -0.182253       NaN

-See also
---------
-pandas.DataFrame.apply
-pandas.DataFrame.transform
-pandas.DataFrame.groupby.aggregate
-pandas.DataFrame.resample.aggregate
-pandas.DataFrame.rolling.aggregate
- -
align(self, other, join='outer', axis=None, level=None, copy=True, fill_value=None, method=None, limit=None, fill_axis=0, broadcast_axis=None)
Align two object on their axes with the
-specified join method for each axis Index

-Parameters
-----------
-other : DataFrame or Series
-join : {'outer', 'inner', 'left', 'right'}, default 'outer'
-axis : allowed axis of the other object, default None
-    Align on index (0), columns (1), or both (None)
-level : int or level name, default None
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-copy : boolean, default True
-    Always returns new objects. If copy=False and no reindexing is
-    required then original objects are returned.
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-method : str, default None
-limit : int, default None
-fill_axis : {0 or 'index', 1 or 'columns'}, default 0
-    Filling axis, method and limit
-broadcast_axis : {0 or 'index', 1 or 'columns'}, default None
-    Broadcast values along this axis, if aligning two objects of
-    different dimensions

-    .. versionadded:: 0.17.0

-Returns
--------
-(left, right) : (DataFrame, type of other)
-    Aligned objects
- -
all(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether all elements are True over requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-all : Series or DataFrame (if level specified)
- -
any(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether any element is True over requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-any : Series or DataFrame (if level specified)
- -
append(self, other, ignore_index=False, verify_integrity=False)
Append rows of `other` to the end of this frame, returning a new
-object. Columns not in this frame are added as new columns.

-Parameters
-----------
-other : DataFrame or Series/dict-like object, or list of these
-    The data to append.
-ignore_index : boolean, default False
-    If True, do not use the index labels.
-verify_integrity : boolean, default False
-    If True, raise ValueError on creating index with duplicates.

-Returns
--------
-appended : DataFrame

-Notes
------
-If a list of dict/series is passed and the keys are all contained in
-the DataFrame's index, the order of the columns in the resulting
-DataFrame will be unchanged.

-See also
---------
-pandas.concat : General function to concatenate DataFrameSeries
-    or Panel objects

-Examples
---------

->>> df = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))
->>> df
-   A  B
-0  1  2
-1  3  4
->>> df2 = pd.DataFrame([[5, 6], [7, 8]], columns=list('AB'))
->>> df.append(df2)
-   A  B
-0  1  2
-1  3  4
-0  5  6
-1  7  8

-With `ignore_index` set to True:

->>> df.append(df2, ignore_index=True)
-   A  B
-0  1  2
-1  3  4
-2  5  6
-3  7  8
- -
apply(self, func, axis=0, broadcast=False, raw=False, reduce=None, args=(), **kwds)
Applies function along input axis of DataFrame.

-Objects passed to functions are Series objects having index
-either the DataFrame's index (axis=0) or the columns (axis=1).
-Return type depends on whether passed function aggregates, or the
-reduce argument if the DataFrame is empty.

-Parameters
-----------
-func : function
-    Function to apply to each column/row
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    * 0 or 'index': apply function to each column
-    * 1 or 'columns': apply function to each row
-broadcast : boolean, default False
-    For aggregation functions, return object of same size with values
-    propagated
-raw : boolean, default False
-    If False, convert each row or column into a Series. If raw=True the
-    passed function will receive ndarray objects instead. If you are
-    just applying a NumPy reduction function this will achieve much
-    better performance
-reduce : boolean or None, default None
-    Try to apply reduction procedures. If the DataFrame is empty,
-    apply will use reduce to determine whether the result should be a
-    Series or a DataFrame. If reduce is None (the default), apply's
-    return value will be guessed by calling func an empty Series (note:
-    while guessing, exceptions raised by func will be ignored). If
-    reduce is True a Series will always be returned, and if False a
-    DataFrame will always be returned.
-args : tuple
-    Positional arguments to pass to function in addition to the
-    array/series
-Additional keyword arguments will be passed as keywords to the function

-Notes
------
-In the current implementation apply calls func twice on the
-first column/row to decide whether it can take a fast or slow
-code path. This can lead to unexpected behavior if func has
-side-effects, as they will take effect twice for the first
-column/row.

-Examples
---------
->>> df.apply(numpy.sqrt) # returns DataFrame
->>> df.apply(numpy.sum, axis=0) # equiv to df.sum(0)
->>> df.apply(numpy.sum, axis=1) # equiv to df.sum(1)

-See also
---------
-DataFrame.applymap: For elementwise operations
-DataFrame.aggregate: only perform aggregating type operations
-DataFrame.transform: only perform transformating type operations

-Returns
--------
-applied : Series or DataFrame
- -
applymap(self, func)
Apply a function to a DataFrame that is intended to operate
-elementwise, i.e. like doing map(func, series) for each series in the
-DataFrame

-Parameters
-----------
-func : function
-    Python function, returns a single value from a single value

-Examples
---------

->>> df = pd.DataFrame(np.random.randn(3, 3))
->>> df
-    0         1          2
-0  -0.029638  1.081563   1.280300
-1   0.647747  0.831136  -1.549481
-2   0.513416 -0.884417   0.195343
->>> df = df.applymap(lambda x: '%.2f' % x)
->>> df
-    0         1          2
-0  -0.03      1.08       1.28
-1   0.65      0.83      -1.55
-2   0.51     -0.88       0.20

-Returns
--------
-applied : DataFrame

-See also
---------
-DataFrame.apply : For operations on rows/columns
- -
assign(self, **kwargs)
Assign new columns to a DataFrame, returning a new object
-(a copy) with all the original columns in addition to the new ones.

-.. versionadded:: 0.16.0

-Parameters
-----------
-kwargs : keyword, value pairs
-    keywords are the column names. If the values are
-    callable, they are computed on the DataFrame and
-    assigned to the new columns. The callable must not
-    change input DataFrame (though pandas doesn't check it).
-    If the values are not callable, (e.g. a Series, scalar, or array),
-    they are simply assigned.

-Returns
--------
-df : DataFrame
-    A new DataFrame with the new columns in addition to
-    all the existing columns.

-Notes
------
-Since ``kwargs`` is a dictionary, the order of your
-arguments may not be preserved. To make things predicatable,
-the columns are inserted in alphabetical order, at the end of
-your DataFrame. Assigning multiple columns within the same
-``assign`` is possible, but you cannot reference other columns
-created within the same ``assign`` call.

-Examples
---------
->>> df = DataFrame({'A': range(1, 11), 'B': np.random.randn(10)})

-Where the value is a callable, evaluated on `df`:

->>> df.assign(ln_A = lambda x: np.log(x.A))
-    A         B      ln_A
-0   1  0.426905  0.000000
-1   2 -0.780949  0.693147
-2   3 -0.418711  1.098612
-3   4 -0.269708  1.386294
-4   5 -0.274002  1.609438
-5   6 -0.500792  1.791759
-6   7  1.649697  1.945910
-7   8 -1.495604  2.079442
-8   9  0.549296  2.197225
-9  10 -0.758542  2.302585

-Where the value already exists and is inserted:

->>> newcol = np.log(df['A'])
->>> df.assign(ln_A=newcol)
-    A         B      ln_A
-0   1  0.426905  0.000000
-1   2 -0.780949  0.693147
-2   3 -0.418711  1.098612
-3   4 -0.269708  1.386294
-4   5 -0.274002  1.609438
-5   6 -0.500792  1.791759
-6   7  1.649697  1.945910
-7   8 -1.495604  2.079442
-8   9  0.549296  2.197225
-9  10 -0.758542  2.302585
- -
boxplot(self, column=None, by=None, ax=None, fontsize=None, rot=0, grid=True, figsize=None, layout=None, return_type=None, **kwds)
Make a box plot from DataFrame column optionally grouped by some columns or
-other inputs

-Parameters
-----------
-data : the pandas object holding the data
-column : column name or list of names, or vector
-    Can be any valid input to groupby
-by : string or sequence
-    Column in the DataFrame to group by
-ax : Matplotlib axes object, optional
-fontsize : int or string
-rot : label rotation angle
-figsize : A tuple (width, height) in inches
-grid : Setting this to True will show the grid
-layout : tuple (optional)
-    (rows, columns) for the layout of the plot
-return_type : {None, 'axes', 'dict', 'both'}, default None
-    The kind of object to return. The default is ``axes``
-    'axes' returns the matplotlib axes the boxplot is drawn on;
-    'dict' returns a dictionary  whose values are the matplotlib
-    Lines of the boxplot;
-    'both' returns a namedtuple with the axes and dict.

-    When grouping with ``by``, a Series mapping columns to ``return_type``
-    is returned, unless ``return_type`` is None, in which case a NumPy
-    array of axes is returned with the same shape as ``layout``.
-    See the prose documentation for more.

-kwds : other plotting keyword arguments to be passed to matplotlib boxplot
-       function

-Returns
--------
-lines : dict
-ax : matplotlib Axes
-(ax, lines): namedtuple

-Notes
------
-Use ``return_type='dict'`` when you want to tweak the appearance
-of the lines after plotting. In this case a dict containing the Lines
-making up the boxes, caps, fliers, medians, and whiskers is returned.
- -
combine(self, other, func, fill_value=None, overwrite=True)
Add two DataFrame objects and do not propagate NaN values, so if for a
-(column, time) one frame is missing a value, it will default to the
-other frame's value (which might be NaN as well)

-Parameters
-----------
-other : DataFrame
-func : function
-fill_value : scalar value
-overwrite : boolean, default True
-    If True then overwrite values for common keys in the calling frame

-Returns
--------
-result : DataFrame
- -
combine_first(self, other)
Combine two DataFrame objects and default to non-null values in frame
-calling the method. Result index columns will be the union of the
-respective indexes and columns

-Parameters
-----------
-other : DataFrame

-Examples
---------
-a's values prioritized, use values from b to fill holes:

->>> a.combine_first(b)


-Returns
--------
-combined : DataFrame
- -
compound(self, axis=None, skipna=None, level=None)
Return the compound percentage of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-compounded : Series or DataFrame (if level specified)
- -
corr(self, method='pearson', min_periods=1)
Compute pairwise correlation of columns, excluding NA/null values

-Parameters
-----------
-method : {'pearson', 'kendall', 'spearman'}
-    * pearson : standard correlation coefficient
-    * kendall : Kendall Tau correlation coefficient
-    * spearman : Spearman rank correlation
-min_periods : int, optional
-    Minimum number of observations required per pair of columns
-    to have a valid result. Currently only available for pearson
-    and spearman correlation

-Returns
--------
-y : DataFrame
- -
corrwith(self, other, axis=0, drop=False)
Compute pairwise correlation between rows or columns of two DataFrame
-objects.

-Parameters
-----------
-other : DataFrame
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    0 or 'index' to compute column-wise, 1 or 'columns' for row-wise
-drop : boolean, default False
-    Drop missing indices from result, default returns union of all

-Returns
--------
-correls : Series
- -
count(self, axis=0, level=None, numeric_only=False)
Return Series with number of non-NA/null observations over requested
-axis. Works with non-floating point data as well (detects NaN and None)

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    0 or 'index' for row-wise, 1 or 'columns' for column-wise
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a DataFrame
-numeric_only : boolean, default False
-    Include only float, int, boolean data

-Returns
--------
-count : Series (or DataFrame if level specified)
- -
cov(self, min_periods=None)
Compute pairwise covariance of columns, excluding NA/null values

-Parameters
-----------
-min_periods : int, optional
-    Minimum number of observations required per pair of columns
-    to have a valid result.

-Returns
--------
-y : DataFrame

-Notes
------
-`y` contains the covariance matrix of the DataFrame's time series.
-The covariance is normalized by N-1 (unbiased estimator).
- -
cummax(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative max over requested axis.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummax : Series



-See also
---------
-pandas.core.window.Expanding.max : Similar functionality
-    but ignores ``NaN`` values.
- -
cummin(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative minimum over requested axis.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummin : Series



-See also
---------
-pandas.core.window.Expanding.min : Similar functionality
-    but ignores ``NaN`` values.
- -
cumprod(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative product over requested axis.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumprod : Series



-See also
---------
-pandas.core.window.Expanding.prod : Similar functionality
-    but ignores ``NaN`` values.
- -
cumsum(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative sum over requested axis.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumsum : Series



-See also
---------
-pandas.core.window.Expanding.sum : Similar functionality
-    but ignores ``NaN`` values.
- -
diff(self, periods=1, axis=0)
1st discrete difference of object

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming difference
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    Take difference over rows (0) or columns (1).

-    .. versionadded: 0.16.1

-Returns
--------
-diffed : DataFrame
- -
div = truediv(self, other, axis='columns', level=None, fill_value=None)
Floating division of dataframe and other, element-wise (binary operator `truediv`).

-Equivalent to ``dataframe / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rtruediv
- -
divide = truediv(self, other, axis='columns', level=None, fill_value=None)
Floating division of dataframe and other, element-wise (binary operator `truediv`).

-Equivalent to ``dataframe / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rtruediv
- -
dot(self, other)
Matrix multiplication with DataFrame or Series objects

-Parameters
-----------
-other : DataFrame or Series

-Returns
--------
-dot_product : DataFrame or Series
- -
drop_duplicates(self, subset=None, keep='first', inplace=False)
Return DataFrame with duplicate rows removed, optionally only
-considering certain columns

-Parameters
-----------
-subset : column label or sequence of labels, optional
-    Only consider certain columns for identifying duplicates, by
-    default use all of the columns
-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Drop duplicates except for the first occurrence.
-    - ``last`` : Drop duplicates except for the last occurrence.
-    - False : Drop all duplicates.
-inplace : boolean, default False
-    Whether to drop duplicates in place or to return a copy

-Returns
--------
-deduplicated : DataFrame
- -
dropna(self, axis=0, how='any', thresh=None, subset=None, inplace=False)
Return object with labels on given axis omitted where alternately any
-or all of the data are missing

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, or tuple/list thereof
-    Pass tuple or list to drop on multiple axes
-how : {'any', 'all'}
-    * any : if any NA values are present, drop that label
-    * all : if all values are NA, drop that label
-thresh : int, default None
-    int value : require that many non-NA values
-subset : array-like
-    Labels along other axis to consider, e.g. if you are dropping rows
-    these would be a list of columns to include
-inplace : boolean, default False
-    If True, do operation inplace and return None.

-Returns
--------
-dropped : DataFrame

-Examples
---------
->>> df = pd.DataFrame([[np.nan, 2, np.nan, 0], [3, 4, np.nan, 1],
-...                    [np.nan, np.nan, np.nan, 5]],
-...                   columns=list('ABCD'))
->>> df
-     A    B   C  D
-0  NaN  2.0 NaN  0
-1  3.0  4.0 NaN  1
-2  NaN  NaN NaN  5

-Drop the columns where all elements are nan:

->>> df.dropna(axis=1, how='all')
-     A    B  D
-0  NaN  2.0  0
-1  3.0  4.0  1
-2  NaN  NaN  5

-Drop the columns where any of the elements is nan

->>> df.dropna(axis=1, how='any')
-   D
-0  0
-1  1
-2  5

-Drop the rows where all of the elements are nan
-(there is no row to drop, so df stays the same):

->>> df.dropna(axis=0, how='all')
-     A    B   C  D
-0  NaN  2.0 NaN  0
-1  3.0  4.0 NaN  1
-2  NaN  NaN NaN  5

-Keep only the rows with at least 2 non-na values:

->>> df.dropna(thresh=2)
-     A    B   C  D
-0  NaN  2.0 NaN  0
-1  3.0  4.0 NaN  1
- -
duplicated(self, subset=None, keep='first')
Return boolean Series denoting duplicate rows, optionally only
-considering certain columns

-Parameters
-----------
-subset : column label or sequence of labels, optional
-    Only consider certain columns for identifying duplicates, by
-    default use all of the columns
-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Mark duplicates as ``True`` except for the
-      first occurrence.
-    - ``last`` : Mark duplicates as ``True`` except for the
-      last occurrence.
-    - False : Mark all duplicates as ``True``.

-Returns
--------
-duplicated : Series
- -
eq(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods eq
- -
eval(self, expr, inplace=None, **kwargs)
Evaluate an expression in the context of the calling DataFrame
-instance.

-Parameters
-----------
-expr : string
-    The expression string to evaluate.
-inplace : bool
-    If the expression contains an assignment, whether to return a new
-    DataFrame or mutate the existing.

-    WARNING: inplace=None currently falls back to to True, but
-    in a future version, will default to False.  Use inplace=True
-    explicitly rather than relying on the default.

-    .. versionadded:: 0.18.0

-kwargs : dict
-    See the documentation for :func:`~pandas.eval` for complete details
-    on the keyword arguments accepted by
-    :meth:`~pandas.DataFrame.query`.

-Returns
--------
-ret : ndarray, scalar, or pandas object

-See Also
---------
-pandas.DataFrame.query
-pandas.DataFrame.assign
-pandas.eval

-Notes
------
-For more details see the API documentation for :func:`~pandas.eval`.
-For detailed examples see :ref:`enhancing performance with eval
-<enhancingperf.eval>`.

-Examples
---------
->>> from numpy.random import randn
->>> from pandas import DataFrame
->>> df = DataFrame(randn(10, 2), columns=list('ab'))
->>> df.eval('a + b')
->>> df.eval('c = a + b')
- -
ewm(self, com=None, span=None, halflife=None, alpha=None, min_periods=0, freq=None, adjust=True, ignore_na=False, axis=0)
Provides exponential weighted functions

-.. versionadded:: 0.18.0

-Parameters
-----------
-com : float, optional
-    Specify decay in terms of center of mass,
-    :math:`\alpha = 1 / (1 + com),\text{ for } com \geq 0`
-span : float, optional
-    Specify decay in terms of span,
-    :math:`\alpha = 2 / (span + 1),\text{ for } span \geq 1`
-halflife : float, optional
-    Specify decay in terms of half-life,
-    :math:`\alpha = 1 - exp(log(0.5) / halflife),\text{ for } halflife > 0`
-alpha : float, optional
-    Specify smoothing factor :math:`\alpha` directly,
-    :math:`0 < \alpha \leq 1`

-    .. versionadded:: 0.18.0

-min_periods : int, default 0
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : None or string alias / date offset object, default=None (DEPRECATED)
-    Frequency to conform to before computing statistic
-adjust : boolean, default True
-    Divide by decaying adjustment factor in beginning periods to account
-    for imbalance in relative weightings (viewing EWMA as a moving average)
-ignore_na : boolean, default False
-    Ignore missing values when calculating weights;
-    specify True to reproduce pre-0.15.0 behavior

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.ewm(com=0.5).mean()
-          B
-0  0.000000
-1  0.750000
-2  1.615385
-3  1.615385
-4  3.670213

-Notes
------
-Exactly one of center of mass, span, half-life, and alpha must be provided.
-Allowed values and relationship between the parameters are specified in the
-parameter descriptions above; see the link at the end of this section for
-a detailed explanation.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-When adjust is True (default), weighted averages are calculated using
-weights (1-alpha)**(n-1), (1-alpha)**(n-2), ..., 1-alpha, 1.

-When adjust is False, weighted averages are calculated recursively as:
-   weighted_average[0] = arg[0];
-   weighted_average[i] = (1-alpha)*weighted_average[i-1] + alpha*arg[i].

-When ignore_na is False (default), weights are based on absolute positions.
-For example, the weights of x and y used in calculating the final weighted
-average of [x, None, y] are (1-alpha)**2 and 1 (if adjust is True), and
-(1-alpha)**2 and alpha (if adjust is False).

-When ignore_na is True (reproducing pre-0.15.0 behavior), weights are based
-on relative positions. For example, the weights of x and y used in
-calculating the final weighted average of [x, None, y] are 1-alpha and 1
-(if adjust is True), and 1-alpha and alpha (if adjust is False).

-More details can be found at
-http://pandas.pydata.org/pandas-docs/stable/computation.html#exponentially-weighted-windows
- -
expanding(self, min_periods=1, freq=None, center=False, axis=0)
Provides expanding transformations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-axis : int or string, default 0

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.expanding(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  3.0
-4  7.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).
- -
fillna(self, value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)
Fill NA/NaN values using the specified method

-Parameters
-----------
-value : scalar, dict, Series, or DataFrame
-    Value to use to fill holes (e.g. 0), alternately a
-    dict/Series/DataFrame of values specifying which value to use for
-    each index (for a Series) or column (for a DataFrame). (values not
-    in the dict/Series/DataFrame will not be filled). This value cannot
-    be a list.
-method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
-    Method to use for filling holes in reindexed Series
-    pad / ffill: propagate last valid observation forward to next valid
-    backfill / bfill: use NEXT valid observation to fill gap
-axis : {0 or 'index', 1 or 'columns'}
-inplace : boolean, default False
-    If True, fill in place. Note: this will modify any
-    other views on this object, (e.g. a no-copy slice for a column in a
-    DataFrame).
-limit : int, default None
-    If method is specified, this is the maximum number of consecutive
-    NaN values to forward/backward fill. In other words, if there is
-    a gap with more than this number of consecutive NaNs, it will only
-    be partially filled. If method is not specified, this is the
-    maximum number of entries along the entire axis where NaNs will be
-    filled. Must be greater than 0 if not None.
-downcast : dict, default is None
-    a dict of item->dtype of what to downcast if possible,
-    or the string 'infer' which will try to downcast to an appropriate
-    equal type (e.g. float64 to int64 if possible)

-See Also
---------
-reindex, asfreq

-Returns
--------
-filled : DataFrame
- -
first_valid_index(self)
Return label for first non-NA/null value
- -
floordiv(self, other, axis='columns', level=None, fill_value=None)
Integer division of dataframe and other, element-wise (binary operator `floordiv`).

-Equivalent to ``dataframe // other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rfloordiv
- -
ge(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods ge
- -
get_value(self, index, col, takeable=False)
Quickly retrieve single value at passed column and index

-Parameters
-----------
-index : row label
-col : column label
-takeable : interpret the index/col as indexers, default False

-Returns
--------
-value : scalar value
- -
gt(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods gt
- -
hist = hist_frame(data, column=None, by=None, grid=True, xlabelsize=None, xrot=None, ylabelsize=None, yrot=None, ax=None, sharex=False, sharey=False, figsize=None, layout=None, bins=10, **kwds)
Draw histogram of the DataFrame's series using matplotlib / pylab.

-Parameters
-----------
-data : DataFrame
-column : string or sequence
-    If passed, will be used to limit data to a subset of columns
-by : object, optional
-    If passed, then used to form histograms for separate groups
-grid : boolean, default True
-    Whether to show axis grid lines
-xlabelsize : int, default None
-    If specified changes the x-axis label size
-xrot : float, default None
-    rotation of x axis labels
-ylabelsize : int, default None
-    If specified changes the y-axis label size
-yrot : float, default None
-    rotation of y axis labels
-ax : matplotlib axes object, default None
-sharex : boolean, default True if ax is None else False
-    In case subplots=True, share x axis and set some x axis labels to
-    invisible; defaults to True if ax is None otherwise False if an ax
-    is passed in; Be aware, that passing in both an ax and sharex=True
-    will alter all x axis labels for all subplots in a figure!
-sharey : boolean, default False
-    In case subplots=True, share y axis and set some y axis labels to
-    invisible
-figsize : tuple
-    The size of the figure to create in inches by default
-layout : tuple, optional
-    Tuple of (rows, columns) for the layout of the histograms
-bins : integer, default 10
-    Number of histogram bins to be used
-kwds : other plotting keyword arguments
-    To be passed to hist function
- -
idxmax(self, axis=0, skipna=True)
Return index of first occurrence of maximum over requested axis.
-NA/null values are excluded.

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    0 or 'index' for row-wise, 1 or 'columns' for column-wise
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be first index.

-Returns
--------
-idxmax : Series

-Notes
------
-This method is the DataFrame version of ``ndarray.argmax``.

-See Also
---------
-Series.idxmax
- -
idxmin(self, axis=0, skipna=True)
Return index of first occurrence of minimum over requested axis.
-NA/null values are excluded.

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    0 or 'index' for row-wise, 1 or 'columns' for column-wise
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-idxmin : Series

-Notes
------
-This method is the DataFrame version of ``ndarray.argmin``.

-See Also
---------
-Series.idxmin
- -
info(self, verbose=None, buf=None, max_cols=None, memory_usage=None, null_counts=None)
Concise summary of a DataFrame.

-Parameters
-----------
-verbose : {None, True, False}, optional
-    Whether to print the full summary.
-    None follows the `display.max_info_columns` setting.
-    True or False overrides the `display.max_info_columns` setting.
-buf : writable buffer, defaults to sys.stdout
-max_cols : int, default None
-    Determines whether full summary or short summary is printed.
-    None follows the `display.max_info_columns` setting.
-memory_usage : boolean/string, default None
-    Specifies whether total memory usage of the DataFrame
-    elements (including index) should be displayed. None follows
-    the `display.memory_usage` setting. True or False overrides
-    the `display.memory_usage` setting. A value of 'deep' is equivalent
-    of True, with deep introspection. Memory usage is shown in
-    human-readable units (base-2 representation).
-null_counts : boolean, default None
-    Whether to show the non-null counts

-    - If None, then only show if the frame is smaller than
-      max_info_rows and max_info_columns.
-    - If True, always show counts.
-    - If False, never show counts.
- -
insert(self, loc, column, value, allow_duplicates=False)
Insert column into DataFrame at specified location.

-If `allow_duplicates` is False, raises Exception if column
-is already contained in the DataFrame.

-Parameters
-----------
-loc : int
-    Must have 0 <= loc <= len(columns)
-column : object
-value : scalar, Series, or array-like
- -
isin(self, values)
Return boolean DataFrame showing whether each element in the
-DataFrame is contained in values.

-Parameters
-----------
-values : iterable, SeriesDataFrame or dictionary
-    The result will only be true at a location if all the
-    labels match. If `values` is a Series, that's the index. If
-    `values` is a dictionary, the keys must be the column names,
-    which must match. If `values` is a DataFrame,
-    then both the index and column labels must match.

-Returns
--------

-DataFrame of booleans

-Examples
---------
-When ``values`` is a list:

->>> df = DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'f']})
->>> df.isin([1, 3, 12, 'a'])
-       A      B
-0   True   True
-1  False  False
-2   True  False

-When ``values`` is a dict:

->>> df = DataFrame({'A': [1, 2, 3], 'B': [1, 4, 7]})
->>> df.isin({'A': [1, 3], 'B': [4, 7, 12]})
-       A      B
-0   True  False  # Note that B didn't match the 1 here.
-1  False   True
-2   True   True

-When ``values`` is a Series or DataFrame:

->>> df = DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'f']})
->>> other = DataFrame({'A': [1, 3, 3, 2], 'B': ['e', 'f', 'f', 'e']})
->>> df.isin(other)
-       A      B
-0   True  False
-1  False  False  # Column A in `other` has a 3, but not at index 1.
-2   True   True
- -
items = iteritems(self)
Iterator over (column name, Series) pairs.

-See also
---------
-iterrows : Iterate over DataFrame rows as (index, Series) pairs.
-itertuples : Iterate over DataFrame rows as namedtuples of the values.
- -
iteritems(self)
Iterator over (column name, Series) pairs.

-See also
---------
-iterrows : Iterate over DataFrame rows as (index, Series) pairs.
-itertuples : Iterate over DataFrame rows as namedtuples of the values.
- -
iterrows(self)
Iterate over DataFrame rows as (index, Series) pairs.

-Notes
------

-1. Because ``iterrows`` returns a Series for each row,
-   it does **not** preserve dtypes across the rows (dtypes are
-   preserved across columns for DataFrames). For example,

-   >>> df = pd.DataFrame([[1, 1.5]], columns=['int', 'float'])
-   >>> row = next(df.iterrows())[1]
-   >>> row
-   int      1.0
-   float    1.5
-   Name: 0, dtype: float64
-   >>> print(row['int'].dtype)
-   float64
-   >>> print(df['int'].dtype)
-   int64

-   To preserve dtypes while iterating over the rows, it is better
-   to use :meth:`itertuples` which returns namedtuples of the values
-   and which is generally faster than ``iterrows``.

-2. You should **never modify** something you are iterating over.
-   This is not guaranteed to work in all cases. Depending on the
-   data types, the iterator returns a copy and not a view, and writing
-   to it will have no effect.

-Returns
--------
-it : generator
-    A generator that iterates over the rows of the frame.

-See also
---------
-itertuples : Iterate over DataFrame rows as namedtuples of the values.
-iteritems : Iterate over (column name, Series) pairs.
- -
itertuples(self, index=True, name='Pandas')
Iterate over DataFrame rows as namedtuples, with index value as first
-element of the tuple.

-Parameters
-----------
-index : boolean, default True
-    If True, return the index as the first element of the tuple.
-name : string, default "Pandas"
-    The name of the returned namedtuples or None to return regular
-    tuples.

-Notes
------
-The column names will be renamed to positional names if they are
-invalid Python identifiers, repeated, or start with an underscore.
-With a large number of columns (>255), regular tuples are returned.

-See also
---------
-iterrows : Iterate over DataFrame rows as (index, Series) pairs.
-iteritems : Iterate over (column name, Series) pairs.

-Examples
---------

->>> df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]},
-                      index=['a', 'b'])
->>> df
-   col1  col2
-a     1   0.1
-b     2   0.2
->>> for row in df.itertuples():
-...     print(row)
-...
-Pandas(Index='a', col1=1, col2=0.10000000000000001)
-Pandas(Index='b', col1=2, col2=0.20000000000000001)
- -
join(self, other, on=None, how='left', lsuffix='', rsuffix='', sort=False)
Join columns with other DataFrame either on index or on a key
-column. Efficiently Join multiple DataFrame objects by index at once by
-passing a list.

-Parameters
-----------
-other : DataFrameSeries with name field set, or list of DataFrame
-    Index should be similar to one of the columns in this one. If a
-    Series is passed, its name attribute must be set, and that will be
-    used as the column name in the resulting joined DataFrame
-on : column name, tuple/list of column names, or array-like
-    Column(s) in the caller to join on the index in other,
-    otherwise joins index-on-index. If multiples
-    columns given, the passed DataFrame must have a MultiIndex. Can
-    pass an array as the join key if not already contained in the
-    calling DataFrame. Like an Excel VLOOKUP operation
-how : {'left', 'right', 'outer', 'inner'}, default: 'left'
-    How to handle the operation of the two objects.

-    * left: use calling frame's index (or column if on is specified)
-    * right: use other frame's index
-    * outer: form union of calling frame's index (or column if on is
-      specified) with other frame's index, and sort it
-      lexicographically
-    * inner: form intersection of calling frame's index (or column if
-      on is specified) with other frame's index, preserving the order
-      of the calling's one
-lsuffix : string
-    Suffix to use from left frame's overlapping columns
-rsuffix : string
-    Suffix to use from right frame's overlapping columns
-sort : boolean, default False
-    Order result DataFrame lexicographically by the join key. If False,
-    the order of the join key depends on the join type (how keyword)

-Notes
------
-on, lsuffix, and rsuffix options are not supported when passing a list
-of DataFrame objects

-Examples
---------
->>> caller = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3', 'K4', 'K5'],
-...                        'A': ['A0', 'A1', 'A2', 'A3', 'A4', 'A5']})

->>> caller
-    A key
-0  A0  K0
-1  A1  K1
-2  A2  K2
-3  A3  K3
-4  A4  K4
-5  A5  K5

->>> other = pd.DataFrame({'key': ['K0', 'K1', 'K2'],
-...                       'B': ['B0', 'B1', 'B2']})

->>> other
-    B key
-0  B0  K0
-1  B1  K1
-2  B2  K2

-Join DataFrames using their indexes.

->>> caller.join(other, lsuffix='_caller', rsuffix='_other')

->>>     A key_caller    B key_other
-    0  A0         K0   B0        K0
-    1  A1         K1   B1        K1
-    2  A2         K2   B2        K2
-    3  A3         K3  NaN       NaN
-    4  A4         K4  NaN       NaN
-    5  A5         K5  NaN       NaN


-If we want to join using the key columns, we need to set key to be
-the index in both caller and other. The joined DataFrame will have
-key as its index.

->>> caller.set_index('key').join(other.set_index('key'))

->>>      A    B
-    key
-    K0   A0   B0
-    K1   A1   B1
-    K2   A2   B2
-    K3   A3  NaN
-    K4   A4  NaN
-    K5   A5  NaN

-Another option to join using the key columns is to use the on
-parameter. DataFrame.join always uses other's index but we can use any
-column in the caller. This method preserves the original caller's
-index in the result.

->>> caller.join(other.set_index('key'), on='key')

->>>     A key    B
-    0  A0  K0   B0
-    1  A1  K1   B1
-    2  A2  K2   B2
-    3  A3  K3  NaN
-    4  A4  K4  NaN
-    5  A5  K5  NaN


-See also
---------
-DataFrame.merge : For column(s)-on-columns(s) operations

-Returns
--------
-joined : DataFrame
- -
kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : Series or DataFrame (if level specified)
- -
kurtosis = kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : Series or DataFrame (if level specified)
- -
last_valid_index(self)
Return label for last non-NA/null value
- -
le(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods le
- -
lookup(self, row_labels, col_labels)
Label-based "fancy indexing" function for DataFrame.
-Given equal-length arrays of row and column labels, return an
-array of the values corresponding to each (row, col) pair.

-Parameters
-----------
-row_labels : sequence
-    The row labels to use for lookup
-col_labels : sequence
-    The column labels to use for lookup

-Notes
------
-Akin to::

-    result = []
-    for row, col in zip(row_labels, col_labels):
-        result.append(df.get_value(row, col))

-Examples
---------
-values : ndarray
-    The found values
- -
lt(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods lt
- -
mad(self, axis=None, skipna=None, level=None)
Return the mean absolute deviation of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mad : Series or DataFrame (if level specified)
- -
max(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the maximum of the values in the object.
-            If you want the *index* of the maximum, use ``idxmax``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmax``.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-max : Series or DataFrame (if level specified)
- -
mean(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the mean of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mean : Series or DataFrame (if level specified)
- -
median(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the median of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-median : Series or DataFrame (if level specified)
- -
melt(self, id_vars=None, value_vars=None, var_name=None, value_name='value', col_level=None)
"Unpivots" a DataFrame from wide format to long format, optionally
-leaving identifier variables set.

-This function is useful to massage a DataFrame into a format where one
-or more columns are identifier variables (`id_vars`), while all other
-columns, considered measured variables (`value_vars`), are "unpivoted" to
-the row axis, leaving just two non-identifier columns, 'variable' and
-'value'.

-.. versionadded:: 0.20.0

-Parameters
-----------
-frame : DataFrame
-id_vars : tuple, list, or ndarray, optional
-    Column(s) to use as identifier variables.
-value_vars : tuple, list, or ndarray, optional
-    Column(s) to unpivot. If not specified, uses all columns that
-    are not set as `id_vars`.
-var_name : scalar
-    Name to use for the 'variable' column. If None it uses
-    ``frame.columns.name`` or 'variable'.
-value_name : scalar, default 'value'
-    Name to use for the 'value' column.
-col_level : int or string, optional
-    If columns are a MultiIndex then use this level to melt.

-See also
---------
-melt
-pivot_table
-DataFrame.pivot

-Examples
---------
->>> import pandas as pd
->>> df = pd.DataFrame({'A': {0: 'a', 1: 'b', 2: 'c'},
-...                    'B': {0: 1, 1: 3, 2: 5},
-...                    'C': {0: 2, 1: 4, 2: 6}})
->>> df
-   A  B  C
-0  a  1  2
-1  b  3  4
-2  c  5  6

->>> df.melt(id_vars=['A'], value_vars=['B'])
-   A variable  value
-0  a        B      1
-1  b        B      3
-2  c        B      5

->>> df.melt(id_vars=['A'], value_vars=['B', 'C'])
-   A variable  value
-0  a        B      1
-1  b        B      3
-2  c        B      5
-3  a        C      2
-4  b        C      4
-5  c        C      6

-The names of 'variable' and 'value' columns can be customized:

->>> df.melt(id_vars=['A'], value_vars=['B'],
-...         var_name='myVarname', value_name='myValname')
-   A myVarname  myValname
-0  a         B          1
-1  b         B          3
-2  c         B          5

-If you have multi-index columns:

->>> df.columns = [list('ABC'), list('DEF')]
->>> df
-   A  B  C
-   D  E  F
-0  a  1  2
-1  b  3  4
-2  c  5  6

->>> df.melt(col_level=0, id_vars=['A'], value_vars=['B'])
-   A variable  value
-0  a        B      1
-1  b        B      3
-2  c        B      5

->>> df.melt(id_vars=[('A', 'D')], value_vars=[('B', 'E')])
-  (A, D) variable_0 variable_1  value
-0      a          B          E      1
-1      b          B          E      3
-2      c          B          E      5
- -
memory_usage(self, index=True, deep=False)
Memory usage of DataFrame columns.

-Parameters
-----------
-index : bool
-    Specifies whether to include memory usage of DataFrame's
-    index in returned Series. If `index=True` (default is False)
-    the first index of the Series is `Index`.
-deep : bool
-    Introspect the data deeply, interrogate
-    `object` dtypes for system-level memory consumption

-Returns
--------
-sizes : Series
-    A series with column names as index and memory usage of
-    columns with units of bytes.

-Notes
------
-Memory usage does not include memory consumed by elements that
-are not components of the array if deep=False

-See Also
---------
-numpy.ndarray.nbytes
- -
merge(self, right, how='inner', on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=False, suffixes=('_x', '_y'), copy=True, indicator=False)
Merge DataFrame objects by performing a database-style join operation by
-columns or indexes.

-If joining columns on columns, the DataFrame indexes *will be
-ignored*. Otherwise if joining indexes on indexes or indexes on a column or
-columns, the index will be passed on.

-Parameters
-----------
-right : DataFrame
-how : {'left', 'right', 'outer', 'inner'}, default 'inner'
-    * left: use only keys from left frame, similar to a SQL left outer join;
-      preserve key order
-    * right: use only keys from right frame, similar to a SQL right outer join;
-      preserve key order
-    * outer: use union of keys from both frames, similar to a SQL full outer
-      join; sort keys lexicographically
-    * inner: use intersection of keys from both frames, similar to a SQL inner
-      join; preserve the order of the left keys
-on : label or list
-    Field names to join on. Must be found in both DataFrames. If on is
-    None and not merging on indexes, then it merges on the intersection of
-    the columns by default.
-left_on : label or list, or array-like
-    Field names to join on in left DataFrame. Can be a vector or list of
-    vectors of the length of the DataFrame to use a particular vector as
-    the join key instead of columns
-right_on : label or list, or array-like
-    Field names to join on in right DataFrame or vector/list of vectors per
-    left_on docs
-left_index : boolean, default False
-    Use the index from the left DataFrame as the join key(s). If it is a
-    MultiIndex, the number of keys in the other DataFrame (either the index
-    or a number of columns) must match the number of levels
-right_index : boolean, default False
-    Use the index from the right DataFrame as the join key. Same caveats as
-    left_index
-sort : boolean, default False
-    Sort the join keys lexicographically in the result DataFrame. If False,
-    the order of the join keys depends on the join type (how keyword)
-suffixes : 2-length sequence (tuple, list, ...)
-    Suffix to apply to overlapping column names in the left and right
-    side, respectively
-copy : boolean, default True
-    If False, do not copy data unnecessarily
-indicator : boolean or string, default False
-    If True, adds a column to output DataFrame called "_merge" with
-    information on the source of each row.
-    If string, column with information on source of each row will be added to
-    output DataFrame, and column will be named value of string.
-    Information column is Categorical-type and takes on a value of "left_only"
-    for observations whose merge key only appears in 'left' DataFrame,
-    "right_only" for observations whose merge key only appears in 'right'
-    DataFrame, and "both" if the observation's merge key is found in both.

-    .. versionadded:: 0.17.0

-Examples
---------

->>> A              >>> B
-    lkey value         rkey value
-0   foo  1         0   foo  5
-1   bar  2         1   bar  6
-2   baz  3         2   qux  7
-3   foo  4         3   bar  8

->>> A.merge(B, left_on='lkey', right_on='rkey', how='outer')
-   lkey  value_x  rkey  value_y
-0  foo   1        foo   5
-1  foo   4        foo   5
-2  bar   2        bar   6
-3  bar   2        bar   8
-4  baz   3        NaN   NaN
-5  NaN   NaN      qux   7

-Returns
--------
-merged : DataFrame
-    The output type will the be same as 'left', if it is a subclass
-    of DataFrame.

-See also
---------
-merge_ordered
-merge_asof
- -
min(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the minimum of the values in the object.
-            If you want the *index* of the minimum, use ``idxmin``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmin``.

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-min : Series or DataFrame (if level specified)
- -
mod(self, other, axis='columns', level=None, fill_value=None)
Modulo of dataframe and other, element-wise (binary operator `mod`).

-Equivalent to ``dataframe % other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rmod
- -
mode(self, axis=0, numeric_only=False)
Gets the mode(s) of each element along the axis selected. Adds a row
-for each mode per label, fills in gaps with nan.

-Note that there could be multiple values returned for the selected
-axis (when more than one item share the maximum frequency), which is
-the reason why a dataframe is returned. If you want to impute missing
-values with the mode in a dataframe ``df``, you can just do this:
-``df.fillna(df.mode().iloc[0])``

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    * 0 or 'index' : get mode of each column
-    * 1 or 'columns' : get mode of each row
-numeric_only : boolean, default False
-    if True, only apply to numeric columns

-Returns
--------
-modes : DataFrame (sorted)

-Examples
---------
->>> df = pd.DataFrame({'A': [1, 2, 1, 2, 1, 2, 3]})
->>> df.mode()
-   A
-0  1
-1  2
- -
mul(self, other, axis='columns', level=None, fill_value=None)
Multiplication of dataframe and other, element-wise (binary operator `mul`).

-Equivalent to ``dataframe * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rmul
- -
multiply = mul(self, other, axis='columns', level=None, fill_value=None)
Multiplication of dataframe and other, element-wise (binary operator `mul`).

-Equivalent to ``dataframe * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rmul
- -
ne(self, other, axis='columns', level=None)
Wrapper for flexible comparison methods ne
- -
nlargest(self, n, columns, keep='first')
Get the rows of a DataFrame sorted by the `n` largest
-values of `columns`.

-.. versionadded:: 0.17.0

-Parameters
-----------
-n : int
-    Number of items to retrieve
-columns : list or str
-    Column name or names to order by
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-DataFrame

-Examples
---------
->>> df = DataFrame({'a': [1, 10, 8, 11, -1],
-...                 'b': list('abdce'),
-...                 'c': [1.0, 2.0, np.nan, 3.0, 4.0]})
->>> df.nlargest(3, 'a')
-    a  b   c
-3  11  c   3
-1  10  b   2
-2   8  d NaN
- -
nsmallest(self, n, columns, keep='first')
Get the rows of a DataFrame sorted by the `n` smallest
-values of `columns`.

-.. versionadded:: 0.17.0

-Parameters
-----------
-n : int
-    Number of items to retrieve
-columns : list or str
-    Column name or names to order by
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-DataFrame

-Examples
---------
->>> df = DataFrame({'a': [1, 10, 8, 11, -1],
-...                 'b': list('abdce'),
-...                 'c': [1.0, 2.0, np.nan, 3.0, 4.0]})
->>> df.nsmallest(3, 'a')
-   a  b   c
-4 -1  e   4
-0  1  a   1
-2  8  d NaN
- -
nunique(self, axis=0, dropna=True)
Return Series with number of distinct observations over requested
-axis.

-.. versionadded:: 0.20.0

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-dropna : boolean, default True
-    Don't include NaN in the counts.

-Returns
--------
-nunique : Series

-Examples
---------
->>> df = pd.DataFrame({'A': [1, 2, 3], 'B': [1, 1, 1]})
->>> df.nunique()
-A    3
-B    1

->>> df.nunique(axis=1)
-0    1
-1    2
-2    2
- -
pivot(self, index=None, columns=None, values=None)
Reshape data (produce a "pivot" table) based on column values. Uses
-unique values from index / columns to form axes of the resulting
-DataFrame.

-Parameters
-----------
-index : string or object, optional
-    Column name to use to make new frame's index. If None, uses
-    existing index.
-columns : string or object
-    Column name to use to make new frame's columns
-values : string or object, optional
-    Column name to use for populating new frame's values. If not
-    specified, all remaining columns will be used and the result will
-    have hierarchically indexed columns

-Returns
--------
-pivoted : DataFrame

-See also
---------
-DataFrame.pivot_table : generalization of pivot that can handle
-    duplicate values for one index/column pair
-DataFrame.unstack : pivot based on the index values instead of a
-    column

-Notes
------
-For finer-tuned control, see hierarchical indexing documentation along
-with the related stack/unstack methods

-Examples
---------

->>> df = pd.DataFrame({'foo': ['one','one','one','two','two','two'],
-                       'bar': ['A', 'B', 'C', 'A', 'B', 'C'],
-                       'baz': [1, 2, 3, 4, 5, 6]})
->>> df
-    foo   bar  baz
-0   one   A    1
-1   one   B    2
-2   one   C    3
-3   two   A    4
-4   two   B    5
-5   two   C    6

->>> df.pivot(index='foo', columns='bar', values='baz')
-     A   B   C
-one  1   2   3
-two  4   5   6

->>> df.pivot(index='foo', columns='bar')['baz']
-     A   B   C
-one  1   2   3
-two  4   5   6
- -
pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All')
Create a spreadsheet-style pivot table as a DataFrame. The levels in the
-pivot table will be stored in MultiIndex objects (hierarchical indexes) on
-the index and columns of the result DataFrame

-Parameters
-----------
-data : DataFrame
-values : column to aggregate, optional
-index : column, Grouper, array, or list of the previous
-    If an array is passed, it must be the same length as the data. The list
-    can contain any of the other types (except list).
-    Keys to group by on the pivot table index.  If an array is passed, it
-    is being used as the same manner as column values.
-columns : column, Grouper, array, or list of the previous
-    If an array is passed, it must be the same length as the data. The list
-    can contain any of the other types (except list).
-    Keys to group by on the pivot table column.  If an array is passed, it
-    is being used as the same manner as column values.
-aggfunc : function or list of functions, default numpy.mean
-    If list of functions passed, the resulting pivot table will have
-    hierarchical columns whose top level are the function names (inferred
-    from the function objects themselves)
-fill_value : scalar, default None
-    Value to replace missing values with
-margins : boolean, default False
-    Add all row / columns (e.g. for subtotal / grand totals)
-dropna : boolean, default True
-    Do not include columns whose entries are all NaN
-margins_name : string, default 'All'
-    Name of the row / column that will contain the totals
-    when margins is True.

-Examples
---------
->>> df
-   A   B   C      D
-0  foo one small  1
-1  foo one large  2
-2  foo one large  2
-3  foo two small  3
-4  foo two small  3
-5  bar one large  4
-6  bar one small  5
-7  bar two small  6
-8  bar two large  7

->>> table = pivot_table(df, values='D', index=['A', 'B'],
-...                     columns=['C'], aggfunc=np.sum)
->>> table
-          small  large
-foo  one  1      4
-     two  6      NaN
-bar  one  5      4
-     two  6      7

-Returns
--------
-table : DataFrame

-See also
---------
-DataFrame.pivot : pivot without aggregation that can handle
-    non-numeric data
- -
pow(self, other, axis='columns', level=None, fill_value=None)
Exponential power of dataframe and other, element-wise (binary operator `pow`).

-Equivalent to ``dataframe ** other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rpow
- -
prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : Series or DataFrame (if level specified)
- -
product = prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : Series or DataFrame (if level specified)
- -
quantile(self, q=0.5, axis=0, numeric_only=True, interpolation='linear')
Return values at the given quantile over requested axis, a la
-numpy.percentile.

-Parameters
-----------
-q : float or array-like, default 0.5 (50% quantile)
-    0 <= q <= 1, the quantile(s) to compute
-axis : {0, 1, 'index', 'columns'} (default 0)
-    0 or 'index' for row-wise, 1 or 'columns' for column-wise
-interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'}
-    .. versionadded:: 0.18.0

-    This optional parameter specifies the interpolation method to use,
-    when the desired quantile lies between two data points `i` and `j`:

-    * linear: `i + (j - i) * fraction`, where `fraction` is the
-      fractional part of the index surrounded by `i` and `j`.
-    * lower: `i`.
-    * higher: `j`.
-    * nearest: `i` or `j` whichever is nearest.
-    * midpoint: (`i` + `j`) / 2.

-Returns
--------
-quantiles : Series or DataFrame

-    - If ``q`` is an array, a DataFrame will be returned where the
-      index is ``q``, the columns are the columns of self, and the
-      values are the quantiles.
-    - If ``q`` is a float, a Series will be returned where the
-      index is the columns of self and the values are the quantiles.

-Examples
---------

->>> df = DataFrame(np.array([[1, 1], [2, 10], [3, 100], [4, 100]]),
-                   columns=['a', 'b'])
->>> df.quantile(.1)
-a    1.3
-b    3.7
-dtype: float64
->>> df.quantile([.1, .5])
-       a     b
-0.1  1.3   3.7
-0.5  2.5  55.0
- -
query(self, expr, inplace=False, **kwargs)
Query the columns of a frame with a boolean expression.

-.. versionadded:: 0.13

-Parameters
-----------
-expr : string
-    The query string to evaluate.  You can refer to variables
-    in the environment by prefixing them with an '@' character like
-    ``@a + b``.
-inplace : bool
-    Whether the query should modify the data in place or return
-    a modified copy

-    .. versionadded:: 0.18.0

-kwargs : dict
-    See the documentation for :func:`pandas.eval` for complete details
-    on the keyword arguments accepted by :meth:`DataFrame.query`.

-Returns
--------
-q : DataFrame

-Notes
------
-The result of the evaluation of this expression is first passed to
-:attr:`DataFrame.loc` and if that fails because of a
-multidimensional key (e.g., a DataFrame) then the result will be passed
-to :meth:`DataFrame.__getitem__`.

-This method uses the top-level :func:`pandas.eval` function to
-evaluate the passed query.

-The :meth:`~pandas.DataFrame.query` method uses a slightly
-modified Python syntax by default. For example, the ``&`` and ``|``
-(bitwise) operators have the precedence of their boolean cousins,
-:keyword:`and` and :keyword:`or`. This *is* syntactically valid Python,
-however the semantics are different.

-You can change the semantics of the expression by passing the keyword
-argument ``parser='python'``. This enforces the same semantics as
-evaluation in Python space. Likewise, you can pass ``engine='python'``
-to evaluate an expression using Python itself as a backend. This is not
-recommended as it is inefficient compared to using ``numexpr`` as the
-engine.

-The :attr:`DataFrame.index` and
-:attr:`DataFrame.columns` attributes of the
-:class:`~pandas.DataFrame` instance are placed in the query namespace
-by default, which allows you to treat both the index and columns of the
-frame as a column in the frame.
-The identifier ``index`` is used for the frame index; you can also
-use the name of the index to identify it in a query.

-For further details and examples see the ``query`` documentation in
-:ref:`indexing <indexing.query>`.

-See Also
---------
-pandas.eval
-DataFrame.eval

-Examples
---------
->>> from numpy.random import randn
->>> from pandas import DataFrame
->>> df = DataFrame(randn(10, 2), columns=list('ab'))
->>> df.query('a > b')
->>> df[df.a > df.b]  # same result as the previous expression
- -
radd(self, other, axis='columns', level=None, fill_value=None)
Addition of dataframe and other, element-wise (binary operator `radd`).

-Equivalent to ``other + dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.add
- -
rdiv = rtruediv(self, other, axis='columns', level=None, fill_value=None)
Floating division of dataframe and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.truediv
- -
reindex(self, index=None, columns=None, **kwargs)
Conform DataFrame to new index with optional filling logic, placing
-NA/NaN in locations having no value in the previous index. A new object
-is produced unless the new index is equivalent to the current one and
-copy=False

-Parameters
-----------
-index, columns : array-like, optional (can be specified in order, or as
-    keywords)
-    New labels / index to conform to. Preferably an Index object to
-    avoid duplicating data
-method : {None, 'backfill'/'bfill', 'pad'/'ffill', 'nearest'}, optional
-    method to use for filling holes in reindexed DataFrame.
-    Please note: this is only  applicable to DataFrames/Series with a
-    monotonically increasing/decreasing index.

-    * default: don't fill gaps
-    * pad / ffill: propagate last valid observation forward to next
-      valid
-    * backfill / bfill: use next valid observation to fill gap
-    * nearest: use nearest valid observations to fill gap

-copy : boolean, default True
-    Return a new object, even if the passed indexes are the same
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-limit : int, default None
-    Maximum number of consecutive elements to forward or backward fill
-tolerance : optional
-    Maximum distance between original and new labels for inexact
-    matches. The values of the index at the matching locations most
-    satisfy the equation ``abs(index[indexer] - target) <= tolerance``.

-    .. versionadded:: 0.17.0

-Examples
---------

-Create a dataframe with some fictional data.

->>> index = ['Firefox', 'Chrome', 'Safari', 'IE10', 'Konqueror']
->>> df = pd.DataFrame({
-...      'http_status': [200,200,404,404,301],
-...      'response_time': [0.04, 0.02, 0.07, 0.08, 1.0]},
-...       index=index)
->>> df
-           http_status  response_time
-Firefox            200           0.04
-Chrome             200           0.02
-Safari             404           0.07
-IE10               404           0.08
-Konqueror          301           1.00

-Create a new index and reindex the dataframe. By default
-values in the new index that do not have corresponding
-records in the dataframe are assigned ``NaN``.

->>> new_index= ['Safari', 'Iceweasel', 'Comodo Dragon', 'IE10',
-...             'Chrome']
->>> df.reindex(new_index)
-               http_status  response_time
-Safari               404.0           0.07
-Iceweasel              NaN            NaN
-Comodo Dragon          NaN            NaN
-IE10                 404.0           0.08
-Chrome               200.0           0.02

-We can fill in the missing values by passing a value to
-the keyword ``fill_value``. Because the index is not monotonically
-increasing or decreasing, we cannot use arguments to the keyword
-``method`` to fill the ``NaN`` values.

->>> df.reindex(new_index, fill_value=0)
-               http_status  response_time
-Safari                 404           0.07
-Iceweasel                0           0.00
-Comodo Dragon            0           0.00
-IE10                   404           0.08
-Chrome                 200           0.02

->>> df.reindex(new_index, fill_value='missing')
-              http_status response_time
-Safari                404          0.07
-Iceweasel         missing       missing
-Comodo Dragon     missing       missing
-IE10                  404          0.08
-Chrome                200          0.02

-To further illustrate the filling functionality in
-``reindex``, we will create a dataframe with a
-monotonically increasing index (for example, a sequence
-of dates).

->>> date_index = pd.date_range('1/1/2010', periods=6, freq='D')
->>> df2 = pd.DataFrame({"prices": [100, 101, np.nan, 100, 89, 88]},
-...                    index=date_index)
->>> df2
-            prices
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88

-Suppose we decide to expand the dataframe to cover a wider
-date range.

->>> date_index2 = pd.date_range('12/29/2009', periods=10, freq='D')
->>> df2.reindex(date_index2)
-            prices
-2009-12-29     NaN
-2009-12-30     NaN
-2009-12-31     NaN
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-The index entries that did not have a value in the original data frame
-(for example, '2009-12-29') are by default filled with ``NaN``.
-If desired, we can fill in the missing values using one of several
-options.

-For example, to backpropagate the last valid value to fill the ``NaN``
-values, pass ``bfill`` as an argument to the ``method`` keyword.

->>> df2.reindex(date_index2, method='bfill')
-            prices
-2009-12-29     100
-2009-12-30     100
-2009-12-31     100
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-Please note that the ``NaN`` value present in the original dataframe
-(at index value 2010-01-03) will not be filled by any of the
-value propagation schemes. This is because filling while reindexing
-does not look at dataframe values, but only compares the original and
-desired indexes. If you do want to fill in the ``NaN`` values present
-in the original dataframe, use the ``fillna()`` method.

-Returns
--------
-reindexed : DataFrame
- -
reindex_axis(self, labels, axis=0, method=None, level=None, copy=True, limit=None, fill_value=nan)
Conform input object to new index with optional
-filling logic, placing NA/NaN in locations having no value in the
-previous index. A new object is produced unless the new index is
-equivalent to the current one and copy=False

-Parameters
-----------
-labels : array-like
-    New labels / index to conform to. Preferably an Index object to
-    avoid duplicating data
-axis : {0 or 'index', 1 or 'columns'}
-method : {None, 'backfill'/'bfill', 'pad'/'ffill', 'nearest'}, optional
-    Method to use for filling holes in reindexed DataFrame:

-    * default: don't fill gaps
-    * pad / ffill: propagate last valid observation forward to next
-      valid
-    * backfill / bfill: use next valid observation to fill gap
-    * nearest: use nearest valid observations to fill gap

-copy : boolean, default True
-    Return a new object, even if the passed indexes are the same
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-limit : int, default None
-    Maximum number of consecutive elements to forward or backward fill
-tolerance : optional
-    Maximum distance between original and new labels for inexact
-    matches. The values of the index at the matching locations most
-    satisfy the equation ``abs(index[indexer] - target) <= tolerance``.

-    .. versionadded:: 0.17.0

-Examples
---------
->>> df.reindex_axis(['A', 'B', 'C'], axis=1)

-See Also
---------
-reindex, reindex_like

-Returns
--------
-reindexed : DataFrame
- -
rename(self, index=None, columns=None, **kwargs)
Alter axes input function or functions. Function / dict values must be
-unique (1-to-1). Labels not contained in a dict / Series will be left
-as-is. Extra labels listed don't throw an error. Alternatively, change
-``Series.name`` with a scalar value (Series only).

-Parameters
-----------
-index, columns : scalar, list-like, dict-like or function, optional
-    Scalar or list-like will alter the ``Series.name`` attribute,
-    and raise on DataFrame or Panel.
-    dict-like or functions are transformations to apply to
-    that axis' values
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False
-    Whether to return a new DataFrame. If True then value of copy is
-    ignored.
-level : int or level name, default None
-    In case of a MultiIndex, only rename labels in the specified
-    level.

-Returns
--------
-renamed : DataFrame (new object)

-See Also
---------
-pandas.NDFrame.rename_axis

-Examples
---------
->>> s = pd.Series([1, 2, 3])
->>> s
-0    1
-1    2
-2    3
-dtype: int64
->>> s.rename("my_name") # scalar, changes Series.name
-0    1
-1    2
-2    3
-Name: my_name, dtype: int64
->>> s.rename(lambda x: x ** 2)  # function, changes labels
-0    1
-1    2
-4    3
-dtype: int64
->>> s.rename({1: 3, 2: 5})  # mapping, changes labels
-0    1
-3    2
-5    3
-dtype: int64
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename(2)
-Traceback (most recent call last):
-...
-TypeError: 'int' object is not callable
->>> df.rename(index=str, columns={"A": "a", "B": "c"})
-   a  c
-0  1  4
-1  2  5
-2  3  6
->>> df.rename(index=str, columns={"A": "a", "C": "c"})
-   a  B
-0  1  4
-1  2  5
-2  3  6
- -
reorder_levels(self, order, axis=0)
Rearrange index levels using input order.
-May not drop or duplicate levels

-Parameters
-----------
-order : list of int or list of str
-    List representing new level order. Reference level by number
-    (position) or by key (label).
-axis : int
-    Where to reorder levels.

-Returns
--------
-type of caller (new object)
- -
reset_index(self, level=None, drop=False, inplace=False, col_level=0, col_fill='')
For DataFrame with multi-level index, return new DataFrame with
-labeling information in the columns under the index names, defaulting
-to 'level_0', 'level_1', etc. if any are None. For a standard index,
-the index name will be used (if set), otherwise a default 'index' or
-'level_0' (if 'index' is already taken) will be used.

-Parameters
-----------
-level : int, str, tuple, or list, default None
-    Only remove the given levels from the index. Removes all levels by
-    default
-drop : boolean, default False
-    Do not try to insert index into dataframe columns. This resets
-    the index to the default integer index.
-inplace : boolean, default False
-    Modify the DataFrame in place (do not create a new object)
-col_level : int or str, default 0
-    If the columns have multiple levels, determines which level the
-    labels are inserted into. By default it is inserted into the first
-    level.
-col_fill : object, default ''
-    If the columns have multiple levels, determines how the other
-    levels are named. If None then the index name is repeated.

-Returns
--------
-resetted : DataFrame
- -
rfloordiv(self, other, axis='columns', level=None, fill_value=None)
Integer division of dataframe and other, element-wise (binary operator `rfloordiv`).

-Equivalent to ``other // dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.floordiv
- -
rmod(self, other, axis='columns', level=None, fill_value=None)
Modulo of dataframe and other, element-wise (binary operator `rmod`).

-Equivalent to ``other % dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.mod
- -
rmul(self, other, axis='columns', level=None, fill_value=None)
Multiplication of dataframe and other, element-wise (binary operator `rmul`).

-Equivalent to ``other * dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.mul
- -
rolling(self, window, min_periods=None, freq=None, center=False, win_type=None, on=None, axis=0, closed=None)
Provides rolling window calculcations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-window : int, or offset
-    Size of the moving window. This is the number of observations used for
-    calculating the statistic. Each window will be a fixed size.

-    If its an offset then this will be the time period of each window. Each
-    window will be a variable sized based on the observations included in
-    the time-period. This is only valid for datetimelike indexes. This is
-    new in 0.19.0
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA). For a window that is specified by an offset,
-    this will default to 1.
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-win_type : string, default None
-    Provide a window type. See the notes below.
-on : string, optional
-    For a DataFrame, column on which to calculate
-    the rolling window, rather than the index
-closed : string, default None
-    Make the interval closed on the 'right', 'left', 'both' or
-    'neither' endpoints.
-    For offset-based windows, it defaults to 'right'.
-    For fixed windows, defaults to 'both'. Remaining cases not implemented
-    for fixed windows.

-    .. versionadded:: 0.20.0

-axis : int or string, default 0

-Returns
--------
-a Window or Rolling sub-classed for the particular operation

-Examples
---------

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]})
->>> df
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

-Rolling sum with a window length of 2, using the 'triang'
-window type.

->>> df.rolling(2, win_type='triang').sum()
-     B
-0  NaN
-1  1.0
-2  2.5
-3  NaN
-4  NaN

-Rolling sum with a window length of 2, min_periods defaults
-to the window length.

->>> df.rolling(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  NaN
-4  NaN

-Same as above, but explicity set the min_periods

->>> df.rolling(2, min_periods=1).sum()
-     B
-0  0.0
-1  1.0
-2  3.0
-3  2.0
-4  4.0

-A ragged (meaning not-a-regular frequency), time-indexed DataFrame

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]},
-....:                 index = [pd.Timestamp('20130101 09:00:00'),
-....:                          pd.Timestamp('20130101 09:00:02'),
-....:                          pd.Timestamp('20130101 09:00:03'),
-....:                          pd.Timestamp('20130101 09:00:05'),
-....:                          pd.Timestamp('20130101 09:00:06')])

->>> df
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  2.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0


-Contrasting to an integer rolling window, this will roll a variable
-length window corresponding to the time period.
-The default for min_periods is 1.

->>> df.rolling('2s').sum()
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  3.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-To learn more about the offsets & frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-The recognized win_types are:

-* ``boxcar``
-* ``triang``
-* ``blackman``
-* ``hamming``
-* ``bartlett``
-* ``parzen``
-* ``bohman``
-* ``blackmanharris``
-* ``nuttall``
-* ``barthann``
-* ``kaiser`` (needs beta)
-* ``gaussian`` (needs std)
-* ``general_gaussian`` (needs power, width)
-* ``slepian`` (needs width).
- -
round(self, decimals=0, *args, **kwargs)
Round a DataFrame to a variable number of decimal places.

-.. versionadded:: 0.17.0

-Parameters
-----------
-decimals : int, dict, Series
-    Number of decimal places to round each column to. If an int is
-    given, round each column to the same number of places.
-    Otherwise dict and Series round to variable numbers of places.
-    Column names should be in the keys if `decimals` is a
-    dict-like, or in the index if `decimals` is a Series. Any
-    columns not included in `decimals` will be left as is. Elements
-    of `decimals` which are not columns of the input will be
-    ignored.

-Examples
---------
->>> df = pd.DataFrame(np.random.random([3, 3]),
-...     columns=['A', 'B', 'C'], index=['first', 'second', 'third'])
->>> df
-               A         B         C
-first   0.028208  0.992815  0.173891
-second  0.038683  0.645646  0.577595
-third   0.877076  0.149370  0.491027
->>> df.round(2)
-           A     B     C
-first   0.03  0.99  0.17
-second  0.04  0.65  0.58
-third   0.88  0.15  0.49
->>> df.round({'A': 1, 'C': 2})
-          A         B     C
-first   0.0  0.992815  0.17
-second  0.0  0.645646  0.58
-third   0.9  0.149370  0.49
->>> decimals = pd.Series([1, 0, 2], index=['A', 'B', 'C'])
->>> df.round(decimals)
-          A  B     C
-first   0.0  1  0.17
-second  0.0  1  0.58
-third   0.9  0  0.49

-Returns
--------
-DataFrame object

-See Also
---------
-numpy.around
-Series.round
- -
rpow(self, other, axis='columns', level=None, fill_value=None)
Exponential power of dataframe and other, element-wise (binary operator `rpow`).

-Equivalent to ``other ** dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.pow
- -
rsub(self, other, axis='columns', level=None, fill_value=None)
Subtraction of dataframe and other, element-wise (binary operator `rsub`).

-Equivalent to ``other - dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.sub
- -
rtruediv(self, other, axis='columns', level=None, fill_value=None)
Floating division of dataframe and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / dataframe``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.truediv
- -
select_dtypes(self, include=None, exclude=None)
Return a subset of a DataFrame including/excluding columns based on
-their ``dtype``.

-Parameters
-----------
-include, exclude : list-like
-    A list of dtypes or strings to be included/excluded. You must pass
-    in a non-empty sequence for at least one of these.

-Raises
-------
-ValueError
-    * If both of ``include`` and ``exclude`` are empty
-    * If ``include`` and ``exclude`` have overlapping elements
-    * If any kind of string dtype is passed in.
-TypeError
-    * If either of ``include`` or ``exclude`` is not a sequence

-Returns
--------
-subset : DataFrame
-    The subset of the frame including the dtypes in ``include`` and
-    excluding the dtypes in ``exclude``.

-Notes
------
-* To select all *numeric* types use the numpy dtype ``numpy.number``
-* To select strings you must use the ``object`` dtype, but note that
-  this will return *all* object dtype columns
-* See the `numpy dtype hierarchy
-  <http://docs.scipy.org/doc/numpy/reference/arrays.scalars.html>`__
-* To select datetimes, use np.datetime64, 'datetime' or 'datetime64'
-* To select timedeltas, use np.timedelta64, 'timedelta' or
-  'timedelta64'
-* To select Pandas categorical dtypes, use 'category'
-* To select Pandas datetimetz dtypes, use 'datetimetz' (new in 0.20.0),
-  or a 'datetime64[ns, tz]' string

-Examples
---------
->>> df = pd.DataFrame({'a': np.random.randn(6).astype('f4'),
-...                    'b': [True, False] * 3,
-...                    'c': [1.0, 2.0] * 3})
->>> df
-        a      b  c
-0  0.3962   True  1
-1  0.1459  False  2
-2  0.2623   True  1
-3  0.0764  False  2
-4 -0.9703   True  1
-5 -1.2094  False  2
->>> df.select_dtypes(include=['float64'])
-   c
-0  1
-1  2
-2  1
-3  2
-4  1
-5  2
->>> df.select_dtypes(exclude=['floating'])
-       b
-0   True
-1  False
-2   True
-3  False
-4   True
-5  False
- -
sem(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased standard error of the mean over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sem : Series or DataFrame (if level specified)
- -
set_index(self, keys, drop=True, append=False, inplace=False, verify_integrity=False)
Set the DataFrame index (row labels) using one or more existing
-columns. By default yields a new object.

-Parameters
-----------
-keys : column label or list of column labels / arrays
-drop : boolean, default True
-    Delete columns to be used as the new index
-append : boolean, default False
-    Whether to append columns to existing index
-inplace : boolean, default False
-    Modify the DataFrame in place (do not create a new object)
-verify_integrity : boolean, default False
-    Check the new index for duplicates. Otherwise defer the check until
-    necessary. Setting to False will improve the performance of this
-    method

-Examples
---------
->>> indexed_df = df.set_index(['A', 'B'])
->>> indexed_df2 = df.set_index(['A', [0, 1, 2, 0, 1, 2]])
->>> indexed_df3 = df.set_index([[0, 1, 2, 0, 1, 2]])

-Returns
--------
-dataframe : DataFrame
- -
set_value(self, index, col, value, takeable=False)
Put single value at passed column and index

-Parameters
-----------
-index : row label
-col : column label
-value : scalar value
-takeable : interpret the index/col as indexers, default False

-Returns
--------
-frame : DataFrame
-    If label pair is contained, will be reference to calling DataFrame,
-    otherwise a new object
- -
shift(self, periods=1, freq=None, axis=0)
Shift index by desired number of periods with an optional time freq

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, optional
-    Increment to use from the tseries module or time rule (e.g. 'EOM').
-    See Notes.
-axis : {0 or 'index', 1 or 'columns'}

-Notes
------
-If freq is specified then the index values are shifted but the data
-is not realigned. That is, use freq if you would like to extend the
-index when shifting and preserve the original data.

-Returns
--------
-shifted : DataFrame
- -
skew(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased skew over requested axis
-Normalized by N-1

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-skew : Series or DataFrame (if level specified)
- -
sort_index(self, axis=0, level=None, ascending=True, inplace=False, kind='quicksort', na_position='last', sort_remaining=True, by=None)
Sort object by labels (along an axis)

-Parameters
-----------
-axis : index, columns to direct sorting
-level : int or level name or list of ints or list of level names
-    if not None, sort on values in specified index level(s)
-ascending : boolean, default True
-    Sort ascending vs. descending
-inplace : bool, default False
-    if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end.
-     Not implemented for MultiIndex.
-sort_remaining : bool, default True
-    if true and sorting by level and index is multilevel, sort by other
-    levels too (in order) after sorting by specified level

-Returns
--------
-sorted_obj : DataFrame
- -
sort_values(self, by, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last')
Sort by the values along either axis

-.. versionadded:: 0.17.0

-Parameters
-----------
-by : str or list of str
-    Name or list of names which refer to the axis items.
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    Axis to direct sorting
-ascending : bool or list of bool, default True
-     Sort ascending vs. descending. Specify list for multiple sort
-     orders.  If this is a list of bools, must match the length of
-     the by.
-inplace : bool, default False
-     if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end

-Returns
--------
-sorted_obj : DataFrame
- -
sortlevel(self, level=0, axis=0, ascending=True, inplace=False, sort_remaining=True)
DEPRECATED: use :meth:`DataFrame.sort_index`

-Sort multilevel index by chosen axis and primary level. Data will be
-lexicographically sorted by the chosen level followed by the other
-levels (in order)

-Parameters
-----------
-level : int
-axis : {0 or 'index', 1 or 'columns'}, default 0
-ascending : boolean, default True
-inplace : boolean, default False
-    Sort the DataFrame without creating a new instance
-sort_remaining : boolean, default True
-    Sort by the other levels too.

-Returns
--------
-sorted : DataFrame

-See Also
---------
-DataFrame.sort_index(level=...)
- -
stack(self, level=-1, dropna=True)
Pivot a level of the (possibly hierarchical) column labels, returning a
-DataFrame (or Series in the case of an object with a single level of
-column labels) having a hierarchical index with a new inner-most level
-of row labels.
-The level involved will automatically get sorted.

-Parameters
-----------
-level : int, string, or list of these, default last level
-    Level(s) to stack, can pass level name
-dropna : boolean, default True
-    Whether to drop rows in the resulting Frame/Series with no valid
-    values

-Examples
-----------
->>> s
-     a   b
-one  1.  2.
-two  3.  4.

->>> s.stack()
-one a    1
-    b    2
-two a    3
-    b    4

-Returns
--------
-stacked : DataFrame or Series
- -
std(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return sample standard deviation over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-std : Series or DataFrame (if level specified)
- -
sub(self, other, axis='columns', level=None, fill_value=None)
Subtraction of dataframe and other, element-wise (binary operator `sub`).

-Equivalent to ``dataframe - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rsub
- -
subtract = sub(self, other, axis='columns', level=None, fill_value=None)
Subtraction of dataframe and other, element-wise (binary operator `sub`).

-Equivalent to ``dataframe - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rsub
- -
sum(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the sum of the values for the requested axis

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sum : Series or DataFrame (if level specified)
- -
swaplevel(self, i=-2, j=-1, axis=0)
Swap levels i and j in a MultiIndex on a particular axis

-Parameters
-----------
-i, j : int, string (can be mixed)
-    Level of index to be swapped. Can pass level name as string.

-Returns
--------
-swapped : type of caller (new object)

-.. versionchanged:: 0.18.1

-   The indexes ``i`` and ``j`` are now optional, and default to
-   the two innermost levels of the index.
- -
to_csv(self, path_or_buf=None, sep=',', na_rep='', float_format=None, columns=None, header=True, index=True, index_label=None, mode='w', encoding=None, compression=None, quoting=None, quotechar='"', line_terminator='\n', chunksize=None, tupleize_cols=False, date_format=None, doublequote=True, escapechar=None, decimal='.')
Write DataFrame to a comma-separated values (csv) file

-Parameters
-----------
-path_or_buf : string or file handle, default None
-    File path or object, if None is provided the result is returned as
-    a string.
-sep : character, default ','
-    Field delimiter for the output file.
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-columns : sequence, optional
-    Columns to write
-header : boolean or list of string, default True
-    Write out column names. If a list of string is given it is assumed
-    to be aliases for the column names
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, or False, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.  If
-    False do not print fields for index names. Use index_label=False
-    for easier importing in R
-mode : str
-    Python write mode, default 'w'
-encoding : string, optional
-    A string representing the encoding to use in the output file,
-    defaults to 'ascii' on Python 2 and 'utf-8' on Python 3.
-compression : string, optional
-    a string representing the compression to use in the output file,
-    allowed values are 'gzip', 'bz2', 'xz',
-    only used when the first argument is a filename
-line_terminator : string, default ``'\n'``
-    The newline character or character sequence to use in the output
-    file
-quoting : optional constant from csv module
-    defaults to csv.QUOTE_MINIMAL. If you have set a `float_format`
-    then floats are converted to strings and thus csv.QUOTE_NONNUMERIC
-    will treat them as non-numeric
-quotechar : string (length 1), default '\"'
-    character used to quote fields
-doublequote : boolean, default True
-    Control quoting of `quotechar` inside a field
-escapechar : string (length 1), default None
-    character used to escape `sep` and `quotechar` when appropriate
-chunksize : int or None
-    rows to write at a time
-tupleize_cols : boolean, default False
-    write multi_index columns as a list of tuples (if True)
-    or new (expanded format) if False)
-date_format : string, default None
-    Format string for datetime objects
-decimal: string, default '.'
-    Character recognized as decimal separator. E.g. use ',' for
-    European data

-    .. versionadded:: 0.16.0
- -
to_dict(self, orient='dict')
Convert DataFrame to dictionary.

-Parameters
-----------
-orient : str {'dict', 'list', 'series', 'split', 'records', 'index'}
-    Determines the type of the values of the dictionary.

-    - dict (default) : dict like {column -> {index -> value}}
-    - list : dict like {column -> [values]}
-    - series : dict like {column -> Series(values)}
-    - split : dict like
-      {index -> [index], columns -> [columns], data -> [values]}
-    - records : list like
-      [{column -> value}, ... , {column -> value}]
-    - index : dict like {index -> {column -> value}}

-      .. versionadded:: 0.17.0

-    Abbreviations are allowed. `s` indicates `series` and `sp`
-    indicates `split`.

-Returns
--------
-result : dict like {column -> {index -> value}}
- -
to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='', float_format=None, columns=None, header=True, index=True, index_label=None, startrow=0, startcol=0, engine=None, merge_cells=True, encoding=None, inf_rep='inf', verbose=True, freeze_panes=None)
Write DataFrame to an excel sheet


-Parameters
-----------
-excel_writer : string or ExcelWriter object
-    File path or existing ExcelWriter
-sheet_name : string, default 'Sheet1'
-    Name of sheet which will contain DataFrame
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-columns : sequence, optional
-    Columns to write
-header : boolean or list of string, default True
-    Write out column names. If a list of string is given it is
-    assumed to be aliases for the column names
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.
-startrow :
-    upper left cell row to dump data frame
-startcol :
-    upper left cell column to dump data frame
-engine : string, default None
-    write engine to use - you can also set this via the options
-    ``io.excel.xlsx.writer``, ``io.excel.xls.writer``, and
-    ``io.excel.xlsm.writer``.
-merge_cells : boolean, default True
-    Write MultiIndex and Hierarchical Rows as merged cells.
-encoding: string, default None
-    encoding of the resulting excel file. Only necessary for xlwt,
-    other writers support unicode natively.
-inf_rep : string, default 'inf'
-    Representation for infinity (there is no native representation for
-    infinity in Excel)
-freeze_panes : tuple of integer (length 2), default None
-    Specifies the one-based bottommost row and rightmost column that
-    is to be frozen

-    .. versionadded:: 0.20.0

-Notes
------
-If passing an existing ExcelWriter object, then the sheet will be added
-to the existing workbook.  This can be used to save different
-DataFrames to one workbook:

->>> writer = pd.ExcelWriter('output.xlsx')
->>> df1.to_excel(writer,'Sheet1')
->>> df2.to_excel(writer,'Sheet2')
->>> writer.save()

-For compatibility with to_csv, to_excel serializes lists and dicts to
-strings before writing.
- -
to_feather(self, fname)
write out the binary feather-format for DataFrames

-.. versionadded:: 0.20.0

-Parameters
-----------
-fname : str
-    string file path
- -
to_gbq(self, destination_table, project_id, chunksize=10000, verbose=True, reauth=False, if_exists='fail', private_key=None)
Write a DataFrame to a Google BigQuery table.

-The main method a user calls to export pandas DataFrame contents to
-Google BigQuery table.

-Google BigQuery API Client Library v2 for Python is used.
-Documentation is available `here
-<https://developers.google.com/api-client-library/python/apis/bigquery/v2>`__

-Authentication to the Google BigQuery service is via OAuth 2.0.

-- If "private_key" is not provided:

-  By default "application default credentials" are used.

-  If default application credentials are not found or are restrictive,
-  user account credentials are used. In this case, you will be asked to
-  grant permissions for product name 'pandas GBQ'.

-- If "private_key" is provided:

-  Service account credentials will be used to authenticate.

-Parameters
-----------
-dataframe : DataFrame
-    DataFrame to be written
-destination_table : string
-    Name of table to be written, in the form 'dataset.tablename'
-project_id : str
-    Google BigQuery Account project ID.
-chunksize : int (default 10000)
-    Number of rows to be inserted in each chunk from the dataframe.
-verbose : boolean (default True)
-    Show percentage complete
-reauth : boolean (default False)
-    Force Google BigQuery to reauthenticate the user. This is useful
-    if multiple accounts are used.
-if_exists : {'fail', 'replace', 'append'}, default 'fail'
-    'fail': If table exists, do nothing.
-    'replace': If table exists, drop it, recreate it, and insert data.
-    'append': If table exists, insert data. Create if does not exist.
-private_key : str (optional)
-    Service account private key in JSON format. Can be file path
-    or string contents. This is useful for remote server
-    authentication (eg. jupyter iPython notebook on remote host)
- -
to_html(self, buf=None, columns=None, col_space=None, header=True, index=True, na_rep='NaN', formatters=None, float_format=None, sparsify=None, index_names=True, justify=None, bold_rows=True, classes=None, escape=True, max_rows=None, max_cols=None, show_dimensions=False, notebook=False, decimal='.', border=None)
Render a DataFrame as an HTML table.

-`to_html`-specific options:

-bold_rows : boolean, default True
-    Make the row labels bold in the output
-classes : str or list or tuple, default None
-    CSS class(es) to apply to the resulting html table
-escape : boolean, default True
-    Convert the characters <, >, and & to HTML-safe sequences.=
-max_rows : int, optional
-    Maximum number of rows to show before truncating. If None, show
-    all.
-max_cols : int, optional
-    Maximum number of columns to show before truncating. If None, show
-    all.
-decimal : string, default '.'
-    Character recognized as decimal separator, e.g. ',' in Europe

-    .. versionadded:: 0.18.0
-border : int
-    A ``border=border`` attribute is included in the opening
-    `<table>` tag. Default ``pd.options.html.border``.

-    .. versionadded:: 0.19.0

-Parameters
-----------
-buf : StringIO-like, optional
-    buffer to write to
-columns : sequence, optional
-    the subset of columns to write; default None writes all columns
-col_space : int, optional
-    the minimum width of each column
-header : bool, optional
-    whether to print column labels, default True
-index : bool, optional
-    whether to print index (row) labels, default True
-na_rep : string, optional
-    string representation of NAN to use, default 'NaN'
-formatters : list or dict of one-parameter functions, optional
-    formatter functions to apply to columns' elements by position or name,
-    default None. The result of each function must be a unicode string.
-    List must be of length equal to the number of columns.
-float_format : one-parameter function, optional
-    formatter function to apply to columns' elements if they are floats,
-    default None. The result of this function must be a unicode string.
-sparsify : bool, optional
-    Set to False for a DataFrame with a hierarchical index to print every
-    multiindex key at each row, default True
-index_names : bool, optional
-    Prints the names of the indexes, default True
-line_width : int, optional
-    Width to wrap a line in characters, default no wrap
-justify : {'left', 'right'}, default None
-    Left or right-justify the column labels. If None uses the option from
-    the print configuration (controlled by set_option), 'right' out
-    of the box.

-Returns
--------
-formatted : string (or unicode, depending on data and options)
- -
to_latex(self, buf=None, columns=None, col_space=None, header=True, index=True, na_rep='NaN', formatters=None, float_format=None, sparsify=None, index_names=True, bold_rows=True, column_format=None, longtable=None, escape=None, encoding=None, decimal='.', multicolumn=None, multicolumn_format=None, multirow=None)
Render a DataFrame to a tabular environment table. You can splice
-this into a LaTeX document. Requires \usepackage{booktabs}.

-`to_latex`-specific options:

-bold_rows : boolean, default True
-    Make the row labels bold in the output
-column_format : str, default None
-    The columns format as specified in `LaTeX table format
-    <https://en.wikibooks.org/wiki/LaTeX/Tables>`__ e.g 'rcl' for 3
-    columns
-longtable : boolean, default will be read from the pandas config module
-    Default: False.
-    Use a longtable environment instead of tabular. Requires adding
-    a \usepackage{longtable} to your LaTeX preamble.
-escape : boolean, default will be read from the pandas config module
-    Default: True.
-    When set to False prevents from escaping latex special
-    characters in column names.
-encoding : str, default None
-    A string representing the encoding to use in the output file,
-    defaults to 'ascii' on Python 2 and 'utf-8' on Python 3.
-decimal : string, default '.'
-    Character recognized as decimal separator, e.g. ',' in Europe.

-    .. versionadded:: 0.18.0

-multicolumn : boolean, default True
-    Use \multicolumn to enhance MultiIndex columns.
-    The default will be read from the config module.

-    .. versionadded:: 0.20.0

-multicolumn_format : str, default 'l'
-    The alignment for multicolumns, similar to `column_format`
-    The default will be read from the config module.

-    .. versionadded:: 0.20.0

-multirow : boolean, default False
-    Use \multirow to enhance MultiIndex rows.
-    Requires adding a \usepackage{multirow} to your LaTeX preamble.
-    Will print centered labels (instead of top-aligned)
-    across the contained rows, separating groups via clines.
-    The default will be read from the pandas config module.

-    .. versionadded:: 0.20.0


-Parameters
-----------
-buf : StringIO-like, optional
-    buffer to write to
-columns : sequence, optional
-    the subset of columns to write; default None writes all columns
-col_space : int, optional
-    the minimum width of each column
-header : bool, optional
-    Write out column names. If a list of string is given, it is assumed to be aliases for the column names.
-index : bool, optional
-    whether to print index (row) labels, default True
-na_rep : string, optional
-    string representation of NAN to use, default 'NaN'
-formatters : list or dict of one-parameter functions, optional
-    formatter functions to apply to columns' elements by position or name,
-    default None. The result of each function must be a unicode string.
-    List must be of length equal to the number of columns.
-float_format : one-parameter function, optional
-    formatter function to apply to columns' elements if they are floats,
-    default None. The result of this function must be a unicode string.
-sparsify : bool, optional
-    Set to False for a DataFrame with a hierarchical index to print every
-    multiindex key at each row, default True
-index_names : bool, optional
-    Prints the names of the indexes, default True
-line_width : int, optional
-    Width to wrap a line in characters, default no wrap

-Returns
--------
-formatted : string (or unicode, depending on data and options)
- -
to_panel(self)
Transform long (stacked) format (DataFrame) into wide (3D, Panel)
-format.

-Currently the index of the DataFrame must be a 2-level MultiIndex. This
-may be generalized later

-Returns
--------
-panel : Panel
- -
to_period(self, freq=None, axis=0, copy=True)
Convert DataFrame from DatetimeIndex to PeriodIndex with desired
-frequency (inferred from index if not passed)

-Parameters
-----------
-freq : string, default
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    The axis to convert (the index by default)
-copy : boolean, default True
-    If False then underlying input data is not copied

-Returns
--------
-ts : TimeSeries with PeriodIndex
- -
to_records(self, index=True, convert_datetime64=True)
Convert DataFrame to record array. Index will be put in the
-'index' field of the record array if requested

-Parameters
-----------
-index : boolean, default True
-    Include index in resulting record array, stored in 'index' field
-convert_datetime64 : boolean, default True
-    Whether to convert the index to datetime.datetime if it is a
-    DatetimeIndex

-Returns
--------
-y : recarray
- -
to_sparse(self, fill_value=None, kind='block')
Convert to SparseDataFrame

-Parameters
-----------
-fill_value : float, default NaN
-kind : {'block', 'integer'}

-Returns
--------
-y : SparseDataFrame
- -
to_stata(self, fname, convert_dates=None, write_index=True, encoding='latin-1', byteorder=None, time_stamp=None, data_label=None, variable_labels=None)
A class for writing Stata binary dta files from array-like objects

-Parameters
-----------
-fname : str or buffer
-    String path of file-like object
-convert_dates : dict
-    Dictionary mapping columns containing datetime types to stata
-    internal format to use when wirting the dates. Options are 'tc',
-    'td', 'tm', 'tw', 'th', 'tq', 'ty'. Column can be either an integer
-    or a name. Datetime columns that do not have a conversion type
-    specified will be converted to 'tc'. Raises NotImplementedError if
-    a datetime column has timezone information
-write_index : bool
-    Write the index to Stata dataset.
-encoding : str
-    Default is latin-1. Unicode is not supported
-byteorder : str
-    Can be ">", "<", "little", or "big". default is `sys.byteorder`
-time_stamp : datetime
-    A datetime to use as file creation date.  Default is the current
-    time.
-dataset_label : str
-    A label for the data set.  Must be 80 characters or smaller.
-variable_labels : dict
-    Dictionary containing columns as keys and variable labels as
-    values. Each label must be 80 characters or smaller.

-    .. versionadded:: 0.19.0

-Raises
-------
-NotImplementedError
-    * If datetimes contain timezone information
-    * Column dtype is not representable in Stata
-ValueError
-    * Columns listed in convert_dates are noth either datetime64[ns]
-      or datetime.datetime
-    * Column listed in convert_dates is not in DataFrame
-    * Categorical label contains more than 32,000 characters

-    .. versionadded:: 0.19.0

-Examples
---------
->>> writer = StataWriter('./data_file.dta', data)
->>> writer.write_file()

-Or with dates

->>> writer = StataWriter('./date_data_file.dta', data, {2 : 'tw'})
->>> writer.write_file()
- -
to_string(self, buf=None, columns=None, col_space=None, header=True, index=True, na_rep='NaN', formatters=None, float_format=None, sparsify=None, index_names=True, justify=None, line_width=None, max_rows=None, max_cols=None, show_dimensions=False)
Render a DataFrame to a console-friendly tabular output.

-Parameters
-----------
-buf : StringIO-like, optional
-    buffer to write to
-columns : sequence, optional
-    the subset of columns to write; default None writes all columns
-col_space : int, optional
-    the minimum width of each column
-header : bool, optional
-    Write out column names. If a list of string is given, it is assumed to be aliases for the column names
-index : bool, optional
-    whether to print index (row) labels, default True
-na_rep : string, optional
-    string representation of NAN to use, default 'NaN'
-formatters : list or dict of one-parameter functions, optional
-    formatter functions to apply to columns' elements by position or name,
-    default None. The result of each function must be a unicode string.
-    List must be of length equal to the number of columns.
-float_format : one-parameter function, optional
-    formatter function to apply to columns' elements if they are floats,
-    default None. The result of this function must be a unicode string.
-sparsify : bool, optional
-    Set to False for a DataFrame with a hierarchical index to print every
-    multiindex key at each row, default True
-index_names : bool, optional
-    Prints the names of the indexes, default True
-line_width : int, optional
-    Width to wrap a line in characters, default no wrap
-justify : {'left', 'right'}, default None
-    Left or right-justify the column labels. If None uses the option from
-    the print configuration (controlled by set_option), 'right' out
-    of the box.

-Returns
--------
-formatted : string (or unicode, depending on data and options)
- -
to_timestamp(self, freq=None, how='start', axis=0, copy=True)
Cast to DatetimeIndex of timestamps, at *beginning* of period

-Parameters
-----------
-freq : string, default frequency of PeriodIndex
-    Desired frequency
-how : {'s', 'e', 'start', 'end'}
-    Convention for converting period to timestamp; start of period
-    vs. end
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    The axis to convert (the index by default)
-copy : boolean, default True
-    If false then underlying input data is not copied

-Returns
--------
-df : DataFrame with DatetimeIndex
- -
transform(self, func, *args, **kwargs)
Call function producing a like-indexed NDFrame
-and return a NDFrame with the transformed values`

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    To apply to column

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Returns
--------
-transformed : NDFrame

-Examples
---------
->>> df = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
-...                   index=pd.date_range('1/1/2000', periods=10))
-df.iloc[3:7] = np.nan

->>> df.transform(lambda x: (x - x.mean()) / x.std())
-                   A         B         C
-2000-01-01  0.579457  1.236184  0.123424
-2000-01-02  0.370357 -0.605875 -1.231325
-2000-01-03  1.455756 -0.277446  0.288967
-2000-01-04       NaN       NaN       NaN
-2000-01-05       NaN       NaN       NaN
-2000-01-06       NaN       NaN       NaN
-2000-01-07       NaN       NaN       NaN
-2000-01-08 -0.498658  1.274522  1.642524
-2000-01-09 -0.540524 -1.012676 -0.828968
-2000-01-10 -1.366388 -0.614710  0.005378

-See also
---------
-pandas.NDFrame.aggregate
-pandas.NDFrame.apply
- -
transpose(self, *args, **kwargs)
Transpose index and columns
- -
truediv(self, other, axis='columns', level=None, fill_value=None)
Floating division of dataframe and other, element-wise (binary operator `truediv`).

-Equivalent to ``dataframe / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : SeriesDataFrame, or constant
-axis : {0, 1, 'index', 'columns'}
-    For Series input, axis to match Series index on
-fill_value : None or float value, default None
-    Fill missing (NaN) values with this value. If both DataFrame
-    locations are missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Notes
------
-Mismatched indices will be unioned together

-Returns
--------
-result : DataFrame

-See also
---------
-DataFrame.rtruediv
- -
unstack(self, level=-1, fill_value=None)
Pivot a level of the (necessarily hierarchical) index labels, returning
-a DataFrame having a new level of column labels whose inner-most level
-consists of the pivoted index labels. If the index is not a MultiIndex,
-the output will be a Series (the analogue of stack when the columns are
-not a MultiIndex).
-The level involved will automatically get sorted.

-Parameters
-----------
-level : int, string, or list of these, default -1 (last level)
-    Level(s) of index to unstack, can pass level name
-fill_value : replace NaN with this value if the unstack produces
-    missing values

-    .. versionadded: 0.18.0

-See also
---------
-DataFrame.pivot : Pivot a table based on column values.
-DataFrame.stack : Pivot a level of the column labels (inverse operation
-    from `unstack`).

-Examples
---------
->>> index = pd.MultiIndex.from_tuples([('one', 'a'), ('one', 'b'),
-...                                    ('two', 'a'), ('two', 'b')])
->>> s = pd.Series(np.arange(1.0, 5.0), index=index)
->>> s
-one  a   1.0
-     b   2.0
-two  a   3.0
-     b   4.0
-dtype: float64

->>> s.unstack(level=-1)
-     a   b
-one  1.0  2.0
-two  3.0  4.0

->>> s.unstack(level=0)
-   one  two
-a  1.0   3.0
-b  2.0   4.0

->>> df = s.unstack(level=0)
->>> df.unstack()
-one  a  1.0
-     b  2.0
-two  a  3.0
-     b  4.0
-dtype: float64

-Returns
--------
-unstacked : DataFrame or Series
- -
update(self, other, join='left', overwrite=True, filter_func=None, raise_conflict=False)
Modify DataFrame in place using non-NA values from passed
-DataFrame. Aligns on indices

-Parameters
-----------
-other : DataFrame, or object coercible into a DataFrame
-join : {'left'}, default 'left'
-overwrite : boolean, default True
-    If True then overwrite values for common keys in the calling frame
-filter_func : callable(1d-array) -> 1d-array<boolean>, default None
-    Can choose to replace values other than NA. Return True for values
-    that should be updated
-raise_conflict : boolean
-    If True, will raise an error if the DataFrame and other both
-    contain data in the same place.
- -
var(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased variance over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0), columns (1)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a Series
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-var : Series or DataFrame (if level specified)
- -
-Class methods inherited from pandas.core.frame.DataFrame:
-
from_csv(path, header=0, sep=',', index_col=0, parse_dates=True, encoding=None, tupleize_cols=False, infer_datetime_format=False) from builtins.type
Read CSV file (DISCOURAGED, please use :func:`pandas.read_csv`
-instead).

-It is preferable to use the more powerful :func:`pandas.read_csv`
-for most general purposes, but ``from_csv`` makes for an easy
-roundtrip to and from a file (the exact counterpart of
-``to_csv``), especially with a DataFrame of time series data.

-This method only differs from the preferred :func:`pandas.read_csv`
-in some defaults:

-- `index_col` is ``0`` instead of ``None`` (take first column as index
-  by default)
-- `parse_dates` is ``True`` instead of ``False`` (try parsing the index
-  as datetime by default)

-So a ``pd.DataFrame.from_csv(path)`` can be replaced by
-``pd.read_csv(path, index_col=0, parse_dates=True)``.

-Parameters
-----------
-path : string file path or file handle / StringIO
-header : int, default 0
-    Row to use as header (skip prior rows)
-sep : string, default ','
-    Field delimiter
-index_col : int or sequence, default 0
-    Column to use for index. If a sequence is given, a MultiIndex
-    is used. Different default from read_table
-parse_dates : boolean, default True
-    Parse dates. Different default from read_table
-tupleize_cols : boolean, default False
-    write multi_index columns as a list of tuples (if True)
-    or new (expanded format) if False)
-infer_datetime_format: boolean, default False
-    If True and `parse_dates` is True for a column, try to infer the
-    datetime format based on the first datetime string. If the format
-    can be inferred, there often will be a large parsing speed-up.

-See also
---------
-pandas.read_csv

-Returns
--------
-y : DataFrame
- -
from_dict(data, orient='columns', dtype=None) from builtins.type
Construct DataFrame from dict of array-like or dicts

-Parameters
-----------
-data : dict
-    {field : array-like} or {field : dict}
-orient : {'columns', 'index'}, default 'columns'
-    The "orientation" of the data. If the keys of the passed dict
-    should be the columns of the resulting DataFrame, pass 'columns'
-    (default). Otherwise if the keys should be rows, pass 'index'.
-dtype : dtype, default None
-    Data type to force, otherwise infer

-Returns
--------
-DataFrame
- -
from_items(items, columns=None, orient='columns') from builtins.type
Convert (key, value) pairs to DataFrame. The keys will be the axis
-index (usually the columns, but depends on the specified
-orientation). The values should be arrays or Series.

-Parameters
-----------
-items : sequence of (key, value) pairs
-    Values should be arrays or Series.
-columns : sequence of column labels, optional
-    Must be passed if orient='index'.
-orient : {'columns', 'index'}, default 'columns'
-    The "orientation" of the data. If the keys of the
-    input correspond to column labels, pass 'columns'
-    (default). Otherwise if the keys correspond to the index,
-    pass 'index'.

-Returns
--------
-frame : DataFrame
- -
from_records(data, index=None, exclude=None, columns=None, coerce_float=False, nrows=None) from builtins.type
Convert structured or record ndarray to DataFrame

-Parameters
-----------
-data : ndarray (structured dtype), list of tuples, dict, or DataFrame
-index : string, list of fields, array-like
-    Field of array to use as the index, alternately a specific set of
-    input labels to use
-exclude : sequence, default None
-    Columns or fields to exclude
-columns : sequence, default None
-    Column names to use. If the passed data do not have names
-    associated with them, this argument provides names for the
-    columns. Otherwise this argument indicates the order of the columns
-    in the result (any names not found in the data will become all-NA
-    columns)
-coerce_float : boolean, default False
-    Attempt to convert values of non-string, non-numeric objects (like
-    decimal.Decimal) to floating point, useful for SQL result sets

-Returns
--------
-df : DataFrame
- -
-Data descriptors inherited from pandas.core.frame.DataFrame:
-
axes
-
Return a list with the row axis labels and column axis labels as the
-only members. They are returned in that order.
-
-
columns
-
-
index
-
-
shape
-
Return a tuple representing the dimensionality of the DataFrame.
-
-
style
-
Property returning a Styler object containing methods for
-building a styled HTML representation fo the DataFrame.

-See Also
---------
-pandas.io.formats.style.Styler
-
-
-Data and other attributes inherited from pandas.core.frame.DataFrame:
-
plot = <class 'pandas.plotting._core.FramePlotMethods'>
DataFrame plotting accessor and method

-Examples
---------
->>> df.plot.line()
->>> df.plot.scatter('x', 'y')
->>> df.plot.hexbin()

-These plotting methods can also be accessed by calling the accessor as a
-method with the ``kind`` argument:
-``df.plot(kind='line')`` is equivalent to ``df.plot.line()``
- -
-Methods inherited from pandas.core.generic.NDFrame:
-
__abs__(self)
- -
__array__(self, dtype=None)
- -
__array_wrap__(self, result, context=None)
- -
__bool__ = __nonzero__(self)
- -
__contains__(self, key)
True if the key is in the info axis
- -
__copy__(self, deep=True)
- -
__deepcopy__(self, memo=None)
- -
__delitem__(self, key)
Delete item
- -
__finalize__(self, other, method=None, **kwargs)
Propagate metadata from other to self.

-Parameters
-----------
-other : the object from which to get the attributes that we are going
-    to propagate
-method : optional, a passed method name ; possibly to take different
-    types of propagation actions based on this
- -
__getattr__(self, name)
After regular attribute access, try looking up the name
-This allows simpler access to columns for interactive use.
- -
__getstate__(self)
- -
__hash__(self)
Return hash(self).
- -
__invert__(self)
- -
__iter__(self)
Iterate over infor axis
- -
__neg__(self)
- -
__nonzero__(self)
- -
__round__(self, decimals=0)
- -
__setattr__(self, name, value)
After regular attribute access, try setting the name
-This allows simpler access to columns for interactive use.
- -
__setstate__(self, state)
- -
abs(self)
Return an object with absolute value taken--only applicable to objects
-that are all numeric.

-Returns
--------
-abs: type of caller
- -
add_prefix(self, prefix)
Concatenate prefix string with panel items names.

-Parameters
-----------
-prefix : string

-Returns
--------
-with_prefix : type of caller
- -
add_suffix(self, suffix)
Concatenate suffix string with panel items names.

-Parameters
-----------
-suffix : string

-Returns
--------
-with_suffix : type of caller
- -
as_blocks(self, copy=True)
Convert the frame to a dict of dtype -> Constructor Types that each has
-a homogeneous dtype.

-NOTE: the dtypes of the blocks WILL BE PRESERVED HERE (unlike in
-      as_matrix)

-Parameters
-----------
-copy : boolean, default True

-       .. versionadded: 0.16.1

-Returns
--------
-values : a dict of dtype -> Constructor Types
- -
as_matrix(self, columns=None)
Convert the frame to its Numpy-array representation.

-Parameters
-----------
-columns: list, optional, default:None
-    If None, return all columns, otherwise, returns specified columns.

-Returns
--------
-values : ndarray
-    If the caller is heterogeneous and contains booleans or objects,
-    the result will be of dtype=object. See Notes.


-Notes
------
-Return is NOT a Numpy-matrix, rather, a Numpy-array.

-The dtype will be a lower-common-denominator dtype (implicit
-upcasting); that is to say if the dtypes (even of numeric types)
-are mixed, the one that accommodates all will be chosen. Use this
-with care if you are not dealing with the blocks.

-e.g. If the dtypes are float16 and float32, dtype will be upcast to
-float32.  If dtypes are int32 and uint8, dtype will be upcase to
-int32. By numpy.find_common_type convention, mixing int64 and uint64
-will result in a flot64 dtype.

-This method is provided for backwards compatibility. Generally,
-it is recommended to use '.values'.

-See Also
---------
-pandas.DataFrame.values
- -
asfreq(self, freq, method=None, how=None, normalize=False, fill_value=None)
Convert TimeSeries to specified frequency.

-Optionally provide filling method to pad/backfill missing values.

-Returns the original data conformed to a new index with the specified
-frequency. ``resample`` is more appropriate if an operation, such as
-summarization, is necessary to represent the data at the new frequency.

-Parameters
-----------
-freq : DateOffset object, or string
-method : {'backfill'/'bfill', 'pad'/'ffill'}, default None
-    Method to use for filling holes in reindexed Series (note this
-    does not fill NaNs that already were present):

-    * 'pad' / 'ffill': propagate last valid observation forward to next
-      valid
-    * 'backfill' / 'bfill': use NEXT valid observation to fill
-how : {'start', 'end'}, default end
-    For PeriodIndex only, see PeriodIndex.asfreq
-normalize : bool, default False
-    Whether to reset output index to midnight
-fill_value: scalar, optional
-    Value to use for missing values, applied during upsampling (note
-    this does not fill NaNs that already were present).

-    .. versionadded:: 0.20.0

-Returns
--------
-converted : type of caller

-Examples
---------

-Start by creating a series with 4 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=4, freq='T')
->>> series = pd.Series([0.0, None, 2.0, 3.0], index=index)
->>> df = pd.DataFrame({'s':series})
->>> df
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:03:00    3.0

-Upsample the series into 30 second bins.

->>> df.asfreq(freq='30S')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    NaN
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``fill value``.

->>> df.asfreq(freq='30S', fill_value=9.0)
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    9.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    9.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    9.0
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``method``.

->>> df.asfreq(freq='30S', method='bfill')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    2.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    3.0
-2000-01-01 00:03:00    3.0

-See Also
---------
-reindex

-Notes
------
-To learn more about the frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
- -
asof(self, where, subset=None)
The last row without any NaN is taken (or the last row without
-NaN considering only the subset of columns in the case of a DataFrame)

-.. versionadded:: 0.19.0 For DataFrame

-If there is no good value, NaN is returned for a Series
-a Series of NaN values for a DataFrame

-Parameters
-----------
-where : date or array of dates
-subset : string or list of strings, default None
-   if not None use these columns for NaN propagation

-Notes
------
-Dates are assumed to be sorted
-Raises if this is not the case

-Returns
--------
-where is scalar

-  - value or NaN if input is Series
-  - Series if input is DataFrame

-where is Index: same shape object as input

-See Also
---------
-merge_asof
- -
astype(self, dtype, copy=True, errors='raise', **kwargs)
Cast object to input numpy.dtype
-Return a copy when copy = True (be really careful with this!)

-Parameters
-----------
-dtype : data type, or dict of column name -> data type
-    Use a numpy.dtype or Python type to cast entire pandas object to
-    the same type. Alternatively, use {col: dtype, ...}, where col is a
-    column label and dtype is a numpy.dtype or Python type to cast one
-    or more of the DataFrame's columns to column-specific types.
-errors : {'raise', 'ignore'}, default 'raise'.
-    Control raising of exceptions on invalid data for provided dtype.

-    - ``raise`` : allow exceptions to be raised
-    - ``ignore`` : suppress exceptions. On error return original object

-    .. versionadded:: 0.20.0

-raise_on_error : DEPRECATED use ``errors`` instead
-kwargs : keyword arguments to pass on to the constructor

-Returns
--------
-casted : type of caller
- -
at_time(self, time, asof=False)
Select values at particular time of day (e.g. 9:30AM).

-Parameters
-----------
-time : datetime.time or string

-Returns
--------
-values_at_time : type of caller
- -
between_time(self, start_time, end_time, include_start=True, include_end=True)
Select values between particular times of the day (e.g., 9:00-9:30 AM).

-Parameters
-----------
-start_time : datetime.time or string
-end_time : datetime.time or string
-include_start : boolean, default True
-include_end : boolean, default True

-Returns
--------
-values_between_time : type of caller
- -
bfill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='bfill') <DataFrame.fillna>`
- -
bool(self)
Return the bool of a single element PandasObject.

-This must be a boolean scalar value, either True or False.  Raise a
-ValueError if the PandasObject does not have exactly 1 element, or that
-element is not boolean
- -
clip(self, lower=None, upper=None, axis=None, *args, **kwargs)
Trim values at input threshold(s).

-Parameters
-----------
-lower : float or array_like, default None
-upper : float or array_like, default None
-axis : int or string axis name, optional
-    Align object with lower and upper along the given axis.

-Returns
--------
-clipped : Series

-Examples
---------
->>> df
-  0         1
-0  0.335232 -1.256177
-1 -1.367855  0.746646
-2  0.027753 -1.176076
-3  0.230930 -0.679613
-4  1.261967  0.570967
->>> df.clip(-1.0, 0.5)
-          0         1
-0  0.335232 -1.000000
-1 -1.000000  0.500000
-2  0.027753 -1.000000
-3  0.230930 -0.679613
-4  0.500000  0.500000
->>> t
-0   -0.3
-1   -0.2
-2   -0.1
-3    0.0
-4    0.1
-dtype: float64
->>> df.clip(t, t + 1, axis=0)
-          0         1
-0  0.335232 -0.300000
-1 -0.200000  0.746646
-2  0.027753 -0.100000
-3  0.230930  0.000000
-4  1.100000  0.570967
- -
clip_lower(self, threshold, axis=None)
Return copy of the input with values below given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
clip_upper(self, threshold, axis=None)
Return copy of input with values above given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
consolidate(self, inplace=False)
DEPRECATED: consolidate will be an internal implementation only.
- -
convert_objects(self, convert_dates=True, convert_numeric=False, convert_timedeltas=True, copy=True)
Deprecated.

-Attempt to infer better dtype for object columns

-Parameters
-----------
-convert_dates : boolean, default True
-    If True, convert to date where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-convert_numeric : boolean, default False
-    If True, attempt to coerce to numbers (including strings), with
-    unconvertible values becoming NaN.
-convert_timedeltas : boolean, default True
-    If True, convert to timedelta where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-copy : boolean, default True
-    If True, return a copy even if no copy is necessary (e.g. no
-    conversion was done). Note: This is meant for internal use, and
-    should not be confused with inplace.

-See Also
---------
-pandas.to_datetime : Convert argument to datetime.
-pandas.to_timedelta : Convert argument to timedelta.
-pandas.to_numeric : Return a fixed frequency timedelta index,
-    with day as the default.

-Returns
--------
-converted : same as input object
- -
copy(self, deep=True)
Make a copy of this objects data.

-Parameters
-----------
-deep : boolean or string, default True
-    Make a deep copy, including a copy of the data and the indices.
-    With ``deep=False`` neither the indices or the data are copied.

-    Note that when ``deep=True`` data is copied, actual python objects
-    will not be copied recursively, only the reference to the object.
-    This is in contrast to ``copy.deepcopy`` in the Standard Library,
-    which recursively copies object data.

-Returns
--------
-copy : type of caller
- -
describe(self, percentiles=None, include=None, exclude=None)
Generates descriptive statistics that summarize the central tendency,
-dispersion and shape of a dataset's distribution, excluding
-``NaN`` values.

-Analyzes both numeric and object series, as well
-as ``DataFrame`` column sets of mixed data types. The output
-will vary depending on what is provided. Refer to the notes
-below for more detail.

-Parameters
-----------
-percentiles : list-like of numbers, optional
-    The percentiles to include in the output. All should
-    fall between 0 and 1. The default is
-    ``[.25, .5, .75]``, which returns the 25th, 50th, and
-    75th percentiles.
-include : 'all', list-like of dtypes or None (default), optional
-    A white list of data types to include in the result. Ignored
-    for ``Series``. Here are the options:

-    - 'all' : All columns of the input will be included in the output.
-    - A list-like of dtypes : Limits the results to the
-      provided data types.
-      To limit the result to numeric types submit
-      ``numpy.number``. To limit it instead to categorical
-      objects submit the ``numpy.object`` data type. Strings
-      can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will include all numeric columns.
-exclude : list-like of dtypes or None (default), optional,
-    A black list of data types to omit from the result. Ignored
-    for ``Series``. Here are the options:

-    - A list-like of dtypes : Excludes the provided data types
-      from the result. To select numeric types submit
-      ``numpy.number``. To select categorical objects submit the data
-      type ``numpy.object``. Strings can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will exclude nothing.

-Returns
--------
-summary:  Series/DataFrame of summary statistics

-Notes
------
-For numeric data, the result's index will include ``count``,
-``mean``, ``std``, ``min``, ``max`` as well as lower, ``50`` and
-upper percentiles. By default the lower percentile is ``25`` and the
-upper percentile is ``75``. The ``50`` percentile is the
-same as the median.

-For object data (e.g. strings or timestamps), the result's index
-will include ``count``, ``unique``, ``top``, and ``freq``. The ``top``
-is the most common value. The ``freq`` is the most common value's
-frequency. Timestamps also include the ``first`` and ``last`` items.

-If multiple object values have the highest count, then the
-``count`` and ``top`` results will be arbitrarily chosen from
-among those with the highest count.

-For mixed data types provided via a ``DataFrame``, the default is to
-return only an analysis of numeric columns. If ``include='all'``
-is provided as an option, the result will include a union of
-attributes of each type.

-The `include` and `exclude` parameters can be used to limit
-which columns in a ``DataFrame`` are analyzed for the output.
-The parameters are ignored when analyzing a ``Series``.

-Examples
---------
-Describing a numeric ``Series``.

->>> s = pd.Series([1, 2, 3])
->>> s.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0

-Describing a categorical ``Series``.

->>> s = pd.Series(['a', 'a', 'b', 'c'])
->>> s.describe()
-count     4
-unique    3
-top       a
-freq      2
-dtype: object

-Describing a timestamp ``Series``.

->>> s = pd.Series([
-...   np.datetime64("2000-01-01"),
-...   np.datetime64("2010-01-01"),
-...   np.datetime64("2010-01-01")
-... ])
->>> s.describe()
-count                       3
-unique                      2
-top       2010-01-01 00:00:00
-freq                        2
-first     2000-01-01 00:00:00
-last      2010-01-01 00:00:00
-dtype: object

-Describing a ``DataFrame``. By default only numeric fields
-are returned.

->>> df = pd.DataFrame([[1, 'a'], [2, 'b'], [3, 'c']],
-...                   columns=['numeric', 'object'])
->>> df.describe()
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Describing all columns of a ``DataFrame`` regardless of data type.

->>> df.describe(include='all')
-        numeric object
-count       3.0      3
-unique      NaN      3
-top         NaN      b
-freq        NaN      1
-mean        2.0    NaN
-std         1.0    NaN
-min         1.0    NaN
-25%         1.5    NaN
-50%         2.0    NaN
-75%         2.5    NaN
-max         3.0    NaN

-Describing a column from a ``DataFrame`` by accessing it as
-an attribute.

->>> df.numeric.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0
-Name: numeric, dtype: float64

-Including only numeric columns in a ``DataFrame`` description.

->>> df.describe(include=[np.number])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Including only string columns in a ``DataFrame`` description.

->>> df.describe(include=[np.object])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding numeric columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.number])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding object columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.object])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-See Also
---------
-DataFrame.count
-DataFrame.max
-DataFrame.min
-DataFrame.mean
-DataFrame.std
-DataFrame.select_dtypes
- -
drop(self, labels, axis=0, level=None, inplace=False, errors='raise')
Return new object with labels in requested axis removed.

-Parameters
-----------
-labels : single label or list-like
-axis : int or axis name
-level : int or level name, default None
-    For MultiIndex
-inplace : bool, default False
-    If True, do operation inplace and return None.
-errors : {'ignore', 'raise'}, default 'raise'
-    If 'ignore', suppress error and existing labels are dropped.

-    .. versionadded:: 0.16.1

-Returns
--------
-dropped : type of caller
- -
equals(self, other)
Determines if two NDFrame objects contain the same elements. NaNs in
-the same location are considered equal.
- -
ffill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='ffill') <DataFrame.fillna>`
- -
filter(self, items=None, like=None, regex=None, axis=None)
Subset rows or columns of dataframe according to labels in
-the specified index.

-Note that this routine does not filter a dataframe on its
-contents. The filter is applied to the labels of the index.

-Parameters
-----------
-items : list-like
-    List of info axis to restrict to (must not all be present)
-like : string
-    Keep info axis where "arg in col == True"
-regex : string (regular expression)
-    Keep info axis with re.search(regex, col) == True
-axis : int or string axis name
-    The axis to filter on.  By default this is the info axis,
-    'index' for Series, 'columns' for DataFrame

-Returns
--------
-same type as input object

-Examples
---------
->>> df
-one  two  three
-mouse     1    2      3
-rabbit    4    5      6

->>> # select columns by name
->>> df.filter(items=['one', 'three'])
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select columns by regular expression
->>> df.filter(regex='e$', axis=1)
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select rows containing 'bbi'
->>> df.filter(like='bbi', axis=0)
-one  two  three
-rabbit    4    5      6

-See Also
---------
-pandas.DataFrame.select

-Notes
------
-The ``items``, ``like``, and ``regex`` parameters are
-enforced to be mutually exclusive.

-``axis`` defaults to the info axis that is used when indexing
-with ``[]``.
- -
first(self, offset)
Convenience method for subsetting initial periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.first('10D') -> First 10 days

-Returns
--------
-subset : type of caller
- -
get(self, key, default=None)
Get item from object for given key (DataFrame column, Panel slice,
-etc.). Returns default value if not found.

-Parameters
-----------
-key : object

-Returns
--------
-value : type of items contained in object
- -
get_dtype_counts(self)
Return the counts of dtypes in this object.
- -
get_ftype_counts(self)
Return the counts of ftypes in this object.
- -
get_values(self)
same as values (but handles sparseness conversions)
- -
groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, **kwargs)
Group series using mapper (dict or key function, apply given function
-to group, return result as series) or by a series of columns.

-Parameters
-----------
-by : mapping, function, str, or iterable
-    Used to determine the groups for the groupby.
-    If ``by`` is a function, it's called on each value of the object's
-    index. If a dict or Series is passed, the Series or dict VALUES
-    will be used to determine the groups (the Series' values are first
-    aligned; see ``.align()`` method). If an ndarray is passed, the
-    values are used as-is determine the groups. A str or list of strs
-    may be passed to group by the columns in ``self``
-axis : int, default 0
-level : int, level name, or sequence of such, default None
-    If the axis is a MultiIndex (hierarchical), group by a particular
-    level or levels
-as_index : boolean, default True
-    For aggregated output, return object with group labels as the
-    index. Only relevant for DataFrame input. as_index=False is
-    effectively "SQL-style" grouped output
-sort : boolean, default True
-    Sort group keys. Get better performance by turning this off.
-    Note this does not influence the order of observations within each
-    group.  groupby preserves the order of rows within each group.
-group_keys : boolean, default True
-    When calling apply, add group keys to index to identify pieces
-squeeze : boolean, default False
-    reduce the dimensionality of the return type if possible,
-    otherwise return a consistent type

-Examples
---------
-DataFrame results

->>> data.groupby(func, axis=0).mean()
->>> data.groupby(['col1', 'col2'])['col3'].mean()

-DataFrame with hierarchical index

->>> data.groupby(['col1', 'col2']).mean()

-Returns
--------
-GroupBy object
- -
head(self, n=5)
Returns first n rows
- -
interpolate(self, method='linear', axis=0, limit=None, inplace=False, limit_direction='forward', downcast=None, **kwargs)
Interpolate values according to different methods.

-Please note that only ``method='linear'`` is supported for
-DataFrames/Series with a MultiIndex.

-Parameters
-----------
-method : {'linear', 'time', 'index', 'values', 'nearest', 'zero',
-          'slinear', 'quadratic', 'cubic', 'barycentric', 'krogh',
-          'polynomial', 'spline', 'piecewise_polynomial',
-          'from_derivatives', 'pchip', 'akima'}

-    * 'linear': ignore the index and treat the values as equally
-      spaced. This is the only method supported on MultiIndexes.
-      default
-    * 'time': interpolation works on daily and higher resolution
-      data to interpolate given length of interval
-    * 'index', 'values': use the actual numerical values of the index
-    * 'nearest', 'zero', 'slinear', 'quadratic', 'cubic',
-      'barycentric', 'polynomial' is passed to
-      ``scipy.interpolate.interp1d``. Both 'polynomial' and 'spline'
-      require that you also specify an `order` (int),
-      e.g. df.interpolate(method='polynomial', order=4).
-      These use the actual numerical values of the index.
-    * 'krogh', 'piecewise_polynomial', 'spline', 'pchip' and 'akima'
-      are all wrappers around the scipy interpolation methods of
-      similar names. These use the actual numerical values of the
-      index. For more information on their behavior, see the
-      `scipy documentation
-      <http://docs.scipy.org/doc/scipy/reference/interpolate.html#univariate-interpolation>`__
-      and `tutorial documentation
-      <http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html>`__
-    * 'from_derivatives' refers to BPoly.from_derivatives which
-      replaces 'piecewise_polynomial' interpolation method in
-      scipy 0.18

-    .. versionadded:: 0.18.1

-       Added support for the 'akima' method
-       Added interpolate method 'from_derivatives' which replaces
-       'piecewise_polynomial' in scipy 0.18; backwards-compatible with
-       scipy < 0.18

-axis : {0, 1}, default 0
-    * 0: fill column-by-column
-    * 1: fill row-by-row
-limit : int, default None.
-    Maximum number of consecutive NaNs to fill. Must be greater than 0.
-limit_direction : {'forward', 'backward', 'both'}, default 'forward'
-    If limit is specified, consecutive NaNs will be filled in this
-    direction.

-    .. versionadded:: 0.17.0

-inplace : bool, default False
-    Update the NDFrame in place if possible.
-downcast : optional, 'infer' or None, defaults to None
-    Downcast dtypes if possible.
-kwargs : keyword arguments to pass on to the interpolating function.

-Returns
--------
-Series or DataFrame of same shape interpolated at the NaNs

-See Also
---------
-reindex, replace, fillna

-Examples
---------

-Filling in NaNs

->>> s = pd.Series([0, 1, np.nan, 3])
->>> s.interpolate()
-0    0
-1    1
-2    2
-3    3
-dtype: float64
- -
isnull(self)
Return a boolean same-sized object indicating if the values are null.

-See Also
---------
-notnull : boolean inverse of isnull
- -
keys(self)
Get the 'info axis' (see Indexing for more)

-This is index for Series, columns for DataFrame and major_axis for
-Panel.
- -
last(self, offset)
Convenience method for subsetting final periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.last('5M') -> Last 5 months

-Returns
--------
-subset : type of caller
- -
mask(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is False and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The mask method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``False`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``mask`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.where`
- -
notnull(self)
Return a boolean same-sized object indicating if the values are
-not null.

-See Also
---------
-isnull : boolean inverse of notnull
- -
pct_change(self, periods=1, fill_method='pad', limit=None, freq=None, **kwargs)
Percent change over given number of periods.

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming percent change
-fill_method : str, default 'pad'
-    How to handle NAs before computing percent changes
-limit : int, default None
-    The number of consecutive NAs to fill before stopping
-freq : DateOffset, timedelta, or offset alias string, optional
-    Increment to use from time series API (e.g. 'M' or BDay())

-Returns
--------
-chg : NDFrame

-Notes
------

-By default, the percentage change is calculated along the stat
-axis: 0, or ``Index``, for ``DataFrame`` and 1, or ``minor`` for
-``Panel``. You can change this with the ``axis`` keyword argument.
- -
pipe(self, func, *args, **kwargs)
Apply func(self, \*args, \*\*kwargs)

-.. versionadded:: 0.16.2

-Parameters
-----------
-func : function
-    function to apply to the NDFrame.
-    ``args``, and ``kwargs`` are passed into ``func``.
-    Alternatively a ``(callable, data_keyword)`` tuple where
-    ``data_keyword`` is a string indicating the keyword of
-    ``callable`` that expects the NDFrame.
-args : positional arguments passed into ``func``.
-kwargs : a dictionary of keyword arguments passed into ``func``.

-Returns
--------
-object : the return type of ``func``.

-Notes
------

-Use ``.pipe`` when chaining together functions that expect
-on Series or DataFrames. Instead of writing

->>> f(g(h(df), arg1=a), arg2=b, arg3=c)

-You can write

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe(f, arg2=b, arg3=c)
-... )

-If you have a function that takes the data as (say) the second
-argument, pass a tuple indicating which keyword expects the
-data. For example, suppose ``f`` takes its data as ``arg2``:

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe((f, 'arg2'), arg1=a, arg3=c)
-...  )

-See Also
---------
-pandas.DataFrame.apply
-pandas.DataFrame.applymap
-pandas.Series.map
- -
pop(self, item)
Return item and drop from frame. Raise KeyError if not found.
- -
rank(self, axis=0, method='average', numeric_only=None, na_option='keep', ascending=True, pct=False)
Compute numerical data ranks (1 through n) along axis. Equal values are
-assigned a rank that is the average of the ranks of those values

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    index to direct ranking
-method : {'average', 'min', 'max', 'first', 'dense'}
-    * average: average rank of group
-    * min: lowest rank in group
-    * max: highest rank in group
-    * first: ranks assigned in order they appear in the array
-    * dense: like 'min', but rank always increases by 1 between groups
-numeric_only : boolean, default None
-    Include only float, int, boolean data. Valid only for DataFrame or
-    Panel objects
-na_option : {'keep', 'top', 'bottom'}
-    * keep: leave NA values where they are
-    * top: smallest rank if ascending
-    * bottom: smallest rank if descending
-ascending : boolean, default True
-    False for ranks by high (1) to low (N)
-pct : boolean, default False
-    Computes percentage rank of data

-Returns
--------
-ranks : same type as caller
- -
reindex_like(self, other, method=None, copy=True, limit=None, tolerance=None)
Return an object with matching indices to myself.

-Parameters
-----------
-other : Object
-method : string or None
-copy : boolean, default True
-limit : int, default None
-    Maximum number of consecutive labels to fill for inexact matches.
-tolerance : optional
-    Maximum distance between labels of the other object and this
-    object for inexact matches.

-    .. versionadded:: 0.17.0

-Notes
------
-Like calling s.reindex(index=other.index, columns=other.columns,
-                       method=...)

-Returns
--------
-reindexed : same as input
- -
rename_axis(self, mapper, axis=0, copy=True, inplace=False)
Alter index and / or columns using input function or functions.
-A scalar or list-like for ``mapper`` will alter the ``Index.name``
-or ``MultiIndex.names`` attribute.
-A function or dict for ``mapper`` will alter the labels.
-Function / dict values must be unique (1-to-1). Labels not contained in
-a dict / Series will be left as-is.

-Parameters
-----------
-mapper : scalar, list-like, dict-like or function, optional
-axis : int or string, default 0
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False

-Returns
--------
-renamed : type of caller

-See Also
---------
-pandas.NDFrame.rename
-pandas.Index.rename

-Examples
---------
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename_axis("foo")  # scalar, alters df.index.name
-     A  B
-foo
-0    1  4
-1    2  5
-2    3  6
->>> df.rename_axis(lambda x: 2 * x)  # function: alters labels
-   A  B
-0  1  4
-2  2  5
-4  3  6
->>> df.rename_axis({"A": "ehh", "C": "see"}, axis="columns")  # mapping
-   ehh  B
-0    1  4
-1    2  5
-2    3  6
- -
replace(self, to_replace=None, value=None, inplace=False, limit=None, regex=False, method='pad', axis=None)
Replace values given in 'to_replace' with 'value'.

-Parameters
-----------
-to_replace : str, regex, list, dict, Series, numeric, or None

-    * str or regex:

-        - str: string exactly matching `to_replace` will be replaced
-          with `value`
-        - regex: regexs matching `to_replace` will be replaced with
-          `value`

-    * list of str, regex, or numeric:

-        - First, if `to_replace` and `value` are both lists, they
-          **must** be the same length.
-        - Second, if ``regex=True`` then all of the strings in **both**
-          lists will be interpreted as regexs otherwise they will match
-          directly. This doesn't matter much for `value` since there
-          are only a few possible substitution regexes you can use.
-        - str and regex rules apply as above.

-    * dict:

-        - Nested dictionaries, e.g., {'a': {'b': nan}}, are read as
-          follows: look in column 'a' for the value 'b' and replace it
-          with nan. You can nest regular expressions as well. Note that
-          column names (the top-level dictionary keys in a nested
-          dictionary) **cannot** be regular expressions.
-        - Keys map to column names and values map to substitution
-          values. You can treat this as a special case of passing two
-          lists except that you are specifying the column to search in.

-    * None:

-        - This means that the ``regex`` argument must be a string,
-          compiled regular expression, or list, dict, ndarray or Series
-          of such elements. If `value` is also ``None`` then this
-          **must** be a nested dictionary or ``Series``.

-    See the examples section for examples of each of these.
-value : scalar, dict, list, str, regex, default None
-    Value to use to fill holes (e.g. 0), alternately a dict of values
-    specifying which value to use for each column (columns not in the
-    dict will not be filled). Regular expressions, strings and lists or
-    dicts of such objects are also allowed.
-inplace : boolean, default False
-    If True, in place. Note: this will modify any
-    other views on this object (e.g. a column form a DataFrame).
-    Returns the caller if this is True.
-limit : int, default None
-    Maximum size gap to forward or backward fill
-regex : bool or same types as `to_replace`, default False
-    Whether to interpret `to_replace` and/or `value` as regular
-    expressions. If this is ``True`` then `to_replace` *must* be a
-    string. Otherwise, `to_replace` must be ``None`` because this
-    parameter will be interpreted as a regular expression or a list,
-    dict, or array of regular expressions.
-method : string, optional, {'pad', 'ffill', 'bfill'}
-    The method to use when for replacement, when ``to_replace`` is a
-    ``list``.

-See Also
---------
-NDFrame.reindex
-NDFrame.asfreq
-NDFrame.fillna

-Returns
--------
-filled : NDFrame

-Raises
-------
-AssertionError
-    * If `regex` is not a ``bool`` and `to_replace` is not ``None``.
-TypeError
-    * If `to_replace` is a ``dict`` and `value` is not a ``list``,
-      ``dict``, ``ndarray``, or ``Series``
-    * If `to_replace` is ``None`` and `regex` is not compilable into a
-      regular expression or is a list, dict, ndarray, or Series.
-ValueError
-    * If `to_replace` and `value` are ``list`` s or ``ndarray`` s, but
-      they are not the same length.

-Notes
------
-* Regex substitution is performed under the hood with ``re.sub``. The
-  rules for substitution for ``re.sub`` are the same.
-* Regular expressions will only substitute on strings, meaning you
-  cannot provide, for example, a regular expression matching floating
-  point numbers and expect the columns in your frame that have a
-  numeric dtype to be matched. However, if those floating point numbers
-  *are* strings, then you can do this.
-* This method has *a lot* of options. You are encouraged to experiment
-  and play with this method to gain intuition about how it works.
- -
resample(self, rule, how=None, axis=0, fill_method=None, closed=None, label=None, convention='start', kind=None, loffset=None, limit=None, base=0, on=None, level=None)
Convenience method for frequency conversion and resampling of time
-series.  Object must have a datetime-like index (DatetimeIndex,
-PeriodIndex, or TimedeltaIndex), or pass datetime-like values
-to the on or level keyword.

-Parameters
-----------
-rule : string
-    the offset string or object representing target conversion
-axis : int, optional, default 0
-closed : {'right', 'left'}
-    Which side of bin interval is closed
-label : {'right', 'left'}
-    Which bin edge label to label bucket with
-convention : {'start', 'end', 's', 'e'}
-loffset : timedelta
-    Adjust the resampled time labels
-base : int, default 0
-    For frequencies that evenly subdivide 1 day, the "origin" of the
-    aggregated intervals. For example, for '5min' frequency, base could
-    range from 0 through 4. Defaults to 0
-on : string, optional
-    For a DataFrame, column to use instead of index for resampling.
-    Column must be datetime-like.

-    .. versionadded:: 0.19.0

-level : string or int, optional
-    For a MultiIndex, level (name or number) to use for
-    resampling.  Level must be datetime-like.

-    .. versionadded:: 0.19.0

-Notes
------
-To learn more about the offset strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-Examples
---------

-Start by creating a series with 9 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=9, freq='T')
->>> series = pd.Series(range(9), index=index)
->>> series
-2000-01-01 00:00:00    0
-2000-01-01 00:01:00    1
-2000-01-01 00:02:00    2
-2000-01-01 00:03:00    3
-2000-01-01 00:04:00    4
-2000-01-01 00:05:00    5
-2000-01-01 00:06:00    6
-2000-01-01 00:07:00    7
-2000-01-01 00:08:00    8
-Freq: T, dtype: int64

-Downsample the series into 3 minute bins and sum the values
-of the timestamps falling into a bin.

->>> series.resample('3T').sum()
-2000-01-01 00:00:00     3
-2000-01-01 00:03:00    12
-2000-01-01 00:06:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but label each
-bin using the right edge instead of the left. Please note that the
-value in the bucket used as the label is not included in the bucket,
-which it labels. For example, in the original series the
-bucket ``2000-01-01 00:03:00`` contains the value 3, but the summed
-value in the resampled bucket with the label``2000-01-01 00:03:00``
-does not include 3 (if it did, the summed value would be 6, not 3).
-To include this value close the right side of the bin interval as
-illustrated in the example below this one.

->>> series.resample('3T', label='right').sum()
-2000-01-01 00:03:00     3
-2000-01-01 00:06:00    12
-2000-01-01 00:09:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but close the right
-side of the bin interval.

->>> series.resample('3T', label='right', closed='right').sum()
-2000-01-01 00:00:00     0
-2000-01-01 00:03:00     6
-2000-01-01 00:06:00    15
-2000-01-01 00:09:00    15
-Freq: 3T, dtype: int64

-Upsample the series into 30 second bins.

->>> series.resample('30S').asfreq()[0:5] #select first 5 rows
-2000-01-01 00:00:00   0.0
-2000-01-01 00:00:30   NaN
-2000-01-01 00:01:00   1.0
-2000-01-01 00:01:30   NaN
-2000-01-01 00:02:00   2.0
-Freq: 30S, dtype: float64

-Upsample the series into 30 second bins and fill the ``NaN``
-values using the ``pad`` method.

->>> series.resample('30S').pad()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    0
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    1
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Upsample the series into 30 second bins and fill the
-``NaN`` values using the ``bfill`` method.

->>> series.resample('30S').bfill()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    1
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    2
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Pass a custom function via ``apply``

->>> def custom_resampler(array_like):
-...     return np.sum(array_like)+5

->>> series.resample('3T').apply(custom_resampler)
-2000-01-01 00:00:00     8
-2000-01-01 00:03:00    17
-2000-01-01 00:06:00    26
-Freq: 3T, dtype: int64

-For DataFrame objects, the keyword ``on`` can be used to specify the
-column instead of the index for resampling.

->>> df = pd.DataFrame(data=9*[range(4)], columns=['a', 'b', 'c', 'd'])
->>> df['time'] = pd.date_range('1/1/2000', periods=9, freq='T')
->>> df.resample('3T', on='time').sum()
-                     a  b  c  d
-time
-2000-01-01 00:00:00  0  3  6  9
-2000-01-01 00:03:00  0  3  6  9
-2000-01-01 00:06:00  0  3  6  9

-For a DataFrame with MultiIndex, the keyword ``level`` can be used to
-specify on level the resampling needs to take place.

->>> time = pd.date_range('1/1/2000', periods=5, freq='T')
->>> df2 = pd.DataFrame(data=10*[range(4)],
-                       columns=['a', 'b', 'c', 'd'],
-                       index=pd.MultiIndex.from_product([time, [1, 2]])
-                       )
->>> df2.resample('3T', level=0).sum()
-                     a  b   c   d
-2000-01-01 00:00:00  0  6  12  18
-2000-01-01 00:03:00  0  4   8  12
- -
sample(self, n=None, frac=None, replace=False, weights=None, random_state=None, axis=None)
Returns a random sample of items from an axis of object.

-.. versionadded:: 0.16.1

-Parameters
-----------
-n : int, optional
-    Number of items from axis to return. Cannot be used with `frac`.
-    Default = 1 if `frac` = None.
-frac : float, optional
-    Fraction of axis items to return. Cannot be used with `n`.
-replace : boolean, optional
-    Sample with or without replacement. Default = False.
-weights : str or ndarray-like, optional
-    Default 'None' results in equal probability weighting.
-    If passed a Series, will align with target object on index. Index
-    values in weights not found in sampled object will be ignored and
-    index values in sampled object not in weights will be assigned
-    weights of zero.
-    If called on a DataFrame, will accept the name of a column
-    when axis = 0.
-    Unless weights are a Series, weights must be same length as axis
-    being sampled.
-    If weights do not sum to 1, they will be normalized to sum to 1.
-    Missing values in the weights column will be treated as zero.
-    inf and -inf values not allowed.
-random_state : int or numpy.random.RandomState, optional
-    Seed for the random number generator (if int), or numpy RandomState
-    object.
-axis : int or string, optional
-    Axis to sample. Accepts axis number or name. Default is stat axis
-    for given data type (0 for Series and DataFrames, 1 for Panels).

-Returns
--------
-A new object of same type as caller.

-Examples
---------

-Generate an example ``Series`` and ``DataFrame``:

->>> s = pd.Series(np.random.randn(50))
->>> s.head()
-0   -0.038497
-1    1.820773
-2   -0.972766
-3   -1.598270
-4   -1.095526
-dtype: float64
->>> df = pd.DataFrame(np.random.randn(50, 4), columns=list('ABCD'))
->>> df.head()
-          A         B         C         D
-0  0.016443 -2.318952 -0.566372 -1.028078
-1 -1.051921  0.438836  0.658280 -0.175797
-2 -1.243569 -0.364626 -0.215065  0.057736
-3  1.768216  0.404512 -0.385604 -1.457834
-4  1.072446 -1.137172  0.314194 -0.046661

-Next extract a random sample from both of these objects...

-3 random elements from the ``Series``:

->>> s.sample(n=3)
-27   -0.994689
-55   -1.049016
-67   -0.224565
-dtype: float64

-And a random 10% of the ``DataFrame`` with replacement:

->>> df.sample(frac=0.1, replace=True)
-           A         B         C         D
-35  1.981780  0.142106  1.817165 -0.290805
-49 -1.336199 -0.448634 -0.789640  0.217116
-40  0.823173 -0.078816  1.009536  1.015108
-15  1.421154 -0.055301 -1.922594 -0.019696
-6  -0.148339  0.832938  1.787600 -1.383767
- -
select(self, crit, axis=0)
Return data corresponding to axis labels matching criteria

-Parameters
-----------
-crit : function
-    To be called on each index (label). Should return True or False
-axis : int

-Returns
--------
-selection : type of caller
- -
set_axis(self, axis, labels)
public verson of axis assignment
- -
slice_shift(self, periods=1, axis=0)
Equivalent to `shift` without copying data. The shifted data will
-not include the dropped periods and the shifted axis will be smaller
-than the original.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative

-Notes
------
-While the `slice_shift` is faster than `shift`, you may pay for it
-later during alignment.

-Returns
--------
-shifted : same type as caller
- -
squeeze(self, axis=None)
Squeeze length 1 dimensions.

-Parameters
-----------
-axis : None, integer or string axis name, optional
-    The axis to squeeze if 1-sized.

-    .. versionadded:: 0.20.0

-Returns
--------
-scalar if 1-sized, else original object
- -
swapaxes(self, axis1, axis2, copy=True)
Interchange axes and swap values axes appropriately

-Returns
--------
-y : same as input
- -
tail(self, n=5)
Returns last n rows
- -
take(self, indices, axis=0, convert=True, is_copy=True, **kwargs)
Analogous to ndarray.take

-Parameters
-----------
-indices : list / array of ints
-axis : int, default 0
-convert : translate neg to pos indices (default)
-is_copy : mark the returned frame as a copy

-Returns
--------
-taken : type of caller
- -
to_clipboard(self, excel=None, sep=None, **kwargs)
Attempt to write text representation of object to the system clipboard
-This can be pasted into Excel, for example.

-Parameters
-----------
-excel : boolean, defaults to True
-        if True, use the provided separator, writing in a csv
-        format for allowing easy pasting into excel.
-        if False, write a string representation of the object
-        to the clipboard
-sep : optional, defaults to tab
-other keywords are passed to to_csv

-Notes
------
-Requirements for your platform
-  - Linux: xclip, or xsel (with gtk or PyQt4 modules)
-  - Windows: none
-  - OS X: none
- -
to_dense(self)
Return dense representation of NDFrame (as opposed to sparse)
- -
to_hdf(self, path_or_buf, key, **kwargs)
Write the contained data to an HDF5 file using HDFStore.

-Parameters
-----------
-path_or_buf : the path (string) or HDFStore object
-key : string
-    identifier for the group in the store
-mode : optional, {'a', 'w', 'r+'}, default 'a'

-  ``'w'``
-      Write; a new file is created (an existing file with the same
-      name would be deleted).
-  ``'a'``
-      Append; an existing file is opened for reading and writing,
-      and if the file does not exist it is created.
-  ``'r+'``
-      It is similar to ``'a'``, but the file must already exist.
-format : 'fixed(f)|table(t)', default is 'fixed'
-    fixed(f) : Fixed format
-               Fast writing/reading. Not-appendable, nor searchable
-    table(t) : Table format
-               Write as a PyTables Table structure which may perform
-               worse but allow more flexible operations like searching
-               / selecting subsets of the data
-append : boolean, default False
-    For Table formats, append the input data to the existing
-data_columns :  list of columns, or True, default None
-    List of columns to create as indexed data columns for on-disk
-    queries, or True to use all columns. By default only the axes
-    of the object are indexed. See `here
-    <http://pandas.pydata.org/pandas-docs/stable/io.html#query-via-data-columns>`__.

-    Applicable only to format='table'.
-complevel : int, 1-9, default 0
-    If a complib is specified compression will be applied
-    where possible
-complib : {'zlib', 'bzip2', 'lzo', 'blosc', None}, default None
-    If complevel is > 0 apply compression to objects written
-    in the store wherever possible
-fletcher32 : bool, default False
-    If applying compression use the fletcher32 checksum
-dropna : boolean, default False.
-    If true, ALL nan rows will not be written to store.
- -
to_json(self, path_or_buf=None, orient=None, date_format=None, double_precision=10, force_ascii=True, date_unit='ms', default_handler=None, lines=False)
Convert the object to a JSON string.

-Note NaN's and None will be converted to null and datetime objects
-will be converted to UNIX timestamps.

-Parameters
-----------
-path_or_buf : the path or buffer to write the result string
-    if this is None, return a StringIO of the converted string
-orient : string

-    * Series

-      - default is 'index'
-      - allowed values are: {'split','records','index'}

-    * DataFrame

-      - default is 'columns'
-      - allowed values are:
-        {'split','records','index','columns','values'}

-    * The format of the JSON string

-      - split : dict like
-        {index -> [index], columns -> [columns], data -> [values]}
-      - records : list like
-        [{column -> value}, ... , {column -> value}]
-      - index : dict like {index -> {column -> value}}
-      - columns : dict like {column -> {index -> value}}
-      - values : just the values array
-      - table : dict like {'schema': {schema}, 'data': {data}}
-        describing the data, and the data component is
-        like ``orient='records'``.

-        .. versionchanged:: 0.20.0

-date_format : {None, 'epoch', 'iso'}
-    Type of date conversion. `epoch` = epoch milliseconds,
-    `iso` = ISO8601. The default depends on the `orient`. For
-    `orient='table'`, the default is `'iso'`. For all other orients,
-    the default is `'epoch'`.
-double_precision : The number of decimal places to use when encoding
-    floating point values, default 10.
-force_ascii : force encoded string to be ASCII, default True.
-date_unit : string, default 'ms' (milliseconds)
-    The time unit to encode to, governs timestamp and ISO8601
-    precision.  One of 's', 'ms', 'us', 'ns' for second, millisecond,
-    microsecond, and nanosecond respectively.
-default_handler : callable, default None
-    Handler to call if object cannot otherwise be converted to a
-    suitable format for JSON. Should receive a single argument which is
-    the object to convert and return a serialisable object.
-lines : boolean, default False
-    If 'orient' is 'records' write out line delimited json format. Will
-    throw ValueError if incorrect 'orient' since others are not list
-    like.

-    .. versionadded:: 0.19.0

-Returns
--------
-same type as input object with filtered info axis

-See Also
---------
-pd.read_json

-Examples
---------

->>> df = pd.DataFrame([['a', 'b'], ['c', 'd']],
-...                   index=['row 1', 'row 2'],
-...                   columns=['col 1', 'col 2'])
->>> df.to_json(orient='split')
-'{"columns":["col 1","col 2"],
-  "index":["row 1","row 2"],
-  "data":[["a","b"],["c","d"]]}'

-Encoding/decoding a Dataframe using ``'index'`` formatted JSON:

->>> df.to_json(orient='index')
-'{"row 1":{"col 1":"a","col 2":"b"},"row 2":{"col 1":"c","col 2":"d"}}'

-Encoding/decoding a Dataframe using ``'records'`` formatted JSON.
-Note that index labels are not preserved with this encoding.

->>> df.to_json(orient='records')
-'[{"col 1":"a","col 2":"b"},{"col 1":"c","col 2":"d"}]'

-Encoding with Table Schema

->>> df.to_json(orient='table')
-'{"schema": {"fields": [{"name": "index", "type": "string"},
-                        {"name": "col 1", "type": "string"},
-                        {"name": "col 2", "type": "string"}],
-             "primaryKey": "index",
-             "pandas_version": "0.20.0"},
-  "data": [{"index": "row 1", "col 1": "a", "col 2": "b"},
-           {"index": "row 2", "col 1": "c", "col 2": "d"}]}'
- -
to_msgpack(self, path_or_buf=None, encoding='utf-8', **kwargs)
msgpack (serialize) object to input file path

-THIS IS AN EXPERIMENTAL LIBRARY and the storage format
-may not be stable until a future release.

-Parameters
-----------
-path : string File path, buffer-like, or None
-    if None, return generated string
-append : boolean whether to append to an existing msgpack
-    (default is False)
-compress : type of compressor (zlib or blosc), default to None (no
-    compression)
- -
to_pickle(self, path, compression='infer')
Pickle (serialize) object to input file path.

-Parameters
-----------
-path : string
-    File path
-compression : {'infer', 'gzip', 'bz2', 'xz', None}, default 'infer'
-    a string representing the compression to use in the output file

-    .. versionadded:: 0.20.0
- -
to_sql(self, name, con, flavor=None, schema=None, if_exists='fail', index=True, index_label=None, chunksize=None, dtype=None)
Write records stored in a DataFrame to a SQL database.

-Parameters
-----------
-name : string
-    Name of SQL table
-con : SQLAlchemy engine or DBAPI2 connection (legacy mode)
-    Using SQLAlchemy makes it possible to use any DB supported by that
-    library. If a DBAPI2 object, only sqlite3 is supported.
-flavor : 'sqlite', default None
-    DEPRECATED: this parameter will be removed in a future version,
-    as 'sqlite' is the only supported option if SQLAlchemy is not
-    installed.
-schema : string, default None
-    Specify the schema (if database flavor supports this). If None, use
-    default schema.
-if_exists : {'fail', 'replace', 'append'}, default 'fail'
-    - fail: If table exists, do nothing.
-    - replace: If table exists, drop it, recreate it, and insert data.
-    - append: If table exists, insert data. Create if does not exist.
-index : boolean, default True
-    Write DataFrame index as a column.
-index_label : string or sequence, default None
-    Column label for index column(s). If None is given (default) and
-    `index` is True, then the index names are used.
-    A sequence should be given if the DataFrame uses MultiIndex.
-chunksize : int, default None
-    If not None, then rows will be written in batches of this size at a
-    time.  If None, all rows will be written at once.
-dtype : dict of column name to SQL type, default None
-    Optional specifying the datatype for columns. The SQL type should
-    be a SQLAlchemy type, or a string for sqlite3 fallback connection.
- -
to_xarray(self)
Return an xarray object from the pandas object.

-Returns
--------
-a DataArray for a Series
-a Dataset for a DataFrame
-a DataArray for higher dims

-Examples
---------
->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)})
->>> df
-   A    B    C
-0  1  foo  4.0
-1  1  bar  5.0
-2  2  foo  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (index: 3)
-Coordinates:
-  * index    (index) int64 0 1 2
-Data variables:
-    A        (index) int64 1 1 2
-    B        (index) object 'foo' 'bar' 'foo'
-    C        (index) float64 4.0 5.0 6.0

->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)}
-                     ).set_index(['B','A'])
->>> df
-         C
-B   A
-foo 1  4.0
-bar 1  5.0
-foo 2  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (A: 2, B: 2)
-Coordinates:
-  * B        (B) object 'bar' 'foo'
-  * A        (A) int64 1 2
-Data variables:
-    C        (B, A) float64 5.0 nan 4.0 6.0

->>> p = pd.Panel(np.arange(24).reshape(4,3,2),
-                 items=list('ABCD'),
-                 major_axis=pd.date_range('20130101', periods=3),
-                 minor_axis=['first', 'second'])
->>> p
-<class 'pandas.core.panel.Panel'>
-Dimensions: 4 (items) x 3 (major_axis) x 2 (minor_axis)
-Items axis: A to D
-Major_axis axis: 2013-01-01 00:00:00 to 2013-01-03 00:00:00
-Minor_axis axis: first to second

->>> p.to_xarray()
-<xarray.DataArray (items: 4, major_axis: 3, minor_axis: 2)>
-array([[[ 0,  1],
-        [ 2,  3],
-        [ 4,  5]],
-       [[ 6,  7],
-        [ 8,  9],
-        [10, 11]],
-       [[12, 13],
-        [14, 15],
-        [16, 17]],
-       [[18, 19],
-        [20, 21],
-        [22, 23]]])
-Coordinates:
-  * items       (items) object 'A' 'B' 'C' 'D'
-  * major_axis  (major_axis) datetime64[ns] 2013-01-01 2013-01-02 2013-01-03  # noqa
-  * minor_axis  (minor_axis) object 'first' 'second'

-Notes
------
-See the `xarray docs <http://xarray.pydata.org/en/stable/>`__
- -
truncate(self, before=None, after=None, axis=None, copy=True)
Truncates a sorted NDFrame before and/or after some particular
-index value. If the axis contains only datetime values, before/after
-parameters are converted to datetime values.

-Parameters
-----------
-before : date
-    Truncate before index value
-after : date
-    Truncate after index value
-axis : the truncation axis, defaults to the stat axis
-copy : boolean, default is True,
-    return a copy of the truncated section

-Returns
--------
-truncated : type of caller
- -
tshift(self, periods=1, freq=None, axis=0)
Shift the time index, using the index's frequency if available.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, default None
-    Increment to use from the tseries module or time rule (e.g. 'EOM')
-axis : int or basestring
-    Corresponds to the axis that contains the Index

-Notes
------
-If freq is not specified then tries to use the freq or inferred_freq
-attributes of the index. If neither of those attributes exist, a
-ValueError is thrown

-Returns
--------
-shifted : NDFrame
- -
tz_convert(self, tz, axis=0, level=None, copy=True)
Convert tz-aware axis to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to convert
-level : int, str, default None
-    If axis ia a MultiIndex, convert a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data

-Returns
--------

-Raises
-------
-TypeError
-    If the axis is tz-naive.
- -
tz_localize(self, tz, axis=0, level=None, copy=True, ambiguous='raise')
Localize tz-naive TimeSeries to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to localize
-level : int, str, default None
-    If axis ia a MultiIndex, localize a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data
-ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise'
-    - 'infer' will attempt to infer fall dst-transition hours based on
-      order
-    - bool-ndarray where True signifies a DST time, False designates
-      a non-DST time (note that this flag is only applicable for
-      ambiguous times)
-    - 'NaT' will return NaT where there are ambiguous times
-    - 'raise' will raise an AmbiguousTimeError if there are ambiguous
-      times
-infer_dst : boolean, default False (DEPRECATED)
-    Attempt to infer fall dst-transition hours based on order

-Returns
--------

-Raises
-------
-TypeError
-    If the TimeSeries is tz-aware and tz is not None.
- -
where(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is True and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The where method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``True`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``where`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.mask`
- -
xs(self, key, axis=0, level=None, drop_level=True)
Returns a cross-section (row(s) or column(s)) from the
-Series/DataFrame. Defaults to cross-section on the rows (axis=0).

-Parameters
-----------
-key : object
-    Some label contained in the index, or partially in a MultiIndex
-axis : int, default 0
-    Axis to retrieve cross-section on
-level : object, defaults to first n levels (n=1 or len(key))
-    In case of a key partially contained in a MultiIndex, indicate
-    which levels are used. Levels can be referred by label or position.
-drop_level : boolean, default True
-    If False, returns object with same levels as self.

-Examples
---------
->>> df
-   A  B  C
-a  4  5  2
-b  4  0  9
-c  9  7  3
->>> df.xs('a')
-A    4
-B    5
-C    2
-Name: a
->>> df.xs('C', axis=1)
-a    2
-b    9
-c    3
-Name: C

->>> df
-                    A  B  C  D
-first second third
-bar   one    1      4  1  8  9
-      two    1      7  5  5  0
-baz   one    1      6  6  8  0
-      three  2      5  3  5  3
->>> df.xs(('baz', 'three'))
-       A  B  C  D
-third
-2      5  3  5  3
->>> df.xs('one', level=1)
-             A  B  C  D
-first third
-bar   1      4  1  8  9
-baz   1      6  6  8  0
->>> df.xs(('baz', 2), level=[0, 'third'])
-        A  B  C  D
-second
-three   5  3  5  3

-Returns
--------
-xs : Series or DataFrame

-Notes
------
-xs is only for getting, not setting values.

-MultiIndex Slicers is a generic way to get/set values on any level or
-levels.  It is a superset of xs functionality, see
-:ref:`MultiIndex Slicers <advanced.mi_slicers>`
- -
-Data descriptors inherited from pandas.core.generic.NDFrame:
-
at
-
Fast label-based scalar accessor

-Similarly to ``loc``, ``at`` provides **label** based scalar lookups.
-You can also set using these indexers.
-
-
blocks
-
Internal property, property synonym for as_blocks()
-
-
dtypes
-
Return the dtypes in this object.
-
-
empty
-
True if NDFrame is entirely empty [no items], meaning any of the
-axes are of length 0.

-Notes
------
-If NDFrame contains only NaNs, it is still not considered empty. See
-the example below.

-Examples
---------
-An example of an actual empty DataFrame. Notice the index is empty:

->>> df_empty = pd.DataFrame({'A' : []})
->>> df_empty
-Empty DataFrame
-Columns: [A]
-Index: []
->>> df_empty.empty
-True

-If we only have NaNs in our DataFrame, it is not considered empty! We
-will need to drop the NaNs to make the DataFrame empty:

->>> df = pd.DataFrame({'A' : [np.nan]})
->>> df
-    A
-0 NaN
->>> df.empty
-False
->>> df.dropna().empty
-True

-See also
---------
-pandas.Series.dropna
-pandas.DataFrame.dropna
-
-
ftypes
-
Return the ftypes (indication of sparse/dense and dtype)
-in this object.
-
-
iat
-
Fast integer location scalar accessor.

-Similarly to ``iloc``, ``iat`` provides **integer** based lookups.
-You can also set using these indexers.
-
-
iloc
-
Purely integer-location based indexing for selection by position.

-``.iloc[]`` is primarily integer position based (from ``0`` to
-``length-1`` of the axis), but may also be used with a boolean
-array.

-Allowed inputs are:

-- An integer, e.g. ``5``.
-- A list or array of integers, e.g. ``[4, 3, 0]``.
-- A slice object with ints, e.g. ``1:7``.
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.iloc`` will raise ``IndexError`` if a requested indexer is
-out-of-bounds, except *slice* indexers which allow out-of-bounds
-indexing (this conforms with python/numpy *slice* semantics).

-See more at :ref:`Selection by Position <indexing.integer>`
-
-
ix
-
A primarily label-location based indexer, with integer position
-fallback.

-``.ix[]`` supports mixed integer and label based access. It is
-primarily label based, but will fall back to integer positional
-access unless the corresponding axis is of integer type.

-``.ix`` is the most general indexer and will support any of the
-inputs in ``.loc`` and ``.iloc``. ``.ix`` also supports floating
-point label schemes. ``.ix`` is exceptionally useful when dealing
-with mixed positional and label based hierachical indexes.

-However, when an axis is integer based, ONLY label based access
-and not positional access is supported. Thus, in such cases, it's
-usually better to be explicit and use ``.iloc`` or ``.loc``.

-See more at :ref:`Advanced Indexing <advanced>`.
-
-
loc
-
Purely label-location based indexer for selection by label.

-``.loc[]`` is primarily label based, but may also be used with a
-boolean array.

-Allowed inputs are:

-- A single label, e.g. ``5`` or ``'a'``, (note that ``5`` is
-  interpreted as a *label* of the index, and **never** as an
-  integer position along the index).
-- A list or array of labels, e.g. ``['a', 'b', 'c']``.
-- A slice object with labels, e.g. ``'a':'f'`` (note that contrary
-  to usual python slices, **both** the start and the stop are included!).
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.loc`` will raise a ``KeyError`` when the items are not found.

-See more at :ref:`Selection by Label <indexing.label>`
-
-
ndim
-
Number of axes / array dimensions
-
-
size
-
number of elements in the NDFrame
-
-
values
-
Numpy representation of NDFrame

-Notes
------
-The dtype will be a lower-common-denominator dtype (implicit
-upcasting); that is to say if the dtypes (even of numeric types)
-are mixed, the one that accommodates all will be chosen. Use this
-with care if you are not dealing with the blocks.

-e.g. If the dtypes are float16 and float32, dtype will be upcast to
-float32.  If dtypes are int32 and uint8, dtype will be upcast to
-int32. By numpy.find_common_type convention, mixing int64 and uint64
-will result in a flot64 dtype.
-
-
-Data and other attributes inherited from pandas.core.generic.NDFrame:
-
is_copy = None
- -
-Methods inherited from pandas.core.base.PandasObject:
-
__dir__(self)
Provide method name lookup and completion
-Only provide 'public' methods
- -
__sizeof__(self)
Generates the total memory usage for a object that returns
-either a value or Series of values
- -
-Methods inherited from pandas.core.base.StringMixin:
-
__bytes__(self)
Return a string representation for a particular object.

-Invoked by bytes(obj) in py3 only.
-Yields a bytestring in both py2/py3.
- -
__repr__(self)
Return a string representation for a particular object.

-Yields Bytestring in Py2, Unicode String in py3.
- -
__str__(self)
Return a string representation for a particular Object

-Invoked by str(df) in both py2/py3.
-Yields Bytestring in Py2, Unicode String in py3.
- -
-Data descriptors inherited from pandas.core.base.StringMixin:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-

- - - - - - - -
 
-class TimeSeries(MySeries)
   One-dimensional ndarray with axis labels (including time series).

-Labels need not be unique but must be a hashable type. The object
-supports both integer- and label-based indexing and provides a host of
-methods for performing operations involving the index. Statistical
-methods from ndarray have been overridden to automatically exclude
-missing data (currently represented as NaN).

-Operations between Series (+, -, /, *, **) align values based on their
-associated index values-- they need not be the same length. The result
-index will be the sorted union of the two indexes.

-Parameters
-----------
-data : array-like, dict, or scalar value
-    Contains data stored in Series
-index : array-like or Index (1d)
-    Values must be hashable and have the same length as `data`.
-    Non-unique index values are allowed. Will default to
-    RangeIndex(len(data)) if not provided. If both a dict and index
-    sequence are used, the index will override the keys found in the
-    dict.
-dtype : numpy.dtype or None
-    If None, dtype will be inferred
-copy : boolean, default False
-    Copy input data
 
 
Method resolution order:
-
TimeSeries
-
MySeries
-
pandas.core.series.Series
-
pandas.core.base.IndexOpsMixin
-
pandas.core.strings.StringAccessorMixin
-
pandas.core.generic.NDFrame
-
pandas.core.base.PandasObject
-
pandas.core.base.StringMixin
-
pandas.core.base.SelectionMixin
-
builtins.object
-
-
-Methods inherited from MySeries:
-
__init__(self, *args, **kwargs)
Initialize a Series.

-Note: this cleans up a weird Series behavior, which is
-that Series() and Series([]) yield different results.
-See: https://github.com/pandas-dev/pandas/issues/16737
- -
set(self, **kwargs)
Uses keyword arguments to update the Series in place.

-Example: series.update(a=1, b=2)
- -
-Methods inherited from pandas.core.series.Series:
-
__add__ = wrapper(left, right, name='__add__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f2f0>)
- -
__and__ = wrapper(self, other)
- -
__array__(self, result=None)
the array interface, return my values
- -
__array_prepare__(self, result, context=None)
Gets called prior to a ufunc
- -
__array_wrap__(self, result, context=None)
Gets called after a ufunc
- -
__div__ = wrapper(left, right, name='__truediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f9d8>)
- -
__divmod__ = wrapper(left, right, name='__divmod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca343bf8>)
- -
__eq__ = wrapper(self, other, axis=None)
- -
__float__ = wrapper(self)
- -
__floordiv__ = wrapper(left, right, name='__floordiv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fb70>)
- -
__ge__ = wrapper(self, other, axis=None)
- -
__getitem__(self, key)
- -
__gt__ = wrapper(self, other, axis=None)
- -
__iadd__ = f(self, other)
- -
__imul__ = f(self, other)
- -
__int__ = wrapper(self)
- -
__ipow__ = f(self, other)
- -
__isub__ = f(self, other)
- -
__iter__(self)
provide iteration over the values of the Series
-box values if necessary
- -
__itruediv__ = f(self, other)
- -
__le__ = wrapper(self, other, axis=None)
- -
__len__(self)
return the length of the Series
- -
__long__ = wrapper(self)
- -
__lt__ = wrapper(self, other, axis=None)
- -
__mod__ = wrapper(left, right, name='__mod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fd08>)
- -
__mul__ = wrapper(left, right, name='__mul__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f840>)
- -
__ne__ = wrapper(self, other, axis=None)
- -
__or__ = wrapper(self, other)
- -
__pow__ = wrapper(left, right, name='__pow__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33fea0>)
- -
__radd__ = wrapper(left, right, name='__radd__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f510>)
- -
__rand__ = wrapper(self, other)
- -
__rdiv__ = wrapper(left, right, name='__rtruediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342510>)
- -
__rfloordiv__ = wrapper(left, right, name='__rfloordiv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342730>)
- -
__rmod__ = wrapper(left, right, name='__rmod__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342b70>)
- -
__rmul__ = wrapper(left, right, name='__rmul__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca3420d0>)
- -
__ror__ = wrapper(self, other)
- -
__rpow__ = wrapper(left, right, name='__rpow__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342950>)
- -
__rsub__ = wrapper(left, right, name='__rsub__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca3422f0>)
- -
__rtruediv__ = wrapper(left, right, name='__rtruediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca342510>)
- -
__rxor__ = wrapper(self, other)
- -
__setitem__(self, key, value)
- -
__sub__ = wrapper(left, right, name='__sub__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f6a8>)
- -
__truediv__ = wrapper(left, right, name='__truediv__', na_op=<function _arith_method_SERIES.<locals>.na_op at 0x7f6aca33f9d8>)
- -
__unicode__(self)
Return a string representation for a particular DataFrame

-Invoked by unicode(df) in py2 only. Yields a Unicode String in both
-py2/py3.
- -
__xor__ = wrapper(self, other)
- -
add(self, other, level=None, fill_value=None, axis=0)
Addition of series and other, element-wise (binary operator `add`).

-Equivalent to ``series + other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.radd
- -
agg = aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a Series or when passed to Series.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : Series

-Examples
---------

->>> s = Series(np.random.randn(10))

->>> s.agg('min')
--1.3018049988556679

->>> s.agg(['min', 'max'])
-min   -1.301805
-max    1.127688
-dtype: float64

-See also
---------
-pandas.Series.apply
-pandas.Series.transform
- -
aggregate(self, func, axis=0, *args, **kwargs)
Aggregate using callable, string, dict, or list of string/callables

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    Function to use for aggregating the data. If a function, must either
-    work when passed a Series or when passed to Series.apply. For
-    a DataFrame, can pass a dict, if the keys are DataFrame column names.

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Notes
------
-Numpy functions mean/median/prod/sum/std/var are special cased so the
-default behavior is applying the function along axis=0
-(e.g., np.mean(arr_2d, axis=0)) as opposed to
-mimicking the default Numpy behavior (e.g., np.mean(arr_2d)).

-agg is an alias for aggregate. Use it.

-Returns
--------
-aggregated : Series

-Examples
---------

->>> s = Series(np.random.randn(10))

->>> s.agg('min')
--1.3018049988556679

->>> s.agg(['min', 'max'])
-min   -1.301805
-max    1.127688
-dtype: float64

-See also
---------
-pandas.Series.apply
-pandas.Series.transform
- -
align(self, other, join='outer', axis=None, level=None, copy=True, fill_value=None, method=None, limit=None, fill_axis=0, broadcast_axis=None)
Align two object on their axes with the
-specified join method for each axis Index

-Parameters
-----------
-other : DataFrame or Series
-join : {'outer', 'inner', 'left', 'right'}, default 'outer'
-axis : allowed axis of the other object, default None
-    Align on index (0), columns (1), or both (None)
-level : int or level name, default None
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-copy : boolean, default True
-    Always returns new objects. If copy=False and no reindexing is
-    required then original objects are returned.
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-method : str, default None
-limit : int, default None
-fill_axis : {0, 'index'}, default 0
-    Filling axis, method and limit
-broadcast_axis : {0, 'index'}, default None
-    Broadcast values along this axis, if aligning two objects of
-    different dimensions

-    .. versionadded:: 0.17.0

-Returns
--------
-(left, right) : (Series, type of other)
-    Aligned objects
- -
all(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether all elements are True over requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-all : scalar or Series (if level specified)
- -
any(self, axis=None, bool_only=None, skipna=None, level=None, **kwargs)
Return whether any element is True over requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-bool_only : boolean, default None
-    Include only boolean columns. If None, will attempt to use everything,
-    then use only boolean data. Not implemented for Series.

-Returns
--------
-any : scalar or Series (if level specified)
- -
append(self, to_append, ignore_index=False, verify_integrity=False)
Concatenate two or more Series.

-Parameters
-----------
-to_append : Series or list/tuple of Series
-ignore_index : boolean, default False
-    If True, do not use the index labels.

-    .. versionadded: 0.19.0

-verify_integrity : boolean, default False
-    If True, raise Exception on creating index with duplicates

-Returns
--------
-appended : Series

-Examples
---------
->>> s1 = pd.Series([1, 2, 3])
->>> s2 = pd.Series([4, 5, 6])
->>> s3 = pd.Series([4, 5, 6], index=[3,4,5])
->>> s1.append(s2)
-0    1
-1    2
-2    3
-0    4
-1    5
-2    6
-dtype: int64

->>> s1.append(s3)
-0    1
-1    2
-2    3
-3    4
-4    5
-5    6
-dtype: int64

-With `ignore_index` set to True:

->>> s1.append(s2, ignore_index=True)
-0    1
-1    2
-2    3
-3    4
-4    5
-5    6
-dtype: int64

-With `verify_integrity` set to True:

->>> s1.append(s2, verify_integrity=True)
-Traceback (most recent call last):
-...
-ValueError: Indexes have overlapping values: [0, 1, 2]
- -
apply(self, func, convert_dtype=True, args=(), **kwds)
Invoke function on values of Series. Can be ufunc (a NumPy function
-that applies to the entire Series) or a Python function that only works
-on single values

-Parameters
-----------
-func : function
-convert_dtype : boolean, default True
-    Try to find better dtype for elementwise function results. If
-    False, leave as dtype=object
-args : tuple
-    Positional arguments to pass to function in addition to the value
-Additional keyword arguments will be passed as keywords to the function

-Returns
--------
-y : Series or DataFrame if func returns a Series

-See also
---------
-Series.map: For element-wise operations
-Series.agg: only perform aggregating type operations
-Series.transform: only perform transformating type operations

-Examples
---------

-Create a series with typical summer temperatures for each city.

->>> import pandas as pd
->>> import numpy as np
->>> series = pd.Series([20, 21, 12], index=['London',
-... 'New York','Helsinki'])
->>> series
-London      20
-New York    21
-Helsinki    12
-dtype: int64

-Square the values by defining a function and passing it as an
-argument to ``apply()``.

->>> def square(x):
-...     return x**2
->>> series.apply(square)
-London      400
-New York    441
-Helsinki    144
-dtype: int64

-Square the values by passing an anonymous function as an
-argument to ``apply()``.

->>> series.apply(lambda x: x**2)
-London      400
-New York    441
-Helsinki    144
-dtype: int64

-Define a custom function that needs additional positional
-arguments and pass these additional arguments using the
-``args`` keyword.

->>> def subtract_custom_value(x, custom_value):
-...     return x-custom_value

->>> series.apply(subtract_custom_value, args=(5,))
-London      15
-New York    16
-Helsinki     7
-dtype: int64

-Define a custom function that takes keyword arguments
-and pass these arguments to ``apply``.

->>> def add_custom_values(x, **kwargs):
-...     for month in kwargs:
-...         x+=kwargs[month]
-...         return x

->>> series.apply(add_custom_values, june=30, july=20, august=25)
-London      95
-New York    96
-Helsinki    87
-dtype: int64

-Use a function from the Numpy library.

->>> series.apply(np.log)
-London      2.995732
-New York    3.044522
-Helsinki    2.484907
-dtype: float64
- -
argmax = idxmax(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of maximum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmax : Index of maximum of values

-Notes
------
-This method is the Series version of ``ndarray.argmax``.

-See Also
---------
-DataFrame.idxmax
-numpy.ndarray.argmax
- -
argmin = idxmin(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of minimum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmin : Index of minimum of values

-Notes
------
-This method is the Series version of ``ndarray.argmin``.

-See Also
---------
-DataFrame.idxmin
-numpy.ndarray.argmin
- -
argsort(self, axis=0, kind='quicksort', order=None)
Overrides ndarray.argsort. Argsorts the value, omitting NA/null values,
-and places the result in the same locations as the non-NA values

-Parameters
-----------
-axis : int (can only be zero)
-kind : {'mergesort', 'quicksort', 'heapsort'}, default 'quicksort'
-    Choice of sorting algorithm. See np.sort for more
-    information. 'mergesort' is the only stable algorithm
-order : ignored

-Returns
--------
-argsorted : Series, with -1 indicated where nan values are present

-See also
---------
-numpy.ndarray.argsort
- -
autocorr(self, lag=1)
Lag-N autocorrelation

-Parameters
-----------
-lag : int, default 1
-    Number of lags to apply before performing autocorrelation.

-Returns
--------
-autocorr : float
- -
between(self, left, right, inclusive=True)
Return boolean Series equivalent to left <= series <= right. NA values
-will be treated as False

-Parameters
-----------
-left : scalar
-    Left boundary
-right : scalar
-    Right boundary

-Returns
--------
-is_between : Series
- -
combine(self, other, func, fill_value=nan)
Perform elementwise binary operation on two Series using given function
-with optional fill value when an index is missing from one Series or
-the other

-Parameters
-----------
-other : Series or scalar value
-func : function
-fill_value : scalar value

-Returns
--------
-result : Series
- -
combine_first(self, other)
Combine Series values, choosing the calling Series's values
-first. Result index will be the union of the two indexes

-Parameters
-----------
-other : Series

-Returns
--------
-y : Series
- -
compound(self, axis=None, skipna=None, level=None)
Return the compound percentage of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-compounded : scalar or Series (if level specified)
- -
compress(self, condition, *args, **kwargs)
Return selected slices of an array along given axis as a Series

-See also
---------
-numpy.ndarray.compress
- -
corr(self, other, method='pearson', min_periods=None)
Compute correlation with `other` Series, excluding missing values

-Parameters
-----------
-other : Series
-method : {'pearson', 'kendall', 'spearman'}
-    * pearson : standard correlation coefficient
-    * kendall : Kendall Tau correlation coefficient
-    * spearman : Spearman rank correlation
-min_periods : int, optional
-    Minimum number of observations needed to have a valid result


-Returns
--------
-correlation : float
- -
count(self, level=None)
Return number of non-NA/null observations in the Series

-Parameters
-----------
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a smaller Series

-Returns
--------
-nobs : int or Series (if level specified)
- -
cov(self, other, min_periods=None)
Compute covariance with Series, excluding missing values

-Parameters
-----------
-other : Series
-min_periods : int, optional
-    Minimum number of observations needed to have a valid result

-Returns
--------
-covariance : float

-Normalized by N-1 (unbiased estimator).
- -
cummax(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative max over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummax : scalar



-See also
---------
-pandas.core.window.Expanding.max : Similar functionality
-    but ignores ``NaN`` values.
- -
cummin(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative minimum over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cummin : scalar



-See also
---------
-pandas.core.window.Expanding.min : Similar functionality
-    but ignores ``NaN`` values.
- -
cumprod(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative product over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumprod : scalar



-See also
---------
-pandas.core.window.Expanding.prod : Similar functionality
-    but ignores ``NaN`` values.
- -
cumsum(self, axis=None, skipna=True, *args, **kwargs)
Return cumulative sum over requested axis.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA

-Returns
--------
-cumsum : scalar



-See also
---------
-pandas.core.window.Expanding.sum : Similar functionality
-    but ignores ``NaN`` values.
- -
diff(self, periods=1)
1st discrete difference of object

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming difference

-Returns
--------
-diffed : Series
- -
div = truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
divide = truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
dot(self, other)
Matrix multiplication with DataFrame or inner-product with Series
-objects

-Parameters
-----------
-other : Series or DataFrame

-Returns
--------
-dot_product : scalar or Series
- -
drop_duplicates(self, keep='first', inplace=False)
Return Series with duplicate values removed

-Parameters
-----------

-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Drop duplicates except for the first occurrence.
-    - ``last`` : Drop duplicates except for the last occurrence.
-    - False : Drop all duplicates.
-inplace : boolean, default False
-If True, performs operation inplace and returns None.

-Returns
--------
-deduplicated : Series
- -
dropna(self, axis=0, inplace=False, **kwargs)
Return Series without null values

-Returns
--------
-valid : Series
-inplace : boolean, default False
-    Do operation in place.
- -
duplicated(self, keep='first')
Return boolean Series denoting duplicate values

-Parameters
-----------
-keep : {'first', 'last', False}, default 'first'
-    - ``first`` : Mark duplicates as ``True`` except for the first
-      occurrence.
-    - ``last`` : Mark duplicates as ``True`` except for the last
-      occurrence.
-    - False : Mark all duplicates as ``True``.

-Returns
--------
-duplicated : Series
- -
eq(self, other, level=None, fill_value=None, axis=0)
Equal to of series and other, element-wise (binary operator `eq`).

-Equivalent to ``series == other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
ewm(self, com=None, span=None, halflife=None, alpha=None, min_periods=0, freq=None, adjust=True, ignore_na=False, axis=0)
Provides exponential weighted functions

-.. versionadded:: 0.18.0

-Parameters
-----------
-com : float, optional
-    Specify decay in terms of center of mass,
-    :math:`\alpha = 1 / (1 + com),\text{ for } com \geq 0`
-span : float, optional
-    Specify decay in terms of span,
-    :math:`\alpha = 2 / (span + 1),\text{ for } span \geq 1`
-halflife : float, optional
-    Specify decay in terms of half-life,
-    :math:`\alpha = 1 - exp(log(0.5) / halflife),\text{ for } halflife > 0`
-alpha : float, optional
-    Specify smoothing factor :math:`\alpha` directly,
-    :math:`0 < \alpha \leq 1`

-    .. versionadded:: 0.18.0

-min_periods : int, default 0
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : None or string alias / date offset object, default=None (DEPRECATED)
-    Frequency to conform to before computing statistic
-adjust : boolean, default True
-    Divide by decaying adjustment factor in beginning periods to account
-    for imbalance in relative weightings (viewing EWMA as a moving average)
-ignore_na : boolean, default False
-    Ignore missing values when calculating weights;
-    specify True to reproduce pre-0.15.0 behavior

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.ewm(com=0.5).mean()
-          B
-0  0.000000
-1  0.750000
-2  1.615385
-3  1.615385
-4  3.670213

-Notes
------
-Exactly one of center of mass, span, half-life, and alpha must be provided.
-Allowed values and relationship between the parameters are specified in the
-parameter descriptions above; see the link at the end of this section for
-a detailed explanation.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-When adjust is True (default), weighted averages are calculated using
-weights (1-alpha)**(n-1), (1-alpha)**(n-2), ..., 1-alpha, 1.

-When adjust is False, weighted averages are calculated recursively as:
-   weighted_average[0] = arg[0];
-   weighted_average[i] = (1-alpha)*weighted_average[i-1] + alpha*arg[i].

-When ignore_na is False (default), weights are based on absolute positions.
-For example, the weights of x and y used in calculating the final weighted
-average of [x, None, y] are (1-alpha)**2 and 1 (if adjust is True), and
-(1-alpha)**2 and alpha (if adjust is False).

-When ignore_na is True (reproducing pre-0.15.0 behavior), weights are based
-on relative positions. For example, the weights of x and y used in
-calculating the final weighted average of [x, None, y] are 1-alpha and 1
-(if adjust is True), and 1-alpha and alpha (if adjust is False).

-More details can be found at
-http://pandas.pydata.org/pandas-docs/stable/computation.html#exponentially-weighted-windows
- -
expanding(self, min_periods=1, freq=None, center=False, axis=0)
Provides expanding transformations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA).
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-axis : int or string, default 0

-Returns
--------
-a Window sub-classed for the particular operation

-Examples
---------

->>> df = DataFrame({'B': [0, 1, 2, np.nan, 4]})
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

->>> df.expanding(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  3.0
-4  7.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).
- -
fillna(self, value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)
Fill NA/NaN values using the specified method

-Parameters
-----------
-value : scalar, dict, Series, or DataFrame
-    Value to use to fill holes (e.g. 0), alternately a
-    dict/Series/DataFrame of values specifying which value to use for
-    each index (for a Series) or column (for a DataFrame). (values not
-    in the dict/Series/DataFrame will not be filled). This value cannot
-    be a list.
-method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
-    Method to use for filling holes in reindexed Series
-    pad / ffill: propagate last valid observation forward to next valid
-    backfill / bfill: use NEXT valid observation to fill gap
-axis : {0, 'index'}
-inplace : boolean, default False
-    If True, fill in place. Note: this will modify any
-    other views on this object, (e.g. a no-copy slice for a column in a
-    DataFrame).
-limit : int, default None
-    If method is specified, this is the maximum number of consecutive
-    NaN values to forward/backward fill. In other words, if there is
-    a gap with more than this number of consecutive NaNs, it will only
-    be partially filled. If method is not specified, this is the
-    maximum number of entries along the entire axis where NaNs will be
-    filled. Must be greater than 0 if not None.
-downcast : dict, default is None
-    a dict of item->dtype of what to downcast if possible,
-    or the string 'infer' which will try to downcast to an appropriate
-    equal type (e.g. float64 to int64 if possible)

-See Also
---------
-reindex, asfreq

-Returns
--------
-filled : Series
- -
first_valid_index(self)
Return label for first non-NA/null value
- -
floordiv(self, other, level=None, fill_value=None, axis=0)
Integer division of series and other, element-wise (binary operator `floordiv`).

-Equivalent to ``series // other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rfloordiv
- -
ge(self, other, level=None, fill_value=None, axis=0)
Greater than or equal to of series and other, element-wise (binary operator `ge`).

-Equivalent to ``series >= other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
get_value(self, label, takeable=False)
Quickly retrieve single value at passed index label

-Parameters
-----------
-index : label
-takeable : interpret the index as indexers, default False

-Returns
--------
-value : scalar value
- -
get_values(self)
same as values (but handles sparseness conversions); is a view
- -
gt(self, other, level=None, fill_value=None, axis=0)
Greater than of series and other, element-wise (binary operator `gt`).

-Equivalent to ``series > other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
hist = hist_series(self, by=None, ax=None, grid=True, xlabelsize=None, xrot=None, ylabelsize=None, yrot=None, figsize=None, bins=10, **kwds)
Draw histogram of the input series using matplotlib

-Parameters
-----------
-by : object, optional
-    If passed, then used to form histograms for separate groups
-ax : matplotlib axis object
-    If not passed, uses gca()
-grid : boolean, default True
-    Whether to show axis grid lines
-xlabelsize : int, default None
-    If specified changes the x-axis label size
-xrot : float, default None
-    rotation of x axis labels
-ylabelsize : int, default None
-    If specified changes the y-axis label size
-yrot : float, default None
-    rotation of y axis labels
-figsize : tuple, default None
-    figure size in inches by default
-bins: integer, default 10
-    Number of histogram bins to be used
-kwds : keywords
-    To be passed to the actual plotting function

-Notes
------
-See matplotlib documentation online for more on this
- -
idxmax(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of maximum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmax : Index of maximum of values

-Notes
------
-This method is the Series version of ``ndarray.argmax``.

-See Also
---------
-DataFrame.idxmax
-numpy.ndarray.argmax
- -
idxmin(self, axis=None, skipna=True, *args, **kwargs)
Index of first occurrence of minimum of values.

-Parameters
-----------
-skipna : boolean, default True
-    Exclude NA/null values

-Returns
--------
-idxmin : Index of minimum of values

-Notes
------
-This method is the Series version of ``ndarray.argmin``.

-See Also
---------
-DataFrame.idxmin
-numpy.ndarray.argmin
- -
isin(self, values)
Return a boolean :class:`~pandas.Series` showing whether each element
-in the :class:`~pandas.Series` is exactly contained in the passed
-sequence of ``values``.

-Parameters
-----------
-values : set or list-like
-    The sequence of values to test. Passing in a single string will
-    raise a ``TypeError``. Instead, turn a single string into a
-    ``list`` of one element.

-    .. versionadded:: 0.18.1

-    Support for values as a set

-Returns
--------
-isin : Series (bool dtype)

-Raises
-------
-TypeError
-  * If ``values`` is a string

-See Also
---------
-pandas.DataFrame.isin

-Examples
---------

->>> s = pd.Series(list('abc'))
->>> s.isin(['a', 'c', 'e'])
-0     True
-1    False
-2     True
-dtype: bool

-Passing a single string as ``s.isin('a')`` will raise an error. Use
-a list of one element instead:

->>> s.isin(['a'])
-0     True
-1    False
-2    False
-dtype: bool
- -
items = iteritems(self)
Lazily iterate over (index, value) tuples
- -
iteritems(self)
Lazily iterate over (index, value) tuples
- -
keys(self)
Alias for index
- -
kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : scalar or Series (if level specified)
- -
kurtosis = kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased kurtosis over requested axis using Fisher's definition of
-kurtosis (kurtosis of normal == 0.0). Normalized by N-1


-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-kurt : scalar or Series (if level specified)
- -
last_valid_index(self)
Return label for last non-NA/null value
- -
le(self, other, level=None, fill_value=None, axis=0)
Less than or equal to of series and other, element-wise (binary operator `le`).

-Equivalent to ``series <= other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
lt(self, other, level=None, fill_value=None, axis=0)
Less than of series and other, element-wise (binary operator `lt`).

-Equivalent to ``series < other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
mad(self, axis=None, skipna=None, level=None)
Return the mean absolute deviation of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mad : scalar or Series (if level specified)
- -
map(self, arg, na_action=None)
Map values of Series using input correspondence (which can be
-a dict, Series, or function)

-Parameters
-----------
-arg : function, dict, or Series
-na_action : {None, 'ignore'}
-    If 'ignore', propagate NA values, without passing them to the
-    mapping function

-Returns
--------
-y : Series
-    same index as caller

-Examples
---------

-Map inputs to outputs (both of type `Series`)

->>> x = pd.Series([1,2,3], index=['one', 'two', 'three'])
->>> x
-one      1
-two      2
-three    3
-dtype: int64

->>> y = pd.Series(['foo', 'bar', 'baz'], index=[1,2,3])
->>> y
-1    foo
-2    bar
-3    baz

->>> x.map(y)
-one   foo
-two   bar
-three baz

-If `arg` is a dictionary, return a new Series with values converted
-according to the dictionary's mapping:

->>> z = {1: 'A', 2: 'B', 3: 'C'}

->>> x.map(z)
-one   A
-two   B
-three C

-Use na_action to control whether NA values are affected by the mapping
-function.

->>> s = pd.Series([1, 2, 3, np.nan])

->>> s2 = s.map('this is a string {}'.format, na_action=None)
-0    this is a string 1.0
-1    this is a string 2.0
-2    this is a string 3.0
-3    this is a string nan
-dtype: object

->>> s3 = s.map('this is a string {}'.format, na_action='ignore')
-0    this is a string 1.0
-1    this is a string 2.0
-2    this is a string 3.0
-3                     NaN
-dtype: object

-See Also
---------
-Series.apply: For applying more complex functions on a Series
-DataFrame.apply: Apply a function row-/column-wise
-DataFrame.applymap: Apply a function elementwise on a whole DataFrame

-Notes
------
-When `arg` is a dictionary, values in Series that are not in the
-dictionary (as keys) are converted to ``NaN``. However, if the
-dictionary is a ``dict`` subclass that defines ``__missing__`` (i.e.
-provides a method for default values), then this default is used
-rather than ``NaN``:

->>> from collections import Counter
->>> counter = Counter()
->>> counter['bar'] += 1
->>> y.map(counter)
-1    0
-2    1
-3    0
-dtype: int64
- -
max(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the maximum of the values in the object.
-            If you want the *index* of the maximum, use ``idxmax``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmax``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-max : scalar or Series (if level specified)
- -
mean(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the mean of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-mean : scalar or Series (if level specified)
- -
median(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the median of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-median : scalar or Series (if level specified)
- -
memory_usage(self, index=True, deep=False)
Memory usage of the Series

-Parameters
-----------
-index : bool
-    Specifies whether to include memory usage of Series index
-deep : bool
-    Introspect the data deeply, interrogate
-    `object` dtypes for system-level memory consumption

-Returns
--------
-scalar bytes of memory consumed

-Notes
------
-Memory usage does not include memory consumed by elements that
-are not components of the array if deep=False

-See Also
---------
-numpy.ndarray.nbytes
- -
min(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
This method returns the minimum of the values in the object.
-            If you want the *index* of the minimum, use ``idxmin``. This is
-            the equivalent of the ``numpy.ndarray`` method ``argmin``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-min : scalar or Series (if level specified)
- -
mod(self, other, level=None, fill_value=None, axis=0)
Modulo of series and other, element-wise (binary operator `mod`).

-Equivalent to ``series % other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmod
- -
mode(self)
Return the mode(s) of the dataset.

-Always returns Series even if only one value is returned.

-Returns
--------
-modes : Series (sorted)
- -
mul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `mul`).

-Equivalent to ``series * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmul
- -
multiply = mul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `mul`).

-Equivalent to ``series * other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rmul
- -
ne(self, other, level=None, fill_value=None, axis=0)
Not equal to of series and other, element-wise (binary operator `ne`).

-Equivalent to ``series != other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.None
- -
nlargest(self, n=5, keep='first')
Return the largest `n` elements.

-Parameters
-----------
-n : int
-    Return this many descending sorted values
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-top_n : Series
-    The n largest values in the Series, in sorted order

-Notes
------
-Faster than ``.sort_values(ascending=False).head(n)`` for small `n`
-relative to the size of the ``Series`` object.

-See Also
---------
-Series.nsmallest

-Examples
---------
->>> import pandas as pd
->>> import numpy as np
->>> s = pd.Series(np.random.randn(10**6))
->>> s.nlargest(10)  # only sorts up to the N requested
-219921    4.644710
-82124     4.608745
-421689    4.564644
-425277    4.447014
-718691    4.414137
-43154     4.403520
-283187    4.313922
-595519    4.273635
-503969    4.250236
-121637    4.240952
-dtype: float64
- -
nonzero(self)
Return the indices of the elements that are non-zero

-This method is equivalent to calling `numpy.nonzero` on the
-series data. For compatability with NumPy, the return value is
-the same (a tuple with an array of indices for each dimension),
-but it will always be a one-item tuple because series only have
-one dimension.

-Examples
---------
->>> s = pd.Series([0, 3, 0, 4])
->>> s.nonzero()
-(array([1, 3]),)
->>> s.iloc[s.nonzero()[0]]
-1    3
-3    4
-dtype: int64

-See Also
---------
-numpy.nonzero
- -
nsmallest(self, n=5, keep='first')
Return the smallest `n` elements.

-Parameters
-----------
-n : int
-    Return this many ascending sorted values
-keep : {'first', 'last', False}, default 'first'
-    Where there are duplicate values:
-    - ``first`` : take the first occurrence.
-    - ``last`` : take the last occurrence.

-Returns
--------
-bottom_n : Series
-    The n smallest values in the Series, in sorted order

-Notes
------
-Faster than ``.sort_values().head(n)`` for small `n` relative to
-the size of the ``Series`` object.

-See Also
---------
-Series.nlargest

-Examples
---------
->>> import pandas as pd
->>> import numpy as np
->>> s = pd.Series(np.random.randn(10**6))
->>> s.nsmallest(10)  # only sorts up to the N requested
-288532   -4.954580
-732345   -4.835960
-64803    -4.812550
-446457   -4.609998
-501225   -4.483945
-669476   -4.472935
-973615   -4.401699
-621279   -4.355126
-773916   -4.347355
-359919   -4.331927
-dtype: float64
- -
pow(self, other, level=None, fill_value=None, axis=0)
Exponential power of series and other, element-wise (binary operator `pow`).

-Equivalent to ``series ** other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rpow
- -
prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : scalar or Series (if level specified)
- -
product = prod(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the product of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-prod : scalar or Series (if level specified)
- -
ptp(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Returns the difference between the maximum value and the
-            minimum value in the object. This is the equivalent of the
-            ``numpy.ndarray`` method ``ptp``.

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-ptp : scalar or Series (if level specified)
- -
put(self, *args, **kwargs)
Applies the `put` method to its `values` attribute
-if it has one.

-See also
---------
-numpy.ndarray.put
- -
quantile(self, q=0.5, interpolation='linear')
Return value at the given quantile, a la numpy.percentile.

-Parameters
-----------
-q : float or array-like, default 0.5 (50% quantile)
-    0 <= q <= 1, the quantile(s) to compute
-interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'}
-    .. versionadded:: 0.18.0

-    This optional parameter specifies the interpolation method to use,
-    when the desired quantile lies between two data points `i` and `j`:

-        * linear: `i + (j - i) * fraction`, where `fraction` is the
-          fractional part of the index surrounded by `i` and `j`.
-        * lower: `i`.
-        * higher: `j`.
-        * nearest: `i` or `j` whichever is nearest.
-        * midpoint: (`i` + `j`) / 2.

-Returns
--------
-quantile : float or Series
-    if ``q`` is an array, a Series will be returned where the
-    index is ``q`` and the values are the quantiles.

-Examples
---------
->>> s = Series([1, 2, 3, 4])
->>> s.quantile(.5)
-2.5
->>> s.quantile([.25, .5, .75])
-0.25    1.75
-0.50    2.50
-0.75    3.25
-dtype: float64
- -
radd(self, other, level=None, fill_value=None, axis=0)
Addition of series and other, element-wise (binary operator `radd`).

-Equivalent to ``other + series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.add
- -
ravel(self, order='C')
Return the flattened underlying data as an ndarray

-See also
---------
-numpy.ndarray.ravel
- -
rdiv = rtruediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.truediv
- -
reindex(self, index=None, **kwargs)
Conform Series to new index with optional filling logic, placing
-NA/NaN in locations having no value in the previous index. A new object
-is produced unless the new index is equivalent to the current one and
-copy=False

-Parameters
-----------
-index : array-like, optional (can be specified in order, or as
-    keywords)
-    New labels / index to conform to. Preferably an Index object to
-    avoid duplicating data
-method : {None, 'backfill'/'bfill', 'pad'/'ffill', 'nearest'}, optional
-    method to use for filling holes in reindexed DataFrame.
-    Please note: this is only  applicable to DataFrames/Series with a
-    monotonically increasing/decreasing index.

-    * default: don't fill gaps
-    * pad / ffill: propagate last valid observation forward to next
-      valid
-    * backfill / bfill: use next valid observation to fill gap
-    * nearest: use nearest valid observations to fill gap

-copy : boolean, default True
-    Return a new object, even if the passed indexes are the same
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level
-fill_value : scalar, default np.NaN
-    Value to use for missing values. Defaults to NaN, but can be any
-    "compatible" value
-limit : int, default None
-    Maximum number of consecutive elements to forward or backward fill
-tolerance : optional
-    Maximum distance between original and new labels for inexact
-    matches. The values of the index at the matching locations most
-    satisfy the equation ``abs(index[indexer] - target) <= tolerance``.

-    .. versionadded:: 0.17.0

-Examples
---------

-Create a dataframe with some fictional data.

->>> index = ['Firefox', 'Chrome', 'Safari', 'IE10', 'Konqueror']
->>> df = pd.DataFrame({
-...      'http_status': [200,200,404,404,301],
-...      'response_time': [0.04, 0.02, 0.07, 0.08, 1.0]},
-...       index=index)
->>> df
-           http_status  response_time
-Firefox            200           0.04
-Chrome             200           0.02
-Safari             404           0.07
-IE10               404           0.08
-Konqueror          301           1.00

-Create a new index and reindex the dataframe. By default
-values in the new index that do not have corresponding
-records in the dataframe are assigned ``NaN``.

->>> new_index= ['Safari', 'Iceweasel', 'Comodo Dragon', 'IE10',
-...             'Chrome']
->>> df.reindex(new_index)
-               http_status  response_time
-Safari               404.0           0.07
-Iceweasel              NaN            NaN
-Comodo Dragon          NaN            NaN
-IE10                 404.0           0.08
-Chrome               200.0           0.02

-We can fill in the missing values by passing a value to
-the keyword ``fill_value``. Because the index is not monotonically
-increasing or decreasing, we cannot use arguments to the keyword
-``method`` to fill the ``NaN`` values.

->>> df.reindex(new_index, fill_value=0)
-               http_status  response_time
-Safari                 404           0.07
-Iceweasel                0           0.00
-Comodo Dragon            0           0.00
-IE10                   404           0.08
-Chrome                 200           0.02

->>> df.reindex(new_index, fill_value='missing')
-              http_status response_time
-Safari                404          0.07
-Iceweasel         missing       missing
-Comodo Dragon     missing       missing
-IE10                  404          0.08
-Chrome                200          0.02

-To further illustrate the filling functionality in
-``reindex``, we will create a dataframe with a
-monotonically increasing index (for example, a sequence
-of dates).

->>> date_index = pd.date_range('1/1/2010', periods=6, freq='D')
->>> df2 = pd.DataFrame({"prices": [100, 101, np.nan, 100, 89, 88]},
-...                    index=date_index)
->>> df2
-            prices
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88

-Suppose we decide to expand the dataframe to cover a wider
-date range.

->>> date_index2 = pd.date_range('12/29/2009', periods=10, freq='D')
->>> df2.reindex(date_index2)
-            prices
-2009-12-29     NaN
-2009-12-30     NaN
-2009-12-31     NaN
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-The index entries that did not have a value in the original data frame
-(for example, '2009-12-29') are by default filled with ``NaN``.
-If desired, we can fill in the missing values using one of several
-options.

-For example, to backpropagate the last valid value to fill the ``NaN``
-values, pass ``bfill`` as an argument to the ``method`` keyword.

->>> df2.reindex(date_index2, method='bfill')
-            prices
-2009-12-29     100
-2009-12-30     100
-2009-12-31     100
-2010-01-01     100
-2010-01-02     101
-2010-01-03     NaN
-2010-01-04     100
-2010-01-05      89
-2010-01-06      88
-2010-01-07     NaN

-Please note that the ``NaN`` value present in the original dataframe
-(at index value 2010-01-03) will not be filled by any of the
-value propagation schemes. This is because filling while reindexing
-does not look at dataframe values, but only compares the original and
-desired indexes. If you do want to fill in the ``NaN`` values present
-in the original dataframe, use the ``fillna()`` method.

-Returns
--------
-reindexed : Series
- -
reindex_axis(self, labels, axis=0, **kwargs)
for compatibility with higher dims
- -
rename(self, index=None, **kwargs)
Alter axes input function or functions. Function / dict values must be
-unique (1-to-1). Labels not contained in a dict / Series will be left
-as-is. Extra labels listed don't throw an error. Alternatively, change
-``Series.name`` with a scalar value (Series only).

-Parameters
-----------
-index : scalar, list-like, dict-like or function, optional
-    Scalar or list-like will alter the ``Series.name`` attribute,
-    and raise on DataFrame or Panel.
-    dict-like or functions are transformations to apply to
-    that axis' values
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False
-    Whether to return a new Series. If True then value of copy is
-    ignored.
-level : int or level name, default None
-    In case of a MultiIndex, only rename labels in the specified
-    level.

-Returns
--------
-renamed : Series (new object)

-See Also
---------
-pandas.NDFrame.rename_axis

-Examples
---------
->>> s = pd.Series([1, 2, 3])
->>> s
-0    1
-1    2
-2    3
-dtype: int64
->>> s.rename("my_name") # scalar, changes Series.name
-0    1
-1    2
-2    3
-Name: my_name, dtype: int64
->>> s.rename(lambda x: x ** 2)  # function, changes labels
-0    1
-1    2
-4    3
-dtype: int64
->>> s.rename({1: 3, 2: 5})  # mapping, changes labels
-0    1
-3    2
-5    3
-dtype: int64
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename(2)
-Traceback (most recent call last):
-...
-TypeError: 'int' object is not callable
->>> df.rename(index=str, columns={"A": "a", "B": "c"})
-   a  c
-0  1  4
-1  2  5
-2  3  6
->>> df.rename(index=str, columns={"A": "a", "C": "c"})
-   a  B
-0  1  4
-1  2  5
-2  3  6
- -
reorder_levels(self, order)
Rearrange index levels using input order. May not drop or duplicate
-levels

-Parameters
-----------
-order : list of int representing new level order.
-       (reference level by number or key)
-axis : where to reorder levels

-Returns
--------
-type of caller (new object)
- -
repeat(self, repeats, *args, **kwargs)
Repeat elements of an Series. Refer to `numpy.ndarray.repeat`
-for more information about the `repeats` argument.

-See also
---------
-numpy.ndarray.repeat
- -
reset_index(self, level=None, drop=False, name=None, inplace=False)
Analogous to the :meth:`pandas.DataFrame.reset_index` function, see
-docstring there.

-Parameters
-----------
-level : int, str, tuple, or list, default None
-    Only remove the given levels from the index. Removes all levels by
-    default
-drop : boolean, default False
-    Do not try to insert index into dataframe columns
-name : object, default None
-    The name of the column corresponding to the Series values
-inplace : boolean, default False
-    Modify the Series in place (do not create a new object)

-Returns
-----------
-resetted : DataFrame, or Series if drop == True
- -
reshape(self, *args, **kwargs)
DEPRECATED: calling this method will raise an error in a
-future release. Please call ``.values.reshape(...)`` instead.

-return an ndarray with the values shape
-if the specified shape matches exactly the current shape, then
-return self (for compat)

-See also
---------
-numpy.ndarray.reshape
- -
rfloordiv(self, other, level=None, fill_value=None, axis=0)
Integer division of series and other, element-wise (binary operator `rfloordiv`).

-Equivalent to ``other // series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.floordiv
- -
rmod(self, other, level=None, fill_value=None, axis=0)
Modulo of series and other, element-wise (binary operator `rmod`).

-Equivalent to ``other % series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.mod
- -
rmul(self, other, level=None, fill_value=None, axis=0)
Multiplication of series and other, element-wise (binary operator `rmul`).

-Equivalent to ``other * series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.mul
- -
rolling(self, window, min_periods=None, freq=None, center=False, win_type=None, on=None, axis=0, closed=None)
Provides rolling window calculcations.

-.. versionadded:: 0.18.0

-Parameters
-----------
-window : int, or offset
-    Size of the moving window. This is the number of observations used for
-    calculating the statistic. Each window will be a fixed size.

-    If its an offset then this will be the time period of each window. Each
-    window will be a variable sized based on the observations included in
-    the time-period. This is only valid for datetimelike indexes. This is
-    new in 0.19.0
-min_periods : int, default None
-    Minimum number of observations in window required to have a value
-    (otherwise result is NA). For a window that is specified by an offset,
-    this will default to 1.
-freq : string or DateOffset object, optional (default None) (DEPRECATED)
-    Frequency to conform the data to before computing the statistic.
-    Specified as a frequency string or DateOffset object.
-center : boolean, default False
-    Set the labels at the center of the window.
-win_type : string, default None
-    Provide a window type. See the notes below.
-on : string, optional
-    For a DataFrame, column on which to calculate
-    the rolling window, rather than the index
-closed : string, default None
-    Make the interval closed on the 'right', 'left', 'both' or
-    'neither' endpoints.
-    For offset-based windows, it defaults to 'right'.
-    For fixed windows, defaults to 'both'. Remaining cases not implemented
-    for fixed windows.

-    .. versionadded:: 0.20.0

-axis : int or string, default 0

-Returns
--------
-a Window or Rolling sub-classed for the particular operation

-Examples
---------

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]})
->>> df
-     B
-0  0.0
-1  1.0
-2  2.0
-3  NaN
-4  4.0

-Rolling sum with a window length of 2, using the 'triang'
-window type.

->>> df.rolling(2, win_type='triang').sum()
-     B
-0  NaN
-1  1.0
-2  2.5
-3  NaN
-4  NaN

-Rolling sum with a window length of 2, min_periods defaults
-to the window length.

->>> df.rolling(2).sum()
-     B
-0  NaN
-1  1.0
-2  3.0
-3  NaN
-4  NaN

-Same as above, but explicity set the min_periods

->>> df.rolling(2, min_periods=1).sum()
-     B
-0  0.0
-1  1.0
-2  3.0
-3  2.0
-4  4.0

-A ragged (meaning not-a-regular frequency), time-indexed DataFrame

->>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]},
-....:                 index = [pd.Timestamp('20130101 09:00:00'),
-....:                          pd.Timestamp('20130101 09:00:02'),
-....:                          pd.Timestamp('20130101 09:00:03'),
-....:                          pd.Timestamp('20130101 09:00:05'),
-....:                          pd.Timestamp('20130101 09:00:06')])

->>> df
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  2.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0


-Contrasting to an integer rolling window, this will roll a variable
-length window corresponding to the time period.
-The default for min_periods is 1.

->>> df.rolling('2s').sum()
-                       B
-2013-01-01 09:00:00  0.0
-2013-01-01 09:00:02  1.0
-2013-01-01 09:00:03  3.0
-2013-01-01 09:00:05  NaN
-2013-01-01 09:00:06  4.0

-Notes
------
-By default, the result is set to the right edge of the window. This can be
-changed to the center of the window by setting ``center=True``.

-The `freq` keyword is used to conform time series data to a specified
-frequency by resampling the data. This is done with the default parameters
-of :meth:`~pandas.Series.resample` (i.e. using the `mean`).

-To learn more about the offsets & frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-The recognized win_types are:

-* ``boxcar``
-* ``triang``
-* ``blackman``
-* ``hamming``
-* ``bartlett``
-* ``parzen``
-* ``bohman``
-* ``blackmanharris``
-* ``nuttall``
-* ``barthann``
-* ``kaiser`` (needs beta)
-* ``gaussian`` (needs std)
-* ``general_gaussian`` (needs power, width)
-* ``slepian`` (needs width).
- -
round(self, decimals=0, *args, **kwargs)
Round each value in a Series to the given number of decimals.

-Parameters
-----------
-decimals : int
-    Number of decimal places to round to (default: 0).
-    If decimals is negative, it specifies the number of
-    positions to the left of the decimal point.

-Returns
--------
-Series object

-See Also
---------
-numpy.around
-DataFrame.round
- -
rpow(self, other, level=None, fill_value=None, axis=0)
Exponential power of series and other, element-wise (binary operator `rpow`).

-Equivalent to ``other ** series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.pow
- -
rsub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `rsub`).

-Equivalent to ``other - series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.sub
- -
rtruediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `rtruediv`).

-Equivalent to ``other / series``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.truediv
- -
searchsorted(self, value, side='left', sorter=None)
Find indices where elements should be inserted to maintain order.

-Find the indices into a sorted Series `self` such that, if the
-corresponding elements in `value` were inserted before the indices,
-the order of `self` would be preserved.

-Parameters
-----------
-value : array_like
-    Values to insert into `self`.
-side : {'left', 'right'}, optional
-    If 'left', the index of the first suitable location found is given.
-    If 'right', return the last such index.  If there is no suitable
-    index, return either 0 or N (where N is the length of `self`).
-sorter : 1-D array_like, optional
-    Optional array of integer indices that sort `self` into ascending
-    order. They are typically the result of ``np.argsort``.

-Returns
--------
-indices : array of ints
-    Array of insertion points with the same shape as `value`.

-See Also
---------
-numpy.searchsorted

-Notes
------
-Binary search is used to find the required insertion points.

-Examples
---------

->>> x = pd.Series([1, 2, 3])
->>> x
-0    1
-1    2
-2    3
-dtype: int64

->>> x.searchsorted(4)
-array([3])

->>> x.searchsorted([0, 4])
-array([0, 3])

->>> x.searchsorted([1, 3], side='left')
-array([0, 2])

->>> x.searchsorted([1, 3], side='right')
-array([1, 3])

->>> x = pd.Categorical(['apple', 'bread', 'bread', 'cheese', 'milk' ])
-[apple, bread, bread, cheese, milk]
-Categories (4, object): [apple < bread < cheese < milk]

->>> x.searchsorted('bread')
-array([1])     # Note: an array, not a scalar

->>> x.searchsorted(['bread'])
-array([1])

->>> x.searchsorted(['bread', 'eggs'])
-array([1, 4])

->>> x.searchsorted(['bread', 'eggs'], side='right')
-array([3, 4])    # eggs before milk
- -
sem(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased standard error of the mean over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sem : scalar or Series (if level specified)
- -
set_value(self, label, value, takeable=False)
Quickly set single value at passed label. If label is not contained, a
-new object is created with the label placed at the end of the result
-index

-Parameters
-----------
-label : object
-    Partial indexing with MultiIndex not allowed
-value : object
-    Scalar value
-takeable : interpret the index as indexers, default False

-Returns
--------
-series : Series
-    If label is contained, will be reference to calling Series,
-    otherwise a new object
- -
shift(self, periods=1, freq=None, axis=0)
Shift index by desired number of periods with an optional time freq

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, optional
-    Increment to use from the tseries module or time rule (e.g. 'EOM').
-    See Notes.
-axis : {0, 'index'}

-Notes
------
-If freq is specified then the index values are shifted but the data
-is not realigned. That is, use freq if you would like to extend the
-index when shifting and preserve the original data.

-Returns
--------
-shifted : Series
- -
skew(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return unbiased skew over requested axis
-Normalized by N-1

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-skew : scalar or Series (if level specified)
- -
sort_index(self, axis=0, level=None, ascending=True, inplace=False, kind='quicksort', na_position='last', sort_remaining=True)
Sort object by labels (along an axis)

-Parameters
-----------
-axis : index to direct sorting
-level : int or level name or list of ints or list of level names
-    if not None, sort on values in specified index level(s)
-ascending : boolean, default True
-    Sort ascending vs. descending
-inplace : bool, default False
-    if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end.
-     Not implemented for MultiIndex.
-sort_remaining : bool, default True
-    if true and sorting by level and index is multilevel, sort by other
-    levels too (in order) after sorting by specified level

-Returns
--------
-sorted_obj : Series
- -
sort_values(self, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last')
Sort by the values along either axis

-.. versionadded:: 0.17.0

-Parameters
-----------
-axis : {0, 'index'}, default 0
-    Axis to direct sorting
-ascending : bool or list of bool, default True
-     Sort ascending vs. descending. Specify list for multiple sort
-     orders.  If this is a list of bools, must match the length of
-     the by.
-inplace : bool, default False
-     if True, perform operation in-place
-kind : {'quicksort', 'mergesort', 'heapsort'}, default 'quicksort'
-     Choice of sorting algorithm. See also ndarray.np.sort for more
-     information.  `mergesort` is the only stable algorithm. For
-     DataFrames, this option is only applied when sorting on a single
-     column or label.
-na_position : {'first', 'last'}, default 'last'
-     `first` puts NaNs at the beginning, `last` puts NaNs at the end

-Returns
--------
-sorted_obj : Series
- -
sortlevel(self, level=0, ascending=True, sort_remaining=True)
DEPRECATED: use :meth:`Series.sort_index`

-Sort Series with MultiIndex by chosen level. Data will be
-lexicographically sorted by the chosen level followed by the other
-levels (in order)

-Parameters
-----------
-level : int or level name, default None
-ascending : bool, default True

-Returns
--------
-sorted : Series

-See Also
---------
-Series.sort_index(level=...)
- -
std(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return sample standard deviation over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-std : scalar or Series (if level specified)
- -
sub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `sub`).

-Equivalent to ``series - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rsub
- -
subtract = sub(self, other, level=None, fill_value=None, axis=0)
Subtraction of series and other, element-wise (binary operator `sub`).

-Equivalent to ``series - other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rsub
- -
sum(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
Return the sum of the values for the requested axis

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-sum : scalar or Series (if level specified)
- -
swaplevel(self, i=-2, j=-1, copy=True)
Swap levels i and j in a MultiIndex

-Parameters
-----------
-i, j : int, string (can be mixed)
-    Level of index to be swapped. Can pass level name as string.

-Returns
--------
-swapped : Series

-.. versionchanged:: 0.18.1

-   The indexes ``i`` and ``j`` are now optional, and default to
-   the two innermost levels of the index.
- -
take(self, indices, axis=0, convert=True, is_copy=False, **kwargs)
return Series corresponding to requested indices

-Parameters
-----------
-indices : list / array of ints
-convert : translate negative to positive indices (default)

-Returns
--------
-taken : Series

-See also
---------
-numpy.ndarray.take
- -
to_csv(self, path=None, index=True, sep=',', na_rep='', float_format=None, header=False, index_label=None, mode='w', encoding=None, date_format=None, decimal='.')
Write Series to a comma-separated values (csv) file

-Parameters
-----------
-path : string or file handle, default None
-    File path or object, if None is provided the result is returned as
-    a string.
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-header : boolean, default False
-    Write out series name
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.
-mode : Python write mode, default 'w'
-sep : character, default ","
-    Field delimiter for the output file.
-encoding : string, optional
-    a string representing the encoding to use if the contents are
-    non-ascii, for python versions prior to 3
-date_format: string, default None
-    Format string for datetime objects.
-decimal: string, default '.'
-    Character recognized as decimal separator. E.g. use ',' for
-    European data
- -
to_dict(self)
Convert Series to {label -> value} dict

-Returns
--------
-value_dict : dict
- -
to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='', float_format=None, columns=None, header=True, index=True, index_label=None, startrow=0, startcol=0, engine=None, merge_cells=True, encoding=None, inf_rep='inf', verbose=True)
Write Series to an excel sheet

-.. versionadded:: 0.20.0


-Parameters
-----------
-excel_writer : string or ExcelWriter object
-    File path or existing ExcelWriter
-sheet_name : string, default 'Sheet1'
-    Name of sheet which will contain DataFrame
-na_rep : string, default ''
-    Missing data representation
-float_format : string, default None
-    Format string for floating point numbers
-columns : sequence, optional
-    Columns to write
-header : boolean or list of string, default True
-    Write out column names. If a list of string is given it is
-    assumed to be aliases for the column names
-index : boolean, default True
-    Write row names (index)
-index_label : string or sequence, default None
-    Column label for index column(s) if desired. If None is given, and
-    `header` and `index` are True, then the index names are used. A
-    sequence should be given if the DataFrame uses MultiIndex.
-startrow :
-    upper left cell row to dump data frame
-startcol :
-    upper left cell column to dump data frame
-engine : string, default None
-    write engine to use - you can also set this via the options
-    ``io.excel.xlsx.writer``, ``io.excel.xls.writer``, and
-    ``io.excel.xlsm.writer``.
-merge_cells : boolean, default True
-    Write MultiIndex and Hierarchical Rows as merged cells.
-encoding: string, default None
-    encoding of the resulting excel file. Only necessary for xlwt,
-    other writers support unicode natively.
-inf_rep : string, default 'inf'
-    Representation for infinity (there is no native representation for
-    infinity in Excel)
-freeze_panes : tuple of integer (length 2), default None
-    Specifies the one-based bottommost row and rightmost column that
-    is to be frozen

-    .. versionadded:: 0.20.0

-Notes
------
-If passing an existing ExcelWriter object, then the sheet will be added
-to the existing workbook.  This can be used to save different
-DataFrames to one workbook:

->>> writer = pd.ExcelWriter('output.xlsx')
->>> df1.to_excel(writer,'Sheet1')
->>> df2.to_excel(writer,'Sheet2')
->>> writer.save()

-For compatibility with to_csv, to_excel serializes lists and dicts to
-strings before writing.
- -
to_frame(self, name=None)
Convert Series to DataFrame

-Parameters
-----------
-name : object, default None
-    The passed name should substitute for the series name (if it has
-    one).

-Returns
--------
-data_frame : DataFrame
- -
to_period(self, freq=None, copy=True)
Convert Series from DatetimeIndex to PeriodIndex with desired
-frequency (inferred from index if not passed)

-Parameters
-----------
-freq : string, default

-Returns
--------
-ts : Series with PeriodIndex
- -
to_sparse(self, kind='block', fill_value=None)
Convert Series to SparseSeries

-Parameters
-----------
-kind : {'block', 'integer'}
-fill_value : float, defaults to NaN (missing)

-Returns
--------
-sp : SparseSeries
- -
to_string(self, buf=None, na_rep='NaN', float_format=None, header=True, index=True, length=False, dtype=False, name=False, max_rows=None)
Render a string representation of the Series

-Parameters
-----------
-buf : StringIO-like, optional
-    buffer to write to
-na_rep : string, optional
-    string representation of NAN to use, default 'NaN'
-float_format : one-parameter function, optional
-    formatter function to apply to columns' elements if they are floats
-    default None
-header: boolean, default True
-    Add the Series header (index name)
-index : bool, optional
-    Add index (row) labels, default True
-length : boolean, default False
-    Add the Series length
-dtype : boolean, default False
-    Add the Series dtype
-name : boolean, default False
-    Add the Series name if not None
-max_rows : int, optional
-    Maximum number of rows to show before truncating. If None, show
-    all.

-Returns
--------
-formatted : string (if not buffer passed)
- -
to_timestamp(self, freq=None, how='start', copy=True)
Cast to datetimeindex of timestamps, at *beginning* of period

-Parameters
-----------
-freq : string, default frequency of PeriodIndex
-    Desired frequency
-how : {'s', 'e', 'start', 'end'}
-    Convention for converting period to timestamp; start of period
-    vs. end

-Returns
--------
-ts : Series with DatetimeIndex
- -
tolist(self)
Convert Series to a nested list
- -
transform(self, func, *args, **kwargs)
Call function producing a like-indexed NDFrame
-and return a NDFrame with the transformed values`

-.. versionadded:: 0.20.0

-Parameters
-----------
-func : callable, string, dictionary, or list of string/callables
-    To apply to column

-    Accepted Combinations are:

-    - string function name
-    - function
-    - list of functions
-    - dict of column names -> functions (or list of functions)

-Returns
--------
-transformed : NDFrame

-Examples
---------
->>> df = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
-...                   index=pd.date_range('1/1/2000', periods=10))
-df.iloc[3:7] = np.nan

->>> df.transform(lambda x: (x - x.mean()) / x.std())
-                   A         B         C
-2000-01-01  0.579457  1.236184  0.123424
-2000-01-02  0.370357 -0.605875 -1.231325
-2000-01-03  1.455756 -0.277446  0.288967
-2000-01-04       NaN       NaN       NaN
-2000-01-05       NaN       NaN       NaN
-2000-01-06       NaN       NaN       NaN
-2000-01-07       NaN       NaN       NaN
-2000-01-08 -0.498658  1.274522  1.642524
-2000-01-09 -0.540524 -1.012676 -0.828968
-2000-01-10 -1.366388 -0.614710  0.005378

-See also
---------
-pandas.NDFrame.aggregate
-pandas.NDFrame.apply
- -
truediv(self, other, level=None, fill_value=None, axis=0)
Floating division of series and other, element-wise (binary operator `truediv`).

-Equivalent to ``series / other``, but with support to substitute a fill_value for
-missing data in one of the inputs.

-Parameters
-----------
-other : Series or scalar value
-fill_value : None or float value, default None (NaN)
-    Fill missing (NaN) values with this value. If both Series are
-    missing, the result will be missing
-level : int or name
-    Broadcast across a level, matching Index values on the
-    passed MultiIndex level

-Returns
--------
-result : Series

-See also
---------
-Series.rtruediv
- -
unique(self)
Return unique values in the object. Uniques are returned in order
-of appearance, this does NOT sort. Hash table-based unique.

-Parameters
-----------
-values : 1d array-like

-Returns
--------
-unique values.
-  - If the input is an Index, the return is an Index
-  - If the input is a Categorical dtype, the return is a Categorical
-  - If the input is a Series/ndarray, the return will be an ndarray

-See Also
---------
-unique
-Index.unique
-Series.unique
- -
unstack(self, level=-1, fill_value=None)
Unstack, a.k.a. pivot, Series with MultiIndex to produce DataFrame.
-The level involved will automatically get sorted.

-Parameters
-----------
-level : int, string, or list of these, default last level
-    Level(s) to unstack, can pass level name
-fill_value : replace NaN with this value if the unstack produces
-    missing values

-    .. versionadded: 0.18.0

-Examples
---------
->>> s = pd.Series([1, 2, 3, 4],
-...     index=pd.MultiIndex.from_product([['one', 'two'], ['a', 'b']]))
->>> s
-one  a    1
-     b    2
-two  a    3
-     b    4
-dtype: int64

->>> s.unstack(level=-1)
-     a  b
-one  1  2
-two  3  4

->>> s.unstack(level=0)
-   one  two
-a    1    3
-b    2    4

-Returns
--------
-unstacked : DataFrame
- -
update(self, other)
Modify Series in place using non-NA values from passed
-Series. Aligns on index

-Parameters
-----------
-other : Series
- -
valid lambda self, inplace=False, **kwargs
- -
var(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs)
Return unbiased variance over requested axis.

-Normalized by N-1 by default. This can be changed using the ddof argument

-Parameters
-----------
-axis : {index (0)}
-skipna : boolean, default True
-    Exclude NA/null values. If an entire row/column is NA, the result
-    will be NA
-level : int or level name, default None
-    If the axis is a MultiIndex (hierarchical), count along a
-    particular level, collapsing into a scalar
-ddof : int, default 1
-    degrees of freedom
-numeric_only : boolean, default None
-    Include only float, int, boolean columns. If None, will attempt to use
-    everything, then use only numeric data. Not implemented for Series.

-Returns
--------
-var : scalar or Series (if level specified)
- -
view(self, dtype=None)
- -
-Class methods inherited from pandas.core.series.Series:
-
from_array(arr, index=None, name=None, dtype=None, copy=False, fastpath=False) from builtins.type
- -
from_csv(path, sep=',', parse_dates=True, header=None, index_col=0, encoding=None, infer_datetime_format=False) from builtins.type
Read CSV file (DISCOURAGED, please use :func:`pandas.read_csv`
-instead).

-It is preferable to use the more powerful :func:`pandas.read_csv`
-for most general purposes, but ``from_csv`` makes for an easy
-roundtrip to and from a file (the exact counterpart of
-``to_csv``), especially with a time Series.

-This method only differs from :func:`pandas.read_csv` in some defaults:

-- `index_col` is ``0`` instead of ``None`` (take first column as index
-  by default)
-- `header` is ``None`` instead of ``0`` (the first row is not used as
-  the column names)
-- `parse_dates` is ``True`` instead of ``False`` (try parsing the index
-  as datetime by default)

-With :func:`pandas.read_csv`, the option ``squeeze=True`` can be used
-to return a Series like ``from_csv``.

-Parameters
-----------
-path : string file path or file handle / StringIO
-sep : string, default ','
-    Field delimiter
-parse_dates : boolean, default True
-    Parse dates. Different default from read_table
-header : int, default None
-    Row to use as header (skip prior rows)
-index_col : int or sequence, default 0
-    Column to use for index. If a sequence is given, a MultiIndex
-    is used. Different default from read_table
-encoding : string, optional
-    a string representing the encoding to use if the contents are
-    non-ascii, for python versions prior to 3
-infer_datetime_format: boolean, default False
-    If True and `parse_dates` is True for a column, try to infer the
-    datetime format based on the first datetime string. If the format
-    can be inferred, there often will be a large parsing speed-up.

-See also
---------
-pandas.read_csv

-Returns
--------
-y : Series
- -
-Data descriptors inherited from pandas.core.series.Series:
-
asobject
-
return object Series which contains boxed values

-*this is an internal non-public method*
-
-
axes
-
Return a list of the row axis labels
-
-
dtype
-
return the dtype object of the underlying data
-
-
dtypes
-
return the dtype object of the underlying data
-
-
ftype
-
return if the data is sparse|dense
-
-
ftypes
-
return if the data is sparse|dense
-
-
imag
-
-
index
-
-
name
-
-
real
-
-
values
-
Return Series as ndarray or ndarray-like
-depending on the dtype

-Returns
--------
-arr : numpy.ndarray or ndarray-like

-Examples
---------
->>> pd.Series([1, 2, 3]).values
-array([1, 2, 3])

->>> pd.Series(list('aabc')).values
-array(['a', 'a', 'b', 'c'], dtype=object)

->>> pd.Series(list('aabc')).astype('category').values
-[a, a, b, c]
-Categories (3, object): [a, b, c]

-Timezone aware datetime data is converted to UTC:

->>> pd.Series(pd.date_range('20130101', periods=3,
-...                         tz='US/Eastern')).values
-array(['2013-01-01T05:00:00.000000000',
-       '2013-01-02T05:00:00.000000000',
-       '2013-01-03T05:00:00.000000000'], dtype='datetime64[ns]')
-
-
-Data and other attributes inherited from pandas.core.series.Series:
-
cat = <class 'pandas.core.categorical.CategoricalAccessor'>
Accessor object for categorical properties of the Series values.

-Be aware that assigning to `categories` is a inplace operation, while all
-methods return new categorical data per default (but can be called with
-`inplace=True`).

-Examples
---------
->>> s.cat.categories
->>> s.cat.categories = list('abc')
->>> s.cat.rename_categories(list('cab'))
->>> s.cat.reorder_categories(list('cab'))
->>> s.cat.add_categories(['d','e'])
->>> s.cat.remove_categories(['d'])
->>> s.cat.remove_unused_categories()
->>> s.cat.set_categories(list('abcde'))
->>> s.cat.as_ordered()
->>> s.cat.as_unordered()
- -
dt = <class 'pandas.core.indexes.accessors.CombinedDatetimelikeProperties'>
Accessor object for datetimelike properties of the Series values.

-Examples
---------
->>> s.dt.hour
->>> s.dt.second
->>> s.dt.quarter

-Returns a Series indexed like the original Series.
-Raises TypeError if the Series does not contain datetimelike values.
- -
plot = <class 'pandas.plotting._core.SeriesPlotMethods'>
Series plotting accessor and method

-Examples
---------
->>> s.plot.line()
->>> s.plot.bar()
->>> s.plot.hist()

-Plotting methods can also be accessed by calling the accessor as a method
-with the ``kind`` argument:
-``s.plot(kind='line')`` is equivalent to ``s.plot.line()``
- -
-Methods inherited from pandas.core.base.IndexOpsMixin:
-
factorize(self, sort=False, na_sentinel=-1)
Encode the object as an enumerated type or categorical variable

-Parameters
-----------
-sort : boolean, default False
-    Sort by values
-na_sentinel: int, default -1
-    Value to mark "not found"

-Returns
--------
-labels : the indexer to the original array
-uniques : the unique Index
- -
item(self)
return the first element of the underlying data as a python
-scalar
- -
nunique(self, dropna=True)
Return number of unique elements in the object.

-Excludes NA values by default.

-Parameters
-----------
-dropna : boolean, default True
-    Don't include NaN in the count.

-Returns
--------
-nunique : int
- -
transpose(self, *args, **kwargs)
return the transpose, which is by definition self
- -
value_counts(self, normalize=False, sort=True, ascending=False, bins=None, dropna=True)
Returns object containing counts of unique values.

-The resulting object will be in descending order so that the
-first element is the most frequently-occurring element.
-Excludes NA values by default.

-Parameters
-----------
-normalize : boolean, default False
-    If True then the object returned will contain the relative
-    frequencies of the unique values.
-sort : boolean, default True
-    Sort by values
-ascending : boolean, default False
-    Sort in ascending order
-bins : integer, optional
-    Rather than count values, group them into half-open bins,
-    a convenience for pd.cut, only works with numeric data
-dropna : boolean, default True
-    Don't include counts of NaN.

-Returns
--------
-counts : Series
- -
-Data descriptors inherited from pandas.core.base.IndexOpsMixin:
-
T
-
return the transpose, which is by definition self
-
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-
base
-
return the base object if the memory of the underlying data is
-shared
-
-
data
-
return the data pointer of the underlying data
-
-
empty
-
-
flags
-
return the ndarray.flags for the underlying data
-
-
hasnans
-
-
is_monotonic
-
Return boolean if values in the object are
-monotonic_increasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic : boolean
-
-
is_monotonic_decreasing
-
Return boolean if values in the object are
-monotonic_decreasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic_decreasing : boolean
-
-
is_monotonic_increasing
-
Return boolean if values in the object are
-monotonic_increasing

-.. versionadded:: 0.19.0

-Returns
--------
-is_monotonic : boolean
-
-
is_unique
-
Return boolean if values in the object are unique

-Returns
--------
-is_unique : boolean
-
-
itemsize
-
return the size of the dtype of the item of the underlying data
-
-
nbytes
-
return the number of bytes in the underlying data
-
-
ndim
-
return the number of dimensions of the underlying data,
-by definition 1
-
-
shape
-
return a tuple of the shape of the underlying data
-
-
size
-
return the number of elements in the underlying data
-
-
strides
-
return the strides of the underlying data
-
-
-Data and other attributes inherited from pandas.core.base.IndexOpsMixin:
-
__array_priority__ = 1000
- -
-Data and other attributes inherited from pandas.core.strings.StringAccessorMixin:
-
str = <class 'pandas.core.strings.StringMethods'>
Vectorized string functions for Series and Index. NAs stay NA unless
-handled otherwise by a particular method. Patterned after Python's string
-methods, with some inspiration from R's stringr package.

-Examples
---------
->>> s.str.split('_')
->>> s.str.replace('_', '')
- -
-Methods inherited from pandas.core.generic.NDFrame:
-
__abs__(self)
- -
__bool__ = __nonzero__(self)
- -
__contains__(self, key)
True if the key is in the info axis
- -
__copy__(self, deep=True)
- -
__deepcopy__(self, memo=None)
- -
__delitem__(self, key)
Delete item
- -
__finalize__(self, other, method=None, **kwargs)
Propagate metadata from other to self.

-Parameters
-----------
-other : the object from which to get the attributes that we are going
-    to propagate
-method : optional, a passed method name ; possibly to take different
-    types of propagation actions based on this
- -
__getattr__(self, name)
After regular attribute access, try looking up the name
-This allows simpler access to columns for interactive use.
- -
__getstate__(self)
- -
__hash__(self)
Return hash(self).
- -
__invert__(self)
- -
__neg__(self)
- -
__nonzero__(self)
- -
__round__(self, decimals=0)
- -
__setattr__(self, name, value)
After regular attribute access, try setting the name
-This allows simpler access to columns for interactive use.
- -
__setstate__(self, state)
- -
abs(self)
Return an object with absolute value taken--only applicable to objects
-that are all numeric.

-Returns
--------
-abs: type of caller
- -
add_prefix(self, prefix)
Concatenate prefix string with panel items names.

-Parameters
-----------
-prefix : string

-Returns
--------
-with_prefix : type of caller
- -
add_suffix(self, suffix)
Concatenate suffix string with panel items names.

-Parameters
-----------
-suffix : string

-Returns
--------
-with_suffix : type of caller
- -
as_blocks(self, copy=True)
Convert the frame to a dict of dtype -> Constructor Types that each has
-a homogeneous dtype.

-NOTE: the dtypes of the blocks WILL BE PRESERVED HERE (unlike in
-      as_matrix)

-Parameters
-----------
-copy : boolean, default True

-       .. versionadded: 0.16.1

-Returns
--------
-values : a dict of dtype -> Constructor Types
- -
as_matrix(self, columns=None)
Convert the frame to its Numpy-array representation.

-Parameters
-----------
-columns: list, optional, default:None
-    If None, return all columns, otherwise, returns specified columns.

-Returns
--------
-values : ndarray
-    If the caller is heterogeneous and contains booleans or objects,
-    the result will be of dtype=object. See Notes.


-Notes
------
-Return is NOT a Numpy-matrix, rather, a Numpy-array.

-The dtype will be a lower-common-denominator dtype (implicit
-upcasting); that is to say if the dtypes (even of numeric types)
-are mixed, the one that accommodates all will be chosen. Use this
-with care if you are not dealing with the blocks.

-e.g. If the dtypes are float16 and float32, dtype will be upcast to
-float32.  If dtypes are int32 and uint8, dtype will be upcase to
-int32. By numpy.find_common_type convention, mixing int64 and uint64
-will result in a flot64 dtype.

-This method is provided for backwards compatibility. Generally,
-it is recommended to use '.values'.

-See Also
---------
-pandas.DataFrame.values
- -
asfreq(self, freq, method=None, how=None, normalize=False, fill_value=None)
Convert TimeSeries to specified frequency.

-Optionally provide filling method to pad/backfill missing values.

-Returns the original data conformed to a new index with the specified
-frequency. ``resample`` is more appropriate if an operation, such as
-summarization, is necessary to represent the data at the new frequency.

-Parameters
-----------
-freq : DateOffset object, or string
-method : {'backfill'/'bfill', 'pad'/'ffill'}, default None
-    Method to use for filling holes in reindexed Series (note this
-    does not fill NaNs that already were present):

-    * 'pad' / 'ffill': propagate last valid observation forward to next
-      valid
-    * 'backfill' / 'bfill': use NEXT valid observation to fill
-how : {'start', 'end'}, default end
-    For PeriodIndex only, see PeriodIndex.asfreq
-normalize : bool, default False
-    Whether to reset output index to midnight
-fill_value: scalar, optional
-    Value to use for missing values, applied during upsampling (note
-    this does not fill NaNs that already were present).

-    .. versionadded:: 0.20.0

-Returns
--------
-converted : type of caller

-Examples
---------

-Start by creating a series with 4 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=4, freq='T')
->>> series = pd.Series([0.0, None, 2.0, 3.0], index=index)
->>> df = pd.DataFrame({'s':series})
->>> df
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:03:00    3.0

-Upsample the series into 30 second bins.

->>> df.asfreq(freq='30S')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    NaN
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    NaN
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``fill value``.

->>> df.asfreq(freq='30S', fill_value=9.0)
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    9.0
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    9.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    9.0
-2000-01-01 00:03:00    3.0

-Upsample again, providing a ``method``.

->>> df.asfreq(freq='30S', method='bfill')
-                       s
-2000-01-01 00:00:00    0.0
-2000-01-01 00:00:30    NaN
-2000-01-01 00:01:00    NaN
-2000-01-01 00:01:30    2.0
-2000-01-01 00:02:00    2.0
-2000-01-01 00:02:30    3.0
-2000-01-01 00:03:00    3.0

-See Also
---------
-reindex

-Notes
------
-To learn more about the frequency strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
- -
asof(self, where, subset=None)
The last row without any NaN is taken (or the last row without
-NaN considering only the subset of columns in the case of a DataFrame)

-.. versionadded:: 0.19.0 For DataFrame

-If there is no good value, NaN is returned for a Series
-a Series of NaN values for a DataFrame

-Parameters
-----------
-where : date or array of dates
-subset : string or list of strings, default None
-   if not None use these columns for NaN propagation

-Notes
------
-Dates are assumed to be sorted
-Raises if this is not the case

-Returns
--------
-where is scalar

-  - value or NaN if input is Series
-  - Series if input is DataFrame

-where is Index: same shape object as input

-See Also
---------
-merge_asof
- -
astype(self, dtype, copy=True, errors='raise', **kwargs)
Cast object to input numpy.dtype
-Return a copy when copy = True (be really careful with this!)

-Parameters
-----------
-dtype : data type, or dict of column name -> data type
-    Use a numpy.dtype or Python type to cast entire pandas object to
-    the same type. Alternatively, use {col: dtype, ...}, where col is a
-    column label and dtype is a numpy.dtype or Python type to cast one
-    or more of the DataFrame's columns to column-specific types.
-errors : {'raise', 'ignore'}, default 'raise'.
-    Control raising of exceptions on invalid data for provided dtype.

-    - ``raise`` : allow exceptions to be raised
-    - ``ignore`` : suppress exceptions. On error return original object

-    .. versionadded:: 0.20.0

-raise_on_error : DEPRECATED use ``errors`` instead
-kwargs : keyword arguments to pass on to the constructor

-Returns
--------
-casted : type of caller
- -
at_time(self, time, asof=False)
Select values at particular time of day (e.g. 9:30AM).

-Parameters
-----------
-time : datetime.time or string

-Returns
--------
-values_at_time : type of caller
- -
between_time(self, start_time, end_time, include_start=True, include_end=True)
Select values between particular times of the day (e.g., 9:00-9:30 AM).

-Parameters
-----------
-start_time : datetime.time or string
-end_time : datetime.time or string
-include_start : boolean, default True
-include_end : boolean, default True

-Returns
--------
-values_between_time : type of caller
- -
bfill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='bfill') <DataFrame.fillna>`
- -
bool(self)
Return the bool of a single element PandasObject.

-This must be a boolean scalar value, either True or False.  Raise a
-ValueError if the PandasObject does not have exactly 1 element, or that
-element is not boolean
- -
clip(self, lower=None, upper=None, axis=None, *args, **kwargs)
Trim values at input threshold(s).

-Parameters
-----------
-lower : float or array_like, default None
-upper : float or array_like, default None
-axis : int or string axis name, optional
-    Align object with lower and upper along the given axis.

-Returns
--------
-clipped : Series

-Examples
---------
->>> df
-  0         1
-0  0.335232 -1.256177
-1 -1.367855  0.746646
-2  0.027753 -1.176076
-3  0.230930 -0.679613
-4  1.261967  0.570967
->>> df.clip(-1.0, 0.5)
-          0         1
-0  0.335232 -1.000000
-1 -1.000000  0.500000
-2  0.027753 -1.000000
-3  0.230930 -0.679613
-4  0.500000  0.500000
->>> t
-0   -0.3
-1   -0.2
-2   -0.1
-3    0.0
-4    0.1
-dtype: float64
->>> df.clip(t, t + 1, axis=0)
-          0         1
-0  0.335232 -0.300000
-1 -0.200000  0.746646
-2  0.027753 -0.100000
-3  0.230930  0.000000
-4  1.100000  0.570967
- -
clip_lower(self, threshold, axis=None)
Return copy of the input with values below given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
clip_upper(self, threshold, axis=None)
Return copy of input with values above given value(s) truncated.

-Parameters
-----------
-threshold : float or array_like
-axis : int or string axis name, optional
-    Align object with threshold along the given axis.

-See Also
---------
-clip

-Returns
--------
-clipped : same type as input
- -
consolidate(self, inplace=False)
DEPRECATED: consolidate will be an internal implementation only.
- -
convert_objects(self, convert_dates=True, convert_numeric=False, convert_timedeltas=True, copy=True)
Deprecated.

-Attempt to infer better dtype for object columns

-Parameters
-----------
-convert_dates : boolean, default True
-    If True, convert to date where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-convert_numeric : boolean, default False
-    If True, attempt to coerce to numbers (including strings), with
-    unconvertible values becoming NaN.
-convert_timedeltas : boolean, default True
-    If True, convert to timedelta where possible. If 'coerce', force
-    conversion, with unconvertible values becoming NaT.
-copy : boolean, default True
-    If True, return a copy even if no copy is necessary (e.g. no
-    conversion was done). Note: This is meant for internal use, and
-    should not be confused with inplace.

-See Also
---------
-pandas.to_datetime : Convert argument to datetime.
-pandas.to_timedelta : Convert argument to timedelta.
-pandas.to_numeric : Return a fixed frequency timedelta index,
-    with day as the default.

-Returns
--------
-converted : same as input object
- -
copy(self, deep=True)
Make a copy of this objects data.

-Parameters
-----------
-deep : boolean or string, default True
-    Make a deep copy, including a copy of the data and the indices.
-    With ``deep=False`` neither the indices or the data are copied.

-    Note that when ``deep=True`` data is copied, actual python objects
-    will not be copied recursively, only the reference to the object.
-    This is in contrast to ``copy.deepcopy`` in the Standard Library,
-    which recursively copies object data.

-Returns
--------
-copy : type of caller
- -
describe(self, percentiles=None, include=None, exclude=None)
Generates descriptive statistics that summarize the central tendency,
-dispersion and shape of a dataset's distribution, excluding
-``NaN`` values.

-Analyzes both numeric and object series, as well
-as ``DataFrame`` column sets of mixed data types. The output
-will vary depending on what is provided. Refer to the notes
-below for more detail.

-Parameters
-----------
-percentiles : list-like of numbers, optional
-    The percentiles to include in the output. All should
-    fall between 0 and 1. The default is
-    ``[.25, .5, .75]``, which returns the 25th, 50th, and
-    75th percentiles.
-include : 'all', list-like of dtypes or None (default), optional
-    A white list of data types to include in the result. Ignored
-    for ``Series``. Here are the options:

-    - 'all' : All columns of the input will be included in the output.
-    - A list-like of dtypes : Limits the results to the
-      provided data types.
-      To limit the result to numeric types submit
-      ``numpy.number``. To limit it instead to categorical
-      objects submit the ``numpy.object`` data type. Strings
-      can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will include all numeric columns.
-exclude : list-like of dtypes or None (default), optional,
-    A black list of data types to omit from the result. Ignored
-    for ``Series``. Here are the options:

-    - A list-like of dtypes : Excludes the provided data types
-      from the result. To select numeric types submit
-      ``numpy.number``. To select categorical objects submit the data
-      type ``numpy.object``. Strings can also be used in the style of
-      ``select_dtypes`` (e.g. ``df.describe(include=['O'])``)
-    - None (default) : The result will exclude nothing.

-Returns
--------
-summary:  Series/DataFrame of summary statistics

-Notes
------
-For numeric data, the result's index will include ``count``,
-``mean``, ``std``, ``min``, ``max`` as well as lower, ``50`` and
-upper percentiles. By default the lower percentile is ``25`` and the
-upper percentile is ``75``. The ``50`` percentile is the
-same as the median.

-For object data (e.g. strings or timestamps), the result's index
-will include ``count``, ``unique``, ``top``, and ``freq``. The ``top``
-is the most common value. The ``freq`` is the most common value's
-frequency. Timestamps also include the ``first`` and ``last`` items.

-If multiple object values have the highest count, then the
-``count`` and ``top`` results will be arbitrarily chosen from
-among those with the highest count.

-For mixed data types provided via a ``DataFrame``, the default is to
-return only an analysis of numeric columns. If ``include='all'``
-is provided as an option, the result will include a union of
-attributes of each type.

-The `include` and `exclude` parameters can be used to limit
-which columns in a ``DataFrame`` are analyzed for the output.
-The parameters are ignored when analyzing a ``Series``.

-Examples
---------
-Describing a numeric ``Series``.

->>> s = pd.Series([1, 2, 3])
->>> s.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0

-Describing a categorical ``Series``.

->>> s = pd.Series(['a', 'a', 'b', 'c'])
->>> s.describe()
-count     4
-unique    3
-top       a
-freq      2
-dtype: object

-Describing a timestamp ``Series``.

->>> s = pd.Series([
-...   np.datetime64("2000-01-01"),
-...   np.datetime64("2010-01-01"),
-...   np.datetime64("2010-01-01")
-... ])
->>> s.describe()
-count                       3
-unique                      2
-top       2010-01-01 00:00:00
-freq                        2
-first     2000-01-01 00:00:00
-last      2010-01-01 00:00:00
-dtype: object

-Describing a ``DataFrame``. By default only numeric fields
-are returned.

->>> df = pd.DataFrame([[1, 'a'], [2, 'b'], [3, 'c']],
-...                   columns=['numeric', 'object'])
->>> df.describe()
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Describing all columns of a ``DataFrame`` regardless of data type.

->>> df.describe(include='all')
-        numeric object
-count       3.0      3
-unique      NaN      3
-top         NaN      b
-freq        NaN      1
-mean        2.0    NaN
-std         1.0    NaN
-min         1.0    NaN
-25%         1.5    NaN
-50%         2.0    NaN
-75%         2.5    NaN
-max         3.0    NaN

-Describing a column from a ``DataFrame`` by accessing it as
-an attribute.

->>> df.numeric.describe()
-count    3.0
-mean     2.0
-std      1.0
-min      1.0
-25%      1.5
-50%      2.0
-75%      2.5
-max      3.0
-Name: numeric, dtype: float64

-Including only numeric columns in a ``DataFrame`` description.

->>> df.describe(include=[np.number])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-Including only string columns in a ``DataFrame`` description.

->>> df.describe(include=[np.object])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding numeric columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.number])
-       object
-count       3
-unique      3
-top         b
-freq        1

-Excluding object columns from a ``DataFrame`` description.

->>> df.describe(exclude=[np.object])
-       numeric
-count      3.0
-mean       2.0
-std        1.0
-min        1.0
-25%        1.5
-50%        2.0
-75%        2.5
-max        3.0

-See Also
---------
-DataFrame.count
-DataFrame.max
-DataFrame.min
-DataFrame.mean
-DataFrame.std
-DataFrame.select_dtypes
- -
drop(self, labels, axis=0, level=None, inplace=False, errors='raise')
Return new object with labels in requested axis removed.

-Parameters
-----------
-labels : single label or list-like
-axis : int or axis name
-level : int or level name, default None
-    For MultiIndex
-inplace : bool, default False
-    If True, do operation inplace and return None.
-errors : {'ignore', 'raise'}, default 'raise'
-    If 'ignore', suppress error and existing labels are dropped.

-    .. versionadded:: 0.16.1

-Returns
--------
-dropped : type of caller
- -
equals(self, other)
Determines if two NDFrame objects contain the same elements. NaNs in
-the same location are considered equal.
- -
ffill(self, axis=None, inplace=False, limit=None, downcast=None)
Synonym for :meth:`DataFrame.fillna(method='ffill') <DataFrame.fillna>`
- -
filter(self, items=None, like=None, regex=None, axis=None)
Subset rows or columns of dataframe according to labels in
-the specified index.

-Note that this routine does not filter a dataframe on its
-contents. The filter is applied to the labels of the index.

-Parameters
-----------
-items : list-like
-    List of info axis to restrict to (must not all be present)
-like : string
-    Keep info axis where "arg in col == True"
-regex : string (regular expression)
-    Keep info axis with re.search(regex, col) == True
-axis : int or string axis name
-    The axis to filter on.  By default this is the info axis,
-    'index' for Series, 'columns' for DataFrame

-Returns
--------
-same type as input object

-Examples
---------
->>> df
-one  two  three
-mouse     1    2      3
-rabbit    4    5      6

->>> # select columns by name
->>> df.filter(items=['one', 'three'])
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select columns by regular expression
->>> df.filter(regex='e$', axis=1)
-one  three
-mouse     1      3
-rabbit    4      6

->>> # select rows containing 'bbi'
->>> df.filter(like='bbi', axis=0)
-one  two  three
-rabbit    4    5      6

-See Also
---------
-pandas.DataFrame.select

-Notes
------
-The ``items``, ``like``, and ``regex`` parameters are
-enforced to be mutually exclusive.

-``axis`` defaults to the info axis that is used when indexing
-with ``[]``.
- -
first(self, offset)
Convenience method for subsetting initial periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.first('10D') -> First 10 days

-Returns
--------
-subset : type of caller
- -
get(self, key, default=None)
Get item from object for given key (DataFrame column, Panel slice,
-etc.). Returns default value if not found.

-Parameters
-----------
-key : object

-Returns
--------
-value : type of items contained in object
- -
get_dtype_counts(self)
Return the counts of dtypes in this object.
- -
get_ftype_counts(self)
Return the counts of ftypes in this object.
- -
groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, **kwargs)
Group series using mapper (dict or key function, apply given function
-to group, return result as series) or by a series of columns.

-Parameters
-----------
-by : mapping, function, str, or iterable
-    Used to determine the groups for the groupby.
-    If ``by`` is a function, it's called on each value of the object's
-    index. If a dict or Series is passed, the Series or dict VALUES
-    will be used to determine the groups (the Series' values are first
-    aligned; see ``.align()`` method). If an ndarray is passed, the
-    values are used as-is determine the groups. A str or list of strs
-    may be passed to group by the columns in ``self``
-axis : int, default 0
-level : int, level name, or sequence of such, default None
-    If the axis is a MultiIndex (hierarchical), group by a particular
-    level or levels
-as_index : boolean, default True
-    For aggregated output, return object with group labels as the
-    index. Only relevant for DataFrame input. as_index=False is
-    effectively "SQL-style" grouped output
-sort : boolean, default True
-    Sort group keys. Get better performance by turning this off.
-    Note this does not influence the order of observations within each
-    group.  groupby preserves the order of rows within each group.
-group_keys : boolean, default True
-    When calling apply, add group keys to index to identify pieces
-squeeze : boolean, default False
-    reduce the dimensionality of the return type if possible,
-    otherwise return a consistent type

-Examples
---------
-DataFrame results

->>> data.groupby(func, axis=0).mean()
->>> data.groupby(['col1', 'col2'])['col3'].mean()

-DataFrame with hierarchical index

->>> data.groupby(['col1', 'col2']).mean()

-Returns
--------
-GroupBy object
- -
head(self, n=5)
Returns first n rows
- -
interpolate(self, method='linear', axis=0, limit=None, inplace=False, limit_direction='forward', downcast=None, **kwargs)
Interpolate values according to different methods.

-Please note that only ``method='linear'`` is supported for
-DataFrames/Series with a MultiIndex.

-Parameters
-----------
-method : {'linear', 'time', 'index', 'values', 'nearest', 'zero',
-          'slinear', 'quadratic', 'cubic', 'barycentric', 'krogh',
-          'polynomial', 'spline', 'piecewise_polynomial',
-          'from_derivatives', 'pchip', 'akima'}

-    * 'linear': ignore the index and treat the values as equally
-      spaced. This is the only method supported on MultiIndexes.
-      default
-    * 'time': interpolation works on daily and higher resolution
-      data to interpolate given length of interval
-    * 'index', 'values': use the actual numerical values of the index
-    * 'nearest', 'zero', 'slinear', 'quadratic', 'cubic',
-      'barycentric', 'polynomial' is passed to
-      ``scipy.interpolate.interp1d``. Both 'polynomial' and 'spline'
-      require that you also specify an `order` (int),
-      e.g. df.interpolate(method='polynomial', order=4).
-      These use the actual numerical values of the index.
-    * 'krogh', 'piecewise_polynomial', 'spline', 'pchip' and 'akima'
-      are all wrappers around the scipy interpolation methods of
-      similar names. These use the actual numerical values of the
-      index. For more information on their behavior, see the
-      `scipy documentation
-      <http://docs.scipy.org/doc/scipy/reference/interpolate.html#univariate-interpolation>`__
-      and `tutorial documentation
-      <http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html>`__
-    * 'from_derivatives' refers to BPoly.from_derivatives which
-      replaces 'piecewise_polynomial' interpolation method in
-      scipy 0.18

-    .. versionadded:: 0.18.1

-       Added support for the 'akima' method
-       Added interpolate method 'from_derivatives' which replaces
-       'piecewise_polynomial' in scipy 0.18; backwards-compatible with
-       scipy < 0.18

-axis : {0, 1}, default 0
-    * 0: fill column-by-column
-    * 1: fill row-by-row
-limit : int, default None.
-    Maximum number of consecutive NaNs to fill. Must be greater than 0.
-limit_direction : {'forward', 'backward', 'both'}, default 'forward'
-    If limit is specified, consecutive NaNs will be filled in this
-    direction.

-    .. versionadded:: 0.17.0

-inplace : bool, default False
-    Update the NDFrame in place if possible.
-downcast : optional, 'infer' or None, defaults to None
-    Downcast dtypes if possible.
-kwargs : keyword arguments to pass on to the interpolating function.

-Returns
--------
-Series or DataFrame of same shape interpolated at the NaNs

-See Also
---------
-reindex, replace, fillna

-Examples
---------

-Filling in NaNs

->>> s = pd.Series([0, 1, np.nan, 3])
->>> s.interpolate()
-0    0
-1    1
-2    2
-3    3
-dtype: float64
- -
isnull(self)
Return a boolean same-sized object indicating if the values are null.

-See Also
---------
-notnull : boolean inverse of isnull
- -
last(self, offset)
Convenience method for subsetting final periods of time series data
-based on a date offset.

-Parameters
-----------
-offset : string, DateOffset, dateutil.relativedelta

-Examples
---------
-ts.last('5M') -> Last 5 months

-Returns
--------
-subset : type of caller
- -
mask(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is False and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The mask method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``False`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``mask`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.where`
- -
notnull(self)
Return a boolean same-sized object indicating if the values are
-not null.

-See Also
---------
-isnull : boolean inverse of notnull
- -
pct_change(self, periods=1, fill_method='pad', limit=None, freq=None, **kwargs)
Percent change over given number of periods.

-Parameters
-----------
-periods : int, default 1
-    Periods to shift for forming percent change
-fill_method : str, default 'pad'
-    How to handle NAs before computing percent changes
-limit : int, default None
-    The number of consecutive NAs to fill before stopping
-freq : DateOffset, timedelta, or offset alias string, optional
-    Increment to use from time series API (e.g. 'M' or BDay())

-Returns
--------
-chg : NDFrame

-Notes
------

-By default, the percentage change is calculated along the stat
-axis: 0, or ``Index``, for ``DataFrame`` and 1, or ``minor`` for
-``Panel``. You can change this with the ``axis`` keyword argument.
- -
pipe(self, func, *args, **kwargs)
Apply func(self, \*args, \*\*kwargs)

-.. versionadded:: 0.16.2

-Parameters
-----------
-func : function
-    function to apply to the NDFrame.
-    ``args``, and ``kwargs`` are passed into ``func``.
-    Alternatively a ``(callable, data_keyword)`` tuple where
-    ``data_keyword`` is a string indicating the keyword of
-    ``callable`` that expects the NDFrame.
-args : positional arguments passed into ``func``.
-kwargs : a dictionary of keyword arguments passed into ``func``.

-Returns
--------
-object : the return type of ``func``.

-Notes
------

-Use ``.pipe`` when chaining together functions that expect
-on Series or DataFrames. Instead of writing

->>> f(g(h(df), arg1=a), arg2=b, arg3=c)

-You can write

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe(f, arg2=b, arg3=c)
-... )

-If you have a function that takes the data as (say) the second
-argument, pass a tuple indicating which keyword expects the
-data. For example, suppose ``f`` takes its data as ``arg2``:

->>> (df.pipe(h)
-...    .pipe(g, arg1=a)
-...    .pipe((f, 'arg2'), arg1=a, arg3=c)
-...  )

-See Also
---------
-pandas.DataFrame.apply
-pandas.DataFrame.applymap
-pandas.Series.map
- -
pop(self, item)
Return item and drop from frame. Raise KeyError if not found.
- -
rank(self, axis=0, method='average', numeric_only=None, na_option='keep', ascending=True, pct=False)
Compute numerical data ranks (1 through n) along axis. Equal values are
-assigned a rank that is the average of the ranks of those values

-Parameters
-----------
-axis : {0 or 'index', 1 or 'columns'}, default 0
-    index to direct ranking
-method : {'average', 'min', 'max', 'first', 'dense'}
-    * average: average rank of group
-    * min: lowest rank in group
-    * max: highest rank in group
-    * first: ranks assigned in order they appear in the array
-    * dense: like 'min', but rank always increases by 1 between groups
-numeric_only : boolean, default None
-    Include only float, int, boolean data. Valid only for DataFrame or
-    Panel objects
-na_option : {'keep', 'top', 'bottom'}
-    * keep: leave NA values where they are
-    * top: smallest rank if ascending
-    * bottom: smallest rank if descending
-ascending : boolean, default True
-    False for ranks by high (1) to low (N)
-pct : boolean, default False
-    Computes percentage rank of data

-Returns
--------
-ranks : same type as caller
- -
reindex_like(self, other, method=None, copy=True, limit=None, tolerance=None)
Return an object with matching indices to myself.

-Parameters
-----------
-other : Object
-method : string or None
-copy : boolean, default True
-limit : int, default None
-    Maximum number of consecutive labels to fill for inexact matches.
-tolerance : optional
-    Maximum distance between labels of the other object and this
-    object for inexact matches.

-    .. versionadded:: 0.17.0

-Notes
------
-Like calling s.reindex(index=other.index, columns=other.columns,
-                       method=...)

-Returns
--------
-reindexed : same as input
- -
rename_axis(self, mapper, axis=0, copy=True, inplace=False)
Alter index and / or columns using input function or functions.
-A scalar or list-like for ``mapper`` will alter the ``Index.name``
-or ``MultiIndex.names`` attribute.
-A function or dict for ``mapper`` will alter the labels.
-Function / dict values must be unique (1-to-1). Labels not contained in
-a dict / Series will be left as-is.

-Parameters
-----------
-mapper : scalar, list-like, dict-like or function, optional
-axis : int or string, default 0
-copy : boolean, default True
-    Also copy underlying data
-inplace : boolean, default False

-Returns
--------
-renamed : type of caller

-See Also
---------
-pandas.NDFrame.rename
-pandas.Index.rename

-Examples
---------
->>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
->>> df.rename_axis("foo")  # scalar, alters df.index.name
-     A  B
-foo
-0    1  4
-1    2  5
-2    3  6
->>> df.rename_axis(lambda x: 2 * x)  # function: alters labels
-   A  B
-0  1  4
-2  2  5
-4  3  6
->>> df.rename_axis({"A": "ehh", "C": "see"}, axis="columns")  # mapping
-   ehh  B
-0    1  4
-1    2  5
-2    3  6
- -
replace(self, to_replace=None, value=None, inplace=False, limit=None, regex=False, method='pad', axis=None)
Replace values given in 'to_replace' with 'value'.

-Parameters
-----------
-to_replace : str, regex, list, dict, Series, numeric, or None

-    * str or regex:

-        - str: string exactly matching `to_replace` will be replaced
-          with `value`
-        - regex: regexs matching `to_replace` will be replaced with
-          `value`

-    * list of str, regex, or numeric:

-        - First, if `to_replace` and `value` are both lists, they
-          **must** be the same length.
-        - Second, if ``regex=True`` then all of the strings in **both**
-          lists will be interpreted as regexs otherwise they will match
-          directly. This doesn't matter much for `value` since there
-          are only a few possible substitution regexes you can use.
-        - str and regex rules apply as above.

-    * dict:

-        - Nested dictionaries, e.g., {'a': {'b': nan}}, are read as
-          follows: look in column 'a' for the value 'b' and replace it
-          with nan. You can nest regular expressions as well. Note that
-          column names (the top-level dictionary keys in a nested
-          dictionary) **cannot** be regular expressions.
-        - Keys map to column names and values map to substitution
-          values. You can treat this as a special case of passing two
-          lists except that you are specifying the column to search in.

-    * None:

-        - This means that the ``regex`` argument must be a string,
-          compiled regular expression, or list, dict, ndarray or Series
-          of such elements. If `value` is also ``None`` then this
-          **must** be a nested dictionary or ``Series``.

-    See the examples section for examples of each of these.
-value : scalar, dict, list, str, regex, default None
-    Value to use to fill holes (e.g. 0), alternately a dict of values
-    specifying which value to use for each column (columns not in the
-    dict will not be filled). Regular expressions, strings and lists or
-    dicts of such objects are also allowed.
-inplace : boolean, default False
-    If True, in place. Note: this will modify any
-    other views on this object (e.g. a column form a DataFrame).
-    Returns the caller if this is True.
-limit : int, default None
-    Maximum size gap to forward or backward fill
-regex : bool or same types as `to_replace`, default False
-    Whether to interpret `to_replace` and/or `value` as regular
-    expressions. If this is ``True`` then `to_replace` *must* be a
-    string. Otherwise, `to_replace` must be ``None`` because this
-    parameter will be interpreted as a regular expression or a list,
-    dict, or array of regular expressions.
-method : string, optional, {'pad', 'ffill', 'bfill'}
-    The method to use when for replacement, when ``to_replace`` is a
-    ``list``.

-See Also
---------
-NDFrame.reindex
-NDFrame.asfreq
-NDFrame.fillna

-Returns
--------
-filled : NDFrame

-Raises
-------
-AssertionError
-    * If `regex` is not a ``bool`` and `to_replace` is not ``None``.
-TypeError
-    * If `to_replace` is a ``dict`` and `value` is not a ``list``,
-      ``dict``, ``ndarray``, or ``Series``
-    * If `to_replace` is ``None`` and `regex` is not compilable into a
-      regular expression or is a list, dict, ndarray, or Series.
-ValueError
-    * If `to_replace` and `value` are ``list`` s or ``ndarray`` s, but
-      they are not the same length.

-Notes
------
-* Regex substitution is performed under the hood with ``re.sub``. The
-  rules for substitution for ``re.sub`` are the same.
-* Regular expressions will only substitute on strings, meaning you
-  cannot provide, for example, a regular expression matching floating
-  point numbers and expect the columns in your frame that have a
-  numeric dtype to be matched. However, if those floating point numbers
-  *are* strings, then you can do this.
-* This method has *a lot* of options. You are encouraged to experiment
-  and play with this method to gain intuition about how it works.
- -
resample(self, rule, how=None, axis=0, fill_method=None, closed=None, label=None, convention='start', kind=None, loffset=None, limit=None, base=0, on=None, level=None)
Convenience method for frequency conversion and resampling of time
-series.  Object must have a datetime-like index (DatetimeIndex,
-PeriodIndex, or TimedeltaIndex), or pass datetime-like values
-to the on or level keyword.

-Parameters
-----------
-rule : string
-    the offset string or object representing target conversion
-axis : int, optional, default 0
-closed : {'right', 'left'}
-    Which side of bin interval is closed
-label : {'right', 'left'}
-    Which bin edge label to label bucket with
-convention : {'start', 'end', 's', 'e'}
-loffset : timedelta
-    Adjust the resampled time labels
-base : int, default 0
-    For frequencies that evenly subdivide 1 day, the "origin" of the
-    aggregated intervals. For example, for '5min' frequency, base could
-    range from 0 through 4. Defaults to 0
-on : string, optional
-    For a DataFrame, column to use instead of index for resampling.
-    Column must be datetime-like.

-    .. versionadded:: 0.19.0

-level : string or int, optional
-    For a MultiIndex, level (name or number) to use for
-    resampling.  Level must be datetime-like.

-    .. versionadded:: 0.19.0

-Notes
------
-To learn more about the offset strings, please see `this link
-<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.

-Examples
---------

-Start by creating a series with 9 one minute timestamps.

->>> index = pd.date_range('1/1/2000', periods=9, freq='T')
->>> series = pd.Series(range(9), index=index)
->>> series
-2000-01-01 00:00:00    0
-2000-01-01 00:01:00    1
-2000-01-01 00:02:00    2
-2000-01-01 00:03:00    3
-2000-01-01 00:04:00    4
-2000-01-01 00:05:00    5
-2000-01-01 00:06:00    6
-2000-01-01 00:07:00    7
-2000-01-01 00:08:00    8
-Freq: T, dtype: int64

-Downsample the series into 3 minute bins and sum the values
-of the timestamps falling into a bin.

->>> series.resample('3T').sum()
-2000-01-01 00:00:00     3
-2000-01-01 00:03:00    12
-2000-01-01 00:06:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but label each
-bin using the right edge instead of the left. Please note that the
-value in the bucket used as the label is not included in the bucket,
-which it labels. For example, in the original series the
-bucket ``2000-01-01 00:03:00`` contains the value 3, but the summed
-value in the resampled bucket with the label``2000-01-01 00:03:00``
-does not include 3 (if it did, the summed value would be 6, not 3).
-To include this value close the right side of the bin interval as
-illustrated in the example below this one.

->>> series.resample('3T', label='right').sum()
-2000-01-01 00:03:00     3
-2000-01-01 00:06:00    12
-2000-01-01 00:09:00    21
-Freq: 3T, dtype: int64

-Downsample the series into 3 minute bins as above, but close the right
-side of the bin interval.

->>> series.resample('3T', label='right', closed='right').sum()
-2000-01-01 00:00:00     0
-2000-01-01 00:03:00     6
-2000-01-01 00:06:00    15
-2000-01-01 00:09:00    15
-Freq: 3T, dtype: int64

-Upsample the series into 30 second bins.

->>> series.resample('30S').asfreq()[0:5] #select first 5 rows
-2000-01-01 00:00:00   0.0
-2000-01-01 00:00:30   NaN
-2000-01-01 00:01:00   1.0
-2000-01-01 00:01:30   NaN
-2000-01-01 00:02:00   2.0
-Freq: 30S, dtype: float64

-Upsample the series into 30 second bins and fill the ``NaN``
-values using the ``pad`` method.

->>> series.resample('30S').pad()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    0
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    1
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Upsample the series into 30 second bins and fill the
-``NaN`` values using the ``bfill`` method.

->>> series.resample('30S').bfill()[0:5]
-2000-01-01 00:00:00    0
-2000-01-01 00:00:30    1
-2000-01-01 00:01:00    1
-2000-01-01 00:01:30    2
-2000-01-01 00:02:00    2
-Freq: 30S, dtype: int64

-Pass a custom function via ``apply``

->>> def custom_resampler(array_like):
-...     return np.sum(array_like)+5

->>> series.resample('3T').apply(custom_resampler)
-2000-01-01 00:00:00     8
-2000-01-01 00:03:00    17
-2000-01-01 00:06:00    26
-Freq: 3T, dtype: int64

-For DataFrame objects, the keyword ``on`` can be used to specify the
-column instead of the index for resampling.

->>> df = pd.DataFrame(data=9*[range(4)], columns=['a', 'b', 'c', 'd'])
->>> df['time'] = pd.date_range('1/1/2000', periods=9, freq='T')
->>> df.resample('3T', on='time').sum()
-                     a  b  c  d
-time
-2000-01-01 00:00:00  0  3  6  9
-2000-01-01 00:03:00  0  3  6  9
-2000-01-01 00:06:00  0  3  6  9

-For a DataFrame with MultiIndex, the keyword ``level`` can be used to
-specify on level the resampling needs to take place.

->>> time = pd.date_range('1/1/2000', periods=5, freq='T')
->>> df2 = pd.DataFrame(data=10*[range(4)],
-                       columns=['a', 'b', 'c', 'd'],
-                       index=pd.MultiIndex.from_product([time, [1, 2]])
-                       )
->>> df2.resample('3T', level=0).sum()
-                     a  b   c   d
-2000-01-01 00:00:00  0  6  12  18
-2000-01-01 00:03:00  0  4   8  12
- -
sample(self, n=None, frac=None, replace=False, weights=None, random_state=None, axis=None)
Returns a random sample of items from an axis of object.

-.. versionadded:: 0.16.1

-Parameters
-----------
-n : int, optional
-    Number of items from axis to return. Cannot be used with `frac`.
-    Default = 1 if `frac` = None.
-frac : float, optional
-    Fraction of axis items to return. Cannot be used with `n`.
-replace : boolean, optional
-    Sample with or without replacement. Default = False.
-weights : str or ndarray-like, optional
-    Default 'None' results in equal probability weighting.
-    If passed a Series, will align with target object on index. Index
-    values in weights not found in sampled object will be ignored and
-    index values in sampled object not in weights will be assigned
-    weights of zero.
-    If called on a DataFrame, will accept the name of a column
-    when axis = 0.
-    Unless weights are a Series, weights must be same length as axis
-    being sampled.
-    If weights do not sum to 1, they will be normalized to sum to 1.
-    Missing values in the weights column will be treated as zero.
-    inf and -inf values not allowed.
-random_state : int or numpy.random.RandomState, optional
-    Seed for the random number generator (if int), or numpy RandomState
-    object.
-axis : int or string, optional
-    Axis to sample. Accepts axis number or name. Default is stat axis
-    for given data type (0 for Series and DataFrames, 1 for Panels).

-Returns
--------
-A new object of same type as caller.

-Examples
---------

-Generate an example ``Series`` and ``DataFrame``:

->>> s = pd.Series(np.random.randn(50))
->>> s.head()
-0   -0.038497
-1    1.820773
-2   -0.972766
-3   -1.598270
-4   -1.095526
-dtype: float64
->>> df = pd.DataFrame(np.random.randn(50, 4), columns=list('ABCD'))
->>> df.head()
-          A         B         C         D
-0  0.016443 -2.318952 -0.566372 -1.028078
-1 -1.051921  0.438836  0.658280 -0.175797
-2 -1.243569 -0.364626 -0.215065  0.057736
-3  1.768216  0.404512 -0.385604 -1.457834
-4  1.072446 -1.137172  0.314194 -0.046661

-Next extract a random sample from both of these objects...

-3 random elements from the ``Series``:

->>> s.sample(n=3)
-27   -0.994689
-55   -1.049016
-67   -0.224565
-dtype: float64

-And a random 10% of the ``DataFrame`` with replacement:

->>> df.sample(frac=0.1, replace=True)
-           A         B         C         D
-35  1.981780  0.142106  1.817165 -0.290805
-49 -1.336199 -0.448634 -0.789640  0.217116
-40  0.823173 -0.078816  1.009536  1.015108
-15  1.421154 -0.055301 -1.922594 -0.019696
-6  -0.148339  0.832938  1.787600 -1.383767
- -
select(self, crit, axis=0)
Return data corresponding to axis labels matching criteria

-Parameters
-----------
-crit : function
-    To be called on each index (label). Should return True or False
-axis : int

-Returns
--------
-selection : type of caller
- -
set_axis(self, axis, labels)
public verson of axis assignment
- -
slice_shift(self, periods=1, axis=0)
Equivalent to `shift` without copying data. The shifted data will
-not include the dropped periods and the shifted axis will be smaller
-than the original.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative

-Notes
------
-While the `slice_shift` is faster than `shift`, you may pay for it
-later during alignment.

-Returns
--------
-shifted : same type as caller
- -
squeeze(self, axis=None)
Squeeze length 1 dimensions.

-Parameters
-----------
-axis : None, integer or string axis name, optional
-    The axis to squeeze if 1-sized.

-    .. versionadded:: 0.20.0

-Returns
--------
-scalar if 1-sized, else original object
- -
swapaxes(self, axis1, axis2, copy=True)
Interchange axes and swap values axes appropriately

-Returns
--------
-y : same as input
- -
tail(self, n=5)
Returns last n rows
- -
to_clipboard(self, excel=None, sep=None, **kwargs)
Attempt to write text representation of object to the system clipboard
-This can be pasted into Excel, for example.

-Parameters
-----------
-excel : boolean, defaults to True
-        if True, use the provided separator, writing in a csv
-        format for allowing easy pasting into excel.
-        if False, write a string representation of the object
-        to the clipboard
-sep : optional, defaults to tab
-other keywords are passed to to_csv

-Notes
------
-Requirements for your platform
-  - Linux: xclip, or xsel (with gtk or PyQt4 modules)
-  - Windows: none
-  - OS X: none
- -
to_dense(self)
Return dense representation of NDFrame (as opposed to sparse)
- -
to_hdf(self, path_or_buf, key, **kwargs)
Write the contained data to an HDF5 file using HDFStore.

-Parameters
-----------
-path_or_buf : the path (string) or HDFStore object
-key : string
-    identifier for the group in the store
-mode : optional, {'a', 'w', 'r+'}, default 'a'

-  ``'w'``
-      Write; a new file is created (an existing file with the same
-      name would be deleted).
-  ``'a'``
-      Append; an existing file is opened for reading and writing,
-      and if the file does not exist it is created.
-  ``'r+'``
-      It is similar to ``'a'``, but the file must already exist.
-format : 'fixed(f)|table(t)', default is 'fixed'
-    fixed(f) : Fixed format
-               Fast writing/reading. Not-appendable, nor searchable
-    table(t) : Table format
-               Write as a PyTables Table structure which may perform
-               worse but allow more flexible operations like searching
-               / selecting subsets of the data
-append : boolean, default False
-    For Table formats, append the input data to the existing
-data_columns :  list of columns, or True, default None
-    List of columns to create as indexed data columns for on-disk
-    queries, or True to use all columns. By default only the axes
-    of the object are indexed. See `here
-    <http://pandas.pydata.org/pandas-docs/stable/io.html#query-via-data-columns>`__.

-    Applicable only to format='table'.
-complevel : int, 1-9, default 0
-    If a complib is specified compression will be applied
-    where possible
-complib : {'zlib', 'bzip2', 'lzo', 'blosc', None}, default None
-    If complevel is > 0 apply compression to objects written
-    in the store wherever possible
-fletcher32 : bool, default False
-    If applying compression use the fletcher32 checksum
-dropna : boolean, default False.
-    If true, ALL nan rows will not be written to store.
- -
to_json(self, path_or_buf=None, orient=None, date_format=None, double_precision=10, force_ascii=True, date_unit='ms', default_handler=None, lines=False)
Convert the object to a JSON string.

-Note NaN's and None will be converted to null and datetime objects
-will be converted to UNIX timestamps.

-Parameters
-----------
-path_or_buf : the path or buffer to write the result string
-    if this is None, return a StringIO of the converted string
-orient : string

-    * Series

-      - default is 'index'
-      - allowed values are: {'split','records','index'}

-    * DataFrame

-      - default is 'columns'
-      - allowed values are:
-        {'split','records','index','columns','values'}

-    * The format of the JSON string

-      - split : dict like
-        {index -> [index], columns -> [columns], data -> [values]}
-      - records : list like
-        [{column -> value}, ... , {column -> value}]
-      - index : dict like {index -> {column -> value}}
-      - columns : dict like {column -> {index -> value}}
-      - values : just the values array
-      - table : dict like {'schema': {schema}, 'data': {data}}
-        describing the data, and the data component is
-        like ``orient='records'``.

-        .. versionchanged:: 0.20.0

-date_format : {None, 'epoch', 'iso'}
-    Type of date conversion. `epoch` = epoch milliseconds,
-    `iso` = ISO8601. The default depends on the `orient`. For
-    `orient='table'`, the default is `'iso'`. For all other orients,
-    the default is `'epoch'`.
-double_precision : The number of decimal places to use when encoding
-    floating point values, default 10.
-force_ascii : force encoded string to be ASCII, default True.
-date_unit : string, default 'ms' (milliseconds)
-    The time unit to encode to, governs timestamp and ISO8601
-    precision.  One of 's', 'ms', 'us', 'ns' for second, millisecond,
-    microsecond, and nanosecond respectively.
-default_handler : callable, default None
-    Handler to call if object cannot otherwise be converted to a
-    suitable format for JSON. Should receive a single argument which is
-    the object to convert and return a serialisable object.
-lines : boolean, default False
-    If 'orient' is 'records' write out line delimited json format. Will
-    throw ValueError if incorrect 'orient' since others are not list
-    like.

-    .. versionadded:: 0.19.0

-Returns
--------
-same type as input object with filtered info axis

-See Also
---------
-pd.read_json

-Examples
---------

->>> df = pd.DataFrame([['a', 'b'], ['c', 'd']],
-...                   index=['row 1', 'row 2'],
-...                   columns=['col 1', 'col 2'])
->>> df.to_json(orient='split')
-'{"columns":["col 1","col 2"],
-  "index":["row 1","row 2"],
-  "data":[["a","b"],["c","d"]]}'

-Encoding/decoding a Dataframe using ``'index'`` formatted JSON:

->>> df.to_json(orient='index')
-'{"row 1":{"col 1":"a","col 2":"b"},"row 2":{"col 1":"c","col 2":"d"}}'

-Encoding/decoding a Dataframe using ``'records'`` formatted JSON.
-Note that index labels are not preserved with this encoding.

->>> df.to_json(orient='records')
-'[{"col 1":"a","col 2":"b"},{"col 1":"c","col 2":"d"}]'

-Encoding with Table Schema

->>> df.to_json(orient='table')
-'{"schema": {"fields": [{"name": "index", "type": "string"},
-                        {"name": "col 1", "type": "string"},
-                        {"name": "col 2", "type": "string"}],
-             "primaryKey": "index",
-             "pandas_version": "0.20.0"},
-  "data": [{"index": "row 1", "col 1": "a", "col 2": "b"},
-           {"index": "row 2", "col 1": "c", "col 2": "d"}]}'
- -
to_msgpack(self, path_or_buf=None, encoding='utf-8', **kwargs)
msgpack (serialize) object to input file path

-THIS IS AN EXPERIMENTAL LIBRARY and the storage format
-may not be stable until a future release.

-Parameters
-----------
-path : string File path, buffer-like, or None
-    if None, return generated string
-append : boolean whether to append to an existing msgpack
-    (default is False)
-compress : type of compressor (zlib or blosc), default to None (no
-    compression)
- -
to_pickle(self, path, compression='infer')
Pickle (serialize) object to input file path.

-Parameters
-----------
-path : string
-    File path
-compression : {'infer', 'gzip', 'bz2', 'xz', None}, default 'infer'
-    a string representing the compression to use in the output file

-    .. versionadded:: 0.20.0
- -
to_sql(self, name, con, flavor=None, schema=None, if_exists='fail', index=True, index_label=None, chunksize=None, dtype=None)
Write records stored in a DataFrame to a SQL database.

-Parameters
-----------
-name : string
-    Name of SQL table
-con : SQLAlchemy engine or DBAPI2 connection (legacy mode)
-    Using SQLAlchemy makes it possible to use any DB supported by that
-    library. If a DBAPI2 object, only sqlite3 is supported.
-flavor : 'sqlite', default None
-    DEPRECATED: this parameter will be removed in a future version,
-    as 'sqlite' is the only supported option if SQLAlchemy is not
-    installed.
-schema : string, default None
-    Specify the schema (if database flavor supports this). If None, use
-    default schema.
-if_exists : {'fail', 'replace', 'append'}, default 'fail'
-    - fail: If table exists, do nothing.
-    - replace: If table exists, drop it, recreate it, and insert data.
-    - append: If table exists, insert data. Create if does not exist.
-index : boolean, default True
-    Write DataFrame index as a column.
-index_label : string or sequence, default None
-    Column label for index column(s). If None is given (default) and
-    `index` is True, then the index names are used.
-    A sequence should be given if the DataFrame uses MultiIndex.
-chunksize : int, default None
-    If not None, then rows will be written in batches of this size at a
-    time.  If None, all rows will be written at once.
-dtype : dict of column name to SQL type, default None
-    Optional specifying the datatype for columns. The SQL type should
-    be a SQLAlchemy type, or a string for sqlite3 fallback connection.
- -
to_xarray(self)
Return an xarray object from the pandas object.

-Returns
--------
-a DataArray for a Series
-a Dataset for a DataFrame
-a DataArray for higher dims

-Examples
---------
->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)})
->>> df
-   A    B    C
-0  1  foo  4.0
-1  1  bar  5.0
-2  2  foo  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (index: 3)
-Coordinates:
-  * index    (index) int64 0 1 2
-Data variables:
-    A        (index) int64 1 1 2
-    B        (index) object 'foo' 'bar' 'foo'
-    C        (index) float64 4.0 5.0 6.0

->>> df = pd.DataFrame({'A' : [1, 1, 2],
-                       'B' : ['foo', 'bar', 'foo'],
-                       'C' : np.arange(4.,7)}
-                     ).set_index(['B','A'])
->>> df
-         C
-B   A
-foo 1  4.0
-bar 1  5.0
-foo 2  6.0

->>> df.to_xarray()
-<xarray.Dataset>
-Dimensions:  (A: 2, B: 2)
-Coordinates:
-  * B        (B) object 'bar' 'foo'
-  * A        (A) int64 1 2
-Data variables:
-    C        (B, A) float64 5.0 nan 4.0 6.0

->>> p = pd.Panel(np.arange(24).reshape(4,3,2),
-                 items=list('ABCD'),
-                 major_axis=pd.date_range('20130101', periods=3),
-                 minor_axis=['first', 'second'])
->>> p
-<class 'pandas.core.panel.Panel'>
-Dimensions: 4 (items) x 3 (major_axis) x 2 (minor_axis)
-Items axis: A to D
-Major_axis axis: 2013-01-01 00:00:00 to 2013-01-03 00:00:00
-Minor_axis axis: first to second

->>> p.to_xarray()
-<xarray.DataArray (items: 4, major_axis: 3, minor_axis: 2)>
-array([[[ 0,  1],
-        [ 2,  3],
-        [ 4,  5]],
-       [[ 6,  7],
-        [ 8,  9],
-        [10, 11]],
-       [[12, 13],
-        [14, 15],
-        [16, 17]],
-       [[18, 19],
-        [20, 21],
-        [22, 23]]])
-Coordinates:
-  * items       (items) object 'A' 'B' 'C' 'D'
-  * major_axis  (major_axis) datetime64[ns] 2013-01-01 2013-01-02 2013-01-03  # noqa
-  * minor_axis  (minor_axis) object 'first' 'second'

-Notes
------
-See the `xarray docs <http://xarray.pydata.org/en/stable/>`__
- -
truncate(self, before=None, after=None, axis=None, copy=True)
Truncates a sorted NDFrame before and/or after some particular
-index value. If the axis contains only datetime values, before/after
-parameters are converted to datetime values.

-Parameters
-----------
-before : date
-    Truncate before index value
-after : date
-    Truncate after index value
-axis : the truncation axis, defaults to the stat axis
-copy : boolean, default is True,
-    return a copy of the truncated section

-Returns
--------
-truncated : type of caller
- -
tshift(self, periods=1, freq=None, axis=0)
Shift the time index, using the index's frequency if available.

-Parameters
-----------
-periods : int
-    Number of periods to move, can be positive or negative
-freq : DateOffset, timedelta, or time rule string, default None
-    Increment to use from the tseries module or time rule (e.g. 'EOM')
-axis : int or basestring
-    Corresponds to the axis that contains the Index

-Notes
------
-If freq is not specified then tries to use the freq or inferred_freq
-attributes of the index. If neither of those attributes exist, a
-ValueError is thrown

-Returns
--------
-shifted : NDFrame
- -
tz_convert(self, tz, axis=0, level=None, copy=True)
Convert tz-aware axis to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to convert
-level : int, str, default None
-    If axis ia a MultiIndex, convert a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data

-Returns
--------

-Raises
-------
-TypeError
-    If the axis is tz-naive.
- -
tz_localize(self, tz, axis=0, level=None, copy=True, ambiguous='raise')
Localize tz-naive TimeSeries to target time zone.

-Parameters
-----------
-tz : string or pytz.timezone object
-axis : the axis to localize
-level : int, str, default None
-    If axis ia a MultiIndex, localize a specific level. Otherwise
-    must be None
-copy : boolean, default True
-    Also make a copy of the underlying data
-ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise'
-    - 'infer' will attempt to infer fall dst-transition hours based on
-      order
-    - bool-ndarray where True signifies a DST time, False designates
-      a non-DST time (note that this flag is only applicable for
-      ambiguous times)
-    - 'NaT' will return NaT where there are ambiguous times
-    - 'raise' will raise an AmbiguousTimeError if there are ambiguous
-      times
-infer_dst : boolean, default False (DEPRECATED)
-    Attempt to infer fall dst-transition hours based on order

-Returns
--------

-Raises
-------
-TypeError
-    If the TimeSeries is tz-aware and tz is not None.
- -
where(self, cond, other=nan, inplace=False, axis=None, level=None, try_cast=False, raise_on_error=True)
Return an object of same shape as self and whose corresponding
-entries are from self where cond is True and otherwise are from
-other.

-Parameters
-----------
-cond : boolean NDFrame, array-like, or callable
-    If cond is callable, it is computed on the NDFrame and
-    should return boolean NDFrame or array. The callable must
-    not change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as cond.

-other : scalar, NDFrame, or callable
-    If other is callable, it is computed on the NDFrame and
-    should return scalar or NDFrame. The callable must not
-    change input NDFrame (though pandas doesn't check it).

-    .. versionadded:: 0.18.1
-        A callable can be used as other.

-inplace : boolean, default False
-    Whether to perform the operation in place on the data
-axis : alignment axis if needed, default None
-level : alignment level if needed, default None
-try_cast : boolean, default False
-    try to cast the result back to the input type (if possible),
-raise_on_error : boolean, default True
-    Whether to raise on invalid data types (e.g. trying to where on
-    strings)

-Returns
--------
-wh : same type as caller

-Notes
------
-The where method is an application of the if-then idiom. For each
-element in the calling DataFrame, if ``cond`` is ``True`` the
-element is used; otherwise the corresponding element from the DataFrame
-``other`` is used.

-The signature for :func:`DataFrame.where` differs from
-:func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to
-``np.where(m, df1, df2)``.

-For further details and examples see the ``where`` documentation in
-:ref:`indexing <indexing.where_mask>`.

-Examples
---------
->>> s = pd.Series(range(5))
->>> s.where(s > 0)
-0    NaN
-1    1.0
-2    2.0
-3    3.0
-4    4.0

->>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
->>> m = df % 3 == 0
->>> df.where(m, -df)
-   A  B
-0  0 -1
-1 -2  3
-2 -4 -5
-3  6 -7
-4 -8  9
->>> df.where(m, -df) == np.where(m, df, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True
->>> df.where(m, -df) == df.mask(~m, -df)
-      A     B
-0  True  True
-1  True  True
-2  True  True
-3  True  True
-4  True  True

-See Also
---------
-:func:`DataFrame.mask`
- -
xs(self, key, axis=0, level=None, drop_level=True)
Returns a cross-section (row(s) or column(s)) from the
-Series/DataFrame. Defaults to cross-section on the rows (axis=0).

-Parameters
-----------
-key : object
-    Some label contained in the index, or partially in a MultiIndex
-axis : int, default 0
-    Axis to retrieve cross-section on
-level : object, defaults to first n levels (n=1 or len(key))
-    In case of a key partially contained in a MultiIndex, indicate
-    which levels are used. Levels can be referred by label or position.
-drop_level : boolean, default True
-    If False, returns object with same levels as self.

-Examples
---------
->>> df
-   A  B  C
-a  4  5  2
-b  4  0  9
-c  9  7  3
->>> df.xs('a')
-A    4
-B    5
-C    2
-Name: a
->>> df.xs('C', axis=1)
-a    2
-b    9
-c    3
-Name: C

->>> df
-                    A  B  C  D
-first second third
-bar   one    1      4  1  8  9
-      two    1      7  5  5  0
-baz   one    1      6  6  8  0
-      three  2      5  3  5  3
->>> df.xs(('baz', 'three'))
-       A  B  C  D
-third
-2      5  3  5  3
->>> df.xs('one', level=1)
-             A  B  C  D
-first third
-bar   1      4  1  8  9
-baz   1      6  6  8  0
->>> df.xs(('baz', 2), level=[0, 'third'])
-        A  B  C  D
-second
-three   5  3  5  3

-Returns
--------
-xs : Series or DataFrame

-Notes
------
-xs is only for getting, not setting values.

-MultiIndex Slicers is a generic way to get/set values on any level or
-levels.  It is a superset of xs functionality, see
-:ref:`MultiIndex Slicers <advanced.mi_slicers>`
- -
-Data descriptors inherited from pandas.core.generic.NDFrame:
-
at
-
Fast label-based scalar accessor

-Similarly to ``loc``, ``at`` provides **label** based scalar lookups.
-You can also set using these indexers.
-
-
blocks
-
Internal property, property synonym for as_blocks()
-
-
iat
-
Fast integer location scalar accessor.

-Similarly to ``iloc``, ``iat`` provides **integer** based lookups.
-You can also set using these indexers.
-
-
iloc
-
Purely integer-location based indexing for selection by position.

-``.iloc[]`` is primarily integer position based (from ``0`` to
-``length-1`` of the axis), but may also be used with a boolean
-array.

-Allowed inputs are:

-- An integer, e.g. ``5``.
-- A list or array of integers, e.g. ``[4, 3, 0]``.
-- A slice object with ints, e.g. ``1:7``.
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.iloc`` will raise ``IndexError`` if a requested indexer is
-out-of-bounds, except *slice* indexers which allow out-of-bounds
-indexing (this conforms with python/numpy *slice* semantics).

-See more at :ref:`Selection by Position <indexing.integer>`
-
-
ix
-
A primarily label-location based indexer, with integer position
-fallback.

-``.ix[]`` supports mixed integer and label based access. It is
-primarily label based, but will fall back to integer positional
-access unless the corresponding axis is of integer type.

-``.ix`` is the most general indexer and will support any of the
-inputs in ``.loc`` and ``.iloc``. ``.ix`` also supports floating
-point label schemes. ``.ix`` is exceptionally useful when dealing
-with mixed positional and label based hierachical indexes.

-However, when an axis is integer based, ONLY label based access
-and not positional access is supported. Thus, in such cases, it's
-usually better to be explicit and use ``.iloc`` or ``.loc``.

-See more at :ref:`Advanced Indexing <advanced>`.
-
-
loc
-
Purely label-location based indexer for selection by label.

-``.loc[]`` is primarily label based, but may also be used with a
-boolean array.

-Allowed inputs are:

-- A single label, e.g. ``5`` or ``'a'``, (note that ``5`` is
-  interpreted as a *label* of the index, and **never** as an
-  integer position along the index).
-- A list or array of labels, e.g. ``['a', 'b', 'c']``.
-- A slice object with labels, e.g. ``'a':'f'`` (note that contrary
-  to usual python slices, **both** the start and the stop are included!).
-- A boolean array.
-- A ``callable`` function with one argument (the calling Series, DataFrame
-  or Panel) and that returns valid output for indexing (one of the above)

-``.loc`` will raise a ``KeyError`` when the items are not found.

-See more at :ref:`Selection by Label <indexing.label>`
-
-
-Data and other attributes inherited from pandas.core.generic.NDFrame:
-
is_copy = None
- -
-Methods inherited from pandas.core.base.PandasObject:
-
__dir__(self)
Provide method name lookup and completion
-Only provide 'public' methods
- -
__sizeof__(self)
Generates the total memory usage for a object that returns
-either a value or Series of values
- -
-Methods inherited from pandas.core.base.StringMixin:
-
__bytes__(self)
Return a string representation for a particular object.

-Invoked by bytes(obj) in py3 only.
-Yields a bytestring in both py2/py3.
- -
__repr__(self)
Return a string representation for a particular object.

-Yields Bytestring in Py2, Unicode String in py3.
- -
__str__(self)
Return a string representation for a particular Object

-Invoked by str(df) in both py2/py3.
-Yields Bytestring in Py2, Unicode String in py3.
- -

- - - - - -
 
-Functions
       
Vector(*args, units=None)
-
abs(*args)
-
cart2pol(x, y, z=None)
-
contour(df, **options)
Makes a contour plot from a DataFrame.

-Note: columns and index must be numerical

-df: DataFrame
-
decorate(**kwargs)
Decorate the current axes.

-Call decorate with keyword arguments like

-decorate(title='Title',
-         xlabel='x',
-         ylabel='y')

-The keyword arguments can be any of the axis properties
-defined by Matplotlib.  To see the list, run plt.getp(plt.gca())

-In addition, you can use `legend=False` to suppress the legend.

-And you can use `loc` to indicate the location of the legend
-(the default value is 'best')
-
fit_leastsq(error_func, params, data, **kwargs)
Find the parameters that yield the best fit for the data.

-`params` can be a sequence, array, or Series

-error_func: function that computes a sequence of errors
-params: initial guess for the best parameters
-data: the data to be fit; will be passed to min_fun
-kwargs: any other arguments are passed to leastsq
-
flip(p=0.5)
-
fsolve(func, x0, *args, **kwargs)
Return the roots of the (non-linear) equations
-defined by func(x) = 0 given a starting estimate.

-Uses scipy.optimize.fsolve, with extra error-checking.

-func: function to find the roots of
-x0: scalar or array, initial guess
-args: additional positional arguments are passed along to fsolve,
-      which passes them along to func

-returns: solution as an array
-
interp_inverse(series, **options)
Interpolate the inverse function of a Series.

-series: Series object, represents a mapping from `a` to `b`
-kind: string, which kind of iterpolation
-options: keyword arguments passed to interpolate

-returns: interpolation object, can be used as a function
-         from `b` to `a`
-
interpolate(series, **options)
Creates an interpolation function.

-series: Series object
-options: any legal options to scipy.interpolate.interp1d

-returns: function that maps from the index of the series to values
-
label_axes(xlabel=None, ylabel=None, title=None, **kwargs)
Puts labels and title on the axes.

-xlabel: string
-ylabel: string
-title: string

-kwargs: options passed to pyplot
-
legend(**kwargs)
-
linrange(start=0, stop=None, step=1, **kwargs)
Returns an array of evenly-spaced values in the interval [start, stop].

-This function works best if the space between start and stop
-is divisible by step; otherwise the results might be surprising.

-By default, the last value in the array is `stop` (at least approximately).
-If you provide the keyword argument `endpoint=False`, the last value
-in the array is `stop-step`. 

-start: first value
-stop: last value
-step: space between values

-Also accepts the same keyword arguments as np.linspace.  See
-https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html

-returns: array or Quantity
-
linspace(start, stop, num=50, **kwargs)
Returns an array of evenly-spaced values in the interval [start, stop].

-start: first value
-stop: last value
-num: number of values

-Also accepts the same keyword arguments as np.linspace.  See
-https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html

-returns: array or Quantity
-
max(*args)
-
max_bounded(max_func, bounds, *args, **options)
Finds the input value that maximizes `max_func`.

-min_func: computes the function to be maximized
-bounds: sequence of two values, lower and upper bounds of the
-        range to be searched
-args: any additional positional arguments are passed to max_func
-options: any keyword arguments are passed as options to minimize_scalar

-returns: OptimizeResult object 
-         (see https://docs.scipy.org/doc/scipy/
-              reference/generated/scipy.optimize.minimize_scalar.html)
-
min(*args)
-
min_bounded(min_func, bounds, *args, **options)
Finds the input value that minimizes `min_func`.

-min_func: computes the function to be minimized
-bounds: sequence of two values, lower and upper bounds of the
-        range to be searched
-args: any additional positional arguments are passed to min_func
-options: any keyword arguments are passed as options to minimize_scalar

-returns: OptimizeResult object 
-         (see https://docs.scipy.org/doc/scipy/
-              reference/generated/scipy.optimize.minimize_scalar.html)
-
newfig(**kwargs)
Creates a new figure.
-
nolegend()
-
plot(*args, **kwargs)
Makes line plots.

-args can be:
-  plot(y)
-  plot(y, style_string)
-  plot(x, y)
-  plot(x, y, style_string)

-kwargs are the same as for pyplot.plot

-If x or y have attributes label and/or units,
-label the axes accordingly.
-
pol2cart(theta, rho, z=None)
-
remove_from_legend(bad_labels)
Removes some labels from the legend.

-bad_labels: sequence of strings
-
round(*args)
-
run_odeint(system, slope_func, **kwargs)
Runs a simulation of the system.

-`system` should contain system parameters and `ts`, which
-is an array or Series that specifies the time when the
-solution will be computed.

-Adds a DataFrame to the System: results

-system: System object
-slope_func: function that computes slopes
-
savefig(filename, *args, **kwargs)
Save the current figure.

-filename: string
-
sleep(...)
sleep(seconds)

-Delay execution for a given number of seconds.  The argument may be
-a floating point number for subsecond precision.
-
subplot(nrows, ncols, plot_number, **kwargs)
-
subplots(*args, **kwargs)
-
sum(*args)
-
underride(d, **options)
Add key-value pairs to d only if key is not in d.

-If d is None, create a new dictionary.

-d: dictionary
-options: keyword args to add to d
-
units_off()
Make all quantities behave as if they were dimensionless.
-
units_on()
Restore the saved behavior of quantities.
-
unpack(series)
Make the names in `series` available as globals.

-series: Series with variables names in the index
-

- - - - - -
 
-Data
       SIMPLOT = <modsim.Simplot object>
-UNITS = <pint.unit.UnitRegistry object>
-
dimensionality
-
Unit's dimensionality (e.g. {length: 1, time: -1})

-This is a simplified version of this method that does no caching.

-returns: dimensionality
-
-
-exp = <ufunc 'exp'>
-log = <ufunc 'log'>
-logger = <Logger modsim.py (WARNING)>
-pi = 3.141592653589793
-sqrt = <ufunc 'sqrt'>
- \ No newline at end of file diff --git a/code/modsim.py b/code/modsim.py deleted file mode 100644 index 55b643723..000000000 --- a/code/modsim.py +++ /dev/null @@ -1,1365 +0,0 @@ -""" -Code from Modeling and Simulation in Python. - -Copyright 2017 Allen Downey - -License: https://creativecommons.org/licenses/by/4.0) -""" - -import logging -logger = logging.getLogger(name='modsim.py') - -#TODO: Make this Python 3.7 when conda is ready - -# make sure we have Python 3.6 or better -import sys -if sys.version_info < (3, 6): - logger.warning('modsim.py depends on Python 3.6 features.') - -import inspect -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -import scipy -import sympy - -import seaborn as sns -sns.set(style='white', font_scale=1.2) - -import pint -UNITS = pint.UnitRegistry() -Quantity = UNITS.Quantity - -# expose some names so we can use them without dot notation -from copy import copy -from numpy import sqrt, log, exp, pi -from pandas import DataFrame, Series -from time import sleep - -from scipy.interpolate import interp1d -from scipy.interpolate import InterpolatedUnivariateSpline -from scipy.integrate import odeint -from scipy.integrate import solve_ivp -from scipy.optimize import leastsq -from scipy.optimize import minimize_scalar - -import scipy.optimize - - -def flip(p=0.5): - """Flips a coin with the given probability. - - p: float 0-1 - - returns: boolean (True or False) - """ - return np.random.random() < p - - -# For all the built-in Python functions that do math, -# let's use the NumPy version instead. - -abs = np.abs -min = np.min -max = np.max -pow = np.power -sum = np.sum -round = np.round - - - -def cart2pol(x, y, z=None): - """Convert Cartesian coordinates to polar. - - x: number or sequence - y: number or sequence - z: number or sequence (optional) - - returns: theta, rho OR theta, rho, z - """ - x = np.asarray(x) - y = np.asarray(y) - - # TODO: use hypot? - rho = np.sqrt(x**2 + y**2) - theta = np.arctan2(y, x) - - if z is None: - return theta, rho - else: - return theta, rho, z - - -def pol2cart(theta, rho, z=None): - """Convert polar coordinates to Cartesian. - - theta: number or sequence - rho: number or sequence - z: number or sequence (optional) - - returns: x, y OR x, y, z - """ - if hasattr(theta, 'units'): - if theta.units == UNITS.degree: - theta = theta.to(UNITS.radian) - if theta.units != UNITS.radian: - msg = """In pol2cart, theta must be either a number or - a Quantity in degrees or radians.""" - raise ValueError(msg) - - x = rho * np.cos(theta) - y = rho * np.sin(theta) - - if z is None: - return x, y - else: - return x, y, z - - -def linspace(start, stop, num=50, **options): - """Returns an array of evenly-spaced values in the interval [start, stop]. - - start: first value - stop: last value - num: number of values - - Also accepts the same keyword arguments as np.linspace. See - https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html - - returns: array or Quantity - """ - underride(options, dtype=np.float64) - - # see if either of the arguments has units - units = getattr(start, 'units', None) - units = getattr(stop, 'units', units) - - array = np.linspace(start, stop, num, **options) - if units: - array = array * units - return array - - -def linrange(start=0, stop=None, step=1, **options): - """Returns an array of evenly-spaced values in the interval [start, stop]. - - This function works best if the space between start and stop - is divisible by step; otherwise the results might be surprising. - - By default, the last value in the array is `stop-step` - (at least approximately). - If you provide the keyword argument `endpoint=True`, - the last value in the array is `stop`. - - start: first value - stop: last value - step: space between values - - returns: array or Quantity - """ - if stop is None: - stop = start - start = 0 - - # TODO: what breaks if we don't make the dtype float? - #underride(options, endpoint=True, dtype=np.float64) - underride(options, endpoint=False) - - # see if any of the arguments has units - units = getattr(start, 'units', None) - units = getattr(stop, 'units', units) - units = getattr(step, 'units', units) - - n = np.round((stop - start) / step) - if options['endpoint']: - n += 1 - - array = np.full(int(n), magnitude(step)) - array[0] = magnitude(start) - array = np.cumsum(array) - - if units: - array = array * units - return array - - -def magnitude(x): - """Returns the magnitude of a Quantity or number. - - x: Quantity or number - - returns: number - """ - return x.magnitude if isinstance(x, Quantity) else x - - -def magnitudes(x): - """Returns the magnitude of a Quantity or number, or sequence. - - x: Quantity or number, or sequence - - returns: number - """ - try: - return [magnitude(elt) for elt in x] - except TypeError: # not iterable - return magnitude(x) - - -def units(x): - """Returns the units of a Quantity or number. - - x: Quantity or number - - returns: Unit object or 1 - """ - return x.units if isinstance(x, Quantity) else 1 - - -def remove_units(series): - """Removes units from the values in a Series. - - Only removes units from top-level values; - does not traverse nested values. - - returns: new Series object - """ - res = copy(series) - print(type(res)) - for label, value in res.iteritems(): - res[label] = magnitude(value) - return res - - -def require_units(x, units): - """Apply units to `x`, if necessary. - - x: Quantity or number - units: Pint Units object - - returns: Quantity - """ - if isinstance(x, Quantity): - return x.to(units) - else: - return Quantity(x, units) - - -def fit_leastsq(error_func, params, *args, **options): - """Find the parameters that yield the best fit for the data. - - `params` can be a sequence, array, or Series - - Whatever arguments are provided are passed along to `error_func` - - error_func: function that computes a sequence of errors - params: initial guess for the best parameters - data: the data to be fit; will be passed to min_fun - options: any other arguments are passed to leastsq - """ - # if any of the params are quantities, strip the units - x0 = [magnitude(x) for x in params] - - # override `full_output` so we get a message if something goes wrong - options['full_output'] = True - - # run leastsq - with units_off(): - best_params, cov_x, infodict, mesg, ier = leastsq(error_func, - x0=x0, args=args, **options) - - details = ModSimSeries(infodict) - details.set(cov_x=cov_x, mesg=mesg, ier=ier) - - # if we got a Params object, we should return a Params object - if isinstance(params, Params): - best_params = Params(Series(best_params, params.index)) - - # return the best parameters and details - return best_params, details - - -def min_bounded(min_func, bounds, *args, **options): - """Finds the input value that minimizes `min_func`. - - Wrapper for https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize_scalar.html - - min_func: computes the function to be minimized - bounds: sequence of two values, lower and upper bounds of the - range to be searched - args: any additional positional arguments are passed to min_func - options: any keyword arguments are passed as options to minimize_scalar - - returns: ModSimSeries object - """ - # try: - # print(bounds[0]) - # min_func(bounds[0], *args) - # except Exception as e: - # msg = """Before running scipy.integrate.min_bounded, I tried - # running the slope function you provided with the - # initial conditions in system and t=0, and I got - # the following error:""" - # logger.error(msg) - # raise(e) - - underride(options, xatol=1e-3) - - # TODO: Do we need to remove units from bounds? - - with units_off(): - res = minimize_scalar(min_func, - bracket=bounds, - bounds=bounds, - args=args, - method='bounded', - options=options) - - if not res.success: - msg = """scipy.optimize.minimize_scalar did not succeed. - The message it returned is %s""" % res.message - raise Exception(msg) - - return ModSimSeries(res) - - -def max_bounded(max_func, bounds, *args, **options): - """Finds the input value that maximizes `max_func`. - - Wrapper for https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize_scalar.html - - min_func: computes the function to be maximized - bounds: sequence of two values, lower and upper bounds of the - range to be searched - args: any additional positional arguments are passed to max_func - options: any keyword arguments are passed as options to minimize_scalar - - returns: ModSimSeries object - """ - def min_func(*args): - return -max_func(*args) - - res = min_bounded(min_func, bounds, *args, **options) - # we have to negate the function value before returning res - res.fun = -res.fun - return res - - -def minimize(min_func, x0, *args, **options): - """Finds the input value that minimizes `min_func`. - - Wrapper for https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html - - min_func: computes the function to be minimized - x0: initial guess - args: any additional positional arguments are passed to min_func - options: any keyword arguments are passed as options to minimize_scalar - - returns: ModSimSeries object - """ - underride(options, tol=1e-3) - - with units_off(): - res = scipy.optimize.minimize(min_func, x0, *args, **options) - - return ModSimSeries(res) - - -def run_odeint(system, slope_func, **options): - """Integrates an ordinary differential equation. - - `system` should contain system parameters and `ts`, which - is an array or Series that specifies the time when the - solution will be computed. - - system: System object - slope_func: function that computes slopes - - returns: TimeFrame - """ - # make sure `system` contains `ts` - if not hasattr(system, 'ts'): - msg = """It looks like `system` does not contain `ts` - as a system variable. `ts` should be an array - or Series that specifies the times when the - solution will be computed:""" - raise ValueError(msg) - - # make sure `system` contains `init` - if not hasattr(system, 'init'): - msg = """It looks like `system` does not contain `init` - as a system variable. `init` should be a State - object that specifies the initial condition:""" - raise ValueError(msg) - - # make the system parameters available as globals - unpack(system) - - # try running the slope function with the initial conditions - try: - slope_func(init, ts[0], system) - except Exception as e: - msg = """Before running scipy.integrate.odeint, I tried - running the slope function you provided with the - initial conditions in system and t=0, and I got - the following error:""" - logger.error(msg) - raise(e) - - # when odeint calls slope_func, it should pass `system` as - # the third argument. To make that work, we have to make a - # tuple with a single element and pass the tuple to odeint as `args` - args = (system,) - - # now we're ready to run `odeint` with `init` and `ts` from `system` - with units_off(): - array = odeint(slope_func, list(init), ts, args, **options) - - # the return value from odeint is an array, so let's pack it into - # a TimeFrame with appropriate columns and index - frame = TimeFrame(array, columns=init.index, index=ts, dtype=np.float64) - return frame - - -def run_ode_solver(system, slope_func, **options): - """Computes a numerical solution to a differential equation. - - `system` must contain `init` with initial conditions, - `t_0` with the start time, and `t_end` with the end time. - - It can contain any other parameters required by the slope function. - - `options` can be any legal options of `scipy.integrate.solve_ivp` - - system: System object - slope_func: function that computes slopes - - returns: TimeFrame - """ - # make sure `system` contains `init` - if not hasattr(system, 'init'): - msg = """It looks like `system` does not contain `init` - as a system variable. `init` should be a State - object that specifies the initial condition:""" - raise ValueError(msg) - - # make sure `system` contains `t_end` - if not hasattr(system, 't_end'): - msg = """It looks like `system` does not contain `t_end` - as a system variable. `t_end` should be the - final time:""" - raise ValueError(msg) - - # make the system parameters available as globals - unpack(system) - - # the default value for t_0 is 0 - t_0 = getattr(system, 't_0', 0) - - # try running the slope function with the initial conditions - # try: - # slope_func(init, t_0, system) - # except Exception as e: - # msg = """Before running scipy.integrate.solve_ivp, I tried - # running the slope function you provided with the - # initial conditions in `system` and `t=t_0` and I got - # the following error:""" - # logger.error(msg) - # raise(e) - - # wrap the slope function to reverse the arguments and add `system` - f = lambda t, y: slope_func(y, t, system) - - def wrap_event(event): - """Wrap the event functions. - - Make events terminal by default. - """ - wrapped = lambda t, y: event(y, t, system) - wrapped.terminal = getattr(event, 'terminal', True) - wrapped.direction = getattr(event, 'direction', 0) - return wrapped - - # wrap the event functions so they take the right arguments - events = options.pop('events', []) - try: - events = [wrap_event(event) for event in events] - except TypeError: - events = wrap_event(events) - - # remove dimensions from the initial conditions. - # we need this because otherwise `init` gets copied into the - # results array along with its units - # try: - # y_0 = [magnitude(x) for x in init] - # except TypeError: - # y_0 = [magnitude(init)] - y_0 = [magnitude(x) for x in init] - - # run the solver - with units_off(): - bunch = solve_ivp(f, [t_0, t_end], y_0, events=events, **options) - - # separate the results from the details - y = bunch.pop('y') - t = bunch.pop('t') - details = ModSimSeries(bunch) - - # pack the results into a TimeFrame - results = TimeFrame(np.transpose(y), index=t, columns=init.index) - return results, details - - -def fsolve(func, x0, *args, **options): - """Return the roots of the (non-linear) equations - defined by func(x) = 0 given a starting estimate. - - Uses scipy.optimize.fsolve, with extra error-checking. - - func: function to find the roots of - x0: scalar or array, initial guess - args: additional positional arguments are passed along to fsolve, - which passes them along to func - - returns: solution as an array - """ - # make sure we can run the given function with x0 - try: - func(x0, *args) - except Exception as e: - msg = """Before running scipy.optimize.fsolve, I tried - running the error function you provided with the x0 - you provided, and I got the following error:""" - logger.error(msg) - raise(e) - - # make the tolerance more forgiving than the default - underride(options, xtol=1e-6) - - x0 = magnitude(x0) - - # run fsolve - with units_off(): - result = scipy.optimize.fsolve(func, x0, args=args, **options) - - return result - - -def crossings(series, value): - """Find the labels where the series passes through value. - - The labels in series must be increasing numerical values. - - series: Series - value: number - - returns: sequence of labels - """ - interp = InterpolatedUnivariateSpline(series.index, series-value) - return interp.roots() - - -def interpolate(series, **options): - """Creates an interpolation function. - - series: Series object - options: any legal options to scipy.interpolate.interp1d - - returns: function that maps from the index of the series to values - """ - # TODO: add error checking for nonmonotonicity - - if sum(series.index.isnull()): - msg = """The Series you passed to interpolate contains - NaN values in the index, which would result in - undefined behavior. So I'm putting a stop to that.""" - raise ValueError(msg) - - # make the interpolate function extrapolate past the ends of - # the range, unless `options` already specifies a value for `fill_value` - underride(options, fill_value='extrapolate') - - # call interp1d, which returns a new function object - interp_func = interp1d(series.index, series.values, **options) - - units = getattr(series, 'units', None) - if units: - return lambda x: Quantity(interp_func(x), units) - else: - return interp_func - - -def interp_inverse(series, **options): - """Interpolate the inverse function of a Series. - - series: Series object, represents a mapping from `a` to `b` - kind: string, which kind of iterpolation - options: keyword arguments passed to interpolate - - returns: interpolation object, can be used as a function - from `b` to `a` - """ - inverse = Series(series.index, index=series.values) - T = interpolate(inverse, **options) - return T - - -def unpack(series): - """Make the names in `series` available as globals. - - series: Series with variables names in the index - """ - # TODO: Make this a context manager, so the syntax is - # with series: - # and maybe even add an __exit__ that copies changes back - frame = inspect.currentframe() - caller = frame.f_back - caller.f_globals.update(series) - - -def source_code(obj): - """Prints the source code for a given object. - - obj: function or method object - """ - print(inspect.getsource(obj)) - - -def underride(d, **options): - """Add key-value pairs to d only if key is not in d. - - If d is None, create a new dictionary. - - d: dictionary - options: keyword args to add to d - """ - if d is None: - d = {} - - for key, val in options.items(): - d.setdefault(key, val) - - return d - - -def plot(*args, **options): - """Makes line plots. - - args can be: - plot(y) - plot(y, style_string) - plot(x, y) - plot(x, y, style_string) - - options are the same as for pyplot.plot - """ - # TODO: add lines to REPLOT_CACHE - - x, y, style = parse_plot_args(*args, **options) - if isinstance(x, pd.DataFrame) or isinstance(y, pd.DataFrame): - raise ValueError("modsimpy.plot can't handle DataFrames.") - - if x is None: - if isinstance(y, np.ndarray): - x = np.arange(len(y)) - - if isinstance(y, pd.Series): - x = y.index - y = y.values - - x = magnitudes(x) - y = magnitudes(y) - underride(options, linewidth=3, alpha=0.6) - - if style is not None: - lines = plt.plot(x, y, style, **options) - else: - lines = plt.plot(x, y, **options) - return lines - -REPLOT_CACHE = {} - -def replot(*args, **options): - """ - """ - try: - label = options['label'] - except KeyError: - raise ValueError('To use replot, you must provide a label argument.') - - axes = plt.gca() - key = (axes, label) - - if key not in REPLOT_CACHE: - lines = plot(*args, **options) - if len(lines) != 1: - raise ValueError('Replot only works with a single plotted element.') - REPLOT_CACHE[key] = lines[0] - return lines - - line = REPLOT_CACHE[key] - x, y, style = parse_plot_args(*args, **options) - line.set_xdata(x) - line.set_ydata(y) - - -def parse_plot_args(*args, **options): - """Parse the args the same way plt.plot does.""" - x = None - y = None - style = None - - if len(args) == 1: - y = args[0] - elif len(args) == 2: - if isinstance(args[1], str): - y, style = args - else: - x, y = args - elif len(args) == 3: - x, y, style = args - - return x, y, style - - -def contour(df, **options): - """Makes a contour plot from a DataFrame. - - Note: columns and index must be numerical - - df: DataFrame - """ - x = df.columns - y = df.index - X, Y = np.meshgrid(x, y) - cs = plt.contour(X, Y, df, **options) - plt.clabel(cs, inline=1, fontsize=10) - - -def savefig(filename, **options): - """Save the current figure. - - Keyword arguments are passed along to plt.savefig - - https://matplotlib.org/api/_as_gen/matplotlib.pyplot.savefig.html - - filename: string - """ - print('Saving figure to file', filename) - plt.savefig(filename, **options) - - -def decorate(**options): - """Decorate the current axes. - - Call decorate with keyword arguments like - - decorate(title='Title', - xlabel='x', - ylabel='y') - - The keyword arguments can be any of the axis properties - - https://matplotlib.org/api/axes_api.html - - In addition, you can use `legend=False` to suppress the legend. - - And you can use `loc` to indicate the location of the legend - (the default value is 'best') - """ - loc = options.pop('loc', 'best') - if options.pop('legend', True): - legend(loc=loc) - - plt.gca().set(**options) - plt.tight_layout() - - -def legend(**options): - """Draws a legend only if there is at least one labeled item. - - options are passed to plt.legend() - https://matplotlib.org/api/_as_gen/matplotlib.pyplot.legend.html - - """ - underride(options, loc='best') - - ax = plt.gca() - handles, labels = ax.get_legend_handles_labels() - ax.legend(handles, labels, **options) - - -def remove_from_legend(bad_labels): - """Removes some labels from the legend. - - bad_labels: sequence of strings - """ - ax = plt.gca() - handles, labels = ax.get_legend_handles_labels() - handle_list, label_list = [], [] - for handle, label in zip(handles, labels): - if label not in bad_labels: - handle_list.append(handle) - label_list.append(label) - ax.legend(handle_list, label_list) - - -# TODO: Either finish SubPlots or remove it -class SubPlots: - - def __init__(self, fig, axes_seq): - self.fig = fig - self.axes_seq = axes_seq - self.current_axes_index = 0 - - def current_axes(self): - return self.axes_seq(self.current_axes_index) - - # TODO: consider making SubPlots iterable - def next_axes(self): - self.current_axes_index += 1 - return self.current_axes() - - -def subplots(*args, **options): - fig, axes_seq = plt.subplots(*args, **options) - return SubPlots(fig, axes_seq) - - -def subplot(nrows, ncols, plot_number, **options): - figsize = {(2, 1): (8, 8), - (3, 1): (8, 10)} - key = nrows, ncols - default = (8, 5.5) - width, height = figsize.get(key, default) - - plt.subplot(nrows, ncols, plot_number, **options) - fig = plt.gcf() - fig.set_figwidth(width) - fig.set_figheight(height) - - -class ModSimSeries(pd.Series): - """Modified version of a Pandas Series, - with a few changes to make it more suited to our purpose. - - In particular: - - 1. I provide a more consistent __init__ method. - - 2. Series provides two special variables called - `dt` and `T` that cause problems if we try to use those names - as variables. I override them so they can be used variable names. - - 3. Series doesn't provide a good _repr_html, so it doesn't look - good in Jupyter notebooks. - - 4. ModSimSeries provides a set() method that takes keyword arguments. - """ - - def __init__(self, *args, **kwargs): - """Initialize a Series. - - Note: this cleans up a weird Series behavior, which is - that Series() and Series([]) yield different results. - See: https://github.com/pandas-dev/pandas/issues/16737 - """ - if args or kwargs: - underride(kwargs, copy=True) - super().__init__(*args, **kwargs) - else: - super().__init__([], dtype=np.float64) - - def _repr_html_(self): - """Returns an HTML representation of the series. - - Mostly used for Jupyter notebooks. - """ - df = pd.DataFrame(self.values, index=self.index, columns=['values']) - return df._repr_html_() - - def __copy__(self, deep=True): - series = super().copy(deep=deep) - return self.__class__(series) - - copy = __copy__ - - def set(self, **kwargs): - """Uses keyword arguments to update the Series in place. - - Example: series.set(a=1, b=2) - """ - for name, value in kwargs.items(): - self[name] = value - - @property - def dt(self): - """Intercept the Series accessor object so we can use `dt` - as a row label and access it using dot notation. - - https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.dt.html - """ - return self.loc['dt'] - - @property - def T(self): - """Intercept the Series accessor object so we can use `T` - as a row label and access it using dot notation. - - https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.T.html - """ - return self.loc['T'] - - -def get_first_label(series): - """Returns the label of the first element.""" - return series.index[0] - -def get_last_label(series): - """Returns the label of the first element.""" - return series.index[-1] - -def get_index_label(series, i): - """Returns the ith label in the index.""" - return series.index[i] - -def get_first_value(series): - """Returns the value of the first element.""" - return series.values[0] - -def get_last_value(series): - """Returns the value of the first element.""" - return series.values[-1] - -def gradient(series): - """Computes the numerical derivative of a series.""" - a = np.gradient(series, series.index) - return TimeSeries(a, series.index) - - -class TimeSeries(ModSimSeries): - """Represents a mapping from times to values.""" - pass - - -class SweepSeries(ModSimSeries): - """Represents a mapping from parameter values to metrics.""" - pass - - -class System(ModSimSeries): - """Contains system variables and their values. - - Takes keyword arguments and stores them as rows. - """ - - def __init__(self, *args, **kwargs): - """Initialize the series. - - If there are no positional arguments, use kwargs. - - If there is one positional argument, copy it and add - in the kwargs. - - More than one positional argument is an error. - """ - if len(args) == 0: - super().__init__(list(kwargs.values()), index=kwargs) - elif len(args) == 1: - super().__init__(*args, copy=True) - self.set(**kwargs) - else: - msg = '__init__() takes at most one positional argument' - raise TypeError(msg) - - -class State(System): - """Contains state variables and their values. - - Takes keyword arguments and stores them as rows. - """ - pass - - -class Condition(System): - """Represents the condition of a system. - - Condition objects are often used to construct a System object. - """ - pass - - -class Params(System): - """Represents a set of parameters. - """ - pass - - -def compute_abs_diff(seq): - xs = np.asarray(seq) - diff = np.ediff1d(xs, np.nan) - if isinstance(seq, Series): - return Series(diff, seq.index) - else: - return diff - -def compute_rel_diff(seq): - xs = np.asarray(seq) - diff = np.ediff1d(xs, np.nan) - return diff / seq - - -class ModSimDataFrame(pd.DataFrame): - """ModSimDataFrame is a modified version of a Pandas DataFrame, - with a few changes to make it more suited to our purpose. - - In particular: - - 1. DataFrame provides two special variables called - `dt` and `T` that cause problems if we try to use those names - as variables. I override them so they can be used as row labels. - - 2. When you select a row or column from a ModSimDataFrame, you get - back an appropriate subclass of Series: TimeSeries, SweepSeries, - or ModSimSeries. - """ - column_constructor = ModSimSeries - row_constructor = ModSimSeries - - def __init__(self, *args, **options): - # TODO: currently ModSimDataFrame underrides to float64 and - # ModSimSeries does not. Does this inconsistency make sense? - # underride(options, dtype=np.float64) - super().__init__(*args, **options) - - def __getitem__(self, key): - """Intercept the column getter to return the right subclass of Series. - """ - obj = super().__getitem__(key) - if isinstance(obj, Series): - obj = self.column_constructor(obj) - return obj - - @property - def dt(self): - """Intercept the Series accessor object so we can use `dt` - as a column label and access it using dot notation. - - https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.dt.html - """ - return self['dt'] - - @property - def T(self): - """Intercept the Series accessor object so we can use `T` - as a column label and access it using dot notation. - - https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.T.html - """ - return self['T'] - - @property - def row(self): - """Gets or sets a row. - - Returns a wrapper for the Pandas LocIndexer, so when we look up a row - we get the right kind of ModSimSeries. - - returns ModSimLocIndexer - """ - li = self.loc - return ModSimLocIndexer(li, self.row_constructor) - - -class ModSimLocIndexer: - """Wraps a Pandas LocIndexer.""" - - def __init__(self, li, constructor): - """Save the LocIndexer and constructor. - """ - self.li = li - self.constructor = constructor - - def __getitem__(self, key): - """Get a row and return the appropriate type of Series. - """ - result = self.li[key] - if isinstance(result, Series): - result = self.constructor(result) - return result - - def __setitem__(self, key, value): - """Setting just passes the request to the wrapped object. - """ - self.li[key] = value - - -class TimeFrame(ModSimDataFrame): - """A DataFrame that maps from time to State. - """ - column_constructor = TimeSeries - row_constructor = State - - -class SweepFrame(ModSimDataFrame): - """A DataFrame that maps from a parameter value to a SweepSeries. - """ - column_constructor = SweepSeries - row_constructor = SweepSeries - - -def Vector(*args, units=None): - """Make a ModSimVector. - - args: can be a single argument or sequence - units: Pint Unit object or Quantity - - If there's only one argument, it should be a sequence. - - Otherwise, the arguments are treated as coordinates. - - returns: ModSimVector - """ - if len(args) == 1: - args = args[0] - - # if it's a series, pull out the values - if isinstance(args, Series): - args = args.values - - # see if any of the arguments have units; if so, save the first one - for elt in args: - found_units = getattr(elt, 'units', None) - if found_units: - break - - if found_units: - # if there are units, remove them - args = [magnitude(elt) for elt in args] - - # if the units keyword is provided, it overrides the units in args - if units is not None: - found_units = units - - return ModSimVector(args, found_units) - - -## Vector functions (should work with any sequence) - -def vector_mag(v): - """Vector magnitude with units. - - returns: number or Quantity - """ - return np.sqrt(np.dot(v, v)) * units(v) - -def vector_mag2(v): - """Vector magnitude squared with units. - - returns: number of Quantity - """ - return np.dot(v, v) * units(v) * units(v) - -def vector_angle(v): - """Angle between v and the positive x axis. - - Only works with 2-D vectors. - - returns: number in radians - """ - assert len(v) == 2 - x, y = v - return np.arctan2(y, x) - -def vector_polar(v): - """Vector magnitude and angle. - - returns: (number or quantity, number in radians) - """ - return vector_mag(v), vector_angle(v) - -def vector_hat(v): - """Unit vector in the direction of v. - - The result should have no units. - - returns: Vector or array - """ - # get the size of the vector - mag = vector_mag(v) - - # check if the magnitude of the Quantity is 0 - if magnitude(mag) == 0: - if isinstance(v, ModSimVector): - return Vector(magnitude(v)) - else: - return magnitude(np.asarray(v)) - else: - return v / mag - -def vector_perp(v): - """Perpendicular Vector (rotated left). - - Only works with 2-D Vectors. - - returns: Vector - """ - assert len(v) == 2 - x, y = v - return Vector(-y, x) - -def vector_dot(v, w): - """Dot product of v and w. - - returns: number or Quantity - """ - return np.dot(v, w) * units(v) * units(w) - -def vector_cross(v, w): - """Cross product of v and w. - - returns: number or Quantity for 2-D, Vector for 3-D - """ - res = np.cross(v, w) - - if len(v)==3 and (isinstance(v, ModSimVector) or - isinstance(w, ModSimVector)): - return ModSimVector(res, units(v) * units(w)) - else: - return res * units(v) * units(w) - -def vector_proj(v, w): - """Projection of v onto w. - - Results has the units of v, but that might not make sense unless - v and w have the same units. - - returns: array or Vector with direction of w and units of v. - """ - w_hat = vector_hat(w) - return vector_dot(v, w_hat) * w_hat - -def scalar_proj(v, w): - """Returns the scalar projection of v onto w. - - Which is the magnitude of the projection of v onto w. - - Results has the units of v, but that might not make sense unless - v and w have the same units. - - returns: scalar with units of v. - """ - return vector_dot(v, vector_hat(w)) - -def vector_dist(v, w): - """Euclidean distance from v to w, with units.""" - if isinstance(v, list): - v = np.asarray(v) - return vector_mag(v-w) - -def vector_diff_angle(v, w): - """Angular difference between two vectors, in radians. - """ - if len(v) == 2: - return vector_angle(v) - vector_angle(w) - else: - #TODO: see http://www.euclideanspace.com/maths/algebra/ - # vectors/angleBetween/ - raise NotImplementedError() - - -class ModSimVector(Quantity): - """Represented as a Pint Quantity with a NumPy array - - x, y, z, mag, mag2, and angle are accessible as attributes. - """ - - @property - def x(self): - """Returns the x component with units.""" - return self[0] - - @property - def y(self): - """Returns the y component with units.""" - return self[1] - - @property - def z(self): - """Returns the z component with units.""" - return self[2] - - @property - def mag(self): - """Returns the magnitude with units.""" - return vector_mag(self) - - @property - def mag2(self): - """Returns the magnitude squared with units.""" - return vector_mag2(self) - - @property - def angle(self): - """Returns the angle between self and the positive x axis.""" - return vector_angle(self) - - # make the vector functions available as methods - polar = vector_polar - hat = vector_hat - perp = vector_perp - dot = vector_dot - cross = vector_cross - proj = vector_proj - comp = scalar_proj - dist = vector_dist - diff_angle = vector_diff_angle - - - -def plot_segment(A, B, **options): - """Plots a line segment between two Vectors. - - For 3-D vectors, the z axis is ignored. - - Additional options are passed along to plot(). - - A: Vector - B: Vector - """ - xs = A.x, B.x - ys = A.y, B.y - plot(xs, ys, **options) - - -@property -def dimensionality(self): - """Unit's dimensionality (e.g. {length: 1, time: -1}) - - This is a simplified version of this method that does no caching. - - returns: dimensionality - """ - dim = self._REGISTRY._get_dimensionality(self._units) - return dim - -# monkey patch Unit and Quantity so they use the non-caching -# version of `dimensionality` -pint.unit._Unit.dimensionality = dimensionality -pint.quantity._Quantity.dimensionality = dimensionality - - -class units_off: - SAVED_PINT_METHOD_STACK = [] - - def __enter__(self): - """Make all quantities behave as if they were dimensionless. - """ - self.SAVED_PINT_METHOD_STACK.append(UNITS._get_dimensionality) - UNITS._get_dimensionality = lambda self: {} - - - def __exit__(self, type, value, traceback): - """Restore the saved behavior of quantities. - """ - UNITS._get_dimensionality = self.SAVED_PINT_METHOD_STACK.pop() diff --git a/code/oem.ipynb b/code/oem.ipynb deleted file mode 100644 index cfa0d7644..000000000 --- a/code/oem.ipynb +++ /dev/null @@ -1,1048 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Case study.\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Electric car" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[Olin Electric Motorsports](https://www.olinelectricmotorsports.com/) is a club at Olin College that designs and builds electric cars, and participates in the [Formula SAE Electric](https://www.sae.org/attend/student-events/formula-sae-electric) competition.\n", - "\n", - "The goal of this case study is to use simulation to guide the design of a car intended to accelerate from standing to 100 kph as quickly as possible. The [world record for this event](https://www.youtube.com/watch?annotation_id=annotation_2297602723&feature=iv&src_vid=I-NCH8ct24U&v=n2XiCYA3C9s), using a car that meets the competition requirements, is 1.513 seconds.\n", - "\n", - "We'll start with a simple model that takes into account the characteristics of the motor and vehicle:\n", - "\n", - "* The motor is an [Emrax 228 high voltage axial flux synchronous permanent magnet motor](http://emrax.com/products/emrax-228/); according to the [data sheet](http://emrax.com/wp-content/uploads/2017/01/emrax_228_technical_data_4.5.pdf), its maximum torque is 240 Nm, at 0 rpm. But maximum torque decreases with motor speed; at 5000 rpm, maximum torque is 216 Nm.\n", - "\n", - "* The motor is connected to the drive axle with a chain drive with speed ratio 13:60 or 1:4.6; that is, the axle rotates once for each 4.6 rotations of the motor.\n", - "\n", - "* The radius of the tires is 0.26 meters.\n", - "\n", - "* The weight of the vehicle, including driver, is 300 kg.\n", - "\n", - "To start, we will assume no slipping between the tires and the road surface, no air resistance, and no rolling resistance. Then we will relax these assumptions one at a time.\n", - "\n", - "* First we'll add drag, assuming that the frontal area of the vehicle is 0.6 square meters, with coefficient of drag 0.6.\n", - "\n", - "* Next we'll add rolling resistance, assuming a coefficient of 0.2.\n", - "\n", - "* Finally we'll compute the peak acceleration to see if the \"no slip\" assumption is credible.\n", - "\n", - "We'll use this model to estimate the potential benefit of possible design improvements, including decreasing drag and rolling resistance, or increasing the speed ratio.\n", - "\n", - "I'll start by loading the units we need." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "radian = UNITS.radian\n", - "m = UNITS.meter\n", - "s = UNITS.second\n", - "minute = UNITS.minute\n", - "hour = UNITS.hour\n", - "km = UNITS.kilometer\n", - "kg = UNITS.kilogram\n", - "N = UNITS.newton\n", - "rpm = UNITS.rpm" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And store the parameters in a `Params` object." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "params = Params(r_wheel=0.26 * m,\n", - " speed_ratio=13/60,\n", - " C_rr=0.2,\n", - " C_d=0.5,\n", - " area=0.6*m**2,\n", - " rho=1.2*kg/m**3,\n", - " mass=300*kg)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`make_system` creates the initial state, `init`, and constructs an `interp1d` object that represents torque as a function of motor speed." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Make a system object.\n", - " \n", - " params: Params object\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " init = State(x=0*m, v=0*m/s)\n", - " \n", - " rpms = [0, 2000, 5000]\n", - " torques = [240, 240, 216]\n", - " interpolate_torque = interpolate(Series(torques, rpms))\n", - " \n", - " return System(params, init=init,\n", - " interpolate_torque=interpolate_torque,\n", - " t_end=3*s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `make_system`" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "system = make_system(params)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "system.init" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Torque and speed\n", - "\n", - "The relationship between torque and motor speed is taken from the [Emrax 228 data sheet](http://emrax.com/wp-content/uploads/2017/01/emrax_228_technical_data_4.5.pdf). The following functions reproduce the red dotted line that represents peak torque, which can only be sustained for a few seconds before the motor overheats." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "system.interpolate_torque" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def compute_torque(omega, system):\n", - " \"\"\"Maximum peak torque as a function of motor speed.\n", - " \n", - " omega: motor speed in radian/s\n", - " system: System object\n", - " \n", - " returns: torque in Nm\n", - " \"\"\"\n", - " unpack(system)\n", - " x = omega.to(rpm)\n", - " return system.interpolate_torque(x) * N * m" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "compute_torque(0*radian/s, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "omega = (5000 * rpm).to(radian/s)\n", - "compute_torque(omega, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot the whole curve." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "xs = linspace(0, 5000, 21) * rpm\n", - "taus = [compute_torque(x, system) for x in xs]\n", - "plot(xs, taus)\n", - "decorate(xlabel='Motor speed (rpm)',\n", - " ylabel='Available torque (N m)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Simulation\n", - "\n", - "Here's the slope function that computes the maximum possible acceleration of the car as a function of it current speed." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object \n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " x, v = state\n", - " unpack(system)\n", - " \n", - " # use velocity, v, to compute angular velocity of the wheel\n", - " omega2 = v / r_wheel * radian\n", - " \n", - " # use the speed ratio to compute motor speed\n", - " omega1 = omega2 / speed_ratio\n", - " \n", - " # look up motor speed to get maximum torque at the motor\n", - " tau1 = compute_torque(omega1, system)\n", - " \n", - " # compute the corresponding torque at the axle\n", - " tau2 = tau1 / speed_ratio\n", - " \n", - " # compute the force of the wheel on the ground\n", - " F = tau2 / r_wheel\n", - " \n", - " # compute acceleration\n", - " a = F/mass\n", - " \n", - " return v, a " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `slope_func` at linear velocity 10 m/s." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "test_state = State(x=0*m, v=10*m/s)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "slope_func(test_state, 0*s, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "results, details = run_ode_solver(system, slope_func, max_step=0.3*s)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And look at the results." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After 3 seconds, the vehicle could be at 40 meters per second, in theory, which is 144 kph." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "v_final = get_last_value(results.v) * m/s" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "v_final.to(km/hour)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `x`" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_position(results):\n", - " plot(results.x, label='x')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Position (m)')\n", - " \n", - "plot_position(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `v`" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_velocity(results):\n", - " plot(results.v, label='v')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Velocity (m/s)')\n", - " \n", - "plot_velocity(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Stopping at 100 kph\n", - "\n", - "We'll use an event function to stop the simulation when we reach 100 kph." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func(state, t, system):\n", - " \"\"\"Stops when we get to 100 km/hour.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object \n", - " \n", - " returns: difference from 100 km/hour\n", - " \"\"\"\n", - " x, v = state\n", - " unpack(system)\n", - " \n", - " v = v * m/s\n", - " \n", - " return v.to(km/hour) - 100 * km/hour " - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "results, details = run_ode_solver(system, slope_func, max_step=0.2*s, events=event_func)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the results look like." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "subplot(2, 1, 1)\n", - "plot_position(results)\n", - "\n", - "subplot(2, 1, 2)\n", - "plot_velocity(results)\n", - "\n", - "savefig('figs/chap11-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "According to this model, we should be able to make this run in just over 2 seconds." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "t_final = get_last_label(results) * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "At the end of the run, the car has gone about 28 meters." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "x, v = get_last_value(results)\n", - "state = State(x=x*m, v=v*m/s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we send the final state back to the slope function, we can see that the final acceleration is about 13 $m/s^2$, which is about 1.3 times the acceleration of gravity." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "v, a = slope_func(state, 0, system)\n", - "v.to(km/hour)" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "a" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "g = 9.8 * m/s**2\n", - "(a / g).to(UNITS.dimensionless)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It's not easy for a vehicle to accelerate faster than `g`, because that implies a coefficient of friction between the wheels and the road surface that's greater than 1. But racing tires on dry asphalt can do that; the OEM team at Olin has tested their tires and found a peak coefficient near 1.5.\n", - "\n", - "So it's possible that our no slip assumption is valid, but only under ideal conditions, where weight is distributed equally on four tires, and all tires are driving." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** How much time do we lose because maximum torque decreases as motor speed increases? Run the model again with no drop off in torque and see how much time it saves." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Drag" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this section we'll see how much effect drag has on the results.\n", - "\n", - "Here's a function to compute drag force, as we saw in Chapter 21." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "def drag_force(v, system):\n", - " \"\"\"Computes drag force in the opposite direction of `v`.\n", - " \n", - " v: velocity\n", - " system: System object\n", - " \n", - " returns: drag force\n", - " \"\"\"\n", - " unpack(system)\n", - " f_drag = -np.sign(v) * rho * v**2 * C_d * area / 2\n", - " return f_drag" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can test it with a velocity of 20 m/s." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "drag_force(20 * m/s, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the resulting acceleration of the vehicle due to drag.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "drag_force(20 * m/s, system) / system.mass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see that the effect of drag is not huge, compared to the acceleration we computed in the previous section, but it is not negligible.\n", - "\n", - "Here's a modified slope function that takes drag into account." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "def slope_func2(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object \n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " x, v = state\n", - " unpack(system)\n", - " \n", - " omega2 = v / r_wheel * radian\n", - " omega1 = omega2 / speed_ratio\n", - " tau1 = compute_torque(omega1, system)\n", - " tau2 = tau1 / speed_ratio\n", - " F = tau2 / r_wheel\n", - " a_motor = F / mass\n", - " a_drag = drag_force(v, system) / mass\n", - " \n", - " a = a_motor + a_drag\n", - " return v, a " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the next run." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "results2, details = run_ode_solver(system, slope_func2, events=event_func)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The time to reach 100 kph is a bit higher." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [], - "source": [ - "t_final2 = get_last_label(results2) * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But the total effect of drag is only about 2/100 seconds." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "t_final2 - t_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "That's not huge, which suggests we might not be able to save much time by decreasing the frontal area, or coefficient of drag, of the car." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Rolling resistance" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next we'll consider [rolling resistance](https://en.wikipedia.org/wiki/Rolling_resistance), which the force that resists the motion of the car as it rolls on tires. The cofficient of rolling resistance, `C_rr`, is the ratio of rolling resistance to the normal force between the car and the ground (in that way it is similar to a coefficient of friction).\n", - "\n", - "The following function computes rolling resistance." - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "def rolling_resistance(system):\n", - " \"\"\"Computes force due to rolling resistance.\n", - " \n", - " system: System object\n", - " \n", - " returns: force\n", - " \"\"\"\n", - " unpack(system)\n", - " return -C_rr * mass * N/kg" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The acceleration due to rolling resistance is 0.2 (it is not a coincidence that it equals `C_rr`)." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [], - "source": [ - "rolling_resistance(system)" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [], - "source": [ - "rolling_resistance(system) / system.mass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a modified slope function that includes drag and rolling resistance." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "def slope_func3(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object \n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " x, v = state\n", - " unpack(system)\n", - " \n", - " omega2 = v / r_wheel * radian\n", - " omega1 = omega2 / speed_ratio\n", - " tau1 = compute_torque(omega1, system)\n", - " tau2 = tau1 / speed_ratio\n", - " F = tau2 / r_wheel\n", - " a_motor = F / mass\n", - " a_drag = drag_force(v, system) / mass\n", - " a_roll = rolling_resistance(system) / mass\n", - " \n", - " a = a_motor + a_drag + a_roll\n", - " return v, a " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the run." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [], - "source": [ - "results3, details = run_ode_solver(system, slope_func3, events=event_func)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The final time is a little higher, but the total cost of rolling resistance is only 3/100 seconds." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [], - "source": [ - "t_final3 = get_last_label(results3) * s" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [], - "source": [ - "t_final3 - t_final2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "So, again, there is probably not much to be gained by decreasing rolling resistance.\n", - "\n", - "In fact, it is hard to decrease rolling resistance without also decreasing traction, so that might not help at all." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Optimal gear ratio" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The gear ratio 13:60 is intended to maximize the acceleration of the car without causing the tires to slip. In this section, we'll consider other gear ratios and estimate their effects on acceleration and time to reach 100 kph.\n", - "\n", - "Here's a function that takes a speed ratio as a parameter and returns time to reach 100 kph." - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [], - "source": [ - "def time_to_speed(speed_ratio, params):\n", - " \"\"\"Computes times to reach 100 kph.\n", - " \n", - " speed_ratio: ratio of wheel speed to motor speed\n", - " params: Params object\n", - " \n", - " returns: time to reach 100 kph, in seconds\n", - " \"\"\"\n", - " params = Params(params, speed_ratio=speed_ratio)\n", - " system = make_system(params)\n", - " results, details = run_ode_solver(system, slope_func3, events=event_func)\n", - " t_final = get_last_label(results) * s\n", - " a_initial = slope_func(system.init, 0, system)\n", - " return t_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can test it with the default ratio:" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [], - "source": [ - "time_to_speed(13/60, params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can try it with different numbers of teeth on the motor gear (assuming that the axle gear has 60 teeth):" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [], - "source": [ - "for teeth in linrange(8, 18):\n", - " print(teeth, time_to_speed(teeth/60, params))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Wow! The speed ratio has a big effect on the results. At first glance, it looks like we could break the world record (1.513 seconds) just by decreasing the number of teeth.\n", - "\n", - "But before we try it, let's see what effect that has on peak acceleration." - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [], - "source": [ - "def initial_acceleration(speed_ratio, params):\n", - " \"\"\"Maximum acceleration as a function of speed ratio.\n", - " \n", - " speed_ratio: ratio of wheel speed to motor speed\n", - " params: Params object\n", - " \n", - " returns: peak acceleration, in m/s^2\n", - " \"\"\"\n", - " params = Params(params, speed_ratio=speed_ratio)\n", - " system = make_system(params)\n", - " a_initial = slope_func(system.init, 0, system)[1] * m/s**2\n", - " return a_initial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the results:" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [], - "source": [ - "for teeth in linrange(8, 18):\n", - " print(teeth, initial_acceleration(teeth/60, params))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As we decrease the speed ratio, the peak acceleration increases. With 8 teeth on the motor gear, we could break the world record, but only if we can accelerate at 2.3 times the acceleration of gravity, which is impossible without very sticky ties and a vehicle that generates a lot of downforce." - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [], - "source": [ - "23.07 / 9.8" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "These results suggest that the most promising way to improve the performance of the car (for this event) would be to improve traction." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/old_notebooks/chap07sympy.ipynb b/code/old_notebooks/chap07sympy.ipynb deleted file mode 100644 index 1114249cd..000000000 --- a/code/old_notebooks/chap07sympy.ipynb +++ /dev/null @@ -1,459 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 7: Thermal systems\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Mixing liquids" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can figure out the final temperature of a mixture by setting the total heat flow to zero and then solving for $T$." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from sympy import *\n", - "\n", - "init_printing() " - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAARIAAAAVBAMAAAB4a3wcAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEHa7q2Yiie9Umd3N\nRDIfxLosAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADtElEQVRIDc1WS4hTWRA96fR7L3+DLhRHNOpC\nlz3QutABwxA3KpqVC/HzQERxk+gMI7gxILpRsWE2fkDfoIKMqK0LQVroCG4aWzs7BUEC4kYEbf89\njsaquvfmffLprQW5t+qck+pKVb2kgZ/WXs5W2c5ZBLnyLAKr2UsQW15ab1c1aw+hcrTUGt70OSQP\nYIOdieYOl8p7jX437C/DW96XpkxKIYLYZiNt3wsP3mB/ZRnJq+aDZPI4BJwArrRl7ASxeoihYOAY\ncI5eyu7DqSIxipRJKXAQyxhp+57ALy4w7xYBN8kRWwEUYf8H7NCAuoIYScRirr7X5oHUJxVgoIpB\noOLBamhEriBGkrBZNTh1YIry4KmhjoPmbM0ASw3CdwhboJlkQTmZabqzNRUg4YJWbdcIBjhv24JY\nrNaGlRMvIjmNFP1V4JXmsqPIAoOUO/SJQlhmRIlNJTc5znk6wzN565Y85wkYpfOxiQDB7pwisjOo\njLK/mA8yx+OTptxhPqY0gK7E/sraWFO/47DctGgd5mNK4wuWeMi+w5GCQnJDfMfLfNKUO0ywFwxb\ndUXqSpx3KnQu/cPOKj540aIm2OE1ecK3M+dMinnkVqrIfcZ4k1zq7mXpQ3qEA5pyhzH2aiPDSVG2\ne5L+rrR/YRM7d/ngRYsaY447WCf8fIRTlZwWNK/zJxoc0kTFFo2xPRZfMEmRe6tY3ZOEhDHcwRuX\niFNM8qKxxR5ygntN9hlLuzl+xt5wHLAlVZ7OOCPJZqgSf6K+XDC/Eurtw5OTkx5ll0ouUAmVBsml\nEn+p/ASMJRr2N0KilczxkJxR3yQpKoaElHaEjp5TVm2l50tM90R9k3gEXczTIdPpuWj8tALP6AWH\n2zU2ViQ3PQRrmrcFOGAqkY3tNWU94MjG2vy74HARt+mlNrbXotEfLZBGNpbF2gZqSNWR/Ehf1ZRH\n9cQpEhnXUzZCvhUm00l5itA9wUX6teDHRj3dfzK5jguLmMLkAf43QlEjFzSBhcMbGsSoSrjzf0z9\n/3tUqjGpJMNyMlNJ7FLpOsfb+ODOO7+1HhQk8A+NWR5Dj/gI2vxlgV9FVQk9Af1MKtmnFaYSHWar\n2Sa5aVfHXa8neE57WOvKGTCpaPXBDBi9zzBwUKPZcoh+ffbvPAGWR0cvy02c/ZV6VujFM25f+yB8\n3KWgl+0fp7HF6t3p062WEKu704ImWq23wKI+Cp+i/5RmsbjbX7C7P01sv1oDb94T8Lu6W7uiPmiH\nh+YTxrNc44XuH/q3++lTknDqAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$C_{1} \\left(T - T_{1}\\right) + C_{2} \\left(T - T_{2}\\right) = 0$$" - ], - "text/plain": [ - "C₁⋅(T - T₁) + C₂⋅(T - T₂) = 0" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "C1, C2, T1, T2, T = symbols('C1 C2 T1 T2 T')\n", - "\n", - "eq = Eq(C1 * (T - T1) + C2 * (T - T2), 0)\n", - "eq" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIUAAAAyBAMAAACAOwXCAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMA74lUMhB2u6tmIpnd\nzURTbmnuAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAC/ElEQVRIDe2Xy2tTQRTGv7xq87AUFEQQGgRx\nUcSK4kbBgBWRbrIQBd3EhfgXCAEVXUmKSvMXaHDrInEh4sbWhesIbgQJieLGXX3j83rOmTP3ziS3\nSbRd9kByvzlz7u/OnEzyEcwEH7GOSAbBNLbNn1gHApn549PYbghnqrX6fZHNHbWgeidmcZlLtcVc\nkWtyH6pL32pd0UhbxsRO4Ai9KHYDu4Anor23C3WkHtY5lSwisYK86JCRuVoG8j/klhvI/QTuhref\nU3X2OYnlBo8mgWYL2R7rkLH1Cw2mpiVVR/YTcJG1xGG9dukxeCODU8C9OUxwImIsz/GgxakpegwR\ne6wllJEnMLAgqR6wVOZKDu1H7hcPMiV+p6C9OqGMpiRP2wlqmYYykl91fFKutFfASBLK2F+ROcxe\n4S1wy1Qqo/DZzC/clivtFSpprIx2SeaSjckXJLhlKpWRWOX5DHBU6mivVpJQxoxMlQuNNH983DKV\ndh3COGRvlL0a3L5O51mn85ruajMjVUr0cn9IcMtUKsOcjJYyZK+6JCrXdcjJyDOHz4G0zEhl5Phk\nJ2kH8nDeq10SCWU0i6T30KtQoTdumUpl4Bgd40eUFMYWftDAOlLf6dtAj8Esz15nZaRlZB7XnnKS\nGXu7v2+q5JRdB85Xb/VomG3Rkq8FrypGhmeMpkyYRooOpe7FlrzFO1/addjsASuAUL6PcqTSLw9e\n1oTKPsaDNu9CwpE2JddEEKxqQmUfw6sed7DJ8Dul/Qj+O+gz2uxpbE/95D+OYnsaWa9Pi/zWy8cx\nHOv1ahH5rZePYbjWK7UxfjuK4VivKbW/QY7fjmI41usxXL8dweizXqq2v+srfGfotzwwMdiPPuul\nMmX4fmsBdB1k9FlvxPD9diijz3ojhu+3QxkF+cUNrXctvx3K8K2XS7Ufvt8OZfjW6zB8vx3K8K3X\nYfh+O5zhWq9U2nPq+K1DiPtsw+kx/NbUDp6PkDHab7V0bcYYfjuSEa5npKB1bMR/jw34D/QXAgUS\n4RvxulUAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$\\left [ \\frac{C_{1} T_{1} + C_{2} T_{2}}{C_{1} + C_{2}}\\right ]$$" - ], - "text/plain": [ - "⎡C₁⋅T₁ + C₂⋅T₂⎤\n", - "⎢─────────────⎥\n", - "⎣ C₁ + C₂ ⎦" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solve(eq, T)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Analysis" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use SymPy to solve the cooling differential equation." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPsAAAArBAMAAABMYuO6AAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMARLvvmVQQid3NIqt2\nMmaorGxOAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADi0lEQVRYCcVXz2sTQRT+Nmk26bZNAyIIFVoQ\nD1rR6kXtQfYqguQfkEZEsLSQIIKVVslFrPWSg0p7EEPFk2CDF5EWjBe9RMjFq6TQk1CsohEPWt/M\n7MzOzm7qcR7szvv5fdmZnck+gIlzyOejrdtw0RYz5x0tWaWfs8qOhxbpvdcff1qk31dPde3Ru+fQ\nV7NH39dFrmKPPj2GcsEefbmCKZSs8ZeLOJKpW6Pvb+YfZK2xw924/eJdhH6ow8ztiC/R+Jzo3dsZ\nghsEa6fWd8ff/AJWOcBAe28cwBtTGapWeQxFJYTgBsFN4AxwEXgMuEvAfw+D/pLiULXKYygqQQOP\nEjTg/QA2MVgBMjvABwMhZmoJsjaW49aFSybo4Fo9QIuSp7P/PdJUwc7BKzEswzGpbFWrPFLJNLmm\nEnTwCIEDDNDZX8ABKmDnYH9RYiSPTlX5Va3ySCWgVwk6uEmQ5oA3gJET401kfYlhjM785YUmjLio\nNTJpFSlRSBzcJFjzWeZxuqboytMCEMkKF5/pgWRTj1ITQKojHWwUtbqH6yF9HFwQhCVf+XTfJcd5\nujLVMBLRPuUaTpsWiGfLiKiVlhpD+ji4SfC2xMpO0nWPrqEdZmniLi+S3G+XxF9UugBMM8/iE0oS\ntVq2UEP6ODgR7EphXLTtSYje/UZjjJ5H2Y0tDcDoQxG1oc00WrjlsysrPvfGwQ0Ctu1JaPKdHZTg\nVJmV5c+32GC6FLY0xuTzWnehha3tlo+DjWxFJKunTwAXBCKP7mzbk9Crl61l2sGrx13G7TS3Ux3N\nzWunSzPY/ItJVLqZMRFU9Ang+ZoGQK+y+OSbp1OnMUs3PxINDY8tDf1GfUZ47Xqr481W0fWuVmE+\nfQJ4lOD6098bDJZOBu9Yk46dArMSJJg0fe5ELXtpB5rOBAYqXlAsnz4JPJkgVxeUzxKYI66liEXG\nH7hIt/v8dq4wGMQkvUzVwQWB2WDmfZG7IEt6jVtm4DBmsIbc/nau/TyIOZ1okg4eEAwXca2uZR3l\nulvTXIlqqm64Z1pNjGDwDpyNjhFSZgguCUZLwU4OcsQXQQxcIUhF+9yQrv+PIbgkmBOHrCr1Okz9\nouyeysuekd6BEFwSUIPJDlkrwhpM59V3vuUs/ADeYLJvKysiGkxrPaZoMK31mGneYFrrMUWDOUqn\npRUp8wbzFi5ZYYdoMC+4TTv0osFcbdlh/wdTXw3MbiYF2AAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$\\frac{d}{d t} T{\\left (t \\right )} = - r \\left(- T_{env} + T{\\left (t \\right )}\\right)$$" - ], - "text/plain": [ - "d \n", - "──(T(t)) = -r⋅(-Tₑₙᵥ + T(t))\n", - "dt " - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "T_init, T_env, r, t = symbols('T_init T_env r t')\n", - "T = Function('T')\n", - "\n", - "eqn = Eq(diff(T(t), t), -r * (T(t) - T_env))\n", - "eqn" - ] - }, - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "Here's the general solution:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMAAAAAYBAMAAABen+92AAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIu+7q82J3ZlmRDJU\ndhDTAJzgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAC50lEQVRIDa1VTWgTQRT+Nk02TZpuIoKoh7ae\npOhBseJNAxY9SGgPBoqK7qXiRageFOrBgIpVUPciVA8atFA8NeBFD2LQQ4Ui7aHUFgmsB7WXYrWg\n4v97M7OT2TZthfSD2fe97715b2cmswHqwtG6Zq8+2fmzek5dGam+uqavPHnwzuS6qz2llZNC0afs\nJf57xt0f8WKsHKpQ07G+dHQtdJ5wYe0Q8UM1suY6Or0Ni3RrjvLTmbCqixmy7SLSh0YPMR/4mEHM\niCna3A68oqGQ2kXYi+gE8CDQlNXFDD0KtBaQKGKKxHGg2TWCgjp7fKDx82I5UgImHbWEdzKqixnJ\ntPP3y2j2sYXEK4DTZgQFjfFvMblEbiX1hi0ygNfS6mJKZlMEunwkkewj5xyNYzRCuFUmt6kQ0sh5\nSGPorVJVg6IqptTAbGViF5Dcv5ADTgWystY3Jg5tCJvT02pThKsfqgH5opjWBbG+s2nwgNQ8kdvs\n2XmBAtPfLBAe0xjOWC7zxdANZLFwOPGL/XiZTrKbyCh7BuI/pfPkIGBtx2zJiGmqG8hiWhckKj4o\nkSI1cUl4LsTqI8LLgiM6R9sr96oRg+kGspgRIUrXgJ9F4BONJQ3iosGYaJDOUsIS9ObzZ/P5HqHL\nYuEUugYE3qIRftFh9uzzAlmi8gYUZAOXhFrQKxDFPlQmEteeTSM5iZOUTdeAwIfcC3pRccisKFhf\nidi+aBBxgQmlh4xuwMWc4042MeJFMnbaf0lp+3zOtbPAZYdmD7BnYtxD6iYJo3TNuzGbMWMB1w24\nWDQ3g5Y3SPsto7Bh7/57mF+KL9r6CpEjwaTAOpc6rzOnBpie8QI5ZFUDWYyPEzn+UB3A5mraGUmt\ntqoUZtxgOegVcEK6BB/bMNCEfnNDB+Vkm5dTEy9qqlJ8ZMYayk7Wmkf/GC5Y5WqgISP4+6oSZhsv\n5sLCsp6TqyCZxZSHoRkjSf3h7DSkNaabuF5CrmPtSv8DUyuwq8EudNcAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$T{\\left (t \\right )} = C_{1} e^{- r t} + T_{env}$$" - ], - "text/plain": [ - " -r⋅t \n", - "T(t) = C₁⋅ℯ + Tₑₙᵥ" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solution_eq = dsolve(eqn)\n", - "solution_eq" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAHwAAAAWBAMAAADwX+WxAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEHa7q2Yiie9Umd3N\nRDIfxLosAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAB+0lEQVQ4EYVTP0jcUBj/5bwkl6O5e1Co0MGG\ns0dL61CoY8EjWJUuBsHZu0kRyl2njnXroNgDtwpFihZR8M/i5CmCS+mBNzi4GQSXTorWP13a770k\n73JpL35Dvt/3+5P3wnsBYuqeGyPeLT1id3tiHPMxWrykLo0MTG6vxZvaq+pxo4bv7fWmonT3v9Yq\nzVmgUje1q1ZSu+kdvug/iFifOjCWHemce0nFYAPmlCQF0CtIbSDdtHL2/iY91oschmsWSBQeeIxS\nFD0JlBegNjzSfx4wAkctFA3aGZC2Kh5tWKKfAmNVJLhfVvqWw59y9oH5gjZvu97kxxvAMIPZYi1v\n8LHL4zpz31pEf/DjNE1H5Q+Wx2T4clsYj+p8lnHtd1TedQWTWaRdTFSV/14UGVfFl4Zf8UkMDAbF\nh0byLKwFWMaT5wEV9F0ODFfELwMy3PV6/cdMvb7AOTr2SIkTT9MbSLmOaMEoV6djBzpzlrp08gzm\nY7xHuULMOy/+ka5KEAl3Gadjh7KnFNRjJ1XUs2wOxi/gM/PiPVDiD66PjEk7j9JzZFlpHjrwsPdN\ng5bim098HQ0vKrG/uv7qz76FFHfb/J4O4m3TQvF2JTfPDVkXDLNYyaAGSyaMKQn/AaYTojqqSoH+\niNoX7GjVgNdWL60Ax3fFzsEs4ImDwzz+AioJZBj7Bd1mAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$C_{1} e^{- r t} + T_{env}$$" - ], - "text/plain": [ - " -r⋅t \n", - "C₁⋅ℯ + Tₑₙᵥ" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "general = solution_eq.rhs\n", - "general" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use the initial condition to solve for $C_1$. First we evaluate the general solution at $t=0$" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAFYAAAARBAMAAAC1JUYQAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEHa7q2Yiie9Umd3N\nRDIfxLosAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABZUlEQVQoFW2RPUvDUBSG32g+C2kvOAgOEqpF\n0cWho2AoFcGl+Qe2P0BSJ0fdFS24KUgHBamD9QfUIrgZaAcHN4vg4qSooC56bnJ7U2PPkPve5zwc\nLicAlKnisl5FovTPfOmt2EnwWQ/WmZdQYVRhNpH6y8cuSbsoS1eJogr4dWhdyXnoMPrcx8hywvwE\nrNUwwpuyUl88Pss7hEsDSwx2zCn5TX6djJlwCezEMEpbTnSmF0RHuvq3IPJo98KYPgnnU5auFr5O\nihT2wwuD9c9VXwc9ntv8Y/WEawTB7W4Q1Dmk9SYq3GyK9H5HvoHWC4xnHe30cQ72NDbhV4lsDHNp\nvVCuFVd78MyykWEHsD6AQzbMXSKqFnKozCPDKkcwgIn8apdGJ99gLP7cODB5q8B/6wrWKUaVdCOa\n6YFhD400WnCESXO3RbQ9yYDRmuLqL2gd40qv9bl+/u7088CpFLKwXcx4uMvhF3itS7lwyWUgAAAA\nAElFTkSuQmCC\n", - "text/latex": [ - "$$C_{1} + T_{env}$$" - ], - "text/plain": [ - "C₁ + Tₑₙᵥ" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "at0 = general.subs(t, 0)\n", - "at0" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we set $T(0) = T_{init}$ and solve for $C_1$" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAHkAAAARBAMAAAALcx5NAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEM3dMiLvu6uJmWZE\nVHYiGvycAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABg0lEQVQ4EX2TMUvDQBiG31jTNprWUhyyqBCd\nXCz4B7K49w9IRREXB0cXoTh1qnUQEUECjoJ0cJJCO4mLpfgLIjiIQyDioiJ6d99drmlJb7j3+773\neXMkRwC5anuNv8PjL9WO60TgAtgBbsZTajIRqMP8BjqK1WpUqU4FmD3dxuwv8KJTqioEokoHmG0D\n+QjoCTKxyXQ6QHS2kkipRqZZmwIQWPNVIKE6LYB3aSpVbHegqoTqtAA2pMl0BSiv88UKnJQSKdXo\n9AhgsO+kF7vusWW57vK26/rCGAEKw9+JXzeMqxD3d6GPft1q0bPiswXQryvfvF71iOA7v26USw46\nP9hFKyockBenBXAakc90akCA2HP8NRph21ysIDIXKhg9mwNsHvtzVZ0+X/toAvtskA/sLeRbZo9M\ndTYBuYB8pg/kD+2fMJD1Znwv0yvKsUpTuzRPPtM39QfE+SM4qCHz5GW8TTm027HLimeLfKa31rDB\naycM8IjiJexmIhRzzhn5XF/xD1YId0zuAa6QAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$- T_{env} + T_{init}$$" - ], - "text/plain": [ - "-Tₑₙᵥ + Tᵢₙᵢₜ" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solutions = solve(Eq(at0, T_init), C1)\n", - "value_of_C1 = solutions[0]\n", - "value_of_C1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then we plug the result into the general solution to get the particular solution:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPUAAAAYBAMAAADQRaYKAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIu+7q82J3ZlmRDJU\ndhDTAJzgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADN0lEQVRIDcVWTWgTURD+Nkk3zTbZjUdFmhYR\n8edQsd5Ec6heJNSDQVGxK4LooRJPQr3kZnsQchFqBQ1a6EVxPXoQAyI9eNlDqSklsodUCloURfGn\npM57u2+zf4HmlA+y82a+b97b7swbCvQKF3t1MKC2end2utSbs2cfLO24e97YzuGvtiPqrEmFDpn7\nm6z21QDp++j4j7EJvXOuNOJyuT1jW6Mnf7l+aBEpOB2QSRu0o5YFZB2xEvorAd7j9lmucw3YB8y4\nfmgRKehzZekjhONImMATCiaAXBmpqsuLhUovxrEsIkAe0m9grh0QKyGOFGR0IXNszACWKIWK+biG\njBWggTS9HMewSw1UkNoEProBd+GIowXqkKuzFzky92SgCoxbUAIsueJspeRypErQtaSUIBxxB8Gl\ngPwp+fOrPEgljIA4Wy57SeqNKAgxWPOEcJ1F1Ml6NsSwEkZAbBeveEnqjSgIMW8eoO5oHHufuQtZ\nSXfCbcNKGAGxXbLmJak3oiDEvHmAl46GLBvbi/STDmHdCKWyEkZAbBerAmu3Gdg21BtREOKQgI/t\nN5SS2N94FM6MqpBcLJ7bWyyWSc3ObiOqNzxifv/baupYVn52tpb3hsWal/BTw0zNvK5DWQLNBwbx\np/i+Oe8NdbKB2YeNMp7naTJxCDG//xQXvMTH9gJpNEdp68WTlVC9rOZTLyqxrKxZb21CbOfrNd4b\na1YTc38wDL2VHvGL+f3HzZbNk2VjG6zXYjpgkvXjhEXVKKxg8AM0a3ARdOkZxNlynrv2I856Y6xR\nkTZKaElfStDtuBCDCSju8lqWBNP0y5zBOlt7IR/dOmvaRS2wIXsKu2xabOeZLbg68bMAHCBBwlSG\nkNClql9sC+KmzZOlHYEL7FFfqTAThmbAwkFMD2BKfBlxNm4F5f+gImb0l41kNeNwrpj7Xz/bPFk2\ntqUhHu30iNfUvPQNU+9xR6rZIkW85mww6RiayCH5zEgaux3OFXP/nWzzZNnYls3gDj5fLTSg5LFc\nwfyKjyAnHixTs2FiHpkbUAri/fw5zSs2z+wqTQc/243n+d+hm7S29nB72fVqZ9cZvoRU8LsJ9j9F\npekmignN5gAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$T_{env} + \\left(- T_{env} + T_{init}\\right) e^{- r t}$$" - ], - "text/plain": [ - " -r⋅t\n", - "Tₑₙᵥ + (-Tₑₙᵥ + Tᵢₙᵢₜ)⋅ℯ " - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "particular = general.subs(C1, value_of_C1)\n", - "particular" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We use a similar process to estimate $r$ based on the observation $T(t_{end}) = T_{end}$" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "t_end, T_end = symbols('t_end T_end')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the particular solution evaluated at $t_{end}$" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQ0AAAAYBAMAAAASULWnAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIu+7q82J3ZlmRDJU\ndhDTAJzgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADOElEQVRIDcVWTWgTURD+Nkm33fxsol7EQ1Px\nIP4cWqwHL7qH6kVCczBaKLQRfzCHShREqCK51YJCxEubogYtVsFiPHoQAqIiIgQsNaVGoogUtFgU\nRUVa523e/mSb5pVeMrCZv2/mzb6ZfS9Ao6i3UQtXr6suVuuN0vzJRq1srTsyOrVuqCdnGVYn+dKr\nw62EGnM6Mn+bs015m3UIkL53dv/o6o/brE5xg2kQgsNbupY69/8yA3TB43hzab4dCIZsoHuAHIcr\niZZ6r9xnRgjBJ4GtwLAZUBGihu7fRbQXngJw27AxngA8QDgFJWs3V+QP3BSImz4hWIP0G8iYARVh\n2qG7csCUGlISaeXKaBxK6QzwGLiVR6DsgJL6mptcIdMnAtMkKf+A92ZARfjo0MOkX5VxHSn/G7Th\nOT3IAt1leB1Iphp1TFi+rABMaTx0LhCsiqpmkjx36BmfxeGXZRyUouj1tetw6mgtMuo4VeUUgNmw\nLSM5RSZ1oGhtrA7pQBnJQAaX5HdMZx2tRUYde+xOEZgNG1DkIZwrUdInQlKcmznbOJpWNX8Cd2/o\ng806WouMOi7anSIwGzbgEQ8hzq4RdnZKOzGX4+bajHW0Fhl1bKc9PXKe6BzlEYHZsNlJv0Z8CxS4\nrXTT7lgu1+ooTsRiZ2OxHoamOiwSgdnxYSe2FWB1BDW7uZasd/RzqaAMPynCOwU6i3Qy9qOqLyKw\nPj+TGkbGSilMapJ+jXipmGCcp12RsY6qfaqmPEy7QnKw/JQjjTqq5lQE1ufn9CIyf7AZxPVPls2p\nKw4UVqyBOfaVqXuRGbS+RbDc+gIyRxt1HOe6zkRgNw2b9DUpzSexSLxyjbSkgEAUcyF7Jocs7146\nVIArS+YIO/QPYBNHGHVMWBFC8LH+nxHAXaArxNvGOGUEmrL0U5xJ06+Agjk6U3bgsg+D5u4ZdTQ7\nX6MeWF/n2xe4ci2pHHG6RoD1gtUttzuvatICBl/hgpTn5vucKykuGKweWMc8kxFG84MccbpGgAEj\nUsjVSAleDdNpjM8sA3c4LHXBDPvpKMYRSOh8lj6CqCPBGlXrf9DaEridjV1bGkjpNQbysGurCP8P\nLrr5vxec5+oAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$T_{env} + \\left(- T_{env} + T_{init}\\right) e^{- r t_{end}}$$" - ], - "text/plain": [ - " -r⋅t_end\n", - "Tₑₙᵥ + (-Tₑₙᵥ + Tᵢₙᵢₜ)⋅ℯ " - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "at_end = particular.subs(t, t_end)\n", - "at_end" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we set $T(t_{end}) = T_{end}$ and solve for $r$" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\nFSYAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$\\frac{1}{t_{end}} \\log{\\left (\\frac{- T_{env} + T_{init}}{T_{end} - T_{env}} \\right )}$$" - ], - "text/plain": [ - " ⎛-Tₑₙᵥ + Tᵢₙᵢₜ⎞\n", - "log⎜─────────────⎟\n", - " ⎝ T_end - Tₑₙᵥ⎠\n", - "──────────────────\n", - " t_end " - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solutions = solve(Eq(at_end, T_end), r)\n", - "value_of_r = solutions[0]\n", - "value_of_r" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use `evalf` to plug in numbers for the symbols. The result is a SymPy float, which we have to convert to a Python float." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "sympy.core.numbers.Float" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "subs = dict(t_end=30, T_end=70, T_init=90, T_env=22)\n", - "r_coffee2 = value_of_r.evalf(subs=subs)\n", - "type(r_coffee2)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMoAAAAPBAMAAABXbk2cAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEJmJdjLNVN0iZu+7\nq0QgoRR7AAAACXBIWXMAAA7EAAAOxAGVKw4bAAADP0lEQVQ4Eb2U3WtcVRTFf3fuzJ3c+chc9KWg\nMENDBbExQyaKaMWxBKGU0sH8AUlFKPigA+KLLxMUFMHgUEEQBa8oghVJfKhSP+pVEAU/MhRUUEoH\nEV98SEdjWiPpuM45MzfjP+B9WHufvc7Z65x99zlww8IhzOdsEMm14C/cIfcUeDNHW3Dm8BcjeLHx\npZkvBnK1CSa3NtNoEDaOtPEW5/sE9+duN3lv1cTTHOiaBdaGm1Kx4D3KLfDqQJpt7zm8Hvf1LYQ1\nOrFjYCqZYArD4XCPM+R3ycCPFIfDJt6HvNKlsIG/As56n1+OsEBQ5xtunpXKO3An5YjihoXsVSqr\njoHjyQST0W57nGxzjSfhJYJ7j0F5g3JCqUdeqcb2pM6CgXUtgayob6ETFVcp/2Vh+gLVFccQvpWw\nz/jgt/i97+1wBA5QNhkqK/gDKj2mr5qRs6nK12aKVdmDrXYwkIoFRU3FjD5+kPAf5lcFTcW2PuNu\np1JNyF6nWmN6W9TIpio7S0f7Npf3h1SamjFl8lp4SI5VeTtI5E4wdhjUKA/fiCmfVdsUdZZdXquT\n+1MzR3as4u3EfGBzhWKX65qxNYLcXTWNjIrXDBK5KYMfa7T0lGBuJ6IUhdfxrxBsj7OPbaoyjLgt\nMrlSlee1GAv3tJyKT5Ao5oKGqZoR/jkKhx67YNyv4GEe36VaH1XM2bGK+oT1vlHxdBZTsUyiVRYo\nvudUniBIg5Z5QFP0fRS9S+GfSN6cmnhxSV1ZI2//vrOpyvtSiW1d9F86bThh1gvCLiUtMPp1gsQF\nR7Q5z8uw2dY2lts/oDumSHlg7lXB/lZnUxX1mDuLqcd6RL6m9QYqA0q7ViV/6dLlT5r7DJ7aEZV6\n8/Uryh1ro3PmQEGP7AaZFbkjm6p09F9sLo5jXoEH4ZiFYsLU344BDfYZcjo2H8ML3fO6gN0aPEPh\nPMtdeJabWrpH1roLaW9lqe65HmOq7Z0jfLoxu2qhENNpjlUqyQRD3qicJb/NbxFv6q2RRPaU94ii\nNy5+D2sjO/vpWowF5mdbZH6+9hPewokWgd6oVQv8cvA7tYFh8Df34gkm1L6YPni4T+6iXstw5qIK\nNq/h//P9CyM1PThWyTs8AAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$0.011610223142273859$$" - ], - "text/plain": [ - "0.011610223142273859" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "r_coffee2 = float(r_coffee2)\n", - "r_coffee2" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.1" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/code/old_notebooks/chap07units.ipynb b/code/old_notebooks/chap07units.ipynb deleted file mode 100644 index d83a4c5c4..000000000 --- a/code/old_notebooks/chap07units.ipynb +++ /dev/null @@ -1,2186 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 7: Thermal systems\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll start with the same code we saw last time: the magic command that tells Jupyter where to put the figures, and the import statement that gets the function defined in the `modsim` module." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# If you want the figures to appear in the notebook, \n", - "# and you want to interact with them, use\n", - "# %matplotlib notebook\n", - "\n", - "# If you want the figures to appear in the notebook, \n", - "# and you don't want to interact with them, use\n", - "# %matplotlib inline\n", - "\n", - "# If you want the figures to appear in separate windows, use\n", - "# %matplotlib qt|\n", - "\n", - "# tempo switch from one to another, you have to select Kernel->Restart\n", - "\n", - "%matplotlib inline\n", - "\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The coffee cooling problem.\n", - "\n", - "I'll use a `State` object to store the initial temperature.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 105, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "

\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
temp90 degC
\n", - "
" - ], - "text/plain": [ - "temp 90 degC\n", - "dtype: object" - ] - }, - "execution_count": 105, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "T_init = UNITS.Quantity(90, UNITS.degC)\n", - "init = State(temp=T_init)\n", - "init" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And a `System` object to contain the system parameters." - ] - }, - { - "cell_type": "code", - "execution_count": 112, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
inittemp 90 degC\n", - "dtype: object
T_env22 degC
r0.01 1 / minute
t00 minute
t_end30 minute
dt1 minute
\n", - "
" - ], - "text/plain": [ - "init temp 90 degC\n", - "dtype: object\n", - "T_env 22 degC\n", - "r 0.01 1 / minute\n", - "t0 0 minute\n", - "t_end 30 minute\n", - "dt 1 minute\n", - "dtype: object" - ] - }, - "execution_count": 112, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "T_env = UNITS.Quantity(22, UNITS.degC)\n", - "r = 0.01 / UNITS.min\n", - "t0 = 0 * UNITS.min\n", - "t_end = 30 * UNITS.min\n", - "dt = 1 * UNITS.min\n", - "\n", - "coffee = System(init=init, T_env=T_env, r=r, t0=t0, t_end=t_end, dt=dt)\n", - "coffee" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `update` function implements Newton's law of cooling." - ] - }, - { - "cell_type": "code", - "execution_count": 113, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def update(system, state):\n", - " \"\"\"Update the thermal transfer model.\n", - " \n", - " system: System object\n", - " state: State (temp)\n", - " \n", - " returns: State (temp)\n", - " \"\"\"\n", - " T = state.temp\n", - " dT = - system.r * (T - system.T_env) * system.dt\n", - " return State(temp=T+dT)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how it works." - ] - }, - { - "cell_type": "code", - "execution_count": 114, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
temp89.32 degC
\n", - "
" - ], - "text/plain": [ - "temp 89.32 degC\n", - "dtype: object" - ] - }, - "execution_count": 114, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "update(coffee, init)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 152, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "system = coffee\n", - "df = TimeFrame(columns=system.init.index)\n", - "df.loc[float(system.t0.magnitude)] = system.init" - ] - }, - { - "cell_type": "code", - "execution_count": 149, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array(1)" - ] - }, - "execution_count": 149, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "array(1)" - ] - }, - { - "cell_type": "code", - "execution_count": 153, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " Add a DataFrame to the System: results\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \"\"\"\n", - " df = TimeFrame(columns=system.init.index)\n", - " t0 = float(system.t0.magnitude)\n", - " t_end = float(system.t_end.magnitude)\n", - " \n", - " df.loc[t0] = system.init\n", - " \n", - " for i in arange(t0, t_end):\n", - " df.loc[i+1] = update_func(system, df.loc[i])\n", - " \n", - " system.results = df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 154, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
temp
0.090 degC
1.089.32 degC
2.088.6468 degC
3.087.980332 degC
4.087.32052868000001 degC
5.086.66732339320001 degC
6.086.020650159268 degC
7.085.38044365767531 degC
8.084.74663922109856 degC
9.084.11917282888757 degC
10.083.4979811005987 degC
11.082.88300128959271 degC
12.082.27417127669679 degC
13.081.67142956392982 degC
14.081.07471526829052 degC
15.080.48396811560761 degC
16.079.89912843445154 degC
17.079.32013715010703 degC
18.078.74693577860596 degC
19.078.17946642081989 degC
20.077.6176717566117 degC
21.077.06149503904558 degC
22.076.51088008865513 degC
23.075.96577128776858 degC
24.075.42611357489089 degC
25.074.89185243914199 degC
26.074.36293391475057 degC
27.073.83930457560307 degC
28.073.32091152984704 degC
29.072.80770241454857 degC
30.072.2996253904031 degC
\n", - "
" - ], - "text/plain": [ - " temp\n", - "0.0 90 degC\n", - "1.0 89.32 degC\n", - "2.0 88.6468 degC\n", - "3.0 87.980332 degC\n", - "4.0 87.32052868000001 degC\n", - "5.0 86.66732339320001 degC\n", - "6.0 86.020650159268 degC\n", - "7.0 85.38044365767531 degC\n", - "8.0 84.74663922109856 degC\n", - "9.0 84.11917282888757 degC\n", - "10.0 83.4979811005987 degC\n", - "11.0 82.88300128959271 degC\n", - "12.0 82.27417127669679 degC\n", - "13.0 81.67142956392982 degC\n", - "14.0 81.07471526829052 degC\n", - "15.0 80.48396811560761 degC\n", - "16.0 79.89912843445154 degC\n", - "17.0 79.32013715010703 degC\n", - "18.0 78.74693577860596 degC\n", - "19.0 78.17946642081989 degC\n", - "20.0 77.6176717566117 degC\n", - "21.0 77.06149503904558 degC\n", - "22.0 76.51088008865513 degC\n", - "23.0 75.96577128776858 degC\n", - "24.0 75.42611357489089 degC\n", - "25.0 74.89185243914199 degC\n", - "26.0 74.36293391475057 degC\n", - "27.0 73.83930457560307 degC\n", - "28.0 73.32091152984704 degC\n", - "29.0 72.80770241454857 degC\n", - "30.0 72.2996253904031 degC" - ] - }, - "execution_count": 154, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run_simulation(coffee, update)\n", - "coffee.results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 155, - "metadata": {}, - "outputs": [ - { - "ename": "ValueError", - "evalue": "setting an array element with a sequence.", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcoffee\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresults\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtemp\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlabel\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'coffee'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m decorate(xlabel='Time (minutes)',\n\u001b[1;32m 3\u001b[0m ylabel='Temperature (C)')\n", - "\u001b[0;32m/home/downey/ModSimPy/code/modsim.py\u001b[0m in \u001b[0;36mplot\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 212\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 213\u001b[0m \u001b[0maxes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgca\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 214\u001b[0;31m \u001b[0maxes\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrelim\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 215\u001b[0m \u001b[0maxes\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mautoscale_view\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 216\u001b[0m \u001b[0maxes\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmargins\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0.02\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36mrelim\u001b[0;34m(self, visible_only)\u001b[0m\n\u001b[1;32m 1941\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mline\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlines\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1942\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mvisible_only\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mline\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_visible\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-> 1943\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_update_line_limits\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mline\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1944\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1945\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mp\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpatches\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36m_update_line_limits\u001b[0;34m(self, line)\u001b[0m\n\u001b[1;32m 1813\u001b[0m \u001b[0mFigures\u001b[0m \u001b[0mout\u001b[0m \u001b[0mthe\u001b[0m \u001b[0mdata\u001b[0m \u001b[0mlimit\u001b[0m \u001b[0mof\u001b[0m \u001b[0mthe\u001b[0m \u001b[0mgiven\u001b[0m \u001b[0mline\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mupdating\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdataLim\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1814\u001b[0m \"\"\"\n\u001b[0;32m-> 1815\u001b[0;31m \u001b[0mpath\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mline\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_path\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 1816\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mpath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvertices\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msize\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1817\u001b[0m \u001b[0;32mreturn\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/matplotlib/lines.py\u001b[0m in \u001b[0;36mget_path\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 987\u001b[0m \"\"\"\n\u001b[1;32m 988\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_invalidy\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_invalidx\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 989\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrecache\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 990\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_path\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 991\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/matplotlib/lines.py\u001b[0m in \u001b[0;36mrecache\u001b[0;34m(self, always)\u001b[0m\n\u001b[1;32m 683\u001b[0m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0masarray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0myconv\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfloat_\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfilled\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnan\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 684\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 685\u001b[0;31m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0masarray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0myconv\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfloat_\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 686\u001b[0m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mravel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 687\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/numpy/core/numeric.py\u001b[0m in \u001b[0;36masarray\u001b[0;34m(a, dtype, order)\u001b[0m\n\u001b[1;32m 529\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 530\u001b[0m \"\"\"\n\u001b[0;32m--> 531\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdtype\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcopy\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0morder\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0morder\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 532\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 533\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: setting an array element with a sequence." - ] - }, - { - "ename": "ValueError", - "evalue": "setting an array element with a sequence.", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/IPython/core/formatters.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, obj)\u001b[0m\n\u001b[1;32m 305\u001b[0m \u001b[0;32mpass\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 306\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 307\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mprinter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 308\u001b[0m \u001b[0;31m# Finally look for special method names\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 309\u001b[0m \u001b[0mmethod\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_real_method\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprint_method\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/IPython/core/pylabtools.py\u001b[0m in \u001b[0;36m\u001b[0;34m(fig)\u001b[0m\n\u001b[1;32m 238\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 239\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;34m'png'\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mformats\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 240\u001b[0;31m \u001b[0mpng_formatter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfor_type\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mFigure\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mfig\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mprint_figure\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfig\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'png'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\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 241\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;34m'retina'\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mformats\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;34m'png2x'\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mformats\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 242\u001b[0m \u001b[0mpng_formatter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfor_type\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mFigure\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mfig\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mretina_figure\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfig\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/IPython/core/pylabtools.py\u001b[0m in \u001b[0;36mprint_figure\u001b[0;34m(fig, fmt, bbox_inches, **kwargs)\u001b[0m\n\u001b[1;32m 122\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 123\u001b[0m \u001b[0mbytes_io\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mBytesIO\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 124\u001b[0;31m \u001b[0mfig\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcanvas\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprint_figure\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbytes_io\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 125\u001b[0m \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbytes_io\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetvalue\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 126\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mfmt\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m'svg'\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/matplotlib/backend_bases.py\u001b[0m in \u001b[0;36mprint_figure\u001b[0;34m(self, filename, dpi, facecolor, edgecolor, orientation, format, **kwargs)\u001b[0m\n\u001b[1;32m 2198\u001b[0m \u001b[0morientation\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0morientation\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2199\u001b[0m \u001b[0mdryrun\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2200\u001b[0;31m **kwargs)\n\u001b[0m\u001b[1;32m 2201\u001b[0m \u001b[0mrenderer\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfigure\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_cachedRenderer\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2202\u001b[0m \u001b[0mbbox_inches\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfigure\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_tightbbox\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrenderer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/matplotlib/backends/backend_agg.py\u001b[0m in \u001b[0;36mprint_png\u001b[0;34m(self, filename_or_obj, *args, **kwargs)\u001b[0m\n\u001b[1;32m 543\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 544\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mprint_png\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfilename_or_obj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 545\u001b[0;31m \u001b[0mFigureCanvasAgg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdraw\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 546\u001b[0m \u001b[0mrenderer\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_renderer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 547\u001b[0m \u001b[0moriginal_dpi\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdpi\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/matplotlib/backends/backend_agg.py\u001b[0m in \u001b[0;36mdraw\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 462\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 463\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 464\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfigure\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdraw\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrenderer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 465\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 466\u001b[0m \u001b[0mRendererAgg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlock\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrelease\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/matplotlib/artist.py\u001b[0m in \u001b[0;36mdraw_wrapper\u001b[0;34m(artist, renderer, *args, **kwargs)\u001b[0m\n\u001b[1;32m 61\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdraw_wrapper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0martist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 62\u001b[0m \u001b[0mbefore\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0martist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 63\u001b[0;31m \u001b[0mdraw\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0martist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 64\u001b[0m \u001b[0mafter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0martist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 65\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/matplotlib/figure.py\u001b[0m in \u001b[0;36mdraw\u001b[0;34m(self, renderer)\u001b[0m\n\u001b[1;32m 1142\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1143\u001b[0m mimage._draw_list_compositing_images(\n\u001b[0;32m-> 1144\u001b[0;31m renderer, self, dsu, self.suppressComposite)\n\u001b[0m\u001b[1;32m 1145\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1146\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclose_group\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'figure'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/matplotlib/image.py\u001b[0m in \u001b[0;36m_draw_list_compositing_images\u001b[0;34m(renderer, parent, dsu, suppress_composite)\u001b[0m\n\u001b[1;32m 137\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mnot_composite\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mhas_images\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 138\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mzorder\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0ma\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mdsu\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 139\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdraw\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrenderer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 140\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 141\u001b[0m \u001b[0;31m# Composite any adjacent images together\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/matplotlib/artist.py\u001b[0m in \u001b[0;36mdraw_wrapper\u001b[0;34m(artist, renderer, *args, **kwargs)\u001b[0m\n\u001b[1;32m 61\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdraw_wrapper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0martist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 62\u001b[0m \u001b[0mbefore\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0martist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 63\u001b[0;31m \u001b[0mdraw\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0martist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 64\u001b[0m \u001b[0mafter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0martist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 65\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36mdraw\u001b[0;34m(self, renderer, inframe)\u001b[0m\n\u001b[1;32m 2424\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstop_rasterizing\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2425\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2426\u001b[0;31m \u001b[0mmimage\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_draw_list_compositing_images\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrenderer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdsu\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2427\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2428\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclose_group\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'axes'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/matplotlib/image.py\u001b[0m in \u001b[0;36m_draw_list_compositing_images\u001b[0;34m(renderer, parent, dsu, suppress_composite)\u001b[0m\n\u001b[1;32m 137\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mnot_composite\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mhas_images\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 138\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mzorder\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0ma\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mdsu\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 139\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdraw\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrenderer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 140\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 141\u001b[0m \u001b[0;31m# Composite any adjacent images together\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/matplotlib/artist.py\u001b[0m in \u001b[0;36mdraw_wrapper\u001b[0;34m(artist, renderer, *args, **kwargs)\u001b[0m\n\u001b[1;32m 61\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdraw_wrapper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0martist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 62\u001b[0m \u001b[0mbefore\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0martist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 63\u001b[0;31m \u001b[0mdraw\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0martist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 64\u001b[0m \u001b[0mafter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0martist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 65\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/matplotlib/lines.py\u001b[0m in \u001b[0;36mdraw\u001b[0;34m(self, renderer)\u001b[0m\n\u001b[1;32m 774\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 775\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_invalidy\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_invalidx\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 776\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrecache\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 777\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mind_offset\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m0\u001b[0m \u001b[0;31m# Needed for contains() method.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 778\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_subslice\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0maxes\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/matplotlib/lines.py\u001b[0m in \u001b[0;36mrecache\u001b[0;34m(self, always)\u001b[0m\n\u001b[1;32m 683\u001b[0m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0masarray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0myconv\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfloat_\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfilled\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnan\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 684\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 685\u001b[0;31m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0masarray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0myconv\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfloat_\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 686\u001b[0m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mravel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 687\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/downey/anaconda3/lib/python3.6/site-packages/numpy/core/numeric.py\u001b[0m in \u001b[0;36masarray\u001b[0;34m(a, dtype, order)\u001b[0m\n\u001b[1;32m 529\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 530\u001b[0m \"\"\"\n\u001b[0;32m--> 531\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdtype\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcopy\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0morder\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0morder\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 532\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 533\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: setting an array element with a sequence." - ] - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(coffee.results.temp, label='coffee')\n", - "decorate(xlabel='Time (minutes)',\n", - " ylabel='Temperature (C)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def final_temp(system):\n", - " \"\"\"Final temperature.\n", - " \n", - " system: System object with results.\n", - " \n", - " returns: temperature (C)\n", - " \"\"\"\n", - " # TODO: if there are no results, return init.temp? \n", - " df = system.results\n", - " return df.temp[system.t_end]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def run(volume, T_init, r, t_end):\n", - " init = State(temp=T_init)\n", - " system = System(volume=volume, init=init, T_env=22, \n", - " r=r, t0=0, t_end=t_end)\n", - " run_simulation(system, update)\n", - " return system" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "72.2996253904031" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "coffee = run(volume=300, T_init=90, r=0.01, t_end=30)\n", - "final_temp(coffee)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def func(x):\n", - " return (x-1) * (x-2) * (x-3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 1.])" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fsolve(func, x0=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 2.])" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fsolve(func, 1.9)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 3.])" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fsolve(func, 2.9)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 3.])" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fsolve(func, 1.5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def error_func1(r):\n", - " \"\"\"Runs a simulation and returns the `error`.\n", - " \n", - " r: thermal insulation factor\n", - " \n", - " returns: difference between final temp and 70C\n", - " \"\"\"\n", - " system = run(volume=300, T_init=90, r=r, t_end=30)\n", - " return final_temp(system) - 70" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2.2996253904030937" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "error_func1(r=0.01)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 88, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIwAAAAPBAMAAADEyjp7AAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEJmJdjLNVN0iZu+7\nq0QgoRR7AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACRElEQVQ4Ea2TP2gTYRjGn8s1d+bSJKctWHDI\nYemmTVAniRi1izg0KLiJ7eQimMVFKIiDLhaPDoIuHiqCBbEdFMR/p5NOCcU/UAiJi4uDitJKW43P\n+96XCA5OHtz3vt/ze78n3/fdG2DLnjHIk0THZyqDcyA9zjQdILO7Ujd4+JBoIiq2JnZ1ZH6U7ymM\nhJJrzDTooEO2261S3RRjDu6awQFydBRRcQp4zykuA94C7CmmGq2XbR86wNl3mCqOxJis40eC3QgZ\nqaWo+BxwldOBJ8BgE+5X5iZOcjeQIcfIjd2J8aljrSTYjmGdT0TFFWCERddPA4Um8qvMTfzLxnZi\nMh5KcX7DtyNARLX5/AJ7icu0KQbIf2du4h+b+YM14C5X8LoDg9trskzEnOBc91bE6whpc6OM9Dcy\nE/s2g37mF6wqV+DYhR5OdaeRiIpRWuHxh/EPGzq/gg2xgf3A/MrN4+udnkjsjZ15xjOJTbFsDpXE\n/m64uhSehRMzwWNfy7wm7i+iJ5bCe/DWfbcmNoUArl5xEvs274D9H8pwYlwDGnUty/pIr1oqCu4s\nAifrQxAbtpInH9zEvs1D7mZrq9V+Wu36aESKi6xbdlUUvPkL+yKaabXW32BgAakpYhP7NgFwkXI2\nBpvrSqg4GwKvE1HxI7YbJanAJWyrsb80Jp2n7TcHj1UoxJiHy46Qsvw0vGoiKv7o4zan2OA7NPEW\nmDVx5/PZCDpkRpf4Me3Gzyi/fUfH4BOj41whouL0kv41K91lqv/j+Q0689AKrZvG4AAAAABJRU5E\nrkJggg==\n", - "text/latex": [ - "$$0.011543084584$$" - ], - "text/plain": [ - "0.011543084584" - ] - }, - "execution_count": 88, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solution = fsolve(error_func1, 0.01, xtol=1e-8)\n", - "r_coffee = solution[0]\n", - "r_coffee" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "70.0000000000064" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "coffee = run(volume=300, T_init=90, r=r_coffee, t_end=30)\n", - "final_temp(coffee)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "18.499850754390966" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "milk = run(volume=50, T_init=5, r=0.1, t_end=15)\n", - "final_temp(milk)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def error_func2(r):\n", - " \"\"\"Runs a simulation and returns the `error`.\n", - " \n", - " r: thermal insulation factor\n", - " \n", - " returns: difference between final temp and 20C\n", - " \"\"\"\n", - " system = run(volume=50, T_init=5, r=r, t_end=15)\n", - " return final_temp(system) - 20" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "-1.500149245609034" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "error_func2(r=0.1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.13296078935465339" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solution = fsolve(error_func2, 0.1)\n", - "r_milk = solution[0]\n", - "r_milk" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "19.999999999999613" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "milk = run(volume=50, T_init=5, r=r_milk, t_end=15)\n", - "final_temp(milk)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "21.76470588235285" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "milk = run(volume=50, T_init=5, r=r_milk, t_end=30)\n", - "final_temp(milk)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file chap07-fig01.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n4f9vjH2cyvwAAAAASUVORK5CYII=\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(coffee.results.temp, label='coffee')\n", - "plot(milk.results.temp, '--', label='milk')\n", - "decorate(xlabel='Time (minutes)',\n", - " ylabel='Temperature (C)',\n", - " loc='center left')\n", - "\n", - "savefig('chap07-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIUAAAAyBAMAAACAOwXCAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMA74lUMhB2u6tmIpnd\nzURTbmnuAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAC/ElEQVRIDe2Xy2tTQRTGv7xq87AUFEQQGgRx\nUcSK4kbBgBWRbrIQBd3EhfgXCAEVXUmKSvMXaHDrInEh4sbWhesIbgQJieLGXX3j83rOmTP3ziS3\nSbRd9kByvzlz7u/OnEzyEcwEH7GOSAbBNLbNn1gHApn549PYbghnqrX6fZHNHbWgeidmcZlLtcVc\nkWtyH6pL32pd0UhbxsRO4Ai9KHYDu4Anor23C3WkHtY5lSwisYK86JCRuVoG8j/klhvI/QTuhref\nU3X2OYnlBo8mgWYL2R7rkLH1Cw2mpiVVR/YTcJG1xGG9dukxeCODU8C9OUxwImIsz/GgxakpegwR\ne6wllJEnMLAgqR6wVOZKDu1H7hcPMiV+p6C9OqGMpiRP2wlqmYYykl91fFKutFfASBLK2F+ROcxe\n4S1wy1Qqo/DZzC/clivtFSpprIx2SeaSjckXJLhlKpWRWOX5DHBU6mivVpJQxoxMlQuNNH983DKV\ndh3COGRvlL0a3L5O51mn85ruajMjVUr0cn9IcMtUKsOcjJYyZK+6JCrXdcjJyDOHz4G0zEhl5Phk\nJ2kH8nDeq10SCWU0i6T30KtQoTdumUpl4Bgd40eUFMYWftDAOlLf6dtAj8Esz15nZaRlZB7XnnKS\nGXu7v2+q5JRdB85Xb/VomG3Rkq8FrypGhmeMpkyYRooOpe7FlrzFO1/addjsASuAUL6PcqTSLw9e\n1oTKPsaDNu9CwpE2JddEEKxqQmUfw6sed7DJ8Dul/Qj+O+gz2uxpbE/95D+OYnsaWa9Pi/zWy8cx\nHOv1ahH5rZePYbjWK7UxfjuK4VivKbW/QY7fjmI41usxXL8dweizXqq2v+srfGfotzwwMdiPPuul\nMmX4fmsBdB1k9FlvxPD9diijz3ojhu+3QxkF+cUNrXctvx3K8K2XS7Ufvt8OZfjW6zB8vx3K8K3X\nYfh+O5zhWq9U2nPq+K1DiPtsw+kx/NbUDp6PkDHab7V0bcYYfjuSEa5npKB1bMR/jw34D/QXAgUS\n4RvxulUAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$\\left [ \\frac{C_{1} T_{1} + C_{2} T_{2}}{C_{1} + C_{2}}\\right ]$$" - ], - "text/plain": [ - "⎡C₁⋅T₁ + C₂⋅T₂⎤\n", - "⎢─────────────⎥\n", - "⎣ C₁ + C₂ ⎦" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "sympy.init_printing() \n", - "\n", - "from sympy import symbols, Eq, solve\n", - "\n", - "C1, C2, T1, T2, T = symbols('C1 C2 T1 T2 T')\n", - "\n", - "eq = Eq(C1 * (T - T1) + C2 * (T - T2), 0)\n", - "\n", - "solve(eq, T)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def mix(s1, s2):\n", - " assert s1.t_end == s2.t_end\n", - " \n", - " volume = s1.volume + s2.volume\n", - " \n", - " temp = (s1.volume * final_temp(s1) + \n", - " s2.volume * final_temp(s2)) / volume\n", - " \n", - " init = State(temp=temp)\n", - " mixture = System(volume=volume, init=init, T_env=22, \n", - " r=s1.r, t0=s1.t_end, t_end=30)\n", - " \n", - " return mixture" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXYAAAAVBAMAAACgZ9gWAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAMmYiu80QdonvRN2Z\nVKvu110NAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAE00lEQVRIDdVWXWibVRh+8n3J1/w3N94VGwSZ\nA3FBJwgWFn8Y4vyJInOIsu9mbmNiA2I3QbYwJ41YamRz4EQWENnFhAac4M1qClNKKVvwVrGxoLtw\nbtpt1dZ18XnfE3uSJmOD9sJ90Pec933e93mfc3rO9wW4nZ9A+rZU76QoeyMC45PFIl4bntBF6GiN\nN/l2CmpMRjfEwCjuE76+zq0YOPMIgyFfENNsOde4ShAaCkzB2ztSBnbumwK06qHi06wUxJ1+nPSK\nvOj3lfEzybbCaTQa1/EK9mSEXEdrQjXvS6i5MWLgHeV4jvWnhKTt8er4oAz3WV+iptlyrnGVINJo\n5BEHfgeqSNa0yq1iJg1FXkd40SA43viLCwJ6CohTc90pISytdbQG64F3jbFBk2sRnWEbglnyHRGF\nbU/SR6TkPXXJl6g2s7nGVYLQ6BvA98CDSKTh5rSqZwm9FSjyK/CxQbDtPBVTeCSDMBBOBetIzJNb\nR2vwDDDjq7FBk2sRnTmXRRruf0GHVhOpIPkncMCXoDazueqaJkmBDwF7EM7Cq2hV9BgGc1DkE+BS\nShHUJdMr4aSMOIHeOqJLnOloDa4DYzU1NmhyLaKzYEmZCp3aQ/Mt2rVZW+4JmCaqcGwzDiJ6xQ+n\nm1VyZhTZ7lO7IkY7j/CIdsxisIooT5EZ1VHjXaP2ITF5G1yBGLj3uWFeMCfTqZ2sMf5Lzb7TyaIt\nl5us/Mm7+FZINu5Jc4PnDjJPq/AyoAj9byhfkNl1E/RG8KgkhdPYVEDgb051tMZlrP8dMQUb1JlF\nDDz4PCI17ERX7WMFq53NWnPpKlUh6Lu8jOeu+rwUja2UAlYF3q/yHCuCKGUoshmbysDdOC1Jg03N\nnN5Q4U21zyN+FIXu2k+R+b99Z7PBlly6RjtTtsCZ2H4MuPeOhTJdqcJ7KbFb+BfLLiPBCvADmAm8\nSbpC88zIqI4aj2sdGxKTt8EViIH5PnAWE6mu2uPsuqydzVpz6WqTPFPOZe6Ds+A7dWw4yj2WKkQ4\nEwQ4wyNpEMT5Yug32rnA3ioSeldlVMdErvE9U1NjgysRhSM5OHO70FX7fml/wBeru9maK5urBL+A\nnwEK7a9FfASohVVuBsElKIKeOhciSDKnr/p+PTMeXxSxLJx50uhojfzfNvhqbNDkWkRnfHc4iz9d\nvLhwniztT6KKB5a1S7OWXHFNk4+4u6/+wZuZ5jHCLKSqdx7BOQji40euRBGuPE6pJ/WuBrjunhL0\ns6ijNXgL+NYYGzS5FtEZvw7xEpseadctXh/Az05z36VZS664hr8KfIXDXFiG3xw8plWRLGKX+Zkl\nEqiixyBx7nBO7uooSxNSfxq7U95VM6pjTKzmfQg1LUHJNcEW+AnsrpHnCu8OmVse9+vi2YpqF35t\nZnPVVZYBOIfxuY91iPJ3Sl6rnDRm8lDk4eLIb02kivUZYFq/Te6n7LRrL3/djDdHddR4w/v5W0yM\nyeiGGDg8TQIcasya97IVH+LvpQrOHh9PC782s7nqKoE7eYGn+cJIGbhzcgqm6ovpJ1kiyPZG4x+D\nYGD0O7K/hBhXsObPZ2vO2Eno8n5VO8OrjqRXzXBzgnBeLsWaP156zSk7CXcwtLEzvNqIs1qCW6mX\nPXfTt5L5v8txMvgXmotuk341B/EAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$\\left ( 70.0000000000064, \\quad 21.76470588235285\\right )$$" - ], - "text/plain": [ - "(70.0000000000064, 21.76470588235285)" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "coffee = run(volume=300, T_init=90, r=r_coffee, t_end=30)\n", - "milk = run(volume=50, T_init=5, r=r_milk, t_end=30)\n", - "final_temp(coffee), final_temp(milk)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIAAAAAPBAMAAADe9tr1AAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAiXYyEM1EmbtmIu9U\n3auvYvmWAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAChElEQVQoFaWSy2sTURTGf5PMZDJJmkddVBC1\nFkRQsOmqS8eFuCiSaRWl4GJw48KFIz5aFMqIooJCguBCXXRcFeqiAR/4KDbiH9C40oXUoCs3VWsa\naWkbz72p+Ad4FvfOPd93v/nOORco7CryqHAHHZYLh7vPq+UyTI1fU9l4QKqwtQZG95YOEl84Vygw\n4g76cMi3vVTAfEVRU2UXTnIkMl/yODIb7BQGXSFTZFYwj3MRjTjtdnuDufaSoMMkw9gaubp8mze/\nuzhVDC9bJRtmXdJVSe8L6a+xjpXnFhqxI2gw/EU2Z1UYieeUPNmh3yXZINPMeRjNdJ3sL7F1L+SM\nby4zGQlDI4aUUxQJiaT6hUSnBCWQa5BYK4XEWlZTCxhWKAQp4bYiakT2U8qDRO5o9yW1n1CLdlAK\nSCylxcGKnLua8EALWAHLA3t8/iIhfLsr3S6Nkq4R3x6o61pgJk/8p/EDS3XobB6zVwkMfMBcrjDL\nJmJU4DozPqUm9lNhbivK8k+AEd4qBx+lWLQD44nZdrnibiIlTU/WVfedlhzSSqVTQl5KwOkbWAM7\nhHcdAV676zDpbyK7Nd1eVSU5K6mIpNAlVBMDMuqQlfL3y2jzSmAayrVnIiDONSLWstKnlpqC08o1\nSSrDWkCejSN3sWScAdOZxcXvb3rFfbkiUxAHGjE3xLSH3ZSRY1fTIV3qPWiBWBXbc15xIGIQ9kpS\n4BfwOZqXHtBB4r+lPhmSB1cZqjkV5nvlpWgBPjFUjB0zR0m9L0zUJZkLuU9miWTenEUjZEQgFfAw\nkiaPfYXTYzdgASbmFioc7JNMz7iPJS9eBIzyRiUxJmd6JoodhNSsCE/tuCDrf8YfgpnQ2xgrylYA\nAAAASUVORK5CYII=\n", - "text/latex": [ - "$$63.1092436975$$" - ], - "text/plain": [ - "63.1092436975" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "mix_last = mix(coffee, milk)\n", - "mix_last.init.temp" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAH8AAAAPBAMAAAA/sQ3hAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAMt2rmYlmIkR2uxDN\nVO+L8+I6AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACN0lEQVQoFaWSzWvUUBTFf9NJJjOTmTaIrhUU\nBFEapRRE0RQ7KFVwpEaoCHZVcNMGKQgKdrZutGBBRNGiO100+LFx4WTjQkSahftWN4JoldaPylTi\nfe9l/AcM5OTlnPvOve/ex6aBMGxqAGp3CsdwG3MemnGGH3nwOt7c4tnYEIXtjTA0MrwBJzybwlSW\nZb4GMejNsllqqbvT0E+x1+Fr9hve8iQqSeiakbncgWnqP+EoFA2oCkZvwSScM8wBWIFX+yNKy9iJ\nFYFv5BdHxOB+yio04ZQBMSjKy0NYijX9AQ57sgeqPvWODbZnZCpicL3lbqgN5dkuGIM1uJFqejHu\nGvT5lP9I2DUwsjJAH0Hc1VIDxYk5z/0uBuJpmP6YwQtDtAPK0gnmyWVjUAvU3jP/gGrsrDs/YGsz\np8vy85iZ1kyTgiztcXJZG2zZoba637qg/niQR2iannnFVbflBm2ZnfE3FdjvRa0kXZAvnLwiEXIE\nTTOiOetXu6mPcFfy5bLuASsxJonJxEE435IeLKWGrvgymURuQ19AXZq4S/yMrCq4BAsptCXIAHyW\nCmKJmooN8w4n6k2wOpKg1MGVCSgTkZVBFrMwLg0LhNUAstzNPThhmEJAJbKkyKSyjJVQkOy5rAw+\nQX8k5SoDBe1ELmfpIz2pK71RzMXw9D6cgMmIPbz0qCsDIyuDCepqtFflGBp6fJzGQIw7dtMzzGKW\nyV2dHj0Oz2/vlQl8kVAtW4dWBykPj7SE+M/nLyZRy9/1kGV0AAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$77.5072507013$$" - ], - "text/plain": [ - "77.5072507013" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "coffee = run(volume=300, T_init=90, r=r_coffee, t_end=1)\n", - "milk = run(volume=50, T_init=5, r=r_milk, t_end=1)\n", - "mix_first = mix(coffee, milk)\n", - "mix_first.init.temp" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAH8AAAAPBAMAAAA/sQ3hAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAiXYyEM1EmbtmIu9U\n3auvYvmWAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACOElEQVQoFaWTv2sTYRjHP5demrv8vKRDFVGi\ni4OD6dRJegri4JBYRe12m2vAHxWFcoKooNAguOiQcxLpYIcqVYqNuJuMTiXUQbqkRZJiY/V83vsR\n/wAP3pf3+32e53vf53nvgOLRCiRtgkeBl8UncK50R4iQHquDVprg1Pdi0WLWnnYkNB2HzzqpGulF\nWzAokK7TbujvWfJiOueiX+YmTd/3u6z7P1TqV6LwBQxXf7htKxIFEvsUOvll8iP6hEvS4hHiKS85\nm55kJlYJq8yhKuSkrfYAjL+jWivU0PoRnX7m0vQkbMEEdOUEr6/KpqqMZQUjgQhIC1WXxG5Ea0mX\nx0EW4+VYwIoFChdLt0YCEeAKGXGwFwk8F4HB1HFH0gxZW0+vi1cvFqjOkWnFDkIwdriOtkNSZqU8\n6uWkqw8arEnxQVn3eeNwhpFAn9TKSCAEHKowy6fIgYYI+DZ3bfQdERAfHRlHLFDoYMbNyvQDQGYF\n89jUfqj7WQT4DU2HRC0QSA2zlZGANGvGzarOzb20hyG15MNb0C0l8FYEGsgHQV6ms3uakYAM/p+D\nABT6GCJJshs4yPZ62x/LcgvioCqUvCTV/9Lr/doMDWalbbnJ8DsIQMYlNzQ/MOPFtDBtmQHM1CEF\nOdXJqqyg6h7nW8FRH4ACZoN2OXFJn4szKLgYlr4GbRFI13nhSehnHNbmv8HC+kaDDfllFLg2/wAm\nbzsRjbb4p8HkQgWWWlL16sgN2Q/4W2GVnP/r+QuWvba+6ZV5pgAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$61.6391467541$$" - ], - "text/plain": [ - "61.6391467541" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run_simulation(mix_first, update)\n", - "final_temp(mix_first)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def run_and_mix(t_add):\n", - " coffee = run(volume=300, T_init=90, r=r_coffee, t_end=t_add)\n", - " milk = run(volume=50, T_init=5, r=r_milk, t_end=t_add)\n", - " mixture = mix(coffee, milk)\n", - " run_simulation(mixture, update)\n", - " return final_temp(mixture)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAH8AAAAPBAMAAAA/sQ3hAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAiXYyEM1EmbtmIu9U\n3auvYvmWAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACOElEQVQoFaWTv2sTYRjHP5demrv8vKRDFVGi\ni4OD6dRJegri4JBYRe12m2vAHxWFcoKooNAguOiQcxLpYIcqVYqNuJuMTiXUQbqkRZJiY/V83vsR\n/wAP3pf3+32e53vf53nvgOLRCiRtgkeBl8UncK50R4iQHquDVprg1Pdi0WLWnnYkNB2HzzqpGulF\nWzAokK7TbujvWfJiOueiX+YmTd/3u6z7P1TqV6LwBQxXf7htKxIFEvsUOvll8iP6hEvS4hHiKS85\nm55kJlYJq8yhKuSkrfYAjL+jWivU0PoRnX7m0vQkbMEEdOUEr6/KpqqMZQUjgQhIC1WXxG5Ea0mX\nx0EW4+VYwIoFChdLt0YCEeAKGXGwFwk8F4HB1HFH0gxZW0+vi1cvFqjOkWnFDkIwdriOtkNSZqU8\n6uWkqw8arEnxQVn3eeNwhpFAn9TKSCAEHKowy6fIgYYI+DZ3bfQdERAfHRlHLFDoYMbNyvQDQGYF\n89jUfqj7WQT4DU2HRC0QSA2zlZGANGvGzarOzb20hyG15MNb0C0l8FYEGsgHQV6ms3uakYAM/p+D\nABT6GCJJshs4yPZ62x/LcgvioCqUvCTV/9Lr/doMDWalbbnJ8DsIQMYlNzQ/MOPFtDBtmQHM1CEF\nOdXJqqyg6h7nW8FRH4ACZoN2OXFJn4szKLgYlr4GbRFI13nhSehnHNbmv8HC+kaDDfllFLg2/wAm\nbzsRjbb4p8HkQgWWWlL16sgN2Q/4W2GVnP/r+QuWvba+6ZV5pgAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$61.6391467541$$" - ], - "text/plain": [ - "61.6391467541" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run_and_mix(1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIAAAAAPBAMAAADe9tr1AAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAiXYyEM1EmbtmIu9U\n3auvYvmWAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACVUlEQVQoFaWTwWsTQRTGf0k32WySbtKCiBeT\nVkRQqPEgvdlVKApFktZcCqL5D5pi1eKhRBQPHiT04qGH7q2ihwasB3txBe+NNxGRoBdLD9W6rU2N\nxjezCendgZl9fN/3vnnzZhYYGMqxMv8ANS4P3lPLLQLk0tAdMGZP5dTiCl+UqbWamXJGSzBeMgtG\ng4yEGK954XKdq26AlLE9Ip7xkYlqfB2e+aLRWs28ae9I0iSxiu2QqEls17ArVo1QQSPJKvECS3CE\nLfjGlQUxOMRMfnHBOpDERB37p3xTBUJ+rEHS10ioglHnCWw4u5BxCIvBIaYhKcTUzhE/MMhXCO+l\nGkRbGonuO6Eqf2Hm3T6cLWmDQ4w2SF0blEZBv5iTkAqa+TJRdTZBtpvHMH6JQfaVVFDVBj2GzUXp\ndn6ahCfgTFqW0HciO6tp+n4EiNkuEpd4LJ2Bm9mugWgVw0NWS+R9zDWRf5AJU7xtdg0EeXr+dykw\niGSTvQo6jOhjdVJ1rD0wKyofa/hcK5/WRxDEarC8ZkgFM1kWT2S8TgVdRvTmgTq21YQRnS+L7afK\nJFsaSTj0tZAebHjCbDsdA9FqxpaO7albkAqSZZ4HFpFGv2ztayQv0KY63LIj0Q0CA6XVjOxtilB6\nUGMUTovGWmfMDdcwCxpJuPCYM3CbJbdPHox6Bz3GlJsqwH0mvPj7gYW6sUu4aEzDJyZyGokWsbL0\ne8ZLTjrj1cCgx8TL4itXd/crkXa7XeczHJ0vwcXhLnJhVv1MgyM5UT2Slm392exoNbNyfE4q+s/x\nDxGe4U+Pm7kyAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$62.9028091285$$" - ], - "text/plain": [ - "62.9028091285" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run_and_mix(15)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIAAAAAPBAMAAADe9tr1AAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAiXYyEM1EmbtmIu9U\n3auvYvmWAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAChElEQVQoFaWSy2sTURTGf5PMZDJJmkddVBC1\nFkRQsOmqS8eFuCiSaRWl4GJw48KFIz5aFMqIooJCguBCXXRcFeqiAR/4KDbiH9C40oXUoCs3VWsa\naWkbz72p+Ad4FvfOPd93v/nOORco7CryqHAHHZYLh7vPq+UyTI1fU9l4QKqwtQZG95YOEl84Vygw\n4g76cMi3vVTAfEVRU2UXTnIkMl/yODIb7BQGXSFTZFYwj3MRjTjtdnuDufaSoMMkw9gaubp8mze/\nuzhVDC9bJRtmXdJVSe8L6a+xjpXnFhqxI2gw/EU2Z1UYieeUPNmh3yXZINPMeRjNdJ3sL7F1L+SM\nby4zGQlDI4aUUxQJiaT6hUSnBCWQa5BYK4XEWlZTCxhWKAQp4bYiakT2U8qDRO5o9yW1n1CLdlAK\nSCylxcGKnLua8EALWAHLA3t8/iIhfLsr3S6Nkq4R3x6o61pgJk/8p/EDS3XobB6zVwkMfMBcrjDL\nJmJU4DozPqUm9lNhbivK8k+AEd4qBx+lWLQD44nZdrnibiIlTU/WVfedlhzSSqVTQl5KwOkbWAM7\nhHcdAV676zDpbyK7Nd1eVSU5K6mIpNAlVBMDMuqQlfL3y2jzSmAayrVnIiDONSLWstKnlpqC08o1\nSSrDWkCejSN3sWScAdOZxcXvb3rFfbkiUxAHGjE3xLSH3ZSRY1fTIV3qPWiBWBXbc15xIGIQ9kpS\n4BfwOZqXHtBB4r+lPhmSB1cZqjkV5nvlpWgBPjFUjB0zR0m9L0zUJZkLuU9miWTenEUjZEQgFfAw\nkiaPfYXTYzdgASbmFioc7JNMz7iPJS9eBIzyRiUxJmd6JoodhNSsCE/tuCDrf8YfgpnQ2xgrylYA\nAAAASUVORK5CYII=\n", - "text/latex": [ - "$$63.1092436975$$" - ], - "text/plain": [ - "63.1092436975" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run_and_mix(30)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "sweep = Sweep()\n", - "for t_add in range(1, 30):\n", - " temp = run_and_mix(t_add)\n", - " sweep[t_add] = temp" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file chap07-fig02.pdf\n" - ] - }, - { - "data": { - "image/png": "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\nCwMvj9jpc/T0AAAAAElFTkSuQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(sweep, color='purple')\n", - "decorate(xlabel='Time added (min)',\n", - " ylabel='Final temperature (C)',\n", - " legend=False)\n", - "\n", - "savefig('chap07-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from sympy.printing import latex\n", - "\n", - "def show(expr, show_latex=True):\n", - " if show_latex:\n", - " print(latex(expr))\n", - " return expr" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPsAAAArBAMAAABMYuO6AAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMARLvvmVQQid3NIqt2\nMmaorGxOAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADi0lEQVRYCcVXz2sTQRT+Nmk26bZNAyIIFVoQ\nD1rR6kXtQfYqguQfkEZEsLSQIIKVVslFrPWSg0p7EEPFk2CDF5EWjBe9RMjFq6TQk1CsohEPWt/M\n7MzOzm7qcR7szvv5fdmZnck+gIlzyOejrdtw0RYz5x0tWaWfs8qOhxbpvdcff1qk31dPde3Ru+fQ\nV7NH39dFrmKPPj2GcsEefbmCKZSs8ZeLOJKpW6Pvb+YfZK2xw924/eJdhH6ow8ztiC/R+Jzo3dsZ\nghsEa6fWd8ff/AJWOcBAe28cwBtTGapWeQxFJYTgBsFN4AxwEXgMuEvAfw+D/pLiULXKYygqQQOP\nEjTg/QA2MVgBMjvABwMhZmoJsjaW49aFSybo4Fo9QIuSp7P/PdJUwc7BKzEswzGpbFWrPFLJNLmm\nEnTwCIEDDNDZX8ABKmDnYH9RYiSPTlX5Va3ySCWgVwk6uEmQ5oA3gJET401kfYlhjM785YUmjLio\nNTJpFSlRSBzcJFjzWeZxuqboytMCEMkKF5/pgWRTj1ITQKojHWwUtbqH6yF9HFwQhCVf+XTfJcd5\nujLVMBLRPuUaTpsWiGfLiKiVlhpD+ji4SfC2xMpO0nWPrqEdZmniLi+S3G+XxF9UugBMM8/iE0oS\ntVq2UEP6ODgR7EphXLTtSYje/UZjjJ5H2Y0tDcDoQxG1oc00WrjlsysrPvfGwQ0Ctu1JaPKdHZTg\nVJmV5c+32GC6FLY0xuTzWnehha3tlo+DjWxFJKunTwAXBCKP7mzbk9Crl61l2sGrx13G7TS3Ux3N\nzWunSzPY/ItJVLqZMRFU9Ang+ZoGQK+y+OSbp1OnMUs3PxINDY8tDf1GfUZ47Xqr481W0fWuVmE+\nfQJ4lOD6098bDJZOBu9Yk46dArMSJJg0fe5ELXtpB5rOBAYqXlAsnz4JPJkgVxeUzxKYI66liEXG\nH7hIt/v8dq4wGMQkvUzVwQWB2WDmfZG7IEt6jVtm4DBmsIbc/nau/TyIOZ1okg4eEAwXca2uZR3l\nulvTXIlqqm64Z1pNjGDwDpyNjhFSZgguCUZLwU4OcsQXQQxcIUhF+9yQrv+PIbgkmBOHrCr1Okz9\nouyeysuekd6BEFwSUIPJDlkrwhpM59V3vuUs/ADeYLJvKysiGkxrPaZoMK31mGneYFrrMUWDOUqn\npRUp8wbzFi5ZYYdoMC+4TTv0osFcbdlh/wdTXw3MbiYF2AAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$\\frac{d}{d t} T{\\left (t \\right )} = - r \\left(- T_{env} + T{\\left (t \\right )}\\right)$$" - ], - "text/plain": [ - "d \n", - "──(T(t)) = -r⋅(-Tₑₙᵥ + T(t))\n", - "dt " - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from sympy import symbols, Function, diff, Eq, dsolve\n", - "\n", - "T_init, T_env, r, t = symbols('T_init T_env r t')\n", - "T = symbols('T', cls=Function)\n", - "\n", - "eqn = Eq(diff(T(t), t), -r * (T(t) - T_env))\n", - "eqn" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "T{\\left (t \\right )} = C_{1} e^{- r t} + T_{env}\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMAAAAAYBAMAAABen+92AAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIu+7q82J3ZlmRDJU\ndhDTAJzgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAC50lEQVRIDa1VTWgTQRT+Nk02TZpuIoKoh7ae\npOhBseJNAxY9SGgPBoqK7qXiRageFOrBgIpVUPciVA8atFA8NeBFD2LQQ4Ui7aHUFgmsB7WXYrWg\n4v97M7OT2TZthfSD2fe97715b2cmswHqwtG6Zq8+2fmzek5dGam+uqavPHnwzuS6qz2llZNC0afs\nJf57xt0f8WKsHKpQ07G+dHQtdJ5wYe0Q8UM1suY6Or0Ni3RrjvLTmbCqixmy7SLSh0YPMR/4mEHM\niCna3A68oqGQ2kXYi+gE8CDQlNXFDD0KtBaQKGKKxHGg2TWCgjp7fKDx82I5UgImHbWEdzKqixnJ\ntPP3y2j2sYXEK4DTZgQFjfFvMblEbiX1hi0ygNfS6mJKZlMEunwkkewj5xyNYzRCuFUmt6kQ0sh5\nSGPorVJVg6IqptTAbGViF5Dcv5ADTgWystY3Jg5tCJvT02pThKsfqgH5opjWBbG+s2nwgNQ8kdvs\n2XmBAtPfLBAe0xjOWC7zxdANZLFwOPGL/XiZTrKbyCh7BuI/pfPkIGBtx2zJiGmqG8hiWhckKj4o\nkSI1cUl4LsTqI8LLgiM6R9sr96oRg+kGspgRIUrXgJ9F4BONJQ3iosGYaJDOUsIS9ObzZ/P5HqHL\nYuEUugYE3qIRftFh9uzzAlmi8gYUZAOXhFrQKxDFPlQmEteeTSM5iZOUTdeAwIfcC3pRccisKFhf\nidi+aBBxgQmlh4xuwMWc4042MeJFMnbaf0lp+3zOtbPAZYdmD7BnYtxD6iYJo3TNuzGbMWMB1w24\nWDQ3g5Y3SPsto7Bh7/57mF+KL9r6CpEjwaTAOpc6rzOnBpie8QI5ZFUDWYyPEzn+UB3A5mraGUmt\ntqoUZtxgOegVcEK6BB/bMNCEfnNDB+Vkm5dTEy9qqlJ8ZMYayk7Wmkf/GC5Y5WqgISP4+6oSZhsv\n5sLCsp6TqyCZxZSHoRkjSf3h7DSkNaabuF5CrmPtSv8DUyuwq8EudNcAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$T{\\left (t \\right )} = C_{1} e^{- r t} + T_{env}$$" - ], - "text/plain": [ - " -r⋅t \n", - "T(t) = C₁⋅ℯ + Tₑₙᵥ" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solution_eq = dsolve(eqn)\n", - "show(solution_eq)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAHwAAAAWBAMAAADwX+WxAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEHa7q2Yiie9Umd3N\nRDIfxLosAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAB+0lEQVQ4EYVTP0jcUBj/5bwkl6O5e1Co0MGG\ns0dL61CoY8EjWJUuBsHZu0kRyl2njnXroNgDtwpFihZR8M/i5CmCS+mBNzi4GQSXTorWP13a770k\n73JpL35Dvt/3+5P3wnsBYuqeGyPeLT1id3tiHPMxWrykLo0MTG6vxZvaq+pxo4bv7fWmonT3v9Yq\nzVmgUje1q1ZSu+kdvug/iFifOjCWHemce0nFYAPmlCQF0CtIbSDdtHL2/iY91oschmsWSBQeeIxS\nFD0JlBegNjzSfx4wAkctFA3aGZC2Kh5tWKKfAmNVJLhfVvqWw59y9oH5gjZvu97kxxvAMIPZYi1v\n8LHL4zpz31pEf/DjNE1H5Q+Wx2T4clsYj+p8lnHtd1TedQWTWaRdTFSV/14UGVfFl4Zf8UkMDAbF\nh0byLKwFWMaT5wEV9F0ODFfELwMy3PV6/cdMvb7AOTr2SIkTT9MbSLmOaMEoV6djBzpzlrp08gzm\nY7xHuULMOy/+ka5KEAl3Gadjh7KnFNRjJ1XUs2wOxi/gM/PiPVDiD66PjEk7j9JzZFlpHjrwsPdN\ng5bim098HQ0vKrG/uv7qz76FFHfb/J4O4m3TQvF2JTfPDVkXDLNYyaAGSyaMKQn/AaYTojqqSoH+\niNoX7GjVgNdWL60Ax3fFzsEs4ImDwzz+AioJZBj7Bd1mAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$C_{1} e^{- r t} + T_{env}$$" - ], - "text/plain": [ - " -r⋅t \n", - "C₁⋅ℯ + Tₑₙᵥ" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "general = solution_eq.rhs\n", - "general" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAFYAAAARBAMAAAC1JUYQAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEHa7q2Yiie9Umd3N\nRDIfxLosAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABZUlEQVQoFW2RPUvDUBSG32g+C2kvOAgOEqpF\n0cWho2AoFcGl+Qe2P0BSJ0fdFS24KUgHBamD9QfUIrgZaAcHN4vg4qSooC56bnJ7U2PPkPve5zwc\nLicAlKnisl5FovTPfOmt2EnwWQ/WmZdQYVRhNpH6y8cuSbsoS1eJogr4dWhdyXnoMPrcx8hywvwE\nrNUwwpuyUl88Pss7hEsDSwx2zCn5TX6djJlwCezEMEpbTnSmF0RHuvq3IPJo98KYPgnnU5auFr5O\nihT2wwuD9c9VXwc9ntv8Y/WEawTB7W4Q1Dmk9SYq3GyK9H5HvoHWC4xnHe30cQ72NDbhV4lsDHNp\nvVCuFVd78MyykWEHsD6AQzbMXSKqFnKozCPDKkcwgIn8apdGJ99gLP7cODB5q8B/6wrWKUaVdCOa\n6YFhD400WnCESXO3RbQ9yYDRmuLqL2gd40qv9bl+/u7088CpFLKwXcx4uMvhF3itS7lwyWUgAAAA\nAElFTkSuQmCC\n", - "text/latex": [ - "$$C_{1} + T_{env}$$" - ], - "text/plain": [ - "C₁ + Tₑₙᵥ" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "at0 = general.subs(t, 0)\n", - "at0" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAHkAAAARBAMAAAALcx5NAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEM3dMiLvu6uJmWZE\nVHYiGvycAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABg0lEQVQ4EX2TMUvDQBiG31jTNprWUhyyqBCd\nXCz4B7K49w9IRREXB0cXoTh1qnUQEUECjoJ0cJJCO4mLpfgLIjiIQyDioiJ6d99drmlJb7j3+773\neXMkRwC5anuNv8PjL9WO60TgAtgBbsZTajIRqMP8BjqK1WpUqU4FmD3dxuwv8KJTqioEokoHmG0D\n+QjoCTKxyXQ6QHS2kkipRqZZmwIQWPNVIKE6LYB3aSpVbHegqoTqtAA2pMl0BSiv88UKnJQSKdXo\n9AhgsO+kF7vusWW57vK26/rCGAEKw9+JXzeMqxD3d6GPft1q0bPiswXQryvfvF71iOA7v26USw46\nP9hFKyockBenBXAakc90akCA2HP8NRph21ysIDIXKhg9mwNsHvtzVZ0+X/toAvtskA/sLeRbZo9M\ndTYBuYB8pg/kD+2fMJD1Znwv0yvKsUpTuzRPPtM39QfE+SM4qCHz5GW8TTm027HLimeLfKa31rDB\naycM8IjiJexmIhRzzhn5XF/xD1YId0zuAa6QAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$- T_{env} + T_{init}$$" - ], - "text/plain": [ - "-Tₑₙᵥ + Tᵢₙᵢₜ" - ] - }, - "execution_count": 45, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solutions = solve(Eq(at0, T_init), C1)\n", - "value_of_C1 = solutions[0]\n", - "value_of_C1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "T_{env} + \\left(- T_{env} + T_{init}\\right) e^{- r t}\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPUAAAAYBAMAAADQRaYKAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIu+7q82J3ZlmRDJU\ndhDTAJzgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADN0lEQVRIDcVWTWgTURD+Nkk3zTbZjUdFmhYR\n8edQsd5Ec6heJNSDQVGxK4LooRJPQr3kZnsQchFqBQ1a6EVxPXoQAyI9eNlDqSklsodUCloURfGn\npM57u2+zf4HmlA+y82a+b97b7swbCvQKF3t1MKC2end2utSbs2cfLO24e97YzuGvtiPqrEmFDpn7\nm6z21QDp++j4j7EJvXOuNOJyuT1jW6Mnf7l+aBEpOB2QSRu0o5YFZB2xEvorAd7j9lmucw3YB8y4\nfmgRKehzZekjhONImMATCiaAXBmpqsuLhUovxrEsIkAe0m9grh0QKyGOFGR0IXNszACWKIWK+biG\njBWggTS9HMewSw1UkNoEProBd+GIowXqkKuzFzky92SgCoxbUAIsueJspeRypErQtaSUIBxxB8Gl\ngPwp+fOrPEgljIA4Wy57SeqNKAgxWPOEcJ1F1Ml6NsSwEkZAbBeveEnqjSgIMW8eoO5oHHufuQtZ\nSXfCbcNKGAGxXbLmJak3oiDEvHmAl46GLBvbi/STDmHdCKWyEkZAbBerAmu3Gdg21BtREOKQgI/t\nN5SS2N94FM6MqpBcLJ7bWyyWSc3ObiOqNzxifv/baupYVn52tpb3hsWal/BTw0zNvK5DWQLNBwbx\np/i+Oe8NdbKB2YeNMp7naTJxCDG//xQXvMTH9gJpNEdp68WTlVC9rOZTLyqxrKxZb21CbOfrNd4b\na1YTc38wDL2VHvGL+f3HzZbNk2VjG6zXYjpgkvXjhEXVKKxg8AM0a3ARdOkZxNlynrv2I856Y6xR\nkTZKaElfStDtuBCDCSju8lqWBNP0y5zBOlt7IR/dOmvaRS2wIXsKu2xabOeZLbg68bMAHCBBwlSG\nkNClql9sC+KmzZOlHYEL7FFfqTAThmbAwkFMD2BKfBlxNm4F5f+gImb0l41kNeNwrpj7Xz/bPFk2\ntqUhHu30iNfUvPQNU+9xR6rZIkW85mww6RiayCH5zEgaux3OFXP/nWzzZNnYls3gDj5fLTSg5LFc\nwfyKjyAnHixTs2FiHpkbUAri/fw5zSs2z+wqTQc/243n+d+hm7S29nB72fVqZ9cZvoRU8LsJ9j9F\npekmignN5gAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$T_{env} + \\left(- T_{env} + T_{init}\\right) e^{- r t}$$" - ], - "text/plain": [ - " -r⋅t\n", - "Tₑₙᵥ + (-Tₑₙᵥ + Tᵢₙᵢₜ)⋅ℯ " - ] - }, - "execution_count": 46, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "particular = general.subs(C1, value_of_C1)\n", - "show(particular)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "t_end, T_end = symbols('t_end T_end')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQ0AAAAYBAMAAAASULWnAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIu+7q82J3ZlmRDJU\ndhDTAJzgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADOElEQVRIDcVWTWgTURD+Nkm33fxsol7EQ1Px\nIP4cWqwHL7qH6kVCczBaKLQRfzCHShREqCK51YJCxEubogYtVsFiPHoQAqIiIgQsNaVGoogUtFgU\nRUVa523e/mSb5pVeMrCZv2/mzb6ZfS9Ao6i3UQtXr6suVuuN0vzJRq1srTsyOrVuqCdnGVYn+dKr\nw62EGnM6Mn+bs015m3UIkL53dv/o6o/brE5xg2kQgsNbupY69/8yA3TB43hzab4dCIZsoHuAHIcr\niZZ6r9xnRgjBJ4GtwLAZUBGihu7fRbQXngJw27AxngA8QDgFJWs3V+QP3BSImz4hWIP0G8iYARVh\n2qG7csCUGlISaeXKaBxK6QzwGLiVR6DsgJL6mptcIdMnAtMkKf+A92ZARfjo0MOkX5VxHSn/G7Th\nOT3IAt1leB1Iphp1TFi+rABMaTx0LhCsiqpmkjx36BmfxeGXZRyUouj1tetw6mgtMuo4VeUUgNmw\nLSM5RSZ1oGhtrA7pQBnJQAaX5HdMZx2tRUYde+xOEZgNG1DkIZwrUdInQlKcmznbOJpWNX8Cd2/o\ng806WouMOi7anSIwGzbgEQ8hzq4RdnZKOzGX4+bajHW0Fhl1bKc9PXKe6BzlEYHZsNlJv0Z8CxS4\nrXTT7lgu1+ooTsRiZ2OxHoamOiwSgdnxYSe2FWB1BDW7uZasd/RzqaAMPynCOwU6i3Qy9qOqLyKw\nPj+TGkbGSilMapJ+jXipmGCcp12RsY6qfaqmPEy7QnKw/JQjjTqq5lQE1ufn9CIyf7AZxPVPls2p\nKw4UVqyBOfaVqXuRGbS+RbDc+gIyRxt1HOe6zkRgNw2b9DUpzSexSLxyjbSkgEAUcyF7Jocs7146\nVIArS+YIO/QPYBNHGHVMWBFC8LH+nxHAXaArxNvGOGUEmrL0U5xJ06+Agjk6U3bgsg+D5u4ZdTQ7\nX6MeWF/n2xe4ci2pHHG6RoD1gtUttzuvatICBl/hgpTn5vucKykuGKweWMc8kxFG84MccbpGgAEj\nUsjVSAleDdNpjM8sA3c4LHXBDPvpKMYRSOh8lj6CqCPBGlXrf9DaEridjV1bGkjpNQbysGurCP8P\nLrr5vxec5+oAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$T_{env} + \\left(- T_{env} + T_{init}\\right) e^{- r t_{end}}$$" - ], - "text/plain": [ - " -r⋅t_end\n", - "Tₑₙᵥ + (-Tₑₙᵥ + Tᵢₙᵢₜ)⋅ℯ " - ] - }, - "execution_count": 48, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "at_end = particular.subs(t, t_end)\n", - "at_end" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\\frac{1}{t_{end}} \\log{\\left (\\frac{- T_{env} + T_{init}}{T_{end} - T_{env}} \\right )}\n" - ] - }, - { - "data": { - "image/png": "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\nFSYAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$\\frac{1}{t_{end}} \\log{\\left (\\frac{- T_{env} + T_{init}}{T_{end} - T_{env}} \\right )}$$" - ], - "text/plain": [ - " ⎛-Tₑₙᵥ + Tᵢₙᵢₜ⎞\n", - "log⎜─────────────⎟\n", - " ⎝ T_end - Tₑₙᵥ⎠\n", - "──────────────────\n", - " t_end " - ] - }, - "execution_count": 49, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solutions = solve(Eq(at_end, T_end), r)\n", - "value_of_r = solutions[0]\n", - "show(value_of_r)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 67, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "sympy.core.numbers.Float" - ] - }, - "execution_count": 67, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "subs = dict(t_end=30, T_end=70, T_init=90, T_env=22)\n", - "r_coffee2 = value_of_r.evalf(subs=subs)\n", - "type(r_coffee2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 69, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(0.011610223142273859, float)" - ] - }, - "execution_count": 69, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "r_coffee2 = float(r_coffee2)\n", - "r_coffee2, type(r_coffee2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 77, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def run_analysis(system):\n", - " T_init = system.init.temp\n", - " T_env = system.T_env\n", - " \n", - " ts = arange(system.t0, system.t_end+1)\n", - " Ts = T_env + (T_init - T_env) * np.exp(-system.r * ts)\n", - " series = TimeSeries(Ts, index=ts)\n", - " \n", - " system.results = TimeFrame(series, columns=['temp'])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 78, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAACMAAAAOBAMAAABXxbiCAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAMt2rmYlmIkR2uxDN\nVO+L8+I6AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAApUlEQVQYGT3MvQnCQBiH8YdY+PFaBBdIKVil\ncAAbsbGIRQTBGcwGrmBAaxFcIBskI2SDww0EtY7veXde8zse7v5M5nmeyfISo8dZdF2XjluZ2uRc\nQY8D7G1yZrDhDqbSFByUvODUagqOkLemUte9bOl/INGFoDz/15CGDaKvfh+90Q10w9h5b53CDIpK\nkzdZwBXWWoJGU9TKg7pxwlFHZHeOiVLnFwvUOo6ThEE/AAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$70.0$$" - ], - "text/plain": [ - "70.0" - ] - }, - "execution_count": 78, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "init = State(temp=90)\n", - "coffee2 = System(volume=300, init=init, T_env=22, \n", - " r=r_coffee2, t0=0, t_end=30)\n", - "run_analysis(coffee2)\n", - "final_temp(coffee2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 89, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAACMAAAAOBAMAAABXxbiCAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAMt2rmYlmIkR2uxDN\nVO+L8+I6AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAApUlEQVQYGT3MvQnCQBiH8YdY+PFaBBdIKVil\ncAAbsbGIRQTBGcwGrmBAaxFcIBskI2SDww0EtY7veXde8zse7v5M5nmeyfISo8dZdF2XjluZ2uRc\nQY8D7G1yZrDhDqbSFByUvODUagqOkLemUte9bOl/INGFoDz/15CGDaKvfh+90Q10w9h5b53CDIpK\nkzdZwBXWWoJGU9TKg7pxwlFHZHeOiVLnFwvUOo6ThEE/AAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$70.0$$" - ], - "text/plain": [ - "70.0" - ] - }, - "execution_count": 89, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "coffee = run(volume=300, T_init=90, r=r_coffee, t_end=30)\n", - "final_temp(coffee)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 90, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
temp
00
10
20
30
40
50
60
70
80
90
100
110
120
13-1.42109e-14
14-1.42109e-14
150
16-1.42109e-14
17-1.42109e-14
18-1.42109e-14
190
20-1.42109e-14
210
220
230
240
250
26-1.42109e-14
270
28-1.42109e-14
290
300
\n", - "
" - ], - "text/plain": [ - " temp\n", - "0 0\n", - "1 0\n", - "2 0\n", - "3 0\n", - "4 0\n", - "5 0\n", - "6 0\n", - "7 0\n", - "8 0\n", - "9 0\n", - "10 0\n", - "11 0\n", - "12 0\n", - "13 -1.42109e-14\n", - "14 -1.42109e-14\n", - "15 0\n", - "16 -1.42109e-14\n", - "17 -1.42109e-14\n", - "18 -1.42109e-14\n", - "19 0\n", - "20 -1.42109e-14\n", - "21 0\n", - "22 0\n", - "23 0\n", - "24 0\n", - "25 0\n", - "26 -1.42109e-14\n", - "27 0\n", - "28 -1.42109e-14\n", - "29 0\n", - "30 0" - ] - }, - "execution_count": 90, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "coffee.results - coffee2.results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.1" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/code/old_notebooks/chap11sympy.ipynb b/code/old_notebooks/chap11sympy.ipynb deleted file mode 100644 index 40c78dedd..000000000 --- a/code/old_notebooks/chap11sympy.ipynb +++ /dev/null @@ -1,246 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 11: Rotation\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from sympy import *\n", - "\n", - "init_printing()" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "T, a, alpha, I, m, g, r = symbols('T a alpha I m g r')" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAE4AAAAJBAMAAABu2Qf6AAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIquJdjLdEETvu2aZ\nVM0GsGrEAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAy0lEQVQYGWNgYFR2TWfAAWx8GhgYOIwqTBwY\nGDoFuD4AlTG/A4MDQCYciDWwT2S8wMz1lmsCA/tsBp4FcBlUxmQGhjUsBbFsChwBDDzfGdgcUKXZ\nn6UBQV4AUIrhHDNDQX0DUJ5zAUN9AKo6GA8oxWBpzsBwHyRQbwCkC4DuA5mRlqYAEoMCoBSD/QWg\nmSB+/QWGdbwCMCkUmn8DUB1QBOhMBgY+B+4kZhRpOIdHgYH7PGsB4w+QCLuKqZAHXAqVYbx7A48J\nA0cCUBQAnD4uhckWRCkAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$a = - \\alpha r$$" - ], - "text/plain": [ - "a = -α⋅r" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "eq1 = Eq(a, -r * alpha)\n", - "eq1" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAH4AAAASBAMAAABvO3eaAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIu+7q82J3ZlmRDJU\ndhDTAJzgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABn0lEQVQ4EaWRPUibQRjHf28S3yRvkmsKHS1p\nEewkCCqUQkmk4haSQUtBSm9ocRAxgx+TIIi4CM3WqfgOYse2kwgO0QZXA4pDQTiknTUtiLXx404h\nkHOR1/9wPPf87n/PPffg/Okt/B14JwkoVxIqESsHtBOBzBxxP6h/HZYrpFRQvy5cUHhB7cb37LZ5\n++AHq/2P92qru75NH42vKe/9w4O8N/7CMOfUPoHbrZOypybO/eQTQz8MG70xofeULG74JUc/2TKJ\neMOsLdpRNBKVKZwT4sctBA7LzLMRLZKRLBsWubg58XvGaMRsOkjUhTgjeUGk+4Y214+KWdSRjy7y\nymT1+C0l/pEqkTwmViJUs2AH4j/swCJ0GabHbyleJzxHrEhU8jWtNB29ftukjsSJvliwANN4daEz\nevyWdOUHFUIS/coFpxWLBuHiJp3o9mLFdu3MKsuOqFNIk/HN/7x2LfqWTznpnJmPbZMSt+9yyG6R\nlf1R+AJ7UB2z/L/2v+d9r0QqR3LwmwWb2+fNKECQMM3dQxMqnLuHnernpbvarwAXjHFA09aCYwAA\nAABJRU5ErkJggg==\n", - "text/latex": [ - "$$T - g m = a m$$" - ], - "text/plain": [ - "T - g⋅m = a⋅m" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "eq2 = Eq(T - m * g, m * a)\n", - "eq2" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAE0AAAAOBAMAAACY64xBAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIu+7q82J3ZlmRDJU\ndhDTAJzgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABGUlEQVQoFW2QvUsDQRDF34V47CXmrrQMNtqK\nGLDTQi29FAp2WQQ/Gj2LgKVaWHutlVvFwiK2dvsnpLQR7j9QDIL4Fd8cZMmKD+7N+80MyzEIXpfS\n4VpH4x810reLcTvUqJxA5WP2atJ2WAWa54iMa0yGvnb0ANxYNArXmAyZccSUFqg59kLHo3mh3mbv\nyesKDMUOH40UBO/iumUXpe5ti3YkovZNmzHxbGAZoi9a3XZxx+op/CTOAcvl71d/SHH84a2UMPUC\nKE4vQ0Gej5pm76+SBU757R7LhOejVFscODgVdcvcHwDPGsisIM9HVbS4r+weSHLule2VQkrTiPs6\nI6pVRFf1AmFrtMXnceuvkIL10QbL/nWujvALuqBADvdlz34AAAAASUVORK5CYII=\n", - "text/latex": [ - "$$T r = I \\alpha$$" - ], - "text/plain": [ - "T⋅r = I⋅α" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "eq3 = Eq(T * r, I * alpha)\n", - "eq3" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "soln = solve([eq1, eq2, eq3], [T, a, alpha])" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAEoAAAAsBAMAAAAwUzMFAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAdqvNmSJEibsy3VQQ\n72YW//XhAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAByUlEQVQ4Ec2UvS9DURjGn6L3q98G1oqhtjap\nzdAmYmFpJCaDxiIWik6WViImg91iM4mPhUSkXVjLxOovUOoWIa7zHveeS3OuaxLPcM573ueXc09y\n7nkAoUC6NSYW3kUs4+25zmLJrb2rStHbc52cW/5QPZN3nDhRlqcSA8rShhRV7llbrQYfVW0NjT6c\nSSn1hbVrdbxuGxnMlHAnpbqbrH0D3aw3ikQPSalYFdDfEdhBDRgHrqTUYhyImtDyGAV2oZhhGVbZ\nAkJNxA5wjXAbPZlpGbXPmmET6UKwjWgT3SXJVQQHrRTDZvtXoewgkEQoxfb21LqnIwydTuSrSl1L\n+kI4up3wh/4RYf1Gf3ve8nrd/4NqoSvjTxkF3SSq1xoueNORIvvdSC1vhpzQG43KE41M559T52jE\nqaM6f9Rhp/+5LvOJv0Oq5FQ0zyl6h1w2JWJCWb5YimMO8+TSO+SyKRETqjaiXeojp5vk0jvksikR\nE9tGUtmKWFaTXBFbNiViggohHltYyWb3stlh6oqYYIUjHlt8Ye/lxgTLC0c8tr5RIiauHQYw+OFo\nbe8lYsK+QLImcw8lmplsSsQEKyRyziWxvrQWvtTfyw/BApwYNLJRJwAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$\\frac{I g m}{I + m r^{2}}$$" - ], - "text/plain": [ - " I⋅g⋅m \n", - "────────\n", - " 2\n", - "I + m⋅r " - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "soln[T]" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAFwAAAAxBAMAAAClq9PvAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEM3dMlSJdrsime9m\nq0Tz+RmlAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACC0lEQVRIDdWVT2sTURTFTzqZMckkr8GdmyYE\n66KbhhbciJBN15mF0oWUDkWMFIoDIi2FlNSNUlHGhYgLSYhfoKt222/Quukf6GJAmZXQVLQ1Cxvf\nq3MhmclMXjelPZt7ePf3HvfNMGcACc2vZSUoQjQ7kycvURU73pbACNGd5Bl5qaoeS2EEKRY5qTov\nRRGUbpCTqi6+S3H/ofjow9VQXK28qLkHH6uWu+TA/eouQu90WqH4U9Qb5msrduqo6zBfGm9CSdHg\nj3j4gTGB5AnSrbhxf8DYeg3l97EzsS1Ti3ETrfIWnkNtIVWEbgkTrW0ba0jloZg4srPcRGsW+A3d\nRM5BM2lwE62cnWmj7mAbuKMJEy2296SIb0AVmHkszCAlGoOIrn5JTC0t7RhvpWH+Ug+XjQvg1wq9\nOSl0G+jI6Mpc7XJzuNoZs8OvHszhn+Gw6PhymJ14+K3+23w5rFFKTPfHfTk8RCnRH/fn8HDNO9XD\n2cKnxT1WeQe2MFKx4M/hI6sX1xKvkFvBI2iJ0cR6IIeflXrxKSWPuskjZErZYaVADo97NLxhsvwj\nn81iH8IE9ed8aa5QuFcojAnPE+0LsHluAjg7pSXvdDSBu2DtmDABab9oifAN8MBO5T9ggzpdVaHH\nTrPzf3W6hSHT7PfT/jz+1/Q2e6ezIm7sQN0tcRMlGiaK6er96PK99h9r4Kmx1f+aIQAAAABJRU5E\nrkJggg==\n", - "text/latex": [ - "$$- \\frac{g m r^{2}}{I + m r^{2}}$$" - ], - "text/plain": [ - " 2 \n", - "-g⋅m⋅r \n", - "────────\n", - " 2\n", - "I + m⋅r " - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "soln[a]" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAEoAAAAnBAMAAABalMPGAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAVIl2uyKZEO8yZt2r\nRM0C/HbBAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABkUlEQVQ4Ec1UP0/CQBx9rVX+lAOMixuNGCai\nGBMXNSExwUWTOriZwG5iWBwNRr9A+QT4DSBMjkScnIibLsLCWPAPISZivStXUpprcDK+4e7d7738\n7q65PmACKbmRJenjzSZJHoKkL5KZieQiq8gXZGUPpV0sQ1Y6yqVLdKg0RCxeVtvIa6iirDaI4Uiu\nOZhFRS+WWkgUcQ9GRKjcYBtIAKfAtU1ErqqOHHAG1EEGAUZEoF1egSsERlhoH1EiREmPDDA/QriP\nqKZRIgRprtRAaphrQLozKPGBUvARXGUDPpd3eSAPceBei7n0sBUXK/+5av0Gf3uBVK44e0NZj7Rn\nu1Q9NGCuHaur+7uDLf7Uvv09TKE/FwV5YyPF+XjyjmqGVWTn8S559fE6ZU/RPlfFrnDBlmPZaZcn\nKx7xxPQXe19KeK/prAh1FveZa91gIwV3TWdF0LLsE/XGnolLnBUftmvNNG9Ns8u4KCvIu6eXMCvk\nT69LlBWq87mc04uy4qT3pfFm/I4zsoK7nAP4zM8+deAHB06So4uK1AUAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$\\frac{g m r}{I + m r^{2}}$$" - ], - "text/plain": [ - " g⋅m⋅r \n", - "────────\n", - " 2\n", - "I + m⋅r " - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "soln[alpha]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.1" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/code/old_notebooks/chap15analysis.ipynb b/code/old_notebooks/chap15analysis.ipynb deleted file mode 100644 index bdadce511..000000000 --- a/code/old_notebooks/chap15analysis.ipynb +++ /dev/null @@ -1,464 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 14\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Mixing liquids" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can figure out the final temperature of a mixture by setting the total heat flow to zero and then solving for $T$." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from sympy import *\n", - "\n", - "init_printing() " - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAARIAAAAVBAMAAAB4a3wcAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEHa7q2Yiie9Umd3NRDIfxLosAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADtElEQVRIDc1WS4hTWRA96fR7L3+DLhRHNOpClz3QutABwxA3KpqVC/HzQERxk+gMI7gxILpRsWE2fkDfoIKMqK0LQVroCG4aWzs7BUEC4kYEbf89jsaquvfmffLprQW5t+qck+pKVb2kgZ/WXs5W2c5ZBLnyLAKr2UsQW15ab1c1aw+hcrTUGt70OSQPYIOdieYOl8p7jX437C/DW96XpkxKIYLYZiNt3wsP3mB/ZRnJq+aDZPI4BJwArrRl7ASxeoihYOAYcI5eyu7DqSIxipRJKXAQyxhp+57ALy4w7xYBN8kRWwEUYf8H7NCAuoIYScRirr7X5oHUJxVgoIpBoOLBamhEriBGkrBZNTh1YIry4KmhjoPmbM0ASw3CdwhboJlkQTmZabqzNRUg4YJWbdcIBjhv24JYrNaGlRMvIjmNFP1V4JXmsqPIAoOUO/SJQlhmRIlNJTc5znk6wzN565Y85wkYpfOxiQDB7pwisjOojLK/mA8yx+OTptxhPqY0gK7E/sraWFO/47DctGgd5mNK4wuWeMi+w5GCQnJDfMfLfNKUO0ywFwxbdUXqSpx3KnQu/cPOKj540aIm2OE1ecK3M+dMinnkVqrIfcZ4k1zq7mXpQ3qEA5pyhzH2aiPDSVG2e5L+rrR/YRM7d/ngRYsaY447WCf8fIRTlZwWNK/zJxoc0kTFFo2xPRZfMEmRe6tY3ZOEhDHcwRuXiFNM8qKxxR5ygntN9hlLuzl+xt5wHLAlVZ7OOCPJZqgSf6K+XDC/Eurtw5OTkx5ll0ouUAmVBsmlEn+p/ASMJRr2N0KilczxkJxR3yQpKoaElHaEjp5TVm2l50tM90R9k3gEXczTIdPpuWj8tALP6AWH2zU2ViQ3PQRrmrcFOGAqkY3tNWU94MjG2vy74HARt+mlNrbXotEfLZBGNpbF2gZqSNWR/Ehf1ZRH9cQpEhnXUzZCvhUm00l5itA9wUX6teDHRj3dfzK5jguLmMLkAf43QlEjFzSBhcMbGsSoSrjzf0z9/3tUqjGpJMNyMlNJ7FLpOsfb+ODOO7+1HhQk8A+NWR5Dj/gI2vxlgV9FVQk9Af1MKtmnFaYSHWar2Sa5aVfHXa8neE57WOvKGTCpaPXBDBi9zzBwUKPZcoh+ffbvPAGWR0cvy02c/ZV6VujFM25f+yB83KWgl+0fp7HF6t3p062WEKu704ImWq23wKI+Cp+i/5RmsbjbX7C7P01sv1oDb94T8Lu6W7uiPmiHh+YTxrNc44XuH/q3++lTknDqAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$C_{1} \\left(T - T_{1}\\right) + C_{2} \\left(T - T_{2}\\right) = 0$$" - ], - "text/plain": [ - "C₁⋅(T - T₁) + C₂⋅(T - T₂) = 0" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "C1, C2, T1, T2, T = symbols('C1 C2 T1 T2 T')\n", - "\n", - "eq = Eq(C1 * (T - T1) + C2 * (T - T2), 0)\n", - "eq" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIUAAAAyBAMAAACAOwXCAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMA74lUMhB2u6tmIpndzURTbmnuAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAC/ElEQVRIDe2Xy2tTQRTGv7xq87AUFEQQGgRxUcSK4kbBgBWRbrIQBd3EhfgXCAEVXUmKSvMXaHDrInEh4sbWhesIbgQJieLGXX3j83rOmTP3ziS3SbRd9kByvzlz7u/OnEzyEcwEH7GOSAbBNLbNn1gHApn549PYbghnqrX6fZHNHbWgeidmcZlLtcVckWtyH6pL32pd0UhbxsRO4Ai9KHYDu4Anor23C3WkHtY5lSwisYK86JCRuVoG8j/klhvI/QTuhrefU3X2OYnlBo8mgWYL2R7rkLH1Cw2mpiVVR/YTcJG1xGG9dukxeCODU8C9OUxwImIsz/GgxakpegwRe6wllJEnMLAgqR6wVOZKDu1H7hcPMiV+p6C9OqGMpiRP2wlqmYYykl91fFKutFfASBLK2F+ROcxe4S1wy1Qqo/DZzC/clivtFSpprIx2SeaSjckXJLhlKpWRWOX5DHBU6mivVpJQxoxMlQuNNH983DKVdh3COGRvlL0a3L5O51mn85ruajMjVUr0cn9IcMtUKsOcjJYyZK+6JCrXdcjJyDOHz4G0zEhl5PhkJ2kH8nDeq10SCWU0i6T30KtQoTdumUpl4Bgd40eUFMYWftDAOlLf6dtAj8Esz15nZaRlZB7XnnKSGXu7v2+q5JRdB85Xb/VomG3Rkq8FrypGhmeMpkyYRooOpe7FlrzFO1/addjsASuAUL6PcqTSLw9e1oTKPsaDNu9CwpE2JddEEKxqQmUfw6sed7DJ8Dul/Qj+O+gz2uxpbE/95D+OYnsaWa9Pi/zWy8cxHOv1ahH5rZePYbjWK7UxfjuK4VivKbW/QY7fjmI41usxXL8dweizXqq2v+srfGfotzwwMdiPPuulMmX4fmsBdB1k9FlvxPD9diijz3ojhu+3QxkF+cUNrXctvx3K8K2XS7Ufvt8OZfjW6zB8vx3K8K3XYfh+O5zhWq9U2nPq+K1DiPtsw+kx/NbUDp6PkDHab7V0bcYYfjuSEa5npKB1bMR/jw34D/QXAgUS4RvxulUAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$\\left [ \\frac{C_{1} T_{1} + C_{2} T_{2}}{C_{1} + C_{2}}\\right ]$$" - ], - "text/plain": [ - "⎡C₁⋅T₁ + C₂⋅T₂⎤\n", - "⎢─────────────⎥\n", - "⎣ C₁ + C₂ ⎦" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solve(eq, T)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Analysis" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use SymPy to solve the cooling differential equation." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPsAAAArBAMAAABMYuO6AAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMARLvvmVQQid3NIqt2MmaorGxOAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADi0lEQVRYCcVXz2sTQRT+Nmk26bZNAyIIFVoQD1rR6kXtQfYqguQfkEZEsLSQIIKVVslFrPWSg0p7EEPFk2CDF5EWjBe9RMjFq6TQk1CsohEPWt/M7MzOzm7qcR7szvv5fdmZnck+gIlzyOejrdtw0RYz5x0tWaWfs8qOhxbpvdcff1qk31dPde3Ru+fQV7NH39dFrmKPPj2GcsEefbmCKZSs8ZeLOJKpW6Pvb+YfZK2xw924/eJdhH6ow8ztiC/R+Jzo3dsZghsEa6fWd8ff/AJWOcBAe28cwBtTGapWeQxFJYTgBsFN4AxwEXgMuEvAfw+D/pLiULXKYygqQQOPEjTg/QA2MVgBMjvABwMhZmoJsjaW49aFSybo4Fo9QIuSp7P/PdJUwc7BKzEswzGpbFWrPFLJNLmmEnTwCIEDDNDZX8ABKmDnYH9RYiSPTlX5Va3ySCWgVwk6uEmQ5oA3gJET401kfYlhjM785YUmjLioNTJpFSlRSBzcJFjzWeZxuqboytMCEMkKF5/pgWRTj1ITQKojHWwUtbqH6yF9HFwQhCVf+XTfJcd5ujLVMBLRPuUaTpsWiGfLiKiVlhpD+ji4SfC2xMpO0nWPrqEdZmniLi+S3G+XxF9UugBMM8/iE0oStVq2UEP6ODgR7EphXLTtSYje/UZjjJ5H2Y0tDcDoQxG1oc00WrjlsysrPvfGwQ0Ctu1JaPKdHZTgVJmV5c+32GC6FLY0xuTzWnehha3tlo+DjWxFJKunTwAXBCKP7mzbk9Crl61l2sGrx13G7TS3Ux3NzWunSzPY/ItJVLqZMRFU9Ang+ZoGQK+y+OSbp1OnMUs3PxINDY8tDf1GfUZ47Xqr481W0fWuVmE+fQJ4lOD6098bDJZOBu9Yk46dArMSJJg0fe5ELXtpB5rOBAYqXlAsnz4JPJkgVxeUzxKYI66liEXGH7hIt/v8dq4wGMQkvUzVwQWB2WDmfZG7IEt6jVtm4DBmsIbc/nau/TyIOZ1okg4eEAwXca2uZR3lulvTXIlqqm64Z1pNjGDwDpyNjhFSZgguCUZLwU4OcsQXQQxcIUhF+9yQrv+PIbgkmBOHrCr1Okz9ouyeysuekd6BEFwSUIPJDlkrwhpM59V3vuUs/ADeYLJvKysiGkxrPaZoMK31mGneYFrrMUWDOUqnpRUp8wbzFi5ZYYdoMC+4TTv0osFcbdlh/wdTXw3MbiYF2AAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$\\frac{d}{d t} T{\\left (t \\right )} = - r \\left(- T_{env} + T{\\left (t \\right )}\\right)$$" - ], - "text/plain": [ - "d \n", - "──(T(t)) = -r⋅(-Tₑₙᵥ + T(t))\n", - "dt " - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "T_init, T_env, r, t = symbols('T_init T_env r t')\n", - "T = Function('T')\n", - "\n", - "eqn = Eq(diff(T(t), t), -r * (T(t) - T_env))\n", - "eqn" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the general solution:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMAAAAAYBAMAAABen+92AAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIu+7q82J3ZlmRDJUdhDTAJzgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAC50lEQVRIDa1VTWgTQRT+Nk02TZpuIoKoh7aepOhBseJNAxY9SGgPBoqK7qXiRageFOrBgIpVUPciVA8atFA8NeBFD2LQQ4Ui7aHUFgmsB7WXYrWg4v97M7OT2TZthfSD2fe97715b2cmswHqwtG6Zq8+2fmzek5dGam+uqavPHnwzuS6qz2llZNC0afsJf57xt0f8WKsHKpQ07G+dHQtdJ5wYe0Q8UM1suY6Or0Ni3RrjvLTmbCqixmy7SLSh0YPMR/4mEHMiCna3A68oqGQ2kXYi+gE8CDQlNXFDD0KtBaQKGKKxHGg2TWCgjp7fKDx82I5UgImHbWEdzKqixnJtPP3y2j2sYXEK4DTZgQFjfFvMblEbiX1hi0ygNfS6mJKZlMEunwkkewj5xyNYzRCuFUmt6kQ0sh5SGPorVJVg6IqptTAbGViF5Dcv5ADTgWystY3Jg5tCJvT02pThKsfqgH5opjWBbG+s2nwgNQ8kdvs2XmBAtPfLBAe0xjOWC7zxdANZLFwOPGL/XiZTrKbyCh7BuI/pfPkIGBtx2zJiGmqG8hiWhckKj4okSI1cUl4LsTqI8LLgiM6R9sr96oRg+kGspgRIUrXgJ9F4BONJQ3iosGYaJDOUsIS9ObzZ/P5HqHLYuEUugYE3qIRftFh9uzzAlmi8gYUZAOXhFrQKxDFPlQmEteeTSM5iZOUTdeAwIfcC3pRccisKFhfidi+aBBxgQmlh4xuwMWc4042MeJFMnbaf0lp+3zOtbPAZYdmD7BnYtxD6iYJo3TNuzGbMWMB1w24WDQ3g5Y3SPsto7Bh7/57mF+KL9r6CpEjwaTAOpc6rzOnBpie8QI5ZFUDWYyPEzn+UB3A5mraGUmttqoUZtxgOegVcEK6BB/bMNCEfnNDB+Vkm5dTEy9qqlJ8ZMYayk7Wmkf/GC5Y5WqgISP4+6oSZhsv5sLCsp6TqyCZxZSHoRkjSf3h7DSkNaabuF5CrmPtSv8DUyuwq8EudNcAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$T{\\left (t \\right )} = C_{1} e^{- r t} + T_{env}$$" - ], - "text/plain": [ - " -r⋅t \n", - "T(t) = C₁⋅ℯ + Tₑₙᵥ" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solution_eq = dsolve(eqn)\n", - "solution_eq" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAHwAAAAWBAMAAADwX+WxAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEHa7q2Yiie9Umd3NRDIfxLosAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAB+0lEQVQ4EYVTP0jcUBj/5bwkl6O5e1Co0MGGs0dL61CoY8EjWJUuBsHZu0kRyl2njnXroNgDtwpFihZR8M/i5CmCS+mBNzi4GQSXTorWP13a770k73JpL35Dvt/3+5P3wnsBYuqeGyPeLT1id3tiHPMxWrykLo0MTG6vxZvaq+pxo4bv7fWmonT3v9YqzVmgUje1q1ZSu+kdvug/iFifOjCWHemce0nFYAPmlCQF0CtIbSDdtHL2/iY91oschmsWSBQeeIxSFD0JlBegNjzSfx4wAkctFA3aGZC2Kh5tWKKfAmNVJLhfVvqWw59y9oH5gjZvu97kxxvAMIPZYi1v8LHL4zpz31pEf/DjNE1H5Q+Wx2T4clsYj+p8lnHtd1TedQWTWaRdTFSV/14UGVfFl4Zf8UkMDAbFh0byLKwFWMaT5wEV9F0ODFfELwMy3PV6/cdMvb7AOTr2SIkTT9MbSLmOaMEoV6djBzpzlrp08gzmY7xHuULMOy/+ka5KEAl3Gadjh7KnFNRjJ1XUs2wOxi/gM/PiPVDiD66PjEk7j9JzZFlpHjrwsPdNg5bim098HQ0vKrG/uv7qz76FFHfb/J4O4m3TQvF2JTfPDVkXDLNYyaAGSyaMKQn/AaYTojqqSoH+iNoX7GjVgNdWL60Ax3fFzsEs4ImDwzz+AioJZBj7Bd1mAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$C_{1} e^{- r t} + T_{env}$$" - ], - "text/plain": [ - " -r⋅t \n", - "C₁⋅ℯ + Tₑₙᵥ" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "general = solution_eq.rhs\n", - "general" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use the initial condition to solve for $C_1$. First we evaluate the general solution at $t=0$" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAFYAAAARBAMAAAC1JUYQAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEHa7q2Yiie9Umd3NRDIfxLosAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABZUlEQVQoFW2RPUvDUBSG32g+C2kvOAgOEqpF0cWho2AoFcGl+Qe2P0BSJ0fdFS24KUgHBamD9QfUIrgZaAcHN4vg4qSooC56bnJ7U2PPkPve5zwcLicAlKnisl5FovTPfOmt2EnwWQ/WmZdQYVRhNpH6y8cuSbsoS1eJogr4dWhdyXnoMPrcx8hywvwErNUwwpuyUl88Pss7hEsDSwx2zCn5TX6djJlwCezEMEpbTnSmF0RHuvq3IPJo98KYPgnnU5auFr5OihT2wwuD9c9VXwc9ntv8Y/WEawTB7W4Q1Dmk9SYq3GyK9H5HvoHWC4xnHe30cQ72NDbhV4lsDHNpvVCuFVd78MyykWEHsD6AQzbMXSKqFnKozCPDKkcwgIn8apdGJ99gLP7cODB5q8B/6wrWKUaVdCOa6YFhD400WnCESXO3RbQ9yYDRmuLqL2gd40qv9bl+/u7088CpFLKwXcx4uMvhF3itS7lwyWUgAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$C_{1} + T_{env}$$" - ], - "text/plain": [ - "C₁ + Tₑₙᵥ" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "at0 = general.subs(t, 0)\n", - "at0" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we set $T(0) = T_{init}$ and solve for $C_1$" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAHkAAAARBAMAAAALcx5NAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEM3dMiLvu6uJmWZEVHYiGvycAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABg0lEQVQ4EX2TMUvDQBiG31jTNprWUhyyqBCdXCz4B7K49w9IRREXB0cXoTh1qnUQEUECjoJ0cJJCO4mLpfgLIjiIQyDioiJ6d99drmlJb7j3+773eXMkRwC5anuNv8PjL9WO60TgAtgBbsZTajIRqMP8BjqK1WpUqU4FmD3dxuwv8KJTqioEokoHmG0D+QjoCTKxyXQ6QHS2kkipRqZZmwIQWPNVIKE6LYB3aSpVbHegqoTqtAA2pMl0BSiv88UKnJQSKdXo9AhgsO+kF7vusWW57vK26/rCGAEKw9+JXzeMqxD3d6GPft1q0bPiswXQryvfvF71iOA7v26USw46P9hFKyockBenBXAakc90akCA2HP8NRph21ysIDIXKhg9mwNsHvtzVZ0+X/toAvtskA/sLeRbZo9MdTYBuYB8pg/kD+2fMJD1Znwv0yvKsUpTuzRPPtM39QfE+SM4qCHz5GW8TTm027HLimeLfKa31rDBaycM8IjiJexmIhRzzhn5XF/xD1YId0zuAa6QAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$- T_{env} + T_{init}$$" - ], - "text/plain": [ - "-Tₑₙᵥ + Tᵢₙᵢₜ" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solutions = solve(Eq(at0, T_init), C1)\n", - "value_of_C1 = solutions[0]\n", - "value_of_C1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then we plug the result into the general solution to get the particular solution:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPUAAAAYBAMAAADQRaYKAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIu+7q82J3ZlmRDJUdhDTAJzgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADN0lEQVRIDcVWTWgTURD+Nkk3zTbZjUdFmhYR8edQsd5Ec6heJNSDQVGxK4LooRJPQr3kZnsQchFqBQ1a6EVxPXoQAyI9eNlDqSklsodUCloURfGnpM57u2+zf4HmlA+y82a+b97b7swbCvQKF3t1MKC2end2utSbs2cfLO24e97YzuGvtiPqrEmFDpn7m6z21QDp++j4j7EJvXOuNOJyuT1jW6Mnf7l+aBEpOB2QSRu0o5YFZB2xEvorAd7j9lmucw3YB8y4fmgRKehzZekjhONImMATCiaAXBmpqsuLhUovxrEsIkAe0m9grh0QKyGOFGR0IXNszACWKIWK+biGjBWggTS9HMewSw1UkNoEProBd+GIowXqkKuzFzky92SgCoxbUAIsueJspeRypErQtaSUIBxxB8GlgPwp+fOrPEgljIA4Wy57SeqNKAgxWPOEcJ1F1Ml6NsSwEkZAbBeveEnqjSgIMW8eoO5oHHufuQtZSXfCbcNKGAGxXbLmJak3oiDEvHmAl46GLBvbi/STDmHdCKWyEkZAbBerAmu3Gdg21BtREOKQgI/tN5SS2N94FM6MqpBcLJ7bWyyWSc3ObiOqNzxifv/baupYVn52tpb3hsWal/BTw0zNvK5DWQLNBwbxp/i+Oe8NdbKB2YeNMp7naTJxCDG//xQXvMTH9gJpNEdp68WTlVC9rOZTLyqxrKxZb21CbOfrNd4ba1YTc38wDL2VHvGL+f3HzZbNk2VjG6zXYjpgkvXjhEXVKKxg8AM0a3ARdOkZxNlynrv2I856Y6xRkTZKaElfStDtuBCDCSju8lqWBNP0y5zBOlt7IR/dOmvaRS2wIXsKu2xabOeZLbg68bMAHCBBwlSGkNClql9sC+KmzZOlHYEL7FFfqTAThmbAwkFMD2BKfBlxNm4F5f+gImb0l41kNeNwrpj7Xz/bPFk2tqUhHu30iNfUvPQNU+9xR6rZIkW85mww6RiayCH5zEgaux3OFXP/nWzzZNnYls3gDj5fLTSg5LFcwfyKjyAnHixTs2FiHpkbUAri/fw5zSs2z+wqTQc/243n+d+hm7S29nB72fVqZ9cZvoRU8LsJ9j9FpekmignN5gAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$T_{env} + \\left(- T_{env} + T_{init}\\right) e^{- r t}$$" - ], - "text/plain": [ - " -r⋅t\n", - "Tₑₙᵥ + (-Tₑₙᵥ + Tᵢₙᵢₜ)⋅ℯ " - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "particular = general.subs(C1, value_of_C1)\n", - "particular" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We use a similar process to estimate $r$ based on the observation $T(t_{end}) = T_{end}$" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "t_end, T_end = symbols('t_end T_end')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the particular solution evaluated at $t_{end}$" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQ0AAAAYBAMAAAASULWnAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIu+7q82J3ZlmRDJUdhDTAJzgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADOElEQVRIDcVWTWgTURD+Nkm33fxsol7EQ1PxIP4cWqwHL7qH6kVCczBaKLQRfzCHShREqCK51YJCxEubogYtVsFiPHoQAqIiIgQsNaVGoogUtFgURUVa523e/mSb5pVeMrCZv2/mzb6ZfS9Ao6i3UQtXr6suVuuN0vzJRq1srTsyOrVuqCdnGVYn+dKrw62EGnM6Mn+bs015m3UIkL53dv/o6o/brE5xg2kQgsNbupY69/8yA3TB43hzab4dCIZsoHuAHIcriZZ6r9xnRgjBJ4GtwLAZUBGihu7fRbQXngJw27AxngA8QDgFJWs3V+QP3BSImz4hWIP0G8iYARVh2qG7csCUGlISaeXKaBxK6QzwGLiVR6DsgJL6mptcIdMnAtMkKf+A92ZARfjo0MOkX5VxHSn/G7ThOT3IAt1leB1Iphp1TFi+rABMaTx0LhCsiqpmkjx36BmfxeGXZRyUouj1tetw6mgtMuo4VeUUgNmwLSM5RSZ1oGhtrA7pQBnJQAaX5HdMZx2tRUYde+xOEZgNG1DkIZwrUdInQlKcmznbOJpWNX8Cd2/og806WouMOi7anSIwGzbgEQ8hzq4RdnZKOzGX4+bajHW0Fhl1bKc9PXKe6BzlEYHZsNlJv0Z8CxS4rXTT7lgu1+ooTsRiZ2OxHoamOiwSgdnxYSe2FWB1BDW7uZasd/RzqaAMPynCOwU6i3Qy9qOqLyKwPj+TGkbGSilMapJ+jXipmGCcp12RsY6qfaqmPEy7QnKw/JQjjTqq5lQE1ufn9CIyf7AZxPVPls2pKw4UVqyBOfaVqXuRGbS+RbDc+gIyRxt1HOe6zkRgNw2b9DUpzSexSLxyjbSkgEAUcyF7Jocs7146VIArS+YIO/QPYBNHGHVMWBFC8LH+nxHAXaArxNvGOGUEmrL0U5xJ06+Agjk6U3bgsg+D5u4ZdTQ7X6MeWF/n2xe4ci2pHHG6RoD1gtUttzuvatICBl/hgpTn5vucKykuGKweWMc8kxFG84MccbpGgAEjUsjVSAleDdNpjM8sA3c4LHXBDPvpKMYRSOh8lj6CqCPBGlXrf9DaEridjV1bGkjpNQbysGurCP8PLrr5vxec5+oAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$T_{env} + \\left(- T_{env} + T_{init}\\right) e^{- r t_{end}}$$" - ], - "text/plain": [ - " -r⋅t_end\n", - "Tₑₙᵥ + (-Tₑₙᵥ + Tᵢₙᵢₜ)⋅ℯ " - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "at_end = particular.subs(t, t_end)\n", - "at_end" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we set $T(t_{end}) = T_{end}$ and solve for $r$" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/latex": [ - "$$\\frac{1}{t_{end}} \\log{\\left (\\frac{- T_{env} + T_{init}}{T_{end} - T_{env}} \\right )}$$" - ], - "text/plain": [ - " ⎛-Tₑₙᵥ + Tᵢₙᵢₜ⎞\n", - "log⎜─────────────⎟\n", - " ⎝ T_end - Tₑₙᵥ⎠\n", - "──────────────────\n", - " t_end " - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solutions = solve(Eq(at_end, T_end), r)\n", - "value_of_r = solutions[0]\n", - "value_of_r" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use `evalf` to plug in numbers for the symbols. The result is a SymPy float, which we have to convert to a Python float." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "sympy.core.numbers.Float" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "subs = dict(t_end=30, T_end=70, T_init=90, T_env=22)\n", - "r_coffee2 = value_of_r.evalf(subs=subs)\n", - "type(r_coffee2)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMoAAAAPBAMAAABXbk2cAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEJmJdjLNVN0iZu+7q0QgoRR7AAAACXBIWXMAAA7EAAAOxAGVKw4bAAADP0lEQVQ4Eb2U3WtcVRTFf3fuzJ3c+chc9KWgMENDBbExQyaKaMWxBKGU0sH8AUlFKPigA+KLLxMUFMHgUEEQBa8oghVJfKhSP+pVEAU/MhRUUEoHEV98SEdjWiPpuM45MzfjP+B9WHufvc7Z65x99zlww8IhzOdsEMm14C/cIfcUeDNHW3Dm8BcjeLHxpZkvBnK1CSa3NtNoEDaOtPEW5/sE9+duN3lv1cTTHOiaBdaGm1Kx4D3KLfDqQJpt7zm8Hvf1LYQ1OrFjYCqZYArD4XCPM+R3ycCPFIfDJt6HvNKlsIG/As56n1+OsEBQ5xtunpXKO3An5YjihoXsVSqrjoHjyQST0W57nGxzjSfhJYJ7j0F5g3JCqUdeqcb2pM6CgXUtgayob6ETFVcp/2Vh+gLVFccQvpWwz/jgt/i97+1wBA5QNhkqK/gDKj2mr5qRs6nK12aKVdmDrXYwkIoFRU3FjD5+kPAf5lcFTcW2PuNup1JNyF6nWmN6W9TIpio7S0f7Npf3h1SamjFl8lp4SI5VeTtI5E4wdhjUKA/fiCmfVdsUdZZdXquT+1MzR3as4u3EfGBzhWKX65qxNYLcXTWNjIrXDBK5KYMfa7T0lGBuJ6IUhdfxrxBsj7OPbaoyjLgtMrlSlee1GAv3tJyKT5Ao5oKGqZoR/jkKhx67YNyv4GEe36VaH1XM2bGK+oT1vlHxdBZTsUyiVRYovudUniBIg5Z5QFP0fRS9S+GfSN6cmnhxSV1ZI2//vrOpyvtSiW1d9F86bThh1gvCLiUtMPp1gsQFR7Q5z8uw2dY2lts/oDumSHlg7lXB/lZnUxX1mDuLqcd6RL6m9QYqA0q7ViV/6dLlT5r7DJ7aEZV68/Uryh1ro3PmQEGP7AaZFbkjm6p09F9sLo5jXoEH4ZiFYsLU344BDfYZcjo2H8ML3fO6gN0aPEPhPMtdeJabWrpH1roLaW9lqe65HmOq7Z0jfLoxu2qhENNpjlUqyQRD3qicJb/NbxFv6q2RRPaU94iiNy5+D2sjO/vpWowF5mdbZH6+9hPewokWgd6oVQv8cvA7tYFh8Df34gkm1L6YPni4T+6iXstw5qIKNq/h//P9CyM1PThWyTs8AAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$0.011610223142273859$$" - ], - "text/plain": [ - "0.011610223142273859" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "r_coffee2 = float(r_coffee2)\n", - "r_coffee2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.4" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/code/orbit_starter.ipynb b/code/orbit_starter.ipynb deleted file mode 100644 index b5ce4a647..000000000 --- a/code/orbit_starter.ipynb +++ /dev/null @@ -1,606 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Starter code for the orbit example\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Earth falling into the sun\n", - "\n", - "Here's a question from the web site [Ask an Astronomer](http://curious.astro.cornell.edu/about-us/39-our-solar-system/the-earth/other-catastrophes/57-how-long-would-it-take-the-earth-to-fall-into-the-sun-intermediate):\n", - "\n", - "\"If the Earth suddenly stopped orbiting the Sun, I know eventually it would be pulled in by the Sun's gravity and hit it. How long would it take the Earth to hit the Sun? I imagine it would go slowly at first and then pick up speed.\"\n", - "\n", - "Here's a solution." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "meter" - ], - "text/latex": [ - "$meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Here are the units we'll need\n", - "\n", - "s = UNITS.second\n", - "N = UNITS.newton\n", - "kg = UNITS.kilogram\n", - "m = UNITS.meter" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
r147000000000.0 meter
v0.0 meter / second
\n", - "
" - ], - "text/plain": [ - "r 147000000000.0 meter\n", - "v 0.0 meter / second\n", - "dtype: object" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# And an inition condition (with everything in SI units)\n", - "\n", - "r_0 = 147e9 * m\n", - "\n", - "init = State(r = r_0,\n", - " v = 0 * m / s)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
initr 147000000000.0 meter\n", - "v 0.0 meter / s...
G6.674e-11 meter ** 2 * newton / kilogram ** 2
m11.989e+30 kilogram
r_final701879000.0 meter
m25.972e+24 kilogram
t_00 second
t_end10000000.0 second
\n", - "
" - ], - "text/plain": [ - "init r 147000000000.0 meter\n", - "v 0.0 meter / s...\n", - "G 6.674e-11 meter ** 2 * newton / kilogram ** 2\n", - "m1 1.989e+30 kilogram\n", - "r_final 701879000.0 meter\n", - "m2 5.972e+24 kilogram\n", - "t_0 0 second\n", - "t_end 10000000.0 second\n", - "dtype: object" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Making a system object\n", - "\n", - "r_earth = 6.371e6 * m\n", - "r_sun = 695.508e6 * m\n", - "\n", - "system = System(init=init,\n", - " G=6.674e-11 * N / kg**2 * m**2,\n", - " m1=1.989e30 * kg,\n", - " r_final=r_sun + r_earth,\n", - " m2=5.972e24 * kg,\n", - " t_0=0 * s,\n", - " t_end=1e7 * s)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "# Here's a function that computes the force of gravity\n", - "\n", - "def universal_gravitation(state, system):\n", - " \"\"\"Computes gravitational force.\n", - " \n", - " state: State object with distance r\n", - " system: System object with m1, m2, and G\n", - " \"\"\"\n", - " r, v = state\n", - " unpack(system)\n", - " \n", - " force = G * m1 * m2 / r**2\n", - " return force" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "3.6686485997501037e+22 newton" - ], - "text/latex": [ - "$3.6686485997501037e+22 newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "universal_gravitation(init, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# The slope function\n", - "\n", - "def slope_func(state, t, system):\n", - " \"\"\"Compute derivatives of the state.\n", - " \n", - " state: position, velocity\n", - " t: time\n", - " system: System object containing `g`\n", - " \n", - " returns: derivatives of y and v\n", - " \"\"\"\n", - " y, v = state\n", - " unpack(system) \n", - "\n", - " force = universal_gravitation(state, system)\n", - " dydt = v\n", - " dvdt = -force / m2\n", - " \n", - " return dydt, dvdt" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " )" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Always test the slope function!\n", - "\n", - "slope_func(init, 0, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "# Here's an event function that stops the simulation\n", - "# before the collision\n", - "\n", - "def event_func(state, t, system):\n", - " r, v = state\n", - " return r - system.r_final" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "146298121000.0 meter" - ], - "text/latex": [ - "$146298121000.0 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Always test the event function!\n", - "\n", - "event_func(init, 0, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[[5432658.401694091]]
nfev236
njev0
nlu0
status1
messageA termination event occurred.
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events [[5432658.401694091]]\n", - "nfev 236\n", - "njev 0\n", - "nlu 0\n", - "status 1\n", - "message A termination event occurred.\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Finally we can run the simulation\n", - "\n", - "results, details = run_ode_solver(system, slope_func, events=event_func)\n", - "details" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "5432658.401694091 second" - ], - "text/latex": [ - "$5432658.401694091 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Here's how long it takes...\n", - "\n", - "t_final = get_last_label(results) * s" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "62.87799076034826 day" - ], - "text/latex": [ - "$62.87799076034826 day$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# ... expressed in units we understand\n", - "\n", - "t_final.to(UNITS.day)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "# Before plotting, we run the simulation again with `t_eval`\n", - "\n", - "ts = linspace(t_0, t_final, 201)\n", - "results, details = run_ode_solver(system, slope_func, events=event_func, t_eval=ts)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "# Scaling the time steps to days\n", - "\n", - "results.index /= 60 * 60 * 24" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "# Scaling the distance to million km\n", - "\n", - "r = results.r / 1e9;" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# And plotting\n", - "\n", - "plot(r, label='r')\n", - "\n", - "decorate(xlabel='Time (day)',\n", - " ylabel='Distance from sun (million km)')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/pendulum.ipynb b/code/pendulum.ipynb deleted file mode 100644 index 5cc315d61..000000000 --- a/code/pendulum.ipynb +++ /dev/null @@ -1,4548 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 10 Example: Springy Pendulum\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# If you want the figures to appear in the notebook, \n", - "# and you want to interact with them, use\n", - "# %matplotlib notebook\n", - "\n", - "# If you want the figures to appear in the notebook, \n", - "# and you don't want to interact with them, use\n", - "# %matplotlib inline\n", - "\n", - "# If you want the figures to appear in separate windows, use\n", - "# %matplotlib qt5\n", - "\n", - "# to switch from one to another, you have to select Kernel->Restart\n", - "\n", - "%matplotlib notebook\n", - "\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Pendulum" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This notebook solves the Spider-Man problem from spiderman.ipynb, demonstrating a different development process for physical simulations.\n", - "\n", - "In pendulum_sympy, we derive the equations of motion for a springy pendulum without drag, yielding:\n", - "\n", - "$ \\ddot{x} = \\frac{k length_{0} x}{m \\sqrt{x^{2} + y^{2}}} - \\frac{k x}{m} $\n", - "\n", - "$ \\ddot{y} = - g + \\frac{k length_{0} y}{m \\sqrt{x^{2} + y^{2}}} - \\frac{k y}{m} $\n", - "\n", - "We'll use the same conditions we saw in `spiderman.ipynb`" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "condition = Condition(g = 9.8,\n", - " m = 75,\n", - " area = 1,\n", - " rho = 1.2,\n", - " v_term = 60,\n", - " duration = 30,\n", - " length0 = 100,\n", - " angle = (270 - 45),\n", - " k = 20)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "Now here's a version of `make_system` that takes a `Condition` object as a parameter.\n", - "\n", - "`make_system` uses the given value of `v_term` to compute the drag coefficient `C_d`." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(condition):\n", - " \"\"\"Makes a System object for the given conditions.\n", - " \n", - " condition: Condition with height, g, m, diameter, \n", - " rho, v_term, and duration\n", - " \n", - " returns: System with init, g, m, rho, C_d, area, and ts\n", - " \"\"\"\n", - " unpack(condition)\n", - " \n", - " theta = np.deg2rad(angle)\n", - " x, y = pol2cart(theta, length0)\n", - " P = Vector(x, y)\n", - " V = Vector(0, 0)\n", - " \n", - " init = State(x=P.x, y=P.y, vx=V.x, vy=V.y)\n", - " C_d = 2 * m * g / (rho * area * v_term**2)\n", - " ts = linspace(0, duration, 501)\n", - " \n", - " \n", - " return System(init=init, g=g, m=m, rho=rho,\n", - " C_d=C_d, area=area, length0=length0,\n", - " k=k, ts=ts)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "Let's make a `System`" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
initx -70.71067811865477 dimensionless\n", - "y -...
g9.8
m75
rho1.2
C_d0.340278
area1
length0100
k20
ts[0.0, 0.06, 0.12, 0.18, 0.24, 0.3, 0.36, 0.42,...
\n", - "
" - ], - "text/plain": [ - "init x -70.71067811865477 dimensionless\n", - "y -...\n", - "g 9.8\n", - "m 75\n", - "rho 1.2\n", - "C_d 0.340278\n", - "area 1\n", - "length0 100\n", - "k 20\n", - "ts [0.0, 0.06, 0.12, 0.18, 0.24, 0.3, 0.36, 0.42,...\n", - "dtype: object" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(condition)\n", - "system" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
x-70.71067811865477 dimensionless
y-70.71067811865474 dimensionless
vx0 dimensionless
vy0 dimensionless
\n", - "
" - ], - "text/plain": [ - "x -70.71067811865477 dimensionless\n", - "y -70.71067811865474 dimensionless\n", - "vx 0 dimensionless\n", - "vy 0 dimensionless\n", - "dtype: object" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.init" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "To write the slope function, we can get the expressions for `ax` and `ay` directly from SymPy and plug them in." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes derivatives of the state variables.\n", - " \n", - " state: State (x, y, x velocity, y velocity)\n", - " t: time\n", - " system: System object with length0, m, k\n", - " \n", - " returns: sequence (vx, vy, ax, ay)\n", - " \"\"\"\n", - " x, y, vx, vy = state\n", - " unpack(system)\n", - "\n", - " ax = k*length0*x/(m*sqrt(x**2 + y**2)) - k*x/m\n", - " ay = -g + k*length0*y/(m*sqrt(x**2 + y**2)) - k*y/m\n", - "\n", - " return vx, vy, ax, ay" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "As always, let's test the slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " ,\n", - " ,\n", - " )" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "And then run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 84 ms, sys: 0 ns, total: 84 ms\n", - "Wall time: 83.6 ms\n" - ] - } - ], - "source": [ - "%time run_odeint(system, slope_func)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Visualizing the results\n", - "\n", - "We can extract the x and y components as `Series` objects." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "xs = system.results.x\n", - "ys = system.results.y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The simplest way to visualize the results is to plot x and y as functions of time." - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "application/javascript": [ - "/* Put everything inside the global mpl namespace */\n", - "window.mpl = {};\n", - "\n", - "\n", - "mpl.get_websocket_type = function() {\n", - " if (typeof(WebSocket) !== 'undefined') {\n", - " return WebSocket;\n", - " } else if (typeof(MozWebSocket) !== 'undefined') {\n", - " return MozWebSocket;\n", - " } else {\n", - " alert('Your browser does not have WebSocket support.' +\n", - " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", - " 'Firefox 4 and 5 are also supported but you ' +\n", - " 'have to enable WebSockets in about:config.');\n", - " };\n", - "}\n", - "\n", - "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", - " this.id = figure_id;\n", - "\n", - " this.ws = websocket;\n", - "\n", - " this.supports_binary = (this.ws.binaryType != undefined);\n", - "\n", - " if (!this.supports_binary) {\n", - " var warnings = document.getElementById(\"mpl-warnings\");\n", - " if (warnings) {\n", - " warnings.style.display = 'block';\n", - " warnings.textContent = (\n", - " \"This browser does not support binary websocket messages. \" +\n", - " \"Performance may be slow.\");\n", - " }\n", - " }\n", - "\n", - " this.imageObj = new Image();\n", - "\n", - " this.context = undefined;\n", - " this.message = undefined;\n", - " this.canvas = undefined;\n", - " this.rubberband_canvas = undefined;\n", - " this.rubberband_context = undefined;\n", - " this.format_dropdown = undefined;\n", - "\n", - " this.image_mode = 'full';\n", - "\n", - " this.root = $('
');\n", - " this._root_extra_style(this.root)\n", - " this.root.attr('style', 'display: inline-block');\n", - "\n", - " $(parent_element).append(this.root);\n", - "\n", - " this._init_header(this);\n", - " this._init_canvas(this);\n", - " this._init_toolbar(this);\n", - "\n", - " var fig = this;\n", - "\n", - " this.waiting = false;\n", - "\n", - " this.ws.onopen = function () {\n", - " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", - " fig.send_message(\"send_image_mode\", {});\n", - " if (mpl.ratio != 1) {\n", - " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", - " }\n", - " fig.send_message(\"refresh\", {});\n", - " }\n", - "\n", - " this.imageObj.onload = function() {\n", - " if (fig.image_mode == 'full') {\n", - " // Full images could contain transparency (where diff images\n", - " // almost always do), so we need to clear the canvas so that\n", - " // there is no ghosting.\n", - " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", - " }\n", - " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " };\n", - "\n", - " this.imageObj.onunload = function() {\n", - " this.ws.close();\n", - " }\n", - "\n", - " this.ws.onmessage = this._make_on_message_function(this);\n", - "\n", - " this.ondownload = ondownload;\n", - "}\n", - "\n", - "mpl.figure.prototype._init_header = function() {\n", - " var titlebar = $(\n", - " '
');\n", - " var titletext = $(\n", - " '
');\n", - " titlebar.append(titletext)\n", - " this.root.append(titlebar);\n", - " this.header = titletext[0];\n", - "}\n", - "\n", - "\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "\n", - "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "mpl.figure.prototype._init_canvas = function() {\n", - " var fig = this;\n", - "\n", - " var canvas_div = $('
');\n", - "\n", - " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", - "\n", - " function canvas_keyboard_event(event) {\n", - " return fig.key_event(event, event['data']);\n", - " }\n", - "\n", - " canvas_div.keydown('key_press', canvas_keyboard_event);\n", - " canvas_div.keyup('key_release', canvas_keyboard_event);\n", - " this.canvas_div = canvas_div\n", - " this._canvas_extra_style(canvas_div)\n", - " this.root.append(canvas_div);\n", - "\n", - " var canvas = $('');\n", - " canvas.addClass('mpl-canvas');\n", - " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", - "\n", - " this.canvas = canvas[0];\n", - " this.context = canvas[0].getContext(\"2d\");\n", - "\n", - " var backingStore = this.context.backingStorePixelRatio ||\n", - "\tthis.context.webkitBackingStorePixelRatio ||\n", - "\tthis.context.mozBackingStorePixelRatio ||\n", - "\tthis.context.msBackingStorePixelRatio ||\n", - "\tthis.context.oBackingStorePixelRatio ||\n", - "\tthis.context.backingStorePixelRatio || 1;\n", - "\n", - " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", - "\n", - " var rubberband = $('');\n", - " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", - "\n", - " var pass_mouse_events = true;\n", - "\n", - " canvas_div.resizable({\n", - " start: function(event, ui) {\n", - " pass_mouse_events = false;\n", - " },\n", - " resize: function(event, ui) {\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " stop: function(event, ui) {\n", - " pass_mouse_events = true;\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " });\n", - "\n", - " function mouse_event_fn(event) {\n", - " if (pass_mouse_events)\n", - " return fig.mouse_event(event, event['data']);\n", - " }\n", - "\n", - " rubberband.mousedown('button_press', mouse_event_fn);\n", - " rubberband.mouseup('button_release', mouse_event_fn);\n", - " // Throttle sequential mouse events to 1 every 20ms.\n", - " rubberband.mousemove('motion_notify', mouse_event_fn);\n", - "\n", - " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", - " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", - "\n", - " canvas_div.on(\"wheel\", function (event) {\n", - " event = event.originalEvent;\n", - " event['data'] = 'scroll'\n", - " if (event.deltaY < 0) {\n", - " event.step = 1;\n", - " } else {\n", - " event.step = -1;\n", - " }\n", - " mouse_event_fn(event);\n", - " });\n", - "\n", - " canvas_div.append(canvas);\n", - " canvas_div.append(rubberband);\n", - "\n", - " this.rubberband = rubberband;\n", - " this.rubberband_canvas = rubberband[0];\n", - " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", - " this.rubberband_context.strokeStyle = \"#000000\";\n", - "\n", - " this._resize_canvas = function(width, height) {\n", - " // Keep the size of the canvas, canvas container, and rubber band\n", - " // canvas in synch.\n", - " canvas_div.css('width', width)\n", - " canvas_div.css('height', height)\n", - "\n", - " canvas.attr('width', width * mpl.ratio);\n", - " canvas.attr('height', height * mpl.ratio);\n", - " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", - "\n", - " rubberband.attr('width', width);\n", - " rubberband.attr('height', height);\n", - " }\n", - "\n", - " // Set the figure to an initial 600x600px, this will subsequently be updated\n", - " // upon first draw.\n", - " this._resize_canvas(600, 600);\n", - "\n", - " // Disable right mouse context menu.\n", - " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", - " return false;\n", - " });\n", - "\n", - " function set_focus () {\n", - " canvas.focus();\n", - " canvas_div.focus();\n", - " }\n", - "\n", - " window.setTimeout(set_focus, 100);\n", - "}\n", - "\n", - "mpl.figure.prototype._init_toolbar = function() {\n", - " var fig = this;\n", - "\n", - " var nav_element = $('
')\n", - " nav_element.attr('style', 'width: 100%');\n", - " this.root.append(nav_element);\n", - "\n", - " // Define a callback function for later on.\n", - " function toolbar_event(event) {\n", - " return fig.toolbar_button_onclick(event['data']);\n", - " }\n", - " function toolbar_mouse_event(event) {\n", - " return fig.toolbar_button_onmouseover(event['data']);\n", - " }\n", - "\n", - " for(var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " // put a spacer in here.\n", - " continue;\n", - " }\n", - " var button = $('');\n", - " button.click(method_name, toolbar_event);\n", - " button.mouseover(tooltip, toolbar_mouse_event);\n", - " nav_element.append(button);\n", - " }\n", - "\n", - " // Add the status bar.\n", - " var status_bar = $('');\n", - " nav_element.append(status_bar);\n", - " this.message = status_bar[0];\n", - "\n", - " // Add the close button to the window.\n", - " var buttongrp = $('
');\n", - " var button = $('');\n", - " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", - " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", - " buttongrp.append(button);\n", - " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", - " titlebar.prepend(buttongrp);\n", - "}\n", - "\n", - "mpl.figure.prototype._root_extra_style = function(el){\n", - " var fig = this\n", - " el.on(\"remove\", function(){\n", - "\tfig.close_ws(fig, {});\n", - " });\n", - "}\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function(el){\n", - " // this is important to make the div 'focusable\n", - " el.attr('tabindex', 0)\n", - " // reach out to IPython and tell the keyboard manager to turn it's self\n", - " // off when our div gets focus\n", - "\n", - " // location in version 3\n", - " if (IPython.notebook.keyboard_manager) {\n", - " IPython.notebook.keyboard_manager.register_events(el);\n", - " }\n", - " else {\n", - " // location in version 2\n", - " IPython.keyboard_manager.register_events(el);\n", - " }\n", - "\n", - "}\n", - "\n", - "mpl.figure.prototype._key_event_extra = function(event, name) {\n", - " var manager = IPython.notebook.keyboard_manager;\n", - " if (!manager)\n", - " manager = IPython.keyboard_manager;\n", - "\n", - " // Check for shift+enter\n", - " if (event.shiftKey && event.which == 13) {\n", - " this.canvas_div.blur();\n", - " // select the cell after this one\n", - " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", - " IPython.notebook.select(index + 1);\n", - " }\n", - "}\n", - "\n", - "mpl.figure.prototype.handle_save = function(fig, msg) {\n", - " fig.ondownload(fig, null);\n", - "}\n", - "\n", - "\n", - "mpl.find_output_cell = function(html_output) {\n", - " // Return the cell and output element which can be found *uniquely* in the notebook.\n", - " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", - " // IPython event is triggered only after the cells have been serialised, which for\n", - " // our purposes (turning an active figure into a static one), is too late.\n", - " var cells = IPython.notebook.get_cells();\n", - " var ncells = cells.length;\n", - " for (var i=0; i= 3 moved mimebundle to data attribute of output\n", - " data = data.data;\n", - " }\n", - " if (data['text/html'] == html_output) {\n", - " return [cell, data, j];\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "// Register the function which deals with the matplotlib target/channel.\n", - "// The kernel may be null if the page has been refreshed.\n", - "if (IPython.notebook.kernel != null) {\n", - " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", - "}\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "newfig()\n", - "plot(vxs, label='vx')\n", - "plot(vys, label='vy')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Velocity (m/s)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another way to visualize the results is to plot y versus x. The result is the trajectory through the plane of motion." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "data": { - "application/javascript": [ - "/* Put everything inside the global mpl namespace */\n", - "window.mpl = {};\n", - "\n", - "\n", - "mpl.get_websocket_type = function() {\n", - " if (typeof(WebSocket) !== 'undefined') {\n", - " return WebSocket;\n", - " } else if (typeof(MozWebSocket) !== 'undefined') {\n", - " return MozWebSocket;\n", - " } else {\n", - " alert('Your browser does not have WebSocket support.' +\n", - " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", - " 'Firefox 4 and 5 are also supported but you ' +\n", - " 'have to enable WebSockets in about:config.');\n", - " };\n", - "}\n", - "\n", - "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", - " this.id = figure_id;\n", - "\n", - " this.ws = websocket;\n", - "\n", - " this.supports_binary = (this.ws.binaryType != undefined);\n", - "\n", - " if (!this.supports_binary) {\n", - " var warnings = document.getElementById(\"mpl-warnings\");\n", - " if (warnings) {\n", - " warnings.style.display = 'block';\n", - " warnings.textContent = (\n", - " \"This browser does not support binary websocket messages. \" +\n", - " \"Performance may be slow.\");\n", - " }\n", - " }\n", - "\n", - " this.imageObj = new Image();\n", - "\n", - " this.context = undefined;\n", - " this.message = undefined;\n", - " this.canvas = undefined;\n", - " this.rubberband_canvas = undefined;\n", - " this.rubberband_context = undefined;\n", - " this.format_dropdown = undefined;\n", - "\n", - " this.image_mode = 'full';\n", - "\n", - " this.root = $('
');\n", - " this._root_extra_style(this.root)\n", - " this.root.attr('style', 'display: inline-block');\n", - "\n", - " $(parent_element).append(this.root);\n", - "\n", - " this._init_header(this);\n", - " this._init_canvas(this);\n", - " this._init_toolbar(this);\n", - "\n", - " var fig = this;\n", - "\n", - " this.waiting = false;\n", - "\n", - " this.ws.onopen = function () {\n", - " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", - " fig.send_message(\"send_image_mode\", {});\n", - " if (mpl.ratio != 1) {\n", - " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", - " }\n", - " fig.send_message(\"refresh\", {});\n", - " }\n", - "\n", - " this.imageObj.onload = function() {\n", - " if (fig.image_mode == 'full') {\n", - " // Full images could contain transparency (where diff images\n", - " // almost always do), so we need to clear the canvas so that\n", - " // there is no ghosting.\n", - " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", - " }\n", - " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " };\n", - "\n", - " this.imageObj.onunload = function() {\n", - " this.ws.close();\n", - " }\n", - "\n", - " this.ws.onmessage = this._make_on_message_function(this);\n", - "\n", - " this.ondownload = ondownload;\n", - "}\n", - "\n", - "mpl.figure.prototype._init_header = function() {\n", - " var titlebar = $(\n", - " '
');\n", - " var titletext = $(\n", - " '
');\n", - " titlebar.append(titletext)\n", - " this.root.append(titlebar);\n", - " this.header = titletext[0];\n", - "}\n", - "\n", - "\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "\n", - "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "mpl.figure.prototype._init_canvas = function() {\n", - " var fig = this;\n", - "\n", - " var canvas_div = $('
');\n", - "\n", - " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", - "\n", - " function canvas_keyboard_event(event) {\n", - " return fig.key_event(event, event['data']);\n", - " }\n", - "\n", - " canvas_div.keydown('key_press', canvas_keyboard_event);\n", - " canvas_div.keyup('key_release', canvas_keyboard_event);\n", - " this.canvas_div = canvas_div\n", - " this._canvas_extra_style(canvas_div)\n", - " this.root.append(canvas_div);\n", - "\n", - " var canvas = $('');\n", - " canvas.addClass('mpl-canvas');\n", - " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", - "\n", - " this.canvas = canvas[0];\n", - " this.context = canvas[0].getContext(\"2d\");\n", - "\n", - " var backingStore = this.context.backingStorePixelRatio ||\n", - "\tthis.context.webkitBackingStorePixelRatio ||\n", - "\tthis.context.mozBackingStorePixelRatio ||\n", - "\tthis.context.msBackingStorePixelRatio ||\n", - "\tthis.context.oBackingStorePixelRatio ||\n", - "\tthis.context.backingStorePixelRatio || 1;\n", - "\n", - " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", - "\n", - " var rubberband = $('');\n", - " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", - "\n", - " var pass_mouse_events = true;\n", - "\n", - " canvas_div.resizable({\n", - " start: function(event, ui) {\n", - " pass_mouse_events = false;\n", - " },\n", - " resize: function(event, ui) {\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " stop: function(event, ui) {\n", - " pass_mouse_events = true;\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " });\n", - "\n", - " function mouse_event_fn(event) {\n", - " if (pass_mouse_events)\n", - " return fig.mouse_event(event, event['data']);\n", - " }\n", - "\n", - " rubberband.mousedown('button_press', mouse_event_fn);\n", - " rubberband.mouseup('button_release', mouse_event_fn);\n", - " // Throttle sequential mouse events to 1 every 20ms.\n", - " rubberband.mousemove('motion_notify', mouse_event_fn);\n", - "\n", - " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", - " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", - "\n", - " canvas_div.on(\"wheel\", function (event) {\n", - " event = event.originalEvent;\n", - " event['data'] = 'scroll'\n", - " if (event.deltaY < 0) {\n", - " event.step = 1;\n", - " } else {\n", - " event.step = -1;\n", - " }\n", - " mouse_event_fn(event);\n", - " });\n", - "\n", - " canvas_div.append(canvas);\n", - " canvas_div.append(rubberband);\n", - "\n", - " this.rubberband = rubberband;\n", - " this.rubberband_canvas = rubberband[0];\n", - " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", - " this.rubberband_context.strokeStyle = \"#000000\";\n", - "\n", - " this._resize_canvas = function(width, height) {\n", - " // Keep the size of the canvas, canvas container, and rubber band\n", - " // canvas in synch.\n", - " canvas_div.css('width', width)\n", - " canvas_div.css('height', height)\n", - "\n", - " canvas.attr('width', width * mpl.ratio);\n", - " canvas.attr('height', height * mpl.ratio);\n", - " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", - "\n", - " rubberband.attr('width', width);\n", - " rubberband.attr('height', height);\n", - " }\n", - "\n", - " // Set the figure to an initial 600x600px, this will subsequently be updated\n", - " // upon first draw.\n", - " this._resize_canvas(600, 600);\n", - "\n", - " // Disable right mouse context menu.\n", - " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", - " return false;\n", - " });\n", - "\n", - " function set_focus () {\n", - " canvas.focus();\n", - " canvas_div.focus();\n", - " }\n", - "\n", - " window.setTimeout(set_focus, 100);\n", - "}\n", - "\n", - "mpl.figure.prototype._init_toolbar = function() {\n", - " var fig = this;\n", - "\n", - " var nav_element = $('
')\n", - " nav_element.attr('style', 'width: 100%');\n", - " this.root.append(nav_element);\n", - "\n", - " // Define a callback function for later on.\n", - " function toolbar_event(event) {\n", - " return fig.toolbar_button_onclick(event['data']);\n", - " }\n", - " function toolbar_mouse_event(event) {\n", - " return fig.toolbar_button_onmouseover(event['data']);\n", - " }\n", - "\n", - " for(var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " // put a spacer in here.\n", - " continue;\n", - " }\n", - " var button = $('');\n", - " button.click(method_name, toolbar_event);\n", - " button.mouseover(tooltip, toolbar_mouse_event);\n", - " nav_element.append(button);\n", - " }\n", - "\n", - " // Add the status bar.\n", - " var status_bar = $('');\n", - " nav_element.append(status_bar);\n", - " this.message = status_bar[0];\n", - "\n", - " // Add the close button to the window.\n", - " var buttongrp = $('
');\n", - " var button = $('');\n", - " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", - " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", - " buttongrp.append(button);\n", - " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", - " titlebar.prepend(buttongrp);\n", - "}\n", - "\n", - "mpl.figure.prototype._root_extra_style = function(el){\n", - " var fig = this\n", - " el.on(\"remove\", function(){\n", - "\tfig.close_ws(fig, {});\n", - " });\n", - "}\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function(el){\n", - " // this is important to make the div 'focusable\n", - " el.attr('tabindex', 0)\n", - " // reach out to IPython and tell the keyboard manager to turn it's self\n", - " // off when our div gets focus\n", - "\n", - " // location in version 3\n", - " if (IPython.notebook.keyboard_manager) {\n", - " IPython.notebook.keyboard_manager.register_events(el);\n", - " }\n", - " else {\n", - " // location in version 2\n", - " IPython.keyboard_manager.register_events(el);\n", - " }\n", - "\n", - "}\n", - "\n", - "mpl.figure.prototype._key_event_extra = function(event, name) {\n", - " var manager = IPython.notebook.keyboard_manager;\n", - " if (!manager)\n", - " manager = IPython.keyboard_manager;\n", - "\n", - " // Check for shift+enter\n", - " if (event.shiftKey && event.which == 13) {\n", - " this.canvas_div.blur();\n", - " // select the cell after this one\n", - " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", - " IPython.notebook.select(index + 1);\n", - " }\n", - "}\n", - "\n", - "mpl.figure.prototype.handle_save = function(fig, msg) {\n", - " fig.ondownload(fig, null);\n", - "}\n", - "\n", - "\n", - "mpl.find_output_cell = function(html_output) {\n", - " // Return the cell and output element which can be found *uniquely* in the notebook.\n", - " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", - " // IPython event is triggered only after the cells have been serialised, which for\n", - " // our purposes (turning an active figure into a static one), is too late.\n", - " var cells = IPython.notebook.get_cells();\n", - " var ncells = cells.length;\n", - " for (var i=0; i= 3 moved mimebundle to data attribute of output\n", - " data = data.data;\n", - " }\n", - " if (data['text/html'] == html_output) {\n", - " return [cell, data, j];\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "// Register the function which deals with the matplotlib target/channel.\n", - "// The kernel may be null if the page has been refreshed.\n", - "if (IPython.notebook.kernel != null) {\n", - " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", - "}\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "newfig()\n", - "decorate(xlabel='x position (m)',\n", - " ylabel='y position (m)',\n", - " xlim=[-100, 100],\n", - " ylim=[-200, -50],\n", - " legend=False)\n", - "\n", - "for x, y in zip(xs, ys):\n", - " plot(x, y, 'bo', update=True)\n", - " sleep(0.01)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a function that encapsulates that code and runs the animation in (approximately) real time." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def animate2d(xs, ys, speedup=1):\n", - " \"\"\"Animate the results of a projectile simulation.\n", - " \n", - " xs: x position as a function of time\n", - " ys: y position as a function of time\n", - " \n", - " speedup: how much to divide `dt` by\n", - " \"\"\"\n", - " # get the time intervals between elements\n", - " ts = xs.index\n", - " dts = np.diff(ts)\n", - " dts = np.append(dts, 0)\n", - "\n", - " # decorate the plot\n", - " newfig()\n", - " decorate(xlabel='x position (m)',\n", - " ylabel='y position (m)',\n", - " xlim=[xs.min(), xs.max()],\n", - " ylim=[ys.min(), ys.max()],\n", - " legend=False)\n", - "\n", - " # loop through the values\n", - " for x, y, dt in zip(xs, ys, dts):\n", - " plot(x, y, 'bo', update=True)\n", - " sleep(dt / speedup)" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "application/javascript": [ - "/* Put everything inside the global mpl namespace */\n", - "window.mpl = {};\n", - "\n", - "\n", - "mpl.get_websocket_type = function() {\n", - " if (typeof(WebSocket) !== 'undefined') {\n", - " return WebSocket;\n", - " } else if (typeof(MozWebSocket) !== 'undefined') {\n", - " return MozWebSocket;\n", - " } else {\n", - " alert('Your browser does not have WebSocket support.' +\n", - " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", - " 'Firefox 4 and 5 are also supported but you ' +\n", - " 'have to enable WebSockets in about:config.');\n", - " };\n", - "}\n", - "\n", - "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", - " this.id = figure_id;\n", - "\n", - " this.ws = websocket;\n", - "\n", - " this.supports_binary = (this.ws.binaryType != undefined);\n", - "\n", - " if (!this.supports_binary) {\n", - " var warnings = document.getElementById(\"mpl-warnings\");\n", - " if (warnings) {\n", - " warnings.style.display = 'block';\n", - " warnings.textContent = (\n", - " \"This browser does not support binary websocket messages. \" +\n", - " \"Performance may be slow.\");\n", - " }\n", - " }\n", - "\n", - " this.imageObj = new Image();\n", - "\n", - " this.context = undefined;\n", - " this.message = undefined;\n", - " this.canvas = undefined;\n", - " this.rubberband_canvas = undefined;\n", - " this.rubberband_context = undefined;\n", - " this.format_dropdown = undefined;\n", - "\n", - " this.image_mode = 'full';\n", - "\n", - " this.root = $('
');\n", - " this._root_extra_style(this.root)\n", - " this.root.attr('style', 'display: inline-block');\n", - "\n", - " $(parent_element).append(this.root);\n", - "\n", - " this._init_header(this);\n", - " this._init_canvas(this);\n", - " this._init_toolbar(this);\n", - "\n", - " var fig = this;\n", - "\n", - " this.waiting = false;\n", - "\n", - " this.ws.onopen = function () {\n", - " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", - " fig.send_message(\"send_image_mode\", {});\n", - " if (mpl.ratio != 1) {\n", - " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", - " }\n", - " fig.send_message(\"refresh\", {});\n", - " }\n", - "\n", - " this.imageObj.onload = function() {\n", - " if (fig.image_mode == 'full') {\n", - " // Full images could contain transparency (where diff images\n", - " // almost always do), so we need to clear the canvas so that\n", - " // there is no ghosting.\n", - " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", - " }\n", - " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " };\n", - "\n", - " this.imageObj.onunload = function() {\n", - " this.ws.close();\n", - " }\n", - "\n", - " this.ws.onmessage = this._make_on_message_function(this);\n", - "\n", - " this.ondownload = ondownload;\n", - "}\n", - "\n", - "mpl.figure.prototype._init_header = function() {\n", - " var titlebar = $(\n", - " '
');\n", - " var titletext = $(\n", - " '
');\n", - " titlebar.append(titletext)\n", - " this.root.append(titlebar);\n", - " this.header = titletext[0];\n", - "}\n", - "\n", - "\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "\n", - "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "mpl.figure.prototype._init_canvas = function() {\n", - " var fig = this;\n", - "\n", - " var canvas_div = $('
');\n", - "\n", - " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", - "\n", - " function canvas_keyboard_event(event) {\n", - " return fig.key_event(event, event['data']);\n", - " }\n", - "\n", - " canvas_div.keydown('key_press', canvas_keyboard_event);\n", - " canvas_div.keyup('key_release', canvas_keyboard_event);\n", - " this.canvas_div = canvas_div\n", - " this._canvas_extra_style(canvas_div)\n", - " this.root.append(canvas_div);\n", - "\n", - " var canvas = $('');\n", - " canvas.addClass('mpl-canvas');\n", - " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", - "\n", - " this.canvas = canvas[0];\n", - " this.context = canvas[0].getContext(\"2d\");\n", - "\n", - " var backingStore = this.context.backingStorePixelRatio ||\n", - "\tthis.context.webkitBackingStorePixelRatio ||\n", - "\tthis.context.mozBackingStorePixelRatio ||\n", - "\tthis.context.msBackingStorePixelRatio ||\n", - "\tthis.context.oBackingStorePixelRatio ||\n", - "\tthis.context.backingStorePixelRatio || 1;\n", - "\n", - " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", - "\n", - " var rubberband = $('');\n", - " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", - "\n", - " var pass_mouse_events = true;\n", - "\n", - " canvas_div.resizable({\n", - " start: function(event, ui) {\n", - " pass_mouse_events = false;\n", - " },\n", - " resize: function(event, ui) {\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " stop: function(event, ui) {\n", - " pass_mouse_events = true;\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " });\n", - "\n", - " function mouse_event_fn(event) {\n", - " if (pass_mouse_events)\n", - " return fig.mouse_event(event, event['data']);\n", - " }\n", - "\n", - " rubberband.mousedown('button_press', mouse_event_fn);\n", - " rubberband.mouseup('button_release', mouse_event_fn);\n", - " // Throttle sequential mouse events to 1 every 20ms.\n", - " rubberband.mousemove('motion_notify', mouse_event_fn);\n", - "\n", - " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", - " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", - "\n", - " canvas_div.on(\"wheel\", function (event) {\n", - " event = event.originalEvent;\n", - " event['data'] = 'scroll'\n", - " if (event.deltaY < 0) {\n", - " event.step = 1;\n", - " } else {\n", - " event.step = -1;\n", - " }\n", - " mouse_event_fn(event);\n", - " });\n", - "\n", - " canvas_div.append(canvas);\n", - " canvas_div.append(rubberband);\n", - "\n", - " this.rubberband = rubberband;\n", - " this.rubberband_canvas = rubberband[0];\n", - " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", - " this.rubberband_context.strokeStyle = \"#000000\";\n", - "\n", - " this._resize_canvas = function(width, height) {\n", - " // Keep the size of the canvas, canvas container, and rubber band\n", - " // canvas in synch.\n", - " canvas_div.css('width', width)\n", - " canvas_div.css('height', height)\n", - "\n", - " canvas.attr('width', width * mpl.ratio);\n", - " canvas.attr('height', height * mpl.ratio);\n", - " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", - "\n", - " rubberband.attr('width', width);\n", - " rubberband.attr('height', height);\n", - " }\n", - "\n", - " // Set the figure to an initial 600x600px, this will subsequently be updated\n", - " // upon first draw.\n", - " this._resize_canvas(600, 600);\n", - "\n", - " // Disable right mouse context menu.\n", - " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", - " return false;\n", - " });\n", - "\n", - " function set_focus () {\n", - " canvas.focus();\n", - " canvas_div.focus();\n", - " }\n", - "\n", - " window.setTimeout(set_focus, 100);\n", - "}\n", - "\n", - "mpl.figure.prototype._init_toolbar = function() {\n", - " var fig = this;\n", - "\n", - " var nav_element = $('
')\n", - " nav_element.attr('style', 'width: 100%');\n", - " this.root.append(nav_element);\n", - "\n", - " // Define a callback function for later on.\n", - " function toolbar_event(event) {\n", - " return fig.toolbar_button_onclick(event['data']);\n", - " }\n", - " function toolbar_mouse_event(event) {\n", - " return fig.toolbar_button_onmouseover(event['data']);\n", - " }\n", - "\n", - " for(var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " // put a spacer in here.\n", - " continue;\n", - " }\n", - " var button = $('
" - ], - "text/plain": [ - "init x -70.71067811865477 dimensionless\n", - "y -...\n", - "g 9.8\n", - "m 75\n", - "rho 1.2\n", - "C_d 0.340278\n", - "area 1\n", - "length0 100\n", - "k 20\n", - "ts [0.0, 0.06, 0.12, 0.18, 0.24, 0.3, 0.36, 0.42,...\n", - "dtype: object" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(condition)\n", - "system" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
x-70.71067811865477 dimensionless
y-70.71067811865474 dimensionless
vx0 dimensionless
vy0 dimensionless
\n", - "
" - ], - "text/plain": [ - "x -70.71067811865477 dimensionless\n", - "y -70.71067811865474 dimensionless\n", - "vx 0 dimensionless\n", - "vy 0 dimensionless\n", - "dtype: object" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.init" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "To write the slope function, we can get the expressions for `ax` and `ay` directly from SymPy and plug them in." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes derivatives of the state variables.\n", - " \n", - " state: State (x, y, x velocity, y velocity)\n", - " t: time\n", - " system: System object with length0, m, k\n", - " \n", - " returns: sequence (vx, vy, ax, ay)\n", - " \"\"\"\n", - " x, y, vx, vy = state\n", - " unpack(system)\n", - "\n", - " ax = x*(g*y - vx**2 - vy**2)/(x**2 + y**2)\n", - " ay = -(g*x**2 + y*(vx**2 + vy**2))/(x**2 + y**2)\n", - "\n", - " return vx, vy, ax, ay" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "As always, let's test the slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " ,\n", - " ,\n", - " )" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "And then run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 132 ms, sys: 0 ns, total: 132 ms\n", - "Wall time: 130 ms\n" - ] - } - ], - "source": [ - "%time run_odeint(system, slope_func)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Visualizing the results\n", - "\n", - "We can extract the x and y components as `Series` objects." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "xs = system.results.x\n", - "ys = system.results.y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The simplest way to visualize the results is to plot x and y as functions of time." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "application/javascript": [ - "/* Put everything inside the global mpl namespace */\n", - "window.mpl = {};\n", - "\n", - "\n", - "mpl.get_websocket_type = function() {\n", - " if (typeof(WebSocket) !== 'undefined') {\n", - " return WebSocket;\n", - " } else if (typeof(MozWebSocket) !== 'undefined') {\n", - " return MozWebSocket;\n", - " } else {\n", - " alert('Your browser does not have WebSocket support.' +\n", - " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", - " 'Firefox 4 and 5 are also supported but you ' +\n", - " 'have to enable WebSockets in about:config.');\n", - " };\n", - "}\n", - "\n", - "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", - " this.id = figure_id;\n", - "\n", - " this.ws = websocket;\n", - "\n", - " this.supports_binary = (this.ws.binaryType != undefined);\n", - "\n", - " if (!this.supports_binary) {\n", - " var warnings = document.getElementById(\"mpl-warnings\");\n", - " if (warnings) {\n", - " warnings.style.display = 'block';\n", - " warnings.textContent = (\n", - " \"This browser does not support binary websocket messages. \" +\n", - " \"Performance may be slow.\");\n", - " }\n", - " }\n", - "\n", - " this.imageObj = new Image();\n", - "\n", - " this.context = undefined;\n", - " this.message = undefined;\n", - " this.canvas = undefined;\n", - " this.rubberband_canvas = undefined;\n", - " this.rubberband_context = undefined;\n", - " this.format_dropdown = undefined;\n", - "\n", - " this.image_mode = 'full';\n", - "\n", - " this.root = $('
');\n", - " this._root_extra_style(this.root)\n", - " this.root.attr('style', 'display: inline-block');\n", - "\n", - " $(parent_element).append(this.root);\n", - "\n", - " this._init_header(this);\n", - " this._init_canvas(this);\n", - " this._init_toolbar(this);\n", - "\n", - " var fig = this;\n", - "\n", - " this.waiting = false;\n", - "\n", - " this.ws.onopen = function () {\n", - " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", - " fig.send_message(\"send_image_mode\", {});\n", - " if (mpl.ratio != 1) {\n", - " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", - " }\n", - " fig.send_message(\"refresh\", {});\n", - " }\n", - "\n", - " this.imageObj.onload = function() {\n", - " if (fig.image_mode == 'full') {\n", - " // Full images could contain transparency (where diff images\n", - " // almost always do), so we need to clear the canvas so that\n", - " // there is no ghosting.\n", - " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", - " }\n", - " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " };\n", - "\n", - " this.imageObj.onunload = function() {\n", - " this.ws.close();\n", - " }\n", - "\n", - " this.ws.onmessage = this._make_on_message_function(this);\n", - "\n", - " this.ondownload = ondownload;\n", - "}\n", - "\n", - "mpl.figure.prototype._init_header = function() {\n", - " var titlebar = $(\n", - " '
');\n", - " var titletext = $(\n", - " '
');\n", - " titlebar.append(titletext)\n", - " this.root.append(titlebar);\n", - " this.header = titletext[0];\n", - "}\n", - "\n", - "\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "\n", - "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "mpl.figure.prototype._init_canvas = function() {\n", - " var fig = this;\n", - "\n", - " var canvas_div = $('
');\n", - "\n", - " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", - "\n", - " function canvas_keyboard_event(event) {\n", - " return fig.key_event(event, event['data']);\n", - " }\n", - "\n", - " canvas_div.keydown('key_press', canvas_keyboard_event);\n", - " canvas_div.keyup('key_release', canvas_keyboard_event);\n", - " this.canvas_div = canvas_div\n", - " this._canvas_extra_style(canvas_div)\n", - " this.root.append(canvas_div);\n", - "\n", - " var canvas = $('');\n", - " canvas.addClass('mpl-canvas');\n", - " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", - "\n", - " this.canvas = canvas[0];\n", - " this.context = canvas[0].getContext(\"2d\");\n", - "\n", - " var backingStore = this.context.backingStorePixelRatio ||\n", - "\tthis.context.webkitBackingStorePixelRatio ||\n", - "\tthis.context.mozBackingStorePixelRatio ||\n", - "\tthis.context.msBackingStorePixelRatio ||\n", - "\tthis.context.oBackingStorePixelRatio ||\n", - "\tthis.context.backingStorePixelRatio || 1;\n", - "\n", - " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", - "\n", - " var rubberband = $('');\n", - " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", - "\n", - " var pass_mouse_events = true;\n", - "\n", - " canvas_div.resizable({\n", - " start: function(event, ui) {\n", - " pass_mouse_events = false;\n", - " },\n", - " resize: function(event, ui) {\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " stop: function(event, ui) {\n", - " pass_mouse_events = true;\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " });\n", - "\n", - " function mouse_event_fn(event) {\n", - " if (pass_mouse_events)\n", - " return fig.mouse_event(event, event['data']);\n", - " }\n", - "\n", - " rubberband.mousedown('button_press', mouse_event_fn);\n", - " rubberband.mouseup('button_release', mouse_event_fn);\n", - " // Throttle sequential mouse events to 1 every 20ms.\n", - " rubberband.mousemove('motion_notify', mouse_event_fn);\n", - "\n", - " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", - " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", - "\n", - " canvas_div.on(\"wheel\", function (event) {\n", - " event = event.originalEvent;\n", - " event['data'] = 'scroll'\n", - " if (event.deltaY < 0) {\n", - " event.step = 1;\n", - " } else {\n", - " event.step = -1;\n", - " }\n", - " mouse_event_fn(event);\n", - " });\n", - "\n", - " canvas_div.append(canvas);\n", - " canvas_div.append(rubberband);\n", - "\n", - " this.rubberband = rubberband;\n", - " this.rubberband_canvas = rubberband[0];\n", - " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", - " this.rubberband_context.strokeStyle = \"#000000\";\n", - "\n", - " this._resize_canvas = function(width, height) {\n", - " // Keep the size of the canvas, canvas container, and rubber band\n", - " // canvas in synch.\n", - " canvas_div.css('width', width)\n", - " canvas_div.css('height', height)\n", - "\n", - " canvas.attr('width', width * mpl.ratio);\n", - " canvas.attr('height', height * mpl.ratio);\n", - " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", - "\n", - " rubberband.attr('width', width);\n", - " rubberband.attr('height', height);\n", - " }\n", - "\n", - " // Set the figure to an initial 600x600px, this will subsequently be updated\n", - " // upon first draw.\n", - " this._resize_canvas(600, 600);\n", - "\n", - " // Disable right mouse context menu.\n", - " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", - " return false;\n", - " });\n", - "\n", - " function set_focus () {\n", - " canvas.focus();\n", - " canvas_div.focus();\n", - " }\n", - "\n", - " window.setTimeout(set_focus, 100);\n", - "}\n", - "\n", - "mpl.figure.prototype._init_toolbar = function() {\n", - " var fig = this;\n", - "\n", - " var nav_element = $('
')\n", - " nav_element.attr('style', 'width: 100%');\n", - " this.root.append(nav_element);\n", - "\n", - " // Define a callback function for later on.\n", - " function toolbar_event(event) {\n", - " return fig.toolbar_button_onclick(event['data']);\n", - " }\n", - " function toolbar_mouse_event(event) {\n", - " return fig.toolbar_button_onmouseover(event['data']);\n", - " }\n", - "\n", - " for(var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " // put a spacer in here.\n", - " continue;\n", - " }\n", - " var button = $('');\n", - " button.click(method_name, toolbar_event);\n", - " button.mouseover(tooltip, toolbar_mouse_event);\n", - " nav_element.append(button);\n", - " }\n", - "\n", - " // Add the status bar.\n", - " var status_bar = $('');\n", - " nav_element.append(status_bar);\n", - " this.message = status_bar[0];\n", - "\n", - " // Add the close button to the window.\n", - " var buttongrp = $('
');\n", - " var button = $('');\n", - " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", - " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", - " buttongrp.append(button);\n", - " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", - " titlebar.prepend(buttongrp);\n", - "}\n", - "\n", - "mpl.figure.prototype._root_extra_style = function(el){\n", - " var fig = this\n", - " el.on(\"remove\", function(){\n", - "\tfig.close_ws(fig, {});\n", - " });\n", - "}\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function(el){\n", - " // this is important to make the div 'focusable\n", - " el.attr('tabindex', 0)\n", - " // reach out to IPython and tell the keyboard manager to turn it's self\n", - " // off when our div gets focus\n", - "\n", - " // location in version 3\n", - " if (IPython.notebook.keyboard_manager) {\n", - " IPython.notebook.keyboard_manager.register_events(el);\n", - " }\n", - " else {\n", - " // location in version 2\n", - " IPython.keyboard_manager.register_events(el);\n", - " }\n", - "\n", - "}\n", - "\n", - "mpl.figure.prototype._key_event_extra = function(event, name) {\n", - " var manager = IPython.notebook.keyboard_manager;\n", - " if (!manager)\n", - " manager = IPython.keyboard_manager;\n", - "\n", - " // Check for shift+enter\n", - " if (event.shiftKey && event.which == 13) {\n", - " this.canvas_div.blur();\n", - " // select the cell after this one\n", - " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", - " IPython.notebook.select(index + 1);\n", - " }\n", - "}\n", - "\n", - "mpl.figure.prototype.handle_save = function(fig, msg) {\n", - " fig.ondownload(fig, null);\n", - "}\n", - "\n", - "\n", - "mpl.find_output_cell = function(html_output) {\n", - " // Return the cell and output element which can be found *uniquely* in the notebook.\n", - " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", - " // IPython event is triggered only after the cells have been serialised, which for\n", - " // our purposes (turning an active figure into a static one), is too late.\n", - " var cells = IPython.notebook.get_cells();\n", - " var ncells = cells.length;\n", - " for (var i=0; i= 3 moved mimebundle to data attribute of output\n", - " data = data.data;\n", - " }\n", - " if (data['text/html'] == html_output) {\n", - " return [cell, data, j];\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "// Register the function which deals with the matplotlib target/channel.\n", - "// The kernel may be null if the page has been refreshed.\n", - "if (IPython.notebook.kernel != null) {\n", - " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", - "}\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "newfig()\n", - "plot(vxs, label='vx')\n", - "plot(vys, label='vy')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Velocity (m/s)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another way to visualize the results is to plot y versus x. The result is the trajectory through the plane of motion." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "application/javascript": [ - "/* Put everything inside the global mpl namespace */\n", - "window.mpl = {};\n", - "\n", - "\n", - "mpl.get_websocket_type = function() {\n", - " if (typeof(WebSocket) !== 'undefined') {\n", - " return WebSocket;\n", - " } else if (typeof(MozWebSocket) !== 'undefined') {\n", - " return MozWebSocket;\n", - " } else {\n", - " alert('Your browser does not have WebSocket support.' +\n", - " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", - " 'Firefox 4 and 5 are also supported but you ' +\n", - " 'have to enable WebSockets in about:config.');\n", - " };\n", - "}\n", - "\n", - "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", - " this.id = figure_id;\n", - "\n", - " this.ws = websocket;\n", - "\n", - " this.supports_binary = (this.ws.binaryType != undefined);\n", - "\n", - " if (!this.supports_binary) {\n", - " var warnings = document.getElementById(\"mpl-warnings\");\n", - " if (warnings) {\n", - " warnings.style.display = 'block';\n", - " warnings.textContent = (\n", - " \"This browser does not support binary websocket messages. \" +\n", - " \"Performance may be slow.\");\n", - " }\n", - " }\n", - "\n", - " this.imageObj = new Image();\n", - "\n", - " this.context = undefined;\n", - " this.message = undefined;\n", - " this.canvas = undefined;\n", - " this.rubberband_canvas = undefined;\n", - " this.rubberband_context = undefined;\n", - " this.format_dropdown = undefined;\n", - "\n", - " this.image_mode = 'full';\n", - "\n", - " this.root = $('
');\n", - " this._root_extra_style(this.root)\n", - " this.root.attr('style', 'display: inline-block');\n", - "\n", - " $(parent_element).append(this.root);\n", - "\n", - " this._init_header(this);\n", - " this._init_canvas(this);\n", - " this._init_toolbar(this);\n", - "\n", - " var fig = this;\n", - "\n", - " this.waiting = false;\n", - "\n", - " this.ws.onopen = function () {\n", - " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", - " fig.send_message(\"send_image_mode\", {});\n", - " if (mpl.ratio != 1) {\n", - " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", - " }\n", - " fig.send_message(\"refresh\", {});\n", - " }\n", - "\n", - " this.imageObj.onload = function() {\n", - " if (fig.image_mode == 'full') {\n", - " // Full images could contain transparency (where diff images\n", - " // almost always do), so we need to clear the canvas so that\n", - " // there is no ghosting.\n", - " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", - " }\n", - " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " };\n", - "\n", - " this.imageObj.onunload = function() {\n", - " this.ws.close();\n", - " }\n", - "\n", - " this.ws.onmessage = this._make_on_message_function(this);\n", - "\n", - " this.ondownload = ondownload;\n", - "}\n", - "\n", - "mpl.figure.prototype._init_header = function() {\n", - " var titlebar = $(\n", - " '
');\n", - " var titletext = $(\n", - " '
');\n", - " titlebar.append(titletext)\n", - " this.root.append(titlebar);\n", - " this.header = titletext[0];\n", - "}\n", - "\n", - "\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "\n", - "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "mpl.figure.prototype._init_canvas = function() {\n", - " var fig = this;\n", - "\n", - " var canvas_div = $('
');\n", - "\n", - " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", - "\n", - " function canvas_keyboard_event(event) {\n", - " return fig.key_event(event, event['data']);\n", - " }\n", - "\n", - " canvas_div.keydown('key_press', canvas_keyboard_event);\n", - " canvas_div.keyup('key_release', canvas_keyboard_event);\n", - " this.canvas_div = canvas_div\n", - " this._canvas_extra_style(canvas_div)\n", - " this.root.append(canvas_div);\n", - "\n", - " var canvas = $('');\n", - " canvas.addClass('mpl-canvas');\n", - " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", - "\n", - " this.canvas = canvas[0];\n", - " this.context = canvas[0].getContext(\"2d\");\n", - "\n", - " var backingStore = this.context.backingStorePixelRatio ||\n", - "\tthis.context.webkitBackingStorePixelRatio ||\n", - "\tthis.context.mozBackingStorePixelRatio ||\n", - "\tthis.context.msBackingStorePixelRatio ||\n", - "\tthis.context.oBackingStorePixelRatio ||\n", - "\tthis.context.backingStorePixelRatio || 1;\n", - "\n", - " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", - "\n", - " var rubberband = $('');\n", - " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", - "\n", - " var pass_mouse_events = true;\n", - "\n", - " canvas_div.resizable({\n", - " start: function(event, ui) {\n", - " pass_mouse_events = false;\n", - " },\n", - " resize: function(event, ui) {\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " stop: function(event, ui) {\n", - " pass_mouse_events = true;\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " });\n", - "\n", - " function mouse_event_fn(event) {\n", - " if (pass_mouse_events)\n", - " return fig.mouse_event(event, event['data']);\n", - " }\n", - "\n", - " rubberband.mousedown('button_press', mouse_event_fn);\n", - " rubberband.mouseup('button_release', mouse_event_fn);\n", - " // Throttle sequential mouse events to 1 every 20ms.\n", - " rubberband.mousemove('motion_notify', mouse_event_fn);\n", - "\n", - " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", - " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", - "\n", - " canvas_div.on(\"wheel\", function (event) {\n", - " event = event.originalEvent;\n", - " event['data'] = 'scroll'\n", - " if (event.deltaY < 0) {\n", - " event.step = 1;\n", - " } else {\n", - " event.step = -1;\n", - " }\n", - " mouse_event_fn(event);\n", - " });\n", - "\n", - " canvas_div.append(canvas);\n", - " canvas_div.append(rubberband);\n", - "\n", - " this.rubberband = rubberband;\n", - " this.rubberband_canvas = rubberband[0];\n", - " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", - " this.rubberband_context.strokeStyle = \"#000000\";\n", - "\n", - " this._resize_canvas = function(width, height) {\n", - " // Keep the size of the canvas, canvas container, and rubber band\n", - " // canvas in synch.\n", - " canvas_div.css('width', width)\n", - " canvas_div.css('height', height)\n", - "\n", - " canvas.attr('width', width * mpl.ratio);\n", - " canvas.attr('height', height * mpl.ratio);\n", - " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", - "\n", - " rubberband.attr('width', width);\n", - " rubberband.attr('height', height);\n", - " }\n", - "\n", - " // Set the figure to an initial 600x600px, this will subsequently be updated\n", - " // upon first draw.\n", - " this._resize_canvas(600, 600);\n", - "\n", - " // Disable right mouse context menu.\n", - " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", - " return false;\n", - " });\n", - "\n", - " function set_focus () {\n", - " canvas.focus();\n", - " canvas_div.focus();\n", - " }\n", - "\n", - " window.setTimeout(set_focus, 100);\n", - "}\n", - "\n", - "mpl.figure.prototype._init_toolbar = function() {\n", - " var fig = this;\n", - "\n", - " var nav_element = $('
')\n", - " nav_element.attr('style', 'width: 100%');\n", - " this.root.append(nav_element);\n", - "\n", - " // Define a callback function for later on.\n", - " function toolbar_event(event) {\n", - " return fig.toolbar_button_onclick(event['data']);\n", - " }\n", - " function toolbar_mouse_event(event) {\n", - " return fig.toolbar_button_onmouseover(event['data']);\n", - " }\n", - "\n", - " for(var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " // put a spacer in here.\n", - " continue;\n", - " }\n", - " var button = $('');\n", - " button.click(method_name, toolbar_event);\n", - " button.mouseover(tooltip, toolbar_mouse_event);\n", - " nav_element.append(button);\n", - " }\n", - "\n", - " // Add the status bar.\n", - " var status_bar = $('');\n", - " nav_element.append(status_bar);\n", - " this.message = status_bar[0];\n", - "\n", - " // Add the close button to the window.\n", - " var buttongrp = $('
');\n", - " var button = $('');\n", - " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", - " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", - " buttongrp.append(button);\n", - " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", - " titlebar.prepend(buttongrp);\n", - "}\n", - "\n", - "mpl.figure.prototype._root_extra_style = function(el){\n", - " var fig = this\n", - " el.on(\"remove\", function(){\n", - "\tfig.close_ws(fig, {});\n", - " });\n", - "}\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function(el){\n", - " // this is important to make the div 'focusable\n", - " el.attr('tabindex', 0)\n", - " // reach out to IPython and tell the keyboard manager to turn it's self\n", - " // off when our div gets focus\n", - "\n", - " // location in version 3\n", - " if (IPython.notebook.keyboard_manager) {\n", - " IPython.notebook.keyboard_manager.register_events(el);\n", - " }\n", - " else {\n", - " // location in version 2\n", - " IPython.keyboard_manager.register_events(el);\n", - " }\n", - "\n", - "}\n", - "\n", - "mpl.figure.prototype._key_event_extra = function(event, name) {\n", - " var manager = IPython.notebook.keyboard_manager;\n", - " if (!manager)\n", - " manager = IPython.keyboard_manager;\n", - "\n", - " // Check for shift+enter\n", - " if (event.shiftKey && event.which == 13) {\n", - " this.canvas_div.blur();\n", - " // select the cell after this one\n", - " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", - " IPython.notebook.select(index + 1);\n", - " }\n", - "}\n", - "\n", - "mpl.figure.prototype.handle_save = function(fig, msg) {\n", - " fig.ondownload(fig, null);\n", - "}\n", - "\n", - "\n", - "mpl.find_output_cell = function(html_output) {\n", - " // Return the cell and output element which can be found *uniquely* in the notebook.\n", - " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", - " // IPython event is triggered only after the cells have been serialised, which for\n", - " // our purposes (turning an active figure into a static one), is too late.\n", - " var cells = IPython.notebook.get_cells();\n", - " var ncells = cells.length;\n", - " for (var i=0; i= 3 moved mimebundle to data attribute of output\n", - " data = data.data;\n", - " }\n", - " if (data['text/html'] == html_output) {\n", - " return [cell, data, j];\n", - " }\n", - " }\n", - " }\n", - " }\n", - "}\n", - "\n", - "// Register the function which deals with the matplotlib target/channel.\n", - "// The kernel may be null if the page has been refreshed.\n", - "if (IPython.notebook.kernel != null) {\n", - " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", - "}\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "newfig()\n", - "decorate(xlabel='x position (m)',\n", - " ylabel='y position (m)',\n", - " xlim=[-100, 100],\n", - " ylim=[-200, -50],\n", - " legend=False)\n", - "\n", - "for x, y in zip(xs, ys):\n", - " plot(x, y, 'bo', update=True)\n", - " sleep(0.01)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a function that encapsulates that code and runs the animation in (approximately) real time." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def animate2d(xs, ys, speedup=1):\n", - " \"\"\"Animate the results of a projectile simulation.\n", - " \n", - " xs: x position as a function of time\n", - " ys: y position as a function of time\n", - " \n", - " speedup: how much to divide `dt` by\n", - " \"\"\"\n", - " # get the time intervals between elements\n", - " ts = xs.index\n", - " dts = np.diff(ts)\n", - " dts = np.append(dts, 0)\n", - "\n", - " # decorate the plot\n", - " newfig()\n", - " decorate(xlabel='x position (m)',\n", - " ylabel='y position (m)',\n", - " xlim=[xs.min(), xs.max()],\n", - " ylim=[ys.min(), ys.max()],\n", - " legend=False)\n", - "\n", - " # loop through the values\n", - " for x, y, dt in zip(xs, ys, dts):\n", - " plot(x, y, 'bo', update=True)\n", - " sleep(dt / speedup)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "application/javascript": [ - "/* Put everything inside the global mpl namespace */\n", - "window.mpl = {};\n", - "\n", - "\n", - "mpl.get_websocket_type = function() {\n", - " if (typeof(WebSocket) !== 'undefined') {\n", - " return WebSocket;\n", - " } else if (typeof(MozWebSocket) !== 'undefined') {\n", - " return MozWebSocket;\n", - " } else {\n", - " alert('Your browser does not have WebSocket support.' +\n", - " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", - " 'Firefox 4 and 5 are also supported but you ' +\n", - " 'have to enable WebSockets in about:config.');\n", - " };\n", - "}\n", - "\n", - "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", - " this.id = figure_id;\n", - "\n", - " this.ws = websocket;\n", - "\n", - " this.supports_binary = (this.ws.binaryType != undefined);\n", - "\n", - " if (!this.supports_binary) {\n", - " var warnings = document.getElementById(\"mpl-warnings\");\n", - " if (warnings) {\n", - " warnings.style.display = 'block';\n", - " warnings.textContent = (\n", - " \"This browser does not support binary websocket messages. \" +\n", - " \"Performance may be slow.\");\n", - " }\n", - " }\n", - "\n", - " this.imageObj = new Image();\n", - "\n", - " this.context = undefined;\n", - " this.message = undefined;\n", - " this.canvas = undefined;\n", - " this.rubberband_canvas = undefined;\n", - " this.rubberband_context = undefined;\n", - " this.format_dropdown = undefined;\n", - "\n", - " this.image_mode = 'full';\n", - "\n", - " this.root = $('
');\n", - " this._root_extra_style(this.root)\n", - " this.root.attr('style', 'display: inline-block');\n", - "\n", - " $(parent_element).append(this.root);\n", - "\n", - " this._init_header(this);\n", - " this._init_canvas(this);\n", - " this._init_toolbar(this);\n", - "\n", - " var fig = this;\n", - "\n", - " this.waiting = false;\n", - "\n", - " this.ws.onopen = function () {\n", - " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", - " fig.send_message(\"send_image_mode\", {});\n", - " if (mpl.ratio != 1) {\n", - " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", - " }\n", - " fig.send_message(\"refresh\", {});\n", - " }\n", - "\n", - " this.imageObj.onload = function() {\n", - " if (fig.image_mode == 'full') {\n", - " // Full images could contain transparency (where diff images\n", - " // almost always do), so we need to clear the canvas so that\n", - " // there is no ghosting.\n", - " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", - " }\n", - " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " };\n", - "\n", - " this.imageObj.onunload = function() {\n", - " this.ws.close();\n", - " }\n", - "\n", - " this.ws.onmessage = this._make_on_message_function(this);\n", - "\n", - " this.ondownload = ondownload;\n", - "}\n", - "\n", - "mpl.figure.prototype._init_header = function() {\n", - " var titlebar = $(\n", - " '
');\n", - " var titletext = $(\n", - " '
');\n", - " titlebar.append(titletext)\n", - " this.root.append(titlebar);\n", - " this.header = titletext[0];\n", - "}\n", - "\n", - "\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "\n", - "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "mpl.figure.prototype._init_canvas = function() {\n", - " var fig = this;\n", - "\n", - " var canvas_div = $('
');\n", - "\n", - " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", - "\n", - " function canvas_keyboard_event(event) {\n", - " return fig.key_event(event, event['data']);\n", - " }\n", - "\n", - " canvas_div.keydown('key_press', canvas_keyboard_event);\n", - " canvas_div.keyup('key_release', canvas_keyboard_event);\n", - " this.canvas_div = canvas_div\n", - " this._canvas_extra_style(canvas_div)\n", - " this.root.append(canvas_div);\n", - "\n", - " var canvas = $('');\n", - " canvas.addClass('mpl-canvas');\n", - " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", - "\n", - " this.canvas = canvas[0];\n", - " this.context = canvas[0].getContext(\"2d\");\n", - "\n", - " var backingStore = this.context.backingStorePixelRatio ||\n", - "\tthis.context.webkitBackingStorePixelRatio ||\n", - "\tthis.context.mozBackingStorePixelRatio ||\n", - "\tthis.context.msBackingStorePixelRatio ||\n", - "\tthis.context.oBackingStorePixelRatio ||\n", - "\tthis.context.backingStorePixelRatio || 1;\n", - "\n", - " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", - "\n", - " var rubberband = $('');\n", - " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", - "\n", - " var pass_mouse_events = true;\n", - "\n", - " canvas_div.resizable({\n", - " start: function(event, ui) {\n", - " pass_mouse_events = false;\n", - " },\n", - " resize: function(event, ui) {\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " stop: function(event, ui) {\n", - " pass_mouse_events = true;\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " });\n", - "\n", - " function mouse_event_fn(event) {\n", - " if (pass_mouse_events)\n", - " return fig.mouse_event(event, event['data']);\n", - " }\n", - "\n", - " rubberband.mousedown('button_press', mouse_event_fn);\n", - " rubberband.mouseup('button_release', mouse_event_fn);\n", - " // Throttle sequential mouse events to 1 every 20ms.\n", - " rubberband.mousemove('motion_notify', mouse_event_fn);\n", - "\n", - " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", - " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", - "\n", - " canvas_div.on(\"wheel\", function (event) {\n", - " event = event.originalEvent;\n", - " event['data'] = 'scroll'\n", - " if (event.deltaY < 0) {\n", - " event.step = 1;\n", - " } else {\n", - " event.step = -1;\n", - " }\n", - " mouse_event_fn(event);\n", - " });\n", - "\n", - " canvas_div.append(canvas);\n", - " canvas_div.append(rubberband);\n", - "\n", - " this.rubberband = rubberband;\n", - " this.rubberband_canvas = rubberband[0];\n", - " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", - " this.rubberband_context.strokeStyle = \"#000000\";\n", - "\n", - " this._resize_canvas = function(width, height) {\n", - " // Keep the size of the canvas, canvas container, and rubber band\n", - " // canvas in synch.\n", - " canvas_div.css('width', width)\n", - " canvas_div.css('height', height)\n", - "\n", - " canvas.attr('width', width * mpl.ratio);\n", - " canvas.attr('height', height * mpl.ratio);\n", - " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", - "\n", - " rubberband.attr('width', width);\n", - " rubberband.attr('height', height);\n", - " }\n", - "\n", - " // Set the figure to an initial 600x600px, this will subsequently be updated\n", - " // upon first draw.\n", - " this._resize_canvas(600, 600);\n", - "\n", - " // Disable right mouse context menu.\n", - " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", - " return false;\n", - " });\n", - "\n", - " function set_focus () {\n", - " canvas.focus();\n", - " canvas_div.focus();\n", - " }\n", - "\n", - " window.setTimeout(set_focus, 100);\n", - "}\n", - "\n", - "mpl.figure.prototype._init_toolbar = function() {\n", - " var fig = this;\n", - "\n", - " var nav_element = $('
')\n", - " nav_element.attr('style', 'width: 100%');\n", - " this.root.append(nav_element);\n", - "\n", - " // Define a callback function for later on.\n", - " function toolbar_event(event) {\n", - " return fig.toolbar_button_onclick(event['data']);\n", - " }\n", - " function toolbar_mouse_event(event) {\n", - " return fig.toolbar_button_onmouseover(event['data']);\n", - " }\n", - "\n", - " for(var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " // put a spacer in here.\n", - " continue;\n", - " }\n", - " var button = $('
" - ], - "text/plain": [ - "t0 0.0\n", - "t_end 10.0\n", - "p0 10.0\n", - "birth_rate 0.9\n", - "death_rate 0.5\n", - "dtype: float64" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = System(t0 = 0, \n", - " t_end = 10,\n", - " p0 = 10,\n", - " birth_rate = 0.9,\n", - " death_rate = 0.5)\n", - "\n", - "system" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a version of run_simulation, similar to the one in Chapter 3, with both births and deaths proportional to the current population." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def run_simulation(system):\n", - " \"\"\"Runs a proportional growth model.\n", - " \n", - " Adds TimeSeries to `system` as `results`.\n", - " \n", - " system: System object with t0, t_end, p0,\n", - " birth_rate and death_rate\n", - " \"\"\"\n", - " results = TimeSeries()\n", - " results[system.t0] = system.p0\n", - " for t in linrange(system.t0, system.t_end):\n", - " births = system.birth_rate * results[t]\n", - " deaths = system.death_rate * results[t]\n", - " results[t+1] = results[t] + births - deaths\n", - " system.results = results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run the simulation and display the results:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
010.000000
114.000000
219.600000
327.440000
438.416000
553.782400
675.295360
7105.413504
8147.578906
9206.610468
10289.254655
11404.956517
\n", - "
" - ], - "text/plain": [ - "0 10.000000\n", - "1 14.000000\n", - "2 19.600000\n", - "3 27.440000\n", - "4 38.416000\n", - "5 53.782400\n", - "6 75.295360\n", - "7 105.413504\n", - "8 147.578906\n", - "9 206.610468\n", - "10 289.254655\n", - "11 404.956517\n", - "dtype: float64" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run_simulation(system)\n", - "system.results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that the simulation actually runs one season past `t_end`. That's an off-by-one error that I'll fix later, but for now we don't really care.\n", - "\n", - "The following function plots the results." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def plot_results(system, title=None):\n", - " \"\"\"Plot the estimates and the model.\n", - " \n", - " system: System object with `results`\n", - " \"\"\"\n", - " newfig()\n", - " plot(system.results, 'bo', label='rabbits')\n", - " decorate(xlabel='Season', \n", - " ylabel='Rabbit population',\n", - " title=title)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's how we call it." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\nRK5CYII=\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_results(system, title='Proportional growth model')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's suppose our goal is to maximize the number of rabbits, so the metric we care about is the final population. We can extract it from the results like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def final_population(system):\n", - " t_end = system.results.index[-1]\n", - " return system.results[t_end]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And call it like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "404.95651696640027" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "final_population(system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To explore the effect of the parameters on the results, we'll define `make_system`, which takes the system parameters as function parameters(!) and returns a `System` object:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(birth_rate=0.9, death_rate=0.5):\n", - " \n", - " system = System(t0 = 0, \n", - " t_end = 10,\n", - " p0 = 10,\n", - " birth_rate = birth_rate,\n", - " death_rate = death_rate)\n", - " return system" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can make a `System`, run a simulation, and extract a metric:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "404.95651696640027" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system()\n", - "run_simulation(system)\n", - "final_population(system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To see the relationship between `birth_rate` and final population, we'll define `sweep_birth_rate`:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def sweep_birth_rate(birth_rates, death_rate=0.5):\n", - " \n", - " for birth_rate in birth_rates:\n", - " system = make_system(birth_rate=birth_rate,\n", - " death_rate=death_rate)\n", - " run_simulation(system)\n", - " p_end = final_population(system)\n", - " plot(birth_rate, p_end, 'gs', label='rabbits')\n", - " \n", - " decorate(xlabel='Births per rabbit per season',\n", - " ylabel='Final population')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The first parameter of `sweep_birth_rate` is supposed to be an array; we can use `linspace` to make one." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 0. , 0.05, 0.1 , 0.15, 0.2 , 0.25, 0.3 , 0.35, 0.4 ,\n", - " 0.45, 0.5 , 0.55, 0.6 , 0.65, 0.7 , 0.75, 0.8 , 0.85,\n", - " 0.9 , 0.95, 1. ])" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "birth_rates = linspace(0, 1, 21)\n", - "birth_rates" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can call `sweep_birth_rate`.\n", - "\n", - "The resulting figure shows the final population for a range of values of `birth_rate`.\n", - "\n", - "Confusingly, the results from a parameter sweep sometimes resemble a time series. It is very important to remember the difference. One way to avoid confusion: LABEL THE AXES.\n", - "\n", - "In the following figure, the x-axis is `birth_rate`, NOT TIME." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\nTkSuQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "birth_rates = linspace(0, 1, 21)\n", - "sweep_birth_rate(birth_rates)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The code to sweep the death rate is similar." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def sweep_death_rate(death_rates, birth_rate=0.9):\n", - " \n", - " for death_rate in death_rates:\n", - " system = make_system(birth_rate=birth_rate,\n", - " death_rate=death_rate)\n", - " run_simulation(system)\n", - " p_end = final_population(system)\n", - " plot(death_rate, p_end, 'r^', label='rabbits')\n", - " \n", - " decorate(xlabel='Deaths per rabbit per season',\n", - " ylabel='Final population')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here are the results. Again, the x-axis is `death_rate`, NOT TIME." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\nMXHiRJw6dQoAMGTIEFy9ehV9+/bFhAkT4OXlhYkTJ6rHadSoEYYPH4433ngDffr0we3bt/H1119X\neTjCy8sLMpkMnp6e2LlzJ+bPn4+ioiJ4e3uja9euCAgIwCuvvILRo0frddtr9WTGipM1vvvuOyxa\ntAg2NjbYvn07PvjgAzzzzDNQqVQwNzfXeE9FKFCpVMjJyanUX7FMdnY2VCoVAFQ5RkUfERHR41y+\nfLlS2/Tp0zF9+nSNtk2bNql/3rBhg/rn2bNnV3q/r6+v+q6L/fv3f+x6bW1tcejQIY3+r7/+Wofq\na1at7lGoOEPUz88P9vb2aNasGd588004Ojo+8b2tq0pj1eknIiKiymo1KLRu3RoANA4dAOWpKSsr\nC1KpFLm5uRp9OTk5AACZTAYrK6tK/RXLSKVSSKVSAKhyDH1fPkJERGSMaj0oWFhYVLqUIz09He3a\ntYNCoah0LkFiYiJkMhlsbW2hUCigVCrVJ3IAwJ07d5CRkQFXV1dYW1tDJpNVOYarq6v+NoyIiMhI\n1WpQMDExwfjx47Fx40acPHkSRUVFiI6OxqVLlzB69Gj4+/sjPj4e+/fvR1FREc6fP4+1a9di/Pjx\nkEgk6N27Nzp27Ih58+YhJycHKpUKYWFhkMvlcHd3h0Qigb+/P9asWYOUlBQUFRUhNjYWJ0+eVN+r\nm4iIiHRX63dmnDRpEkpKShASEoLs7GzY2dlh1apV6Ny5MwAgPDwcS5cuxYwZMyCVSuHn54cJEyYA\nKA8akZGRCA0NhYeHByQSCdzd3REZGQkTExMAQEBAAAoLCxEUFASVSgU7OzssWbKk0pUQRERE9HgS\noe/HTj0lMjMz4enpiSNHjsDa2trQ5RAREemVrp97fCgUERERacWgQERERFoxKBAREZFWDApERESk\nFYMCERERacWgQERERFoxKBAREZFWDApERESkFYMCERERacWgQERERFoxKBAREZFWDApERESkFYMC\nERERacWgQERERFoxKBAREZFWDApERESkFYMCERERacWgQERERFoxKBAREZFWDApERESkFYMCERER\nacWgQERERFoxKBAREZFWDApERESkFYMCERERacWgQERERFoxKBAREZFWDApERESkFYMCERERacWg\nQERERFoxKBAREZFWDApERESkFYMCERERadWwtlfo4eGBrKwsNGigmVFiYmJgZ2eH2NhYrF69Gmlp\n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\nAABJRU5ErkJggg==\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "death_rates = linspace(0.1, 1, 20)\n", - "sweep_death_rate(death_rates)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the previous sweeps, we hold one parameter constant and sweep the other.\n", - "\n", - "You can also sweep more than one variable at a time, and plot multiple lines on a single axis.\n", - "\n", - "To keep the figure from getting too cluttered, I'll reduce the number of values in `birth_rates`:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 0.4, 0.6, 0.8, 1. ])" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "birth_rates = linspace(0.4, 1, 4)\n", - "birth_rates" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "By putting one for loop inside another, we can enumerate all pairs of values.\n", - "\n", - "The results show 4 lines, one for each value of `birth_rate`.\n", - "\n", - "(I did not plot the lines between the data points because of a limitation in `plot`.)" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "for birth_rate in birth_rates:\n", - " for death_rate in death_rates:\n", - " system = make_system(birth_rate=birth_rate,\n", - " death_rate=death_rate)\n", - " run_simulation(system)\n", - " p_end = final_population(system)\n", - " plot(death_rate, p_end, 'c^', label='rabbits')\n", - " \n", - "decorate(xlabel='Deaths per rabbit per season',\n", - " ylabel='Final population')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you suspect that the results depend on the difference between `birth_rate` and `death_rate`, you could run the same loop, plotting the \"net birth rate\" on the x axis.\n", - "\n", - "If you are right, the results will fall on a single curve, which means that knowing the difference is sufficient to predict the outcome; you don't actually have to know the two parameters separately." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\nERGR0ZhAEBERkdGYQBAREZHRmEAQERGR0ZhAEBERkdGYQBAREZHRmEAQERGR0ZhAEBERkdGYQBAR\nEZHRmEAQERGR0ZhAEBERkdGYQBAREZHRmEAQERGR0azNHQAREREZT52oRkl+CeTucti42+j8K3OQ\nmfz6TCCIiIgaIMfOjsjYloG89Dyd7Q7+DvCc4mny67MLg4iIqAFyCnWCJJN0tklWElRDVPVyfSYQ\nREREDZC1kzUc/B10tjmHO8PWw7Zers8EgoiIqIFShCm0r2UOMiijlPV2bSYQREREDZRjJ0fInEoH\nTLpFudXL4MkyTCCIiIgaKEkmQRGqgLylHC7hLvV6bc7CICIiasAUPRSwf9QekpVU88F1iAkEERFR\nA2bnZQd41f912YVBRERERmMCQUREREZjAkFERERGYwJBRERERmMCQUREREZjAkFERERGYwJBRERE\nRmMCQUREREZjAkFERERGYwJBRERERmMCQUREREZjAkFERERGs7gE4vz583jxxRfRo0cPhISEYOjQ\nofi///s/7f7k5GQMHjwYQUFB6N+/PxYtWoTi4mLtfrVajbi4OISFhaFHjx6Ii4uDWq3W7i8uLsai\nRYswYMAABAUF4emnn8auXbvqtY5EREQNnUGrcebm5mLDhg1ITU1Fdna23mM+/fTTBw6mpKQEMTEx\n6Ny5M/bs2QN7e3ts3rwZU6ZMwc6dO3Hr1i3Ex8fj/fffR9++fXHhwgXExcXBxsYGkydPRmFhIWJj\nYxEQEIDk5GRYW1tjwYIFiImJQXJyMmxsbLBixQp8+eWX+Oijj9C+fXscPHgQU6dOhbu7O7p16/bA\ndSAiImoKDGqBmDNnDj788EOo1WrY2Njo/a8uaDQaXLlyBU8//TRcXFwgl8sxcuRIFBYW4o8//sCm\nTZvQp08fREZGQi6Xw8/PD2PHjsXGjRtRUlKClJQUXLx4ETNmzEDz5s2hUCgwffp0qNVqHDhwAEII\nbN68GePGjUPHjh0hl8vRr18/hIeHY8OGDXVSByIioqbAoBaIgwcPYuHChXj66adNGoxSqURwcDA+\n//xzdOrUCU5OTti6dStcXV3RrVs3LFy4ECNHjtQpExAQgOzsbKSnpyM1NRXe3t5wdXXV7ndxcYGX\nlxfS0tLQvn17aDQaBAQEVDrHxo0bTVo3IiKixsSgBKK4uBghISGmjgUAsHTpUsTGxqJHjx6QJAmu\n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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "for birth_rate in birth_rates:\n", - " for death_rate in death_rates:\n", - " system = make_system(birth_rate=birth_rate,\n", - " death_rate=death_rate)\n", - " run_simulation(system)\n", - " p_end = final_population(system)\n", - " net_birth_rate = birth_rate - death_rate\n", - " plot(net_birth_rate, p_end, 'mv', label='rabbits')\n", - " \n", - "decorate(xlabel='Net births per rabbit per season',\n", - " ylabel='Final population')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "On the other hand, if you guess that the results depend on the ratio of the parameters, rather than the difference, you could check by plotting the ratio on the x axis.\n", - "\n", - "If the results don't fall on a single curve, that suggests that the ratio alone is not sufficient to predict the outcome. " - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\niejoaDz33HMoKSmpt8/YsWMRFhaGfv36YdKkSThw4ICxPTg4GGfOnMFbb72F4OBgkxGUoqIixMfH\nIzw8HAMHDjSZchJC4J133sHIkSMRHh6OAQMGYM6cOVCpVLc9NqLbEkTULBYsWCBiYmIabKuqqhJR\nUVFi0aJFxm0pKSmif//+4ocffhDnz58XBw4cEMOGDRNz5swxvmft2rUiKChI5OXlidLSUiGEEPff\nf794+umnjf1MnDhRDBkyROzevVsUFRWJzz77TERERJh8VkP1DB8+XDz00EPixx9/FIWFhWLdunWi\nd+/e4p133hFCCFFSUiKeeuopMWDAAFFcXCzKy8sb7Gvq1Kli4MCBIiEhQeTl5YkTJ06IGTNmiLCw\nMHHhwgUhhBArVqwQQUFBorq62vieP/3pT+KZZ54Rp06dEteuXROHDx8WQUFB4v333xfFxcXG77R/\n//4iMTFR5OTkiP/9738iOTlZ/N///Z+4ePGiEEKIZcuWicGDB4v9+/eL8+fPi4MHD4qHH35YPPHE\nE40ee0Pfa1pamggNDRUbNmwQhYWFYv/+/WLEiBHioYceEjqdrtHvcuLEieK+++4TP/zwg/jll1/E\nq6++KgYNGmRyvNu2bRNBQUHi1VdfFadPnxY//vijiIuLE2PHjhV6vV4IIcR///tfERQUJJYvXy6K\niorEr7/+KlJSUkRERITxWC9evGjsp7i4WNTW1hq/25kzZ4qsrCxx6tQpsWjRIhEUFCRyc3OFEEJ8\n/PHHIiIiQnz99dfi3LlzIi8vT8THx4u4uLhGj4voThggiJpJYwHi0qVL4q9//asICQkRx44dM9le\nVFRksu8bb7whIiIihMFgEEJc/4c/KChIqFQq4z43B4icnBwRFBQkvvzyS5N+3nzzTRESEtLoSX/H\njh0iKChI5Ofnm2yfP3++GDx48B2P6WZTp04VvXv3FleuXDFuu3DhgggKChLr1q0TQjQcIEJCQsS1\na9eM7yksLBRBQUFiy5YtJp8fFBQk/ve//xm31R3z119/LYQQIjExsV5YuHDhgjhx4kSjNd/6vdbU\n1IiIiAixePFik/327dsngoKCRFZWVoP91NX88ccfm2z/85//bHK8DzzwgJg6darJPgcPHhRBQUFi\nz549QgghysvLxc8//2wSVn755RcRFBQkdu7cKYQQQqfTiaCgILFixQrjPnXfbV0/QvwWNOq+/7//\n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\nRK5CYII=\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "for birth_rate in birth_rates:\n", - " for death_rate in death_rates:\n", - " system = make_system(birth_rate=birth_rate,\n", - " death_rate=death_rate)\n", - " run_simulation(system)\n", - " p_end = final_population(system)\n", - " birth_ratio = birth_rate / death_rate\n", - " plot(birth_ratio, p_end, 'y>', label='rabbits')\n", - " \n", - "decorate(xlabel='Ratio of births to deaths',\n", - " ylabel='Final population')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.1" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/code/rabbits2.ipynb b/code/rabbits2.ipynb deleted file mode 100644 index f77cb8cf8..000000000 --- a/code/rabbits2.ipynb +++ /dev/null @@ -1,399 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Rabbit example\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Rabbit Redux\n", - "\n", - "This notebook starts with a version of the rabbit population growth model and walks through some steps for extending it.\n", - "\n", - "In the original model, we treat all rabbits as adults; that is, we assume that a rabbit is able to breed in the season after it is born. In this notebook, we extend the model to include both juvenile and adult rabbits.\n", - "\n", - "As an example, let's assume that rabbits take 3 seasons to mature. We could model that process explicitly by counting the number of rabbits that are 1, 2, or 3 seasons old. As an alternative, we can model just two stages, juvenile and adult. In the simpler model, the maturation rate is 1/3 of the juveniles per season.\n", - "\n", - "To implement this model, make these changes in the System object:\n", - "\n", - "0. Before you make any changes, run all cells and confirm your understand them.\n", - "\n", - "1. Then, add a second initial populations: `juvenile_pop0`, with value `0`.\n", - "\n", - "2. Add an additional variable, `mature_rate`, with the value `0.33`." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
t00.0
t_end10.0
adult_pop010.0
birth_rate0.9
death_rate0.5
\n", - "
" - ], - "text/plain": [ - "t0 0.0\n", - "t_end 10.0\n", - "adult_pop0 10.0\n", - "birth_rate 0.9\n", - "death_rate 0.5\n", - "dtype: float64" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = System(t0 = 0, \n", - " t_end = 10,\n", - " adult_pop0 = 10,\n", - " birth_rate = 0.9,\n", - " death_rate = 0.5)\n", - "\n", - "system" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now update `run_simulation` with the following changes:\n", - "\n", - "1. Add a second TimeSeries, named `juveniles`, to keep track of the juvenile population, and initialize it with `juvenile_pop0`.\n", - "\n", - "2. Inside the for loop, compute the number of juveniles that mature during each time step.\n", - "\n", - "3. Also inside the for loop, add a line that stores the number of juveniles in the new `TimeSeries`. For simplicity, let's assume that only adult rabbits die.\n", - "\n", - "4. During each time step, subtract the number of maturations from the juvenile population and add it to the adult population.\n", - "\n", - "5. After the for loop, store the `juveniles` `TimeSeries` as a variable in `System`." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def run_simulation(system):\n", - " \"\"\"Runs a proportional growth model.\n", - " \n", - " Adds TimeSeries to `system` as `results`.\n", - " \n", - " system: System object with t0, t_end, p0,\n", - " birth_rate and death_rate\n", - " \"\"\"\n", - " adults = TimeSeries()\n", - " adults[system.t0] = system.adult_pop0\n", - " \n", - " for t in linrange(system.t0, system.t_end):\n", - " births = system.birth_rate * adults[t]\n", - " deaths = system.death_rate * adults[t]\n", - " \n", - " adults[t+1] = adults[t] + births - deaths\n", - " \n", - " system.adults = adults" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your changes in `run_simulation`:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
010.000000
114.000000
219.600000
327.440000
438.416000
553.782400
675.295360
7105.413504
8147.578906
9206.610468
10289.254655
11404.956517
\n", - "
" - ], - "text/plain": [ - "0 10.000000\n", - "1 14.000000\n", - "2 19.600000\n", - "3 27.440000\n", - "4 38.416000\n", - "5 53.782400\n", - "6 75.295360\n", - "7 105.413504\n", - "8 147.578906\n", - "9 206.610468\n", - "10 289.254655\n", - "11 404.956517\n", - "dtype: float64" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run_simulation(system)\n", - "system.adults" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, update `plot_results` to plot both the adult and juvenile `TimeSeries`." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def plot_results(system, title=None):\n", - " \"\"\"Plot the estimates and the model.\n", - " \n", - " system: System object with `results`\n", - " \"\"\"\n", - " newfig()\n", - " plot(system.adults, 'bo-', label='adults')\n", - " decorate(xlabel='Season', \n", - " ylabel='Rabbit population',\n", - " title=title)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And test your updated version of `plot_results`." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_results(system, title='Proportional growth model')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This notebook demonstrates the steps we recommend for starting your project:\n", - "\n", - "1. Start with one of the examples from the book, either by copying a notebook or pasting code into a new notebook. Get the code working before you make any changes.\n", - "\n", - "2. Make one small change, and run the code again.\n", - "\n", - "3. Repeat step 2 until you have a basic implementation of your model.\n", - "\n", - "If you start with working code that you understand and make small changes, you can avoid spending a lot of time debugging.\n", - "\n", - "One you have a basic model working, you can think about what metrics to measure, what parameters to sweep, and how to use the model to predict, explain, or design." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Bonus question\n", - "\n", - "Suppose you only have room for 30 adult rabbits. Whenever the adult population exceeds 30, you take any excess rabbits to market (as pets for kind children, of course). Modify `run_simulation` to model this strategy. What effect does it have on the behavior of the system? You might have to run for more than 10 seasons to see what happens." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.1" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/code/rabbits3.ipynb b/code/rabbits3.ipynb deleted file mode 100644 index 1aab70499..000000000 --- a/code/rabbits3.ipynb +++ /dev/null @@ -1,630 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Rabbit example\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Rabbit is Rich\n", - "\n", - "This notebook starts with a version of the rabbit population growth model. You will modify it using some of the tools in Chapter 5. Before you attempt this diagnostic, you should have a good understanding of State objects, as presented in Section 5.4. And you should understand the version of `run_simulation` in Section 5.7.\n", - "\n", - "### Separating the `State` from the `System`\n", - "\n", - "Here's the `System` object from the previous diagnostic. Notice that it includes system parameters, which don't change while the simulation is running, and population variables, which do. We're going to improve that by pulling the population variables into a `State` object." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
t00.00
t_end20.00
juvenile_pop00.00
adult_pop010.00
birth_rate0.90
mature_rate0.33
death_rate0.50
\n", - "
" - ], - "text/plain": [ - "t0 0.00\n", - "t_end 20.00\n", - "juvenile_pop0 0.00\n", - "adult_pop0 10.00\n", - "birth_rate 0.90\n", - "mature_rate 0.33\n", - "death_rate 0.50\n", - "dtype: float64" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = System(t0 = 0, \n", - " t_end = 20,\n", - " juvenile_pop0 = 0,\n", - " adult_pop0 = 10,\n", - " birth_rate = 0.9,\n", - " mature_rate = 0.33,\n", - " death_rate = 0.5)\n", - "\n", - "system" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the following cells, define a `State` object named `init` that contains two state variables, `juveniles` and `adults`, with initial values `0` and `10`. Make a version of the `System` object that does NOT contain `juvenile_pop0` and `adult_pop0`, but DOES contain `init`." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Updating `run_simulation`\n", - "\n", - "Here's the version of `run_simulation` from last time:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def run_simulation(system):\n", - " \"\"\"Runs a proportional growth model.\n", - " \n", - " Adds TimeSeries to `system` as `results`.\n", - " \n", - " system: System object\n", - " \"\"\"\n", - " juveniles = TimeSeries()\n", - " juveniles[system.t0] = system.juvenile_pop0\n", - " \n", - " adults = TimeSeries()\n", - " adults[system.t0] = system.adult_pop0\n", - " \n", - " for t in linrange(system.t0, system.t_end):\n", - " maturations = system.mature_rate * juveniles[t]\n", - " births = system.birth_rate * adults[t]\n", - " deaths = system.death_rate * adults[t]\n", - " \n", - " if adults[t] > 30:\n", - " market = adults[t] - 30\n", - " else:\n", - " market = 0\n", - " \n", - " juveniles[t+1] = juveniles[t] + births - maturations\n", - " adults[t+1] = adults[t] + maturations - deaths - market\n", - " \n", - " system.adults = adults\n", - " system.juveniles = juveniles" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the cell below, write a version of `run_simulation` that works with the new `System` object (the one that contains a `State` object named `init`).\n", - "\n", - "Hint: you only have to change two lines." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your changes in `run_simulation`:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
010.000000
15.000000
25.470000
36.209900
47.057723
58.021560
69.117031
710.362107
811.777219
913.385586
1015.213601
1117.291261
1219.652658
1322.336542
1425.386953
1528.853947
1632.794414
1734.478600
1836.487431
1937.893339
2039.401924
2140.546917
\n", - "
" - ], - "text/plain": [ - "0 10.000000\n", - "1 5.000000\n", - "2 5.470000\n", - "3 6.209900\n", - "4 7.057723\n", - "5 8.021560\n", - "6 9.117031\n", - "7 10.362107\n", - "8 11.777219\n", - "9 13.385586\n", - "10 15.213601\n", - "11 17.291261\n", - "12 19.652658\n", - "13 22.336542\n", - "14 25.386953\n", - "15 28.853947\n", - "16 32.794414\n", - "17 34.478600\n", - "18 36.487431\n", - "19 37.893339\n", - "20 39.401924\n", - "21 40.546917\n", - "dtype: float64" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run_simulation(system)\n", - "system.adults" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Plotting the results\n", - "\n", - "Here's a version of `plot_results` that plots both the adult and juvenile `TimeSeries`." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def plot_results(system, title=None):\n", - " \"\"\"Plot the estimates and the model.\n", - " \n", - " system: System object with `results`\n", - " \"\"\"\n", - " newfig()\n", - " plot(system.adults, 'bo-', label='adults')\n", - " plot(system.juveniles, 'gs-', label='juveniles')\n", - " decorate(xlabel='Season', \n", - " ylabel='Rabbit population',\n", - " title=title)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If your changes in the previous section were successful, you should be able to run this new version of `plot_results`." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/png": "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\n/vWvxMbG4u3tzZgxY4iPj+ejjz6yKf/xxx8TFxfHPffcg6+vLx06dODhhx/miy++qNlT3llKSkqI\nj49n9uzZ6PV6vL29mTRpEuXl5ezZs8epdWtqzfm62DNnzhxSUlLo37+/s6vSqMLCQlauXMmoUaNs\njn3wwQcMHjyYlJQUvLy86Nq1KxMmTOD999/HbDbblD98+DDbtm1jxowZREVF4e/vz5QpU9DpdHzx\nxReOOJ1Gz6eqqorp06fTv39/3N3d6du3LwMGDODnn392SN3OV2Pncr6cfW3O51w2bdpEamoqs2bN\navJ6na9zfQa3lPeMSwT3ffv2YTQa6dmzp9XjvXr1Ii0tzab87t276dWrl01Zk8nEvn37mrSu5xIY\nGMi8efOIj4+veSwzMxOAqKgou88pKyvjkUceYeDAgQwaNIjZs2fbbJvrTBs2bODPf/4zffv25dZb\nb2XTpk12yzXn61Lff//7X3755RemTZvWYJnmcl1Gjx5tdwgEGv6dFxYWkpGRYVM+LS0NT09PLrvs\nsprHPDw86N69u933WlNo7Hz+8pe/cOedd9b8rGka2dnZtG3bttHXfP/997nuuutITk7mrrvuYseO\nHZe0zg1p7FxA5Q+/9957ueKKKxg6dCgvvfQSBoPBbllnX5tznYuFwWDgueee44knniAoKKjRss64\nLuf6DG4p7xmXCO6WVl1ISIjV46GhoeTl5dktHxwcbFMWsFvemUpLS5k1axbXXnutzZcXUPWOj49n\n3LhxbNmyhSVLlrBr1y6mT5/uhNraSkhIIC4ujg8++IAffviB6667jilTpths6wst57qYzWYWLlzI\nAw88QEBAgN0yzf26WDT2O7fXW2IpX38cNCQkpFldI4vXX3+dkydP2p0vYdG9e3e6d+/O2rVr+eab\nb+jatSv33XcfWVlZDqyprTZt2tCxY0cef/xxtm7dyksvvcS6dets5ndYtJRrs2LFCkJCQrjxxhsb\nLddcrkv9z+CW8p5xieDemPq/0EtdvillZ2dz1113ER4ezoIFC+yWGTJkCB9++CEDBw7Ew8ODxMRE\npk2bxubNm8nJyXFwjW299dZbzJo1i7CwMAICAnjooYdITEzkk08+Oa/XaU7XZePGjZw+fbrRzYqa\n+3VpCs3pGlVXVzN37lzef/99lixZYjXhrr41a9bw0EMPERAQQGhoKE899RT+/v58/vnnDqyxrTvv\n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\nrkJggg==\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_results(system, title='Proportional growth model')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "That's the end of the diagnostic. If you were able to get it done quickly, and you would like a challenge, here are two bonus questions:\n", - "\n", - "\n", - "### Bonus question #1\n", - "\n", - "Write a version of `run_simulation` that puts the results into a single `TimeFrame` named `results`, rather than two `TimeSeries` objects.\n", - "\n", - "Write a version of `plot_results` that can plot the results in this form.\n", - "\n", - "WARNING: This question is substantially harder, and requires you to have a good understanding of everything in Chapter 5. We don't expect most people to be able to do this exercise at this point." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "run_simulation(system)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\nSxe2bdvGb37zGz755BPCwq6+XXzmjpkAZJVkkZJXv9JLn4A+dNV3vWH5FkIIIWyFTT25FxYWsmPH\nDn7/+98TGhqKq6srkyZNIjw8nPfff/+az1tcVcyZvDOm18FewQR5Bt2ILAshhBA2x6aC+9GjR6mu\nrmbAgAFm+wcOHEhSUlITR11ean4qdagFYbycvejl1+u68imEEELYMpsK7nl5eQD4+vqa7ffz8yM3\nN/eaz1ur1QLgpHMi0hApE9UIIYSwazbX5t6U6+nRHhkYSW55LgHuAdKBTgghhN2zqUfYgIAAAAoK\nCsz25+fnExgYeM3ndXNyo6t3VwnsQgghOgSbCu5RUVG4uLhw+PBhs/0HDx4kNja2jXIlhBBCtC82\nVS3v7e3N+PHjSUxMJCIiguDgYDZt2kRmZiaTJk26pnMuH7v8BudSCCGEsG02FdwB5s+fz5IlS5g8\neTKlpaVERkaycuVKunZtekx6ba3qMHfhwoXWyqYQQgjRZozxzhj/GtNpmqa1ZoZawv79+3nkkUfa\nOhtCCCFEq9q4caPVZmu7CO4VFRX8+OOPGAwGHB0d2zo7QgghRIuqra0lJyeHqKgo3NwsO4vbRXAX\nQgghRD2b6i0vhBBCiOsnwV0IIYSwMxLchRBCCDsjwV0IIYSwMxLchRBCCDtjc5PYXKvy8nIWL17M\nnj17KCwspFevXjz11FPceuutVtN/++23JCYmcvr0aby9vbntttuYN28e7u7urZxzS7m5uSxdupSv\nv/6asrIyevXqxTPPPMPw4cMt0m7dupV58+bh4uJitj8uLo4lS5a0VpabNGrUKC5evIiDg/n3yO3b\ntxMaGmqR3lbLZd++fUyfPt1if01NDffddx9/+ctfzPbbWrmkp6czf/58fvjhB7744gu6detmeu9f\n//oXq1atIi0tDYPBQFxcHE899VSTw0rz8vJYtGgR+/bto7y8nMjISObMmUNUVFRrXU6z17Nx40Y2\nbtxIVlYWfn5+3HfffcyaNcvib9CoT58+ODs7WyxOdeDAAYvyawlNXUtiYiJ///vfcXZ2Nkv/2GOP\n8fTTT1s9V1uXTVPXcvfdd3P+/HmztJqmUV1dzcmTJ62eqy3L5XL34HbxmdHsxNy5c7V7771XO3v2\nrFZRUaFt3rxZi4qK0s6cOWORNjU1VYuKitLWr1+vlZWVaT/99JN2//33a3Pnzm2DnFuaOHGiNn36\ndC07O1urqKjQli5dqt10003ahQsXLNJ+9NFH2siRI9sgl1dm5MiR2kcffXRFaW29XBrLzs7Whg4d\nqu3du9fiPVsql127dmnDhw/X5syZo0VERGjp6emm9/bu3av1799f27lzp1ZZWamdOHFC++Uvf6kl\nJiY2eb6pU6dq06ZN07KysrSSkhLtjTfe0IYOHarl5eW1xuU0ez2bN2/WYmJitL1792o1NTXa/v37\ntejoaG3t2rVNni8iIkL7/vvvWyPrFpq7ljfffFObMmXKVZ2vLcumuWux5plnnmn2s92W5dLcPbi9\nfGbsolq+sLCQHTt28Pvf/57Q0FBcXV2ZNGkS4eHhvP/++xbpP/jgA8LCwpg6dSru7u50796d3/72\nt2zfvt20pnxbKS4uJjw8nPnz52MwGHB1deWJJ56grKyMI0eOtGneWpotl4s1CxYsIC4ujqFDh7Z1\nVppVUFDAxo0bGTdunMV7GzZsYMSIEcTFxeHi4kKfPn2YNm0a7733HnV1dRbpT506xd69e5kzZw7B\nwcF4enoya9YsdDod27dvb43LafZ6qqqqSEhIYOjQoTg6OhITE8OwYcP4/vvvWyVvV6u5a7labV02\nV3Mtu3fvZt++fcybN6/F83W1LncPbi+fGbsI7kePHqW6upoBAwaY7R84cCBJSUkW6Q8fPszAgQMt\n0tbU1HD06NEWzevleHt788orrxAeHm7al56eDkBwcLDVY0pLS/nd737H8OHDue2225g/f77Fsrlt\n6dNPP+VXv/oVMTExPPDAA+zevdtqOlsul8b++9//cvDgQWbPnt1kGlsplwkTJlhtAoGmf+cFBQWk\npaVZpE9KSsLZ2Zm+ffua9jk5OdG/f3+rn7WW0Nz1PProozz00EOm15qmkZmZSefOnZs953vvvced\nd95JbGwsDz/8MPv377+heW5Kc9cCav7wX//619x8882MGjWKxYsXU1FRYTVtW5fN5a7FqKKigpde\neonnnnsOvV7fbNq2KJfL3YPby2fGLoK78anO19fXbL+fnx+5ublW0/v4+FikBaymb0slJSXMmzeP\n22+/3eLLC6h8h4eHM2XKFL7++mtWrFjBoUOHSEhIaIPcWoqIiCAsLIwNGzbw1VdfceeddzJr1iyL\nZX2h/ZRLXV0dy5YtY8aMGXh5eVlNY+vlYtTc79xabYkxfeN2UF9fX5sqI6O///3vnD9/3mp/CaP+\n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\nTkSuQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_results(system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Bonus question #2\n", - "\n", - "Factor out the update function.\n", - "\n", - "1. Write a function called `update` that takes a `State` object and a `System` object and returns a new `State` object that represents the state of the system after one time step.\n", - "\n", - "2. Write a version of `run_simulation` that takes an update function as a parameter and uses it to compute the update.\n", - "\n", - "3. Run your new version of `run_simulation` and plot the results.\n", - "\n", - "WARNING: This question is substantially harder, and requires you to have a good understanding of everything in Chapter 5. We don't expect most people to be able to do this exercise at this point." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'update' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mrun_simulation\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msystem\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mupdate\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mNameError\u001b[0m: name 'update' is not defined" - ] - } - ], - "source": [ - "run_simulation(system, update)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "plot_results(system)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.1" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/code/soln/chap01soln.ipynb b/code/soln/chap01soln.ipynb deleted file mode 100644 index 123ddb595..000000000 --- a/code/soln/chap01soln.ipynb +++ /dev/null @@ -1,788 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 1\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Jupyter\n", - "\n", - "Welcome to Modeling and Simulation, welcome to Python, and welcome to Jupyter.\n", - "\n", - "This is a Jupyter notebook, which is a development environment where you can write and run Python code. Each notebook is divided into cells. Each cell contains either text (like this cell) or Python code.\n", - "\n", - "### Selecting and running cells\n", - "\n", - "To select a cell, click in the left margin next to the cell. You should see a blue frame surrounding the selected cell.\n", - "\n", - "To edit a code cell, click inside the cell. You should see a green frame around the selected cell, and you should see a cursor inside the cell.\n", - "\n", - "To edit a text cell, double-click inside the cell. Again, you should see a green frame around the selected cell, and you should see a cursor inside the cell.\n", - "\n", - "To run a cell, hold down SHIFT and press ENTER. \n", - "\n", - "* If you run a text cell, Jupyter typesets the text and displays the result.\n", - "\n", - "* If you run a code cell, it runs the Python code in the cell and displays the result, if any.\n", - "\n", - "To try it out, edit this cell, change some of the text, and then press SHIFT-ENTER to run it." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Adding and removing cells\n", - "\n", - "You can add and remove cells from a notebook using the buttons in the toolbar and the items in the menu, both of which you should see at the top of this notebook.\n", - "\n", - "Try the following exercises:\n", - "\n", - "1. From the Insert menu select \"Insert cell below\" to add a cell below this one. By default, you get a code cell, as you can see in the pulldown menu that says \"Code\".\n", - "\n", - "2. In the new cell, add a print statement like `print('Hello')`, and run it.\n", - "\n", - "3. Add another cell, select the new cell, and then click on the pulldown menu that says \"Code\" and select \"Markdown\". This makes the new cell a text cell.\n", - "\n", - "4. In the new cell, type some text, and then run it.\n", - "\n", - "5. Use the arrow buttons in the toolbar to move cells up and down.\n", - "\n", - "6. Use the cut, copy, and paste buttons to delete, add, and move cells.\n", - "\n", - "7. As you make changes, Jupyter saves your notebook automatically, but if you want to make sure, you can press the save button, which looks like a floppy disk from the 1990s.\n", - "\n", - "8. Finally, when you are done with a notebook, select \"Close and Halt\" from the File menu." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using the notebooks\n", - "\n", - "The notebooks for each chapter contain the code from the chapter along with addition examples, explanatory text, and exercises. I recommend you \n", - "\n", - "1. Read the chapter first to understand the concepts and vocabulary, \n", - "2. Run the notebook to review what you learned and see it in action, and then\n", - "3. Attempt the exercises.\n", - "\n", - "If you try to work through the notebooks without reading the book, you're gonna have a bad time. The notebooks contain some explanatory text, but it is probably not enough to make sense if you have not read the book. If you are working through a notebook and you get stuck, you might want to re-read (or read!) the corresponding section of the book." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Importing modsim\n", - "\n", - "The following cell imports `modsim`, which is a collection of functions we will use throughout the book. Whenever you start the notebook, you will have to run the following cell. It does three things:\n", - "\n", - "1. It uses a Jupyter \"magic command\" to specify whether figures should appear in the notebook, or pop up in a new window.\n", - "\n", - "2. It configures Jupyter to display some values that would otherwise be invisible. \n", - "\n", - "3. It imports everything defined in `modsim`.\n", - "\n", - "Select the following cell and press SHIFT-ENTER to run it." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "If this cell runs successfully, it produces no output other than this message.\n" - ] - } - ], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim library\n", - "from modsim import *\n", - "\n", - "print('If this cell runs successfully, it produces no output other than this message.')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The first time you run this on a new installation of Python, it might produce a warning message in pink. That's probably ok, but if you get a message that says `modsim.py depends on Python 3.7 features`, that means you have an older version of Python, and some features in `modsim.py` won't work correctly.\n", - "\n", - "If you need a newer version of Python, I recommend installing Anaconda. You'll find more information in the preface of the book." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## The penny myth\n", - "\n", - "The following cells contain code from the beginning of Chapter 1.\n", - "\n", - "`modsim` defines `UNITS`, which contains variables representing pretty much every unit you've ever heard of. It uses [Pint](https://pint.readthedocs.io/en/latest/), which is a Python library that provides tools for computing with units.\n", - "\n", - "The following lines create new variables named `meter` and `second`." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "meter" - ], - "text/latex": [ - "$meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "meter = UNITS.meter" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "second" - ], - "text/latex": [ - "$second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "second = UNITS.second" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To find out what other units are defined, type `UNITS.` (including the period) in the next cell and then press TAB. You should see a pop-up menu with a list of units." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a variable named `a` and give it the value of acceleration due to gravity." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "9.8 meter/second2" - ], - "text/latex": [ - "$9.8 \\frac{meter}{second^{2}}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = 9.8 * meter / second**2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create `t` and give it the value 4 seconds." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "4 second" - ], - "text/latex": [ - "$4 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t = 4 * second" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Compute the distance a penny would fall after `t` seconds with constant acceleration `a`. Notice that the units of the result are correct." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "78.4 meter" - ], - "text/latex": [ - "$78.4 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a * t**2 / 2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise**: Compute the velocity of the penny after `t` seconds. Check that the units of the result are correct." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "39.2 meter/second" - ], - "text/latex": [ - "$39.2 \\frac{meter}{second}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "a * t" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise**: Why would it be nonsensical to add `a` and `t`? What happens if you try?" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "# a + t" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The error messages you get from Python are big and scary, but if you read them carefully, they contain a lot of useful information.\n", - "\n", - "1. Start from the bottom and read up.\n", - "2. The last line usually tells you what type of error happened, and sometimes additional information.\n", - "3. The previous lines are a \"traceback\" of what was happening when the error occurred. The first section of the traceback shows the code you wrote. The following sections are often from Python libraries.\n", - "\n", - "In this example, you should get a `DimensionalityError`, which is defined by Pint to indicate that you have violated a rules of dimensional analysis: you cannot add quantities with different dimensions.\n", - "\n", - "Before you go on, you might want to delete the erroneous code so the notebook can run without errors." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Falling pennies\n", - "\n", - "Now let's solve the falling penny problem.\n", - "\n", - "Set `h` to the height of the Empire State Building:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "381 meter" - ], - "text/latex": [ - "$381 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "h = 381 * meter" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Compute the time it would take a penny to fall, assuming constant acceleration.\n", - "\n", - "$ a t^2 / 2 = h $\n", - "\n", - "$ t = \\sqrt{2 h / a}$" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "8.817885349720552 second" - ], - "text/latex": [ - "$8.817885349720552 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t = sqrt(2 * h / a)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Given `t`, we can compute the velocity of the penny when it lands.\n", - "\n", - "$v = a t$" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "86.41527642726142 meter/second" - ], - "text/latex": [ - "$86.41527642726142 \\frac{meter}{second}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v = a * t" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can convert from one set of units to another like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "hour" - ], - "text/latex": [ - "$hour$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "mile = UNITS.mile\n", - "hour= UNITS.hour" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "193.30546802805438 mile/hour" - ], - "text/latex": [ - "$193.30546802805438 \\frac{mile}{hour}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v.to(mile/hour)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Suppose you bring a 10 foot pole to the top of the Empire State Building and use it to drop the penny from `h` plus 10 feet.\n", - "\n", - "Define a variable named `foot` that contains the unit `foot` provided by `UNITS`. Define a variable named `pole_height` and give it the value 10 feet.\n", - "\n", - "What happens if you add `h`, which is in units of meters, to `pole_height`, which is in units of feet? What happens if you write the addition the other way around?" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "384.048 meter" - ], - "text/latex": [ - "$384.048 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "foot = UNITS.foot\n", - "pole_height = 10 * foot\n", - "\n", - "h + pole_height" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "1260.0 foot" - ], - "text/latex": [ - "$1260.0 foot$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "pole_height + h" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** In reality, air resistance limits the velocity of the penny. At about 18 m/s, the force of air resistance equals the force of gravity and the penny stops accelerating.\n", - "\n", - "As a simplification, let's assume that the acceleration of the penny is `a` until the penny reaches 18 m/s, and then 0 afterwards. What is the total time for the penny to fall 381 m?\n", - "\n", - "You can break this question into three parts:\n", - "\n", - "1. How long until the penny reaches 18 m/s with constant acceleration `a`.\n", - "2. How far would the penny fall during that time?\n", - "3. How long to fall the remaining distance with constant velocity 18 m/s?\n", - "\n", - "Suggestion: Assign each intermediate result to a variable with a meaningful name. And assign units to all quantities!" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "18.0 meter/second" - ], - "text/latex": [ - "$18.0 \\frac{meter}{second}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "v_terminal = 18 * meter / second " - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Time to reach terminal velocity 1.8367346938775508 second\n" - ] - } - ], - "source": [ - "# Solution\n", - "\n", - "t1 = v_terminal / a\n", - "print('Time to reach terminal velocity', t1)" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Height fallen in t1 16.530612244897956 meter\n" - ] - } - ], - "source": [ - "# Solution\n", - "\n", - "h1 = a * t1**2 / 2\n", - "print('Height fallen in t1', h1)" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Time to fall remaining distance 20.24829931972789 second\n" - ] - } - ], - "source": [ - "# Solution\n", - "\n", - "t2 = (h - h1) / v_terminal\n", - "print('Time to fall remaining distance', t2)" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Total falling time 22.085034013605444 second\n" - ] - } - ], - "source": [ - "# Solution\n", - "\n", - "t_total = t1 + t2\n", - "print('Total falling time', t_total)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Restart and run all\n", - "\n", - "When you change the contents of a cell, you have to run it again for those changes to have an effect. If you forget to do that, the results can be confusing, because the code you are looking at is not the code you ran.\n", - "\n", - "If you ever lose track of which cells have run, and in what order, you should go to the Kernel menu and select \"Restart & Run All\". Restarting the kernel means that all of your variables get deleted, and running all the cells means all of your code will run again, in the right order.\n", - "\n", - "**Exercise:** Select \"Restart & Run All\" now and confirm that it does what you want." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap02soln.ipynb b/code/soln/chap02soln.ipynb deleted file mode 100644 index 2fc0e51ad..000000000 --- a/code/soln/chap02soln.ipynb +++ /dev/null @@ -1,2082 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 2\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim library\n", - "from modsim import *\n", - "\n", - "# set the random number generator\n", - "np.random.seed(7)\n", - "\n", - "# If this cell runs successfully, it produces no output." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Modeling a bikeshare system" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll start with a `State` object that represents the number of bikes at each station.\n", - "\n", - "When you display a `State` object, it lists the state variables and their values:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin10
wellesley2
\n", - "
" - ], - "text/plain": [ - "olin 10\n", - "wellesley 2\n", - "dtype: int64" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bikeshare = State(olin=10, wellesley=2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can access the state variables using dot notation." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "10" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bikeshare.olin" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "2" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bikeshare.wellesley" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** What happens if you spell the name of a state variable wrong? Edit the previous cell, change the spelling of `wellesley`, and run the cell again.\n", - "\n", - "The error message uses the word \"attribute\", which is another name for what we are calling a state variable. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Add a third attribute called `babson` with initial value 0, and display the state of `bikeshare` again." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Updating\n", - "\n", - "We can use the update operators `+=` and `-=` to change state variables." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "bikeshare.olin -= 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we display `bikeshare`, we should see the change." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin9
wellesley2
\n", - "
" - ], - "text/plain": [ - "olin 9\n", - "wellesley 2\n", - "dtype: int64" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Of course, if we subtract a bike from `olin`, we should add it to `wellesley`." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin9
wellesley3
\n", - "
" - ], - "text/plain": [ - "olin 9\n", - "wellesley 3\n", - "dtype: int64" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bikeshare.wellesley += 1\n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Functions\n", - "\n", - "We can take the code we've written so far and encapsulate it in a function." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def bike_to_wellesley():\n", - " bikeshare.olin -= 1\n", - " bikeshare.wellesley += 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When you define a function, it doesn't run the statements inside the function, yet. When you call the function, it runs the statements inside." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin8
wellesley4
\n", - "
" - ], - "text/plain": [ - "olin 8\n", - "wellesley 4\n", - "dtype: int64" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bike_to_wellesley()\n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "One common error is to omit the parentheses, which has the effect of looking up the function, but not calling it." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bike_to_wellesley" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The output indicates that `bike_to_wellesley` is a function defined in a \"namespace\" called `__main__`, but you don't have to understand what that means." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Define a function called `bike_to_olin` that moves a bike from Wellesley to Olin. Call the new function and display `bikeshare` to confirm that it works." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def bike_to_olin():\n", - " bikeshare.wellesley -= 1\n", - " bikeshare.olin += 1" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin9
wellesley3
\n", - "
" - ], - "text/plain": [ - "olin 9\n", - "wellesley 3\n", - "dtype: int64" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "bike_to_olin()\n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Conditionals" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`modsim.py` provides `flip`, which takes a probability and returns either `True` or `False`, which are special values defined by Python.\n", - "\n", - "The Python function `help` looks up a function and displays its documentation." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Help on function flip in module modsim:\n", - "\n", - "flip(p=0.5)\n", - " Flips a coin with the given probability.\n", - " \n", - " p: float 0-1\n", - " \n", - " returns: boolean (True or False)\n", - "\n" - ] - } - ], - "source": [ - "help(flip)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the following example, the probability is 0.7 or 70%. If you run this cell several times, you should get `True` about 70% of the time and `False` about 30%." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "flip(0.7)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the following example, we use `flip` as part of an if statement. If the result from `flip` is `True`, we print `heads`; otherwise we do nothing." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "if flip(0.7):\n", - " print('heads')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With an else clause, we can print heads or tails depending on whether `flip` returns `True` or `False`." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "heads\n" - ] - } - ], - "source": [ - "if flip(0.7):\n", - " print('heads')\n", - "else:\n", - " print('tails')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step\n", - "\n", - "Now let's get back to the bikeshare state. Again let's start with a new `State` object." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin10
wellesley2
\n", - "
" - ], - "text/plain": [ - "olin 10\n", - "wellesley 2\n", - "dtype: int64" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bikeshare = State(olin=10, wellesley=2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Suppose that in any given minute, there is a 50% chance that a student picks up a bike at Olin and rides to Wellesley. We can simulate that like this." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin10
wellesley2
\n", - "
" - ], - "text/plain": [ - "olin 10\n", - "wellesley 2\n", - "dtype: int64" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "if flip(0.5):\n", - " bike_to_wellesley()\n", - " print('Moving a bike to Wellesley')\n", - "\n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And maybe at the same time, there is also a 40% chance that a student at Wellesley rides to Olin." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin10
wellesley2
\n", - "
" - ], - "text/plain": [ - "olin 10\n", - "wellesley 2\n", - "dtype: int64" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "if flip(0.4):\n", - " bike_to_olin()\n", - " print('Moving a bike to Olin')\n", - "\n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can wrap that code in a function called `step` that simulates one time step. In any given minute, a student might ride from Olin to Wellesley, from Wellesley to Olin, or both, or neither, depending on the results of `flip`." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "def step():\n", - " if flip(0.5):\n", - " bike_to_wellesley()\n", - " print('Moving a bike to Wellesley')\n", - " \n", - " if flip(0.4):\n", - " bike_to_olin()\n", - " print('Moving a bike to Olin')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Since this function takes no parameters, we call it like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin10
wellesley2
\n", - "
" - ], - "text/plain": [ - "olin 10\n", - "wellesley 2\n", - "dtype: int64" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "step()\n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameters\n", - "\n", - "As defined in the previous section, `step` is not as useful as it could be, because the probabilities `0.5` and `0.4` are \"hard coded\".\n", - "\n", - "It would be better to generalize this function so it takes the probabilities `p1` and `p2` as parameters:" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "def step(p1, p2):\n", - " if flip(p1):\n", - " bike_to_wellesley()\n", - " print('Moving a bike to Wellesley')\n", - " \n", - " if flip(p2):\n", - " bike_to_olin()\n", - " print('Moving a bike to Olin')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can call it like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Moving a bike to Wellesley\n", - "Moving a bike to Olin\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin10
wellesley2
\n", - "
" - ], - "text/plain": [ - "olin 10\n", - "wellesley 2\n", - "dtype: int64" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "step(0.5, 0.4)\n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** At the beginning of `step`, add a print statement that displays the values of `p1` and `p2`. Call it again with values `0.3`, and `0.2`, and confirm that the values of the parameters are what you expect. " - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.3 0.2\n" - ] - } - ], - "source": [ - "# Solution\n", - "\n", - "def step(p1, p2):\n", - " print(p1, p2)\n", - " if flip(p1):\n", - " bike_to_wellesley()\n", - " print('Moving a bike to Wellesley')\n", - " \n", - " if flip(p2):\n", - " bike_to_olin()\n", - " print('Moving a bike to Olin')\n", - " \n", - "step(0.3, 0.2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## For loop" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Before we go on, I'll redefine `step` without the print statements." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "def step(p1, p2):\n", - " if flip(p1):\n", - " bike_to_wellesley()\n", - " \n", - " if flip(p2):\n", - " bike_to_olin()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And let's start again with a new `State` object:" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin10
wellesley2
\n", - "
" - ], - "text/plain": [ - "olin 10\n", - "wellesley 2\n", - "dtype: int64" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bikeshare = State(olin=10, wellesley=2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use a `for` loop to move 4 bikes from Olin to Wellesley." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin6
wellesley6
\n", - "
" - ], - "text/plain": [ - "olin 6\n", - "wellesley 6\n", - "dtype: int64" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "for i in range(4):\n", - " bike_to_wellesley()\n", - " \n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Or we can simulate 4 random time steps." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin5
wellesley7
\n", - "
" - ], - "text/plain": [ - "olin 5\n", - "wellesley 7\n", - "dtype: int64" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "for i in range(4):\n", - " step(0.3, 0.2)\n", - " \n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If each step corresponds to a minute, we can simulate an entire hour like this." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin4
wellesley8
\n", - "
" - ], - "text/plain": [ - "olin 4\n", - "wellesley 8\n", - "dtype: int64" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "for i in range(60):\n", - " step(0.3, 0.2)\n", - "\n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After 60 minutes, you might see that the number of bike at Olin is negative. We'll fix that problem in the next notebook.\n", - "\n", - "But first, we want to plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## TimeSeries\n", - "\n", - "`modsim.py` provides an object called a `TimeSeries` that can contain a sequence of values changing over time.\n", - "\n", - "We can create a new, empty `TimeSeries` like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
\n", - "
" - ], - "text/plain": [ - "Series([], dtype: float64)" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results = TimeSeries()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we can add a value to the `TimeSeries` like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
04
\n", - "
" - ], - "text/plain": [ - "0 4\n", - "dtype: int64" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results[0] = bikeshare.olin\n", - "results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `0` in brackets is an `index` that indicates that this value is associated with time step 0.\n", - "\n", - "Now we'll use a for loop to save the results of the simulation. I'll start one more time with a new `State` object." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin10
wellesley2
\n", - "
" - ], - "text/plain": [ - "olin 10\n", - "wellesley 2\n", - "dtype: int64" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bikeshare = State(olin=10, wellesley=2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a for loop that runs 10 steps and stores the results." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "for i in range(10):\n", - " step(0.3, 0.2)\n", - " results[i] = bikeshare.olin" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can display the results." - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
010
110
29
39
410
510
610
711
811
911
\n", - "
" - ], - "text/plain": [ - "0 10\n", - "1 10\n", - "2 9\n", - "3 9\n", - "4 10\n", - "5 10\n", - "6 10\n", - "7 11\n", - "8 11\n", - "9 11\n", - "dtype: int64" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `TimeSeries` is a specialized version of a Pandas `Series`, so we can use any of the functions provided by `Series`, including several that compute summary statistics:" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "10.1" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results.mean()" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "count 10.000000\n", - "mean 10.100000\n", - "std 0.737865\n", - "min 9.000000\n", - "25% 10.000000\n", - "50% 10.000000\n", - "75% 10.750000\n", - "max 11.000000\n", - "dtype: float64" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results.describe()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can read the documentation of `Series` [here](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.html)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Plotting\n", - "\n", - "We can also plot the results like this." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap01-fig01.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results, label='Olin')\n", - "\n", - "decorate(title='Olin-Wellesley Bikeshare',\n", - " xlabel='Time step (min)', \n", - " ylabel='Number of bikes')\n", - "\n", - "savefig('figs/chap01-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`decorate`, which is defined in the `modsim` library, adds a title and labels the axes." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Help on function decorate in module modsim:\n", - "\n", - "decorate(**options)\n", - " Decorate the current axes.\n", - " \n", - " Call decorate with keyword arguments like\n", - " \n", - " decorate(title='Title',\n", - " xlabel='x',\n", - " ylabel='y')\n", - " \n", - " The keyword arguments can be any of the axis properties\n", - " \n", - " https://matplotlib.org/api/axes_api.html\n", - " \n", - " In addition, you can use `legend=False` to suppress the legend.\n", - " \n", - " And you can use `loc` to indicate the location of the legend\n", - " (the default value is 'best')\n", - "\n" - ] - } - ], - "source": [ - "help(decorate)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`savefig()` saves a figure in a file." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Help on function savefig in module modsim:\n", - "\n", - "savefig(filename, **options)\n", - " Save the current figure.\n", - " \n", - " Keyword arguments are passed along to plt.savefig\n", - " \n", - " https://matplotlib.org/api/_as_gen/matplotlib.pyplot.savefig.html\n", - " \n", - " filename: string\n", - "\n" - ] - } - ], - "source": [ - "help(savefig)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The suffix of the filename indicates the format you want. This example saves the current figure in a PDF file named `chap01-fig01.pdf`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Wrap the code from this section in a function named `run_simulation` that takes three parameters, named `p1`, `p2`, and `num_steps`.\n", - "\n", - "It should:\n", - "\n", - "1. Create a `TimeSeries` object to hold the results.\n", - "2. Use a for loop to run `step` the number of times specified by `num_steps`, passing along the specified values of `p1` and `p2`.\n", - "3. After each step, it should save the number of bikes at Olin in the `TimeSeries`.\n", - "4. After the for loop, it should plot the results and\n", - "5. Decorate the axes.\n", - "\n", - "To test your function:\n", - "\n", - "1. Create a `State` object with the initial state of the system.\n", - "2. Call `run_simulation` with appropriate parameters.\n", - "3. Save the resulting figure.\n", - "\n", - "Optional:\n", - "\n", - "1. Extend your solution so it creates two `TimeSeries` objects, keeps track of the number of bikes at Olin *and* at Wellesley, and plots both series at the end." - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def run_simulation(p1, p2, num_steps):\n", - " olin = TimeSeries()\n", - " wellesley = TimeSeries()\n", - " \n", - " for i in range(num_steps):\n", - " step(p1, p2)\n", - " olin[i] = bikeshare.olin\n", - " wellesley[i] = bikeshare.wellesley\n", - " \n", - " plot(olin, label='Olin')\n", - " plot(wellesley, label='Wellesley')\n", - " decorate(title='Olin-Wellesley Bikeshare',\n", - " xlabel='Time step (min)', \n", - " ylabel='Number of bikes')" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "bikeshare = State(olin=10, wellesley=2)\n", - "run_simulation(0.3, 0.2, 60)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Opening the hood\n", - "\n", - "The functions in `modsim.py` are built on top of several widely-used Python libraries, especially NumPy, SciPy, and Pandas. These libraries are powerful but can be hard to use. The intent of `modsim.py` is to give you the power of these libraries while making it easy to get started.\n", - "\n", - "In the future, you might want to use these libraries directly, rather than using `modsim.py`. So we will pause occasionally to open the hood and let you see how `modsim.py` works.\n", - "\n", - "You don't need to know anything in these sections, so if you are already feeling overwhelmed, you might want to skip them. But if you are curious, read on." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Pandas\n", - "\n", - "This chapter introduces two objects, `State` and `TimeSeries`. Both are based on the `Series` object defined by Pandas, which is a library primarily used for data science.\n", - "\n", - "You can read the documentation of the `Series` object [here](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.html)\n", - "\n", - "The primary differences between `TimeSeries` and `Series` are:\n", - "\n", - "1. I made it easier to create a new, empty `Series` while avoiding a [confusing inconsistency](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.html).\n", - "\n", - "2. I provide a function so the `Series` looks good when displayed in Jupyter.\n", - "\n", - "3. I provide a function called `set` that we'll use later.\n", - "\n", - "`State` has all of those capabilities; in addition, it provides an easier way to initialize state variables, and it provides functions called `T` and `dt`, which will help us avoid a confusing error later." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Pyplot\n", - "\n", - "The `plot` function in `modsim.py` is based on the `plot` function in Pyplot, which is part of Matplotlib. You can read the documentation of `plot` [here](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.plot.html).\n", - "\n", - "`decorate` provides a convenient way to call the `pyplot` functions `title`, `xlabel`, and `ylabel`, and `legend`. It also avoids an annoying warning message if you try to make a legend when you don't have any labelled lines." - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Help on function decorate in module modsim:\n", - "\n", - "decorate(**options)\n", - " Decorate the current axes.\n", - " \n", - " Call decorate with keyword arguments like\n", - " \n", - " decorate(title='Title',\n", - " xlabel='x',\n", - " ylabel='y')\n", - " \n", - " The keyword arguments can be any of the axis properties\n", - " \n", - " https://matplotlib.org/api/axes_api.html\n", - " \n", - " In addition, you can use `legend=False` to suppress the legend.\n", - " \n", - " And you can use `loc` to indicate the location of the legend\n", - " (the default value is 'best')\n", - "\n" - ] - } - ], - "source": [ - "help(decorate)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### NumPy\n", - "\n", - "The `flip` function in `modsim.py` uses NumPy's `random` function to generate a random number between 0 and 1.\n", - "\n", - "You can get the source code for `flip` by running the following cell." - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[0;32mdef\u001b[0m \u001b[0mflip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0.5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Flips a coin with the given probability.\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m p: float 0-1\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m returns: boolean (True or False)\u001b[0m\n", - "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrandom\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrandom\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0mp\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "%psource flip" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap03soln.ipynb b/code/soln/chap03soln.ipynb deleted file mode 100644 index 961b849a2..000000000 --- a/code/soln/chap03soln.ipynb +++ /dev/null @@ -1,1272 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 3\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim library\n", - "from modsim import *\n", - "\n", - "# set the random number generator\n", - "np.random.seed(7)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## More than one State object\n", - "\n", - "Here's the code from the previous chapter, with two changes:\n", - "\n", - "1. I've added DocStrings that explain what each function does, and what parameters it takes.\n", - "\n", - "2. I've added a parameter named `state` to the functions so they work with whatever `State` object we give them, instead of always using `bikeshare`. That makes it possible to work with more than one `State` object." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "def step(state, p1, p2):\n", - " \"\"\"Simulate one minute of time.\n", - " \n", - " state: bikeshare State object\n", - " p1: probability of an Olin->Wellesley customer arrival\n", - " p2: probability of a Wellesley->Olin customer arrival\n", - " \"\"\"\n", - " if flip(p1):\n", - " bike_to_wellesley(state)\n", - " \n", - " if flip(p2):\n", - " bike_to_olin(state)\n", - " \n", - "def bike_to_wellesley(state):\n", - " \"\"\"Move one bike from Olin to Wellesley.\n", - " \n", - " state: bikeshare State object\n", - " \"\"\"\n", - " state.olin -= 1\n", - " state.wellesley += 1\n", - " \n", - "def bike_to_olin(state):\n", - " \"\"\"Move one bike from Wellesley to Olin.\n", - " \n", - " state: bikeshare State object\n", - " \"\"\"\n", - " state.wellesley -= 1\n", - " state.olin += 1\n", - " \n", - "def decorate_bikeshare():\n", - " \"\"\"Add a title and label the axes.\"\"\"\n", - " decorate(title='Olin-Wellesley Bikeshare',\n", - " xlabel='Time step (min)', \n", - " ylabel='Number of bikes')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's `run_simulation`, which is a solution to the exercise at the end of the previous notebook." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(state, p1, p2, num_steps):\n", - " \"\"\"Simulate the given number of time steps.\n", - " \n", - " state: State object\n", - " p1: probability of an Olin->Wellesley customer arrival\n", - " p2: probability of a Wellesley->Olin customer arrival\n", - " num_steps: number of time steps\n", - " \"\"\"\n", - " results = TimeSeries() \n", - " for i in range(num_steps):\n", - " step(state, p1, p2)\n", - " results[i] = state.olin\n", - " \n", - " plot(results, label='Olin')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can create more than one `State` object:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin10
wellesley2
\n", - "
" - ], - "text/plain": [ - "olin 10\n", - "wellesley 2\n", - "dtype: int64" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bikeshare1 = State(olin=10, wellesley=2)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin2
wellesley10
\n", - "
" - ], - "text/plain": [ - "olin 2\n", - "wellesley 10\n", - "dtype: int64" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bikeshare2 = State(olin=2, wellesley=10)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Whenever we call a function, we indicate which `State` object to work with:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "bike_to_olin(bikeshare1)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "bike_to_wellesley(bikeshare2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And you can confirm that the different objects are getting updated independently:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin11
wellesley1
\n", - "
" - ], - "text/plain": [ - "olin 11\n", - "wellesley 1\n", - "dtype: int64" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bikeshare1" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin1
wellesley11
\n", - "
" - ], - "text/plain": [ - "olin 1\n", - "wellesley 11\n", - "dtype: int64" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bikeshare2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Negative bikes" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the code we have so far, the number of bikes at one of the locations can go negative, and the number of bikes at the other location can exceed the actual number of bikes in the system.\n", - "\n", - "If you run this simulation a few times, it happens often." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAbQAAAEYCAYAAAA06gPTAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvNQv5yAAAIABJREFUeJzt3Xd4XGeV+PHvqFiSJduSJVmy4ybb0nHcZKcTSCGFhbChJOwuECAmELKBsKGEFkILoYUEsoRQFpJNCFk2u0sWWNovIaSROD2W+7Fc5CrZai7qZeb3x3sljUYjaSRN0+h8nmce6773zp1zpfGced/7Fl8gEMAYY4yZ7NISHYAxxhgTDZbQjDHGpARLaMYYY1KCJTRjjDEpwRKaMcaYlGAJzRhjTEqwhGaSloisF5GeoO0LRSQgIvMTGddoROR+EflL0PZXRWRXlF+jRkRuieY5JyI0nnjE570X3hfL1zCTS0aiAzBTj4iUALcAlwPzgOPAU8BtqrpxhKc+B8wFjk7w9b8OXKuqpUFlaUAD0DVM+d2q+pWJvO5kJCJPAhcEFTUDrwG3qOqGoPIzgbY4hmbMEFZDM3ElIguAl4FzgeuBZcBbgW7geRF583DPVdUuVa1TVf8Ew/gLUCIiq4LKTsP9f8gOU14APD7B15zM/gP3RWIu8EagCfiTiOT1HaCq9aramqD4okpEpiU6BjM+VkMz8XYPkAm8UVVPeGX7gfeIyB+B+0WkTFXbQ58oIhcCTwALVPVg0PabgC8BZwA1wCdV9f+NEMMGXG3iYmCLV3YRrpaYFqa8FXg+KI53A58HlgN1wCPAl8bygS4ilwJfxSXMJuBR4CZVbfT2rwS+B5yN+33tB76pqg8Oc74MXK33alzi2Q38QFV/6u1/AJirqm8Ked4TwD5VXT9CuO2qWuf9XCcitwLvAspxtTVEpAb4uareNkx8lwC/Bm5V1Tuj+DuYKSIPAu8AjuFq0rcHve57gRtxf6tu4AXc+2Ont38xsBd4H3AVrjb6Y+AmEVkGfAf3fggALwGfVtXNI/yuTAJZDc3EjYgU4GpjPwxKZsG+BZQAl47x1HcA3wQqcbW/h0Ukf7iDVbUL+Bvug6rPRcBfvUdo+TPecxCR9bgPvDuBFcAHgEuAn0QarIhcBPwW+E9gDe7DeDHwvyLi8w77FdCIq8muBj6Fa+4bzs+BK4DrgFOBW4HviMiHvP0/AS4RkbKgOJbiPsB/NobYpwPrcc2w1RE+5yrgN8D1QcksWr+DrwBPA2uB73rX/Mag/VnA13FJ81KgF/hDmFrYd3A10dXAPV6z+N9wzdvnAecACjwpIsWRXLeJP6uhmXgqx32J2jrM/r5yGeN5v6aqfwYQkc8C78d9qx+plvY48EWvZuMD3gB81ovvqyHlwffOvgp8IaiWsEdEbgCeEpF/UdWRkk6fL+NqT3f3FYjI1cA+XFLeCCwCvqeq2/peZ7iTeUnqA8AKVd3hFe8VEQE+DtyrqhtEZAvwIVxNDuDDwHZVfXaUeK/2aqUAubhk9i5VbRntQkXkJlzt+Z2q+ljQrmj9Dh5W1b6E/AMR+Siuxv4EgKr+e0g863FJ8kwg+Lp/qqq/DDruq0CNql4fVPYvwGW4mtxdo127iT9LaCaefKPsH+9M2f0dSVS1TkR6cTU9ROQnuOakPitUdT8uoX0H98GWgWuC7GtK6g4qz8Xdc8P7Zr4I+J6I3BF0zr7rWoZrlhrNmcA5XiIMVe5dzx3Az70P4CeB36nqq8Oc7wwvhpddDuuXgauR9PkpcLOIfMU7fj3udzCa/wVu9n6eBbwH+K2IXDhKJ56PAHOA16vqKyH7ovU7CH39Q3h/ewARWYv7QrIWKGLgb7WIwQntxTDxnS4ioUk7x4vPJCFLaCaeqgE/sAr3IRmqrzOGjvG8XWHK+prTv4z7YOxz2Pv3Ndx9m4uBdOBJVQ0AiMhTQeX1wKaQc96IVwMIcTDCeNNwiSTc/bA6AFX9uog8BLwZ1+x5s4jcrqrhusL3xXUuQ3saBn9JeNB73bd6zykAfhFBvCdUNXjYwSsicjmuCfADIzxvA64TyYdE5NW+329QzNH4HYT+7QPeufuaRx/FNR1e03deXEtAaJNj6P3PNNyXnnAJ93iYMpMELKGZuFHVJhH5E/AxEfnXMPfRbgaOAI8Nffa4X/MoYbr5q6rf6xBxMe7D61dBu58ArvTK/9r3QayqR0TkACBBzVzj8TKwMiRJhIt9D/Aj4Eci8nngMww0Fwbrq/0sVNXfj3C+EyLyn8C1uGv7tao2jecCgB5g+ijHbMbdy3scyBSRjwQltWj/DsI5FSgGvqiq2wFE5FxGbynoi289cChcByWTnCyhmXj7GG482V+9gbdbgVLgk7hv8++I4wfI48D3cR9w1waV/xVXq/Mx9Bv6F4F7ReQYrqNDN+6D8y2qel2Er/tl4FER+T7wAHAS14z1D97rpeNqL7/G9cDLx9VStoU7maruEpH7gJ959xA34JpKTweKVTW4WfGn3n4Y3PllJDki0jc2r6/JcQWuI86IVHWriFyA+53+u4hc4w27iOrvYBj7gE7g4yJyJ67TybeJrGn7h7j7jb8RkduAA8B84C3AH1T1uTHEYeLEejmauFLVfbh7Pi/gPlx3A3/C9UZ7XV/njjh53Hvd+r5u3F6M23BdwKfh3T8L2vcg8I+4ZrsXcffMvoq7dxMRVX0C14S2GngG16T5fdyHejeu9lMA3Atsx3VuOQK8d4TTfsQ7xxdxH/qP47rwD+pIoaov4WpOu1X1qQhDfi9Q6z1eAt4GXKOqD0XyZFVVXG/Ki4AHRSQ9Rr+D0NdtwN0/vRT3xekO4CZcs/dozz0CvA7XAeYRXDP4Q7h7b7WRxmDiy2crVhszdXi9N/fheg/emeh4jIkma3I0ZgrwpvCagxunlocbt2ZMSrGEZszUsBB3L6oW+KCqWk89k3KsydEYY0xKmHI1NBHJwg2arGXwoFNjjDHJLR03V+lLqtoZunPKJTRcMnsm0UEYY4wZt/NwA+YHmYoJrRbgoYceorS0dLRjjTHGJIm6ujquuuoqGGboxFRMaL0ApaWlzJ+f1AsfG2OMCS/s7SIbWG2MMSYlWEIzxhiTEiyhGWOMSQmW0IwxxqSEhHQK8Rb1W4+bmPRXqro+aN/FwD24mQ1eANZ7E9qGO89i4N9xqxPvB25Q1b+EO9YYY0xqS1QN7TBwG3BfcKGIFOFmtv4SMBu3JtHDI5znV7iFGgtxs4z/j7eqcFLz+wPoviaONIWuxWiMMWa8EpLQVPURVf0N0Biy6wpgq6r+t6p24JblqBSR5aHnEJEK4DTgK6rarqq/xi2LcWVso5+4v1Ud4rEX9/PIE9XUN9vagcYYEw3Jdg9tJVDVt6Gqrbj1slYOc+weVT0ZVFY1zLFJo62jm617XB7v9Qd4VYcspmyMMWYcki2h5QGhs4AfB2ZM8NiksXlXA73+gQmhdx88RktbVwIjMsaY1JBsCa0FmBlSNhO3iu1Ejk0KPb1+tuwZ3MrqDwTYtKshQREZY0z03H333dx0000AHD58mHXr1tHbG7854JMtoW0FKvs2RCQXWOqVhzt2iYgE18gqhzk2Kei+Zto7ewBIT/P1l2/d20h3j038b4xJfo888giXX345lZWVvP71r+crX/kKJ06cGHLcvHnzeO2110hPT49bbAlJaCKSISLZuKUA0kUk21sa/n+BVSJypbf/y8AmVd0Reg5V3QlsBL7iPf+dwBrg1/G7ksgFAgGqquv7t89eNZf8vCwAOrt62VHTnKjQjDEmIvfddx933HEHn/nMZ3j55Zd5+OGHOXz4MB/84Afp6kr8rZNE1dBuAdqBzwPv836+RVXrcb0UvwE048aXvbvvSSLyExH5SdB53g2c4R37beBd3jmSzv4jJ2k60QFAZkYaK5cUsqa8qH9/VXU9ttiqMSZZtbS0cPfdd3PLLbdw/vnnk5mZyfz587nrrrs4fPgwv/vd7wYdf/DgQUSEnh7XKvX+97+fu+66i3e/+92sW7eOa665hqampqjGmJCB1ar6VVyX/HD7/gIM6abv7fvnkO0a4MKoBhcjVTsH8uyKskKyMtM5dfFsXthaR2dXL8daOqmpPUHZvFkJjNIYk0xe06O8uK2O7h5/zF4jMyONs1aUsk7mjHjcq6++SmdnJ29605sGlefm5nL++efz3HPPUVZWNuI5fv/73/Ozn/2MuXPncu2113Lffff133OLhmS7h5aSGo+3s/+I66vi8/lYs8zVzDIz0llRVth/3MadSVm5NMYkyMad9TFNZgDdPf6IPnuam5spKCggI2NoPai4uJjm5tFvm1xxxRWUlZWRnZ3Nm9/8ZrZv3z6umIdjCS0OqqoHejEumTeTWd69M4DKZUWk+VwHkUP1LTbQ2hjTb21FMZkZsf2YzsxIY23F6BMsFRQU0Nzc3N+EGKy+vp6CgoJRz1FcPPA6OTk5tLVFd7akqbjAZ1y1dXSj+wbaiStD3jh506exdP4sqg8cA6Cq+iiXnLUorjEaY5LTOpkzalNgvKxbt45p06bx6KOPctlll/WXt7W18fTTT/OpT32Kurq6BEZoNbSY27KnsX8g9ZyC6cwtzB1yzNqKgTfszgPHaG3vjlt8xhgTiRkzZvCxj32M2267jaeffpru7m4OHjzIjTfeSGlpKW9/+9sTHaLV0MaqraObV/UoJ1sj66J6sL6l/+e1FcX4fL4hx5TMdomutrEVvz/AH57dy4zpmRGdf0HJDFYtLRr9QGOMmaBrr72W/Px8br/9dvbv309eXh6XXHIJd9xxB9OmTUt0ePimWldxb8mZvY8//jjz588f8/Ofee0QVbvG3nkjLyeT91+2YtCA6mC7Dh7jzxtqxnxegMvPW8Ki0tBJU4wxJrUcPHiQiy++GKDM6+U+iDU5jlH+jKzRDwrjzBWlwyYzgCXzZlGcnzOuc1vvSGOMsSbHMVu9rIiSwukRNzmC6/gxp2DkZJWW5uOdFy6jtqGVnt7Ru+n29Pr5y0sHCAQCHDhykoZj7RSNMyEaY0wqsIQ2DnMKpjOnYHrUzzstM51FcyNvOtx7+AS7Dvb1jqzn4jMXRj0mY4yZLKzJcRILHjuyc38zbR3WO9IYM3VZQpvESgtzKZntaoq9/gBbdocuAG6MMVOHJbRJLriWtnl3Q0T334wxJhVZQpvklp6ST16OG7PW3tnDzv22DI0xZmqyhDbJpaX5WFM+UEur2mnL0BhjpiZLaClgRdns/glMG090cPBoyyjPMMaY1GMJLQVkT8vg1MWz+7df23k0gdEYY0xiWEJLEZXlA/NE7q8bWB3bGGOmChtYnSJm5WWxeO5M9h4+DriB1m88fcGIz2k41s6Trx4MO34tMz2N008toWLhyGscjXSO4eTlTOPiMxcMWhfOGGMmyhJaCllXUdyf0HRfM+esmktOVvg/cSAQ4IlXDnCkafgF9v768gEWlMyY0DnCOdHaxdOvHeLy85aM6XnGGDMSa3JMIXOLcin25ozs6fWzdc/wA61rG1tHTUTROMdw9tWdsGZRY0xUWQ0thfh8PtaWF/PYi/sB2LSrgXUVxaSnD/3eUhU0Q//yRQWcuaK0f3t/3Umeeu3ghM4xnGc2HqKm9oQ7d3U9F47SLGqMMZGyGlqKWTY/n9xsN9C6raObam/y4mDHWzrZc/hE//Y6mcOsvKz+x4qy2f2Dtcd7juEewcvJ79jXTEdnT9Su3RgztSVdDU1EQgdR5QA/UtWPhzl2PXAv0B5U/Peq+mTMAkxy6elprF5WxPNbagFXi5KFBYNWyt60q6F/8PXCkhkUzsoZco5VSyd2juHMK8qlOD+H+mPt9PT62bKnkTNOLRn/BRtjjCfpamiqmtf3AEpwyeq/R3jKhuDnTOVk1mfVkkIyvCbC+mPtHG5o7d/X2d3Ltr0D98Uqg+aCjPY5wvH5fIOO37yrgV6bf9IYEwVJl9BCvAs4CjyT6EAmk+ysDJYvGuhuH7yi9fa9jXT3uAQye2Y2C0tmxOwcwykPahZt7ejuX9PNGGMmItkT2tXAL1R1pMkJ14lIg4jsFJEviUjSNaMmQnAtqKb2BMdOduL3B9i0q2HgmKDB2LE6Rzh9zaJ9Ntr8k8aYKEjahCYiC4ELgAdGOOxpYBUwB7gSeA/wmdhHl/wKZmSz2Fv9OhAIUFVdz55DxznR2gVATlYGsmjkQdPROMdwRmrSNMaY8UjahAZ8APibqu4d7gBV3aOqe1XVr6qbgVtxzZQGV3vqs6OmiVd2HOnfXhmUUGJ9jnBGatI0xpjxSPaENlLtLJwAMLb2rxQ2f04eRfmu92F3r5/6Y64zaFqaj9VLi0Z6alTPMZzgZNnXpGmMMeOVlAlNRM4FTmHk3o2IyFtEpMT7eTnwJeC3sY9wcvD5fFQuG9oDsWJBPrneOLN4nGM4BTOzWVQ60KS5aZfV0owx45eUCQ3XGeQRVT0ZXCgiC0Wkxbu/BnAxsElEWoE/Ao8A34xvqMmtYmE+07MHJ57K8jnDHB27cwxnbVDHk+01TXR02UBrY8z4JGWPQFW9bpjy/UBe0PZNwE3ximsycoOkC3lxax3gmhD75nuM5zmGM39OHoWzcmg83k53j59te5s4TaKTLI0xU0uy1tBMFK2rKGbJKbMomT2dC06bP6FzzCkY/znC8fl8VJYP3IvbVF2P329d+I0xY5eUNTQTXZkZ6Vx2blnCzzGcioUFbNhcS3tnDy3t3ew+dIzyBeMbDmCMmbqshmYSLiM9bVCPSRtobYwZD0toJimsWlpIepobcXGkqY26xvGts2aMmbrGndBEJD+agZipbXp25qBZRzZWWxd+Y8zYRJTQROTLIvLeoO1fA00iUiciZ8QsOjOlBA+0Dp5iyxhjIhFpDe3DwB5wg5mBc4GzgYeAO2MTmplqCmflsMCbud8GWhtjxirShDYHOOT9fDnwsKq+BNwDrItFYGZqWhtUS9u2t4mu7t4ERmOMmUwiTWiHgRUi4gPeAvzZK58O2NQOJmoWls6gYEY2AF3dvWzf25TgiIwxk0WkCe0HwMPAVqAD+ItXfj6wOQZxmSkqdKB11S4baG2MiUxECU1V7wIuBW4BzlbVvlrZPuDmGMVmpihZNJvsaW7M/4nWLvYePp7giIwxk0HE3fZV9SVVfQSYLiJpXtkfVPXZmEVnpqTMDDd3ZB9bK80YE4lIu+1ni8i/erPaHwQWe+XfEZEbYxifmaJWLy0izRtoXdvYypEmG2htjBlZpDW024GzcM2OHUHlzwLroxyTMeTmZFKxYGDsvtXSjDGjiTShXQHcoKrP4VaF7rMVWBb1qIxh8Jpruw8eo6XNBlobY4YXaUKbBYS7M58PdEcvHGMGFBfkcEqxW/7OHwiwaVdDgiMyxiSzSBPaE7jZQvoERCQD18PxsahHZYwneEXrrXsb6e6xgdbGmPAiTWg3AleIyHNAFvBjYBewFlsx2sTQ4rkzyc/LAqCzq5cdNc0JjsgYk6wiHYe2F1iJS2R34eZ1/BZQqaoHYheemep8Ph9rggdaV9taacaY8CJasVpEilS1AXgwzL6Vqro16pEZ4zl18Wxe2FpHZ1cvx1o6qak9Qdm8WYkOyxiTZCJtcvyLiBSEForIWcCTUY3ImBCZGemsKLOB1saYkUWa0F7AJbX+r8Ui8kZch5CvxSIwY4JVLisizecGWh+qb6G+uT3BERljkk1ETY6qep2I3Ac8JiKXABfi1kL7qKoOaYacKBF5EjiHgZn8D6mqhDnOB3ybgR6Y9wKfU1W7yZJi8qZPY+n8fKoPuE4hVdVHueSsRQmOyhiTTCJKaJ4PAQ8AzwPzgfep6m9jEpVzg6r+fJRjPgK8A6jEDfh+DNdh5ScxjMskyNqK4v6EtvPAMYryc/B5tbZEycvJZMkps6IaRyAQoKb2BDNzp1E4Kydq5zUm1Q2b0ETkA2GKnwQuAn4DzOo7RlV/EZPoRnc1cKeqHgQQkTuBa7GElpJKZk9nbmEutY2t+P0B/lZ1ONEhAXDOqrmccWpJ1M63YXMtr+pRMjPSeNdF5ZbUjInQSDW04e6NdQPneQ9wNaNYJLRvici3AQW+qKpPhjlmJVAVtF3llZkUddryOfzh2b2JDmOQjTvrWVtRTEZ6xItXDKu9s6d/RpTuHj+vaT2XnLVwwuc1ZioYNqGpalk8AwnxOWAb0AW8G/g/EVmrqrtDjstj8JRcx4E8EfHZfbTUVDZvFm86exF1ja2JDoU9h47T0t5NR1cPuq+ZlUsKR3/SKLbuaaSn19+/XX2gmXPXzGV6duaEz21MqhvLPbS4UdUXgjYfEJH3AJcBd4cc2gLMDNqeCbRYMkttFQsLqFg4ZBRJ3M3Mndbf7LlxZz0rymZP6F5ab69/yHyVvf4Am3c1cPaquROK1ZipYKR7aPcBN6rqSe/nYanqNVGPbLAAEO6TYiuuQ8iL3nalV2ZMzK0oK+TFbUfo6u6l+WQH++tOsmjuzNGfOIzqg8do63Bzfaf5fPi9GVG27Gnk9FNLotKkaUwqG6mG5hvm55gSkXzgbOApXLf9fwLOBz4R5vBfAJ8SkT/ikt6nGVqLMyYmpmWms6Jsdv9A743V9eNOaIFAgKqgAeNnrihhe00TJ1q7aO+MXpOmMalspHtoHwz3cxxkArcBy4FeYAfwDlVVETkP+JOq5nnH/hRYAmz2tn/ulRkTF2uWFVNV3UAgEODAkZM0Hm8fV6/Eww2t1B9zg8Uz0tNYvbSIzIy0/ibNquqJN2kak+rGdA9NRCqAvgHOO1S1OtoBqWo9cOYw+57BdQTp2w4An/UexsTdzNxpLDllFrsPHgNc4rnojLH3Sgyezmv5ogKyszI4tayQF7bW0d3jp+lEB/uPnGRR6fibNI1JdRE1yovIPBF5DFdbut977BCRR0XklNiFZ0zyW1s+sGab7mvuvw8WqWMn3YTLfSq9NeCyMgfPYVlVbXNYGjOSSO8yPwBMA8pVtVBVC4EKXPPg/TGKzZhJobRwOiWzpwOuV+KWPY1jen7wkjiLSmdSMCO7f9+aZUX9zYz761yTpjEmvEgT2htwU1H1jwPzfv6Et8+YKcvn81EZVEvbvKth0FiykXR09bCjpql/O3iFboBZeVksmTfQzFhVPbhbvzFmQKQJbSdQHKa8ELdytTFT2tL5+eTluMHP7Z097Nwf2cra2/Y00e0lv8JZOcyfkzfkmMqK4CbNpjE3aRozVYw0Di34zvatwD0icjvwEq6L/FnAZ4AvxzRCYyaB9DQfa5YV89xm1yvxxa111DaMPptJ8L2zteXFYXsxzi3MZU7BdI42t9HrD/DnDfuYlTdt1HP7fDB/zoy4DULXfU20tvdQWV5Euo2ZMwkwUi/HGlzigoFxaPeGOe5hID2KMRkzKa1YMpuXttXR3eunpb2b7UFNiaPJycqgfGF+2H0+n4+1FcU8+sI+AA43tHA4wpbHbXubyMnKYEHJjIhjGY9dB4/x2Iv7ATjZ1sUFp82P6esZE85IX6PKcGO8lng/D/dYEuMYjZkUsqdlsKY8XMv86E5fPmfEmUCWzs9n9szsYfePJNYrfAcCAV7To/3b22uaaO/sGeEZxsTGSAOr98UzEGNSwdkrS5lXlEvrGO5zzZg+Ley9s2DpaT7eeeEyDhw5GVGHk55eP89sPEwgEGBf3QmaTnSMOyGOpq6xjSNNbYNee+uexqguqWNMJJJycmJjJqu0NN+E5nMcSU5Wxpjuhx040sLew24xik3V9Vx4+oKYxLUxzPi4TbsaWFdRbPfSTFzZu82YFBU8BGDHvmY6YtAMeLylkz2HBlZwyprmbqe3dXRT7c2eYky8WEIzJkXNK8qlON/NK9nT6x/zgO9IbN7d0D8ofEHJDNZVzOnft3HnwIBxY+Jh2IQmIt8TkVzv5/NFxJonjZlEfD7foDFsm3c10BvhgO9IdHX3sm1v0KDw8mJWLSkk02tmbDjWzqH6lqi9njGjGamGdgPQ19f3CWB27MMxxkRT+fx8cr3Vrluj3Ay4bW8jXd29ABTMyGZh6QyyszKQxQMfFVUx7mFpTLCRal07gG+KyFO4cWj/KCInwh2oqr+IRXDGmIlJT09j9bIint9SC7gEIwsLJrwMjd8fGLS69tqKgUHhleVFbNnt9tXUnaT5ZMeg+SmNiZWREtqHgG8At+AGWH8Wtz5ZqABuoU1jTBJataSQl7cfoafXT/2xdg43tHJK8cjDBEaz5/BxTrR2AW78nSwa6H1ZMCObxXNnUlN7gkAgwKbqBhtobeJipHFoLwFvAhARP3CGatDoSWPMpJCdlcHyRQX9nUI27qyfcEILbkpctbRwyKDwyvLi/mm9dtQ0cfbKUrKz7Da8ia2IejmqapolM2Mmr+DVAGpqT3DsZOe4z3WkqY3aRjdPZVqaj9VLi4YcM39OHkVeD8vuXv+gziPGxErEX5lE5ArgU8Byr2gH8H1V/XUsAjPGRE/BzGwWlc5kX51rBvzNU7vIGWeNqa1jYDxbxYJ8cr1VBoL5fD4qlxXz+MtufseXttdRfWDwCgQZ6WmctnwOZfNmjSsOY0JF9I4WkU8AtwF3A7d7xecC94vIAlW9K0bxGWOiZG1FMfvqXDNgS3s3Le0TX4amsnzOsPsqFuazYUstbR3ddPe4+3ehHn1hH1e/dQXZ06w50kxcpO+iTwAfVtX/DCr7nYhUAd8GLKEZk+Tmz8ljYekM9tedjMr5KhYWUFyQM+z+9PQ0zl5ZyhOvHBj2mO4eP9v2NHHa8uETozGRijShlQCvhSl/FbB3ojGTgM/n4/I3LKH5ZGfEK2oPJzMjjfy8rFGPW7mkkEVzZw5ZlPTgkZb+teM27aqnsqKY9LSJDSUwJtKE9ipwo4h8TFUDACLiAz5J+ERnjElCPp8vZrPuDycvJ7N/Ne8+s2dm89rOo7R39tDS3s3ug8fithCpSV2RJrQbgT8Al4nIy17ZGcB04LJoBiQiWcCPgEtws5PsAm5W1T+FOXY9btHR4Mb5v1fVJ6MZkzEmujK8Ad8vbq0DoKq6nvIF+RMe8G2mtogSmqq+LCJLgPcBgps55DHgIVWN9mRtGcAB4AJgPy5h/peIrFbVmjCvW8c/AAAcm0lEQVTHb1DVN0Q5BmNMjK1aUsgr24/Q6w9wpKmNusY25hblJjosM4lF3LVIVVuBn8YwluDX+WpQ0e9FZC9wOlAT69c3xsTH9OxMZFFB/xi1jdX1ltDMhCR9X1kRKQEqgK3DHLJORBqAJuBB4Fuqauu/GzMJVJYX9ye0PYeOc7ylk1kRdDYxJpykXg9NRDKBh4AHVHVHmEOeBlbhelpeCbwH+Ez8IjTGTEThrBwWlLhFPQKBwRMeGzNWSZvQRCQNV+Pqwi1lM4Sq7lHVvarqV9XNwK3Au+IYpjFmgtYGTcu1vaapf0kaY8Zq1IQmIpki8hERKY1HQN5r+nC9F0uAK1U10ikNArgOK8aYSWJh6Yz+5WXcoqHRX1nbTA2jJjQvmXwfiOfglR8DpwKXq+rQ+XI8IvIW7x4bIrIc+BLw2/iEaIyJBp/PR2X5wATHm3Y14PcHEhiRmawi7RTyGK4bfU3sQnFEZBFwHdAJ1IlI367rgGeAbcAKVd0PXIybTzIPOAL8EvhmrGM0xkSXLJrN81vq6Ojq4URrF3sOH2fZ/PxEh2UmmUgT2jPAd0XkdcBGoC14ZzRXrFbVfYzcbJgXdOxNwE3Rem1jTGJkZqSxaqlbiBTcemuW0MxYRZrQbgBagb/zHsFsxWpjzIStXlrEq3oUvz9AbWMrR5raKJk9PdFhmUkk0plCymIdiDFmasvNyaRiQT479rl10zburOfvzlmU4KjMZDLmbvsiUup1qTfGmKgKXl9t98FjnGzrSmA0ZrKJKDGJSLaI/KuItAIHgcVe+XdE5MYYxmeMmUKKC3I4pdjdJvfbQGszRpHWtG4HzgIuBTqCyp8F1kc5JmPMFLa2YmCg9bY9jXT32EBrE5lIE9oVwA2q+hyuE0ifrcCyqEdljJmyFs+d2b94aGd3L9trmhIckZksIk1os4DjYcrzgUhn8TDGmFG5gdYDtbSqahtobSITaUJ7Avhw0HZARDKAm3GDro0xJmqWLy4ga1o6AMdbOtlXdyLBEZnJINKEdiNwhYg8B2ThpqbaBazFBjYbY6IsMyOdlWWF/dsbd9YnMBozWUSU0FR1L7AS+AlwF7AH+BZQqaoHYheeMWaqWrOsiDSfmzToUH0L9c3DTutqDDC2Fau7sRlBjDFxkjd9Gkvn51N9wA20rqo+yiVn2UBrM7yIE5qInIubAqtvtmAFfuj1fDTGmKhbW1Hcn9B27j/GOavnkZeTmeCoTLKKdGD1R4G/Ar3A/d6jB/irt88YY6KuZPZ05hbmAm6g9WYbaG1GEGkN7Rbg46r6s+BCEXka+Drwo2gHZowxAJUVxdRuaAVg655Gzji1hMwMm33PDBXpu2I68FSY8qe9fcYYExNL5s1iZu40ADq6etB9NtDahBdpQrsP+JiIhK5Tdj2u+dEYY2IiLc1H5bLBA60DARtobYYatslRRO4L2kwH3gm8TURewU1/dTpQCPxvTCM0xkx5p5bN5oVtdXR199J8soP9dSdZNHdmosMySWakGpov6OEHfg08CZwEWnBNkI8weG5HY4yJummZ6awom92/vbHaBlqboYatoanqB+MZiDHGjGTNsuL+5sYDR07SeLydwlk5iQ7LJBHrKmSMmRRm5k5jySmz+rerrJZmQkTUbV9EFgLfBc4HinHNkP1UNT36oRljzGBry4vZffAYALqvmXNWzWV6tg20Nk6k49B+hRtUfSNwFLtvZoxJgNLC6ZTMns6RpjZ6/QG27G7krJWliQ7LJIlIE9oa4HRV3RnLYPqIyGzgXuBNQAPwBVX9jzDH+YBvM7C0zb3A51TVEq4xKahvrbRHX9gHwObdDZy2fA4Z6Xb3xESe0P6CS2pxSWjAPUAXUIJbouYPIlKlqltDjvsI8A6gEldrfAy3EsBP4hSnMSbOls7PJ2/TYVrau2nv7GHD5lqK8+PUOcQHpbNzyZ+RFZ/XM2MSaUK7BnhQRF4HbCNklWpVjdos/CKSC1wJrFLVFuBvIvI74P3A50MOvxq4U1UPes+9E7gWS2jGpKz0NB9ryot5btNhIP6dQzLT0/jHSysomJEd19c1o4s0ob0DuBS4AGhk8D20ANFdVqYC6A1p3qzyXjvUSm9f8HEroxiLMSYJrSibzSvbj9DZ3Rv31+7u9bNxZz1vPH1B3F/bjCzShPYd3ATFd8Th/lQecDyk7DgwI4JjjwN5IuKz+2jGpK7saRlcft4Sttc00dvrj8trdvcGhvSwzMmKeAUuEweR/jUCwG/ilCRagNA5bWbiZigZ7diZQIslM2NSX2lhLqXe0jLxEAgE+O/Huzja3EZPr79/5n+TPCLtGnQ78EkRicd4s51AhoiUB5VVAqEdQvDKKiM4zhhjJsT1sCzq3960qyFutUMTmUhraH8PnAZcISLVDO0UclG0AlLVVhF5BLhVRD6M6+X4duDcMIf/AviUiPwRV4v8NHB3tGIxxphgy+bns2FzLS3t3bR1dFN98BjLF80e/YkmLiJNaE96j3j5KG7JmqO4TijXq+pWETkP+JOq5nnH/RRYAmz2tn/ulRljTNSlp6exelkRGzbXArBxZz2ysACfL3RlLZMIESU0Vf1arAMJeb0mXM/K0PJncB1B+rYDwGe9hzHGxNzKskJe3naE7l4/DcfaOVTfwvw54fqsmXiz4fXGGDMG2VkZyOKBZsaqnTZJcrKIdHJiPyPM32iTExtjppLK8iK27G4AoKbuJM0nO2ygdRKI9B7aG0O2M3FTYV0PxLU50hhjEq1gRjaL586kpvYEgUCATdUNXHDa/ESHNeVFeg/tqTDFf/F6PH4S+GVUozLGmCRXWV5MTe0JAHbUNHH2ylKybaB1Qk30HtoO4KxoBGKMMZPJ/Dl5FHmTInf3+tm2tynBEZmxLPAZzAeUAjfjJis2xpgpxefzUbmsmMdf3g/Apl31VFYUk55mXfgTJdIaWg2wN+SxAZgLfCgmkRljTJKrWJjfv2J2S3t3/1yPJjEibfAtC9n2A/Wq2hHleIwxZtJIT09j1dJCXtxaB7iB1uUL8m2gdYJE2ilkX6wDMcaYyWjVkkJe2X6EXn+Ao81t1Da2Mq8ob/QnmqgbMaGJyJcjOEdAVb8epXiMMWZSmZ6diSyazba9jYAbaG0JLTFGq6GFjj8LdTaQBVhCM8ZMWZXlRf0Jbc/hExxv6WRWXlaCo5p6Rkxoqho2oYnIW4FbgQ7gmzGIyxhjJo3CWTksLJnB/iMn3UDrXQ2ct/aURIc15YxpHJqIXCoiG4CHgN8DZap6W0wiM8aYSaSyorj/5217G+ns7k1gNFNTpOPQLsQ1K64Ffgi81ZsR3xhjDLCwZAazZ2bTdKKD7h4/2/c2srZiTqLDmlJGrKGJyOtF5HFcbex5YImqfsGSmTHGDOZWtB6opW3a1YDfP+yc7iYGRquhPQO041aGPglcLyJDDlLVW6MfmjHGTC6yqIDnt9TS3tnDidYu9hw6zrIF+YkOa8oYLaE9jVs2Zrn3CCeA6yBijDFTWkZ6GiuXFPLy9iMAbKyut4QWR6P1crwwTnEYY0xKWLOsiNf0KL3+AHWNrTzyRPWgmUN8Ppg/ZwanL59jM4pEma11YIwxUTQ9O5PyBQXs2Oe6GhxuaB1yzMGjLeTPyGLZfKu9RdNEl48xxhgT4vRT55CZMfLHa9XO+jhFM3VYDc0YY6KsYEY2H7hsBY3H2weV9/QG+ONze/H7A9Q2tnKkqY2S2dMTFGXqsYRmjDExkJOVwfw5M4aUVyzIZ8e+ZsDNzv935yyKd2gpK2kSmohkAT8CLgFmA7uAm1X1T8Mcvx64FzesoM/fq+qTsY3UGGPGr7J8Tn9C233wGC1tc8mbPi3BUaWGpElouFgOABcA+4HLgP8SkdWqWjPMczao6hviFJ8xxkxYcUEOpxTncai+BX8gQNWuBl6/Zl6iw0oJSZPQVLUV+GpQ0e9FZC9wOm7FbGOMSQlrK4o5VN8CwLY9jZy1ooTMjPQERzX5JU1CCyUiJUAFsHWEw9aJSAPQBDwIfEtVe+IRnzHGjNfiuTPJz8viWEsnnd29bK9pYs2y4tGfaEaUlN32RSQTN6P/A6q6Y5jDngZWAXOAK4H3AJ+JT4TGGDN+Q+Z9rG4gELB5HycqbjU0EXkSd38snGf77oWJSBquttUF3DDc+VR1T9DmZhG5FZfQvhWVgI0xJoaWLy7g+a21dHb1cqylk5raE5TNm5XosCa1uCW0SKbREhEfrudiCXCZqnaP4SUCgM0jY4yZFDIz0llZVsirehRwXfgtoU1MsjU5/hg4FbhcVdtHOlBE3uLdZ0NElgNfAn4b+xCNMSY61iwrIs2bz/FQfQv1zSN+7JlRJE1CE5FFwHW4RUTrRKTFe1zl7V/obS/0nnIxsElEWoE/Ao8A30xE7MYYMx5506exNGg+x6rqowmMZvJLml6OqrqPEZoMVXU/kBe0fRNwUxxCM8aYmFlbUUz1ATfQeueBY7xu9TxyczITHNXklDQ1NGOMmYpKZk9nbmEuAH5/gM27GxIc0eRlCc0YYxJsbcVAF/4tuxvp7vEnMJrJyxKaMcYkWNm8WczMdfM5dnT1oN5aamZsLKEZY0yCpaX5qAyaKaTKBlqPiyU0Y4xJAqeWzWZappvPsflkB/vrTiY4osnHEpoxxiSBaZnprCib3b+9sdpWtB4rS2jGGJMk1iwrxucNtD5w5OSQFa/NyCyhGWNMkpiZO40lpwxMf1VltbQxsYRmjDFJZG3QLPy6r5m2jrFMaTu1WUIzxpgkUlo4nZLZ0wHo9QfYsrsxwRFNHkkz9ZUxxhi3VtraimL+3/P7ANi8uwFZVNB/b208crLSI14Ru62jm57eyIYM+HyQl5MZUWx9wxAmch2jsYRmjDFJZukp+eTlHKalvZv2zh4e/NP2CZ0vPc3HZa8vY1HpzGGPCQQCPPrC/v55JSOVPyOLd72xnOys4dNJW0c3jzy5i5a2bt523hLmFecNe+xEWJOjMcYkmbQ0H2uC7qVNVK8/wHObakccrF3X2DbmZAZw7GQnm0aZf/KVHUc5drKTnl4/+4/Ebnyd1dCMMSYJrVlWRH1zO3WNrRM6T2t7N/5AgMbj7Rw82sKCkhlhjwse95Y1LZ1pozRR+v0BWr0OK1t2N3K6zCE9fWgdqbO7l217B+4Dzi3KHc9lRMQSmjHGJKGM9DT+7pxFEz7P068dZNMuV4PauLM+bEI73tLJnkPH+7evuHAZhbNyRjxvrz/Ag3/cRkt7N20d3ezcf4xTgwaG99m2Z2Cy5dkzs1k4TEKNBmtyNMaYFBY8WHtf3QmaTnQMOWbTroG5IxeWzBg1mYG7L7cmaP7JjdX1Q5o0/f5AfzIFqCwvjmmnEEtoxhiTwvJnZLF47kBnkE0hg7W7unvZXjMwu39lReT37lYsmU2m18zY16QZbM+h45xs6wIgJysDWVQw5vjHwhKaMcakuOD11nbsa6a9s6d/e9veRrq6e4GxNwlmT8tg+eKBZsbQmU2C78utWlJIRph7bNFkCc0YY1LcvKJcivNdM2JPr5+te1wnjWg0CQY/p6b2BM0nXZNmXWNrf4eW9DQfq5cVTfg6RmMJzRhjUpzP5xvUlLh5VwO9vX72HD7OiVbXJJg9bXxNgvkzslhcOlCrq6pu8P4dqJ2VLyhgenbmeMOPmCU0Y4yZAsrn55PrJZXWjm6qDx6jamdQk+DS8TcJrpU5/T/vqGmivrmd3QcHek2uHcN9uYmwhGaMMVNAenraoGa/DZtqqfWaBNPSfKxeOv4mwdAmzd//bQ9+r8fj/DkzKMofvddkNCTdODQReRI4B+i7a3lIVWWYY33At4EPe0X3Ap9TVVu73BhjQqxaUsjL24/Q0+vvHxQNULEgn9yc8TcJ9jVp/uXF/QCDzl1ZHvt7Z32StYZ2g6rmeY+wyczzEeAdQCWwBvh74Lp4BGiMMZNNdlYGy8PcJ6ssnxPm6LEpn58/5D5Z6JCBWEvWhBapq4E7VfWgqh4C7gTWJzYkY4xJXpUhc0SeUpxHccHEmwTT09NYE9KTsXJZbAdSh0rWhPYtEWkQkWdF5MIRjlsJVAVtV3llxhhjwiiYmU1ZUK1pnUy8dtZn5ZJCMjNcWsnJymD54tgOpA6VjAntc8AS4BTg34D/E5GlwxybBxwP2j4O5Hn31owxxoRx8VkLqSwv5qIzFkS1STAnK4PLz1vC6qVFvO28pRGvwRYtce0U4nX4uGCY3c+q6htU9YWgsgdE5D3AZcDdYZ7TAgT/NWYCLdYpxBhjhpc9LYPz1p4Sk3PPK8pjXlFs1jsbTVwTmqpeOI6nBYDhalxbcR1CXvS2K70yY4wxU0xSddsXkXzgbOApXLf9fwLOBz4xzFN+AXxKRP6IS3yfJnxNzhhjTIpLqoQGZAK3AcuBXmAH8A5VVQAROQ/4k6r21Wd/irvfttnb/rlXZowxZopJqoSmqvXAmSPsfwbXEaRvOwB81nsYY4yZwpKxl6MxxhgzZpbQjDHGpISkanKMk3SAurq6RMdhjDFmDII+t8MOcJuKCW0uwFVXXZXoOIwxxozPXGB3aOFUTGgvAecBtbielMYYYyaHdFwyeyncTl8gYJNqGGOMmfysU4gxxpiUYAnNGGNMSrCEZowxJiVYQjPGGJMSLKEZY4xJCZbQjDHGpISpOA5tQkRkNnAv8CagAfiCqv5HYqMaPxG5AVgPrAZ+parrg/ZdDNwDLAReANar6r4EhDluIpIF/Ai4BJgN7AJuVtU/efsn/TUCiMgvgYuBXKAOuF1Vf+7tS4lr7CMi5bgVNv5HVd/nlb0X+BZQBDwGXKOqTYmLcny8RZDPwS2fBXBIVcXblyrX+G7gK7j3Yx3u/fhMNN6nVkMbu3uALqAEuAr4sYisTGxIE3IYt2TPfcGFIlIEPAJ8CZcIXgYejnt0E5cBHMCtlD4Ldz3/JSKLU+gawX3QLVbVmcDbgNtE5PQUu8Y+9xA0sNb7//dT4P24/5dtuC8xk9UNqprnPfqSWUpco4hcCnwH+CAwA7fe5Z5ovU9tYPUYiEgu0AysUtWdXtmDuG9Rn09ocBMkIrcB8/tqaCLyEdw3pHO97VxcjXSdqu5IWKBRICKbgK8BhaTgNYqIAE8CNwL5pNA1et/urwC2ActU9X0i8k1cMn+vd8xSYDtQqKonExft2Hk1tF/21a6DylPiGkXkOeBeVb03pDwqnzdWQxubCqC3L5l5qoDJXEMbzkrctQGgqq24udMm9bWKSAnu77iVFLtGEfmRiLThFsatBf5ICl2jiMwEbsWtTB8s9Bp341pRKuIXXVR9S0QaRORZEbnQK5v01ygi6cAZQLGI7BKRgyLyQxHJIUrvU0toY5MHHA8pO46rOqealLtWEckEHgIe8L71pdQ1qupHcbGfh2u+6SS1rvHruG/3B0LKU+kaPwcsAU4B/g34P682lgrXWAJkAu/CvUfXAuuAW4jS9VmnkLFpAWaGlM0EJk2VfwxS6lpFJA14EPet9gavOKWuEUBVe4G/icj7gOtJkWsUkbW4jj3rwuxOiWsEUNUXgjYfEJH3AJeRGtfY7v17t6rWAojI93AJ7WmicH1WQxubnUCG18uqTyWu+SrVbMVdG9Dfpr2USXitIuLD9UwtAa5U1W5vV8pcYxgZDFxLKlzjhcBiYL+I1AE3AVeKyKsMvcYlQBbu/+tkFwB8pMA1qmozcBB3TaGi8j61TiFjJCL/ifuDfBhXZf4jcK6qTrYPCABEJAP34fcVYD5wLa7LcAGui/s1wB9wnSguUNVzEhTquInIT3B/q0tUtSWovJgUuEYRmQNcBPwe9y34ElyT43uB50iNa5zO4G/wN+ES3PXAHGAD8FbgVVxvwAxVfXecw5wQEckHzgaewv0f/Cdcs+NpuP+jqXCNtwJvwV1HN/A7XAemHxCF96nV0Mbuo0AOcBT4FXD9ZE1mnltwH4KfB97n/XyLqtYDVwLfwPXsPBuYVP95AERkEXAdLqHViUiL97gqVa4R9wXrety332bgDuATqvrbVLlGVW1T1bq+B64JrkNV673/f/+Muz96FHff5aMJDHe8MnFDaOpxPfw+DrxDnVS5xq/jhlzsxPXSfA34RrTep1ZDM8YYkxKshmaMMSYlWEIzxhiTEiyhGWOMSQmW0IwxxqQES2jGGGNSgiU0Y4wxKcESmjGjEJH7ReT+RMeRCCKSJiJbReSsCZxjvYjUjOH4Bd7EtXnjfU0zNdlcjmZKE5HRBmKW4ZZhSRp9yTV4MdYYei9wRFVfnMA5HsbN/hARVT0gIo/ifu/fmMDrminGEpqZ6uYG/XwTcC5uva0+9d6Ev1PVR4GfTeQEqtrOwMS0kfolcJ+IfFNVbfYHExFLaGZK86ZRAkBEWoCu4DKv/H7v2PXedg1wN24V7EtwU/j8I64290OgFLgf+GTfh7E3b+RduDnseoA/A/+iqk3h4hKR03Dz263DrRCwCXg78Angau+Yq724fN72JcC3cWtIHQC+r6o/9vYtBvYC/wB8GbeO1t+Aq1X10DAxnAK8DnhnUNmFwBPA33nXOg/4BfAvuGmbrsNNXXSdqj7mPWc98FVVXRz0+/QBh3BTdrUB31HVHwS9/FNAEXAmMJHaoZlC7B6aMeNzE25evdNwH8gPAZ/BzT/3bty8e5cFHf8/QC9uHagLcZM/PzDC+X+Jm4x2NfAG7/zg5mn8L+8x13v0rVL9a+AeXEL7FPA1Ebky5LzfwC2QeTYwbZQYXgccVtUjYfZ9GpfE34WbUPbPuN/DWcD/Afd7688Npy9Jno2b3+8uEelfzNGrFVcBrx/hHMYMYjU0Y8bnYVV9GEBEfgj8J7BWVau8sieA84E/iMj5wDLgor7mSxG5FjgkIqWhNULPAuC3qrrH2+6fAFtE2mFw7RK3MOSPVfXfve09InIXbvWEXwcd992gmtM1QLWILB9mmfuFuJWvw/mcqm4MutZSVb3V2/4mrsa2DFd7DadGVW/2ft4pIp/CJfvgib7rgEXDPN+YISyhGTM+wR+8R71/t4WUFXs/r8Y1Qx53FalBluA+uEP9EHjU6xzxKC6BNo4Qz2pgtYjcEFSWAewPOa6/+U5Vd4lIMyBAuISWjVv1OpzQ628K2QZ3/cMltM0h27W4ZWCCdeBWtjAmItbkaMz4dAf9HAAIWji0r6zv/1ceLmGsDXmU45bPGEJVv4Brvnse+ACg3qKOw8kDvhty/lXApSHHjaWDRSOQP0x8odfaHbSv7zVG+nzpDtkO/n31KcAto2JMRKyGZkzsVeEWozymqhF/QKvqFmAL8G0R2Qq8A/geLhlkh3mNClXdNcppz8KrHYnIUlzS0GGO3QQsFZHMkAQWL8txnWuMiYglNGNi71FcE90jInIzcBh3f+kKVf3n0INFJAf4DvDfuCbDlbj7WTu9Q/YB/+QtXtrqJcnvAs+KyNdw9/PS8Dp+qOpPgk7/Ga+XZgPwr8Bfh7l/BvAKrrv96biaYtyIyFzcfcQn4/m6ZnKzJkdjYkxV/cCbgWrgf3HJ7Qe47u3h9OLuJ/0Kl8R+CHxNVX/v7b8Xd89qO251Y1T1FeBNwMXAq8AzuO79NSHn/jIukb0I+IH1I8Td5cXwDxFeajS9C/izqh4d9UhjPLZitTFTQNA4tDJVrRnD85YAzwLLVLU1NtENec00XFPrR1T1b/F4TZMarIZmjBmWN2zgJuLbfb4UuMeSmRkrq6EZMwWMt4ZmzGRiCc0YY0xKsCZHY4wxKcESmjHGmJRgCc0YY0xKsIRmjDEmJVhCM8YYkxL+P3d4CCJGepxCAAAAAElFTkSuQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "bikeshare = State(olin=10, wellesley=2)\n", - "run_simulation(bikeshare, 0.4, 0.2, 60)\n", - "decorate_bikeshare()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can fix this problem using the `return` statement to exit the function early if an update would cause negative bikes." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "def bike_to_wellesley(state):\n", - " \"\"\"Move one bike from Olin to Wellesley.\n", - " \n", - " state: bikeshare State object\n", - " \"\"\"\n", - " if state.olin == 0:\n", - " return\n", - " state.olin -= 1\n", - " state.wellesley += 1\n", - " \n", - "def bike_to_olin(state):\n", - " \"\"\"Move one bike from Wellesley to Olin.\n", - " \n", - " state: bikeshare State object\n", - " \"\"\"\n", - " if state.wellesley == 0:\n", - " return\n", - " state.wellesley -= 1\n", - " state.olin += 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now if you run the simulation again, it should behave." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "bikeshare = State(olin=10, wellesley=2)\n", - "run_simulation(bikeshare, 0.4, 0.2, 60)\n", - "decorate_bikeshare()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Comparison operators" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `if` statements in the previous section used the comparison operator `<`. The other comparison operators are listed in the book.\n", - "\n", - "It is easy to confuse the comparison operator `==` with the assignment operator `=`.\n", - "\n", - "Remember that `=` creates a variable or gives an existing variable a new value." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "5" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x = 5" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Whereas `==` compared two values and returns `True` if they are equal." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x == 5" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can use `==` in an `if` statement." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "yes, x is 5\n" - ] - } - ], - "source": [ - "if x == 5:\n", - " print('yes, x is 5')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But if you use `=` in an `if` statement, you get an error." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "# If you remove the # from the if statement and run it, you'll get\n", - "# SyntaxError: invalid syntax\n", - "\n", - "#if x = 5:\n", - "# print('yes, x is 5')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Add an `else` clause to the `if` statement above, and print an appropriate message.\n", - "\n", - "Replace the `==` operator with one or two of the other comparison operators, and confirm they do what you expect." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Metrics" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we have a working simulation, we'll use it to evaluate alternative designs and see how good or bad they are. The metric we'll use is the number of customers who arrive and find no bikes available, which might indicate a design problem." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First we'll make a new `State` object that creates and initializes additional state variables to keep track of the metrics." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin10
wellesley2
olin_empty0
wellesley_empty0
\n", - "
" - ], - "text/plain": [ - "olin 10\n", - "wellesley 2\n", - "olin_empty 0\n", - "wellesley_empty 0\n", - "dtype: int64" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bikeshare = State(olin=10, wellesley=2, \n", - " olin_empty=0, wellesley_empty=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next we need versions of `bike_to_wellesley` and `bike_to_olin` that update the metrics." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "def bike_to_wellesley(state):\n", - " \"\"\"Move one bike from Olin to Wellesley.\n", - " \n", - " state: bikeshare State object\n", - " \"\"\"\n", - " if state.olin == 0:\n", - " state.olin_empty += 1\n", - " return\n", - " state.olin -= 1\n", - " state.wellesley += 1\n", - " \n", - "def bike_to_olin(state):\n", - " \"\"\"Move one bike from Wellesley to Olin.\n", - " \n", - " state: bikeshare State object\n", - " \"\"\"\n", - " if state.wellesley == 0:\n", - " state.wellesley_empty += 1\n", - " return\n", - " state.wellesley -= 1\n", - " state.olin += 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now when we run a simulation, it keeps track of unhappy customers." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file chap02-fig01.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "run_simulation(bikeshare, 0.4, 0.2, 60)\n", - "decorate_bikeshare()\n", - "savefig('figs/chap02-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After the simulation, we can print the number of unhappy customers at each location." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "6" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bikeshare.olin_empty" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bikeshare.wellesley_empty" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Exercise:** As another metric, we might be interested in the time until the first customer arrives and doesn't find a bike. To make that work, we have to add a \"clock\" to keep track of how many time steps have elapsed:\n", - "\n", - "1. Create a new `State` object with an additional state variable, `clock`, initialized to 0. \n", - "\n", - "2. Write a modified version of `step` that adds one to the clock each time it is invoked.\n", - "\n", - "Test your code by running the simulation and check the value of `clock` at the end." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin10
wellesley2
olin_empty0
wellesley_empty0
clock0
\n", - "
" - ], - "text/plain": [ - "olin 10\n", - "wellesley 2\n", - "olin_empty 0\n", - "wellesley_empty 0\n", - "clock 0\n", - "dtype: int64" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bikeshare = State(olin=10, wellesley=2, \n", - " olin_empty=0, wellesley_empty=0,\n", - " clock=0)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def step(state, p1, p2):\n", - " \"\"\"Simulate one minute of time.\n", - " \n", - " state: bikeshare State object\n", - " p1: probability of an Olin->Wellesley customer arrival\n", - " p2: probability of a Wellesley->Olin customer arrival\n", - " \"\"\"\n", - " state.clock += 1\n", - " \n", - " if flip(p1):\n", - " bike_to_wellesley(state)\n", - " \n", - " if flip(p2):\n", - " bike_to_olin(state)" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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0kkn0djYyKJFi7j66qtZt24dt91224jnd3R0YGbs2bMHgLe//e1cffXVnHPOORxzzDGcf/75bNmypWDtq/k5qGJb3NbKio5uAFZv6OHoZfPK3CIRqVR/8o089GQnA3uGinaMxin1HPeS+Rxj41+LHn30Ufr7+znzzDNHbJ8xYwYnnXQSDz74IEuWLBlzH7fffjvf+c53WLBgARdccAE33HADF198cV7nkKYeVJ7i81DruvrYM1i8XzwRqW6PPdNV1OAEMLBniMee6Ur03K1btzJnzhymTNm3rzJ37ly2bt067j7OOusslixZwtSpU3nNa17DU089lXObR6MAladZLc3MbmkGYM/gEOs39ZW5RSJSqY5eNpfGKcW97DZOqefoZXMTPXfOnDls3bp1eMgurqurizlzxs9Mnjt377GmTZvGjh07kjd2HBriK4D2tla6e8P9UGs29NDeVpy0dhGpbsfYvERDb6VyzDHH0NTUxJ133snrXve64e07duzg/vvv52Mf+xidnZ1la596UAWweL4SJUSk+rS2tvKBD3yAyy+/nPvvv5+BgQE6Ojq46KKLmD9/Pm984xvL2j71oApg4dwW6uvqGEql6OreyY5dAyoeKyJV4YILLmD27NlceeWVrF69mpaWFk4//XSuuuoqmpqaytq2ukq+udTMDgZW3n333SxatKjczRnTrfc8y7po/unM4w9SVQkRkQQ6Ojo47bTTAJa4+wvxxzTEVyDxeScN84mI5E8BqkAyA1Ql90xFRKqBAlSBxMse9e4cYGtU5VxERCZGAapA9il71KlhPhGRfChAFVB7rKrEas1DiYjkpaRp5lFW3jeAE4B+4MfAR9x939uYq1B8HmptVy+Dg0M0NOgzgIjIRJT6PqhvABuBBcBs4C7g/cDXS9yOokiXPeru7WfP4BAr1m6jbb/pE95fXV0drdMbqaurK2ArRUSqQ6kD1BLgWnffBXSa2S+Bw0vchqKKlz268w+r8t7fgQe08KaTD6W+XkFKRCaXUo8/fQ04x8ymm9lC4LXAL0vchqKKlz0qhHWbelnb1VvQfYqIVINS96DuAy4AtgMNwPeAn5W4DUV18IKZHLn0AFZ39jCUx71Q/QOD9O8eBKBjowrQisjkU7IAZWb1wP8C3wZOBFqAG4ArgH8pVTuKra6ujpOOyb8s08p127jjtyuBkBF4whF5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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "run_simulation(bikeshare, 0.4, 0.2, 60)\n", - "decorate_bikeshare()" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin1
wellesley11
olin_empty5
wellesley_empty0
clock60
\n", - "
" - ], - "text/plain": [ - "olin 1\n", - "wellesley 11\n", - "olin_empty 5\n", - "wellesley_empty 0\n", - "clock 60\n", - "dtype: int64" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "bikeshare" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Continuing the previous exercise, let's record the time when the first customer arrives and doesn't find a bike.\n", - "\n", - "1. Create a new `State` object with an additional state variable, `t_first_empty`, initialized to -1 as a special value to indicate that it has not been set. \n", - "\n", - "2. Write a modified version of `step` that checks whether`olin_empty` and `wellesley_empty` are 0. If not, it should set `t_first_empty` to `clock` (but only if `t_first_empty` has not already been set).\n", - "\n", - "Test your code by running the simulation and printing the values of `olin_empty`, `wellesley_empty`, and `t_first_empty` at the end." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin10
wellesley2
olin_empty0
wellesley_empty0
clock0
t_first_empty-1
\n", - "
" - ], - "text/plain": [ - "olin 10\n", - "wellesley 2\n", - "olin_empty 0\n", - "wellesley_empty 0\n", - "clock 0\n", - "t_first_empty -1\n", - "dtype: int64" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "bikeshare = State(olin=10, wellesley=2, \n", - " olin_empty=0, wellesley_empty=0,\n", - " clock=0, t_first_empty=-1)" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def step(state, p1, p2):\n", - " \"\"\"Simulate one minute of time.\n", - " \n", - " state: bikeshare State object\n", - " p1: probability of an Olin->Wellesley customer arrival\n", - " p2: probability of a Wellesley->Olin customer arrival\n", - " \"\"\"\n", - " state.clock += 1\n", - " \n", - " if flip(p1):\n", - " bike_to_wellesley(state)\n", - " \n", - " if flip(p2):\n", - " bike_to_olin(state)\n", - " \n", - " if state.t_first_empty != -1:\n", - " return\n", - " \n", - " if state.olin_empty + state.wellesley_empty > 0:\n", - " state.t_first_empty = state.clock" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "run_simulation(bikeshare, 0.4, 0.2, 60)\n", - "decorate_bikeshare()" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin0
wellesley12
olin_empty8
wellesley_empty0
clock60
t_first_empty30
\n", - "
" - ], - "text/plain": [ - "olin 0\n", - "wellesley 12\n", - "olin_empty 8\n", - "wellesley_empty 0\n", - "clock 60\n", - "t_first_empty 30\n", - "dtype: int64" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "bikeshare" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap04soln.ipynb b/code/soln/chap04soln.ipynb deleted file mode 100644 index ea84c0e0a..000000000 --- a/code/soln/chap04soln.ipynb +++ /dev/null @@ -1,1190 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 4\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim library\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Returning values" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a simple function that returns a value:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "def add_five(x):\n", - " return x + 5" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's how we call it." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "8" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "y = add_five(3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you run a function on the last line of a cell, Jupyter displays the result:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "10" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "add_five(5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But that can be a bad habit, because usually if you call a function and don't assign the result in a variable, the result gets discarded.\n", - "\n", - "In the following example, Jupyter shows the second result, but the first result just disappears." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "10" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "add_five(3)\n", - "add_five(5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When you call a function that returns a variable, it is generally a good idea to assign the result to a variable." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "8 10\n" - ] - } - ], - "source": [ - "y1 = add_five(3)\n", - "y2 = add_five(5)\n", - "\n", - "print(y1, y2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Write a function called `make_state` that creates a `State` object with the state variables `olin=10` and `wellesley=2`, and then returns the new `State` object.\n", - "\n", - "Write a line of code that calls `make_state` and assigns the result to a variable named `init`." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def make_state():\n", - " state = State(olin=10, wellesley=2)\n", - " return state" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin10
wellesley2
\n", - "
" - ], - "text/plain": [ - "olin 10\n", - "wellesley 2\n", - "dtype: int64" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "init = make_state()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Running simulations" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the code from the previous notebook." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "def step(state, p1, p2):\n", - " \"\"\"Simulate one minute of time.\n", - " \n", - " state: bikeshare State object\n", - " p1: probability of an Olin->Wellesley customer arrival\n", - " p2: probability of a Wellesley->Olin customer arrival\n", - " \"\"\"\n", - " if flip(p1):\n", - " bike_to_wellesley(state)\n", - " \n", - " if flip(p2):\n", - " bike_to_olin(state)\n", - " \n", - "def bike_to_wellesley(state):\n", - " \"\"\"Move one bike from Olin to Wellesley.\n", - " \n", - " state: bikeshare State object\n", - " \"\"\"\n", - " if state.olin == 0:\n", - " state.olin_empty += 1\n", - " return\n", - " state.olin -= 1\n", - " state.wellesley += 1\n", - " \n", - "def bike_to_olin(state):\n", - " \"\"\"Move one bike from Wellesley to Olin.\n", - " \n", - " state: bikeshare State object\n", - " \"\"\"\n", - " if state.wellesley == 0:\n", - " state.wellesley_empty += 1\n", - " return\n", - " state.wellesley -= 1\n", - " state.olin += 1\n", - " \n", - "def decorate_bikeshare():\n", - " \"\"\"Add a title and label the axes.\"\"\"\n", - " decorate(title='Olin-Wellesley Bikeshare',\n", - " xlabel='Time step (min)', \n", - " ylabel='Number of bikes')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a modified version of `run_simulation` that creates a `State` object, runs the simulation, and returns the `State` object." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(p1, p2, num_steps):\n", - " \"\"\"Simulate the given number of time steps.\n", - " \n", - " p1: probability of an Olin->Wellesley customer arrival\n", - " p2: probability of a Wellesley->Olin customer arrival\n", - " num_steps: number of time steps\n", - " \"\"\"\n", - " state = State(olin=10, wellesley=2, \n", - " olin_empty=0, wellesley_empty=0)\n", - " \n", - " for i in range(num_steps):\n", - " step(state, p1, p2)\n", - " \n", - " return state" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now `run_simulation` doesn't plot anything:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
olin0
wellesley12
olin_empty3
wellesley_empty0
\n", - "
" - ], - "text/plain": [ - "olin 0\n", - "wellesley 12\n", - "olin_empty 3\n", - "wellesley_empty 0\n", - "dtype: int64" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "state = run_simulation(0.4, 0.2, 60)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But after the simulation, we can read the metrics from the `State` object." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "3" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "state.olin_empty" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run simulations with different values for the parameters. When `p1` is small, we probably don't run out of bikes at Olin." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "state = run_simulation(0.2, 0.2, 60)\n", - "state.olin_empty" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When `p1` is large, we probably do." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "14" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "state = run_simulation(0.6, 0.2, 60)\n", - "state.olin_empty" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## More for loops" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`linspace` creates a NumPy array of equally spaced numbers." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([0. , 0.25, 0.5 , 0.75, 1. ])" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "p1_array = linspace(0, 1, 5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use an array in a `for` loop, like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.0\n", - "0.25\n", - "0.5\n", - "0.75\n", - "1.0\n" - ] - } - ], - "source": [ - "for p1 in p1_array:\n", - " print(p1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This will come in handy in the next section.\n", - "\n", - "`linspace` is defined in `modsim.py`. You can get the documentation using `help`." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Help on function linspace in module modsim:\n", - "\n", - "linspace(start, stop, num=50, **options)\n", - " Returns an array of evenly-spaced values in the interval [start, stop].\n", - " \n", - " start: first value\n", - " stop: last value\n", - " num: number of values\n", - " \n", - " Also accepts the same keyword arguments as np.linspace. See\n", - " https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html\n", - " \n", - " returns: array or Quantity\n", - "\n" - ] - } - ], - "source": [ - "help(linspace)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`linspace` is based on a NumPy function with the same name. [Click here](https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html) to read more about how to use it." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** \n", - "Use `linspace` to make an array of 10 equally spaced numbers from 1 to 10 (including both)." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.])" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "linspace(1, 10, 10)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** The `modsim` library provides a related function called `linrange`. You can view the documentation by running the following cell:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Help on function linrange in module modsim:\n", - "\n", - "linrange(start=0, stop=None, step=1, **options)\n", - " Returns an array of evenly-spaced values in the interval [start, stop].\n", - " \n", - " This function works best if the space between start and stop\n", - " is divisible by step; otherwise the results might be surprising.\n", - " \n", - " By default, the last value in the array is `stop-step`\n", - " (at least approximately).\n", - " If you provide the keyword argument `endpoint=True`,\n", - " the last value in the array is `stop`.\n", - " \n", - " start: first value\n", - " stop: last value\n", - " step: space between values\n", - " \n", - " Also accepts the same keyword arguments as np.linspace. See\n", - " https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html\n", - " \n", - " returns: array or Quantity\n", - "\n" - ] - } - ], - "source": [ - "help(linrange)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use `linrange` to make an array of numbers from 1 to 11 with a step size of 2." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 1., 3., 5., 7., 9., 11.])" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "linrange(1, 11, 2, endpoint=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Sweeping parameters" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`p1_array` contains a range of values for `p1`." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ])" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "p2 = 0.2\n", - "num_steps = 60\n", - "p1_array = linspace(0, 1, 11)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following loop runs a simulation for each value of `p1` in `p1_array`; after each simulation, it prints the number of unhappy customers at the Olin station:" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.0 0\n", - "0.1 0\n", - "0.2 0\n", - "0.30000000000000004 2\n", - "0.4 5\n", - "0.5 0\n", - "0.6000000000000001 22\n", - "0.7000000000000001 23\n", - "0.8 26\n", - "0.9 29\n", - "1.0 40\n" - ] - } - ], - "source": [ - "for p1 in p1_array:\n", - " state = run_simulation(p1, p2, num_steps)\n", - " print(p1, state.olin_empty)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can do the same thing, but storing the results in a `SweepSeries` instead of printing them.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "sweep = SweepSeries()\n", - "\n", - "for p1 in p1_array:\n", - " state = run_simulation(p1, p2, num_steps)\n", - " sweep[p1] = state.olin_empty" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And then we can plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap02-fig02.pdf\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3Xl8XGXZ8PHfJE2apG2aNE2a7nuv0hYoS8siSMv2CPoAAr7vIyiibCoiCgiyL4IsD5sLKoiCsqj4Iu4oUvattFBK14uu6ZombdKmadM0y7x/3GeSyWQ7k86aXN/PJ5/knDlzzjWTmXOd+z73EggGgxhjjDGpJiPZARhjjDEdsQRljDEmJVmCMsYYk5IsQRljjElJlqCMMcakJEtQxhhjUpIlKJMQInKhiDSGLc8RkaCIjEpmXN0RkSdF5OWw5dtEZHWMj7FeRG6K5T4PRGQ8iYjP+yx8KZ7HMOmnX7IDMOlNRIYBNwH/DYwAdgGvA3eq6kddPPUdYDhQcYDH/wFwiaqWhq3LALYD+ztZ/xNVvfVAjpuOROQ14ISwVdXAIuAmVX03bP0sYG8CQzOmQ1aCMj0mIqOBhcCxwDeAScBngQbgPRH5TGfPVdX9qlquqs0HGMbLwDARmRG27nDcZzung/WFwLwDPGY6exZ3YTAcmAtUAS+KyMDQBqpaqap7khRfTIlIdrJjMD1nJShzIB4BsoC5qlrjrdsAfFFE/gk8KSLjVbUu8okiMgd4FRitqpvClk8FbgaOBNYD31XVf3cRw7u4q/2TgKXeuhNxpbiMDtbvAd4Li+N/gO8DU4Fy4E/AzdGcoEXkFOA2XAKsAl4CrlHVHd7j04EHgaNw79cG4Ieq+lQn++uHK5V+BZdI1gA/VtVHvcd/AwxX1VMjnvcqUKaqF3YRbp2qlnt/l4vIHcC5wGRcaQoRWQ88rqp3dhLfycDzwB2q+kAM34N8EXkKOAvYiSvp3hd23POAK3H/qwZgPu7z8Yn3+DhgHfAl4HxcafHnwDUiMgm4F/d5CAILgKtVdUkX75VJMitBmR4RkUJcaemnYckp3N3AMOCUKHd9P/BD4FBc6ewPIlLQ2caquh94C3fiCTkReMX7iVz/pvccRORC3AnsAWAacAFwMvALv8GKyInAX4DfA4fgTq7jgBdEJOBt9jtgB66keTBwFa56rTOPA2cDlwEHAXcA94rIRd7jvwBOFpHxYXFMxJ2QfxlF7HnAhbhqz1U+n3M+8GfgG2HJKVbvwa3AG8BM4H+91zw37PH+wA9wSfAUoAn4RwelpHtxJcWDgUe8aui3cNXJxwNHAwq8JiLFfl63SQ4rQZmemoy7wFnWyeOh9RLlfm9X1X8BiMi1wJdxV91dlaLmATd6JY8AcBxwrRffbRHrw+893QZcH3YVv1ZEvgW8LiLfVtWukkjILbjSzU9CK0TkK0AZLsl+BIwFHlTV5aHjdLYzL+lcAExT1ZXe6nUiIsAVwK9U9V0RWQpchCtpAVwMrFDVt7uJ9yteqRFgAC45nauqtd29UBG5Ble6/byq/ifsoVi9B39Q1VCC/bGIfBNXon4VQFWfiIjnQlzSmwWEv+5HVfXpsO1uA9ar6jfC1n0bOB1X0nq4u9duksMSlOmpQDeP93QU4paGFapaLiJNuJIYIvILXPVNyDRV3YBLUPfiTlT9cFV+oaqbhrD1A3D3rPCunMcCD4rI/WH7DL2uSbhqoO7MAo72Elukyd7ruR943Duhvgb8VVU/7GR/R3oxLHQ5qUU/XIkh5FHgBhG51dv+Qtx70J0XgBu8vwcDXwT+IiJzumnUcilQAnxKVT+IeCxW70Hk8Tfj/e8BRGQm7gJjJjCU1v/VWNomqPc7iO8IEYlMwrlefCZFWYIyPbUKaAZm4E56kUKNEzTK/e7vYF2oKvoW3IkuZIv3exHuvsdJQCbwmqoGAUTk9bD1lcDHEfu8Eu8KPcImn/Fm4BJDR/eTygFU9Qci8gzwGVw14w0icp+qdtR0OxTXsbRvSRee9J/yjvtZ7zmFwG99xFujquHN5D8Qkf/GVbld0MXz3sU1qrhIRD4Mvb9hMcfiPYj83we9fYeqI1/CVdV9LbRfXEk9soov8v5hBu4ipqMEuquDdSZFWIIyPaKqVSLyInC5iPyog/tQNwDbgP+0f3aPj1lBB83SVbXZayBwEu5k9Luwh18FzvHWvxI6sarqNhHZCEhYtVJPLASmR5z0O4p9LfAz4Gci8n3ge7RWz4ULlU7GqOrfu9hfjYj8HrgE99qeV9WqnrwAoBHI62abJbh7YfOALBG5NCxJxfo96MhBQDFwo6quABCRY+m+JB+K70Jgc0cNdkzqsgRlDsTluP5Mr3gdOZcBpcB3cVfbZyXwhDAPeAh3wrokbP0ruFJXgPZX0DcCvxKRnbgb/w24E+FpqnqZz+PeArwkIg8BvwF246qNvuAdLxNXunge18KsAFeKWN7RzlR1tYj8Gvildw/uXVzV5BFAsaqGV+M96j0ObRuDdCVXREJ9w0JVfNNwDVO6pKrLROQE3Hv6hIh8zesmENP3oBNlQD1whYg8gGuEcQ/+qpJ/irtf92cRuRPYCIwCTgP+oarvRBGHSSBrxWd6TFXLcPdM5uNOlmuAF3GtrY4JNXZIkHnecStDzY69GJfjmixn491/CnvsKeD/4KrJ3sfdc7oNd+/DF1V9FVdldTDwJq4K8SHcSboBVzopBH4FrMA19tgGnNfFbi/19nEj7iQ+D9fkvE3DAlVdgCvZrFHV132GfB6w1ftZAJwBfE1Vn/HzZFVVXGvBE4GnRCQzTu9B5HG34+4/noK7ELofuAZXzdzdc7cBx+AahPwJV+38DO7e1Va/MZjEC/RkRl2vU98JwCeq6qt5qjEmtrzWiWW41nEPJDseY2LNVwlKRJ71mmUiIlm4K+a/ActE5HNxjM8YE0FEMrxquhuBgbh+U8b0On6r+ObQ2ozzv4FBuB7ut+H6RRhjEmcMrmrqMuCrqmot0Uyv5LeRxBBcnTG4OuA/ea2gnsUNE2OMSRBVXY+/1mvGpDW/CaoSGI/rG3IKcLW3Pg8fNykTRUT64zrlbaVtp0ZjjDHJlYmreVugqvV+nuA3Qf0ReEZEPgHyae3bMhOfY3glyCxcKyJjjDGp6Xhch+tu+U1Q1+L6DozFjQAc6uE+gigGp0yArQDPPPMMpaWl3W1rjDEmQcrLyzn//PMhiqb93SYor9XeXcAjXr+XFqp6f8fPSpomgNLSUkaNSumJWo0xpq/yfful21Z8qtoAfBO7KWuMMSaB/DYzfx03eKUxxhiTEH7vQT0D3OPNWLmAiNGCbSwrY4wxseY3QYUm/+poCuggrvlgVETkBdzMm3NV9TVv3VG4acRn4MYdu1pVX4x238YYY9Kf3wQ1vvtN/BORL+NGaA5fV4QbaPQp3Lw0Z+KmjD44VuP91dTUUFFRQUNDQyx216tkZWVRUlJCfn5+skMxxhjAZ4KKbL13IERkJK4kdjxuoMuQ84Ea4DvePDPLReQ03HAu1xzocWtqati2bRsjR44kNzeXQMDafIQEg0Hq6urYvNkN4m1JypjUUVG9Fy2rZvDAbMaW5jN4YP9kh5QwvueDEpG5wBW4qbBPV9VNInIRbqj/16I45uPAD1V1Q8SU1rOBVyNm6pwHnBzFvjtVUVHByJEjycvrbl62vicQCJCXl8fIkSPZsmWLJShjUkRDYzN/e3MtdfWN3prNFAzqz9jSfMYNz2fE0AFkZvbeWZN8JSgR+TxultJngCm0TrGci+vE+5rP/VwGZKnqox08XIKbujtcpbf+gDU0NJCbmxuLXfVaubm5Vv1pTApZvXFnWHJydu6uZ+fuShavqiSrXwajSgYxtnQQY4fnMygvu5M9pSe/JaibgG+p6uMi8n/C1r+Dm9q7WyIyBriVzpurx73Ozar1umbvjzGpZena7S1/F+XnULNnPw1NrcOfNjQ2s27LLtZt2dWyzZjhrnRVWjSAzIz0/k77TVBTiZiN1FONG+ncj8Nx04GvjqjamyciT+JGS48sLRUDFT73b4wxvUZF1V62VblR5TIzApw1ZxJZ/TLYUllLWfluyrbWsLO27ZirO2r2saNmH4u0guysTEYP80pXpfkMyM1Kxss4IH4TVDVuFNr1EesPxf/02POAQyLWLQEuBl4CzgG+G/H4ibjJEU0H/vSnP/HTn/6UV155BYDvf9/NfHLPPfckMyxjTAwsW7ej5e9JowrI7e9O12NK8xlTms/xM0eyc3c9ZeU1lJXXsLmilqbm1lv4+xuaWLNpJ2s27QSguCCXscPzGVuaz7AheWSkQenKb4J6HrhLRM70loMiMg24B/iDnx2o6m5gafg6ryS1TlU3i8gzwG0i8jDwKHAGcBQugfVJa9as4eGHH2b+/Pns27ePCRMmcMEFF3D22Wd3uP2NN96Y4AiNMfFQ39DEJ2XVLcszJg7tcLuCQf0pGFTMoZOLaWhsYnPlHsq2uoRVs2d/m20rd9ZRubOOhSu2kZPdz5Wuhg9izLBB5OWkZunKb4K6Afg7rhquP7AQKMBV+90ei0BUdYeInI7rqPsNXEfds2PVByrdqCrnnXcec+bM4YknniA/P58333yTO+64gy1btvCtb32r3XMGDRqUhEiNMbH2SVl1y72movwcSou6b32c1S+Tcd79p2AwSPXuei9Z7WZLZS3NwdbS1b79jazaWM2qjdUEAgFKCltLVyWFqdMNx28/qD3AXBGZAxyJG8Nvoaq+ciAHV9VAxPJ7wBEHss/e4s4772Ty5Mncf//9LR+W8847j4yMDO644w7OPPPMds+JrOI78cQTueCCC/jggw944403GDlyJLfddhuzZ89O3AsxxkQlGAyydE1r44gZE4dGnTACgQBD8nMYkp/DYVLC/oYmNm7bTVn5bjaU11Bb19paNxgMss273/X+snJy+/draRU4etggcrJ990aKuaiO7PV3ei0ukZgWVVVVvP/++zz00EPtPpjnnHMO9913Hy+//DKDBw/udl+//OUv+d73vsfVV1/NY489xjXXXMO8efPIykrNIr0xfd3WHXvYUbMPgKx+GcjYwgPeZ3ZWJhNHFTBxVAHBYJAdu/axfmsNG8pr2LpjL8Gw0lVdfSMry6pZWeZKV8OL8hhTms/4EfkUDU5sV51oOupOxTVaKCFiFHRVvSXGccXdIq3g/eXlNDQmZsb6rH4ZzJ5WymHSfbeujRs3AjB+fPsRprKyshg9ejRlZWUcckhkm5P2TjvtNM466ywArrzySp5//nk2bNjAxIkTo3wFxphEWLamtXHElDGFZGdFPdRplwKBAEMLchlakMuRBw1j3/5GV7raupuy8po2/a6CwSBbtu9hy/Y9zF9WzkmzRjN1rN+G2wfOb0fdy4EfA7twzb7DR3sIAmmXoD76pDJhyQlcf4WPPqn0laBCVzOdFevDr3a6M2XKlJa/S0rcsauqqixBGZOC6uobWe21ugOYMaHjxhGxlJPdj8mjC5k8upBgMEhldR3ry2so21pDRXVdy/kmGAyyuaI29RIUcD1wI3BvxFBEaWvmlOKEl6BmTin2te2YMWMA14pv6tSpbR5raGhg06ZNfP7zn/e1r379Wv/FoYTX3Jy4xGyM8W/F+qqWpuLDhuRRXJjYKrVAIEDJkDxKhuQxe1ope/c1tNy7am4OcuRBwxIaj98ElQc811uSE8BhUuKrNJMMQ4YMYdasWTz11FOcfvrpbUpSzz//PPv27ePkk09mwYIFSYzSGBNLwWCQZWtbq/cSUXrqTl5OFjJ2CJLAUlM4v6MMPo3rl2QS5KabbkJVueaaa1ixYgWbNm3i2Wef5Z577uGb3/wmo0ePTnaIxpgY2lRRyy5vZIj+2ZlMGl2Q5IiSz28J6hrc3ExzgY+BNiOKquodsQ6sr5s6dSrPPfccDz/8MBdccEFLR92bb76Zc845J9nhGWNiLLxp+dSxQ8jq13tHKffLb4K6GPgMUAtMp30jCUtQcTB58mQeeeSRTh8/++yz24wqETnEUWgIpHCqGrsAjTExUVvXwLotNS3L0ycUJTGa1OE3Qd0K3Azc3ZvuQxljTCpYvm5Hy0gPI4sHMiQ/J8kRpQa/Zcj+wO8tORljTGw1NwdZHt44YqKVnkL8JqhnsUYSxhgTc2VhQw/l9u/HhBHdjxDTV/it4tsO3CIinwYW076RxA9jHZgxxvQFS8IaR0wbX9Srp3CPlt8E9WXcKBKHeT/hgkBaJKhgMJgyo/SmomhGqDDGHLhdtfVs3FYLuE6y08Ynp79RqvI7mnn7QeHSTFZWFnV1deTldT9sfV9VV1dng8gak0DL1u5ouTAcM2wQgwf2T3JEqSXqsqSI5IhI2jUxKSkpYfPmzezdu9dKChGCwSB79+5l8+bNLeP1GWPiq6mpmRXrq1qWrXFEe9GMZv5V3Hh847zldcBdqvpkXCKLsfz8fAC2bNlCQ0NDN1v3PVlZWQwbNqzlfTLGxNeazbtaRg4fmJvF2FL77kXyO5r5lbjp3X8OvA4EgBOAn4nIIFX9SfxCjJ38/Hw7ARtjUsLSsGk1pk8oIiPD7o9H8luCugK4UlUfC1v3ZxFZCXwPSIsEZYwxqaCqZh9btrvGERmBAAeNt+q9jvi9BzUamNfB+nneY8YYY3wKn5Rw/MjBDMy1xkkd8ZugNgFzOlg/x3vMGGOMDw2NTawsC2scYePudcpvFd/PgR+LyCTgTVzfpxNwVX9pN5uuMcYky6qNO6lvaAKgYGB/RpUMTHJEqctvP6j7RaQOuM77AVdyukZVfx6v4IwxprcJn5Rw+oQiGzygC76bmavqI8AjIjLIW94dt6iMMaYXqqjay7aqvQBkZgSYOs5GjuiKr3tQIvKKiBSAS0yh5CQi+SLSftIhY4wx7SwNKz1NGlVAbn/fZYQ+yW8jiTlAdgfr+wPHxywaY4zppeobmli1obplecbEoUmMJj10mb5FZEzY4qiIIY4ygdOB8ngEZowxvcknZdU0NDUDUDQ4l9IiGxe0O92VL9fjWuwFgQUdPN4MXB/jmIwxplcJBoMsDZtWY4Y1jvCluwR1PG5YozeAM4GqsMf2A2WqWhGn2IwxplfYumMPO2r2AZDVLwMZW5jkiNJDlwlKVd8GEJHxwAab8t0YY6IXPnLElDGFZGdlJjGa9OG3CUkBMBBYBiAipwNf9ZbvVNXG+IRnjDHpbe++BlZv2tmyPGOCNY7wy28rvkeBgwFEZBTw/3AJ6xLgzviEZowx6W9lWTVNza7yadiQPIoLc5McUfrwm6AEWOT9fTawQFVPAy4A/m88AjPGmHQXDAbbjBxhpafo+E1Q2cA+7+85wIve358ApTGOyRhjeoWN23azq7YegP7ZmUwaXZDkiNKL3wSlwLlev6hTgJe99cOB6k6fZYwxfVh46Wnq2CFk9fN7yjXgP0HdDvwQWAe8paoLvfWn0lr1Z4wxxlNb18C6LTUtyzatRvR8JShV/QswBjgC+GzYQ/NwM+oaY4wJs3zdDpqDrnHEyOKBFObndPMMEyma0cy3Adsi1r0bzcFE5Hbgi7hZeGtwVYVXq2q59/hngfuB8cBS4Buq2tEIFsYYk7Kam4MsD28cMdFKTz3hK0GJyK+7elxVv+bzeCuBy3BVhSXAA8BvgP8SkanAn4DbgL8AXwdeFJHJqmr3uYwxaaOsvIbaugYAcvv3Y8KIwUmOKD35LUGNjljOAqbhWve97/dgqvq7sMX1InIfEFp3KfCeqt4NICJX4oZXOh/4qd9jGGNMsi0JG3dv2vgiMjOtcURP+J1R95TIdSLSH3gCeL0nBxaRwcB5wNveqtnAf8KOGfTmmjoKS1DGmDSxq7aeDeVuPtdAIMB0axzRYz1O66paj2vZd0M0zxOR80WkFtiJu9f0Re+hEiBy4NlKb70xxqSF8KblY4YNIn9AR1PpGT8OtNw5EIi2cvWvwGHASUADbhglcKOmG2NM2mpqambF+tZJH6xxxIHx20jivIhVAWAEriFDVFV83nTxu4FVIqLAJhGZhmshGFlaKqZ9qcoYY1LSms27qKt3Y2cPzM1ibGl+kiNKb34bSTwdsRzEJY6XgWsO4PihUlMTrrHFXFyn4JC5uGbnxhiT8pauCW9aPpSMDKsYOhB+G0kccBMUEcnCNSH/My65jQZ+AHwIrAIeAxaLyHW4asDLcFWIzxzosY0xJt6qavaxZXstABmBAAeNG5LkiNJfIts+BoGDcMnnE1ziWQ18TlWbVXUlcA5wIfARbjbf060PlDEmHYRP6T5+5GAG5GYlMZrewe89qB8Ba1X1RxHrvw2MU9WrutuHN6nh2d1s83fg735iMsaYVNHQ2ISWtV5L27h7seG3BHU20NGwRu8C58YuHGOMST+rNu6kvqEJgIKB/RlVMjDJEfUOfhNUMa5PUqQdWD8lY0wfF944YvqEIgIBaxwRC34T1CbgmA7WHwNsiV04xhiTXiqq9lJRvReAzAxrHBFLfpuZ/xZ4UETqaB2O6FTcYK+PdvosY4zp5ZaGjRwxeXQBOf19TxJhuuH3nbwLmAg8j2uNB64P0zPAHXGIyxhjUl59QxOrNrQ2jpg+YWgSo+l9/PaDagK+IiJ3AId7qz9U1TVxi8wYY1KcllXR0NQMQNHgXEqL8pIcUe8SVVnUS0iWlIwxfV4wGGTZmraTElrjiNiySUqMMaYHtm7fw46afQBk9ctAxhQmOaLexxKUMcb0QHjjiCljCsnOykxiNL2TJShjjInS3n0NrNm0s2V5hjWOiAtLUMYYE6WV66tpanYNmocNyaO4MDfJEfVOvhKUiPxaRI6OdzDGGJPqgsEgS9e2Dgxrpaf48duKbzzwtogsx02L8ZSq7uzmOcYY0+ts3Labmj37AeifncnkMQVJjqj38lWCUtW5wBTcSOPXA1tE5CkROT6ewRljTKoJbxwxdewQ+mXanZJ48f3OquoaVb0eN9HgeUAhME9EVojId0RkULyCNMaYVFC7dz/rt9S0LNu0GvHVk9TfDDR6vwNAHW7a9w0icmYMYzPGmJSyfH0VzUHXOGJUyUAK83OSHFHv5jtBichYb6ijDcCzQDlwjKoeDowFHgYeiUuUxhiTZM3NQZavbTuthokvv634/o0b4uhM4G5gpKpeqqoLoWWsvkeBEfEK1Bhjkmn91hpq6xoAyMvJYsKIwUmOqPfz24pvK3Ccqr7XxTbbcK39jDGm1wlvWn7QuCFkWuOIuPM7mvmFPrYJAmUHGpAxxqSaXbX1bCjfDUAgELDqvQTxPZq5iJwFXAVM81atAB5U1RfiEZgxxqSKZWH3nsYMG0T+gOwkRtN3+L0H9V3gj8BG4BbgVlxjiT+IyFXxC88YY5KrqamZFeurWpZnTLTSU6L4LUFdA1ylqj8JW/eIiLwHfB94MOaRGWNMClizeRd19Y0ADMzNYmxpfpIj6jv83uUbDLzYwfoXARvnwxjTay1tMynhUDIybFLCRPGboP4FnNrB+v8CXopdOMYYkzp27Kpjy/ZaADICAQ4aNyTJEfUtfqv43gDuEJHZQKip+dHA57z154U2VNVnYxuiMcYkVl19I8vW7mDpmtam5eNHDmZAblYSo+p7/Caoh73fF3g/HT0GEMSNMmGMMWln+846Pl5dyScbdtLY1NzmsUMm2bQaiea3H5T1SDPG9ErNzUHWb61h8apKNlfWtns8LyeLo2eUMrJ4YBKi69t894MyxpjeZN/+Rlaur+Lj1dtb5ncKV1KYx6GThzJpVIGNGpEk1lHXGNOnVO/ex8ertrOyrIqGxrbVeBmBABNHDeaQScWUFuURCFiLvWTylaC8jrr3Ac8Bv8dNs3EsrqPu91XV+kEZY1JWMBhkw7bdLF5V2TJkUbic7H5MnzCEgycOZWCejRKRKqyjrjGm12pobGLl+moWr65k5+76do8X5edwyORipowpJKufVeOlGr8JqquOuvfELhxjjDlwu2rrWbJmOyvWVVHf0NTmsUAgwLjh+RwyaSijSgZaNV4K85ugQh11V0est466xpiUEAwG2bJ9D4tXVbJuSw1Bb+bbkOysTKaNd9V4gwf2T1KUJhrWUdcYk9Yam5r5ZEM1H6/ezvadde0eLxjYn0MmD2Xq2CFkZ2UmIULTU9ZR1xiTlmrrGliyejvL1+1oGcw13Jhhgzh0cjFjSgdZNV6aso66xpi0Ur5jD4tXbWfNpp00R1TjZWVmIGMLOWRyMUPyc5IUoYmVhHXUFZEbgS8Ak4Fq4E/ADapaG7bNFOAx4CigHLhdVZ9MVIzGmNTU1NTMms27WLyqkm1Ve9s9nj8gmxkThzJt/BBysm38gd4imo66hcBngLFAm44CqnqHj10ci+tL9QEwDJeIBgFf9fafBfwD+BCYhUtSj4nIOlV93W+cxpjeY+++Bpavq2LJ6u3s2dfQ7vERQwdy6OShjB8x2KbB6IX8dtSdhWvJFwDygUqgBNgLbAW6TVCq+tnwRRG5GXg0bN1pwEhgpqruAZaKyAnAFYAlKGP6kGAwyPJ1Vby1eHO70R4yMwJMGVPIIZOKKS7MTVKEJhH8lqD+F3ge+DqwC/gUsB/XIOKhHh57KLAzbHk2MN9LTiHzgDt7uH9jTBrat7+RVz/YxJpNO9usz8vJ4uCJRUyfUERejk170Rf4TVAzgW+oarOINAPZqrpWRK4Dfg1ENR6fiAzGjU7x67DVJUBFxKahkpoxpg/Ysr2Wl94ro7autTqvYFB/Zh00zAZt7YP8JqgmXIkJXBIZDawEtuPuSfkmIv1xpbG1tB2FwiqQjemjmpuDLFyxjQUrtrXpYDtjQhGfOnSkDUPUR/lNUB/jSlFrcB11bxCRDOASQP0eTET64QabHQScpKrhnRe2AZMinlJM+1KVMaYXqdmzn//ML2Prjtba/f7ZmZx4xGgmjipIYmQm2fwmqLuA0GxdN+Na272Iq4I7188OvIT2W1wSOiG8ebnnfeAqEclT1VA70hOB+T5jNMakmVUbq3ntg01txssbWTyQU2aPsVHFje+Oui+H/b0emC4iQ4BqVQ12+sS2HgPmAKcD2SJS6q2vVNUmXCvBLcCvROROXDPzLwKn+Ny/MSZNNDQ28eZHm1nxMDjEAAAgAElEQVS+rqplXUYgwOzppRwuJdZk3AAH0FFXVau636qNi7zfiyLWjwfWq+p+Efksrun5B7iOupdaHyhjepeK6r28NL+szfQX+QOyOfWosZQWDUhiZCbV+O0HFcCNwXcqrpNtmzuWqnpid/tQ1W4viVRVcaUsY0wvEwwG+eiTSt5dupXm5taKlyljCjnh8FH0t4FcTQS/Jaj7gO8ALwPrcYPCGmOML3v3NfDy+xvYsK11NtusfhmccPgoZEyhDeZqOuQ3QX0ZOE9V/xjPYIwxvU/Z1hpeXrChzYjjJYV5nHrUWAoG2bxMpnN+E1QWbow8Y4zxpampmXeWbGXxqsqWdYFAgMOmFHPU9FLrdGu65TdBPQWcg6vqM8aYLlXV7OOl+WVtJhAckJPFybPHMHrYoCRGZtJJpwlKRG4IW9wJXCcixwIf0TqqBACq+sP4hGeMSSehQV7f/GgzjU2tg7yOH57P3CNH2xh6JipdlaAuiViuAQ71fsIFAUtQxvRx++obefXDtoO8ZmYEOO7QkcyYWGQNIUzUOk1Qqjo+kYEYY9LXlspaXprfdpDXovwcTj16LEWDbUoM0zM29aQxpseam4MsWF7OwpUVbQd5nTiU4w4dQT9rCGEOQDQz6p5A5x11vxbjuIwxKa5mz35eml9GedggrznZ/Thp1mjGjxicxMhMb+F3JInvAffiRi7fjHXUNaZP+2RDNa99uIn9YYO8jioZyMmzxzIw1xpCmNjwW4K6ArhSVX8Sz2CMMamtobGJNxZtZsV6G+TVxJ/fBFUA/D2egRhjUltFlTfIa60N8moSw2+C+jNubqZfxTEWY0wKCgaDLPqkkvciBnkVb5DXbBvk1cSJ3wT1LnCniMwAFtO+o+6zsQ7MGJN8e+oaeHnBBjZGDPI65/BRyNghSYzM9AV+E9Qj3u8rO3gsCFiCMqaXWbdlF68s3NhmkNdhQ9wgr4MH2iCvJv78zqhrnRmM6SOqa/bx1uItlJXXtKwLBAIcLsXMnj6cTGsIYRLEOuoaYwDYt7+RBcu3sWT1dprDOt0OzM3ipFk2yKtJPL/9oG7p6nFVvSM24RhjEq25OcjydTuYv6y8TXVeIBBg2vghHD1jOLn97VrWJF40ExaGywJGAvuArYAlKGPS0ObKWt78aHObaTEARgwdyPEzR1JcaOPomeTxew9qcuQ6ESkBfgM8GuugjDHxVbNnP29/vKXNyOPg+jUde8gIJo4cbKOPm6TrcbldVStE5CZcC74/xy4kY0y8NDQ28cHKChZpBU1hfZqyMjM44qBhzJxSbAO8mpRxoBXLDcCIWARijImfYDDIJxuqeXfJ1jZTYoDrcHvMwcMZmJedpOiM6ZjfRhLHRqwK4BLTtcDCWAdljImdbVV7efOjzW1GHQcoKczj+JkjGT7UhikyqclvCeotXIfcyErpt2k/864xJgXU1jXw3pItrCyrbrM+LyeLYw8ejowttPtMJqX5TVCRs+s2A5Wqui/G8RhjDlBjUzMffVLJByu30dDY3LI+MyPAzCnFHDF1mI2fZ9KC31Z8ZfEOxBhzYILBIGs37+Ltj7dQs6fNcJlMGDmYTx0ywoYoMmnFet8Z0wts31nHW4s3s6mits36ovwcjps50kaBMGnJEpQxaayuvpH5y8pZtnYHwbDhiXKy+3HU9FKmTyiySQRN2rIEZUwaamoOsnTNdt5fXk79/tZp1zMCAWZMLGL2tFJybHgik+Y6/QSLyKeBd1S1sbNtjDGJV1Zew9uLt1BV07aN0uhhgzju0BEUDbbhiUzv0NUl1qvAcKBCRNYCs1R1R2LCMsZEqt69j3cWb2Hd1po26wcP7M9xh45g3PB8azZuepWuElQ1rnl5BTAOsPFPjEmC+oYmFi7fxuLVlW2mXM/OyuTIg4Zx6KShZNrwRKYX6ipB/RV4VUQ24zrpviciTR1tqKpT4hGcMX1Zc3OQFeureG/p1nbTYBw0rpCjZwwnLycriREaE19dJahLgH8BU3DTafwJqO1ie2NMjOzYVcfL72+gMmIajOFFAzh+5khKhuQlKTJjEqfTBKWqTcBzACJyEnCXqu7sbHtjTGzUNzTx1zfWsmdf66CuA3OzOPaQEUweXWD3mUyf4Xckibmhv0Ukx1tnwxwZEwfvLtnakpz6ZWZwuJRwmBST1c+GJzJ9i++OEiLyVeBGXIMJRGQdrlT1ZBT7OBu4HDgSyFfVQMTjRwGPADOAtcDVqvqi3/0bk+7Kd+xh2drWxrInzRrN5NGFSYzImOTx1fRHRK4EfoZrOHEOcC7wd+BnInJFFMfLA14B7ungGEXAi7gR0g8HngJeEJF2s/ka0xs1NQd5deHGlhEhxpbmM2lUQZKjMiZ5/JagrgCuVNXHwtb9WURWAt8DfuJnJ6r6NICIzOng4fOBGuA7qhoElovIacBlwDU+4zQmbS3SCnZ4nW+zMjM44fBRdr/J9Gl+O0+MBuZ1sH6e91gszAZe9ZJT+P6PitH+jUlZO3fXs3DFtpbl2dNLyR9gM9yavs1vgtoEzOlg/RzvsVgowXUKDlfprTem1woGg7z24SYam9zcTcUFuRw6uTjJURmTfH6r+H4O/FhEJgFv4jrunoCr+rslRrFYXYbpk3RDNZsqdgOuE+7cI0bbCOTG4L+Z+f0iUgdc5/2AKzldo6o/j1Es22hfWiqmfanKmF6jrr6RtxdvaVk+ZNJQ64RrjMd3M3NVfQR4REQGecu7YxzL+8B3I9adCMyP8XGMSRnvfLylZRijgblZHD2jNMkRGZM6op4w5kASk4gMAcYAk7zlmd5Dy4FngNtE5GHgUeAMXAOJi3t6PGNS2cZtu1mxvqpl+YTDR1lnXGPCJHoI5DOARcAvveVF3s8IbyqP04HjgY+AC4GzVXVVgmM0Ju4am5p5/cPW9kUTRxUwfsTgJEZkTOpJ6JSb3qgTT3bx+HvAEYmKx5hkWbhiGztr6wE3bcbxM0cmOSJjUo9NImNMglXV7ONDbW37c8zBwxmYa9NmGBOp2xKUiGQBdwGPqGpZ/EMypvcKBt1wRqGJB0uLBjBjQlGSozImNXVbglLVBuCbWD8lYw7Y8nVVbN2xB4CMQIC5R9hwRsZ0xm8V3+vAsfEMxJjebk9dA+983Nrn6TApoWhwbhIjMia1+W0k8Qxwj4iMAxYAe8IfVNV3YhyXMb3OW4s3U9/QBEDBwP7MmjYsyREZk9r8Jqinvd93dvBYELDOG8Z0oWxrDas2tk5IfcLho+iXaW2UjOmK3wQ1Pq5RGNOLNTQ28fqi1j5PU8cWMnrYoCRGZEx68DsWn7XeM6aH3l+2jZo9+wHIye7HsYeMSHJExqSHaKZ8n4sbvXwScLqqbhKRi4A1qvpanOIzJq1VVO/lo1WVLcvHHTqCvBzr82SMH36nfP88bjr2amAKEJpJLRe4Nj6hGZPempuDvPbBppYp3EeVDELGFiY5KmPSh9+7tDcB31LVi4CGsPXvADM7fooxfduS1dupqN4LQGZGgDk2hbsxUfGboKYCL3ewvhoYErtwjOkddu/dz3vLtrYsz5pWSsGg/kmMyJj04zdBVQPDO1h/KLA5duEYk/6CwSBvfLiJhkY3hfuQ/BwOm2JTuBsTLb8J6nngrtBkhUBQRKYB9wB/iEtkxqSpNZt3sW5rTcvy3CNGk2l9noyJmt9vzQ24sfi2AXnAQmAJUAbcHp/QjEk/9Q1NvLmotVJhxoQihg8dkMSIjElffvtB7QHmisgc4EhcYluoqq/EMTZj0s67S7ayZ59rR5SXk8XRB3dUM26M8SOqCQu9/k6vxSUSY9Jc+Y49LFu7o2X5+JkjyMlO6JygxvQq0XTUPQu4CpjmrVoBPKiqL8QjMGPSSVOzm+cp1OdpbGk+k0YVJDkqY9Kb34663wX+CGwEbgFuBTYAfxCRq+IXnjHpYZFWsKNmHwBZmRmcYH2ejDlgfktQ1wBXqepPwtY9IiLvAd8HHox5ZMakiZ2761m4YlvL8lEzSskfkN3FM4wxfvhtxTcYN9RRpBcBq8cwfVYwGOS1DzfR2OT6PBUX5nLIJOvzZEws+E1Q/wJO7WD9fwEvxS4cY9KLbqhmU8VuAAKBAHMPH01GhlXtGRMLnVbxich5YYtvAHeIyGzgPW/d0cDngDviF54xqauuvpG3F7dO4X7IpKGUDMlLYkTG9C5d3YN6uoN1F3g/4R4CfhyziIxJE+98vIW6+kYABuVlc/SM0iRHZEzv0mmCUlUbm8WYTmyq2M2K9VUty58+bCRZ/TKTGJExvY8lIWOi1NjUzGsftE7hPnFUAeNHDE5iRMb0TtF01J0KnAiUEJHYVPWWGMdlTMpauGIbO2vrAcjOyuT4mSOTHJExvZOvBCUil+PuM+0CKoBg2MNBXOddY3q9qpp9fKgVLcvHHDycgbk2hbsx8eC3BHU9cCNwr6oGu9vYmN4oGHTDGTU3u6/A8KIBzJhQlOSojOm9/N6DygOes+Rk+rLl66rYumMPABmBAHOOsOGMjIknvwnqaeCMeAZiTCrbU9fAOx+39nk6TEooGpybxIiM6f2iGYvvBRGZC3wMNIQ/qKrWWdf0am8t3kx9QxMABQP7M2vasCRHZEzv5zdBXQx8BqgFptO+kYQlKNNrlW2tYdXGnS3LJxw+in42hbsxcec3Qd0K3AzcbfehTF/S0NjE64ta+zxNHVvI6GGDkhiRMX2H38vA/sDvLTmZvub9Zduo2bMfgJzsfnzqUOvzZEyi+E1Qz2KNJEwfU1ldx+JVlS3Lxx06gtz+NoW7MYni99u2HbhFRD4NLKZ9I4kfxjIoEbkeuAI319RLwKWqYb0jjYmz5uYgr36wkWZvCvdRJYOQsYVJjsqYvsVvgvoybhSJw7yfcEEgZglKRL4K3IAbNX0d8DDwO+CkWB3DmO4sWb2diuq9AGRmBJhjU7gbk3C+EpSqjo93IGGuAB5U1RcARORrwBoRmaGqS+N10IbGZj5eXcn2nfvidQiTRtZv3dXy96xppRQM6p/EaIzpm1KqQl1E+gOHAt8NrVPVtSKyHjgKiFuC0rIq3l2yNV67N2mqKD+Hw6bYFO7GJIPfwWJ/3dXjqvq12IRDEa7hRuT9pkrcKOpxMygvO567N2koMyPAnCNGk2l9noxJCr8lqNERy1nANCAbeD+G8SStkn/s8Hy+eKqwY5dV8RmnuCCXwvycZIdhTJ/l9x7UKZHrvOq4J4DXYxjPdqAZV1paEba+mPalqpgrGpxr46sZY0yK6HHdharW41rv3RCrYLx9LgbmhtaJyHhgHDA/VscxxhiT+g60kcRAINZzXf8UeFhEFgHrgYeAV+PZgs8YY0zq8dtI4ryIVQFgBPB1YlvFh6r+WkSGAb/AJb//AJfG8hjGGGNSn98S1NMRy0HcPaGXcVNxxJSq3g3cHev9GmOMSR9+G0lYO1tjjDEJlVIddWMgE6C8vDzZcRhjjAkTdl7O9PucLhNUB/eeOqSqz/o9YJwNBzj//POTHYcxxpiODQfW+NmwuxJU5L2ncOFzQ6VKgloAHA9sBZqSHIsxxphWmbjktMDvEwLBYPRzEIrIAOA64GpglarOjHonxhhjTBeibvwgIhcCClwMXEn76TeMMcaYA+a7kYSInIDrNDvV+323qtbGKzBjjDF9W7cJSkQmAfcD/w38AThLVTfEOzBjjDF9W3et+B4ALgc+AI5R1ViOXG6MMcZ0qstGEiLSDOwF3ulqJ6p6aozjMsYY08d1V8X3W9o2JzfGGGMSokfNzI0xxph4szH2jDHGpCRLUMYYY1JSbxsstlsicj1wBVAAvARcqqodTicvIlOAx4CjgHLgdlV9MkGhxpXf90FEZgI3AscBg4ClwE2q+nICw42LaD4LYc85AngPeFtV58Q9yASI8jvRD7gZ+CowDCgDLlfV/yQo3LiI8j04A7gdEKAK+H/Ataq6P0HhxpyInI1rsX0kkK+qgW62T8i5sU+VoETkq7gp6i8HjsV9GH/XybZZwD+AbcAs4E7gMa/DclqL5n3AjRRSBpwLzAT+DfxNRKYmINS4ifI9CD2nP/AkMZ6kM5l68D48CpwJXIQ7QV+MG/sybUV5XpiIS0jPANOBC4BzcBdx6SwPeAW4p7sNE3lu7GslqCuAB1X1BQAR+RqwRkRmdDCl/GnASGCmqu4Blnr/gCtI/xOU7/dBVZ+IeO6tInIucCqwMiHRxkc0n4WQO3Ff4mpgTkKijD/f74OIHIw7IU9R1XXe6vWJDDZOovksHA7UqOr93vI6EXkOOCJx4caeqj4NICJzfGyesHNjnylBeVe/h+JOMACo6lrcF+yoDp4yG5jv/QNC5nWybdrowfsQ+fwAUATsjFOIcdeT90BEPgWcAVyfgBATogfvw2eB1cD/iMhGEVkpIjeJiO/5fVJND96DD4BBInKWiAREZDTwGVy1YF+RsHNjn0lQuJNqBm6q+nCVQEkH25dEsW06ifZ9iPQt3LD5f4txXIkU1XsgInnAE7j7EnvjH17CRPtZGAdMwJWez8Ul62/jZjVIV1G9B17yOgNX1bsf2AC8rqo/jm+YKSVh58a+lKC6vOkXg+3TRY9fl4h8BldHfb6qVscupISL9j24B/i3qqZ71W6kaN+HDCAbuFBV53tVYnfh7kelq6jeAxEZAfwM95k4ApesPiMiV8UhtlSVsHNjX7oHtR1oxmX5FWHri2l/NQDuBuCkiHWdbZtOon0fABCR44E/AheparpXZ0T7HnwamCEi3/CWM4CAiDQCE1W1LJ7BxlFPvhP1Ea9XgdFxizD+on0PvgmsUdVQY4KPRWQQboaHB+MZaApJ2Lmxz5SgVLUeWAzMDa0TkfG4aov5HTzlfeAor3on5MROtk0bPXgfEJHZwN+Bq1X19wkIM6568B6cg2vBGPr5BbDQ+3tLnMONmx68D+8B/UVkVNi6SbhqrrTUg/cgj/azdTfTh86lJPDc2JdKUAA/BR4WkUW4m6APAa+q6lLvJPxb4CRV3Qz8C3fy+ZWI3Im7AfhF4JSkRB5bvt8Hr+XWv3DNi/8qIqXePupUdVcSYo8V3++Bqq4Jf6KIVAB7umjtl06i+U78G1fK+KWIXIObvvt64IGkRB470bwH/wSuFJFveX+PA35Aet+TRUSGAGPwSkZe/0eA5bgLsaScG/tS1kdVfw3cjbsCfhfYjXtjwV0ZCZDlbbsf12ppOK7lzi24m+Rpfx8imvcBV3ooBL6H6+8S+vlRAkOOuSjfg14ryu9EI+47EQAWAL/E3Y/pM58Fr4P614DLcJ3Wf4trwfedxEYdc2cAi3D/U7y/FwEjSOK50QaLNcYYk5L6VAnKGGNM+rAEZYwxJiVZgjLGGJOSLEEZY4xJSZagjDHGpCRLUMYYY1KSJag0ISJBEflSjPf5pIik/cSD8SQi60XkprDl10Tk8Rjt+0gR2SoiA2Kxv26OdZuIrI73cUx89OS7KiJfFJEF3gwEaamvjSSRMN6IC+tx01KMVtWGA9zlcNJoigsvmT7V3cycMTzeTcDFqjrO5/bnAFfieslnAauAp4GHuvlfnQ00Hli0LR4C7ouYtqBLIpID/BwX93Rgg6pGjovWkftxIyakNG8YpY3AXFV9LcnhxISIzAWeA0pVNXKYJL+uJPoCxe+Bm4DzcZ/ttGMlqPj5Gm7WyR24GUi7JCLZXa1X1XJV3RfTCHugszjTiYjcjvvyvgIcA0zDDfR5NfBPb1rzDqlqlarWxCCGI3Gzkf4myqdm4qZ5eAz3GnxR1VpV3R7lsUwnovwefB746wEkJ1R1V7QzCKhqEPgVaTzKhZWg4kBEMoBLcFc9U4FLcdNEh2+zHndVMwT4v8BaYJaIBL3nHY0bTuQl4Ave+i+r6tMi8gxQrKqnRuzzRWCXqv6PN+DlA95+CoA1uKv1p6J8LZ3FcxeuNDEWl4T/AVynqru8WTmfCns+wG9U9UJv3RW46bXH4a6WnwTu9YbS6SiGAO6EfCJu6JWtuJPz7apaLyIX4sZDCz/e7ap6Wwf7OgI3NMt1qnpf2EPrRGQlbqibb9PJyNQi8hqwWlUvDl8GyrzXlA38Fbi8m5LRl4A3VLUqbN8XAo/jJsD7ETAR+Bj4uqp+CODt8zJv+2G4Kcq7JSK3AV8KlbZCy7ikfC9uRPL3caXQNZ3sJrSvy73XOhHY5b2Oc73H1gOPq+qdYds/DkxS1Tne8nHeMQ/xNlkLXKuq/8Z9HgBeFRGAslCpWES+AlznHbcS97m5LfS58f4Xa3Cfj0tx/4tHgJtxJYnLcRflj6lqyxTt3gXJTcBXcDUVa4Afq+qjYdt09j24ATft/UigBvgQOEtV68LesrNwo6AjIk8Co4C/eK+lAPdZ/gZu2pLrgXzczAHf8oYVanmeqp4csZ/ncNPNFwKv4v5/lWHHfgF4QESmqmrazYBtJaj4OBUYgBtM8ilgjohM6GC7b+OGqD8G9+UIuRV3ojwc9+GL9FvgJBEZGVrhnaxOofWKfCBulsvPAAfjTvBPeNUN0eoonjrcSWAacCFuCvTQpG3v4CY2BPeFH477codOjNfgvogHeesv847RmQBuiP/zvOd8B/gqcIP3+B9wJ7xNYce7v/1uAHdS3ksH48ep6nu4UlW09/rOxV1ozPFiPAu4tpvnnIBLCJEygPtwJ7TZuM/HPyJGjo6V4bgT4/m4RFcA/LqrJ3ilz3txY/AdjPt8feT3gN7su3/FjXx9uPdzG+5/grcMbgzI4bhSJiLyWS+2p7zjXo1LOJGfm3NxVbbHAVfhPiN/x30fjsd99m4QkdPCnvM47mLrMtzn6w7gXhGJnOeqzfdARM4Gvo/7DE/Gff9ejHi9s3DJI/z+0SzgSG/783Cft7/g/genAV/2frqbZ2sWbhT2z+L+DzOJ+Nyr6jrcZ6gn3/uksxJUfFwGPONd2W31bm5eTOsJNWRBR1f5wJ9V9Sdd7P8/QDnug32vt+583FXlSwCqugRYEvacn4jIybgvxKvRvZz28YRfIQPrReR64Pci8lVV3S8iu7ztykMbeSfZa4GzVfVf3up13v2jH+OudNtR1WbcFW748SbiTuK3qmqdiNQCTeHH64TgSkD1nTy+jOgn4Nugqt/1/l4pIr/HXaR0lXTHA5s7WB8AvhcaeFNEvowrVZyHO5HGUn9cqbzSO9a9wLMiktNRdbLXmONa4GZVDb+f9WEUx8zHnbD/qqqrvHWrwh4PXf1XRfwvvw88r6p3e8ufePd57xGRH4RKGsA6Vb0ubJurcfeATw9bdxVwEvCiV9NwATAtrISxTlzx7QpcFVlIm++BlzTLgX959y030D5Zfx54MeL9rAcu8WJeISKh6dJHep/L5SLykhfjz9u9g6324yaPrPfi+TnehWCEzbiZkNOOJagYE5HhwOfwrvw8TwI/EpFbIqqxOrqC7mo94E7YXjXfl2lNUF/GJcUmL448XFXWf+OuRLNxJ6Rok1OH8XhXj9/BDc+fT+tsq6V0PkfSdCAXeD6sKg7cfZUcESmOqJ4IP94luCQ/Dlc67UfPagDi0Wgj8qS0GZegupILdHZP8d3QH6paLSIrcCXVWNsS8X5vxr0/JXQ8x9N0IAfvIqgnvNfzOPBvEXkFeB14QVW1m6dOx5WUw73uxTOR1skGF0dsU+79RK4LTU9+JO41L/SqFEP60X7ep8jvwXO4WpAyL6HMwyWx3WHbfB5XIgu3IiyhhuLRiIumclxprisrIp6zGRjWwXb7cJ+3tGNVfLF3Ee7DvVBEGsXNuvos7sR9RsS2nd2j8NOq6zfAdBE5QkRm0DpnS8j/4kpYd+CK9zNxVY49aeTQJh4ROQpXR/4G7gt4OPB17+Gu9h/6vH2BthMAHoyrIqnq6Eki8gXcvYQ/AKcDh+FeV0+mw1Bgkoj07+Tx6d420dgfsRyk++9WJa5a0I94tYTsKG7oPvaupkBopn28bf5PqnoJbrr0/+CqOpeKyGXdHLOj4wY6WB/ZAjPYybrQawz9Ppa2n8kZtN4jC2nzPfDmRpqKaxBVgasBUBEZDSAiB+FKLv+M2E+0MXamo/9fR5+VIbSWTNOKlaBiyGsccTHwQ+B3EQ9fh7tn86dYHEtVl4nIh7jqiXrgI1X9OGyTT+NKVH8Ii20K7l7OgToO2K6q4f2Dzo3YJnRzNzOs9dIy3NXcBFWN/NJ25dPAIlVtabggIuM6OF6mj309g6sGuRJ3r6eFl3hPxN3fiLcPccmwI0fj7oUhIgW4k+CjnWybSMtx/7//om31cbgKXEOWcIcRcfHhTfa4FHhQRH6B+248SutJN/J/uQyXzB4JW/dp3L3QtVG9irY+8H6PUdW/R/tkrwTzL+BfInIz7vt1FvAT3H2tV5I5saeI5OJKmAuTFcOBsAQVW5/BzUr5qKq2qSIRkSeA/4jIOFVdH6Pj/QZ3b6aB9o0CFDhTRJ4HanE3jEcQmwSlQLF3E/lVXML6ZsQ267zfZ4jIW7gZeGtF5IfAD73qlP/gPoMHA4eF3Tvo6HgXiciZuJPa53Bf/sjjlYrIMbh7GntVdW/ENqjqAi+Gu7xq0OdwN+jn4KpL5+FOLvH2T9wN+0hB4D7vPkk1cBfuyv3Z0AYiMo3W6tRsCZv9NKLqKKa8/98DwG0iUof7/+UCp4fdG3oZ+KaIvIBr2fh1XEvPKi/2SbgWrn/D3VsbgWu8ELqPtR33eT1VRJYB9V7z6ruBv4nI93EXeTNxjSseOJDXrKqrReTXuFmCr8VVrw7AlfCKVfXezp7rff4zcFV/O3H3jAbhEjm42oXHehpbjByHu4BNy4lWrYovti4D5kcmJ8/ruGL2xTE83rO4llclhJ3APN/FnSBexZ10NxPR1L2nvCvNu3AlxSXA/+Bm3A3fZgGupdwvcEnxp976H3ixXYy7X/CWt7y+i0M+imu99QRuls+jcCencH/GVTv+A/c+d9qKzmtifB7uhDIfWIkrNfjNRD8AAAEgSURBVD0InBaDTtV+PAOUiEhkM/FmXGOaR3FXvcOBz0Y0Wf8n7n24DNc8PHz203i7GdeS89u4i4WXaG15By7J/wNXHfsmrhn6H8Me34Orzv098AnwPGGtPr0GMZcD/weXwBZ56/+Jq0r7infch3AtCW+PwWu61NvfjbjkMs87Tncls2pca9LXcPfArsLNLDvPq+Y7DNc6L5m+hKtJqU1yHD1iM+oakyReldARqnqWt3whrg+R1WykORG5EviCqh6XxBhG4/rRHRbDWpuEshKUMcnzv8AHkoCx+EzCbaV9KT/RxuGas69Pchw9ZiUoY1KElaCMacsSlDHGmJRkVXzGGGNSkiUoY4wxKckSlDHGmJRkCcoYY0xKsgRljDEmJf1//pQdhmfp5IYAAAAASUVORK5CYII=\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(sweep, label='Olin')\n", - "\n", - "decorate(title='Olin-Wellesley Bikeshare',\n", - " xlabel='Arrival rate at Olin (p1 in customers/min)', \n", - " ylabel='Number of unhappy customers')\n", - "\n", - "savefig('figs/chap02-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Exercise:** Wrap this code in a function named `sweep_p1` that takes an array called `p1_array` as a parameter. It should create a new `SweepSeries`, run a simulation for each value of `p1` in `p1_array`, store the results in the `SweepSeries`, and return the `SweepSeries`.\n", - "\n", - "Use your function to plot the number of unhappy customers at Olin as a function of `p1`. Label the axes." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def sweep_p1(p1_array):\n", - " p2 = 0.2\n", - " num_steps = 60\n", - " sweep = SweepSeries()\n", - " \n", - " for p1 in p1_array:\n", - " state = run_simulation(p1, p2, num_steps)\n", - " sweep[p1] = state.olin_empty\n", - " \n", - " return sweep" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "p1_array = linspace(0, 1, 101)\n", - "sweep = sweep_p1(p1_array)\n", - "plot(sweep, 'bo', label='Olin')\n", - "decorate(title='Olin-Wellesley Bikeshare',\n", - " xlabel='Arrival rate at Olin (p1 in customers/min)', \n", - " ylabel='Number of unhappy customers')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Write a function called `sweep_p2` that runs simulations with `p1=0.5` and a range of values for `p2`. It should store the results in a `SweepSeries` and return the `SweepSeries`.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def sweep_p2(p2_array):\n", - " p1 = 0.5\n", - " num_steps = 60\n", - " sweep = SweepSeries()\n", - " \n", - " for p2 in p2_array:\n", - " state = run_simulation(p1, p2, num_steps)\n", - " sweep[p2] = state.olin_empty\n", - " \n", - " return sweep" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3XmYHFW5x/HvhEyWCQQIkgBJCGHJCwjKlQSUKyYkguDC7tULGNkEBRHBCLJDZFVEBBFZBISAoDeAKyAMq7IGEJXlRSA7JAECBCYZMpPM/eNUJ5Wenp7qmeqe6unf53nmmZnqWt6u7q7T59R5z6lra2tDREQka/r0dAAiIiKFqIASEZFMUgElIiKZpAJKREQySQWUiIhkkgooERHJJBVQUhFmdqiZtcb+n2BmbWY2oifj6oyZ3WBm98X+P9vMXkn5GLPM7PQ099kd+fFUIr7ovXBIOY8h1advTwcg1c3MhgGnA18CNgHeAx4CznX3fxTZ9FFgY2BRN4//Q+Ab7r5RbFkf4C1geQfLL3f3s7pz3GpkZg8C42OL3gGeBU5398diy8cBSysYmkhBqkFJl5nZSGAGsAvwLWBL4AtAC/C4me3Z0bbuvtzdF7j7ym6GcR8wzMy2iy37BOG9PaDA8vWBxm4es5rdQvhisDGwG7AYuMvM1s6t4O5vuntTD8WXKjPr19MxSNepBiXdcQVQD+zm7kuiZXOA/zWzvwA3mNlod1+Wv6GZTQAeAEa6+7zY/3sAZwBjgVnACe5+T5EYHiN8258E/DtaNpFQi+tTYHkT8Hgsjq8CPwC2BhYAtwNnlHKBNrPdgbMJBeBi4K/AFHd/O3r8o8AlwM6E8zUHON/db+pgf30JtdKvEwqSV4HL3P2q6PFfAxu7+x552z0AzHb3Q4uEu8zdF0R/LzCzqcCBwFaE2hRmNgu41t3P7SC+zwLTganu/pMUz8FgM7sJ2Bd4l1DT/VHsuAcBxxNeqxbgCcL74+Xo8c2AmcAhwMGE2uKVwBQz2xK4iPB+aAOeAr7n7v8qcq6kh6kGJV1iZusTaks/jxVOcRcAw4DdS9z1xcD5wMcJtbPbzGy9jlZ29+XA3wgXnpyJwP3RT/7yR6JtMLNDCRewnwDbApOBzwK/TBqsmU0Efg/cCnyMcHHdDLjDzOqi1X4DvE2oaW4PnEhoXuvItcD+wNHANsBU4CIzOyJ6/JfAZ81sdCyOLQgX5GtKiL0BOJTQ7PmfhNscDNwJfCtWOKV1Ds4CHgZ2AH4cPefdYo/3B35IKAR3B1YAfy5QS7qIUFPcHrgiaob+G6E5eVfgk4ADD5rZhkmet/QM1aCkq7YifMF5voPHc8utxP2e4+53A5jZScDXCN+6i9WiGoHToppHHfBp4KQovrPzlsfvPZ0NnBL7Fv+amX0beMjMvuPuxQqRnDMJtZvLcwvM7OvAbEIh+w9gFHCJu7+QO05HO4sKncnAtu7+UrR4ppkZcBzwK3d/zMz+DRxBqGkBHAm86O5/7yTer0e1RoBBhMLpQHf/oLMnamZTCLXb/dz93thDaZ2D29w9V8BeZmbHEGrUDwC4+/V58RxKKPTGAfHnfZW7T4utdzYwy92/FVv2HeDzhJrWpZ09d+kZKqCkq+o6ebyroxCv6ljh7gvMbAWhJoaZ/ZLQfJOzrbvPIRRQFxEuVH0JTX65ppuW2PJBhHtWRN+cRwGXmNnFsX3mnteWhGagzowDPhkVbPm2ip7PxcC10QX1QeAP7v5MB/sbG8UwI5RJq/Ql1BhyrgJONbOzovUPJZyDztwBnBr9vS7wv8DvzWxCJ51ajgKGAv/t7k/nPZbWOcg//nyi1x7AzHYgfMHYAfgIq1+rUaxZQD1ZIL4dzSy/EB4YxScZpQJKuuo/wEpgO8JFL1+uc4KXuN/lBZblmqLPJFzocl6Pfj9LuO8xCVgLeNDd2wDM7KHY8jeBf+bt83iib+h55iWMtw+hYCh0P2kBgLv/0MxuBvYkNDOeamY/cvdCXbdzce1C+5508UL/pui4X4i2WR+4MUG8S9w93k3+aTP7EqHJbXKR7R4jdKo4wsyeyZ3fWMxpnIP8174t2neuOfKvhKa6w3P7JdTU85v48u8f9iF8iSlUgL5XYJlkhAoo6RJ3X2xmdwHHmtnPCtyHOhVYCNzbfusuH3MRBbqlu/vKqIPAJMLF6Dexhx8ADoiW35+7sLr7QjObC1isWakrZgAfzbvoF4r9NeAXwC/M7AfA91ndPBeXq51s6u5/KrK/JWZ2K/ANwnOb7u6Lu/IEgFagoZN1/kW4F9YI1JvZUbFCKu1zUMg2wIbAae7+IoCZ7ULnNflcfIcC8wt12JHsUgEl3XEsIZ/p/iiR83lgI+AEwrftfSt4QWgEfkq4YH0jtvx+Qq2rjvbfoE8DfmVm7xJu/LcQLoR7ufvRCY97JvBXM/sp8GvgfUKz0Zej461FqF1MJ/QwW49Qi3ih0M7c/RUzuw64JroH9xihaXJHYEN3jzfjXRU9Dmt2BilmoJnlcsNyTXzbEjqmFOXuz5vZeMI5vd7MDo/SBFI9Bx2YDXwIHGdmPyF0wriQZE3JPyfcr7vTzM4F5gIjgL2AP7v7oyXEIRWkXnzSZe4+m3DP5AnCxfJV4C5Cb6tP5To7VEhjdNw3c92OoxhfIHRZ7kd0/yn22E3A/xCayZ4k3HM6m3DvIxF3f4DQZLU98AihCfGnhIt0C6F2sj7wK+BFQmePhcBBRXZ7VLSP0wgX8UZCl/M1Oha4+1OEms2r7v5QwpAPAt6Ifp4C9gYOd/ebk2zs7k7oLTgRuMnM1irTOcg/7luE+4+7E74IXQxMITQzd7btQuBThA4htxOanW8m3Lt6I2kMUnl1XZlRN0rqGw+87O6JuqeKSLqi3omzCb3jftLT8YikLVENysxuibplYmb1hG/MfwSeN7MvljE+EcljZn2iZrrTgLUJeVMivU7SJr4JrO7G+SVgHUKG+9mEvAgRqZxNCU1TRwOHubt6okmvlLSTxBBCmzGENuDbo15QtxCGiRGRCnH3WSTrvSZS1ZIWUG8Cowm5IbsD34uWN5DgJmWlmFl/QlLeG6yZ1CgiIj1rLULL21Pu/mGSDZIWUL8Dbjazl4HBrM5t2YGEY3hVyDhCLyIREcmmXQkJ151KWkCdRMgdGEUYATiX4b4JJQxOWQFvANx8881stNFGna0rIiIVsmDBAg4++GAooWt/pwVU1GvvPOCKKO9lFXe/uPBWPWYFwEYbbcSIEZmeqFVEpFYlvv3SaS8+d28BjkE3ZUVEpIKSdjN/iDB4pYiISEUkvQd1M3BhNGPlU+SNFqyxrEREJG1JC6jc5F+FpoBuI3QfFBERSU3SAmp056vUhpdnL6ZxxlwWLl5Kv/o+1FHHhy0rGDakgUljRzJm1JCeDlFEymTJkiUsWrSIlpaWng4lc+rr6xk6dCiDBw9ObZ+JCqj83nu16uXZi5l2jzNoQF/W6gMvznwHaMNGrc+SpuVMu8c55HOmQkqkF1qyZAkLFy5k+PDhDBw4kLo69RvLaWtrY9myZcyfHyYCSKuQSjzdhpntZma3m9k/zWxEtOwIM5uQSiRVoHHGXAYN6MuggfW88fZS+vdbiwH9+vL6W00MGljPoAF9aZwxt6fDFJEyWLRoEcOHD6ehoUGFU566ujoaGhoYPnw4ixa1m1O0y5KOZr4fYZ6fd4AxrJ5ieSAhibcmLFy8lIYBodK5rLmVvmvV0bdvH5Z92ApAw4C+LFycP0u3iPQGLS0tDBw4sKfDyLSBAwem2vyZtAZ1OvBtdz+CMAFZzqOE4Y5qwrAhDSxtDoXRwAF9aV3RRmvrSgb2D4XW0uZWhg3pbOZsEalWqjkVl/b5SVpAbU3ebKSRdwgjndeESWNH0tTcStOyFjbeoIEPl6+geXkrm3xkEE3LWmhqbmXS2JE9HaaISK+QtIB6hzAKbb6PU8L02NVuzKghHPI5Y/CgfqxYCduMXp9tR29A64o2Bg/qpw4SIlJVbr/9diZOnLjq/x/84Af84AfZmUEpaTfz6cB5ZrZP9H+bmW0LXAjcVpbIMmrMqCEqhESkKrz66qtceumlPPHEEzQ3N7P55pszefJk9t9//4Lrn3baaRWOsLikBdSpwJ8Ikxb2B2YA6xGa/c5JsgMzOw34MrAVoUZ2O3Cqu38QW2cMcDWwM7AAOMfdb0gYo4iIRNydgw46iAkTJnD99dczePBgHnnkEaZOncrrr7/Ot7/97XbbrLPOOj0QaceS5kE1AbtFXcrHEpoGZ7j7/SUcaxfgR8DTwDBCQbQOcBisGjX9z8AzhHmddgauNrOZ7v5QCcfpFeIJwUoCFukdKvm5Pvfcc9lqq624+OKLV3VeOOigg+jTpw9Tp05ln332abdNrnnvwgsvBGDixIlMnjyZp59+mocffpjhw4dz9tlns9NOO5Ul5nyJ86AA3P1Bd7/Y3X9UYuGEu3/B3W/x4GHgDCB+hvYChgOHu/u/3f1XwK3AcaUcpzfIJQQvaVrOBusOWJUE/PLsxT0dmoh0USU/14sXL+bJJ59k8uTJ7XrWHXDAAQwYMID77ivU7629a665hkmTJvH73/+eHXbYgSlTplRsJI2kTXyY2dbARGAoeQWbu5/ZhWN/BHg39v9OwBNRbS2nkcLj//Vq8YRgYNXvxhlzVYsSqVKV/FzPnRsGDBg9uv0odfX19YwcOZLZs2fzsY99rNN97bXXXuy7774AHH/88UyfPp05c+awxRZbpBpzIYkKKDM7FrgMeA9YRBggNqcNKKmAMrN1gSnAdbHFQ6N9x70ZLa8pCxcvZYN1B6yxTEnAItWtkp/rtrZwie4oLyn3eBJjxoxZ9ffQoeFyvHjx4uwUUMApwGnARe6e/JkVYGb9Cb0CXyP0AsxRBlxk2JAGljQtX/UNC5QELFLtKvm53nTTTYHQi2/rrbde47GWlhbmzZvHfvvtl2hfffuuLiZyBd7KlStTirS4pPegGoDfplA49SXcV1oH2M/dW2MPL6R9bWlD2teqer14QnBbW5uSgEV6gUp+rocMGcK4ceO46aab2tWWpk+fTnNzM5/97GdTP27akhZQ04C9u3MgM+sD3AhsCewV714eeRLY2cziXycmAk9057jVKJ4Q/PZ7zUoCFukFKv25Pv3003F3pkyZwosvvsi8efO45ZZbuPDCCznmmGMYOTL7X3iTNvFNAe4ws92Af7LmeHy4+9QE+7gamAB8HuhnZhtFy9909xXA3cDrwK/M7FxCN/P/BXZPGGOvooRgkd6nkp/rrbfemt/+9rdceumlTJ48eVWi7hlnnMEBBxxQkRi6qy7JzTIzOwa4HPiA0HFhjU4S7j6m4IZr7qOjA41291nROgZcBXySkKh7dimJutGU9DMbGxsZMWJE0s1ERDr14osvss022/R0GJnX0XmaN28ekyZNgtg1vzNJa1BnEfKWLujqfSh377QThLs7oZbVo6opSbaaYhURKUXSe1D9gVu720miGlRTkmw1xSoiUqqkBdQtdLOTRLWIJ9PV1dVleqbcaopVRKRUSZv43gLONLPPAM/RvpPE+WkH1lOqKUm2mmIVESlV0gLqa4RRJP4r+olrA3pNAVVNSbLVFKtIb9DW1qZZdYsoZYSKJJKOZt5+QKdeatLYkUy7x4FQG1na3EpTcyv7jS//sB6lqqZYRapdfX09y5Yto6FBXwA7smzZMurr6ztfMaGSRjMHMLMBZjag8zWrUzUlyVZTrCLVbujQocyfP5+lS5emXlOodm1tbSxdupT58+evGq8vDaWMZn4YYTy+zaL/ZwLn9cYJBaspSbaaYhWpZoMHDwbg9ddfr9h0E9Wkvr6eYcOGrTpPaUg6mvnxhIFdrwQeIgzsOh74hZmt4+6XpxaRiEhGDR48ONULsBSXtAZ1HHC8u18dW3anmb0EfJ8wykRNK5Ywm+Vk2izHJiK1Lek9qJGEyQPzNUaP1bRiCbNZTqbNcmwiIklrUPMIQxC9mrd8QvRYTSs2UyaQ2dlxNXOviGRZ0gLqSuAyM9sSeISQ+zSe0PTXlenee5XOEmazmkyrRF8RybKkeVAXm9ky4OToB0LNaYq7X1mu4KpFZwmzWU2mVaKviGRZ4jwod7/C3TcF1gXWdfdNVTgFxWbKzPLsuFmOTUQkUQFlZveb2XoA7v6+u78fLR9sZveXM8BqUCxhNsvJtFmOTUQk6T2oCUC/Asv7A7umFk0VK5Ywm+Vk2izHJiK1rWgBZWabxv4dkTfE0VqE6dsXlCMwERGpbZ3VoGYReuy1AU8VeHwlcErKMYmIiHRaQO1KGNboYWAfIJ7BuRyY7e6LyhSbiIjUsKIFlLv/HcDMRgNzamHKdxERyYaknSTWA9YGngcws88Dh0X/n+vureUJT0REalXSPKirgO0BzGwE8H+EAusbwLnlCU1ERGpZ0gLKgGejv/cHnnL3vYDJwFfKEZiIiNS2pAVUP6A5+nsCcFf098vARinHJCIikriAcuDAKC9qd+C+aPnGwDvlCExERGpb0gLqHOB8YCbwN3efES3fg9VNfyIiIqlJOpr576Pa08bAP2MPNQK3lyOwSsvCzLJZiEFEJCuSdjPH3RcCC/OWPZZ6RD0gN7PsoAF915hZtpIDp2YhBhGRLElUQJnZdcUed/fD0wmnZ2RhZtksxCAikiVJa1D5EwTVA9sSevc9mWpEPSALM8tmIQYRkSxJeg9q9/xlZtYfuB54KO2gKi0LM8tmIQYRkSxJPKNuPnf/kNCz79T0wukZWZhZNgsxiIhkSZcLqMjahCngq1oWZpbNQgwiIlmStJPEQXmL6oBNgG/SC5r4IBszy2YhBhGRrEjaSWJa3v9twCLCiBJTkh7MzPYHjgXGAoPdvS722ATggbxNnnP3HZLuX0REeo+knSS62xSY0wDcTyjYzu9gnY1jf7ekdFwREakyiRN10+Du02BVbamjdRZULCAREcmspPegfga85u4/y1v+HWAzdz8xrYDMbBah88ajwEnuPietfYuISPVI2nS3P1BoWKPHgANTiuUN4AhgH+DrwFDgATMbmNL+RUSkiiRt4tsQeLPA8rcJBUm3ubsTpvUAwMxmAHOALwK/S+MYIiJSPZLWoOYBnyqw/FPA6+mFs5q7vw+8AmxWjv2LiEi2Ja1B3QhcYmbLgHujZXsAPwGuKkdgUdPe5sDscuxfRESyLWkBdR6wBTCdkAMFIVn3ZmBq0oOZ2RBgU2DL6P9cjtMLwKHAAuB5YD3gLOAD4C9J9y8iIr1H0jyoFcDXzWwq8Ilo8TPu/mqJx9ubMMBsTm423tGEEdIvAUYA7wF/Bya5+wclHkNERHqBkvKgogKp1EIpvv0NwA0dPHxF9CMiItLtwWJFRETKQgWUiIhkkgooERHJJBVQIiKSSYkKKDO7zsw+We5gREREcpL24hsN/N3MXgCuBm5y93fLF5aIiNS6RDUod98NGAP8CTgFeN3MbjKzXcsZnIiI1K7E96Dc/VV3PwUYCRwErA80mtmLZvZdM1unXEGKiEjt6cqEhSuB1uh3HbCMMO37WWZ2qLv/PsX4pAQvz15M44y5LFy8lGFDGpg0diRjRg2p2PYiImlKXIMys1HRUEdzgFsI4+Z9yt0/AYwCLkUjQfSYl2cvZto9zpKm5Wyw7gCWNC1n2j3Oy7MXV2R7EZG0Je3Fdw9hiKN9gAuA4e5+lLvPgFVj9V0FbFKuQKW4xhlzGTSgL4MG1lNXV8eggfUMGtCXxhlzK7K9iEjakjbxvQF82t0fL7LOQkJvP+kBCxcvZYN1B6yxrGFAXxYuXlqR7UVE0pZ0NPNDE6zThuZu6jHDhjSwpGk5gwbWr1q2tLmVYUMaKrK9iEjaSrkHta+ZPWxmb0U/j5jZfuUMTpKbNHYkTc2tNC1roa2tjaZlLTQ1tzJp7MiKbC8ikrak96BOAH4HzAXOJEwmOAe4zcxOLF94ktSYUUM45HPG4EH9ePu9ZgYP6schn7PEvfC6u72ISNqS3oOaApzo7pfHll1hZo8DPyBMNCg9bMyoId0qULq7vYhImpI28a0L3FVg+V2E6dlFRERSlbQGdTewB/BK3vLPAX9NNaKMUfJqaXS+RCQtSQuoh4GpZrYTkOtq/kngi9Hyg3Iruvst6YbYc3LJq4MG9F0jeVX3ZgrT+RKRNCUtoC6Nfk+Ofgo9BtBGGGWiV4gnrwKrfjfOmKsLbgE6XyKSpqR5UDU5saGSV0uj8yUiaarJgiepYUMaWNrcusYyJa92TOdLRNKkRN0ilLxaGp0vEUmTEnWLUPJqaXS+RCRNStTthJJXS6PzJSJpUaKuiIhkkhJ1a0SaCbRKxhWRSlCibg1IM4FWybgiUilK1K0BaSbQKhlXRCpFibo1IM0EWiXjikilqOCpAWkm0CoZV0QqJWkTH2a2PrAnMAroF3/M3aemHJekaNLYkUy7x4FQ21na3EpTcyv7jd+iR/clIlJMogLKzMYRevLVAYOBN4GhwFLgDUAFVIblEmjjPe/2G79Fl+4ZpbkvEZFiktagfgxMB74JvAf8N7Cc0CHip+UJTdKUZgKtknFFpBKSFlA7AN9y95VmthLo5+6vmdnJwHXAHUl2Ymb7A8cCY4HB7l6X9/jOwBXAdsBrwPfcvVCCsIiI9HJJC6gVhBoTwCJgJPAS8BbhnlRSDcD9wH3A+fEHzGwDwsgUNxG6su8D3GFm27v7f0o4RiZ0lMyqJFcRkWSSFlD/JNSiXiUk6p5qZn2AbwCe9GDuPg3AzCYUePhgYAnwXXdvA14ws72AowljAVaNjpJZd/34xjzy3BtKchURSSBpN/PzCLUogDMIHSTuAnYFvpNSLDsBD0SFU04jsHNK+6+YeDJrXV0dgwbWM2hAX+586LWCyxtnzO3pkEVEMidpou59sb9nAR81syHAO3kFSncMBZ7NW5brLVhVOkpmffeDD9l6wPrtlivJVUSkvcR5UPncfXGagRC6sPcKw4Y0sKRp+aphgCAks663dn+WNre2W64kVxGR9pLmQdUROi7sAQwjr2nQ3SemEMtC2teWNiR0yqgqHSWz7jt+cx557o12y5XkKiLSXtJ7UD8CrgWGALMInSXiP2l4EpiQt2wi8ERK+6+YjmaW3X3nzTTjrIhIQkmb+L4GHOTuv+vOwaL7VpsCW0b/7xA99AJwM3C2mV0KXAXsTeggcWR3jtlTOkpmVZKriEgySWtQ9cAzKRxvb0JHiGui/5+NfjZx97eBzxN6Bv4DOBTYvxpzoEREpPuS1qBuAg4gNPV1mbvfANxQ5PHHgR27cwwREekdOiygzOzU2L/vAieb2S6E2s3y+LruvsaoECIiIt1VrAb1jbz/lwAfj37i2sgbtkhERKS7Oiyg3H10JQMRERGJ04y6IiKSSaXMqDuejhN1D085LhERqXFJR5L4PnARYeTy+YT7TiIiImWTtAZ1HHC8u19ezmBERERykt6DWg/4UzkDERERiUtag7qTMC7er8oYS83R7LrF6fyI1LakBdRjwLlmth3wHO0TdW9JO7DerqNZdzV4bKDzIyJJC6grot/HF3isDVABVaL4rLvAqt+NM+bqAozOj4gkn1FX+VIp62jWXc2uG+j8iIgKnh4ybEgDS5tb11im2XVX0/kRkaR5UGcWe9zdp6YTTu3oaNZdza4b6PyISCkTFsbVA8OBZuANQAVUiXKz7sZ7qe03fgvdX4no/IhI0ntQW+UvM7OhwK8Js99KF2h23eJ0fkRqW5fvQbn7IuB0whBIIiIiqUo8WGwHWoBN0ghEKicLCbBZiEFEsi1pJ4ld8hbVEQqmk4AZaQcl5ZOFBNgsxCAi2Ze0BvU3QkJuXd7yv9N+5l3JsCwkwGYhBhHJvqQFVP7suiuBN929OeV4pMyykACbhRhEJPuS9uKbXe5ApDKGDWlgSdPyVbUWqHwCbBZiEJHs00gSNWbS2JE0NbfStKyFtrY2mpa10NTcyqSxI2sqBhHJPhVQNSaXADt4UD/efq+ZwYP6VbxzQhZiEJHs6243c6lCWUiAzUIMIpJtHdagzOwzZqYCTEREekSxAugBYGNgkZm9Boxz97crE5ZkTVcSa5NuU859i0j1KnYP6h1Wdy/frJN1pRfLJdYuaVq+RmLty7MXd3ubcu5bRKpbsRrUH4AHzGw+IUn3cTNbUWhFdx9TjuAkG7qSWJt0m3LuW0SqW7EC6hvA3cAYwnQatwMfVCIoyZauJNYm3aac+xaR6tZhAeXuK4DfApjZJOA8d3+3UoFJdnQlsTbpNuXct4hUt0T3ldx9t1zhZGYDzGxAZ9tI79GVxNqk25Rz3yJS3RJ3fDCzw8zsFUIz3wdm9h8zO7RskUlmdCWxNuk25dy3iFS3pNNtHA9cCFwJPEQY1Xw88AszW8fdLy9fiJIFXUmsTbpNOfctItUraSLuccDx7n51bNmdZvYS8H0glQLKzG4Avp63+AR3vzSN/YuISPVIWkCNBBoLLG8ELksvHCB0zDg+9v+SlPdfdXoqKVXJsCLSk5Leg5oHTCiwfEL0WJqWufuC2E9N9x3uqaRUJcOKSE9LWoO6ErjMzLYEHiEk7o4nNP2dmXJMe5vZm8AbwG+AH7t7a8rHqBo9lZSqZFgR6WlJJyy82MyWASdHPxBqTlPc/coU4/kLcCswH9gR+DGwDnBqiseoKj2VlKpkWBHpaYlHK3f3K4ArzGyd6P/30w7G3X8b+/df0dBKV5rZae7elvbxqkFPJaUqGVZEelrJA8C6+/vlKJw68AwwCPhIhY6XOT2VlKpkWBHpaVkfoXw7YCnwVk8H0lN6KilVybAi0tMyNSGhmV0C3AYsBD4BXAJcVavNezk9lZSqZFgR6UmZKqCAbYE/AYOBOcBVwEU9GpGIiPSITgsoM6sHzgOucPfZ5QzG3fcs5/57g2pNni1n3NV6TkSkuE7vQbl7C3AMYfw96UHVmjxbzrir9ZyISOeSdpJ4CNilnIFI5+LJs3V1dQwaWM+gAX1pnDG3p0MrqpxxV+s5EZHOJb0HdTNwoZltBjwFNMUfdPdHU45LCqjW5Nlyxl2t50REOpe0gJoW/T63wGNtwFrphCPFVGvybDnjrtZzIiKdS9rEN7rIz+Zr4xKBAAAZ10lEQVTlCU3yVWvybDnjrtZzIiKdSzoWX1l770kyueTZeI+1/cZvkfkea+WMu1rPiYh0LnEelJntRhi9fEvg8+4+z8yOAF519wfLFJ/kqdbk2XLGXa3nRESKS9TEZ2b7AXcB7wBjgH7RQwOBk8oTmoiI1LKkNajTgW+7+7Vm9j+x5Y9Sw1NhZJmSV0Wk2iXtJLE1cF+B5e8AuupljJJXRaQ3SFpAvQNsXGD5xwmTC0qGKHlVRHqDpAXUdOC83GSFQJuZbQtcSBh9XDJk4eKlNAxYs/VWyasiUm2SFlCnEsbiWwg0ADOAfwGzgXPKE5p01bAhDSxtbl1jmZJXRaTaJM2DagJ2M7MJwFhCwTbD3e8vY2zSRZPGjmTaPQ6EmtPS5laamlvZb/wWPRyZiEhyJc0HFeU7PViWSCQ1Sl4Vkd6glETdfYETCZMKArwIXOLud5QjMOkeJa+KSLVLmqh7AvA7YC5wJnAWYcbb28zsxPKFJyIitSppDWoKcKK7Xx5bdoWZPQ78ALgk9cikJinBWERykvbiW5cw1FG+u4D10gtHapkSjEUkLmkBdTewR4HlnwP+ml44UsuUYCwicR028ZnZQbF/HwammtlOwOPRsk8CXwSmli88qSWaHVdE4ordg5pWYNnk6Cfup8BlqUUkNUuz44pIXIcFlLsnbf4TSYUSjEUkToWQZEYuwXjwoH68/V4zgwf145DPmXrxidSoUhJ1twYmAkPJK9jc/cyU45IapQRjEclJVECZ2bGE+0zvAYuAttjDbYTkXRERkdQkrUGdApwGXOTubZ2tLJIVXUn8VbKwSDYkvQfVAPxWhZNUk64k/ipZWCQ7khZQ04C9yxmISNq6kvirZGGR7ChlLL47zGw34J9AS/xBd1eyrmROVxJ/lSwskh1JC6gjgT2BD4CP0r6ThAooyZyuJP4qWVgkO5IWUGcBZwAX6D6UVIuuJP4qWVgkO5Leg+oP3KrCSapJVxJ/lSwskh1Ja1C3EDpJXFrGWERS15XEXyULi2RD0gLqLeBMM/sM8BztO0mcn2ZQZnYKcBxhrqm/Ake5+6I0jyEiItmWtID6GmEUif+KfuLagNQKKDM7DDiVMGr6TEKt7TfApLSOIempZFJrkmMVWyf+WL/6PtRRx4ctKxg2pIEtR6zLK/Pea7dd0m3ij3X0d9JtyrnvrMVTS8+1t8RTycT1ura2bN1WMrNngD+6+1nR/5sDrwLbu/u/O9l2M2BmY2MjI0aMKHustS6X1DpoQN81OhSU455NkmMVWwdY9djyllZenvMe0IaNWp9lH7Yy8/UlbD58XYYNaVi13a4f35hHnnuj02361/dZ9dgmGw7i9TeXtvs76Tbl3HfW4qml59pb4qnvu1aXP+Pz5s1j0qRJAKPdfVaSbTJVQJlZf2ApMNHdH4otnwmc6+6/6mT7zVABVTFXTn+uXZfspmUtDB7Uj28d8PGKH6vYOsCqx16Y+TbLW1ZSB9TXh35Cy5pbGTigL9uO3mDVdnMWvM+mG63T6TbAqsfeX7actQf2a/d30m3Kue+sxVNLz7W3xLPt6A26/BnvSgGVdLDY64o97u6HJ9lPAhsQehbm3296kzCKumRIJZNakxyrs3Vyjy1rbqV/v7Woq6tj2Yet0Ab9+60V/o5t9+4HH7L1gPU73yb6u66ujuUtK+m7dl27v5NuU859Zy2eWnquvSaeMn7GC0l6D2pk3v/1wLZAP+DJFOOpS3FfUmaVTGpNcqzO1sk9NnBA3+gbYhsD+4ePQPzbY2679dbuz9Lm1kTb5B7rV9+H1hVt7f5Ouk059521eGrpufameCqZuJ4oD8rdd8/7mUAotP4CTE8xnreAlbSvLW1I+1qV9LBJY0fS1NxK07IW2traaFrWQlNzK5PG5n+fqcyxiq0Tf2zjDRr4cPkKmpe3sslHBrH+Ov35sGUFQwYPWGO7fcdvnmib+GOjNlqn4N9JtynnvrMWTy09194STzk/44V06x6UmW0H/NndR6UVUNRJ4g/ufnb0/2jgNdRJIpPUi682em5V474VT7Z68VW8k4SZfRK4293X6/JO2u/zcELX8q8Bs4CfArj7xATbboYKKBGRzClnJ4mD8hbVAZsA3wQear9F17n7dWY2DPglsC5wL3BUmscQEZHsS9pJYlre/22Ee0L3EabiSJW7XwBckPZ+RUSkeiQqoNw96aCyIiIiqUhag6oWawEsWLCgp+MQEZGY2HV5raTbFC2gCtx7Ksjdb0l6wDLbGODggw/u6ThERKSwjQnD13WqsxpU/r2nuHj3v6wUUE8BuwJvACt6OBYREVltLULh9FTSDbrUzdzMBgEnA98D/uPuO5S8ExERkSJK7vxgZocCDhwJHE/76TdERES6LXEnCTMbT0ia3Tr6fYG7f1CuwEREpLZ1WkCZ2ZbAxcCXgNuAfd19TrkDExGR2tZZL76fAMcCTwOfcvc0Ry4XERHpUNFOEma2kjCB4KPFduLue6Qcl4iI1LjOmvhuZM3u5CIiIhWRqSnfRUREcjTGnoiIZJIKKBERyaTeNlhsp8zsFOA4YD3gr8BR7l5wOnkzGwNcDewMLADOcfcbKhRqWSU9D2a2A3Aa8GlgHeDfwOnufl8Fwy2LUt4LsW12BB4H/u7uE8oeZAWU+JnoC5wBHAYMA2YDx7r7vRUKtyxKPAd7A+cABiwG/g84yd2XVyjc1JnZ/oQe22OBwe5e18n6Fbk21lQNyswOA04lvBC7EN6Mv+lg3Xrgz8BCYBxwLnB1lLBc1Uo5D4SRQmYDBwI7APcAfzSzrSsQatmUeA5y2/QHbiDlSTp7UhfOw1XAPsARhAv0kYSxL6tWideFLQgF0s3AR4HJwAGEL3HVrAG4H7iwsxUreW2stRrUccAl7n4HrJpe/lUz287d/5237l7AcGAHd28C/h29AMdR/ReoxOfB3a/P2/YsMzsQ2AN4qSLRlkcp74Wccwkf4neACRWJsvwSnwcz255wQR7j7jOjxbMqGWyZlPJe+ASwxN0vjv6faWa/BXasXLjpc/dpAGY2IcHqFbs21kwNKvr2+3HCBQYAd3+N8AHbucAmOwFPRC9ATmMH61aNLpyH/O3rgA2Ad8sUYtl15RyY2X8DewOnVCDEiujCefgC8ArwVTOba2YvmdnpZpZ4fp+s6cI5eBpYx8z2NbM6MxsJ7EloFqwVFbs21kwBRbio9iFMVR/3JjC0wPpDS1i3mpR6HvJ9mzBs/h9TjquSSjoHZtYAXE+4L7G0/OFVTKnvhc2AzQm15wMJhfV3CLMaVKuSzkFUeO1NaOpdDswBHnL3y8obZqZU7NpYSwVU0Zt+KaxfLbr8vMxsT0Ib9cHu/k56IVVcqefgQuAed6/2pt18pZ6HPkA/4FB3fyJqEjuPcD+qWpV0DsxsE+AXhPfEjoTCak8zO7EMsWVVxa6NtXQP6i1gJaGUfzG2fEPafxuAcANwy7xlHa1bTUo9DwCY2a7A74Aj3L3amzNKPQefAbYzs29F//cB6sysFdjC3WeXM9gy6spn4sO85+vAyLJFWH6lnoNjgFfdPdeZ4J9mtg5hhodLyhlohlTs2lgzNSh3/xB4Dtgtt8zMRhOaLZ4osMmTwM5R807OxA7WrRpdOA+Y2U7An4DvufutFQizrLpwDg4g9GDM/fwSmBH9/XqZwy2bLpyHx4H+ZjYitmxLQjNXVerCOWig/WzdK6mhaykVvDbWUg0K4OfApWb2LOEm6E+BB9z939FF+EZgkrvPB+4mXHx+ZWbnEm4A/i+we49Enq7E5yHquXU3oXvxH8xso2gfy9z9vR6IPS2Jz4G7vxrf0MwWAU1FevtVk1I+E/cQahnXmNkUwvTdpwA/6ZHI01PKOfgLcLyZfTv6ezPgh1T3PVnMbAiwKVHNKMp/BHiB8EWsR66NtVTq4+7XARcQvgE/BrxPOLEQvhkZUB+tu5zQa2ljQs+dMwk3yav+PkQp54FQe1gf+D4h3yX387MKhpy6Es9Br1XiZ6KV8JmoA54CriHcj6mZ90KUoH44cDQhaf1GQg++71Y26tTtDTxLeE2J/n4W2IQevDZqsFgREcmkmqpBiYhI9VABJSIimaQCSkREMkkFlIiIZJIKKBERySQVUCIikkkqoMrEzNrM7JCU93mDmVX9RIFZkv86mdksMzs9xf1PiI4xovO1Uzne98zsTxU61oNmdm0ljiXp68p73cyuMrOLO18zHbU2kkSHohESZhGmkRjp7i3d3OXGVNGUFNFF+qbOZtJM8XinA0e6+2adrDcH+I27nxxbtj8wHfhRB8s3j81XVDOi0QDOIDZsT8LtDiPM87QdMAB4mTA/0s2dbLo/0NqFUCuq0u/tSjCzc4Dt3X3/buxmHFDq6PxTgZfN7BfRyO5lpRrUaocTZol8mzBjaFFm1q/Ycndf4O7NqUbYBR3FWUUagUl5yyYSxn8rtHxmLRZOkSOAl9392RK3mwT8Afg8YQblW4GbzOwrxTZy98XuvqRLkUo7JX5W9wPu6M7x3P3NvDmdkmwzn/CZPKY7x05KNSjAzPoA3wCOB7YGjiJM6xxfZxYwDRgCfAV4DRhnZm3Rdp8kDP/xV+DL0fKvufs0M7sZ2NDd98jb513Ae+7+1WiAyp9E+1kPeJVQQ7ipxOfSUTznEb7xjiIUwn8GTnb396JZNG+KbQ/wa3c/NFp2HGE67M2AuYS5cC6Khr4pFEMdcDWhwNiEMDTSrcA57v6hmR1KGL8sfrxz3P3sArtrBCab2fqxKT4mEma3vbLA8lVNoGa2NmE6iAMJwzU58EN3v73DE9j+ufQFTge+TqgVvwpc5u5XxdY5kjAn0migCXgeOMjd53Wwzy2BiwgFQxth2KDvufu/opGxXweOdvdbYttsRnjPTXT3BzsI92Dgt3nHugEYQRg37nvRebgr2v9bAO6e3xT9YzP7DPA/wG0dHAszexB4xd2PjP8PzCa8X/oRCr5ji10Io9fpXMKwWkMJ75er3f386HnPBHZ197/FtnkFmJZ7z3T0GhDGliv43rYwdfkPga8RRuN+BTg377y3Eea8+jTwRcJn50TgXsIwT3sT5kL6vrtPj203jPAaf4FQK/0ncIq7Pxw9PgF4INrnKcBYYIqZ3UgYOmovwmu1CPidu58Y2/cWwDaEAZxz16abgI9Ez/lDQk3nGuBi4BBCTekCd/95bD+zgGvd/dzY/zcC60bnpCXa78nuHh8g9w7C0FBTKDPVoII9gEGED/FNwAQz27zAet8hvGE+Rbhg5ZxFGMPrE8BpBba7EZhkZsNzC6I38O7Ar6NFaxMuxnsC2xMu8NebWUnNNUXiWUYoeLcFDiVMWZ6bZO1RwkSEEC7CGxMKOczsbMIb8RTCh+J4wjhkZxU5fh1hSP6Dom2+CxwGnBo9fhvhwzsvdryO2rUbCe/T3aJ4NiKMC/Z/hItQfPk20fq5QvKPhNlSv0JovroSuNXM8mtexVxLKNiPjvY/FbjIzI6IjrMjYQy3C6K4JhBe74Ki1/1vhPfRroQvEg48aGYbuvv7wC2EL0xxRxAuoAXHOzOz9YGPEUaazrdTFNeehFrSx4Drij7rcJF6q5N1CjmQ8CVuAuH13xc4qaOVo9fpT4QL/XGEczyZcNFPpJPXoMP3NnA+4Tx/l/D+mAZMK/D+OI1wbfh4FOuNhC9c9xJqnH8GbjSzDaJ4BhIKn3UIBc1/Rdvfa2bb5O37J8CPoud9J6Gg/gShFWcrwnv3xbxt9gcezJuT7TjgP4Q5qi6Lfu4gFO7jCAPiXmZm27Y7gWs6jvAFYWfC9e67hNcj7glgWIHnkjrVoIKjgZujGsEbUUeEI1l9Qc15qoNv+Xe6++VF9n8vsIDwTeaiaNnBhA/hXwHc/V/Av2LbXG5mnyV8yB8o7em0jyf3LSkyy8xOIVysD3P35Wb2XrTegtxK0XD6JwH7u/vd0eKZ0f2jywj3O9px95WEWkf8eFsQmgXOcvdlZvYBsCJ+vA729YaZvUiobdxOqCU95+7vmNkDecvbWD1193jCF4lhsVHXrzazTxI+hI3Fjhs9/9GED+e27v5S7PlbtI9fEUaAbiKc81xz17/a7Wy1bwGz3D03txRm9h1CwXEwcClh5PinzWwrd/+PhSnVDyXU3DoaPHMU4YvB/AKP9SHU5t+LjncscE9u/wWe9yGEgrMrA6DOcfcTor9fMrNbCV8AO/pCM5HwWo1z9xnRsteAh0s4ZtHXoMh7+zvACe7+u2jx+WY2jlAgxd8ft7r7r6PtziK8hq+4+w3RsjMJheCnCAXYV4DBwFdirQznRQXf0ax5Xs9z9z/E4hoFPOvuuakr5hAK2bj9CIVp3IPufkm0j/MJn9sVsWUXRcsmEkYo78gjsbmu/hPdn9yDMKN0Tq5lYHPaF56pqvkCysw2JlSzx8UW3wD8zMzOzGvGKvTttNhyIFywo2a+r7G6gPoaoVBcEcXRQBgV+EuEb3n9gP6UXjgVjCfqQPBdQpPHYFbPjroRHc9p9FFgIDA91jwCYcr3AdE3/oLfdM3sG4RCfjNC7bQvXa+xNxI+JBAKpFwhdD/w49jy52LxjCM8v/mhPFmlH+GbZhJjCRf9GXn76MvqOYHuJVxQZ5rZvVFMt+eazwoYB+wYFdBxAwnfmHH3Z8xsBuH8nUz4Fj6M1bXtQgZGvwvd93whb2qUv0e/tyHvXJjZPoSmoSPc/Zkix+vIP/L+n8/q166QHYF3YoVTV5T6GkD4HPSjfUH4EKG1IO653B/u/qaZrSA02eWWvWNmy1k95fk4wufq3bz3TX9CS0Zc/mf1F4TP21jC+/5uwmzOK2HV9Won4MtFYlxpZm/mxbjSwjQxnU3LXuj1G523LPceG0iZqYkvNJ30JVyEWi3MknoL4Q22d966HbWjJ7nR+Gvgo2a2o5ltx+o5VnJ+TKhhTSU0W+1AaBboSieHNeIxs50Js+E+TPj29Qngm9HDxfafe398mTUn7NuecDFdXGgjM/sycAWhKS93430qXZ++4j5gjIWu2hNZXUA9DGwRWx7/1tsHeC8v7h0ITZx7JTxu7vnvkreP7QjNZLj7B4SCbD9C77dvAq9EzU4d7bOxQFwGnB1b75dA7j7JkYTaQbEZS3MF85CEz60dM/sq4TU7qtR7nzHL8/5vo/PrTLEpFVZGv/N74K16L3XhNSh27LoCywr16M1fFn+efQg1i/zXeBvaN92u8Vl193sINcLzCPeupgH3R7VoCE2mM6LOCp3FUyzGjiR5/XLvscTNsF1V0zUoC50jjiS0Rf8m7+GTCfdsEt9QL8bdnzezZwhNRh8C/3D3f8ZW+QyhRnVbLLYxhHs53fVp4C13X9XsZmYH5q2zPFq+VuyG6POEb0ubu/tfSjjeZwjNFKumwI5uducfby2SeZBQYzmScMP/YQB3f9fM/sHqmlo8R2wGobPJAO/6xIJPR783dfcOc4ui8/Uw8HDUBPQCoWn26QKrzyA018139/xv03G3EqYQP5pwo/3zncT6GiGt4aMFjruNmQ2ONX/tEv1e1TwT1XgvB76eew9WyNPAEDMb20EtKncR3CS3wMyGAsPjK3XyGhR6b79C+ByOJ7zPcz6T939XzCB8zpd08qWiIHdfTLge/cbMrifcT96W0Gy5P93svZeC7Qmfx1J7i5aspgsowk3jTYGr3H2NaaujN8a9ZraZu89K6Xi/JtybaaF9pwAH9jGz6cAHhJ5Cm5BOAeXAhtGN/QcIBVZ+N9Fc1+y9zexvhBlzP4jas8+PmiruJbxntgf+y2M5SAWOd0TUXPRvQhNqfr7GTGAjM/sUoZlpqbsXzMmIeho+TTgnT0XfmHPuj5YvBx7JW34fcLuZnUxoAlmfcHFudvdr6IS7v2Jm1xFmkD2JcKEYRGiW2tDdL4qe4+aEi+Ob0WMj6bid/+eEWvudFmYjnUsodPcC/uzuj0bHbjKzaYSb6HNYs/AtFOtKM7uHcMHN76TRRriJfzrh2+8V0bH+A2BmJxBq8McCD9nqWZOXRxfLcrqf8LrdZmYnEpqlNgG2cfdro/uVfwdOMrOXCO+/8wiFC1H8nb0GHb23LwN+GDWH/YPQUrAP3Z8Z9mbgBODPZnYaoVY3jFDLf9Hd7+xoQwu9bZ8mFJIrCfclPwDmRB1hJhBep540AfibVyDFoNab+I4GnsgvnCIPEd7sR6Z4vFsI3+qHRn/HnUDonvsAoQloPnld3bsq+vZ/HqGm+C/gq4QZcuPrPEXo3vpLQqH482j5D6PYjiRc5P8W/T+ryCGvIvSGvJ7wLWtn1my+gtBj6XeEHlBvUqSnV6SR0Cvq/rzlud5Sj8e7MkedCfYm1IAvAV6KjvUFQlfxpI4iTAF+GuGC10jowZlLUnyHcN/wbsKF6EeEnlgFe8m5+0LCzfS3oticcEEbReg9FXc1oQn22iKdI+KuBA6MepHFPUl43e4lTNv+PKFXZc7xhNrsL1lz1uRUWg+KiZ7XFwjN2b8knI9phC7TOYcTLtKPEmqWV7PmuSr6GnT03ia8ptcQOqY8T2hiP8TdO+1A08lzaiZ8UZhB+Ay8TDiXOxE+48U0E5rDn462/xiwV3QP8YuEPLeXuxNfd0S9Lg8ifMbLTjPqimSUmX2eUJBv2llvx9g29wF/cvdLo/9vAEa4+2fLFqhUhJndATwfb6rvgRj+h9B7d4e83KiyqPUmPpHMiXp0bkro1XlL0sIpcgzdb6KSbHqMCtRqO9EfOKwShROogBLJopMI9yqfpPOmzzVEzT891gQk5ePuP8pADF3t3dklauITEZFMqvVOEiIiklEqoEREJJNUQImISCapgBIRkUxSASUiIpn0/xpNNE+Vc8AEAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "p2_array = linspace(0, 1, 101)\n", - "sweep = sweep_p2(p2_array)\n", - "plot(sweep, 'bo', label='Olin')\n", - "\n", - "decorate(title='Olin-Wellesley Bikeshare',\n", - " xlabel='Arrival rate at Wellesley (p2 in customers/min)', \n", - " ylabel='Number of unhappy customers')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Optional Exercises\n", - "\n", - "The following two exercises are a little more challenging. If you are comfortable with what you have learned so far, you should give them a try. If you feel like you have your hands full, you might want to skip them for now.\n", - "\n", - "**Exercise:** Because our simulations are random, the results vary from one run to another, and the results of a parameter sweep tend to be noisy. We can get a clearer picture of the relationship between a parameter and a metric by running multiple simulations with the same parameter and taking the average of the results.\n", - "\n", - "Write a function called `run_multiple_simulations` that takes as parameters `p1`, `p2`, `num_steps`, and `num_runs`.\n", - "\n", - "`num_runs` specifies how many times it should call `run_simulation`.\n", - "\n", - "After each run, it should store the total number of unhappy customers (at Olin or Wellesley) in a `TimeSeries`. At the end, it should return the `TimeSeries`.\n", - "\n", - "Test your function with parameters\n", - "\n", - "```\n", - "p1 = 0.3\n", - "p2 = 0.3\n", - "num_steps = 60\n", - "num_runs = 10\n", - "```\n", - "\n", - "Display the resulting `TimeSeries` and use the `mean` function provided by the `TimeSeries` object to compute the average number of unhappy customers (see Section 2.7)." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def run_multiple_simulations(p1, p2, num_steps, num_runs):\n", - " results = TimeSeries()\n", - " \n", - " for i in range(num_runs):\n", - " state = run_simulation(p1, p2, num_steps)\n", - " results[i] = state.olin_empty + state.wellesley_empty\n", - " \n", - " return results" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
00
15
20
34
43
55
60
710
81
91
\n", - "
" - ], - "text/plain": [ - "0 0\n", - "1 5\n", - "2 0\n", - "3 4\n", - "4 3\n", - "5 5\n", - "6 0\n", - "7 10\n", - "8 1\n", - "9 1\n", - "dtype: int64" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "p1 = 0.3\n", - "p2 = 0.3\n", - "num_steps = 60\n", - "num_runs = 10\n", - "run_multiple_simulations(p1, p2, num_steps, num_runs)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Continuting the previous exercise, use `run_multiple_simulations` to run simulations with a range of values for `p1` and\n", - "\n", - "```\n", - "p2 = 0.3\n", - "num_steps = 60\n", - "num_runs = 20\n", - "```\n", - "\n", - "Store the results in a `SweepSeries`, then plot the average number of unhappy customers as a function of `p1`. Label the axes.\n", - "\n", - "What value of `p1` minimizes the average number of unhappy customers?" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "p1_array = linspace(0, 1, 20)\n", - "p2 = 0.3\n", - "num_steps = 60\n", - "num_runs = 20\n", - "\n", - "sweep = SweepSeries()\n", - "for p1 in p1_array:\n", - " results = run_multiple_simulations(p1, p2, num_steps, num_runs)\n", - " sweep[p1] = results.mean()" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot(sweep, label='total', color='green')\n", - " \n", - "decorate(title='Olin-Wellesley Bikeshare',\n", - " xlabel='Arrival rate at Olin (p1 in customers/min)', \n", - " ylabel='Average total unhappy customers')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap05soln.ipynb b/code/soln/chap05soln.ipynb deleted file mode 100644 index f2743e342..000000000 --- a/code/soln/chap05soln.ipynb +++ /dev/null @@ -1,1685 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 5\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Reading data\n", - "\n", - "Pandas is a library that provides tools for reading and processing data. `read_html` reads a web page from a file or the Internet and creates one `DataFrame` for each table on the page." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "from pandas import read_html" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The data directory contains a downloaded copy of https://en.wikipedia.org/wiki/World_population_estimates\n", - "\n", - "The arguments of `read_html` specify the file to read and how to interpret the tables in the file. The result, `tables`, is a sequence of `DataFrame` objects; `len(tables)` reports the length of the sequence." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "6" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "filename = 'data/World_population_estimates.html'\n", - "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", - "len(tables)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can select the `DataFrame` we want using the bracket operator. The tables are numbered from 0, so `tables[2]` is actually the third table on the page.\n", - "\n", - "`head` selects the header and the first five rows." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
United States Census Bureau (2017)[28]Population Reference Bureau (1973–2016)[15]United Nations Department of Economic and Social Affairs (2015)[16]Maddison (2008)[17]HYDE (2007)[24]Tanton (1994)[18]Biraben (1980)[19]McEvedy & Jones (1978)[20]Thomlinson (1975)[21]Durand (1974)[22]Clark (1967)[23]
Year
195025576286542.516000e+092.525149e+092.544000e+092.527960e+092.400000e+092.527000e+092.500000e+092.400000e+09NaN2.486000e+09
19512594939877NaN2.572851e+092.571663e+09NaNNaNNaNNaNNaNNaNNaN
19522636772306NaN2.619292e+092.617949e+09NaNNaNNaNNaNNaNNaNNaN
19532682053389NaN2.665865e+092.665959e+09NaNNaNNaNNaNNaNNaNNaN
19542730228104NaN2.713172e+092.716927e+09NaNNaNNaNNaNNaNNaNNaN
\n", - "
" - ], - "text/plain": [ - " United States Census Bureau (2017)[28] \\\n", - "Year \n", - "1950 2557628654 \n", - "1951 2594939877 \n", - "1952 2636772306 \n", - "1953 2682053389 \n", - "1954 2730228104 \n", - "\n", - " Population Reference Bureau (1973–2016)[15] \\\n", - "Year \n", - "1950 2.516000e+09 \n", - "1951 NaN \n", - "1952 NaN \n", - "1953 NaN \n", - "1954 NaN \n", - "\n", - " United Nations Department of Economic and Social Affairs (2015)[16] \\\n", - "Year \n", - "1950 2.525149e+09 \n", - "1951 2.572851e+09 \n", - "1952 2.619292e+09 \n", - "1953 2.665865e+09 \n", - "1954 2.713172e+09 \n", - "\n", - " Maddison (2008)[17] HYDE (2007)[24] Tanton (1994)[18] \\\n", - "Year \n", - "1950 2.544000e+09 2.527960e+09 2.400000e+09 \n", - "1951 2.571663e+09 NaN NaN \n", - "1952 2.617949e+09 NaN NaN \n", - "1953 2.665959e+09 NaN NaN \n", - "1954 2.716927e+09 NaN NaN \n", - "\n", - " Biraben (1980)[19] McEvedy & Jones (1978)[20] Thomlinson (1975)[21] \\\n", - "Year \n", - "1950 2.527000e+09 2.500000e+09 2.400000e+09 \n", - "1951 NaN NaN NaN \n", - "1952 NaN NaN NaN \n", - "1953 NaN NaN NaN \n", - "1954 NaN NaN NaN \n", - "\n", - " Durand (1974)[22] Clark (1967)[23] \n", - "Year \n", - "1950 NaN 2.486000e+09 \n", - "1951 NaN NaN \n", - "1952 NaN NaN \n", - "1953 NaN NaN \n", - "1954 NaN NaN " - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "table2 = tables[2]\n", - "table2.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`tail` selects the last five rows." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
United States Census Bureau (2017)[28]Population Reference Bureau (1973–2016)[15]United Nations Department of Economic and Social Affairs (2015)[16]Maddison (2008)[17]HYDE (2007)[24]Tanton (1994)[18]Biraben (1980)[19]McEvedy & Jones (1978)[20]Thomlinson (1975)[21]Durand (1974)[22]Clark (1967)[23]
Year
201270138713137.057075e+097.080072e+09NaNNaNNaNNaNNaNNaNNaNNaN
201370921280947.136796e+097.162119e+09NaNNaNNaNNaNNaNNaNNaNNaN
201471699681857.238184e+097.243784e+09NaNNaNNaNNaNNaNNaNNaNNaN
201572478927887.336435e+097.349472e+09NaNNaNNaNNaNNaNNaNNaNNaN
201673259967097.418152e+09NaNNaNNaNNaNNaNNaNNaNNaNNaN
\n", - "
" - ], - "text/plain": [ - " United States Census Bureau (2017)[28] \\\n", - "Year \n", - "2012 7013871313 \n", - "2013 7092128094 \n", - "2014 7169968185 \n", - "2015 7247892788 \n", - "2016 7325996709 \n", - "\n", - " Population Reference Bureau (1973–2016)[15] \\\n", - "Year \n", - "2012 7.057075e+09 \n", - "2013 7.136796e+09 \n", - "2014 7.238184e+09 \n", - "2015 7.336435e+09 \n", - "2016 7.418152e+09 \n", - "\n", - " United Nations Department of Economic and Social Affairs (2015)[16] \\\n", - "Year \n", - "2012 7.080072e+09 \n", - "2013 7.162119e+09 \n", - "2014 7.243784e+09 \n", - "2015 7.349472e+09 \n", - "2016 NaN \n", - "\n", - " Maddison (2008)[17] HYDE (2007)[24] Tanton (1994)[18] \\\n", - "Year \n", - "2012 NaN NaN NaN \n", - "2013 NaN NaN NaN \n", - "2014 NaN NaN NaN \n", - "2015 NaN NaN NaN \n", - "2016 NaN NaN NaN \n", - "\n", - " Biraben (1980)[19] McEvedy & Jones (1978)[20] Thomlinson (1975)[21] \\\n", - "Year \n", - "2012 NaN NaN NaN \n", - "2013 NaN NaN NaN \n", - "2014 NaN NaN NaN \n", - "2015 NaN NaN NaN \n", - "2016 NaN NaN NaN \n", - "\n", - " Durand (1974)[22] Clark (1967)[23] \n", - "Year \n", - "2012 NaN NaN \n", - "2013 NaN NaN \n", - "2014 NaN NaN \n", - "2015 NaN NaN \n", - "2016 NaN NaN " - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "table2.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Long column names are awkard to work with, but we can replace them with abbreviated names." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "table2.columns = ['census', 'prb', 'un', 'maddison', \n", - " 'hyde', 'tanton', 'biraben', 'mj', \n", - " 'thomlinson', 'durand', 'clark']" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the DataFrame looks like now. " - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
censusprbunmaddisonhydetantonbirabenmjthomlinsondurandclark
Year
195025576286542.516000e+092.525149e+092.544000e+092.527960e+092.400000e+092.527000e+092.500000e+092.400000e+09NaN2.486000e+09
19512594939877NaN2.572851e+092.571663e+09NaNNaNNaNNaNNaNNaNNaN
19522636772306NaN2.619292e+092.617949e+09NaNNaNNaNNaNNaNNaNNaN
19532682053389NaN2.665865e+092.665959e+09NaNNaNNaNNaNNaNNaNNaN
19542730228104NaN2.713172e+092.716927e+09NaNNaNNaNNaNNaNNaNNaN
\n", - "
" - ], - "text/plain": [ - " census prb un maddison hyde \\\n", - "Year \n", - "1950 2557628654 2.516000e+09 2.525149e+09 2.544000e+09 2.527960e+09 \n", - "1951 2594939877 NaN 2.572851e+09 2.571663e+09 NaN \n", - "1952 2636772306 NaN 2.619292e+09 2.617949e+09 NaN \n", - "1953 2682053389 NaN 2.665865e+09 2.665959e+09 NaN \n", - "1954 2730228104 NaN 2.713172e+09 2.716927e+09 NaN \n", - "\n", - " tanton biraben mj thomlinson durand \\\n", - "Year \n", - "1950 2.400000e+09 2.527000e+09 2.500000e+09 2.400000e+09 NaN \n", - "1951 NaN NaN NaN NaN NaN \n", - "1952 NaN NaN NaN NaN NaN \n", - "1953 NaN NaN NaN NaN NaN \n", - "1954 NaN NaN NaN NaN NaN \n", - "\n", - " clark \n", - "Year \n", - "1950 2.486000e+09 \n", - "1951 NaN \n", - "1952 NaN \n", - "1953 NaN \n", - "1954 NaN " - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "table2.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The first column, which is labeled `Year`, is special. It is the **index** for this `DataFrame`, which means it contains the labels for the rows.\n", - "\n", - "Some of the values use scientific notation; for example, `2.544000e+09` is shorthand for $2.544 \\cdot 10^9$ or 2.544 billion.\n", - "\n", - "`NaN` is a special value that indicates missing data." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Series\n", - "\n", - "We can use dot notation to select a column from a `DataFrame`. The result is a `Series`, which is like a `DataFrame` with a single column." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Year\n", - "1950 2557628654\n", - "1951 2594939877\n", - "1952 2636772306\n", - "1953 2682053389\n", - "1954 2730228104\n", - "Name: census, dtype: int64" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "census = table2.census\n", - "census.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Year\n", - "2012 7013871313\n", - "2013 7092128094\n", - "2014 7169968185\n", - "2015 7247892788\n", - "2016 7325996709\n", - "Name: census, dtype: int64" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "census.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Like a `DataFrame`, a `Series` contains an index, which labels the rows.\n", - "\n", - "`1e9` is scientific notation for $1 \\cdot 10^9$ or 1 billion." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "From here on, we will work in units of billions." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Year\n", - "1950 2.525149\n", - "1951 2.572851\n", - "1952 2.619292\n", - "1953 2.665865\n", - "1954 2.713172\n", - "Name: un, dtype: float64" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "un = table2.un / 1e9\n", - "un.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Year\n", - "1950 2.557629\n", - "1951 2.594940\n", - "1952 2.636772\n", - "1953 2.682053\n", - "1954 2.730228\n", - "Name: census, dtype: float64" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "census = table2.census / 1e9\n", - "census.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what these estimates look like." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file chap03-fig01.pdf\n" - ] - }, - { - "data": { - "image/png": "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qayYm0aA/+iZE7PfThKuQbWrgSXAOMwZICqBw8AGrfUhj0Y4SoWEhLBu3Truv/9+t6dxWr16NT/72c+w2+3k5eXxyiuvsGbNGtauXUtCgll8MSEhga1bt/a4/4MPPsidd97J66+/DpiVfS+e6mr9+vVERkby0Ucf8YMf/ICAgIDLOEshxGA5nA72Fx/lUOlxDMOgoqaZytoWijlARlwsyXHdZ38IDxl9v6u9tqCUUmlKqTeAUuBHQDyQC+wH6jGT1mal1GGl1J3DEexoEh4ezpo1a/jlL3854H39/f2ZNGkSL774IjExMbz55pv97lNdXU1hYSFf/epXCQgIICAggHnz5l0yQe26det45JFH8PPzY9OmTQOOTQhx+SpcraZDJcfA9UdkWzvEMZFYxrHtUNElf1yORn21oHYCvwJm9tYlXCnlD1wPfEcpla61/qkHYnTbvqIjHCg+6ta2U+InsjxzUbdlW/N2c7Iip9d95qbMYH7qpSUxBuvBBx/kuuuu41vf+hbjx48f8P42m42rr76a7du397ttdHQ048aN4/HHH+crX/kKs2fP7lbEEGDfvn2UlpZy4403cubMGdatW9drDSohxNBzOB0cKPmcQyXHuiWg1IhEbp44lw+2FBEVFsiy2akjtmfeQPSVoJTWurmP9Wit7cAHwAdKqeAhjWwMiI+P54477uDll1/mpZdeGtQxEhISqKur63xdXl5+Sato69athISE8M477/DGG2/wwgsvUFhYyLx583j++ec7Cym+//77LF++nMjISL74xS/y9a9/naqqKmJjYwd9jkII91Q0VbH57E5qWupobe8gMMAPf6sfi9LnMC1+EhaLhdtWhRAdHjgmkhP0cYmvv+TUw/Ytlx/O6OBOyffzvvWtb7F9+3ZOnjw5qPcqKysjMjKy83VCQgL79u3r9jhf5TYpKYknn3ySDRs28OmnnxIcHMz3v/99AFpbW/nkk0+46aabALOSb3JyMh988MGg4hJCuM/hdPC3nK1UNtVSXNlITmEd/o5wbsu+geyEyZ0JKSbCuxVuh5vbM0kopa7DnKIogYsSm9b6viGOa1Dmp868rEtwyzMXXXLZbzAGUvI9Ojqab3zjG4NqQTmdTj799FMWL148qBjvuusuvvvd7wLw97//ncbGRp555hmee+45AOrr61m/fj3f/OY3B3x8IYT7bFYb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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(census, ':', label='US Census')\n", - "plot(un, '--', label='UN DESA')\n", - " \n", - "decorate(xlabel='Year',\n", - " ylabel='World population (billion)')\n", - "savefig('figs/chap03-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following expression computes the elementwise differences between the two series, then divides through by the UN value to produce [relative errors](https://en.wikipedia.org/wiki/Approximation_error), then finds the largest element.\n", - "\n", - "So the largest relative error between the estimates is about 1.3%." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1.3821293828998855" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "max(abs(census - un) / un) * 100" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Break down that expression into smaller steps and display the intermediate results, to make sure you understand how it works.\n", - "\n", - "1. Compute the elementwise differences, `census - un`\n", - "2. Compute the absolute differences, `abs(census - un)`\n", - "3. Compute the relative differences, `abs(census - un) / un`\n", - "4. Compute the percent differences, `abs(census - un) / un * 100`\n" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Year\n", - "1950 0.032480\n", - "1951 0.022089\n", - "1952 0.017480\n", - "1953 0.016188\n", - "1954 0.017056\n", - "1955 0.020448\n", - "1956 0.023728\n", - "1957 0.028307\n", - "1958 0.032107\n", - "1959 0.030321\n", - "1960 0.016999\n", - "1961 0.001137\n", - "1962 -0.000978\n", - "1963 0.008650\n", - "1964 0.017462\n", - "1965 0.021303\n", - "1966 0.023203\n", - "1967 0.021812\n", - "1968 0.020639\n", - "1969 0.021050\n", - "1970 0.021525\n", - "1971 0.023573\n", - "1972 0.023695\n", - "1973 0.022914\n", - "1974 0.021304\n", - "1975 0.018063\n", - "1976 0.014049\n", - "1977 0.011268\n", - "1978 0.008441\n", - "1979 0.007486\n", - " ... \n", - "1987 -0.018115\n", - "1988 -0.023658\n", - "1989 -0.028560\n", - "1990 -0.031861\n", - "1991 -0.037323\n", - "1992 -0.038763\n", - "1993 -0.040597\n", - "1994 -0.042404\n", - "1995 -0.042619\n", - "1996 -0.041576\n", - "1997 -0.040716\n", - "1998 -0.040090\n", - "1999 -0.039403\n", - "2000 -0.039129\n", - "2001 -0.038928\n", - "2002 -0.038837\n", - "2003 -0.039401\n", - "2004 -0.040006\n", - "2005 -0.041050\n", - "2006 -0.041964\n", - "2007 -0.043192\n", - "2008 -0.044599\n", - "2009 -0.046508\n", - "2010 -0.057599\n", - "2011 -0.061999\n", - "2012 -0.066201\n", - "2013 -0.069991\n", - "2014 -0.073816\n", - "2015 -0.101579\n", - "2016 NaN\n", - "Length: 67, dtype: float64" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "census - un" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Year\n", - "1950 0.032480\n", - "1951 0.022089\n", - "1952 0.017480\n", - "1953 0.016188\n", - "1954 0.017056\n", - "1955 0.020448\n", - "1956 0.023728\n", - "1957 0.028307\n", - "1958 0.032107\n", - "1959 0.030321\n", - "1960 0.016999\n", - "1961 0.001137\n", - "1962 0.000978\n", - "1963 0.008650\n", - "1964 0.017462\n", - "1965 0.021303\n", - "1966 0.023203\n", - "1967 0.021812\n", - "1968 0.020639\n", - "1969 0.021050\n", - "1970 0.021525\n", - "1971 0.023573\n", - "1972 0.023695\n", - "1973 0.022914\n", - "1974 0.021304\n", - "1975 0.018063\n", - "1976 0.014049\n", - "1977 0.011268\n", - "1978 0.008441\n", - "1979 0.007486\n", - " ... \n", - "1987 0.018115\n", - "1988 0.023658\n", - "1989 0.028560\n", - "1990 0.031861\n", - "1991 0.037323\n", - "1992 0.038763\n", - "1993 0.040597\n", - "1994 0.042404\n", - "1995 0.042619\n", - "1996 0.041576\n", - "1997 0.040716\n", - "1998 0.040090\n", - "1999 0.039403\n", - "2000 0.039129\n", - "2001 0.038928\n", - "2002 0.038837\n", - "2003 0.039401\n", - "2004 0.040006\n", - "2005 0.041050\n", - "2006 0.041964\n", - "2007 0.043192\n", - "2008 0.044599\n", - "2009 0.046508\n", - "2010 0.057599\n", - "2011 0.061999\n", - "2012 0.066201\n", - "2013 0.069991\n", - "2014 0.073816\n", - "2015 0.101579\n", - "2016 NaN\n", - "Length: 67, dtype: float64" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "abs(census - un)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Year\n", - "1950 0.012862\n", - "1951 0.008585\n", - "1952 0.006674\n", - "1953 0.006072\n", - "1954 0.006286\n", - "1955 0.007404\n", - "1956 0.008439\n", - "1957 0.009887\n", - "1958 0.011011\n", - "1959 0.010208\n", - "1960 0.005617\n", - "1961 0.000369\n", - "1962 0.000311\n", - "1963 0.002702\n", - "1964 0.005350\n", - "1965 0.006399\n", - "1966 0.006829\n", - "1967 0.006289\n", - "1968 0.005827\n", - "1969 0.005821\n", - "1970 0.005832\n", - "1971 0.006258\n", - "1972 0.006166\n", - "1973 0.005847\n", - "1974 0.005332\n", - "1975 0.004437\n", - "1976 0.003388\n", - "1977 0.002670\n", - "1978 0.001965\n", - "1979 0.001712\n", - " ... \n", - "1987 0.003591\n", - "1988 0.004604\n", - "1989 0.005461\n", - "1990 0.005988\n", - "1991 0.006900\n", - "1992 0.007054\n", - "1993 0.007277\n", - "1994 0.007490\n", - "1995 0.007423\n", - "1996 0.007142\n", - "1997 0.006903\n", - "1998 0.006709\n", - "1999 0.006511\n", - "2000 0.006386\n", - "2001 0.006274\n", - "2002 0.006183\n", - "2003 0.006197\n", - "2004 0.006216\n", - "2005 0.006302\n", - "2006 0.006365\n", - "2007 0.006473\n", - "2008 0.006604\n", - "2009 0.006805\n", - "2010 0.008328\n", - "2011 0.008860\n", - "2012 0.009350\n", - "2013 0.009772\n", - "2014 0.010190\n", - "2015 0.013821\n", - "2016 NaN\n", - "Length: 67, dtype: float64" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "abs(census - un) / un" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1.4014999251669376" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "max(abs(census - un) / census) * 100" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`max` and `abs` are built-in functions provided by Python, but NumPy also provides version that are a little more general. When you import `modsim`, you get the NumPy versions of these functions." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Constant growth" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can select a value from a `Series` using bracket notation. Here's the first element:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2.557628654" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "census[1950]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And the last value." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "7.325996709" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "census[2016]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But rather than \"hard code\" those dates, we can get the first and last labels from the `Series`:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1950" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_0 = get_first_label(census)" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2016" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_end = get_last_label(census)" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "66" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "elapsed_time = t_end - t_0" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we can get the first and last values:" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2.557628654" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "p_0 = get_first_value(census)" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "7.325996709" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "p_end = get_last_value(census)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then we can compute the average annual growth in billions of people per year." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "4.768368055" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "total_growth = p_end - p_0" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.07224800083333333" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "annual_growth = total_growth / elapsed_time" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### TimeSeries" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's create a `TimeSeries` to contain values generated by a linear growth model." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
\n", - "
" - ], - "text/plain": [ - "Series([], dtype: float64)" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results = TimeSeries()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Initially the `TimeSeries` is empty, but we can initialize it so the starting value, in 1950, is the 1950 population estimated by the US Census." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
19502.557629
\n", - "
" - ], - "text/plain": [ - "1950 2.557629\n", - "dtype: float64" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results[t_0] = census[t_0]\n", - "results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After that, the population in the model grows by a constant amount each year." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "for t in linrange(t_0, t_end):\n", - " results[t+1] = results[t] + annual_growth" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the results looks like, compared to the actual data." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file chap03-fig02.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(census, ':', label='US Census')\n", - "plot(un, '--', label='UN DESA')\n", - "plot(results, color='gray', label='model')\n", - "\n", - "decorate(xlabel='Year', \n", - " ylabel='World population (billion)',\n", - " title='Constant growth')\n", - "savefig('figs/chap03-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The model fits the data pretty well after 1990, but not so well before." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises\n", - "\n", - "**Optional Exercise:** Try fitting the model using data from 1970 to the present, and see if that does a better job.\n", - "\n", - "Hint: \n", - "\n", - "1. Copy the code from above and make a few changes. Test your code after each small change.\n", - "\n", - "2. Make sure your `TimeSeries` starts in 1950, even though the estimated annual growth is based on later data.\n", - "\n", - "3. You might want to add a constant to the starting value to match the data better." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "def compute_annual_growth(t_0, t_end):\n", - " \"\"\"Average annual growth over given period.\n", - " \n", - " t_0: start date\n", - " t_end: end_date\n", - " \n", - " returns: average annual growth\n", - " \"\"\"\n", - " elapsed_time = t_end - t_0\n", - " p_0 = census[t_0]\n", - " p_end = census[t_end]\n", - " total_growth = p_end - p_0\n", - " annual_growth = total_growth / elapsed_time\n", - " return annual_growth\n", - "\n", - "# compute annual growth using data from 1970 to the end\n", - "t_0 = 1970\n", - "t_end = get_last_label(census)\n", - "annual_growth = compute_annual_growth(t_0, t_end)\n", - "\n", - "# Run the simulation over the whole time range.\n", - "# I subtract 0.45 from the initial value to shift\n", - "# the fitted curve down so it fits the data better.\n", - "t_0 = get_first_label(census)\n", - "t_end = get_last_label(census)\n", - "p_0 = get_first_value(census) - 0.45\n", - "\n", - "# initialize the result\n", - "results = TimeSeries()\n", - "results[t_0] = p_0\n", - "\n", - "# run the simulation\n", - "for t in linrange(t_0, t_end):\n", - " results[t+1] = results[t] + annual_growth\n", - " \n", - "# plot the results\n", - "plot(census, ':', label='US Census')\n", - "plot(un, '--', label='UN DESA')\n", - "plot(results, '--', color='gray', label='model')\n", - "\n", - "decorate(xlabel='Year', \n", - " ylabel='World population (billion)',\n", - " title='Constant growth')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap06soln.ipynb b/code/soln/chap06soln.ipynb deleted file mode 100644 index f223a4c9b..000000000 --- a/code/soln/chap06soln.ipynb +++ /dev/null @@ -1,807 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 6\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *\n", - "\n", - "from pandas import read_html" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Code from the previous chapter\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "filename = 'data/World_population_estimates.html'\n", - "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", - "table2 = tables[2]\n", - "table2.columns = ['census', 'prb', 'un', 'maddison', \n", - " 'hyde', 'tanton', 'biraben', 'mj', \n", - " 'thomlinson', 'durand', 'clark']" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Year\n", - "1950 2.525149\n", - "1951 2.572851\n", - "1952 2.619292\n", - "1953 2.665865\n", - "1954 2.713172\n", - "Name: un, dtype: float64" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "un = table2.un / 1e9\n", - "un.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Year\n", - "1950 2.557629\n", - "1951 2.594940\n", - "1952 2.636772\n", - "1953 2.682053\n", - "1954 2.730228\n", - "Name: census, dtype: float64" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "census = table2.census / 1e9\n", - "census.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.07224800083333333" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_0 = get_first_label(census)\n", - "t_end = get_last_label(census)\n", - "elapsed_time = t_end - t_0\n", - "\n", - "p_0 = get_first_value(census)\n", - "p_end = get_last_value(census)\n", - "total_growth = p_end - p_0\n", - "\n", - "annual_growth = total_growth / elapsed_time" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### System objects" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can rewrite the code from the previous chapter using system objects." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
t_01950.000000
t_end2016.000000
p_02.557629
annual_growth0.072248
\n", - "
" - ], - "text/plain": [ - "t_0 1950.000000\n", - "t_end 2016.000000\n", - "p_0 2.557629\n", - "annual_growth 0.072248\n", - "dtype: float64" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = System(t_0=t_0, \n", - " t_end=t_end,\n", - " p_0=p_0,\n", - " annual_growth=annual_growth)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we can encapsulate the code that runs the model in a function." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation1(system):\n", - " \"\"\"Runs the constant growth model.\n", - " \n", - " system: System object\n", - " \n", - " returns: TimeSeries\n", - " \"\"\"\n", - " results = TimeSeries()\n", - " results[system.t_0] = system.p_0\n", - " \n", - " for t in linrange(system.t_0, system.t_end):\n", - " results[t+1] = results[t] + system.annual_growth\n", - " \n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also encapsulate the code that plots the results." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_results(census, un, timeseries, title):\n", - " \"\"\"Plot the estimates and the model.\n", - " \n", - " census: TimeSeries of population estimates\n", - " un: TimeSeries of population estimates\n", - " timeseries: TimeSeries of simulation results\n", - " title: string\n", - " \"\"\"\n", - " plot(census, ':', label='US Census')\n", - " plot(un, '--', label='UN DESA')\n", - " plot(timeseries, color='gray', label='model')\n", - " \n", - " decorate(xlabel='Year', \n", - " ylabel='World population (billion)',\n", - " title=title)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we run it." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "results = run_simulation1(system)\n", - "plot_results(census, un, results, 'Constant growth model')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Proportional growth" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a more realistic model where the number of births and deaths is proportional to the current population." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation2(system):\n", - " \"\"\"Run a model with proportional birth and death.\n", - " \n", - " system: System object\n", - " \n", - " returns: TimeSeries\n", - " \"\"\"\n", - " results = TimeSeries()\n", - " results[system.t_0] = system.p_0\n", - " \n", - " for t in linrange(system.t_0, system.t_end):\n", - " births = system.birth_rate * results[t]\n", - " deaths = system.death_rate * results[t]\n", - " results[t+1] = results[t] + births - deaths\n", - " \n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "I picked a death rate that seemed reasonable and then adjusted the birth rate to fit the data." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "system.death_rate = 0.01\n", - "system.birth_rate = 0.027" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what it looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap03-fig03.pdf\n" - ] - }, - { - "data": { - "image/png": "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BifRSwCRUTNZsWIFkZGRx7jF04e/A6NVwEjf9UigZoTyY66qqoqKigr6+/sn/F4BAQHk5OSQlZXld53FixdzxRVXAHDDDTfw+c9/nttvv52FCxcCcPbZZ7Nx40ZOOeUU3n//fd544w0yMszkHj/4wQ/4+te/zu23345hGLz66qv89re/ZcGCBQB885vf5IYbbvDe6+mnn+bUU0/1lqWlpXHLLbfwyCOPcP3114/L90CI6cbd30t3dRHdNUX0dDsJstswMKhv66OuN5iAxGTC2/qIsllZvGgeKidHek3jzN8A9Q3gIaXUt4B1nrKlwH2nYMz0AAAgAElEQVTArRPRsKNVXV09KcEJoL+/n+rq6sMKUNnZ2d6vo6OjAZg9e7a3bObMmdTU1FBdXc2MGTO8wQmgsLCQvr4+6uvrMQyD/v5+CgoOdmQHAtWAiooK3nnnHQoLC71lLpcLt9vt/5sU4gTTVbGRtsoi9jV30tLRS2JsGHusEbQGxRLg2Y8pJSGUwsJCYmNllt5EGCuTRB++i3NtwNoRTn0dCBzndh21zMzMSe1BZWZmHnadAQOfuoaWjbZX1+BPaf7s59XV1cWFF17IV7/61cNqoxAnspDMBdRs3khLRy8tOCg9EE5qaow3e3Z0dDQLFy4kODj4mLZzOhurB/VlRs4ecVzIyso6rB7NVJWRkUFrays1NTXeXtSnn36K3W4nNTUVwzAICAigpKSEU045BYCioiKfa+Tm5rJhwwbS0tImvf1CHA/6mvdgC52BNTDEW2YLDiV10WlsfL+cfZ29hAcF4nKZefOysrLIzc3FapUFtxNprIW6f5zEdohRZGVlsXz5cr773e/ywx/+kO7ubn72s59x6aWXEhoaCsAFF1zAz372M372s59hGAYPPvigzzWuuuoqnnvuOe68806uvPJKAgMDKS8vp66uTnpV4oTW39FMl15Pz54dtIZnkrHsTG9uvN7eXipa+wkMDSJjRgiO4ADsdjsLFiwgISHhGLf8xDBq+FdKhYx2bDzOF/679957iY2N5eqrr+arX/0qixYt4o477vAe/973vkdaWhpXX3013/rWt7jlllt86icmJvLUU0+xc+dOrrjiCi699FKefPJJEhMTh95KiBOCOTPvPzS//1eadlRStbOFhuJPKCndAZhLO9577z327t3LjLBAHMEBREZGctppp0lw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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "results = run_simulation2(system)\n", - "plot_results(census, un, results, 'Proportional model')\n", - "savefig('figs/chap03-fig03.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The model fits the data pretty well for the first 20 years, but not so well after that." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Factoring out the update function" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`run_simulation1` and `run_simulation2` are nearly identical except the body of the loop. So we can factor that part out into a function." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def update_func1(pop, t, system):\n", - " \"\"\"Compute the population next year.\n", - " \n", - " pop: current population\n", - " t: current year\n", - " system: system object containing parameters of the model\n", - " \n", - " returns: population next year\n", - " \"\"\"\n", - " births = system.birth_rate * pop\n", - " deaths = system.death_rate * pop\n", - " return pop + births - deaths" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The name `update_func` refers to a function object." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "update_func1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Which we can confirm by checking its type." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "function" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(update_func1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`run_simulation` takes the update function as a parameter and calls it just like any other function." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Simulate the system using any update function.\n", - " \n", - " system: System object\n", - " update_func: function that computes the population next year\n", - " \n", - " returns: TimeSeries\n", - " \"\"\"\n", - " results = TimeSeries()\n", - " results[system.t_0] = system.p_0\n", - " \n", - " for t in linrange(system.t_0, system.t_end):\n", - " results[t+1] = update_func(results[t], t, system)\n", - " \n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we use it." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
t_01950.000000
t_end2016.000000
p_02.557629
birth_rate0.027000
death_rate0.010000
\n", - "
" - ], - "text/plain": [ - "t_0 1950.000000\n", - "t_end 2016.000000\n", - "p_0 2.557629\n", - "birth_rate 0.027000\n", - "death_rate 0.010000\n", - "dtype: float64" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_0 = get_first_label(census)\n", - "t_end = get_last_label(census)\n", - "p_0 = census[t_0]\n", - "\n", - "system = System(t_0=t_0, \n", - " t_end=t_end,\n", - " p_0=p_0,\n", - " birth_rate=0.027,\n", - " death_rate=0.01)" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "results = run_simulation(system, update_func1)\n", - "plot_results(census, un, results, 'Proportional model, factored')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Remember not to put parentheses after `update_func1`. What happens if you try?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** When you run `run_simulation`, it runs `update_func1` once for each year between `t_0` and `t_end`. To see that for yourself, add a print statement at the beginning of `update_func1` that prints the values of `t` and `pop`, then run `run_simulation` again." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Combining birth and death" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Since births and deaths get added up, we don't have to compute them separately. We can combine the birth and death rates into a single net growth rate." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def update_func2(pop, t, system):\n", - " \"\"\"Compute the population next year.\n", - " \n", - " pop: current population\n", - " t: current year\n", - " system: system object containing parameters of the model\n", - " \n", - " returns: population next year\n", - " \"\"\"\n", - " net_growth = system.alpha * pop\n", - " return pop + net_growth" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how it works:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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ruVPc4c7eawQYhYwB7hyOPtPJpdddhlPP/00559/vssigoMJDQ0lPDyc8PBwEhISOPXUUzn33HP5y1/+wm23dd9TBAUF9TlWor6+nl27dvHOO++QmpoK6NOlnHzyyb22XbduHddccw3R0dG88847LsdXFGX4LBYL27Zto6amxpEWGhrK/Pnz8fb2RrNZsbW30dFp43BtC0daIGPFmQQnz8Jg0DtpGwwGzl18bM614P4vHiClLAFKBt1wcD8Cdkopc0fgWP1qLtxKS/F2t7b1iU0jYPapLmmNOV/SVpE/6L7mGfPwSzlxWHkczA9/+EM++ugj3n77bS699NJhHyckJITzzjuPzz77zK0AYjab8fX15dNPPyU5OdlRxdhTRUUFhYWFnHrqqURFRXHjjTeydu3aPuf8UhTFfY2NjWzZssVlgdKec+oZjB74py+m5J1/UW6I5lBoMh01gZxlOD6+f/0GKCHEXcATUspW+9/9klI+4O4JhRBG4Ar09aWUQXh7e3P99dfzzDPPcOGFFx7VsRITE3nttddc0n7yk5+4BJ+kpCTefPNNTCYT9913H/feey8vvPACmZmZnHzyyaxcuZKQkBDH9uvWrWPZsmV4e3sze/ZsvL292bRpU58lLUVR3FNVVcWOHTtcOkPMiJrCNENNr67gXmExxHzvf9j8jT7reHuHDavVhofHsR+kBipB/QR4Hmi1/90fDX2clLtWoC8b/68h7DOpXXrppfztb3/jjTfe4NRTTx18h35omtbrw/373/+eOXPmOB47d3648MILWbFiBd999x3bt2/nxRdf5B//+Af/+c9/iIiIAGD9+vXcdVf3/cs555zDunXrVIBSlGHQNI2SkhIKCgocnSGMmhVh7iSoKguLBo1e4USnzXbZL2raVOanWYkO9yc2YuIvROiufgOUlDKhr79HwI+Aj6WUVSN4zD75pZx4VFVvAbNP7VXtNxL8/Pxoamrqld7Y2Ii/v3+vdC8vL2644QaeeuopFi1aNOzzlpaWEh0d7ZIWERFBXFxfvf91/v7+nHHGGZxxxhnceOONnHnmmbz22mv84he/YNu2bezdu5cbb7zRsb2mafj4+NDS0oLZbB52XhVlsrFarWRlZbFv3z49wWbDs62OVJ9m/C1GWts6OVjTTO3ez2BKPNFTXQPRollR45Dr0TWmZUAhhB/wffT1pSat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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "system.alpha = system.birth_rate - system.death_rate\n", - "\n", - "results = run_simulation(system, update_func2)\n", - "plot_results(census, un, results, 'Proportional model, combined birth and death')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises\n", - "\n", - "**Exercise:** Maybe the reason the proportional model doesn't work very well is that the growth rate, `alpha`, is changing over time. So let's try a model with different growth rates before and after 1980 (as an arbitrary choice).\n", - "\n", - "Write an update function that takes `pop`, `t`, and `system` as parameters. The system object, `system`, should contain two parameters: the growth rate before 1980, `alpha1`, and the growth rate after 1980, `alpha2`. It should use `t` to determine which growth rate to use. Note: Don't forget the `return` statement.\n", - "\n", - "Test your function by calling it directly, then pass it to `run_simulation`. Plot the results. Adjust the parameters `alpha1` and `alpha2` to fit the data as well as you can.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": true, - "scrolled": false - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def update_func3(pop, t, system):\n", - " \"\"\"Compute the population next year.\n", - " \n", - " pop: current population\n", - " t: current year\n", - " system: system object containing parameters of the model\n", - " \n", - " returns: population next year\n", - " \"\"\"\n", - " if t < 1980:\n", - " net_growth = system.alpha1 * pop\n", - " else:\n", - " net_growth = system.alpha2 * pop\n", - " return pop + net_growth" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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C+yDYRh71TfffffdzJ49u+O1byOEk08+meXLl/PNN9/w/fff8/e//53nnnuOf//738THxwPw1ltvccstndcpxx9/PG+++aZORNqYZng9tBb+QEX+GjaVN7GnxUNgQhrjIkIICQkhJyeHmJiYHn+TY9E+E5GUMrWnv0eSsIx5B1RlFjFzyV7VdQMhLCyMpqamvaY3NjYSHh6+13S73c7ll1/On/70J+bPn7/f2y0qKiIpKanLtPj4eFJSehqdQwkPD+eYY47hmGOO4YorruDYY4/l5Zdf5pprrmHt2rWUlpZyxRVXdMxvGAbBwcG0tLRAdQprAAAgAElEQVQQGhq637Fq2kjlrCqlcdN/KSqvYtueVppanBgGNFTtIWPqXHJyDukYZVknIUVX5A+DlJQU8vPzu0xrampi+/btTJkypcdlVqxYQWBgIK+88kqP7/eltraWd955h2XLlu3X8gARERHExsbS2toKwKpVq/jxj3/Mm2++2fFv1apVJCQk8OGHH+73djRtJPK0NtGw7kPKv36btUXllNQ5sFkteG1BNIZMxBsliIhL60hCWqfe7hGd7e9KpJT/GJhwxoZzzz2Xiy++mOzsbBYvXkxTUxNPPfUU8fHxHHHEET0uExAQwBVXXMGdd97p1zZqamoICgqiqamJDRs28NRTTxEVFcUll3TtCKO+vp6qqi4jdDB+/Hhqa2u56aabOP3008nIyMBisfDWW29RUFDAzTffjNPp5P333+f+++/fq7n20UcfzapVqzjllFP83ymaNkIZXg+txRto3vo9u+pbKKxpw2MAFiv26DgSIhNpMOJYelgayXERwx3uQam3e0Qv+rkOA9CJqB/mz5/Po48+ytNPP80jjzxCaGgoc+fO5dlnn+31aunkk0/mmWeeoaSkpM9tnHHGGVgsFkJDQ5kyZQonnngi559//l5Vf1ddddVey3744YfEx8eTkZHBk08+SVlZGTabjdTUVB555BEWLFjAe++9h8vlYtGiRXstv3z5cv76179SWVnZcS9J00Yjd2MNDes/oqWumnVlTdQ7PAQHBWALG4c9OgGRmUV6ejoGFmwH6TDdBwNLe9PCg50QYgpQvK8eBTZv3kxmZuaQx6UNLv29agczr7ONvLeeY21xNS6PgcdqJ25yCjEJieTk5HR5eHws8OnZJVVKWeLvciO//3BN07Rh4HK5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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "system.alpha1 = 0.0195\n", - "system.alpha2 = 0.014\n", - "\n", - "results = run_simulation(system, update_func3)\n", - "plot_results(census, un, results, 'Proportional model, parameter changes over time')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap07soln.ipynb b/code/soln/chap07soln.ipynb deleted file mode 100644 index 0233fddf2..000000000 --- a/code/soln/chap07soln.ipynb +++ /dev/null @@ -1,788 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 7\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *\n", - "\n", - "from pandas import read_html" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Code from the previous chapter" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "filename = 'data/World_population_estimates.html'\n", - "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", - "table2 = tables[2]\n", - "table2.columns = ['census', 'prb', 'un', 'maddison', \n", - " 'hyde', 'tanton', 'biraben', 'mj', \n", - " 'thomlinson', 'durand', 'clark']" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Year\n", - "1950 2.525149\n", - "1951 2.572851\n", - "1952 2.619292\n", - "1953 2.665865\n", - "1954 2.713172\n", - "Name: un, dtype: float64" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "un = table2.un / 1e9\n", - "un.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Year\n", - "1950 2.557629\n", - "1951 2.594940\n", - "1952 2.636772\n", - "1953 2.682053\n", - "1954 2.730228\n", - "Name: census, dtype: float64" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "census = table2.census / 1e9\n", - "census.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_results(census, un, timeseries, title):\n", - " \"\"\"Plot the estimates and the model.\n", - " \n", - " census: TimeSeries of population estimates\n", - " un: TimeSeries of population estimates\n", - " timeseries: TimeSeries of simulation results\n", - " title: string\n", - " \"\"\"\n", - " plot(census, ':', label='US Census')\n", - " plot(un, '--', label='UN DESA')\n", - " plot(timeseries, color='gray', label='model')\n", - " \n", - " decorate(xlabel='Year', \n", - " ylabel='World population (billion)',\n", - " title=title)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Simulate the system using any update function.\n", - " \n", - " system: System object\n", - " update_func: function that computes the population next year\n", - " \n", - " returns: TimeSeries\n", - " \"\"\"\n", - " results = TimeSeries()\n", - " results[system.t_0] = system.p_0\n", - " \n", - " for t in linrange(system.t_0, system.t_end):\n", - " results[t+1] = update_func(results[t], t, system)\n", - " \n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Quadratic growth" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the implementation of the quadratic growth model." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def update_func_quad(pop, t, system):\n", - " \"\"\"Compute the population next year with a quadratic model.\n", - " \n", - " pop: current population\n", - " t: current year\n", - " system: system object containing parameters of the model\n", - " \n", - " returns: population next year\n", - " \"\"\"\n", - " net_growth = system.alpha * pop + system.beta * pop**2\n", - " return pop + net_growth" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a `System` object with the parameters `alpha` and `beta`:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
t_01950.000000
t_end2016.000000
p_02.557629
alpha0.025000
beta-0.001800
\n", - "
" - ], - "text/plain": [ - "t_0 1950.000000\n", - "t_end 2016.000000\n", - "p_0 2.557629\n", - "alpha 0.025000\n", - "beta -0.001800\n", - "dtype: float64" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_0 = get_first_label(census)\n", - "t_end = get_last_label(census)\n", - "p_0 = census[t_0]\n", - "\n", - "system = System(t_0=t_0, \n", - " t_end=t_end,\n", - " p_0=p_0,\n", - " alpha=0.025,\n", - " beta=-0.0018)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here are the results." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap03-fig04.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "results = run_simulation(system, update_func_quad)\n", - "plot_results(census, un, results, 'Quadratic model')\n", - "savefig('figs/chap03-fig04.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Can you find values for the parameters that make the model fit better?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Equilibrium\n", - "\n", - "To understand the quadratic model better, let's plot net growth as a function of population." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "pop_array = linspace(0, 15, 100)\n", - "net_growth_array = system.alpha * pop_array + system.beta * pop_array**2\n", - "None" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what it looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap03-fig05.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "sns.set_style('whitegrid')\n", - "\n", - "plot(pop_array, net_growth_array)\n", - "decorate(xlabel='Population (billions)',\n", - " ylabel='Net growth (billions)')\n", - "savefig('figs/chap03-fig05.pdf')\n", - "\n", - "sns.set_style('white')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what it looks like. Remember that the x axis is population now, not time." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It looks like the growth rate passes through 0 when the population is a little less than 14 billion.\n", - "\n", - "In the book we found that the net growth is 0 when the population is $-\\alpha/\\beta$:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "13.88888888888889" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "-system.alpha / system.beta" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This is the equilibrium the population tends toward." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`sns` is a library called Seaborn which provides functions that control the appearance of plots. In this case I want a grid to make it easier to estimate the population where the growth rate crosses through 0." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Dysfunctions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When people first learn about functions, there are a few things they often find confusing. In this section I present and explain some common problems with functions.\n", - "\n", - "As an example, suppose you want a function that takes a `System` object, with variables `alpha` and `beta`, as a parameter and computes the carrying capacity, `-alpha/beta`. Here's a good solution:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "13.88888888888889\n" - ] - } - ], - "source": [ - "def carrying_capacity(system):\n", - " K = -system.alpha / system.beta\n", - " return K\n", - " \n", - "sys1 = System(alpha=0.025, beta=-0.0018)\n", - "pop = carrying_capacity(sys1)\n", - "print(pop)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's see all the ways that can go wrong.\n", - "\n", - "**Dysfunction #1:** Not using parameters. In the following version, the function doesn't take any parameters; when `sys1` appears inside the function, it refers to the object we created outside the function.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "13.88888888888889\n" - ] - } - ], - "source": [ - "def carrying_capacity():\n", - " K = -sys1.alpha / sys1.beta\n", - " return K\n", - " \n", - "sys1 = System(alpha=0.025, beta=-0.0018)\n", - "pop = carrying_capacity()\n", - "print(pop)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This version actually works, but it is not as versatile as it could be. If there are several `System` objects, this function can only work with one of them, and only if it is named `system`.\n", - "\n", - "**Dysfunction #2:** Clobbering the parameters. When people first learn about parameters, they often write functions like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "13.88888888888889\n" - ] - } - ], - "source": [ - "def carrying_capacity(system):\n", - " system = System(alpha=0.025, beta=-0.0018)\n", - " K = -system.alpha / system.beta\n", - " return K\n", - " \n", - "sys1 = System(alpha=0.025, beta=-0.0018)\n", - "pop = carrying_capacity(sys1)\n", - "print(pop)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this example, we have a `System` object named `sys1` that gets passed as an argument to `carrying_capacity`. But when the function runs, it ignores the argument and immediately replaces it with a new `System` object. As a result, this function always returns the same value, no matter what argument is passed.\n", - "\n", - "When you write a function, you generally don't know what the values of the parameters will be. Your job is to write a function that works for any valid values. If you assign your own values to the parameters, you defeat the whole purpose of functions.\n", - "\n", - "\n", - "**Dysfunction #3:** No return value. Here's a version that computes the value of `K` but doesn't return it." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "None\n" - ] - } - ], - "source": [ - "def carrying_capacity(system):\n", - " K = -system.alpha / system.beta\n", - " \n", - "sys1 = System(alpha=0.025, beta=-0.0018)\n", - "pop = carrying_capacity(sys1)\n", - "print(pop)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A function that doesn't have a return statement always returns a special value called `None`, so in this example the value of `pop` is `None`. If you are debugging a program and find that the value of a variable is `None` when it shouldn't be, a function without a return statement is a likely cause.\n", - "\n", - "**Dysfunction #4:** Ignoring the return value. Finally, here's a version where the function is correct, but the way it's used is not." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "13.88888888888889" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "def carrying_capacity(system):\n", - " K = -system.alpha / system.beta\n", - " return K\n", - " \n", - "sys2 = System(alpha=0.025, beta=-0.0018)\n", - "carrying_capacity(sys2)\n", - "\n", - "# print(K) This line won't work because K only exists inside the function." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this example, `carrying_capacity` runs and returns `K`, but the return value is dropped.\n", - "\n", - "When you call a function that returns a value, you should do something with the result. Often you assign it to a variable, as in the previous examples, but you can also use it as part of an expression.\n", - "\n", - "For example, you could eliminate the temporary variable `pop` like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "13.88888888888889\n" - ] - } - ], - "source": [ - "print(carrying_capacity(sys1))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Or if you had more than one system, you could compute the total carrying capacity like this:\n" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "27.77777777777778" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "total = carrying_capacity(sys1) + carrying_capacity(sys2)\n", - "total" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Exercise:** In the book, I present a different way to parameterize the quadratic model:\n", - "\n", - "$ \\Delta p = r p (1 - p / K) $\n", - "\n", - "where $r=\\alpha$ and $K=-\\alpha/\\beta$. Write a version of `update_func` that implements this version of the model. Test it by computing the values of `r` and `K` that correspond to `alpha=0.025, beta=-0.0018`, and confirm that you get the same results. " - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(0.025, 13.88888888888889)" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "system = System(t_0=t_0, \n", - " t_end=t_end,\n", - " p_0=p_0,\n", - " alpha=0.025,\n", - " beta=-0.0018)\n", - "\n", - "system.r = system.alpha\n", - "system.K = -system.alpha/system.beta\n", - "\n", - "system.r, system.K" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def update_func_quad2(pop, t, system):\n", - " \"\"\"Compute the population next year.\n", - " \n", - " pop: current population\n", - " t: current year\n", - " system: system object containing parameters of the model\n", - " \n", - " returns: population next year\n", - " \"\"\"\n", - " net_growth = system.r * pop * (1 - pop / system.K)\n", - " return pop + net_growth" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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JyoqCj8/P1sHjTO0Wu0Fe/zFxsaSkZFBcXGxXee9VGFhYeh0Og4cONCk/eDBg7ZbqD179qS8vJycnBzb+79+HRsbCzT2ugwLC2vy7+znVEJcDAko0a4tX74cjUbDvffey549ezh58iRHjx7lscce4z//+Q9r1qyxXSEMHjyYhoYG/vSnP5GXl8eXX37Jli1bmhwvPDycAwcOsH//frKzs3nttddITExsss3s2bPZvHkz27ZtIycnh02bNrFv3z676g0PD2fHjh0cP36c1NRUFi1a1CyMunXrxuHDhykqKqK8vLzZ1QzA1KlTCQoK4qGHHuKnn34iPz+fffv28a9//etivnwX5OzszMyZM1m7di1ffvklOTk5bNy4kd27d/Pggw8CMHToUHr37s3ixYs5evQoqampLF68uMmtu7CwMG655Raefvpptm3bRm5uLmlpaWzdutV2q1CIiyW3+ES7FhgYyKeffsqGDRtYvnw5JSUlWCwW/P392bFjB927d7dt26NHD1asWMGbb77Je++9x6BBg1i0aBGLFi2ybTNv3jyKioqYN28eWq2WyZMnM3PmTD777DPbNvfeey/l5eWsWrUKg8HAqFGjmD9/PqtXr75gvatWrWL58uXMmDEDX19f5syZY+sGf8aCBQtYvnw5kyZNwmAwsHv37mbHcXZ25oMPPuDll19m4cKF1NXVERwc3KSDx5WycOFC1Go1K1eupKKigtDQUF5++WWGDh0KNN6iW79+PX/84x+5++678fLyYs6cOU26qgOsWLGCTZs2sXHjRgoKCnB1daVnz57cfffdV7xmcW1QyZLvoqP57rvveOSRR5gzZw6PPvpoW5cjhGglcotPdDhjxoxh06ZNqNVq8vPz27ocIUQrkSsoIYQQ7VKHeQbV0NDAsWPH8PPzk3EVQgjRwVgsFk6fPk1cXBxOTk527dNhAurYsWPysFUIITq4LVu2MGDAALu27TABdWZamC1btlz06HohhBBtq7i4mLvvvvu8U3z9WocJqDO39bp27Uq3bt3auBohhBCX4mIe0UgvPiGEEO2SBJQQQoh2SQJKCCFEuyQBJYQQol3qMJ0kLsRqtVJQUEBtbW1blyKuAK1Wi7+/P+7u7m1dihCijXSagCotLUWlUhEVFYVaLReGHZmiKNTX11NYWAggISVEG6qvryc/Px9nZ2e6det2Vdf3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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "results = run_simulation(system, update_func_quad2)\n", - "plot_results(census, un, results, 'Quadratic model')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap08soln.ipynb b/code/soln/chap08soln.ipynb deleted file mode 100644 index d94cd103b..000000000 --- a/code/soln/chap08soln.ipynb +++ /dev/null @@ -1,1068 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 8\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *\n", - "\n", - "from pandas import read_html" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Functions from the previous chapter" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def plot_results(census, un, timeseries, title):\n", - " \"\"\"Plot the estimates and the model.\n", - " \n", - " census: TimeSeries of population estimates\n", - " un: TimeSeries of population estimates\n", - " timeseries: TimeSeries of simulation results\n", - " title: string\n", - " \"\"\"\n", - " plot(census, ':', label='US Census')\n", - " plot(un, '--', label='UN DESA')\n", - " plot(timeseries, color='gray', label='model')\n", - " \n", - " decorate(xlabel='Year', \n", - " ylabel='World population (billion)',\n", - " title=title)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Simulate the system using any update function.\n", - " \n", - " system: System object\n", - " update_func: function that computes the population next year\n", - " \n", - " returns: TimeSeries\n", - " \"\"\"\n", - " results = TimeSeries()\n", - " results[system.t_0] = system.p_0\n", - " \n", - " for t in linrange(system.t_0, system.t_end):\n", - " results[t+1] = update_func(results[t], t, system)\n", - " \n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Reading the data" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "filename = 'data/World_population_estimates.html'\n", - "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", - "table2 = tables[2]\n", - "table2.columns = ['census', 'prb', 'un', 'maddison', \n", - " 'hyde', 'tanton', 'biraben', 'mj', \n", - " 'thomlinson', 'durand', 'clark']" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "un = table2.un / 1e9\n", - "census = table2.census / 1e9\n", - "plot(census, ':', label='US Census')\n", - "plot(un, '--', label='UN DESA')\n", - " \n", - "decorate(xlabel='Year', \n", - " ylabel='World population (billion)',\n", - " title='Estimated world population')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Running the quadratic model" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the update function for the quadratic growth model with parameters `alpha` and `beta`." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def update_func_quad(pop, t, system):\n", - " \"\"\"Update population based on a quadratic model.\n", - " \n", - " pop: current population in billions\n", - " t: what year it is\n", - " system: system object with model parameters\n", - " \"\"\"\n", - " net_growth = system.alpha * pop + system.beta * pop**2\n", - " return pop + net_growth" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Extract the starting time and population." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2.557628654" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_0 = get_first_label(census)\n", - "t_end = get_last_label(census)\n", - "p_0 = get_first_value(census)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Initialize the system object." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
t_01950.000000
t_end2016.000000
p_02.557629
alpha0.025000
beta-0.001800
\n", - "
" - ], - "text/plain": [ - "t_0 1950.000000\n", - "t_end 2016.000000\n", - "p_0 2.557629\n", - "alpha 0.025000\n", - "beta -0.001800\n", - "dtype: float64" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = System(t_0=t_0, \n", - " t_end=t_end,\n", - " p_0=p_0,\n", - " alpha=0.025,\n", - " beta=-0.0018)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Run the model and plot results." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "results = run_simulation(system, update_func_quad)\n", - "plot_results(census, un, results, 'Quadratic model')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Generating projections" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To generate projections, all we have to do is change `t_end`" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap04-fig01.pdf\n" - ] - }, - { - "data": { - "image/png": "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k5v7ZteCggIuv/xyTjzxRN5//33uvfdeFi1axEsvvVRn+SeeeIJLL72Ud999lx49enD77bd75t18881MmDCB999/n9dee40zzzzT5xhffvllNmzYwLPPPssHH3zAbbfdRmxsrM+vPxq+1qC+Av4kIpOpO6LuGXb5L90L2gnmqBhjHgUePdr1dAUuVy2H1nzGul37qa51ERERTv9BwznplJnahFd1OCs27mXFpr0ATB7Vh8mj654B+CYjl7WZeQAcO64vadK7zvwvVuewccdBAGamD2D0kLrDyHyyLJutOQUAzJmSinNgot9i/9nPfsZnn33GueeeS//+/UlPT+ekk05izpzGfttbsrOz+fnPf97sel999VWmT5/OFVdcAUBqaioLFizgySef5PLLL/csd/HFFzNr1iwArr76ai666CLKy8uJiopiz549zJ49mwEDrEv5w4cP93m/9u7dy+jRoxk3bhyAZx3twdcE5U4Wv7Ifjc0D6wbeo05QynclmavYmLmN4kqrxV5kr34cO2MGUVFRAY5Mqa4lLCyMxx57jOzsbL777jtWrFjBDTfcwGmnncbf/va3I15vZmYmS5YsIS3tp4EjampqPK103dyn8wCSkpIAyM/Pp2/fvlxyySVcccUVHHvssRx77LGcdtppJCb6lpzPPvtsLrvsMjZv3sxxxx3HqaeeyqhRo1p+oR/4eqOutk8OQmX7d7H2u6/ZW1oDDgiL78n4ydN9/uAppXwTGxtLcXHDqwxFRUUNGiGlpqaSmprKRRddxH//+18WLlzIdddd12jNIzU1lZ07dza77dLSUs4880x++9vfNrucd0td99kTdxJbuHAhZ555JkuXLuW9997jscce46233mLAgAE4HA5cLleddXn3BjFu3DgWL17M0qVL+frrr7nwwgu58cYbufTSS5uNxx+07XEHVVNewpoP32XTvhLCw0LolpDAoNFpDByoYzupjmvy6Ian9bwdN74fx43v1+T8mekDmJne9CmoU6amcsrU1FbHlZqa2uDaUnFxMdnZ2QwaNKjJ1w0ZYg0GWlpa2ng8p5zCCy+8wHXXXddgVOuioiLi4uIYMWIEK1asIDW19XF7czqdOJ1OfvOb33D66afz+eefc9lll9GjRw8OHDjgWS4/P7/ONEBCQgLnnHMO55xzDs899xz/+c9/NEGppm378gO27DsE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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "system.t_end = 2250\n", - "results = run_simulation(system, update_func_quad)\n", - "plot_results(census, un, results, 'World population projection')\n", - "savefig('figs/chap04-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The population in the model converges on the equilibrium population, `-alpha/beta`" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "13.856665141368708" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results[system.t_end]" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "13.88888888888889" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "-system.alpha / system.beta" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** What happens if we start with an initial population above the carrying capacity, like 20 billion? Run the model with initial populations between 1 and 20 billion, and plot the results on the same axes." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "p0_array = linspace(1, 25, 11)\n", - "\n", - "for system.p_0 in p0_array:\n", - " results = run_simulation(system, update_func_quad)\n", - " plot(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Comparing projections" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can compare the projection from our model with projections produced by people who know what they are doing." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
United States Census Bureau (2015)[28]Population Reference Bureau (1973-2015)[15]United Nations Department of Economic and Social Affairs (2015)[16]
Year
20167.334772e+09NaN7.432663e+09
20177.412779e+09NaNNaN
20187.490428e+09NaNNaN
20197.567403e+09NaNNaN
20207.643402e+09NaN7.758157e+09
\n", - "
" - ], - "text/plain": [ - " United States Census Bureau (2015)[28] \\\n", - "Year \n", - "2016 7.334772e+09 \n", - "2017 7.412779e+09 \n", - "2018 7.490428e+09 \n", - "2019 7.567403e+09 \n", - "2020 7.643402e+09 \n", - "\n", - " Population Reference Bureau (1973-2015)[15] \\\n", - "Year \n", - "2016 NaN \n", - "2017 NaN \n", - "2018 NaN \n", - "2019 NaN \n", - "2020 NaN \n", - "\n", - " United Nations Department of Economic and Social Affairs (2015)[16] \n", - "Year \n", - "2016 7.432663e+09 \n", - "2017 NaN \n", - "2018 NaN \n", - "2019 NaN \n", - "2020 7.758157e+09 " - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "table3 = tables[3]\n", - "table3.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`NaN` is a special value that represents missing data, in this case because some agencies did not publish projections for some years." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "table3.columns = ['census', 'prb', 'un']" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This function plots projections from the UN DESA and U.S. Census. It uses `dropna` to remove the `NaN` values from each series before plotting it." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def plot_projections(table):\n", - " \"\"\"Plot world population projections.\n", - " \n", - " table: DataFrame with columns 'un' and 'census'\n", - " \"\"\"\n", - " census_proj = table.census / 1e9\n", - " un_proj = table.un / 1e9\n", - " \n", - " plot(census_proj.dropna(), 'b:', label='US Census')\n", - " plot(un_proj.dropna(), 'g--', label='UN DESA')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Run the model until 2100, which is as far as the other projections go." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
t_01950.000000
t_end2100.000000
p_02.557629
alpha0.025000
beta-0.001800
\n", - "
" - ], - "text/plain": [ - "t_0 1950.000000\n", - "t_end 2100.000000\n", - "p_0 2.557629\n", - "alpha 0.025000\n", - "beta -0.001800\n", - "dtype: float64" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = System(t_0=t_0, \n", - " t_end=2100,\n", - " p_0=p_0,\n", - " alpha=0.025,\n", - " beta=-0.0018)" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap04-fig02.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "results = run_simulation(system, update_func_quad)\n", - "\n", - "plot_results(census, un, results, 'World population projections')\n", - "plot_projections(table3)\n", - "savefig('figs/chap04-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "People who know what they are doing expect the growth rate to decline more sharply than our model projects." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Optional exercise:** The net growth rate of world population has been declining for several decades. That observation suggests one more way to generate projections, by extrapolating observed changes in growth rate.\n", - "\n", - "The `modsim` library provides a function, `compute_rel_diff`, that computes relative differences of the elements in a sequence. It is a wrapper for the NumPy function `ediff1d`:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[0;32mdef\u001b[0m \u001b[0mcompute_rel_diff\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mseq\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mxs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0masarray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mseq\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mdiff\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mediff1d\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mxs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnan\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdiff\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mseq\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "%psource compute_rel_diff" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we can use it to compute the relative differences in the `census` and `un` estimates:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "alpha_census = compute_rel_diff(census)\n", - "plot(alpha_census)\n", - "\n", - "alpha_un = compute_rel_diff(un)\n", - "plot(alpha_un)\n", - "\n", - "decorate(xlabel='Year', label='Net growth rate')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Other than a bump around 1990, net growth rate has been declining roughly linearly since 1965. As an exercise, you can use this data to make a projection of world population until 2100.\n", - "\n", - "1. Define a function, `alpha_func`, that takes `t` as a parameter and returns an estimate of the net growth rate at time `t`, based on a linear function `alpha = intercept + slope * t`. Choose values of `slope` and `intercept` to fit the observed net growth rates since 1965.\n", - "\n", - "2. Call your function with a range of `ts` from 1960 to 2020 and plot the results.\n", - "\n", - "3. Create a `System` object that includes `alpha_func` as a system variable.\n", - "\n", - "4. Define an update function that uses `alpha_func` to compute the net growth rate at the given time `t`.\n", - "\n", - "5. Test your update function with `t_0 = 1960` and `p_0 = census[t_0]`.\n", - "\n", - "6. Run a simulation from 1960 to 2100 with your update function, and plot the results.\n", - "\n", - "7. Compare your projections with those from the US Census and UN." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def alpha_func(t):\n", - " intercept = 0.02\n", - " slope = -0.00021\n", - " return intercept + slope * (t - 1970)" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "ts = linrange(1960, 2020)\n", - "alpha_model = TimeSeries(alpha_func(ts), ts)\n", - "plot(alpha_model, color='gray', label='model')\n", - "plot(alpha_census)\n", - "plot(alpha_un)\n", - "\n", - "decorate(xlabel='Year', label='Net growth rate')" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "3.043001508" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "t_0 = 1960\n", - "t_end = 2100\n", - "p_0 = census[t_0]" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
t_01960
t_end2100
p_03.043
alpha_func<function alpha_func at 0x7fecd5c80d90>
\n", - "
" - ], - "text/plain": [ - "t_0 1960\n", - "t_end 2100\n", - "p_0 3.043\n", - "alpha_func \n", - "dtype: object" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "system = System(t_0=t_0, \n", - " t_end=t_end,\n", - " p_0=p_0,\n", - " alpha_func=alpha_func)" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def update_func_alpha(pop, t, system):\n", - " \"\"\"Update population based on a quadratic model.\n", - " \n", - " pop: current population in billions\n", - " t: what year it is\n", - " system: system object with model parameters\n", - " \"\"\"\n", - " net_growth = system.alpha_func(t) * pop\n", - " return pop + net_growth" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "3.1102518413268" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "update_func_alpha(p_0, t_0, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "results = run_simulation(system, update_func_alpha);" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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667z7f+b3/yGu+++m8zMTB588EHuueceXnvtNQC+//3vs27dOh599FHsdjt79uzx+3W9+OKLHDx4kOeff57o6GhKS0sJDJzY7eSKMpye8US9e516NA+7qvcPeYzJaCIhLI6ksHiSwuIxB5j7bDcajdNinrrhalC/BH4MvCGldA2y/VUAIUQWsBm4AXh4xCMcRTsKatlxqBaAFXOSWDG370KHW/dXs6+oAYAzFqSwWCT02f7h7koKSpsAWL80jblZsX22/3N7BUcqWwA4b2U6uTOjRyz2Cy64gPfee48vfelLzJgxg6VLl3LOOedw3nnnDXlMRUUFX/7yl4c978svv8yaNWu4/vrrAUhPT+fWW2/l6aef7pOgrrrqKjZs2ADATTfdxJVXXondbic4OJhjx46xceNG0tLSAJg1a5bfr6u2tpa5c+eyYMECAN85FGUysTvtVFvrqGo7RrW1lvNyziLOEuPbHhYYSmRwBG12veUixBxMUlgCSeF6QooNicZonA4paHjDJaj5UsoTjuKSUpYCNwsh1KCUMWQymXjqqaeoqKhg27Zt7Nixg9tvv50LL7yQX/7yl6d83qKiIj744AMWL17sK3O7B46Z6GnOA4iPjwegubmZlJQUrr76aq6//nrOOOMMzjjjDC688EKio/1LzpdeeinXXnsthYWFrF27lvPPP9/vGqKijBeP5qHe1khFWzVVbcdo6mzps726vbZPggJYkjIPTdNICosnPChMjesbxJAJyp/k1G9/7fTDUQBCQ0Ox2QaOZ7BarYSFhfUpS09PJz09nSuvvJK//e1vbN68mdtuu23Qmkd6ejplZWXDXruzs5NLLrmEb3/728PuZzId/9Xp+cPqSWKbN2/mkksu4aOPPuKtt97iqaee4vXXXyctLQ2DweBr8ujRezaIBQsWsGXLFj766CM+/fRTvvrVr3LHHXfwjW98Y9h4FGWsOd1OqtprqWit4mhrNXaXY8h9j1nrWZjU94PWrNjM0Q5x0vO7F58QIhvYACTSb5kOKeXPRziuMbFi7sBmvd7WLkxl7cLUIbevX5rG+qVDN0F9YVU6X1iVftJxpaenc+jQoT5lNpuNiooKMjIyhjwuKysL0JPMoPF84Qu88MIL3HbbbcTG9m2OtFqthIeHk5eXx44dO0hPP/m4e8vNzSU3N5cbbriBiy66iPfff59rr72W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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot_results(census, un, results, 'World population projections')\n", - "plot_projections(table3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Related viewing:** You might be interested in this [video by Hans Rosling about the demographic changes we expect in this century](https://www.youtube.com/watch?v=ezVk1ahRF78)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap09soln.ipynb b/code/soln/chap09soln.ipynb deleted file mode 100644 index 00e0f78f9..000000000 --- a/code/soln/chap09soln.ipynb +++ /dev/null @@ -1,1156 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 9\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import everything from SymPy.\n", - "from sympy import *\n", - "\n", - "# Set up Jupyter notebook to display math.\n", - "init_printing() " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following displays SymPy expressions and provides the option of showing results in LaTeX format." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from sympy.printing import latex\n", - "\n", - "def show(expr, show_latex=False):\n", - " \"\"\"Display a SymPy expression.\n", - " \n", - " expr: SymPy expression\n", - " show_latex: boolean\n", - " \"\"\"\n", - " if show_latex:\n", - " print(latex(expr))\n", - " return expr" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Analysis with SymPy" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a symbol for time." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAAcAAAANBAMAAACX52mGAAAAKlBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADmU0mKAAAADXRSTlMAEN0iVJnNiUSru3YyqpcZkgAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAEFJREFUCB1jYGBUZmBgYLkAJFgTgARTAQND59qVGxgYfIE8hjAQoQ7EjDeABMcFBgEG9gSWAwysE6QZGJhXb2AAACNICiO70y/lAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$t$$" - ], - "text/plain": [ - "t" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t = symbols('t')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you combine symbols and numbers, you get symbolic expressions." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAACoAAAAQBAMAAACSDPCjAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEN0iVJnNiUSru3Yy72Yb8tN+AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAgklEQVQYGWNgwASJQCFGZTRxq49AAZYPEFFGAwgtFQkSZU2A8Fg2QGgGbpAoUwEW0c71KyGKUNX6QzWiioZhFVXHJsr4AyTKnpaWdC0tLQDEBrmB4wODAIjNgGIuewLLAUxR1gnSYEFUtcyrIc6Fi7LO/TQPog5Ews1FCAFZHA8QXABlRyV9n9FN0gAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$t + 1$$" - ], - "text/plain": [ - "t + 1" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "expr = t + 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result is an `Add` object, which just represents the sum without trying to compute it." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "sympy.core.add.Add" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(expr)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`subs` can be used to replace a symbol with a number, which allows the addition to proceed." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAOBAMAAADkjZCYAAAALVBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAOrOgAAAADnRSTlMAIom7VJlmdt1E7xDNMpCRWcAAAAAJcEhZcwAADsQAAA7EAZUrDhsAAABSSURBVAgdY2AQUjJhYGAMYPBLYGB/wsDXwMC5kmHfAQYgAIoAwVEg5tUIADEZtC6ASK7VDIwCDMxAxa8ZmJ8xcBkwcDxkYEtg8CtgYJgaaskAAFKHDvy4QzOnAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$3$$" - ], - "text/plain": [ - "3" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "expr.subs(t, 2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`f` is a special class of symbol that represents a function." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "f" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "f = Function('f')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The type of `f` is `UndefinedFunction`" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "sympy.core.function.UndefinedFunction" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(f)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "SymPy understands that `f(t)` means `f` evaluated at `t`, but it doesn't try to evaluate it yet." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAVBAMAAADV4/HZAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEJl2IquJVETdZu8yu83OyatpAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAA70lEQVQYGXWQv0tCcRTFP0/lpYbPaNLFJGoNKXB+syAGgUsK4tYiLg5BYGtTDdUWiO0i6B/g3JJD7W011uYidO7F1Qfnxz2H7/1+ebDte7Miu/B6/zQmtXTbcm4O+0R7UIyJLAhWBajKDCHZke78iubCicqpNHyAtMCz8K3Nl09lwjLp9voCugrzFciNNkcnChoLyPQh8aXhWtBydm8VdmTehSvBggPBg0+pHbkngDsNM8GW9rgBLQ1WCsJzOA503aHe9KfAHlY/k/lRuZTyaASpKYNoZG5spLbCS9FdLnYpweuRu80P+vDBqWacjfkHTUosPNDmWPAAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$f{\\left (t \\right )}$$" - ], - "text/plain": [ - "f(t)" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "f(t)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`diff` returns a `Derivative` object that represents the time derivative of `f`" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAADYAAAArBAMAAADBI2arAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMARLvvmVQQid3NIqt2MmaorGxOAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAByUlEQVQ4EXWRMUhbURSG//eS3GdSiWJB3BSEQrsYSgdxkAwuVsGMpVD6wKXFIaFUULDwtoIuKdRN5OEqSJBClwgRBJcWOndoMnRQXLSQuCjtf+6tvsd7uQfOPf853819N/cHJEae6dJ/6fYfy9Tp2ZkX2Fm+bGP7G+2KhTkzmPItbLmDeQvCko+PNvYU6srC1DVyl6o/VF249Zf9GXZw1qhZ2Mrpz+ZwnP2SpvgjPqJWi8+BwoSeHiXYg1UakveBNyHyCVYd5eGnHL4GBhN34AiYYfKhVEmaKNYonYCLPNQ2M4qL6S+AV4Zz+KcJvI+AqBum2wEfiuKcGUXhlnqgAmTrFC8iQKW3Z4bJa+za+HsXl/xUwJGwKpMsHm6DnZw5DhozFkfItNjKXdbxKnkXfZTHHy8o7prjxijGfWonAPa+UXxiRvFOy00zKJSkOpNlKbnGrhT81iu8lq5DFTFl4FGgOzfU5a1ewe/QgeKJGf739olhfH6ach8Hooqh6T8bU0wTXwtfv3eNKfGp0Q9Dt2deOcXUrDiiTUmxbE8c0aakWGZCHNEvmWLVGv+br01Jswoe50JtSorlW8Utz5iSYqr5Yf/YmJJk/wCtwWawhZ8YAAAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$\\frac{d}{d t} f{\\left (t \\right )}$$" - ], - "text/plain": [ - "d \n", - "──(f(t))\n", - "dt " - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "dfdt = diff(f(t), t)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "sympy.core.function.Derivative" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(dfdt)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We need a symbol for `alpha`" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAA0AAAAJBAMAAAAbVLtZAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAiZl2RCLdEO9Uu81mqzKufkATAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAATElEQVQIHWNgYBAycmAAgjAH9lbWCQwMjQwMi5gLGJi+MzBoMzIwcCxgYJAQZ2CoF2BgkAdK8x8A0kDlTBcYePT5ChgYBM8cYBJmAAB/Jgs3CDnD2gAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$\\alpha$$" - ], - "text/plain": [ - "α" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "alpha = symbols('alpha')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can write the differential equation for proportional growth." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIMAAAArBAMAAACp/CbWAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMARLvvmVQQid3NIqt2MmaorGxOAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAC3UlEQVRIDaVVTWgTURD+kjS7zdZuA4IIHhoQBK1o9aKISARBbAvmKIg0Ug8VhARRsPhDFESxlwgqeJGl4knQokIvFSIUvSj05FFz6MVfUkG9+PfNvKTd7dbLvoE3b2a+t9/bN7x5A4ikNhZ1tlF9JZuv9dv+sjXFOWsG3LKk8GbefLekWBukf9hROPvQVbej6PqB7qodRaaASt6OolLFKMpWHJUSNmcDK4pcw590rRjgzF58+CJM8U4cfz4cWsXubUrwSww5MRfAK2j4eQxcCjgjQ8CUuj2xnWZGS8iVgfEAuaUvYkbPWd7mO4BzA6ivQJ3F48ArBkeBNdUV4LJbWZdXONsyq5cRQGLYw3GIWwyGkYjNDZAJoJd6LIIAbo0vGAeucdzmWF3kaVjPIZc6V4qs8Z9OFuEWkXrybRY4E8HEcUf2y/Rp9zNFN2wfaMjyiPQ1gHSzfaAPCk3cFdH3xLuMseABo784tnHIgfw6VUgez/Pv+GdauUdCgJqZKtLFl4D3m+5VDmYM2RpVSDRPeXNIvA8Bah4uk7va/scdDEnGelv425EWfclThhRauTGKnczGrjJVjctI4SxyJkVEBujJQfrhmJyT9brIJAHnJ7+WFzI9TcWDpFos8FSNTkj20k43gQkcA0w6Q+gfUsiemQYV0+nWs/Pw66EV7T9zuceww1UHIhidYWCh9bZ9zPNM+vQpqmJkWUpeYfmzqdc0bkYwOu6lrzjY4DHLdHi1vK10cnk6y+IWxGbtiHiDZo7r0xLqDgxwX6dOUz6aa4q/oEG4DTOv1NnpexLyiwa4YCY2ZVY3royrm6ZJOak6rro31TS4RbXTziYPJ5fq85BGvYJOZomaEeXPBeqbJ6e9n9wouasdeSSGH3Tc/8xeU4CPbZRFJHc1sUhTNtWdmEKbsj5XSSlMU7bqy6YpW/Vl05St+rJpylrdSXNhmrJWd1IK05S1upNSmKas1Z2Q4h/fi6vrDhK77gAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$\\frac{d}{d t} f{\\left (t \\right )} = \\alpha f{\\left (t \\right )}$$" - ], - "text/plain": [ - "d \n", - "──(f(t)) = α⋅f(t)\n", - "dt " - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "eq1 = Eq(dfdt, alpha*f(t))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And use `dsolve` to solve it. The result is the general solution." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAHIAAAAYBAMAAADUnLRyAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEJl2IquJVETdZu8yu83OyatpAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACE0lEQVQ4EZ1TT0gUURz+ZnebGbd1GyRCIVMshejfktDBSAYPHopYUfKQBaOevNgSdAgC9xodMtCgQzrkrYNIRR0bOnZpD3kXL+6pFLREEPp+7+nM7E562B+8974/73v73pu3QGOVDxrLAR1Oo8mnjQQnJ+AMLFwOo98FZYOQHwmayicKHjaU33LdRaai4IP/BIxb9+5nvNC4gmy1jG3FR2dKyPPEbS7y4YQI3PRhXfJD/gX2B9grwo3dVuAGwQyQ9jjWVvsr8mtuKG4jt4X00GkK1ia7T2xXuYpaK5wl4Dd3gxeRVIW56aR4VMCcBWw2vGbTJyc4rNRfQd8OKTDQ5/eU7JGA9zk23wmzE/bD/RFgIpqiUVHWxFlNzvRe0ED3pwpAs3+w62WlTf+SWhf8iK5UrsKdzWFQM90XA6CpBKTWyBNvY5EuK9c9C9wpGdEDoMgrxcky0x7BD7aa+qqYA4vJsaU+J25OkUiygy2ZXKQIK1DJHcGxWiWW3b6EATxXxtQbqXnB6kumGOZv/lFm1H0klBuaxjNA31BkouiRjOvkZz6VmGXskphDQLdRALpijoLWHvDY0cl+GPGvYm9xhryEu70E1fokWlaHyxRlt+nz7+O2WRE2p6XMStyKY0nW1pO8L8I7rZoFPSZ7K7Hm2zY1q9lVw7lkRCuZizv1iy7pMx/8s38elTxGvy1e1j1mRp31D4aaaFK01Y1LAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$f{\\left (t \\right )} = C_{1} e^{\\alpha t}$$" - ], - "text/plain": [ - " α⋅t\n", - "f(t) = C₁⋅ℯ " - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solution_eq = dsolve(eq1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can tell it's a general solution because it contains an unspecified constant, `C1`.\n", - "\n", - "In this example, finding the particular solution is easy: we just replace `C1` with `p_0`" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "C1, p_0 = symbols('C1 p_0')" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAG4AAAAYBAMAAADpDtUUAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEJl2IquJVETdZu8yu83OyatpAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACDklEQVQ4EZ1TP2gTYRT/fU365ZIml0OLKApNaelU5UjRoYOEDg52MCAUpEswU5dyS4dOvaVd7dCKQluCFQd1sK3o4nDg5hTQzh5CsZNcINoOIr733fXypXCB5gfvvd/7R9733gXoB6bXTxcwYvXXt3zxtvpjWDM7k3HjF8Xexn4CybqDdg0/VfZSuYJ0U9FBL6H+LHwTuWMXbeXOrTgw6aViA/DPChLsRxj7MN5xVpxeBe4QyQShTWhR4TbyLaSqw1E53hMZ8IHrKpusjiEDa4CeCMh1wCBBljzTIdIDM9ONCcd46AG5+c0SZAlY/X5oK9LVtvbt820rjFyZGtdTRRsoNCiyQpLzSWHxF+MH09ojRzZT9RrNtIF7FIjxwKMRebxbJJn1OK5I2jlCLniFJWDWEZ1zU5J/Zsgl9ZQkH5DSIHAC2dpC0cL87rSlZbBADveJUyLn+2C0UNhrY8jBb72J+CEJz2kEsMLFYuE5Y5MLadsjky1kG/jDroYD4rwX6We8aC9a1izhzQ3qs/EBSGkJNZ6s0tmrl0mVtBTTYjP/KU9z2rgLod+BHhDePf3Vpru7XKzhyVq5Iv6i6CI19lqLQzbZo09aYTayseGt4RnvsxtLZoMDL6JoPbKxUccv8/26sX1N+YWKMsJXpqPECXM5MdoJhWw3fGv0v43aO0XL/+jRvXBfJV/2KtFz/wHKAnGYvLBmjAAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$f{\\left (t \\right )} = p_{0} e^{\\alpha t}$$" - ], - "text/plain": [ - " α⋅t\n", - "f(t) = p₀⋅ℯ " - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "particular = solution_eq.subs(C1, p_0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the next example, we have to work a little harder to find the particular solution." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Solving the quadratic growth equation \n", - "\n", - "We'll use the (r, K) parameterization, so we'll need two more symbols:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "r, K = symbols('r K')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can write the differential equation." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/latex": [ - "$$\\frac{d}{d t} f{\\left (t \\right )} = r \\left(1 - \\frac{1}{K} f{\\left (t \\right )}\\right) f{\\left (t \\right )}$$" - ], - "text/plain": [ - "d ⎛ f(t)⎞ \n", - "──(f(t)) = r⋅⎜1 - ────⎟⋅f(t)\n", - "dt ⎝ K ⎠ " - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "eq2 = Eq(diff(f(t), t), r * f(t) * (1 - f(t)/K))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And solve it." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAK4AAAAwBAMAAAB6TlzuAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEJl2IquJVETdZu8yu83OyatpAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAD8klEQVRYCa2WTYhTVxTH/y+TeXlJxiTUD1pkNOJYaOlI/Bhpq7avoi6UOvGDLjrUTlEXs3EC4kJEJi50U2RcjKILNTguiigMKq5EHxS66MaUVgpuJh0QwY9MOjqaFsfxnPPy3ssXeJPJgXvvOfec88vNzb03B2hKru/6aS0nGuN/IzITa4pRJ2lHBp1jMr8e2GKSZjhREcvRGh87ikAoK3k/IJBgZZ5DWTqHxYf7gQ7B4SY+F6LLPeJ8gNrYM/sohh/fWhL9Na1Jk3Vpxc6MTDE39Ononk0XPxNbuXtLkcdMO/yCPewEAsWDWTGEO5w+h8e2T7U3XtF6UqXoKRlP3gL0/403YjD3m43UvRRTudOL0PY70WdJ2QaMAJE4zpCxN/80n89gN52LMSdIbYwU8Jsb2U3aMuFGUxgyeZ7XiztAW3IBa8oSzYXiErxoTReCGSyyhHvfRFjmmesvAL5EP2nqMtw/eJqjjRFsBQ58vIr0EeiXunBUdpu5Rpbabos0dRlc53vN0dtTmnuQaH9dkX1wLXVlMuPnk4a+0S9jpaxfp1Jevs9TG9JmgHGLMqYbynpvsPECGE5SGB3jVopOpz/4LxFv01FqIThYIB6v9StoXQ1xf5foa/VzFk5OJ/DF7F8EX361fkid2Q9Wm/BnxdFu1fE3O/XdUAqRGD19dCBzzUJq87Tih8A6mg/QJvLYImEcblHz5YDFLYISRj9N15oagv308KVaBQ71nYlDjwMnxh8kRGkROZqg5zNDsCFqoRx1OJhnmWC1eem1aAv466+kFuANaY3wMsNp6s5S6yhQ1xoZIAxztSIpNdzobFNCqAfUeB+MAmL2wcDAeRb+R5yD3KRc/t30XMAq/W5zoLmp8vX1JF2L5Hzq4q6jUnEqSJyaSWHyj0pnPcvgZ5Xvhf/PBN2LdL0YwKsg28bg38NBbuSzuil6lqfpyRHZXhqrhrIKsj2LQ+KVPNI0XliNHI5keO5yybGvNFYNZRVkONGWFq/DrT3y/5D/wkcSNM+UQcvJUNN5FSSWHn9ou5l7+WrP5k8mrMr4b/kLjNp/K6V3vYSvjCPLqyAxdPK57WbuL8Vguvqp6lxRvjHbJPiKnVLTexUkBjPDlviJ659PXdSsCm8r51b5qky+4vTJ/OtOIJgE2vP5u/knaE8AP1fFQpHLlV435doVJF4j8FJItFSELaBHM8V2OzWuVHpeBdlB0BuCYG4vteW6S7QVNa5d6TkVJAamzMC9OAOYe4raiQ1slYkat6zSc2+PQJjryRJ5quTqqXHLKr2KChJxD1qpqXFfVSYpWGrc2w1Xemrchis9xfPbSKUnm+Tr++97hd1qLuQdInUkJQI5nK4AAAAASUVORK5CYII=\n", - "text/latex": [ - "$$f{\\left (t \\right )} = \\frac{K e^{C_{1} K + r t}}{e^{C_{1} K + r t} - 1}$$" - ], - "text/plain": [ - " C₁⋅K + r⋅t \n", - " K⋅ℯ \n", - "f(t) = ───────────────\n", - " C₁⋅K + r⋅t \n", - " ℯ - 1" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solution_eq = dsolve(eq2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result, `solution_eq`, contains `rhs`, which is the right-hand side of the solution." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAG0AAAAwBAMAAADpxulMAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAdqvNEDJUuyJEiWaZ3e/xv6KKAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAClElEQVRIDe2VTWsTURSG3yTTNDNNOrEgIhbbUEFB0cCom/oxBaEiBesvaP6AGlwUVzqIoAuhwY0rzRRxayOIInYRBUUUUexKRKKILnSjVqm1lnjOmcw4SW5JIuKqB2bue849z507Nx8voIqeE49HHJ7QT08itmCzbCMSw9BOeX0bgTMOyYKP7fKFYpygrttefTPiaVb5Wpv2rSZUw0cqHvEm3mK9CJ+LP68DIjvmNyFa9TYR4TUPyLy2OFoKuNm7feef7hmvA80sED/plVJ5GaM2lRavegnf7yymCrEciVAUXeBWLTfLLPTrDmAs6d854fdLjNDNpGI4Zgo45JdSLjWVkKI8VsYLIGpZ76xhRNPAlTBEej+6XBokkrRu0hbOzOG4w0UqoXcc6NMcUn9iHrOSaNP9Dl5BuwbhJhz0lnmCuSm67hmcBaH/NJYluewkXBjbpm3hjMojHPvFE8xdouviWs6CML64z3KUJT5gjHYjwe8XBHOKiC1hKkv16Kc1N2vTXQMPQ43lkA5JM49uPnBzMFRsQxbT0L8y57bRHGqZoZeq2HTUxKVD9VayQg0DxESyGHNaNQfz2lA1g0hlgYj+DaWgvCpWT6DpBKp/FZ+b1vlPhX/nQLq/4xj9NlcKhQN1+72HbV81jwoHCrijTe07/YrKgZjreXJjy4W5B35bbTwXeKDKgYQrFl5idwM2ej/gVA7E3Nl1dPvRwEF2J8UmB8KQtc+ySsiQn71Rc+wkKgeSc3lPf3GDBxtA73niJAoHYi5B38Nk2lVxnpMoHIg5PU9XpvFzl+eFnEQcL1hb9hlk2t7XFHOyhHAhJ6l3oGTANAqPcxvLLXPhOnUSWlW4zpxE9iJc506S3L68teW7rNTwG0T+5g1w9TGBAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$\\frac{K e^{C_{1} K + r t}}{e^{C_{1} K + r t} - 1}$$" - ], - "text/plain": [ - " C₁⋅K + r⋅t \n", - " K⋅ℯ \n", - "───────────────\n", - " C₁⋅K + r⋅t \n", - "ℯ - 1" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "general = solution_eq.rhs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can evaluate the right-hand side at $t=0$" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAFIAAAAwBAMAAABwPjD5AAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAdqvNEDJUuyJEiWaZ3e/xv6KKAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACZklEQVQ4Ee2VS2gTURSG/zSTMHlMkqoLtdVmIShYahHBhUpGEQKlYLp107hyZ4OICIIddCEoSBeCUJCkPsCNZgShCzdRfISuRBcWRawbERWsrTZGI/Hcc2eGmXqNbsSNB3LPOf//5dzhkjsBVDHdf2U16xcaReSOqBCpnbKRn+MyNAd9kxRVa6QJJErsdJVwQIU4WqoCRDLcpDKhiSC5or3VxJbFAqt9JqDRh2Lf+eOcfcsi1QctKTyVaQOlscntsvHW2Fd6tKLTtjhPvqFUtctyH8cC4k1o19zuGRVngVlKO2FkXVnm8Dz2e8pDqu5JcgnRL57ORXowUeNiqLsOw8YQ7UkzI4S9D5LlSvWRUGKzOANcmllFNZHXW1b0Q00YXlTXJpdEc66o3XdFIhWRs3VxThi42WM69kir6FSB1ADGxXEsBFRFE/sIlLNk0LF2jvhnwPhEzFsg1BE15okQ81ZCq3ci9+QWMljXPkb4ndudwP/ePzmB9p/G33y6xOGZ3ZbYIDZ+FOGGKUpl6DugnZDOeuCkpYRYHJ0ApqW9EdHMr0HxW8eI9F9hzc/gNlcKiZuxlzutmbdd2cunhc9hlDh1mUC0eVk2rMglf9cj0zUhxW5Y9C77FqPrtyx4T9aMCqDbMCwgXMPjZRxdL56pTfVaSdowaTKZLmKMvhAMSV609AqeQ7tKV56QUQupWpBzZuqvMVxAvH/KZDL+so5D8qXsw3lm17vuW64mZnqh7XpB8aQgBCbTWc+L9D3w6mAhyUpQVHZMir+pjNL2iUyGBjFs+URlySR6e2yl6xOTA983+9rflT8Adki4u0VauJwAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$\\frac{K e^{C_{1} K}}{e^{C_{1} K} - 1}$$" - ], - "text/plain": [ - " C₁⋅K \n", - " K⋅ℯ \n", - "─────────\n", - " C₁⋅K \n", - "ℯ - 1" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "at_0 = general.subs(t, 0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we want to find the value of `C1` that makes `f(0) = p_0`.\n", - "\n", - "So we'll create the equation `at_0 = p_0` and solve for `C1`. Because this is just an algebraic identity, not a differential equation, we use `solve`, not `dsolve`.\n", - "\n", - "The result from `solve` is a list of solutions. In this case, [we have reason to expect only one solution](https://en.wikipedia.org/wiki/Picard%E2%80%93Lindel%C3%B6f_theorem), but we still get a list, so we have to use the bracket operator, `[0]`, to select the first one." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(list, 1)" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solutions = solve(Eq(at_0, p_0), C1)\n", - "type(solutions), len(solutions)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAKcAAAAyBAMAAADLk9YvAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAzRAiu5mrdu/dZjJURIkjnMqiAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADvUlEQVRYCZ1XS2gTQRj+k2zabt4I1oNgV5SiF5OD4EHBgAd7EAwIUhTs6sXHweRk8RKL+Lgo5CLiA63gvQte9GJy8NAeJOvZwwZsLeIhqdaKjxpnZndmd2b2RQeS+f7v//4v+5rZPwCRo2BGSphgkqEIsBKR96bzJTfa5kIJFXSJCiFesNzBbwzKYFGTuWBG6Tm5vadDTJP3gx18Msl7lBwNMVVqVBVvXqSyMNOuRlXxZrXn6MJMHwR45ZcPz17wyeW+R5tmqEasL4w8hWVoNkUe6FGEHKlalaps4khCh4VML6eL+QXNZkJM20HLSSt2oL/fAOkelxuRptPigbDY0mCqXAH2CNFEwllUIUd6h2ql+Qskf7Q1eCwmRp27EGya/S3WsHg7ZG4i04eMcEDqpw2CTenPipUoXofXRrsCd6XULcKkL/26IqVsQpkLSEB+c/UUlA3YlARTPJM93+MJGKsKBAsLLQRVU7770K8wEQH7dT6Gck0gWJjAmdGa/JzCgslEBCg6H4PVEAgWljsYrqxoeOJGW6iRTLsdTu8Guec73IBH4tlJpjcMviBOVNR5FTJNrjZNyF89M1vFqZkKL4gTFau8Cpm+qcBxsDR1nlyZrZiOlSTTZwBt4zKMtEimrvGCONFYC4ZsoAJFz/4FKPammSlx2f0Ij53gEbMqDgywXJ0jRexL0XNohRSrR419HULWWSo+kE3JkdYOrX61TWa0+GZUqdpXjobo9OE2gGW+p4y45CgfNqNryg2lirYe2AUfPr6y+a08p8Ldz9Y3Kqlzn01IDIfoiNEQ17H9U+Hf5Dm9NhzXoL5meKQnYM9SA8dd08Ni6CMWFM4mtIbodxVPLlcFyKAPurzEGiM6JDFNsJlsKPl/yKLDOASST9AjSxhxcwBZ7C0kmGxChU1IfuJTB1abZwkjXHQAHzFfCvYmnRjAWzFBY2VAkTOHiR0JWTDFUqYmlLJQeiX6iYW2irz4rF6/xVwEgBcYN/zEfFtlv6L7zbRY6vqIzYSfmG+r7Lf6xHwKPyf+Y0agXTHbuoBvqxRy2usAJw2hloWWySABvmLL21YVG0iYR22KpfOlbqTWXIyQv5hrq0grWfiD9lWpIaRWGT7jL+baqnFcqg5QU4AWVcCwNxaa9Bd726o8bvomJzYacH14jJaJc1fzMP5irq2K9W+GtDceXx/ItVWL3oPwERMqhS55xPC2VTH/yy1FWAKUO1hit1Wxzh4greOSkMG1VS9DhN7URW8QgfOlCAFNp02KoudJDWv+Awx9MnnyNIhSAAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$\\frac{1}{K} \\log{\\left (- \\frac{p_{0}}{K - p_{0}} \\right )}$$" - ], - "text/plain": [ - " ⎛ -p₀ ⎞\n", - "log⎜──────⎟\n", - " ⎝K - p₀⎠\n", - "───────────\n", - " K " - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "value_of_C1 = solutions[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now in the general solution, we want to replace `C1` with the value of `C1` we just figured out." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/latex": [ - "$$- \\frac{K p_{0} e^{r t}}{\\left(K - p_{0}\\right) \\left(- \\frac{p_{0} e^{r t}}{K - p_{0}} - 1\\right)}$$" - ], - "text/plain": [ - " r⋅t \n", - " -K⋅p₀⋅ℯ \n", - "────────────────────────\n", - " ⎛ r⋅t ⎞\n", - " ⎜ p₀⋅ℯ ⎟\n", - "(K - p₀)⋅⎜- ─────── - 1⎟\n", - " ⎝ K - p₀ ⎠" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "particular = general.subs(C1, value_of_C1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result is complicated, but SymPy provides a method that tries to simplify it." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIcAAAAyCAMAAABBPjj+AAAAM1BMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADxgEwMAAAAEHRSTlMAdqvNEDJUuyJEiWaZ3e9AqUJ7gQAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAppJREFUWAntmNuCqyAMRYPghZuT///akwS11mJRBzvnobwMVkw2MQmzBLh36PZe+wetGzQHV967rIu17asGUTe8O56Uou2D96r5CVGH0tLTQlGnR7p+KD7rLVqIFnRXXHp2gUUnj/gDpgejelmMZclndTjaIYBxxyw3iu3b8ayX8nqNtKZNMSmvHiUtVAPmmO6yxXkFp4cP8xVY1w+tUpK5y48ULqUCeR5YNEBQcFT4w8b7GaeHw0cZOmgiZYqjPFBOTd5sZA10YVN62EbVLheH5CzgnKRDB7oh5R7Bk9+O5zCMdNvUr9R1hDR6DvdUu0CNRDQ5NJG3LH2zGb1S/t4OihLpHpc4W3Hdj0ZqkyMBKFFZq68+t5gK8eHKca4YDK3oiRQtmDpMdecrg24KRFwOLs2140cz6WCZY0pXUbx6tuY0pkIEteyZX1RLbaJN74WdK84ew3V7zzCNRpQOqSJiOl7oAHHSKoxESpotqKA8v6APDkkP8UfH2VQvH3S/uJL0kCtFiZL6x3LzcxOzdDTqqcGFe5vG7r66iPrOsth1/L3xjcCfRgD/j/GnMfg6/3QEiuD5GUEXMf8cQx/YymXMn/8PP8LQ+zoS5Q/DZczfZ+hTWDhT/mXM32fo7sTpvlA+XMX8fYY+o4M+0STVlzF/w9CrDNjoeE/XkCgfSpift0JUvGHofR3v6Xqi/CLmZ62Q0w1Dr2TAczwKdD1RPhQwP2uFnW4Yel/Hebo2Oi5jbrJZK+z0haH5R6d5xFH+LN9f6tB1xgp5fGVo1pHG83shdVXoOmOF3L0y9KyCAGnTP+rQdcYKuXxl6H0ddeg6YyXH0A8Z23jUoeuslZXTzHTzXurQ9QUr9vkzQh26/rWVOnT9ayt16Dpr5R/tGCGi4YRyLgAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$\\frac{K p_{0} e^{r t}}{K + p_{0} e^{r t} - p_{0}}$$" - ], - "text/plain": [ - " r⋅t \n", - " K⋅p₀⋅ℯ \n", - "────────────────\n", - " r⋅t \n", - "K + p₀⋅ℯ - p₀" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "particular = simplify(particular)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Often simplicity is in the eye of the beholder, but that's about as simple as this expression gets.\n", - "\n", - "Just to double-check, we can evaluate it at `t=0` and confirm that we get `p_0`" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAABMAAAANBAMAAAC5okgUAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMARIm7IjJ2qxDdVM1m75kH/PNjAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAg0lEQVQIHWNgEDIJS29gAAOFygVMBmxqCgwMLAu2M/B8OMSwnIGBg+EXA9OPqQz8QEWMPxiYb3xh4F3AwMA0gaH/6Q8GzgMMDNwPGM62ApkJDAz8BqxXWYEKgEx5oTwHjq8M/AIMDJoMQDARbMI+EDMPbO4vEJPp0QsGhpW/E0BsIAAAr7QhXSEGXYsAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$p_{0}$$" - ], - "text/plain": [ - "p₀" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "particular.subs(t, 0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This solution is called the [logistic function](https://en.wikipedia.org/wiki/Population_growth#Logistic_equation).\n", - "\n", - "In some places you'll see it written in a different form:\n", - "\n", - "$f(t) = \\frac{K}{1 + A e^{-rt}}$\n", - "\n", - "where $A = (K - p_0) / p_0$.\n", - "\n", - "We can use SymPy to confirm that these two forms are equivalent. First we represent the alternative version of the logistic function:" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAGkAAAAuBAMAAADZ8SrdAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAzRAiu5mrdu/dZkSJMlRpnuOYAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACJ0lEQVRIDe1UQWsTURD+dpM2bbPp9mLBg7hHDxVzFC8GSs+GHjy1qL2Ip+ZST4WKnksjgiAiCUihhyr5AYXsrXhqbwVPexK8NWmpKLZx5r19Zt5usqE9d2DfzPd9M5vH7EwANieg47KWexRctgRwF1avUAVsXFeJZl93QzTj8db7poHfTTDUf0kruQpQWO2sYLx3K60qZvwwJTgBUf4MMD2f0gxBasLWGbdD4GtCEFDlCAy8YXTQwlzZoi1wx0IEinVmbqMUsh9iTpQQvCoTHWwneGBv/uhloFmdJDLyTQLFv95vwekwfBF5lcJ+SGgq2Y7JiFjvV/iEvbRcNIup4138IHK6Dr9n7JiIiRYdzh881K9zb34ge3tI/y24gNf9BD8ASpwpTVX5FeTPJctxsYv86xNMRAOq1A3bNRTPklVeHfeWu5hsmkaLBNWNA7pRIxAsh04VO3epqjagG95TSmjQsxHSIc2vlDZLdEOqGqtKgWL6yu6z3nMUGqdlW2rvrZXdc/gt+ll6bHtnwz76xuFH1cP7fTaO0kMRCzfYr6nvtR9TfZdPXMwo7gVH3tIifboZQ/73vJWD7GenZuhBL35gxKH+MytimIcmpgU1zGk6m9HDrFcgO1Oqepj1Ckh+RMzD/EqvwIhMKfMwL+oVkPSI2Axzc0SeLath1itgC5mIhzlegcw8W+RhjlfAFjKRGma9Apl5lqiHWa+AJWQCPcxqBTLzLPEfom2MwIZHu24AAAAASUVORK5CYII=\n", - "text/latex": [ - "$$\\frac{1}{p_{0}} \\left(K - p_{0}\\right)$$" - ], - "text/plain": [ - "K - p₀\n", - "──────\n", - " p₀ " - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A = (K - p_0) / p_0" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAALcAAAA1BAMAAADxOGd/AAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAdqvNEDJUuyJEiWaZ3e/xv6KKAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADWElEQVRYCe1WTWgTQRR+yW6T7KZp0noQKtIUe/CijQY9FTrqQSlo9ib41xQPnpTe9CA2B/Wgpc3dQ1IUWrCS9qAoLRhFodiDUaFURFNBRbyYNtaaqsQ3k93t7na3zRIWFfogO2++971vJ2/eZAJQvTWVdxNoL0rVZ9hhFpF8JmEno3qusAzgj1fPt8UUS8CP2MqwQeYKcMwG3R41GPFn7WXYYKfSmZwNuj1qprn+u70MG+zOMR9tRmdsCaDPoRMEwjxAKuzMukFcBAh8c0g8UABw4SF1wvZ3LoRga/mCE9obmhsV+J8rUHbO8ET/BTvq4DtP7HBQHM7+2+Kc9aW1svLj636Hu6aMGKEwx57U05gq7uvFK6K92Abu8h5NXOu6TdfIFHx9RMuUfVWcvToYAfBYXxMY1Zl/enx7/6unAPzFdiJHDmkYqngzBVNpgHuaqMFlHA3mTyVfw14GtBAZ79HEVfHPFMwkoSuhiRrcI4b55c0I/GCgqfg5mS/kqNMJdWkcrIyLK5Ez59EItAIIcwwzE+/PY5yamKXPIkzSQWfC8JXGtgpSIXU1TimEj9gEYVZlM3GFBd4xdIVl8beKKI7onoFhaKb19tMdFWbhmhzzFQDqQ2k6W1M8EEeGOJ9+TkedXeXC8NCf9oSxkWjtBuI89gczoRff1SpRf03xhiQyuJ8wGGFpcOAtNdrxJBiHzGEJ8D9YXQHnO8e3kApH+6yI34xG30WjHTQQVH7i0WfiwV7wLmpTmJ8icCmWgE+y+MIqAgXWXDkrSyoEwurcO8D/6ibwBkuQQ5ll++JsQzMSQJ4YkzvA/wHFX8gb+gU7xEhZZ+ViGBPy+GlJ40NnS3BK6k7Ae+yMLAY2Aa+2osp7/PXlWGXSo2IrDn5jflu5FVz5pcQKSj2hNDQFMQlKuOFJnLseTegJ+pkqPjnRhIWo2KziGEcxh0ggSbtlwBgzmd9QsAelOprJbPXRlANcFh1Xlvb5kAxVM/j2hX0F/vYtxvUmLFJicRoYHSXARywoprA75JnzkoMsRi8LM/Pkdymw5fsVgm5skNzZIDQQBl7Xhcwm981AS2wwcZIMQECyJNQSON34BDvAIfFnuDK1LLWs0ix3BkFlQ83itWD1CwQv7umRWjRY7h/dcRpevXc9owAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$\\frac{K}{1 + \\frac{1}{p_{0}} \\left(K - p_{0}\\right) e^{- r t}}$$" - ], - "text/plain": [ - " K \n", - "──────────────────\n", - " -r⋅t\n", - " (K - p₀)⋅ℯ \n", - "1 + ──────────────\n", - " p₀ " - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "logistic = K / (1 + A * exp(-r*t))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To see whether two expressions are equivalent, we can check whether their difference simplifies to 0." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAOBAMAAADkjZCYAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEJmJdjLNVN0iZu+7q0QgoRR7AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAVklEQVQIHWNgEDJRZWBgSGeQmMDAtYGBOYGB5wID+0cG/gsMfN8Z5BUY+L4wzDdgYP0MJeUNQCL8Cgzs3xk4DjBwfWRg2cDAlMDA0M4gHcDAIOxylQEA9FISlFfRJtkAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$0$$" - ], - "text/plain": [ - "0" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "simplify(particular - logistic)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This test only works one way: if SymPy says the difference reduces to 0, the expressions are definitely equivalent (and not just numerically close).\n", - "\n", - "But if SymPy can't find a way to simplify the result to 0, that doesn't necessarily mean there isn't one. Testing whether two expressions are equivalent is a surprisingly hard problem; in fact, there is no algorithm that can solve it in general." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises\n", - "\n", - "**Exercise:** Solve the quadratic growth equation using the alternative parameterization\n", - "\n", - "$\\frac{df(t)}{dt} = \\alpha f(t) + \\beta f^2(t) $" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "alpha, beta = symbols('alpha beta')" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAANcAAAArBAMAAAAZAwDkAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMARLvvmVQQid3NIqt2MmaorGxOAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEIUlEQVRYCb2XbagUVRjH/7t7d+bueF0vCBGE3QUhKKPUL0X0skYUvkBLEAQZd8ygWwh3CQvtja0Iw5uwQQp+kaHsU6CXCiRSGEESIeF+kvqSCwaRZaxCty/V9f8855y7M7MbfrkzD8w5z8tvz3/mzMw5O4BYaX1T+2KaNa1idFRlKixQbF+BWvi0MLHg1MW/ChNbG5UXixLzHsVYtyixsUWMt4sSqzQwO5mPmP/4k5mBZ9uYRphJrkz4Jr7NDDTbwt3VKJNcmfAgLmcGrsX1OX9lBs+OsgknMzfIO/3Ol2eT2M8S1BeSqRH+6p4kR2Enrg7w6RAw5LVB0novnYsQNDQ4M1RcTng7tgGfmXAYm2i9KCeqED6mZ8hVQ2d/arqFWgjMRKiZwUa1q17ninOElZHYNZQarCnkN+mR9A4CXbpJ867vAs4zMw1MtJOVlD9726QpJ7FXLRL0UO3QFwi/0JGBqn0zrmW0kxwe4rGVJ7MxWUn51EAlYpPEdlpkghc1T1+gUru0oKQsUbst4Dq/Q4AHDvA4zGO0yYZ0u5SSWEJsbcSaQD89+0yopCxRtRYzA6t/PdcEp7n01Y3TwN5BwXr+ji3i/f7gN1pNY05s3fELhxz02NKSknfcvyGWgVO2JgbKPTPF+E1L+4+K6X4XvIfd0RfM/sPjPh466wYDnFj88t5H5JcCqQkpc1rnVCbt5AIwzqvVXeC5ZEX8Shvl5vdA8C+DD3mkMCfWBi6wphB7Q25lr4+NZkyjd36SgvwFLicK6j4dcnhW9II2MZXCnFgMTLGmkP5MSLm5q/tYctZnLDe1QjHdBYbENgP+AyGbDjEZYoBxsj8wk83HD1dYU4i9kt519hRL2QZGMo1T8Ozzhn0fic2x4P3NIeT/Q3mejUxjAlu+Zz7z3zmIvZKlPkLzmGvGNA+zkwdkP16AfUAS1f8oJmdXidnIbU9hdhprvF3ypirEXkm/W11Avauha/RqfZ73di8GnnBp12/nitC/ZGfvLWZTmBXjuf44yZpOsf6S5Nj8HjZNDV1TWqRX6nDp/IHOJy7tev/dP/FUzNkLmZGXOoVZsXW/nn+DJQOJI2Rwb8yXWs5hYH5DfK6ZYsFG0w+3r0lqPDKFZcyKtU0aCqnvyM81cp8Vz9d6EsviSfNj7Yaa6vwxydWbprKM3WliI2YhTTnybQPws2ImAt6f0bBMl/aKtsPN+F0dTd5jShnMizXtIA0M6XU1kPmV1/mPbRoGDe3sYOonm/q5SEO7eWYwec1oDkqQ9hrkXZb1xNkJceqRC/+nD3pSuCVGxpBXBadxmZX1pACTzwqzWRQgpp8Vg2UzV0XzWaGbRa46Orj5rNDNIn+xSkOWscFKlqui+azQzSJXHR3cfFboZpG/mPms0M0ifzHzWaGbRe5iNwFEQAR5bSP+FQAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$\\frac{d}{d t} f{\\left (t \\right )} = \\alpha f{\\left (t \\right )} + \\beta f^{2}{\\left (t \\right )}$$" - ], - "text/plain": [ - "d 2 \n", - "──(f(t)) = α⋅f(t) + β⋅f (t)\n", - "dt " - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "eq3 = Eq(diff(f(t), t), alpha*f(t) + beta*f(t)**2)" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/latex": [ - "$$f{\\left (t \\right )} = - \\frac{\\alpha e^{\\alpha \\left(C_{1} + t\\right)}}{\\beta \\left(e^{\\alpha \\left(C_{1} + t\\right)} - 1\\right)}$$" - ], - "text/plain": [ - " α⋅(C₁ + t) \n", - " -α⋅ℯ \n", - "f(t) = ───────────────────\n", - " ⎛ α⋅(C₁ + t) ⎞\n", - " β⋅⎝ℯ - 1⎠" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "solution_eq = dsolve(eq3)" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAKMAAAA5BAMAAACov4aWAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEM3dMomZdkQi71S7ZqsQsTlgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAERklEQVRYCe1XXWgcVRT+7v7MT/ZvrKRFKHZdixalEBWhBZXBl6JYsgWThYp21YbQGnQrmqASO6CiFK2DYkFBGgTFEsQBxTdlBCkoqKMgghhYfWjFB5s+WOxL9Zw7Mztz87c7wfgg/WDvOee753xz792dmbPAwDDdFVJLK3CDU8OU+vOZfU8pFYVACTMGnwOvBdg6F5YZbHI27mW7ThgdFP4CKn5YX2azzcLWMFzXWHYx1AQKnbBaSn4ElO11qRWfbAvkgaMWIOjDYMkTNx+EHl1CkoMPu3C2iDuB28KSX9jIVd4IlLohmW3UfHz/El4GLsi6F06xkZIXAcOXZMZhyMOxNo4Dt1Lh28Bu4NfGDY1GYMzR2dYzqsn0motRC/cAUxQ+KCXDVerdq2B0B5ccn3YoWbSm3ZyDAwJXAvkAw3ZKUus0UWkOLPm6I06ZHp5zzaY5uaDdhzx91Q9MHCEB2ni4SuOsjarD0UB4FzitW+Y8Ntthvh7EdT1JJnIx29dq54GHiqh+MPNdnOtHzp4LHnlaFH0a2f5myAf270Otm6S+kriJJ1IJCbuit41O/YCHGpkYJTv2Ula3UsHabi0gSci7urN25sCzWheVxwqWXsdmd+CiPoljC4HWAqZnabmXcfkE/gcnsOkWxvYN2cnf/xIWN2R1/41owUlf5810EPte7KStng5Un96vCUSTeqtvJra4CUXeXiWKgjVewNek88sWzJ0QX0Wcw3Y7ijbbpbh/KQFzQS5F66RnXgRGHWpWI84nK85DsF0GfjGreEZ8wcRJZeJLgF7A2BOl+mRLI4DMjDhpclSlt9MM+RWbeylgPOZb47AwAp1ewLgiIn3AfPg6GzNxUmTNb0nSXFzCUgdFPSgwH/F5p9ppUhuV9yVRpRqA/ZwHfMZBAnH6HE/fnjDS6wB/kFPgRTGmUNnhULNXa3Nk7HfZ+PShvivcDxMxuJ+VjVhMsO3A4NaxMscBYR7Ge9yZ5Zu0pQB5F9VG453GThyjSepgVUjJp1XOuGvyWpsorU7D8MwhXOQFF3xoPpEWSxLIx7PC5XZThZR8lZeaQBdjh20Kyz7tcjfewA4UFy0xwick6L9IIjlZBH5ICkNPSo7aCk151AnQ4XeBtzxxECdmgwkPNwHFMy26eiJ5/AngN6WWAil50lFonRa3SEy+Dfz+42y8hVmiJBJJDus8pB+2oaTHdA/PR98Mr/JSjw37fAoLRx9hss0D/dqD0CbjSqtsAtUupbDkn0mqsayY565OEiIvPEtX4et0hMxUfXkL0jmE+Dh20vZIOpC+lFzyjX+Nwi6e1OeAxyEOycTBByn5k5Jv7G2NWcwYdPfokwvKZP/g0Q/voBOaUhLpNxTh/djJbq9XShLJCYXPEtCDNI0tveDunpfVqXSVCvpZRqh6sZfV5u1VKkR9lYm+9LIbv1fxSc/L5pj+qvmlde6cnoAbg38A720WzX1eYbMAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$- \\frac{\\alpha e^{\\alpha \\left(C_{1} + t\\right)}}{\\beta \\left(e^{\\alpha \\left(C_{1} + t\\right)} - 1\\right)}$$" - ], - "text/plain": [ - " α⋅(C₁ + t) \n", - " -α⋅ℯ \n", - "───────────────────\n", - " ⎛ α⋅(C₁ + t) ⎞\n", - "β⋅⎝ℯ - 1⎠" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "general = solution_eq.rhs" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIAAAAA0BAMAAACwSy2bAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEM3dMomZdkQi71S7ZqsQsTlgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAC4UlEQVRIDe1W32vTUBg9d23Tpq5rFLbB2ENlvgpDEVpUiE/DB1kFZ0FxRnDUqcxO2IbI2GAKIj4EwZc6YS8DhwoF/4E++KIwqSKKsIK+TBgoFXEI4o/v3jRp1iXdXZ1PeiC534/znZvcm+QLIIOl5aNXZHh+nNsldM/5JSXiwW9ArChB9KNE00Aw55eViE9pAKMDCI0ZTKKgnnLACrwHkvgQqs9K+KuCc30BShEvb0gU1FP2UeAekEK0gJtGfVbCv0icM1wgbqJfrIVEEULZc5zWPjGMQAntOhdomcZp2UVUk+gy7yKSwh1gcGiUtFJQs2XlJJeVAO19qzGKfIGdtdkp25AaL2lQEmmsvJm0b7pvtSBVWSU9pKdmXsPPzdS4uewrCXwHaGgSP0igAswD4eYURoDXlUO4DDbcnEDoVBlDOYSz5ebq/1f9ayuwYy/Hri247V/NYQtm/psSsedDneYfTKDuBnu2qXq1LOYLlqyq/mlgqZHAq/rkVfaEh2arcfocoa9qrx92Am362nBMxy0eoRRH+DOdtgvToyvP8mxCZJ0T9b/H5ITTViRQFGOb5tWVu0e4wJjFtM854BPZUdMKxA0+Rk6YUDy6sri+Ls6oIYfIAnkz1UiArkQtIWB6dmUh0FKoVdNch7M9OgWO08Ey46ZSBBSNC3h1ZSEQMojrIMwGzuvkLdIxY6ppHAQ7Bi7g1ZWFQCxBXAf08xJOkLefrvwBOnSEljOaEPDqykJgW69TTQa1oEiFxvu0wY8mXtgpugIbrIe/+nt07guBIOc7uEZ/g3PkkUD8nRMNTl1wbLfhIZCmmXkh3UKc7A0gBCK9blYCyJsUoEXkv5Y5d269LQTWLuIigklOHBSL2cG1GkAIKIaLETmSGdC4zx+k8cmSK+VhKitfPoLvbw20ixZcy26H/Ma8O+EIxAx3uKGdcWc7HeepY21gsISbQI9BFfYHxfZ9x1afhaZXUA5vifYbUhvGyXjP54gAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$- \\frac{\\alpha e^{C_{1} \\alpha}}{\\beta \\left(e^{C_{1} \\alpha} - 1\\right)}$$" - ], - "text/plain": [ - " C₁⋅α \n", - " -α⋅ℯ \n", - "─────────────\n", - " ⎛ C₁⋅α ⎞\n", - "β⋅⎝ℯ - 1⎠" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "at_0 = general.subs(t, 0)" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAJgAAAAyBAMAAABSaw+aAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAzRAiu5mrdu/dZolEVDLjuNgcAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAELklEQVRIDZVXTWhcVRT+8uY382bezAgWxM2DCo0IdhZCVZAMlOAf6uiiLUbapwuxuuhQSF0UadrqohQkixYqhpKNIHHRUbCjRnQCriw0s1ICQge70HaTMdWW1rbx3L+Ze++7b2IuZO453/nOd9+77/6cAKPb96PDKupFyhrR+w0zmFqsmYDylpXxgDLi/UUL+sB72UKkW6gI44l1d5zQ0gkzVOjhJxMZeC9y65E9yWITnQGZG08DqyYy8Ca63Mwli00NuMJoAm9bkHJLs9xKFivZwzRRPKmy7f7TTcTGG2ZG8ereh3q4tPuPI6EZYN6VGvtNfrILPM44ouW8pX09tA52/Xqu3VKo7MXIyWJnLL5PI1dS3W0o9L/AL1awwOckUSy4a/FzQLHv4R789fMoh2bUu8X8RLHMrEnHZaAwi+I60iduIN+1olXmJ4rlI4tO05SN4M9h5/51jC9Y0WNshhPF1mx6BXiyhrEGPn+cxJqW2CQDEsX4UHrGCwjmgXI9+Dig17TF8nUg887td/WMof3c0ORW8Xp7KQRWLs3UvPsod6xwenYIBG+2hg63PrR8Whmsfct+5mNfE/4NFpDt0UhZovf+MX1IsQcZPBNbZyjoKykdmclFvnA0bAe3vXus86df0yLcDHS+LVbiWVoKLTNqq3/ZMy8pxpvYYrn7kvV/u5sakcS89tEOiu+9/n6d8Iw+oRov0fxbi5DYtRqexUo4vsDeZMtix00xOuDWegeRnWNw1j4aNa7TPIUN0SiajgKa8nJrnxLr85SyZIzqBPOcNkQ6KtFKKdcne+JyyAqKxtjENMX4kzV2tX/gWZmtvuYn2mD0AT6indf5TWLG9tB4ieYpLZKu488eHsby5asc3fI6075mcOhmLTV9sYOxjQ16QjpU9b2mDSpMR9WhrzPFfwnbV5vksM+R3OJVh7GdZGKpTg9Ff4htdEngnaPqMDa65Hrnacl1yXGNtF0JOqqO2MHAuI+1j+7nOdqEKo1flUHzYFcdo7/+Kypz2GtisaqDL/KvZzpDtm4dC4Xn731DwUrMVXWwC+WpjncyxeYo1tYEmprHRO0ZEVVirqqDXXWngalcGFMiQF7C+RbSjcOmGF0HsaqDbka2A1+VV4WlmOlz4ECITKVliuUcVcfzNHwd+OYrS0a4RbFq6cbzT4cEfVmtnqtW+eXkqjpuATtpzB+7TjGcZbD3L4mpu0XNGSXZVQcrqcoLJObWwoWQBe6QWF8ylFglXnWMRXTSRygcCEJJNrt8g/l7gM/6v4uIEnNUHSs1oiwtLmTagmr/igLZ/24R001DzFV1TNnZts8nWwflk8mvr1cd9v8fepawJ3sW9rPwpRgfS1YdEx2LGnNLczGIAzv4r1F1bPqWfPJdcvGqg5+ALqqGZeXEa5DbXHbDJjo4L0zY9ry3GPIf3/hCgoUsF60AAAAASUVORK5CYII=\n", - "text/latex": [ - "$$\\frac{1}{\\alpha} \\log{\\left (\\frac{\\beta p_{0}}{\\alpha + \\beta p_{0}} \\right )}$$" - ], - "text/plain": [ - " ⎛ β⋅p₀ ⎞\n", - "log⎜────────⎟\n", - " ⎝α + β⋅p₀⎠\n", - "─────────────\n", - " α " - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "solutions = solve(Eq(at_0, p_0), C1)\n", - "value_of_C1 = solutions[0]" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAJ4AAAAyBAMAAABfdX/dAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAiZl2RCLdEO9Uu81mqzKufkATAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADWklEQVRIDe1WT2jTUBz+0iTLurZLbWHqLusYU1CYbXfcJhmI7OJWdxAFkYF/QCYzG+jBg/UPYwxl5LSbUATBo8wxkKJkiDhwaGGgXoSAIN42ZaNM8c9LXtK+Zm1Zs8IQfIe8936/7/vy/iXvAzyUgOGBVIWSVqskPaRmPXAqUKIRqMOjXRWyNYcblFA8jGc18yoRDiE4r2CjUrrm+DSEOxBu1cyrRNhAcx587lqlfLV4JKkAQuu57g8UNd7diXlwa6qPLGHt5ZQiTYoaF7qMViQSRPkiTmK4V2/XhBWjdjncBh7y6plADu+CYX8OpzVpJ8fElwcOc1BlDW3XDeTx8WXvTj6Lxgywby8wpOJxOoVp/PIwR4aSJmvepgEtkL6vqriCb0zSQ1PWiR7hzSE4RfTOm+vJe9BxKL4cgkeaVfzAV2M1hbvogdTpJL3Ue5Z1XxTCerQTaQPr4DuWvci4ONwICTQoZH/rUwIxosPHyPmrT0mTLQYiEbU+cv6rT+sj9F9ll1bgT33LLs3i33ntC29DPfuqPE/a+qOpBGUFeO2GwfYLbf9IoWk3KkJZ4FsIGbZvtrlETBIPzBmueDmoCwJRx9aBXMAKh4D1/wKklM0pC3XrkYsnlHMFfRm8OQbZ1vHH7XQ5qItpXWQDzgCcZKOGgRgG7C6r54Javsdh0XpwuGeMtUJSNJkiIyP3Zp890YKeBe1/9LlPtSWo7ynVi49HZlC0QjieEsMBBW0SOjiKLOhZ0PB7jcvw0bCZo76nVI8kelC0QuI0JgyxY9m3HyeOuvRMqKg9R3BtEQ9IzvY9ZMcWLpEyapj4OPEtjBUK3e9+bYaZUhwfgUrYBJcfg6wCju9hsBAMYImxQnKOzZKTmM0u3MtmYyRKoUIeTVMbIFsGx/ewDHONPjFWSA6zWavtjI9Cya2f7sqjQQcc32Oh7EcAECcZK9RI9OIsAHD0KJTc+ks3iR4BOb6HhTfp+KKgaIX4J5hIsYCiHoXKmeaZZjJf86XU95SgBxcTxEkWrRCSvXoJoKhHoUP9yZT0G7LiQtndsFVXtULOfCm0xSRcsPbXliipKKiqFRJ0yqDQObOTtM4fDZc8pbjV3Y4VolBp0yRw7QdLZAod80yRFdqOFaLQ2Z90BIT1F3B5IuCse4W8AAAAAElFTkSuQmCC\n", - "text/latex": [ - "$$\\frac{\\alpha p_{0} e^{\\alpha t}}{\\alpha - \\beta p_{0} e^{\\alpha t} + \\beta p_{0}}$$" - ], - "text/plain": [ - " α⋅t \n", - " α⋅p₀⋅ℯ \n", - "────────────────────\n", - " α⋅t \n", - "α - β⋅p₀⋅ℯ + β⋅p₀" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "particular = general.subs(C1, value_of_C1)\n", - "particular.simplify()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Use [WolframAlpha](https://www.wolframalpha.com/) to solve the quadratic growth model, using either or both forms of parameterization:\n", - "\n", - " df(t) / dt = alpha f(t) + beta f(t)^2\n", - "\n", - "or\n", - "\n", - " df(t) / dt = r f(t) (1 - f(t)/K)\n", - "\n", - "Find the general solution and also the particular solution where `f(0) = p_0`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap10soln.ipynb b/code/soln/chap10soln.ipynb deleted file mode 100644 index c59a281ec..000000000 --- a/code/soln/chap10soln.ipynb +++ /dev/null @@ -1,1314 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 10\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *\n", - "\n", - "from pandas import read_html" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Under the hood\n", - "\n", - "To get a `DataFrame` and a `Series`, I'll read the world population data and select a column.\n", - "\n", - "`DataFrame` and `Series` contain a variable called `shape` that indicates the number of rows and columns." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "(67, 11)" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "filename = 'data/World_population_estimates.html'\n", - "tables = read_html(filename, header=0, index_col=0, decimal='M')\n", - "table2 = tables[2]\n", - "table2.columns = ['census', 'prb', 'un', 'maddison', \n", - " 'hyde', 'tanton', 'biraben', 'mj', \n", - " 'thomlinson', 'durand', 'clark']\n", - "table2.shape" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(67,)" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "census = table2.census / 1e9\n", - "census.shape" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(67,)" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "un = table2.un / 1e9\n", - "un.shape" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `DataFrame` contains `index`, which labels the rows. It is an `Int64Index`, which is similar to a NumPy array." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Int64Index([1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960,\n", - " 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, 1969, 1970, 1971,\n", - " 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982,\n", - " 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993,\n", - " 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,\n", - " 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015,\n", - " 2016],\n", - " dtype='int64', name='Year')" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "table2.index" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And `columns`, which labels the columns." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Index(['census', 'prb', 'un', 'maddison', 'hyde', 'tanton', 'biraben', 'mj',\n", - " 'thomlinson', 'durand', 'clark'],\n", - " dtype='object')" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "table2.columns" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And `values`, which is an array of values." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[2557628654, 2516000000.0, 2525149000.0, 2544000000.0,\n", - " 2527960000.0, 2400000000.0, 2527000000.0, 2500000000.0,\n", - " 2400000000.0, nan, 2486000000.0],\n", - " [2594939877, nan, 2572850917.0, 2571663000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [2636772306, nan, 2619292068.0, 2617949000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [2682053389, nan, 2665865392.0, 2665959000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [2730228104, nan, 2713172027.0, 2716927000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [2782098943, nan, 2761650981.0, 2769074000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [2835299673, nan, 2811572031.0, 2822502000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [2891349717, nan, 2863042795.0, 2879934000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [2948137248, nan, 2916030167.0, 2939254000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [3000716593, nan, 2970395814.0, 2995909000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [3043001508, nan, 3026002942.0, 3041507000.0, 3042000000.0, nan,\n", - " nan, nan, nan, nan, nan],\n", - " [3083966929, nan, 3082830266.0, 3082161000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [3140093217, nan, 3141071531.0, 3135787000.0, nan, nan, nan, nan,\n", - " nan, nan, 3036000000.0],\n", - " [3209827882, nan, 3201178277.0, 3201354000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [3281201306, nan, 3263738832.0, 3266477000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [3350425793, nan, 3329122479.0, 3333138000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [3420677923, nan, 3397475247.0, 3402224000.0, nan, nan, nan, nan,\n", - " nan, nan, 3288000000.0],\n", - " [3490333715, nan, 3468521724.0, 3471464000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [3562313822, nan, 3541674891.0, 3543086000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [3637159050, nan, 3616108749.0, 3615743000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [3712697742, nan, 3691172616.0, 3691157000.0, 3710000000.0, nan,\n", - " 3637000000.0, nan, 3600000000.0, '3,600,000,000– 3,700,000,000',\n", - " 3632000000.0],\n", - " [3790326948, nan, 3766754345.0, 3769818000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [3866568653, nan, 3842873611.0, 3846499000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [3942096442, nan, 3919182332.0, 3922793000.0, 3923000000.0, nan,\n", - " nan, nan, nan, nan, 3860000000.0],\n", - " [4016608813, nan, 3995304922.0, 3997677000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [4089083233, nan, 4071020434.0, 4070671000.0, nan, nan, nan,\n", - " 3900000000.0, 4000000000.0, nan, nan],\n", - " [4160185010, nan, 4146135850.0, 4141445000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [4232084578, nan, 4220816737.0, 4213539000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [4304105753, nan, 4295664825.0, 4286317000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [4379013942, nan, 4371527871.0, 4363144000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [4451362735, nan, 4449048798.0, 4439529000.0, 4461000000.0, nan,\n", - " nan, nan, nan, nan, nan],\n", - " [4534410125, nan, 4528234634.0, 4514838000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [4614566561, nan, 4608962418.0, 4587307000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [4695736743, nan, 4691559840.0, 4676388000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [4774569391, nan, 4776392828.0, 4756521000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [4856462699, nan, 4863601517.0, 4837719000.0, nan, 5000000000.0,\n", - " nan, nan, nan, nan, nan],\n", - " [4940571232, nan, 4953376710.0, 4920968000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [5027200492, nan, 5045315871.0, 5006672000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [5114557167, nan, 5138214688.0, 5093306000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [5201440110, nan, 5230000000.0, 5180540000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [5288955934, nan, 5320816667.0, 5269029000.0, 5308000000.0, nan,\n", - " nan, nan, nan, nan, nan],\n", - " [5371585922, nan, 5408908724.0, 5351922000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [5456136278, nan, 5494899570.0, 5435722000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [5538268316, nan, 5578865109.0, 5518127000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [5618682132, nan, 5661086346.0, 5599396000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [5699202985, 5760000000.0, 5741822412.0, 5681575000.0, nan, nan,\n", - " nan, nan, nan, nan, nan],\n", - " [5779440593, nan, 5821016750.0, 5762212000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [5857972543, 5840000000.0, 5898688337.0, 5842122000.0, nan, nan,\n", - " nan, nan, nan, nan, nan],\n", - " [5935213248, nan, 5975303657.0, 5921366000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [6012074922, nan, 6051478010.0, 5999622000.0, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [6088571383, 6067000000.0, 6127700428.0, 6076558000.0,\n", - " 6145000000.0, nan, nan, 5750000000.0, nan, nan, nan],\n", - " [6165219247, 6137000000.0, 6204147026.0, 6154791000.0, nan, nan,\n", - " nan, nan, nan, nan, nan],\n", - " [6242016348, 6215000000.0, 6280853817.0, 6231704000.0, nan, nan,\n", - " nan, nan, nan, nan, nan],\n", - " [6318590956, 6314000000.0, 6357991749.0, 6308364000.0, nan, nan,\n", - " nan, nan, nan, nan, nan],\n", - " [6395699509, 6396000000.0, 6435705595.0, 6374056000.0, nan, nan,\n", - " nan, nan, nan, nan, nan],\n", - " [6473044732, 6477000000.0, 6514094605.0, 6462987000.0, nan, nan,\n", - " nan, nan, nan, nan, nan],\n", - " [6551263534, 6555000000.0, 6593227977.0, 6540214000.0, nan, nan,\n", - " nan, nan, nan, nan, nan],\n", - " [6629913759, 6625000000.0, 6673105937.0, 6616689000.0, nan, nan,\n", - " nan, nan, nan, nan, nan],\n", - " [6709049780, 6705000000.0, 6753649228.0, 6694832000.0, nan, nan,\n", - " nan, nan, nan, nan, nan],\n", - " [6788214394, 6809972000.0, 6834721933.0, 6764086000.0, nan, nan,\n", - " nan, nan, nan, nan, nan],\n", - " [6858584755, 6892319000.0, 6916183482.0, nan, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [6935999491, 6986951000.0, 6997998760.0, nan, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [7013871313, 7057075000.0, 7080072417.0, nan, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [7092128094, 7136796000.0, 7162119434.0, nan, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [7169968185, 7238184000.0, 7243784000.0, nan, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [7247892788, 7336435000.0, 7349472000.0, nan, nan, nan, nan, nan,\n", - " nan, nan, nan],\n", - " [7325996709, 7418151841.0, nan, nan, nan, nan, nan, nan, nan, nan,\n", - " nan]], dtype=object)" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "table2.values" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `Series` does not have `columns`, but it does have `name`." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'census'" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "census.name" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It contains `values`, which is an array." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([2.55762865, 2.59493988, 2.63677231, 2.68205339, 2.7302281 ,\n", - " 2.78209894, 2.83529967, 2.89134972, 2.94813725, 3.00071659,\n", - " 3.04300151, 3.08396693, 3.14009322, 3.20982788, 3.28120131,\n", - " 3.35042579, 3.42067792, 3.49033371, 3.56231382, 3.63715905,\n", - " 3.71269774, 3.79032695, 3.86656865, 3.94209644, 4.01660881,\n", - " 4.08908323, 4.16018501, 4.23208458, 4.30410575, 4.37901394,\n", - " 4.45136274, 4.53441012, 4.61456656, 4.69573674, 4.77456939,\n", - " 4.8564627 , 4.94057123, 5.02720049, 5.11455717, 5.20144011,\n", - " 5.28895593, 5.37158592, 5.45613628, 5.53826832, 5.61868213,\n", - " 5.69920299, 5.77944059, 5.85797254, 5.93521325, 6.01207492,\n", - " 6.08857138, 6.16521925, 6.24201635, 6.31859096, 6.39569951,\n", - " 6.47304473, 6.55126353, 6.62991376, 6.70904978, 6.78821439,\n", - " 6.85858475, 6.93599949, 7.01387131, 7.09212809, 7.16996819,\n", - " 7.24789279, 7.32599671])" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "census.values" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And it contains `index`:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Int64Index([1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960,\n", - " 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, 1969, 1970, 1971,\n", - " 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982,\n", - " 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993,\n", - " 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,\n", - " 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015,\n", - " 2016],\n", - " dtype='int64', name='Year')" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "census.index" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you ever wonder what kind of object a variable refers to, you can use the `type` function. The result indicates what type the object is, and the module where that type is defined.\n", - "\n", - "`DataFrame`, `Int64Index`, `Index`, and `Series` are defined by Pandas.\n", - "\n", - "`ndarray` is defined by NumPy." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "pandas.core.frame.DataFrame" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(table2)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "pandas.core.indexes.numeric.Int64Index" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(table2.index)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "pandas.core.indexes.base.Index" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(table2.columns)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "numpy.ndarray" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(table2.values)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "pandas.core.series.Series" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(census)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "pandas.core.indexes.numeric.Int64Index" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(census.index)" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "numpy.ndarray" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(census.values)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Optional exercise\n", - "\n", - "The following exercise provides a chance to practice what you have learned so far, and maybe develop a different growth model. If you feel comfortable with what we have done so far, you might want to give it a try.\n", - "\n", - "**Optional Exercise:** On the Wikipedia page about world population estimates, the first table contains estimates for prehistoric populations. The following cells process this table and plot some of the results." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Select `tables[1]`, which is the second table on the page." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Population Reference Bureau (1973–2016)[15]United Nations Department of Economic and Social Affairs (2015)[16]Maddison (2008)[17]HYDE (2010)[citation needed]Tanton (1994)[18]Biraben (1980)[19]McEvedy & Jones (1978)[20]Thomlinson (1975)[21]Durand (1974)[22]Clark (1967)[23]
Year
-10000NaNNaNNaN2M[24]NaNNaN4.01–10MNaNNaN
-9000NaNNaNNaN4.NaNNaNNaNNaNNaNNaN
-80005.NaNNaN5.NaNNaNNaNNaN5–10MNaN
-7000NaNNaNNaN8.NaNNaNNaNNaNNaNNaN
-6000NaNNaNNaN11.NaNNaNNaNNaNNaNNaN
\n", - "
" - ], - "text/plain": [ - " Population Reference Bureau (1973–2016)[15] \\\n", - "Year \n", - "-10000 NaN \n", - "-9000 NaN \n", - "-8000 5. \n", - "-7000 NaN \n", - "-6000 NaN \n", - "\n", - " United Nations Department of Economic and Social Affairs (2015)[16] \\\n", - "Year \n", - "-10000 NaN \n", - "-9000 NaN \n", - "-8000 NaN \n", - "-7000 NaN \n", - "-6000 NaN \n", - "\n", - " Maddison (2008)[17] HYDE (2010)[citation needed] Tanton (1994)[18] \\\n", - "Year \n", - "-10000 NaN 2M[24] NaN \n", - "-9000 NaN 4. NaN \n", - "-8000 NaN 5. NaN \n", - "-7000 NaN 8. NaN \n", - "-6000 NaN 11. NaN \n", - "\n", - " Biraben (1980)[19] McEvedy & Jones (1978)[20] Thomlinson (1975)[21] \\\n", - "Year \n", - "-10000 NaN 4.0 1–10M \n", - "-9000 NaN NaN NaN \n", - "-8000 NaN NaN NaN \n", - "-7000 NaN NaN NaN \n", - "-6000 NaN NaN NaN \n", - "\n", - " Durand (1974)[22] Clark (1967)[23] \n", - "Year \n", - "-10000 NaN NaN \n", - "-9000 NaN NaN \n", - "-8000 5–10M NaN \n", - "-7000 NaN NaN \n", - "-6000 NaN NaN " - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "table1 = tables[1]\n", - "table1.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Not all agencies and researchers provided estimates for the same dates. Again `NaN` is the special value that indicates missing data." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Population Reference Bureau (1973–2016)[15]United Nations Department of Economic and Social Affairs (2015)[16]Maddison (2008)[17]HYDE (2010)[citation needed]Tanton (1994)[18]Biraben (1980)[19]McEvedy & Jones (1978)[20]Thomlinson (1975)[21]Durand (1974)[22]Clark (1967)[23]
Year
1913NaNNaN1793.NaNNaNNaNNaNNaNNaNNaN
1920NaN1860.01863.1912.NaNNaNNaNNaNNaN1968.
1925NaNNaNNaNNaNNaNNaN2000.0NaNNaNNaN
1930NaN2070.0NaN2092.NaNNaNNaNNaNNaN2145.
1940NaN2300.02299.2307.NaNNaNNaNNaNNaN2340.
\n", - "
" - ], - "text/plain": [ - " Population Reference Bureau (1973–2016)[15] \\\n", - "Year \n", - "1913 NaN \n", - "1920 NaN \n", - "1925 NaN \n", - "1930 NaN \n", - "1940 NaN \n", - "\n", - " United Nations Department of Economic and Social Affairs (2015)[16] \\\n", - "Year \n", - "1913 NaN \n", - "1920 1860.0 \n", - "1925 NaN \n", - "1930 2070.0 \n", - "1940 2300.0 \n", - "\n", - " Maddison (2008)[17] HYDE (2010)[citation needed] Tanton (1994)[18] \\\n", - "Year \n", - "1913 1793. NaN NaN \n", - "1920 1863. 1912. NaN \n", - "1925 NaN NaN NaN \n", - "1930 NaN 2092. NaN \n", - "1940 2299. 2307. NaN \n", - "\n", - " Biraben (1980)[19] McEvedy & Jones (1978)[20] Thomlinson (1975)[21] \\\n", - "Year \n", - "1913 NaN NaN NaN \n", - "1920 NaN NaN NaN \n", - "1925 NaN 2000.0 NaN \n", - "1930 NaN NaN NaN \n", - "1940 NaN NaN NaN \n", - "\n", - " Durand (1974)[22] Clark (1967)[23] \n", - "Year \n", - "1913 NaN NaN \n", - "1920 NaN 1968. \n", - "1925 NaN NaN \n", - "1930 NaN 2145. \n", - "1940 NaN 2340. " - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "table1.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Some of the estimates are in a form we can't read as numbers. We could clean them up by hand, but for simplicity I'll replace any value that has an `M` in it with `NaN`." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "table1.replace('M', np.nan, regex=True, inplace=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Again, we'll replace the long column names with more convenient abbreviations." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "table1.columns = ['prb', 'un', 'maddison', 'hyde', 'tanton', \n", - " 'biraben', 'mj', 'thomlinson', 'durand', 'clark']" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This function plots selected estimates." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def plot_prehistory(table):\n", - " \"\"\"Plots population estimates.\n", - " \n", - " table: DataFrame\n", - " \"\"\"\n", - " plot(table.prb, 'ro', label='PRB')\n", - " plot(table.un, 'co', label='UN')\n", - " plot(table.hyde, 'yo', label='HYDE')\n", - " plot(table.tanton, 'go', label='Tanton')\n", - " plot(table.biraben, 'bo', label='Biraben')\n", - " plot(table.mj, 'mo', label='McEvedy & Jones')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the results. Notice that we are working in millions now, not billions." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_prehistory(table1)\n", - "decorate(xlabel='Year', \n", - " ylabel='World population (millions)',\n", - " title='Prehistoric population estimates')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use `xlim` to zoom in on everything after Year 0." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_prehistory(table1)\n", - "decorate(xlim=[0, 2000], xlabel='Year', \n", - " ylabel='World population (millions)',\n", - " title='Prehistoric population estimates')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "See if you can find a model that fits these data well from Year -1000 to 1940, or from Year 1 to 1940.\n", - "\n", - "How well does your best model predict actual population growth from 1950 to the present?" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def update_func_prop(pop, t, system):\n", - " \"\"\"Compute the population next year with proportional growth.\n", - " \n", - " pop: current population\n", - " t: current year\n", - " system: system object containing parameters of the model\n", - " \n", - " returns: population next year\n", - " \"\"\"\n", - " net_growth = system.alpha * pop\n", - " return pop + net_growth" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
t_01.0000
t_end2016.0000
p_0255.0000
alpha0.0011
\n", - "
" - ], - "text/plain": [ - "t_0 1.0000\n", - "t_end 2016.0000\n", - "p_0 255.0000\n", - "alpha 0.0011\n", - "dtype: float64" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "t_0 = 1\n", - "p_0 = table1.biraben[t_0]\n", - "\n", - "prehistory = System(t_0=t_0, \n", - " t_end=2016,\n", - " p_0=p_0,\n", - " alpha=0.0011)" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def run_simulation(system, update_func):\n", - " \"\"\"Simulate the system using any update function.\n", - " \n", - " system: System object\n", - " update_func: function that computes the population next year\n", - " \n", - " returns: TimeSeries\n", - " \"\"\"\n", - " results = TimeSeries()\n", - " results[system.t_0] = system.p_0\n", - " \n", - " for t in linrange(system.t_0, system.t_end):\n", - " results[t+1] = update_func(results[t], t, system)\n", - " \n", - " return results" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "results = run_simulation(prehistory, update_func_prop)\n", - "plot_prehistory(table1)\n", - "plot(results, color='gray', label='model')\n", - "decorate(xlim=[0, 2000], xlabel='Year', \n", - " ylabel='World population (millions)',\n", - " title='Prehistoric population estimates')" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvNQv5yAAAIABJREFUeJzs3Xl8FdXd+PHPvTe52fedhCQkwGGRVQRlEcWtqI9aqnV93G2x2mptfexjq+JWrbVuVOXpokhtf9W2itpaq4JrRawoigiHLAQIIfu+39w7vz9mspJlAtnzfb9egZs5s3zvJLnfOXPOnOMwDAMhhBBipHEOdwBCCCFEdyRBCSGEGJEkQQkhhBiRJEEJIYQYkSRBCSGEGJEkQQkhhBiRJEGJXimlrlRKtfSxTrpSylBKLR2quEbS8QeDUmq9UurtAdjPqD83dn4HxdjkN9wBiKOnlFoPXGF96wUOAv8Efqq1LhuCEA4ASYCtYymlfgZcq7VOH47jj1VKqWzgea31mg6LR825UUqlYMZ7stb63Q5FL2D+Pg9FDG8D+VrrK4fieKJ3kqDGjg+Ab2P+TI8FfgdMBM7qbmWllFtr3TwQB9Zae4HCgdhXf3V4H8Ny/JFuOH82A0Vr3QA0DHccYuhJgho7mrXWrR9E+UqpY4B7lFJBQAKwF7gMuBRYDjwN/FgpNRn4BXAKYAD/AX6ktd7RcedKqSXAWmAasBNYrbXeZpWlW/tfprX+0Fp2O3AtkAxUA58B5wEXAvda67QOY3K31nqNUioMeBhYBYQBXwG3a63f7HKcTu9DKfXrbo4fb72vM4EIYB/wC631M92dPKsWmgK8DvwIiMK8av+u1rrUWsdhlX3PWvcAsFZr/ViH/eQBf7TO+beBZuD3wP9qrX0d1vmd1vq+Dtv9DpistT6ph/jmA/djXnwEA7uAO7TWb1jl7wKZwF1KqbuszSZZ/3c9Nwp4xDp/AO8AP9RaZ1vlV2Je4Cynh595T5RSFwE/sbYpBF6y4qyzypdi/lxmW5vkAv+jtf6XdT4B3jFDZJ/WOr01Hq21X5f4TgOeACYDnwAXW68fB6YCW4ArtNYHre0mAb8CjgcigRzgIa31H6zy9Zh/ByilWu9InKy1flcplWDFfRYQCHyJ+TN931rf3yr/NhAHlAPvaa0v6u18id5JG9TY1YD58+14EfIL4E/ALOBJ64/uQ6AYWIb5h6uBd5VScR22cwIPADcB84EK4EWlVLcXOEqpVZgfUjcBUzA/SFpv0bxgxZGPeespCTMpATwDnIGZgOYB/wb+rpSa1uUQnd5HN8cPAt4D5mAmshnA94H67uLtYCFwEvANzMQ224qp1fcwk+uDwEzgl8CDSqlruuzn+0ABcBzwQ+BG4OY+jt2XcODPVnzzgX8Bryqlplrlq4A8zA/g1vN6oOtOrHPzJuaH7HLrKxR4Qynl7rBqv37m1r6vxLzw+RXmOb8cOBVYZ5W7gFeBrdY+5wNraP+5zLf+/5YV/3G9nA8ncBfmRdASYALm79Y9wPXAUsyLiEc6bBMKbML8+c4CfgM8q5Q62Sq/CfNOxIu0n8OPrHP2DuZF00rM383XgbeUUtOtbb+PmZwuw/ydPwf4uJf4hQ1SgxqDlFIzgBuArVrrGqVUjFX0f1rr5zustwbI01pf32HZDzA/nC8FWmsGDuBmrfVn1jp3Yl6dZmImtK7SMK+e39Bae4D9wPYOx6gFvB1qfFg1ufOBs6yraYCblFLLgP8Bru6w/67vI73L8S/BrD1M1lrnW8tyu4mzKyfw31rrKmu/NwD/UkpN0VpnYSbdtVrr31jrZ1m1kZ9i1pJafaG1vtN6ra0PsVvo/GHZL13aZAB+ppT6L+AC4H6tdblSygvUdjmvXXd1CeYV/rEdaoYXYSa3i4AN1nr9/ZmDmWz+t7VGAuQqpW4E3rN+r8Csmb5qnU+ArA7bl1j/l3d8Dz1ojW+7Fd9vgIeABR1q9v+H+bMBwLor0PHOwFql1KnWOXlHa12llGoGGrqcwysxLxAu1Fq3dta4Xyl1CvBdzIuPNGAPZq3JwPyd/08f70H0QRLU2HGS9cHvAgIwrxS/22WdT7p8fxxwrLVdR0GYV4GtDOCLDt8ftP5PoPsPqxeBHwD7lFJvWrFs1FrX9BL/DOv/97ssfx84ocuyru+jq2OBrzskJ7u+bk1Oln9b/09XShVhXpF3je89zEQarLVurQls6bLOv4H/VUqFa62r+xkTAFaN9m5gBZCI+bcbiPnB2B8zMd9naesCrXWRUkpbZa369TO34ksDHlFKPdyhyGH9P1lr/R/rVua/lFKbMc/dy1rrnhJebww6J5vWhPJll2UxSimX1tqrlAoG7gT+C7N25Mb8W3mnj2Mdh3nOK7sk/ADa28aeBd4CspVSb1mvXxuodt7xSm7xjR1bgbnAdCBIa32a1rprraGuy/dOzOQxt8uXwrwabuWzGttbGR22P4x1z38aZq2nGLgDsyYxsZ/vCcwPuK5D7nd9H90ZrGH6u+7X0e1ava/j62aZfx/7WI95G/Z/rP/nYtZK3b1s05Puzk3X89yvn3mH5TfR+XdpDubFzg4ArfV1mBcQb2HeXvxKKdX1QsqObuOzauxdY24917/EvAV3D3CyFd/r9H0OnZhtfl3/TqYD11nH3Y5Za/8xZrvj48B2pVT4Ebw3YZEa1NjR0NrI3Q+fAlcCB62eUgNGa90EvIHZtnEHUITZSWIt5h+wq8smO63/T8T80Gi1DPi8n4ffBlytlErpZy1qepdazmLr/11a62qlVD7mh+o/OmxzIrC3Q+0JzLa8jk4ACjrstxizzaSjeZgN6z05EbMzwasASqkQIAOzI0mr7s5rVzuB1Uqp2A63+BIwOxU83OuWvbBqYQcApbX+bR/rfmXF/YhSah3wHeD/rPix8R6O1InAH7XWLwAopZyY77uowzrdncNPMdvTqrXWxT3tXGtdC7wMvKyU+jlwCPP35bUBewfjjCSo8e3XwDXARqXUfZiN6imYDcH/0Fp/dCQ7tToNODFvxVVi9owKA762VtkLJCqlTsBsg6jXWucopf4CPGVdUe/DbOw+BrONoD/+H2ZN41Wl1P9g9tbKAGJbP5x6YAAbrOe0ojE7YPyjQ3vJA8CvlFJZwLuYt9uux2zv62iu1b73J2ABZq1iTYfyt4HvKaVett7naszbY70lKA1cqpT6EPMD9B4O/yDdCyxRSqVidjzobn9/wrzN9YJS6lbM2sXDmLfwejs3dvwU+L1SqhLYCHgwaxkrtdbftdoZr8P8wD6AmaSXYfbwBCgFaoHTlVI7gSatdcVRxtSRBs5VSv3NOs4tVgwdE9Re4GSlVCZQZX39EbOzyz+UUj/FbGtKwPz579Jab7TOZQFmrbYes0eh11pXHCG5xTeOaa2LMK/uSzG7A2vMP8Y0zKu/I1UBXIX5Ib4L84PgO1rrTVb5RuAvmDWREsxkAmaPrH8Bz2O2fywBztZa7+7Pwa3azHLMq/Q/WzE8idm21ptPMHs1vmXFsdN6H62exvxwvx0z2d4G/ERr/fsu+1mLeQ4/xbwIeBp4tEP5LzDf+wuYvcaqMM9Hb66iPelvxKyddm2EvwuzS73GPK+pXXdi1ZRPB5ow29Pew7xl+o2jbS+xOkd8G7Mr9idWfGtob7+qw7zd92fMD+6/AR9h9nLE6oZ/g7WPA/S/5tyXH2JeELyDeWv7IPDXLuv8CvPv4QvMc7hEa92I+fv0KWZb0x7Mv5eF1v7AfJTiFsz2xx3AN4FvHWH7mrA4ZEZdIdqfg9Jan3qU+8mjyzNOQogjIzUoIYQQI5IkKCGEECOS3OITQggxIo3oXnxKqQDMh+QOYfaIEUIIMba4MB+c/o/1eEqbEZ2gMJPTB8MdhBBCiEG3DLMXbZuRnqAOAfzxj38kMTFxuGMRQggxwAoLC7n00kuhm0dbRnqC8gIkJiaSkpIy3LEIIYQYPIc140gvPiGEECOSJCghhBAjkiQoIYQQI5IkKCGEECOSJCghhBAjkiQoIYQQI9JI72YuhBBilGr2emjxtRDsf/hMN6WVDezeV06ku7HH7SVBCSGEGFDNXg9fFWm+LNpFemQKJ006oVO5z2fw2ge51DV6mBjp63E/kqCEEEIMiNrmOnRpLjuKdtPcYs5/qUtzmREzjfjwqLb1nE4HU9Oi+FwXk1tQ1eP+JEEJIYQ4Yl6fl32VB9ldmk1+dSFYM2Q0ebyUVTXQUOdiq5HPfy2M6rTd9PRoaus9RAYEsq6HfUuCGgRKKd58803S0tLalq1du5Z9+/bx8MMPA7Bu3TpefPFFKioqCAsLY/78+Tz22GM97vO1117j2WefZe/evYSEhDBt2jRWr17NggULBv39CCFEVxUNVewuzSardC+NLU2HlbsdQbhr4okhgeJCB16fgcvpaCuPDg/kjOPTyM/P7/EYkqCGwcsvv8wrr7zC+vXrSU1NpaSkhM2bN/e4/rPPPstvfvMb7r77bpYuXYq/vz8ffPABmzZtkgQlhBgWnxXsIKd8H16fQW1DM+EhATiA5PBEVGwm6ZETeb5sN7UNHvz9nNTUNRMZFtCvY0g382GwY8cOli5dSmpqKgBxcXFceOGF3a5bU1PDE088wZ133snpp59OcHAw/v7+rFixgttuuw0An8/Hb37zG0499VQWLVrETTfdRGVlJQD5+fkopXj55Zc56aSTWLRoEU8//XTb/r/88ktWrVrF/PnzWbx4MQ888AAAW7du5cQTT+wUy4oVK/joo4963U4IMbYYhkFNU+1hy1VcJgdLa9H7Kygs8jAlXHHx7HM5S53C5Jh0/Fwuls1NZtXJk/nvldP7nZxgCGtQSqmu7zAIeEpr/f2BOsYnOwv55OtCABbOSGThzM5TdHz4xUG27ykBYMnsCcxT8Z3K39l2gJ25ZQCcfOxEZmbEdCqvqGkkKizwqOOcM2cO999/PwkJCSxatIgZM2bgcrm6Xffzzz+nqamJ0047rcf9bdiwgbfffpvnn3+e6Oho7rvvPu655x4eeeSRtnW2bdvGG2+8QV5eHueffz6nn346mZmZ3H///Vx++eWcd9551NXVkZWVZes9HOl2QojRocHTSFbZXnaX5lDXXM9/z1mFn6s9ZSSHJZLonkiwEUiwIwr/hnjCAkI77SMzJfKoYhiyBKW1botcKRUCFAF/GarjjyTnnnsuDoeDl156iV//+tcEBARwzTXX8J3vfOewdSsrK4mKisLPr+cf1QsvvMCdd97ZNmfWjTfeyMknn0xLS0vbOjfeeCOBgYFMmzaNadOmsXv3bjIzM/Hz82P//v2Ul5cTHR3N3Llzbb2HI91OCDFy+Qwf+VWH0KW55FUewDAMWrw+quqaeW/3Tk6ZOadtXYfDwVkzl/LqB7nERgYRHX70F+9dDVcb1PlAMWN0tlyXy9UpOQC0tLTg7+/f9v0555zDOeecg8fj4e233+bWW29l+vTpLFu2rNN2kZGRVFRU0NLS0mOSKigo4IYbbsDpbL9j63Q6KSsra/s+Nja27XVQUBD19fWAWRN64oknWLlyJSkpKW3JrS9Hup0QYuSpbqplT2kOujSXuub6tuW1DR72FVbjMFx81VDIihmzcTjaOzqkxIdx0WmK2MjDH8QdCMOVoK4ANmitjYHc6cKZh9/W62jpnGSWzknusfzkYydy8rETeyy3e3svKSmJ/Px8MjMz25bl5+eTnp5+2Lr+/v6sXLmS3/72t2RlZR2WoObNm0dAQABvv/023/jGN7o9XmJiIj//+c859thjDyvrrYcMQHp6Oo888gg+n48333yTH/zgB2zdupWgoCAaG9uf8PZ6vZSXl/e5XXBwcK/HE0KMHAerC/n80E4Kqgu7LU+LTsRTnESQLxZng4uSygbio9r/xp1Ox6AlJxiGThJKqVRgOfDcUB97qJx55pk8/fTTFBYW4vP5+Oijj9i8eTNnnHEGAC+99BLvvvsutbW1+Hw+3nvvPbKzs5k9e/Zh+woLC+MHP/gB99xzD2+//TYNDQ14PB7ee+89HnroIQAuvvhiHnvsMQ4ePAhAeXk5b7/9tq1YX3nlFcrLy3E6nYSHhwNmDXDSpEk0NTXx7rvv4vF4ePrpp2lubu5zOyHE6FHbXEdeeQEllQ1k51fi8foI9AtgVuI0LjjmbL51zDeYP3EaE2LDOWl+ChGh/e/ocDRs1aCUUguAJUAaZueGUuAL4F2tdWk/j3k58KHWem8/txs1brjhBh5//HEuueQSqqqqSE1N5eGHH2bq1KkAhIaGsm7dOnJycvB6vSQnJ7NmzZoeu4xfddVVxMTE8NRTT/HjH/+YkJAQZs6cyerVqwG4/PLLMQyDq6++muLiYmJiYjjzzDM59dRT+4z1gw8+4MEHH6SxsZEJEybw6KOPEhAQQEBAAHfddRc/+9nP8Hq9XHvttW1tXL1tJ4QYeZpamsmrPMDUmIxOt+gyolIpLN1ETX0jwY5oJgUcwxlzZuFytl9srjgutdPzS0PJYRjd32VTSgUDNwPXAbGYCakQaASigGlACvAG8KDWeoudAyql9ljrP2Nj3XRg76ZNm0hJSbGzeyGEEJjdwwtqitClOeytOEBzSwsrUpejkjo3Y7yz82u276zB3xFAYkwI56+YMqRx5ufnc8oppwBM0lrndSzrrQalgX8BVwPvaa0PG9FPKZUIXAD8Tin1a631013X6bL+YiCZcdp7TwghBlttUx26LBddmkNtUx1NHi9F5fXU1jdTX77tsAS1ZKqivmIfU1KjmDQhYpii7l5vCeoErXWvLexa60JgrVLq10DPvRPaXQG8pLWu6UeMQgghetHi87KvMh9dmtNpPDwwOzLU1DcTQChNtYHUN3oIDmzvUez2d3HW0ozhCLtPPSaovpJTl3UN4JCN9b5rd59CCCH6tr/yIJv3fkRtYwOVtU3ERgThtNqZ3H5uZiakE17roqrCSVxkELUNnRPUSGa3k8R1QJHW+lXr+18B12DeBrxUa509eCEKIYToSXRQJHsPVVBRYz4WEuDnYmZyOio2k7TIFPycLqaE1uP2dx3RcEPDyW4389uAKgCl1DLM5HQdkA08MTihCSGEAHOEh32V+byZ/T6VjdWdykIDQkgKS8CfQGIcaaQ5FnLm1BVkRqfhZ/XGi48OHnXJCew/qJsM5FqvzwNe0Fr/RSn1BfDxoEQmhBDjXHlDJXtKc8kq20tlfR2VtU0UFXn57yWdR225YM4KXijKIS0xnGnp0cMU7cCzm6DKgInAAWAl8DNruROQpzOFEGKANLY0kVO+jz2luZTUmcOVNTa3kJ1vzjxbXbWHZs8y3P7tH98x4WFcfc4xBLrH1gxKdt/NH4A/KaU0EAH801q+CNg9GIEJIcR40TpI656yXPIq8vEaPjo+Ghvg9iPUHYzbE02YkcDegmpUWuea0lhLTmAzQWmt/1cp9Tnmg7nf0Vo3WEUG8PPBCk4IIcaD0voKXvl6ExU1TVTVNpEcH0pooD9Op5O0iBRUbAalYS4OlTUwLS2K9KTw4Q55SNhOuVrrF7tZtmFgwxkb+pryfevWrVx++eVccskl3HXXXW3rXHzxxVxwwQWsWrXqsH2uXbuWdevW4Xa7AYiPj2fJkiWsXr2a+HhzXqutW7dyxRVXEBTUefDGZ555hnnz5pGVlcUDDzzAjh07MAyD1NRUbrrpJpYvX9627oEDBzjttNO46KKLWLNmzUCeFiEE5jxLLqcLt6u9q3dccDSNdX6UWhONehsCWaLmkxmdRqCf2bkh9eimVhqVbCcopdQZmIO8xtOl95/W+uoBjmvMCw4OZuPGjVxzzTW2h3FauXIlDz/8MB6Ph7y8PNauXcuqVat46aWX2pJUfHw877//frfbr169mosvvph169YBtCWqjl555RUiIiJ4/fXXuf3229sSohDiyHl9XvZXFbCnNJe8ynxmxcxicUb74NAOh4PFGbN5s2IP4SSQ4I1mRtyUTuPmjUe2upkrpR4AXgOOxUxqji5fop/CwsJYtWoVTz75ZL+39ff3Z8qUKTz66KNER0fz7LPP9rlNeXk5+fn5fPvb38btduN2uzn22GMPG6B248aN3HTTTfj5+bF58+Z+xyaEMBmGQVFtCR/u+4Q/fPESb+x5jy3Zu9mdV87r2z8/7OJwccZMTp26iPOXzeKSM6aN++QE9mtQ1wAXaq1fHsxgjtanB7/ks4IdttadFjeZE9MXdVr2ft5Wdpf0/Mzx/AmzWJB8+JQYR2r16tWcccYZXHfddWRk9H+oEZfLxSmnnMKHH37Y57pRUVGkpaVx6623csEFFzB37txOkxgCfPrppxQWFnLWWWeRk5PDxo0be5yDSgjRvZqmWrLK8sgq20tVh2eWnA6ormvG6zPwGAb7iipJT4xqK3c4HCyb2/N8deOR3Qd1vcDXgxnIeBQXF8dFF13EE08c+bPO8fHxVFVVtX1fXFzMggULOn3V19fjcDjYsGEDycnJPPjggyxdupRLL72UvLy8tm1ffvllTjzxRCIiIjj77LP54IMPOs3KK4ToWW1zHa/tfps/bN/Im7u2UlxT0ak8LDCUWfEzSHccxzFhizB8Qz4d36hjtwb1AHCrUmq11rqlz7XHOTtTvre67rrrOO2009i9+8h66xcVFRER0T4CcW9tUImJidx5550AHDp0iDvuuIPbbruNF154gcbGRt544w3uu+8+wJzJNykpiddee40rr7zyiGITYjwJ9gtid0EB+aWVGAbERQaREhdBRlQqU2InkRQaT1VtMw1NLSTGBMstPBvsJqhvAvOB/7Lmc/J0LNRarxjowI7EguTZR3UL7sT0RYfd9jsS/ZnyPSoqiiuuuILHHnus38fx+Xy88847LF68+IhivPTSS7nlllsAeOutt6itreXuu+9uS1LV1dW88sorkqCEsBiGQWl9OVlle0kJTyI1sv2WnNPpZHJ0Ovkl2wlxRBHelMxls5fh79d+YRoZFjAqhxwaLnYT1LvWl7Chdcp3pRTx8fF8/PHHbN68mRdeeKHb9a+66qrWCbts8Xg87N+/n7Vr11JaWmorgVRVVfHcc89x7rnnMnHiRCorK/nb3/7G3LlzAbNzxLe+9S1++MMftm1TVFTE+eefj9YapZTt+IQYa2qaaskuN9uViqorqKxtIoT93HJ650dCTlHzKcwLIS48HJUahdMhA+0cDbsP6t492IGMJX1N+d5VaGgo1157LQ8//HCv+/3nP//Jpk2bMAyD+Ph4Fi9ezEsvvURCQkLbOsXFxcybN6/Tdg8++CDLli3j4MGDXHXVVVRUVBAcHMyiRYu44447KCoqYsuWLbz88svExcW1bRcXF8eyZcvYuHEjt91221GcESFGn6aWZvZW7GdP2V4Ka4oB8PoMcvIr8RlQykEKK6pIjGq/xR4eFMpVK+cQGDD2RnUYDj1O+d4dpdSpmFO9A+zSWm8alKjaj5eOTPkuhBgiPp+P/VUFZJXtJa8yH6/P2za3UquDxfV468IIJ4HlM6ax6JikYYp2bDjSKd/bKKUmARuByZhzQFmLVTZwntZ678CFK4QQw8Pja+G1Xe9QWlVPdW0z8dHBxIQHgsNBSngiU2ImQVI4WfurUanjZ8ih4WK3HvoUkAecpLWuAFBKRQPrrbKVdg+olLoIuAtIBQqBK7XWH9gPWQghjl5VYzUOh5PwgNC2ZQF+bsKcsWRVZwHgqXdz/Mz5TI5JJ9i/fQixKSkxQx7veGQ3QS0HFrQmJwCtdblS6nZgq92DKaVOA34BXAh8AkjdWAgxZBo8jeSU7yO7PI+CqmISAyeyau5JndZZNnkW+w40EWrEEe8XjYqZSoC/dHYYDnYTVDXmSOZdH9ZNAWr6cby7gXu01q2THB7sx7ZCCNFvLT4v+yrzySrby4GqArOdqaiG2gYP2dRwujqB0KD2rt+ZsSl8a34gsZFBJETL80rDyW6CWg+sV0rdQXuN6XjgHqusT0opF7AAeNVquwrEbNe6tcP0HUIIcdQMw+BQTRFZZXnkVuzH421/dNPhcOD1GWA4CHREsGt/Ccepzp2wjsmM7bpLMQzsJqjbgQrgXiDRWlYEPA780uY+EgB/4HxgGebDvq9gzs77U5v7EEKIXjV7Pfx15z8oq62msqaJkCA/QoPaR+VPCI1jQupUcrJgYlwUsWFhwxit6I3d56B8mG1Hv1BKhVvLqnvf6jCttaS1WutDAEqpR5AEJYQYQG6XP9XVPrIOmnMrNXncTIiKYUrMJKZEpxMeGEaTx8uyKS1EhMqoDiNZv58mO4LE1LpdhVIqH3MWXiGEOGJen5d9lQfZU5ZLQmgc85JmdiqflzIVfbCQMEc8EY0JnDvleIIC24ccCvB3SceHUaDHBKWUygWO01qXKaX20kti0VrbnSviWeD7Sqk3MG/x3Qz8vR/xjms/+clPSEhI6DQcUU9WrFjBfffdd0Tj9AkxEhmGQUl9OXtKc8kuy6Oqvp6KmiZamnKZdfY0/PzaE86xqdM5kOsmJMjNtLQo3G4Z2WE06u2ndjdQa71eM0DHuxeIBfYAjcCLwP0DtG8hxBhU39xAVvledGkulQ3m1DIGcKCohuYWHwBf7stnfmZa2zZ+ThffOllmpB3tekxQWuvnunt9NLTWHuB71pcQQnTLZ/jIq8hHl+awv6oAn8/A5WxPNg4gMTKSurJQwkigsuLwRCTJafTr7RZfqt2daK33D0w4Y8OKFSu45JJLeOWVVzhw4ABnnnkmt9xyCz/5yU/Ytm0bc+bM4fHHHyciIoJNmzbxyCOPUFRUxPTp01mzZk3bNB1ff/01P/3pT8nLy2P58uWH/cG98847PPbYYxw8eJDJkyezZs0apk2b1l1IQow67+V+woGyciprmogIDSApJgQ/lx8ZUalMjc0gyIhg2+4iVFo0KfGhfe9QjDq93eLLo+8ODQ5rnWFvbczJyWHPnj2HTRQ4kPz8/Jg6dWqneZ568uabb/Lss8/i9Xo577zz2LVrF/fffz+TJ0/m2muv5Q9/+ANnnXUWP/rRj3jyySdZuHAh69evZ/Xq1fzjH/8AzFHRr7jiCi699FI2bdrEj370I6699loAdu7cye233866des45pi9aSqjAAAgAElEQVRjePXVV/ne977HG2+8gdvt7i00IUaUppZmGlsaiQhsH9fO6XCSFDSRzysLAPDUBbFs/iImx6Th72rv7HDqwrTD9ifGjt4S1KQhi2IA5ObmDmpyAnNW3NzcXFsJ6rLLLiM21nzYb8GCBURHRzNjxgwATjvtNLZs2YLD4WD58uUsWbIEgGuuuYYNGzbw+eef43Q68Xg8XHHFFTgcDr7xjW+wfv36tv2/+OKLXHjhhcyZMweAb37zm6xbt47t27ezcOHCAX7nQgwswzAorC1hd0k2uRX7CSKci+ev7HSX4ITMGXyZU4J/YwxRAeHEByR3Sk5i7OutDWrfUAZytDIyMoakBpWRYa/DYmtyAggICOj0fWBgIPX19RQXFzNhwoS25U6nk6SkJIqKinC5XCQkJHT6g+24bkFBARs3buT5559vW+bxeCguLj6i9ybEUKj3NLCndC+6NIeqxmpKKxsor26kuaWYRcklZCbGt60bERjGqvlL8fdzMjE+DKdT2pTGm97aoC63uxOt9YaBCefIZWZm2qrZjCTx8fHs2bOn7XvDMDh06FBbYioqKsIwjLYkVVBQwMSJEwFzyvbVq1dz/fXXD0vsQthlGAYHqwvZVZJNXuUBOs5B1+Tx0tziI8ARwld5hZ0SFMDklMihDleMIH11M7fDAIY9QY1GK1eu5Le//S1btmxhwYIFbNiwAbfb3TYjrp+fHxs2bODSSy9l8+bN7Nixg0WLFgFwwQUXcOONN7J48WJmz55NQ0MDn3zyCQsWLCA0VBqMxcjwZeEudhZriqur8PoMQoPab9H5u/w5duJ09uxyEOGOJCFUxr8TnfV2i29UtUGNRhkZGfzyl7/k3nvvbevFt27durZODmvXruWOO+7gscceY/ny5Zx22mlt286aNYt7772Xe+65h3379hEYGMj8+fNZsGDBcL0dIQ6zv7yE7bkHaWjyEuB2MTklkqSwOKbFTiYjKhWX00VeTDWpCWG4XM7hDleMMP2a8n2oyZTvQowOjS1NlNaXkxLeeYq3/MpCHn7rLzh8LsIdCVy8ZDHTkmUaONHuiKZ8V0o9A9ykta6xXvdIa331QAQqhBg9DMOgpK6MncV72FOaR1VtM99Z9G0iQ4Lb1kmOSGBx8vGUHvJj6sRo4kKlTUnY11sblKOH10KIcazF20J2+T6+LtlDaV05pVUNFFc04PMZbP56B6uOW9S2rsPh4Kx5c3EtcBAo4+GJfuqtDeqq7l4LIcan6qZavi7ew+7SHJpbmtuW+7mc+HwGgY5QCgobO/U8BQgJkmeXxJHp1yWNUmoqoKxvd2utswY+JCHESFJUW8LnBTv5ujCP+iYP8ZHtt/BcThcLUqcRWAUJITHMzIjBZ4BL7rmIAWArQSmlJgDPAadgzqwLEKmU2gRcpbU+OEjxCSGGWVldFZt27qSp2YvDAZGhgcSEhDMjfgoqNpNAvwAWTvAQEugnA7SKAWW3X+dzgBuYorWO0VrHAFMxp3BfP0ixCSGGWG1zHV179qq4SQT7BwIQTDST3HO4aNY5zEmcQaCfOSNtaJC/JCcx4Oze4lsKLNRa57Qu0FrnKKVuBj4elMiEEEOidVy8Lw7t4sv8HFakn8iCjMlt5S6nizPUUj77qpI5k1I4JjNGkpEYEnYT1B4grpvlMUD2wIUjhBgqXp+XnPJ97Cjazb6yIg4U1+L1Gmxq/LxTggI4PnMqx03y4e837BMXiHHE7nxQ9wBPKqUeAv6DObzRQuBW4M5BjVAIMaAaPI3sKsliZ/EeGjyNAAS4/fD5zFt7FbUNlFTWERcZ0raN0+nA6ZTkJIaW3fmgWuvzv+9mvRewOR+UUupd4Higdcjxg1pr1fMWQoiBUt5QyacHdrJtvyYq3I2zw226QH9/pkRn4m6MY8GUdMKCA4YxUiFMwzEf1I1a698N0r6FEN1o8DTy5Pt/pbSqHsMApzOEqLBAgt3BzIyfwvTYyWD4EeDvkvYlMWKMmfmghBA9C/IPJDk0iZJKs59TY62bk2cvJiPaHLBViJGox27mSqlv2d2JUipFKXW8zdUfUEqVKqX+rZQ6ye4xhBB9q2qo5o1dW3jl8y2HlZ0yfT4RzjiOjT6Bi+acxeSYdElOYkTr7RbftUqpB4A/Aq8DX2it28Y3UUolA0uAbwMnANfaON5twNdAM3AR8JpSam7H7utCiP4xDIP86kNsL9jFh1rT0NSCv9OfFdPmEhYU1LZeWnQSPzn7fBl6SIwavd3iW6mUWgpcj9lbL0ApVQE0AZFAMLAL8yHeq7XW1X0dTGu9tcO3zymlLgbOBNYe+VsQYnxq9nrYU5rDzuIsqhqrrR5N5r8en4cPdu/izHnzO20jyUmMJr0+B6W1/hD4UCnlB8wBUoFAoAz4UmtdeJTHN5CR0oXol4LKEt7RX1DRUoQ1tyVg/iFFhwdRXRbAnCTFsWnSQVaMbrYe1NVatwDbrK8jopSKBBYB72F2M78QOBG4+Uj3KcR488Knm9mS+xU+A8JD3aTGhwHgdvmj4jKZMj2TUP9QAgNkagsx+g3lb7E/cB8wDfACu4HztNZ6CGMQYlSbFJvIv3O+AqCmrplQ/zDmTpjOlJh0/F1y+06MLUOWoLTWJcBxQ3U8IUarxuZm/r1Hs/NQHt9bcRZ+rvbOtvMmTuHVLz8izBnF8ZOO4YSpmbj9pbYkxib5zRZihKhurGFXSTYbP/0Pdc3mEESf7c1l4eT2cfH8Xf7cfsblBAe4e9qNEGOGJCghhpHH28K+ynx2l2ZTUF0EQHCwgzrrgY4tuV91SlCAJCcxbvR3Rt0AzFHNOz3gq7XeP5BBCTGWeb0+tucd4KOcndQ4iomN7Nx2FBkWQGODg9kTFCepWcMUpRDDz+6MujMwB4pd2KXIgdlVXB5HF8KmD7J38NL29wDwczmIiYgyx79zOEiNmMC02MlMPD5JRnkQ457dGtR6oARz4sJDtI9yLoToRUOTh0B356nQ56dl8tpXH+Bp8dHiNXD4AliQOh0Vm0GIO3gYoxViZLGboGYCc7TWMjmhEDbsPljI+3oHe0r3cv2J55KZmNBWFh4YyoykdAyvg6VTZqESJsoI4kJ0w26C+hiYisyeK0SPmr0ecsv3s6csh09z9lJZ2wTAB1lfdUpQAFefcJYkJSH60J9bfI8rpaYAXwGejoVa6/cHOC4hRoX6pmayiw9S6jnE3or9eH1eACLDA9oS1MGaQ4dtJ8lJiL7ZTVDPWf8/2k2ZdJIQ405hVSUvffIxORV5OP29TE6J7DSoZFiQm5lJ6SxMn87slIxhi1OI0czuWHw9zhslxHhkODxkVWbhNQxohsbmFoLcfkQHRzI1JoPJMekE+wf1vSMhRI/kQV0hemAYBvuLK9mSvYcVx8wgPiKsrSwxLJaEsCgKqsoJCwxmcuRkFk2aTkxQlNy+E2KA2E5QSqlVwC2Yg72COdjro1rrvw1GYEIMF6/PS371IV7b9hnZpfvx4cPtNlh1XPuk0Q6HgzNnnYDL4WJ6UipOh9xkEGKg2X1Q92bMkcjXAg9ZixcD65VSE7XWjw1SfEIMOsMwaPJ4qWwqJ6ssj9yK/TS1NFHvbMKHD4AvC7L5prGoU+1odnLmcIUsxLhgtwZ1M3Ct1vrPHZa9qpT6AngQkAQlRp2Gpha27N7LZ/s1NZSQnNh5jLuwYDf+fk4Sw6OZP1FhGIbcvhNiCNlNUAnA590s/wyIH7hwhBg6B6sLeUX/E8MaFyW+JQp/P/NWXYg7mMkx6VxwTBqxIdHDGKUQ45fdBPUZcJNS6gattQGglHIAP6T7xCXEiFFV28SufSXMyogjJCigbXlGzATCg4Kpqq/H6XTg9TqZlTSZKTGTSAyNk9qSEMPMboK6CfgHcKZS6lNr2QIgGDhzMAIT4mi1eFv465ZtfFmQTZ1RTq1xEqce0z46uNPpZFH6dKqb6liYrpgUnSIDtAoxgth9DupTpVQGcBmgMEcxfwv4o9a6dhDjE8I2n88Ah0FBTRHZVmeHwsYaao16ALbn7+mUoADOnrVkOEIVQthgu5u51roO+L+BOKg1ZNIO4K9a68sGYp9ifGpsbiH7QCVf7NtPjVFCaGwd9c0NbeURIQEUl9cTEuRPVISfdHQQYhTpMUEppS4HXtBaN1mve6S13tDP4z4J/Kef2whxmAPlxaz/9HWaMduRpoVF4eyQgGJDI1lw/CymxWcQERg+jJEKIfqrtxrU3ZjtTk3W654YgO0EpZS6CKgEPgIm97G6EAB4fQb5RTWkJIThcrYnoNTYWFwBHmgyn2eqb2whLjyMzOh0JsekExccLTUmIUapHhOU1npSd6+PhlIqHLgHOAW4ZiD2Kca+j3cWsCVLU9J8iAuOW8y8SeltZQF+bo6ZkM6h2iJmJWdwTOJkksMTZWQHIcYAuyNJ3Ak8rLWu77I8CLhVa32PzePdC/xea31AKdW/SMW4YhgGRXWlZJXu5YODOylsqgHg49xdnRIUwDfnnkiAy42/y3/oAxVCDBq7nSTuAtYB9V2WhwB3YtaKeqWUmgucCszrT4BifKhv9JB1oJK6llrc4dVkleVR02R2EA0JcUIF+Ps5qTaK8fl8OJ3tNaRQd8hwhS2EGES9JiilVKr10gGkKKUCOxS7MBNOsc1jnQSkA/ut2lMo4FJKzdBaz+9HzGKMyS+p5Ll3P6LKKKLFVYdKjeo0t1Kg249j0pKYkzyZqbEZnZKTEGLs6qsGlYfZCcLg8F53DqAB+LHNY/0G6DiW348xE9b1NrcXY0B33bxDQpyUOXNpafFBC9Q1NBMa5Mbt5yYjKlVGdhBinOorQU3CTES5wEKgpEOZByjSWnvtHMhqv2q7RaiUqgUatdYlPW8lxoqKmkZ255WzLS+Xc46fQWpc+/h2UcHhpEYlUFRTQlRYECo+nenxmaRGJuMnIzsIMW71mqC01vuslwN+T0VrvWag9ylGHsMwKK0v56+ffEJWaR4teHg/y8tlccs7rXfu3EU0tDSREZ1KoF9AD3sTQown/Zmw0I1Zi5oIdOoudQQP6ooxytPipabeg8vdTHb5PrLL8qhqrKber5kWPADo4lx8vmWd2pImRaf2tEshxDhlt5v5HOBVIAKzc0M5EIPZBlVMPx7UFWNTbYOH97bv5cuCHJr9y0hK6txeFBrsJjIsgPjwcOZNnIIPY+Cr5UKIMcVuDeoJ4DXgB0AVcDzQCDwH/G5wQhOjSZ2nmrfz/4XXZ0ALRDVHEOg2f738XH5MipzIWVMnMSE8QR6iFULYYjdBzQOu0Vr7lFItQKDWOlcpdRvwF+CFQYtQjBiGYVBa2cieA2XMzIghMjSorSw+LJrY0HCKqqsIdLvw+SA1MpkpMZNIi0jGz2X7brIQQgD2E1Qd0Dof9iFgCvA1ZvdzmVF3HPAZPl7asp3t+VnUGaWUtSzknPnHtZU7HA6Oz5xOcV0ps5MnkxmdJp0dhBBHxW6C+hBYgZmUXgKeUEotAc4C3huk2MQwavZ48fdzUlRbQnb5PnIr9nOosZJqow6AHQU5nRIUwIopx8mzSkKIAWM3Qd0AtN7PWYP5PNMJmJMW3jfwYYnhUN/o4cusEnYc2E+9s4yYpGbqm9tHtwoPCaCovJ7QIDcxUS483hb8O9y6k+QkhBhIdmfULe7wugX4+aBFJIZNeUMVf/n6NTw04XCAf2QUfq72Dg1hgcGsOm4aU2MnyTQWQohB19uEhSfa3YnW+v2BCUcMNsMwKKtqJOtABfNUHIHu9kfakiKiCAxy4Gkwa0ONzV5iw4JIj5rI5Jh0EkPjpAeeEGLI9FaDetfmPgzMgWPFCNfi8/LC+9vYXZxHrVFGo2sZJ884pq3c5XQxLzWTgzWHmDUhk6lx6UwIk27hQojh0duEhfKpNIoZhoGnxYfP0cL+yoPkVeaTX32IQ54aqgyzXemL/JxOCQrg7JlLcLvcMmK4EGLYycMpY0xVbRNb9+zjywO5OIKqCY9uwTCMtvLwEDellQ2EBrsJCvMcNrp4oH9gd7sVQogh158ZdXvUjxl1xSDLKdvP37PeAMDV7EBFRuHskIDiwiI5duFMMmJSSQiJlY4OQogRy24N6uQu3/sDyvr/M2zMqCsGjmEY5BWXsS0vm/MWHIefq70JcPqEiQT4+9HkaQEHNHm8pEUnkh6ZQlpkCpGB4ZKUhBCjgt1u5l0TVOvo5k8DXwx0UOJwLT4vhbXFHKgq4M3tX1FWXwVARnw88ydltK0X5B/I7JR0/Fwu5qRkkB6VQrA7qKfdCiHEiHXEbVBa62al1EOYvf2eGLCIBAA+n4+comJKGouo9JRxqKYYr8+aG9K/qW29z/fndEpQAJcdt1JqSUKIUe9oO0lMYxAmMxzv3tq5jbd2baPR20h0eAATYkM7lYeHuKmu8TAxMpFpE5IP216SkxBiLLDbSeKZLoscQCJm29Sjdg+mlHoeOAUIAQqBh7TW4266jiaPl7yCKg6V19Dka+SMY1Wncn9/F43eRgDqGlralkcEhjMxIonk8CQSj48jwN+NEEKMVXZrUF0vyX2YbU9rtdav9+N4D2BO29GklJoGvKuU+lxrva0f+xhVvF4fLmu4IJ/PR3F9GVlFB/j7th00GtUEuoI4ff7UTrWeGUkTeXk7uJ1uUsISWTxxBmlREwgLCO3pMEIIMebY7SRx1UAcTGu9s8O3hvWVCYypBOXzGXz81SHyi2sorCnjxEWRFNYVc6imGI/XgwE0O6sxvAYN3noKKitIjopu2z4uNJqbTr6AtJh4XE4ZpEMIMT71qw1KKXU6ZvdygN3A21pro5dNutvHU8CVmKOjfw70pwY24vh85tt3OjtUMh0Gm3I/pLypFC8t+HIiCA7sMOo3EB0WiMNhTvSHw9Npnw6Hg4y4pKEIXwghRiy7bVAzMOeBSgG0tXgqcFAp9a0uNaNeaa2/p5T6PuZ0HScBTb1vMTJlH6hkz/5yckuKOX1BBlNT2udtdDqcBAS34G0y248aPS1tCSo0IIQJYQmsyEhiQngCwf7SBVwIIbpjtwb1LPAlsFBrXQ2glAoHfgs8Ayzqz0G11l7gQ6XUZcD1jPBu6j6fgdPpwDAMyhsqOVRTzPvZmuzig7TgIWp/U6cEBTArJR1nQBMxoaFMjEwiOTyBCWEJhAeESS87IYSwwW6Cmg1c0ZqcALTW1UqpNZgjSRzN8TOPYvtBU1bVwBfZJeQUHSIgrImERINDtcU0tzQDUO9spgXz1lx+VeFh2y/JmMWiSTOICoyQhCSEEEfAboLaBkzHbHfqaBqw3c4OlFLxmNPG/x1oAE4FLgYusRnDoPG0ePH369wZQRfv5dXszfjwEdDkojEwslN5SKA/SbEhRIUGMzk2+bBBV8MDw4YkdiGEGKvsJqjfA08opeYC/8HsfbcQuBq4q+Pkhr1MXmhg3s5bh/lw7z7gZq31K0cY+1Fp8njZ/HkO2cUF1HlquO28szslmEkJceDwgQEejxevz8DldBDoH0hSaDxJYeZXdFCk1JCEEGIQ9CdBAdzRTVnHB217nLxQa10CLLcf2sDwen2UVjUSFeZPRVMlRbWlFNeVUlhTyrZ9+XitXniHKiqZEB3Vtl1CWAwpcRGEBQaRGZ9Mcng8SWEJREgbkhBCDAm7z0GNyuGM/vT+VrKKDlJvVJOc7CLQ3fltBAf6UVPvwemAnJKCTgnK6XBy47KLCPQLGOqwhRBCMMonLKxr8FBUXk9heQ3J8SGkJUR1Kj/QkE2ZrwyA2oZgAruM6p0UHc7M5Ggy4yYwNTblsP1LchJCiOFjO0EppVYBt2B2jACzw8SjWuu/DUZgHfl8Bg1NLQQF+GEYBrXNdRTVlvLhriy+LsinyahlUc1s0hI630FMiUzgYGUZbn8nDoeD8MBQ4kPiSAiNJSE0luigSJyOUVk5FEKIMc/ug7o3A/cBa4GHrMWLgfVKqYla68cGKT4AnnnjU9KmRpGc4qSotoQGjzmQalVLE41GLQAHq4oP225RpiI1LpKJUYkkhMYSJNOZCyHEqGG3BnUzcK3W+s8dlr2qlPoCeBAY1AR10LeT+sooPCGdu3oHBfgREuRPUICLmMjDR/aeHDuRybETBzM0IYQQg8RugkrAHDevq8+A+G6WDzjDMLsIOgB/lz/xITEkhMYSPz2W+NBYaS8SQogxxm6C+gy4SSl1Q+vgsEopB/BDuk9cA2puRjIzp0wjPiSWxNA4IoPCpe1ICCHGOLsJ6ibgH8CZSqlPrWULgGDgzMEIrKNzZ5xGSsrhveyEEEKMXbaqIVrrT4EMzAkH9wMHrNcZVpkQQggxoGx3M9da1wH/N4ixCCGEsBiG0fZ/f75at/H5fLa3P5LjHO32YM4yXlZW1uM56M9zUIuBG2mfsFADv9Zaf9SPcy6EGKP680E10Osd6b59Pt+QbHOkCWM8KC8v77HM7nNQ3wMeAf4CrLcWHwdsVkrdorV+6ihjFGJI9efDzM46o2E9u9sczf6EGEh2a1A/A76vtf5tx4VKqfeBewFJUINgID5IulvHznZDvd5QxyTEaOBwOHr86qu8t22cTqft7Y9mGztfxcWHD7LQym6CCgbe62b5+1bZoCotLcXPz29IPgz72r+d4w/ksYUYTY70g8ruekO1zUiIp7VsrGtpaemxzG6Cega4QSl1c+tzUJbrab/lN2i++OILDhw4MNiHEePMYH94jfT17K7b3+MKMVDsJqgY4JvAOUqpbZiDOhxrLX9ZKfVM64pa66sHPMpxbKA+mI60bKDXG8pj9bZex3MrhBiZ7CYoH9B11PKOt/wG9S89JiaG+Pj4I/qA6qm8r+2G6sPWTpkQQoxHdicsvOpoD6SUCsDsTHEqEA1kA7drrf/Z17Zz586VkSSEEGKcGcoB7fwwR6BYDkRgTh//olIqfQhjEEIIMUoM2Yy61kgUazos+rtSai9mW1beUMUhhBBidBi2IcGVUgnAVGDncMUghBBi5BqWBKWU8gf+CDyntd49HDEIIYQY2YY8QSmlnMAfgGbMsf2EEEKIw/TYBtXx2aa+2H32yZrk8PeYM/SeqbX22D2GEEKI8aW3ThIdH8BxYj6oWwF8apXNB6KAl/txvKeB6cCpWuuG/oUqhBBiPOkxQXV89kkp9SvM23Lf11r7rGVO4HGg54GUOlBKpQHfBZqAQqVaZ+3gu1rrPx5R9EIIIcYsu93MrwYWtSYnAK21Tym1FvgE+GFfO9Ba72OQR5wQQggxdtjtJNEELOtm+TKrTAghhBhQdmtQ9wNPKaWWY9aYDGAhcCFw6yDFJoQQYhyzVYPSWq8FTgfcwDXAdUAg8A2rTAghhBhQtoc60lq/R/eTFgohhBADrrfnoFLt7kRrvX9gwhFCCCFMvdWg8jDbmqC9953RzWsDcA1GcEIIIcav3hLUpCGLQgghhOiitwd19wEopdzAs8AdWuvcoQpMCCHE+NZnLz6tdTNwhp11hRBCiIFiN+k8izlMkRBCCDEk7HYzjwdWKaX+C9gO1HcstDuauRBCCGGX3QTlA/7a4XsZU08IIcSgspWgOo5sLoQQQgwF2yNJACilJgPTrG93aa1zBj4kIYQQwmaCUkrFAOuBs4BKa3GEUup14EqtddnghCeEEGK8stuL7ykgFpiutY7WWkcDM61lTw1WcEIIIcYvuwlqJXC91lq3LtBa7wZusMqEEEKIAWW3DaoFCOpmeRA2p3wHUErdCFwJzAL+n9b6SrvbCiGEGF/sJqi/Ac9YCWartex4YK1VZlcBcB/myBTdJTwhhBACsJ+gfgA8ArzeYZsWzBEmfmT3YFrrlwCUUguAFPthCiGEGG/sPgfVAFyvlLoVyMB8UDdHa107mMEJIYQYv3pNUEqp2zFn0f2P1rrZSkhfDklkQgghxrW+alCXAfcCTUqpT4D3ra+PtNb1vW4phBBCHIVeu5lrrWdgDhR7CfAf4HTgH0CFUupjpdRDSqmzBz9MIYQQ402fbVDWKBEbrS+UUkGYPfjOBlZjdpKwNeW7UsrPOqYLcCmlAoEWrbXtrupCCCHGB9tj8Sml4oBlwInW13TM9qgP+3G8nwF3dfj+MuBuYE0/9iGEEGIc6KuTxGWYyWgZkIT5DNSHmLWmj63efbZprdcgyUgIIYQNfdWgNgAHMJ+B+q10jBBCCDFU+kpQFwBLgf8G7ldKfYFZg/oA+LfWumKQ4xNCCDFO9ZqgtNZ/wxrKSCkVBpyAebvvx8CflVJ5wAda6+sHN0whhBDjjd3RzNFa12it38S83fcI8AyQCnxnkGITQggxjvXZi08pNYH23nvLgBlAM+ZzUU9gPrgrhBBCDKi+evHlAOlALbAF+DNm+9NWrXXzoEcnhBBi3OqrBvVrzBrS51pr3xDEI4QQQgB9d5J4dKgCEUIIITqy3UlCCCGEGEqSoIQQQoxIkqCEEEKMSJKghBBCjEiSoIQQQoxIkqCEEEKMSJKghBBCjEiSoIQQQoxIkqCEEEKMSLanfB8ISqlo4PfA6UAp8L9a6z8NZQxCCCFGh6GuQT2JORJ6AnAp8LRSauYQxyCE+P/t3XmwHFUVx/FvSFjCKggIFISwhCNGEgRZBYmIVUAVAiYiGgm7LJUCi7KoFAQSQFChVBYFo6CAYliDUVZFREUslipJ5AG/EAKoxLBkwQTCFuMf905shvfejPMyPf3i71P1Km/6zu3XJ3emz3T3nT5m/UBpCSoi1gFGA+dIWiLpQeCXpGq9ZmZm71HmEdQOwDJJswrLZgA+gjIzs/cpM0GtC7xWt+w1YL0St8HMzPqJMidJLAHWr1u2PrC4lz4DAebNm9eubTIzsw4q7N8H1reVmaBmAYMiYpikZ/KykUBXL302Bxg7dmy7t83MzDprc+DZ4oIBy5cvL+2vR8SNwHLgBGBn4C5gb0ndJqmIWBPYDfgnsKys7TQzs9IMJCWnRyW9VWwoO0FtBPwY+AwwH5jg7xdaLB0AAAipSURBVEGZmVl3Sk1QZmZmzfKtjszMrJKcoMzMrJKcoMzMrJKcoMzMrJKcoMzMrJJK+6JuRIwHjgF2AqZKOqbQdgIwAdgMeBA4TtLc3DYZOBsozo8fIWlObt+ZVMJjR+Ap4HhJj7c5nG61GmNu3wW4FNgFeB24SNJluW0o8BNgD+BvwHhJ97U/ovfrwzjeDexbWNUagCTtlNuHUpEY8/a0GueawGXA4cDqwJ+AkyW9mNsrU3KmDzF+gBTjQfnpV0qaXOg7lIqMZR6PK4EDgI2A2cBZku7O7Z8mVVkYAjwMHCPphULfq4AxwBvAxZK+U1h3j33L1McYjwC+Svpe6iOSRtWtu6P71zKPoOYCXyd9D2qFiNgPuAg4lPSf+xwwta7vTZLWLfzUktMawHTgZ8CGwHXA9Ly8E1qKMSI2Bu4BpgAfBLYHfl1YxVTgL7ntbODWiNikbVH0rqUYJR1UHEPgIeCWwiqqFCO0/no9HdgLGAFsASwCrii0V6nkTKsxfhdYGxgK7A4cFRHHFtqrNJaDgL8D+wEbAOcAN0fE0Py+m5aXbQQ8BtxU6DsZGAZsDXwKODMiDoQV79ne+papLzEuIH0w/mb9Squwfy0tQUmaJukXpC/oFh0C3CKpS9LbwAXAJyNiuyZWO4o0OJdKekvS5cAAYP+VuOlN60OMZwD3Srohx7FY0lMAEbED6ahqkqSlkm4D/koqXVK6lTGO+RP2vsBP8+NKxQh9inMb0li+JOlN4EbyHfurVnKmDzEeQjqaeEPS86RP2MdB9cZS0uuSJkt6XtK/Jd1BSri7Ap8DuiTdksdqMjAyIj6cu48DLpC0ML8ff0Q64qSJvqXpS4yS7pN0M+nDSr1RdHj/WoVrUAPyT/ExwEcLyw6JiAUR0RURpxSWDwdmSip+23gm1Svh0SjGPYEFEfFQRLwcEb+KiCG5bTgwR1LxprpVLFPSzDjWjAP+KOm5/Li/xAiN47wG+EREbBERa5OOku7Obf2l5EwzY1nfXmur9FhGxIdI49BF2qYZtTZJr5PuBTc8IjYkHQHPKHQvxtFj33ZufzOajbGJVXV8/1qFBHUXcEREjIiIwcC5pPv1rZ3bbyad/9wEOBE4NyK+mNv6SwmPRjFuCRxNOj00hPeeUllVYiwaB1xbeNxfYoTGcc4iXXd5EfgX6bV7fm7rL3E2ivEeYEJErBcR25OOnmptlY0xIlYHbgCuk/Q0vW/ruoXH9W006Nsx/2OMjXQ8xo4nKEm/BSYBtwEvAM+TSnD8I7c/KWmupGWSHiJdnB2Tu7dSwqN0jWIElgK3S3o0H4afB+wdERuw6sQIQETsQ7rwfmthcb+IEZqK8ypgLdL1l3VI5/9rR1D9Is4mYjyN9Jp9hnSNYmqhrZIxRsRqpFPKbwPj8+LetnVJ4XF9W6O+HdFCjI10PMaOJygASd+XNEzSpqQ3xSDgiR6evpz/nl7oAkZERPF0wwh6L+HREQ1inEmKq6b2+wBSLNtGRPFTS6MyJR3R5DgeDUyTtKSwrN/ECA3jHAlcK2mB0p2ZrwB2zxerV5ScKayuknH2FmOObaykzSQNJ+1HHsldKzeWef9wDWliymhJ7+SmrrxtteetA2xHumazkFRFYWRhVcU4euzbpjB61UqMTay24/vXMqeZD8p/byAwMCLWAt7Ny7YnBb0V8EPgsvwCISIOBf5Amg21G+nT21l5tQ+QynCcFhE/IJ0CBLi/hJDep9UYSVNyb4uIy/NzzgEelLQIWBQRjwOTImIiaWrvCDp00bkPMZJPF32edOF2BUmzqhQj9CnOR4FxEfEAaWryqcBcSa/m9U4Dzs/TuHcmzZTbu6y4ivrwntyO9H5cRJou/xXSDLJKjiXpqHZH4ABJSwvLbwcuiYjRwJ2kU5kz86kxgOuBiRHxGGnHfyJwbJN9y9ZSjBExkPR1iEHAavk1sCwnuAfo8P61zCOoiaTTAhOAL+ffJ5JOh/ycdDj5CPBn0g665kjSvP7FpBfMtyRdB5BnGB1GuqaxiHQu/LC8vBNailHS/aSkeyfwMmnn8KXCeo8EPg4sJE0HHSPplTbH0pNWxxHSWL0G/K6b9VYpRmg9zq8Bb5JOf70CHEz6TlTNqcBg0jhPBU5RD/XQStBqjLuSZuYtBr4BjK2LoTJjGRFbAyeRPgzMi4gl+Wds3qbRwIV5W/fI214ziTSh4AXg98Alku4BaKJvafoY41Gkcb+KNLN2KWm2YiX2ry63YWZmlVSJa1BmZmb1nKDMzKySnKDMzKySnKDMzKySnKDMzKySnKDMzKySnKDMzKySnKDM2iQi7oyI6d0snx4Rd3Rim8z6Eycos/Y5CRiVq5YCEBFjSHV2Tl7ZfyxXVjVbZfhOEmZtFBEnk4rEfYR0X7OngPMkTcn3wjufdAPd9UnVTsfXbhuU7/x+Ielmn++QqiyfJml+bq+VZb8w/40BkoZgtorwEZRZe00Bnga+DVwMiHTzVUjJaX/gC8DHgIeBe/MdpyHV4/ke6b52B5NKrBfLx0MqqncE8FngwHYFYdYJPoIya7Nc1G8GqYzKSEnP5mq784GdJM0uPHcOcKakW7tZz56kO0wPlrQ8H0FNAbaS1F3JbrN+rbRyG2b/ryTNjojb8+/P5sXDSHcNfzwiik8fDGwLEBFbku4Wvg+wMemMx5rApsBL+fkvOTnZqsoJyqwc79Y9rpUU34tU4qBofv73etJR1/HAXGAbUjn21QvPfWPlbqZZdThBmXXGk6SktZmk3/TwnL2Aw3O9MCJit7I2zqwKnKDMOkDSwoi4Erg6Ik4nXaPanFRh92pJzwBzgGMjYjbplOBZPa7QbBXkBGXWOWcArwKXkmbjzSNVG66Vjz+eNOPvCWAmKUFNK38zzTrDs/jMzKyS/D0oMzOrJCcoMzOrJCcoMzOrJCcoMzOrJCcoMzOrJCcoMzOrJCcoMzOrJCcoMzOrpP8AxGNZPcVU/dsAAAAASUVORK5CYII=\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot(census, ':', label='US Census')\n", - "plot(un, '--', label='UN DESA')\n", - "plot(results / 1000, color='gray', label='model')\n", - "decorate(xlim=[1950, 2016], xlabel='Year', \n", - " ylabel='World population (billions)',\n", - " title='Prehistoric population estimates')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap11soln.ipynb b/code/soln/chap11soln.ipynb deleted file mode 100644 index 29f610a6a..000000000 --- a/code/soln/chap11soln.ipynb +++ /dev/null @@ -1,894 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 11\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### SIR implementation\n", - "\n", - "We'll use a `State` object to represent the number (or fraction) of people in each compartment." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
S89
I1
R0
\n", - "
" - ], - "text/plain": [ - "S 89\n", - "I 1\n", - "R 0\n", - "dtype: int64" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "init = State(S=89, I=1, R=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To convert from number of people to fractions, we divide through by the total." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
S0.988889
I0.011111
R0.000000
\n", - "
" - ], - "text/plain": [ - "S 0.988889\n", - "I 0.011111\n", - "R 0.000000\n", - "dtype: float64" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "init /= sum(init)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`make_system` creates a `System` object with the given parameters." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(beta, gamma):\n", - " \"\"\"Make a system object for the SIR model.\n", - " \n", - " beta: contact rate in days\n", - " gamma: recovery rate in days\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " init = State(S=89, I=1, R=0)\n", - " init /= sum(init)\n", - "\n", - " t0 = 0\n", - " t_end = 7 * 14\n", - "\n", - " return System(init=init, t0=t0, t_end=t_end,\n", - " beta=beta, gamma=gamma)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's an example with hypothetical values for `beta` and `gamma`." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
initS 0.988889\n", - "I 0.011111\n", - "R 0.000000\n", - "dtyp...
t00
t_end98
beta0.333333
gamma0.25
\n", - "
" - ], - "text/plain": [ - "init S 0.988889\n", - "I 0.011111\n", - "R 0.000000\n", - "dtyp...\n", - "t0 0\n", - "t_end 98\n", - "beta 0.333333\n", - "gamma 0.25\n", - "dtype: object" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "tc = 3 # time between contacts in days \n", - "tr = 4 # recovery time in days\n", - "\n", - "beta = 1 / tc # contact rate in per day\n", - "gamma = 1 / tr # recovery rate in per day\n", - "\n", - "system = make_system(beta, gamma)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The update function takes the state during the current time step and returns the state during the next time step." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def update_func(state, t, system):\n", - " \"\"\"Update the SIR model.\n", - " \n", - " state: State with variables S, I, R\n", - " t: time step\n", - " system: System with beta and gamma\n", - " \n", - " returns: State object\n", - " \"\"\"\n", - " s, i, r = state\n", - "\n", - " infected = system.beta * i * s \n", - " recovered = system.gamma * i\n", - " \n", - " s -= infected\n", - " i += infected - recovered\n", - " r += recovered\n", - " \n", - " return State(S=s, I=i, R=r)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To run a single time step, we call it like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
S0.985226
I0.011996
R0.002778
\n", - "
" - ], - "text/plain": [ - "S 0.985226\n", - "I 0.011996\n", - "R 0.002778\n", - "dtype: float64" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "state = update_func(init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run a simulation by calling the update function for each time step." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \n", - " returns: State object for final state\n", - " \"\"\"\n", - " state = system.init\n", - " \n", - " for t in linrange(system.t0, system.t_end):\n", - " state = update_func(state, t, system)\n", - " \n", - " return state" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result is the state of the system at `t_end`" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
S0.520568
I0.000666
R0.478766
\n", - "
" - ], - "text/plain": [ - "S 0.520568\n", - "I 0.000666\n", - "R 0.478766\n", - "dtype: float64" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run_simulation(system, update_func)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise** Suppose the time between contacts is 4 days and the recovery time is 5 days. After 14 weeks, how many students, total, have been infected?\n", - "\n", - "Hint: what is the change in `S` between the beginning and the end of the simulation?" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.3787177442414792" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "tc = 4 # time between contacts in days \n", - "tr = 5 # recovery time in days\n", - "\n", - "beta = 1 / tc # contact rate in per day\n", - "gamma = 1 / tr # recovery rate in per day\n", - "\n", - "system = make_system(beta, gamma)\n", - "s0 = system.init.S\n", - "\n", - "final = run_simulation(system, update_func)\n", - "s_end = final.S\n", - "s0 - s_end" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using TimeSeries objects" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we want to store the state of the system at each time step, we can use one `TimeSeries` object for each state variable." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " Add three Series objects to the System: S, I, R\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \"\"\"\n", - " S = TimeSeries()\n", - " I = TimeSeries()\n", - " R = TimeSeries()\n", - "\n", - " state = system.init\n", - " t0 = system.t0\n", - " S[t0], I[t0], R[t0] = state\n", - " \n", - " for t in linrange(system.t0, system.t_end):\n", - " state = update_func(state, t, system)\n", - " S[t+1], I[t+1], R[t+1] = state\n", - " \n", - " return S, I, R" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we call it." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "tc = 3 # time between contacts in days \n", - "tr = 4 # recovery time in days\n", - "\n", - "beta = 1 / tc # contact rate in per day\n", - "gamma = 1 / tr # recovery rate in per day\n", - "\n", - "system = make_system(beta, gamma)\n", - "S, I, R = run_simulation(system, update_func)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And then we can plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_results(S, I, R):\n", - " \"\"\"Plot the results of a SIR model.\n", - " \n", - " S: TimeSeries\n", - " I: TimeSeries\n", - " R: TimeSeries\n", - " \"\"\"\n", - " plot(S, '--', label='Susceptible')\n", - " plot(I, '-', label='Infected')\n", - " plot(R, ':', label='Recovered')\n", - " decorate(xlabel='Time (days)',\n", - " ylabel='Fraction of population')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what they look like." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap05-fig01.pdf\n" - ] - }, - { - "data": { - "image/png": "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Oi4buz36b4PWDj8upw23S/6TCDGSTJ48mR//+McA7N27l29/+9vcddddlJaW9nlOSkoK9957L4888gjf+c53mD9/PrfccguTJ0+murqa8ePHx5xTWVlJbW0tCxcu7N4WCoWi3vcs1mqz2SgsLKS2tjbhn6W4uLj7dUlJCYFAgKampqhjqqqqCAaDLFu2rHtbOByOOre/yKfhAEtNdjJ7Sj6zp+TT0u5DlzcxMc4Ai3c/ruJAVStTx2cza3Ie+dnJQxCtEMPf4rLEu+DiOXfBuJhuv/4yefJkLr30Uh5//HFmzpyJ1+vt3te7VbV8+XKWL1+O1+vlF7/4BbfffjuPPfYYxcXFlJeXx1y7uLiYsWPHsm7duj7vf/jwka7PcDhMTU0NBQUFCcdfXV0d9drpdJKdnR21vaioCJfLxYYNG3A4BjaFSBffIMpMS2LxzCIKslOitnt9QfZVthAIhdm+v4HHX9H8v9d2s+dQM+GwdAUKMVzt3buXRx55pDsxVFdX8/e//505c+YwY8YM3n//faqqqmhra+PXv/5193n19fW8+uqrdHZ24nK5SElJwW635l5efvnlPPLII2zduhXTNDl48CCVlZXMnj2btLQ0HnroIbxeL6FQiF27drFly5bu627bto1169YRDAb53e9+h8vlYs6cOQDk5eVx6NCho/48zz77LHv27MHj8fDLX/6SVatWdcfVpaCggKVLl/LjH/+Y9vZ2wuEw5eXlbNy4sV/+TXuSBDUMtHsCZGe4o7ZVN3Tw0oYD/P6F7WzaWYsv0P8jZIQQJyctLY3NmzdzxRVXMHfuXK688kqmTZvGrbfeytKlS1m9ejWf+tSnuPTSSzn33HO7zwuHwzz66KMsX76cxYsX8/777/P971tTPS+66CLWrl3LzTffzPz58/na175GS0sLdrudBx98kJ07d7Jy5UrOOOMMvvvd79Le3t593ZUrV/LCCy+waNEinnnmGe677z6cTmuay5e//GUefPBBFi5cyMMPPxz351mzZk137H6/n+985ztxj/vJT35CIBBg9erVLFq0iJtuuom6urr++mftZoyWBVGVUhOB/a+++ipjx44d6nCOm2maHG7o5OO99eypiG05uZx2yiblMmdqPmkyr0oI0ct9993HwYMH4466G24qKipYuXIlQKnW+kBfx8kzqGHCMAyK81Ipzktl6ewS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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_results(S, I, R)\n", - "savefig('figs/chap05-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using a DataFrame" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Instead of making three `TimeSeries` objects, we can use one `DataFrame`.\n", - "\n", - "We have to use `row` to selects rows, rather than columns. But then Pandas does the right thing, matching up the state variables with the columns of the `DataFrame`." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \n", - " returns: TimeFrame\n", - " \"\"\"\n", - " frame = TimeFrame(columns=system.init.index)\n", - " frame.row[system.t0] = system.init\n", - " \n", - " for t in linrange(system.t0, system.t_end):\n", - " frame.row[t+1] = update_func(frame.row[t], t, system)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we run it, and what the result looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
SIR
00.9888890.0111110.000000
10.9852260.0119960.002778
20.9812870.0129360.005777
30.9770550.0139340.009011
40.9725170.0149880.012494
\n", - "
" - ], - "text/plain": [ - " S I R\n", - "0 0.988889 0.011111 0.000000\n", - "1 0.985226 0.011996 0.002778\n", - "2 0.981287 0.012936 0.005777\n", - "3 0.977055 0.013934 0.009011\n", - "4 0.972517 0.014988 0.012494" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "tc = 3 # time between contacts in days \n", - "tr = 4 # recovery time in days\n", - "\n", - "beta = 1 / tc # contact rate in per day\n", - "gamma = 1 / tr # recovery rate in per day\n", - "\n", - "system = make_system(beta, gamma)\n", - "results = run_simulation(system, update_func)\n", - "results.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can extract the results and plot them." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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Oi4buz36b4PWDj8upw23S/6TCDGSTJ48mR//+McA7N27l29/+9vcddddlJaW9nlOSkoK9957L4888gjf+c53mD9/PrfccguTJ0+murqa8ePHx5xTWVlJbW0tCxcu7N4WCoWi3vcs1mqz2SgsLKS2tjbhn6W4uLj7dUlJCYFAgKampqhjqqqqCAaDLFu2rHtbOByOOre/yKfhAEtNdjJ7Sj6zp+TT0u5DlzcxMc4Ai3c/ruJAVStTx2cza3Ie+dnJQxCtEMPf4rLEu+DiOXfBuJhuv/4yefJkLr30Uh5//HFmzpyJ1+vt3te7VbV8+XKWL1+O1+vlF7/4BbfffjuPPfYYxcXFlJeXx1y7uLiYsWPHsm7duj7vf/jwka7PcDhMTU0NBQUFCcdfXV0d9drpdJKdnR21vaioCJfLxYYNG3A4BjaFSBffIMpMS2LxzCIKslOitnt9QfZVthAIhdm+v4HHX9H8v9d2s+dQM+GwdAUKMVzt3buXRx55pDsxVFdX8/e//505c+YwY8YM3n//faqqqmhra+PXv/5193n19fW8+uqrdHZ24nK5SElJwW635l5efvnlPPLII2zduhXTNDl48CCVlZXMnj2btLQ0HnroIbxeL6FQiF27drFly5bu627bto1169YRDAb53e9+h8vlYs6cOQDk5eVx6NCho/48zz77LHv27MHj8fDLX/6SVatWdcfVpaCggKVLl/LjH/+Y9vZ2wuEw5eXlbNy4sV/+TXuSBDUMtHsCZGe4o7ZVN3Tw0oYD/P6F7WzaWYsv0P8jZIQQJyctLY3NmzdzxRVXMHfuXK688kqmTZvGrbfeytKlS1m9ejWf+tSnuPTSSzn33HO7zwuHwzz66KMsX76cxYsX8/777/P971tTPS+66CLWrl3LzTffzPz58/na175GS0sLdrudBx98kJ07d7Jy5UrOOOMMvvvd79Le3t593ZUrV/LCCy+waNEinnnmGe677z6cTmuay5e//GUefPBBFi5cyMMPPxz351mzZk137H6/n+985ztxj/vJT35CIBBg9erVLFq0iJtuuom6urr++mftZoyWBVGVUhOB/a+++ipjx44d6nCOm2maHG7o5OO99eypiG05uZx2yiblMmdqPmkyr0oI0ct9993HwYMH4466G24qKipYuXIlQKnW+kBfx8kzqGHCMAyK81Ipzktl6ewS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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_results(results.S, results.I, results.R)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Exercise** Suppose the time between contacts is 4 days and the recovery time is 5 days. Simulate this scenario for 14 weeks and plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "tc = 4 # time between contacts in days \n", - "tr = 5 # recovery time in days\n", - "\n", - "beta = 1 / tc # contact rate in per day\n", - "gamma = 1 / tr # recovery rate in per day\n", - "\n", - "system = make_system(beta, gamma)\n", - "results = run_simulation(system, update_func)\n", - "\n", - "plot_results(results.S, results.I, results.R)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap12soln.ipynb b/code/soln/chap12soln.ipynb deleted file mode 100644 index 8ca4dad69..000000000 --- a/code/soln/chap12soln.ipynb +++ /dev/null @@ -1,1633 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 12\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Code\n", - "\n", - "Here's the code from the previous notebook that we'll need." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(beta, gamma):\n", - " \"\"\"Make a system object for the SIR model.\n", - " \n", - " beta: contact rate in days\n", - " gamma: recovery rate in days\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " init = State(S=89, I=1, R=0)\n", - " init /= sum(init)\n", - "\n", - " t0 = 0\n", - " t_end = 7 * 14\n", - "\n", - " return System(init=init, t0=t0, t_end=t_end,\n", - " beta=beta, gamma=gamma)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def update_func(state, t, system):\n", - " \"\"\"Update the SIR model.\n", - " \n", - " state: State with variables S, I, R\n", - " t: time step\n", - " system: System with beta and gamma\n", - " \n", - " returns: State object\n", - " \"\"\"\n", - " s, i, r = state\n", - "\n", - " infected = system.beta * i * s \n", - " recovered = system.gamma * i\n", - " \n", - " s -= infected\n", - " i += infected - recovered\n", - " r += recovered\n", - " \n", - " return State(S=s, I=i, R=r)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \n", - " returns: TimeFrame\n", - " \"\"\"\n", - " frame = TimeFrame(columns=system.init.index)\n", - " frame.row[system.t0] = system.init\n", - " \n", - " for t in linrange(system.t0, system.t_end):\n", - " frame.row[t+1] = update_func(frame.row[t], t, system)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Metrics" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Given the results, we can compute metrics that quantify whatever we are interested in, like the total number of sick students, for example." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "def calc_total_infected(results):\n", - " \"\"\"Fraction of population infected during the simulation.\n", - " \n", - " results: DataFrame with columns S, I, R\n", - " \n", - " returns: fraction of population\n", - " \"\"\"\n", - " return get_first_value(results.S) - get_last_value(results.S)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's an example.|" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.333 0.25 0.46716293183605073\n" - ] - } - ], - "source": [ - "beta = 0.333\n", - "gamma = 0.25\n", - "system = make_system(beta, gamma)\n", - "\n", - "results = run_simulation(system, update_func)\n", - "print(beta, gamma, calc_total_infected(results))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Write functions that take a `TimeFrame` object as a parameter and compute the other metrics mentioned in the book:\n", - "\n", - "1. The fraction of students who are sick at the peak of the outbreak.\n", - "\n", - "2. The day the outbreak peaks.\n", - "\n", - "3. The fraction of students who are sick at the end of the semester.\n", - "\n", - "Note: Not all of these functions require the `System` object, but when you write a set of related functons, it is often convenient if they all take the same parameters.\n", - "\n", - "Hint: If you have a `TimeSeries` called `I`, you can compute the largest value of the series like this:\n", - "\n", - " I.max()\n", - "\n", - "And the index of the largest value like this:\n", - "\n", - " I.idxmax()\n", - "\n", - "You can read about these functions in the `Series` [documentation](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.html)." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.043536202687592354" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "def fraction_sick_at_peak(results):\n", - " return results.I.max()\n", - "\n", - "fraction_sick_at_peak(results)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "30" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "def time_of_peak(results):\n", - " return results.I.idxmax()\n", - "\n", - "time_of_peak(results)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.0006741943156034474" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "def sick_at_end(results):\n", - " return get_last_value(results.I)\n", - "\n", - "sick_at_end(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### What if?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use this model to evaluate \"what if\" scenarios. For example, this function models the effect of immunization by moving some fraction of the population from S to R before the simulation starts." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "def add_immunization(system, fraction):\n", - " \"\"\"Immunize a fraction of the population.\n", - " \n", - " Moves the given fraction from S to R.\n", - " \n", - " system: System object\n", - " fraction: number from 0 to 1\n", - " \"\"\"\n", - " system.init.S -= fraction\n", - " system.init.R += fraction" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's start again with the system we used in the previous sections." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
initS 0.988889\n", - "I 0.011111\n", - "R 0.000000\n", - "dtyp...
t00
t_end98
beta0.333333
gamma0.25
\n", - "
" - ], - "text/plain": [ - "init S 0.988889\n", - "I 0.011111\n", - "R 0.000000\n", - "dtyp...\n", - "t0 0\n", - "t_end 98\n", - "beta 0.333333\n", - "gamma 0.25\n", - "dtype: object" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "tc = 3 # time between contacts in days \n", - "tr = 4 # recovery time in days\n", - "\n", - "beta = 1 / tc # contact rate in per day\n", - "gamma = 1 / tr # recovery rate in per day\n", - "\n", - "system = make_system(beta, gamma)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And run the model without immunization." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.468320811028781" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results = run_simulation(system, update_func)\n", - "calc_total_infected(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now with 10% immunization." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.30650802853979753" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system2 = make_system(beta, gamma)\n", - "add_immunization(system2, 0.1)\n", - "results2 = run_simulation(system2, update_func)\n", - "calc_total_infected(results2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "10% immunization leads to a drop in infections of 16 percentage points.\n", - "\n", - "Here's what the time series looks like for S, with and without immunization." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap05-fig02.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results.S, '-', label='No immunization')\n", - "plot(results2.S, '--', label='10% immunization')\n", - "\n", - "decorate(xlabel='Time (days)',\n", - " ylabel='Fraction susceptible')\n", - "\n", - "savefig('figs/chap05-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can sweep through a range of values for the fraction of the population who are immunized." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.0 0.468320811028781\n", - "0.1 0.30650802853979753\n", - "0.2 0.16136545700638427\n", - "0.30000000000000004 0.0728155898425179\n", - "0.4 0.03552021675299155\n", - "0.5 0.019688715782459176\n", - "0.6000000000000001 0.011622057998337987\n", - "0.7000000000000001 0.006838737800619332\n", - "0.8 0.003696496253713877\n", - "0.9 0.0014815326722661948\n", - "1.0 -0.00016121210941239666\n" - ] - } - ], - "source": [ - "immunize_array = linspace(0, 1, 11)\n", - "for fraction in immunize_array:\n", - " system = make_system(beta, gamma)\n", - " add_immunization(system, fraction)\n", - " results = run_simulation(system, update_func)\n", - " print(fraction, calc_total_infected(results))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This function does the same thing and stores the results in a `Sweep` object." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "def sweep_immunity(immunize_array):\n", - " \"\"\"Sweeps a range of values for immunity.\n", - " \n", - " immunize_array: array of fraction immunized\n", - " \n", - " returns: Sweep object\n", - " \"\"\"\n", - " sweep = SweepSeries()\n", - " \n", - " for fraction in immunize_array:\n", - " system = make_system(beta, gamma)\n", - " add_immunization(system, fraction)\n", - " results = run_simulation(system, update_func)\n", - " sweep[fraction] = calc_total_infected(results)\n", - " \n", - " return sweep" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we run it." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
0.000.468321
0.050.387288
0.100.306508
0.150.229234
0.200.161365
0.250.108791
0.300.072816
0.350.049938
0.400.035520
0.450.026121
0.500.019689
0.550.015072
0.600.011622
0.650.008956
0.700.006839
0.750.005119
0.800.003696
0.850.002500
0.900.001482
0.950.000603
1.00-0.000161
\n", - "
" - ], - "text/plain": [ - "0.00 0.468321\n", - "0.05 0.387288\n", - "0.10 0.306508\n", - "0.15 0.229234\n", - "0.20 0.161365\n", - "0.25 0.108791\n", - "0.30 0.072816\n", - "0.35 0.049938\n", - "0.40 0.035520\n", - "0.45 0.026121\n", - "0.50 0.019689\n", - "0.55 0.015072\n", - "0.60 0.011622\n", - "0.65 0.008956\n", - "0.70 0.006839\n", - "0.75 0.005119\n", - "0.80 0.003696\n", - "0.85 0.002500\n", - "0.90 0.001482\n", - "0.95 0.000603\n", - "1.00 -0.000161\n", - "dtype: float64" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "immunize_array = linspace(0, 1, 21)\n", - "infected_sweep = sweep_immunity(immunize_array)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's what the results look like." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap05-fig03.pdf\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3Xl8XHW5+PHPzGRf2iZdknQvXZ5uUJaWvSKCiMtVvHi9IiJXf+CC3KtyBeFeNxA3FAURgat49Qq4gggqIAJKK0sBoYUuT7rv6ZYmzb7MzO+P75lkOp0kZ9pMZiZ53q9XXpn5njPnPJmZzDPf5Xy/gWg0ijHGGJNtgpkOwBhjjEnGEpQxxpisZAnKGGNMVrIEZYwxJitZgjLGGJOVLEEZY4zJSpagDAAicoWItA/RuZaLyN1H8bjPiMhOEYmIyBfSEduxEJE8EYmKyAcyHMcsL47TMxnHsRqq5zNbXjdzpLxMBzBSichPgcuTbLpEVX+ZxvNOBzYDS1V1edym+4FH03XeBO8GulN5gIhMAb4LXAX8Hjg0GIGIyF+BDap6xWAcL0tsBmqAA5kO5FioareI1AANg3XMZK93Os6TDiKyBfixqt6c4VCGjCWozFoGvD+hLOk/iYjkq2pXugJR1TagLV3HTzhX/VE8bCYQAH6vqrsHOaRhRVXDQF2m4xgMqjokf8dQnSeRiASBgPeamQQBm0kiM7wa1GRVPb+P7fcB44DHgc8CU4AS4M3ADcDxuCba14DPqerLcY8tB74G/DMwHtgF3IWrgSQmuY2qOktErgB+oKpFccf5J+BGYAEucf4GuE5VWxNifNiLaQzwNHClqu7v529fDryhqp+Iu7/ai/Mq3Benh4F/V9VWEbkZ+O+Ew0xR1R0i8jbgK8CJQL33fF0bnwRF5BLgWmA+0AK8CHwA+CFwacJxl6rqchGpBm4B3g4UASuB6+NrnSJyHnAbMBtYB1wDPEUftWARmQesAU5T1RVx5WcBy4HZqrpBRD6Oe82ne/G+DnxQVXf19ZwmnGcWsB44Q1VfiLt/CfD/gLOArcCV3u8fAUtxNa+Pqepz3nHOB570noMbgRNwr9NluNfobtzz/gZwuaqu8x6X7L00nbiae9yxz/eOfQqwEfiMqv7Fe0we7v16iar+so/3AcC9qnqFiMwEvgOcDowGNgDfVNUHvOPdR5LXG3gh/jzevpOA7wEXAIXePteo6qsJz02f8ffx2twMvM97zFdwX7wWAuXAzcDJuPfbWuALqvpn73HLca9bvNj/wBzgW8C5QBRY4cW6uq84coX1QWW3M4GzcU1ii4BOoBS4A/dPeBbun/5xEakAEJEA8CfgHbgP+3nAR4B6Ve0GTvWO/R5cM1DSfgoROQmXJJ72zv0R4CLgzoRdT/difAfwTmAx7p8lVf+K+yd9E/Ah4F+A//S2fZPemuYJXty7ROQC4HfAfbiE/V5csvht3N9xJfB/wIPASbh/4idxH7CfAp4HHvCOWQO8KCIlwN9wHxQXeo/7M/AX78MAEZkM/AGX7E4GrgO+398fqKprgZc5smn3MuA5LzmdBvwA+CoguC8kD/R33BTcjHvvnIj78P4F8FPcl5eTvLIHvMQQ72vA9bjXNuI97k7gi8ASr+zeo4zpO8BNuPfYSuDXIjKqj32/Se/rVIN7vbuBv3rby3Cv09tw74efAD8XkTd525O+3okn8f6Hfg/Mwr2vT8M1l/5FRCqPIf6YKcDHcK/7AmAH7r1/P3AOLtk9BTzqfbkA9xmwA/e/FYt9l9c0uRzYiUu2ZwCbgL+KyNgB4sh61sSXWW8Wkea4+3tUdWbc/W7gw7Eai+fB+AN431YP4r7p/cr7fTZwkqq+5u22id5/4n3e7/oBmjWuA15U1c9599eJyKeB34jIF1R1p1feDnxUVTu9eO4BPt7fH92HTXHnUhH5tfe3fFVVm0XkYCz+WNwi8iXgu6rakzRF5N+AjSKyEPdt/0bgTlX9Wty5VsXt3wm0xT8XInI5LjldEtf0cpP3rfljwOeAq4HdwMe9fdZ4Azd+N8Df+TPgKyLyWVXtFJFCXPK9wds+FWgGHlHVJq/s9QGO6ddtqvoIgIh8HXgO+I6q/t4r+xru2/csXI0w5suq+oy3z/dwH/AXqerTXtl3cR/MRaqa6kCbL8fVEj4PfBCX9J5K3FFVm3HPDSIyDbgH+Laq3udtX4lLEj1/r4i8FVdzfFZVG/t4vRM/By/AJYm5qqrePpcBW4BPAF8/mvjjFAEfivsfAvdFMN4NIvJu4GLgW6paLyJhoDkh9k8Btap6dVzZVbha7yW4Lzs5yxJUZr3I4d+mEwcOrE5ITnjNGDfiai4TcLXgEmCat8spuA/x1zg2C3A1sXh/w/UDzcd9YwNYE0tOnp1A1VGcLzHenbjaVH8WA4tF5DNJts3GNfnV4L5Vp2IJMAloFJH48kLclwFwz8GLCX0H8YNO+vILXFPrO3HJ7F241+9X3vYngG3AZhF5EvfB9ZCqDsaAh/gP79iH3KokZRM4PEGl8rhtKcYU/7rH3lP9vn+8Juw/AM8S1+QnIqXAl3DPaQ1QgHvNnkwxpgW4L4saK1DVNhF5ydt2TPEDuxKSEyIyAfd//Rbv8XlAMb3/131ZApyW8EUX77GzB3hs1rMElVltqrqhn+0tScr+RG9fzQ5cs9/zuH/GmMHqWEw8TiBJeWfCPlGOrun4aI4TxDWF/SLJtjog1tSS6vMRxPWrvC/JtthrEjiK46KqB0Tkj8CHcQnqw7jaUoO3/ZCInIKrBZ+Ha5a6RUTOHYQvHfH9j9F+yhKf91QfF6H3vRKT30dMPa+7qka9LwR9vu4iEsIl8xZc60L8a/BdXM3hP4Fab5/bcDWWVCV7bZO95inF70n2f/1zoBrXV7oZN2Dptxz+f51MEPelJtmXtMYBHpv1LEHlEBGpAuYAV6vqk17ZNNxAhZhXgAkicmIfH2ixf6jQAKdbjWsPj/cm3D/o2lRjT5NXgAV9JXkRacE1w70NeKyPY3Ry5HPxMm4QRUM/gz1WA+8XkVBcLSqxE7sv/wf80uvPejuuL6WH11f4V1w/wpcAxTXXHGuCGip7gQIRGRtX8zt5kI79PVzt9TRv5Gm8NwE/V9XfQE8ym8Phtbpkr3ei1UC1iEhcE18xrsZ+27H/CUm9CfiPuCbYctwgmZfj9unrvXoJsF1VO9IUW8ZYgsot+3HNVh8Tka24xHQLhw8PfxJXo/qtiFyD67+YBMxR1Z8Ae4BW4AIRWQd0qupBjnQL8LKIfAf4MXAccDvws8TmiQz6IvCYF+N9uP6J2bg+nY97fTw3AXeIyF5cjSWIq5nc54302wyc6TWdNuJGK/4c+DTwR69faT2u2eU84HVVfRQ3SOAzwF1ev8xkXG3Ojz8ATbiawEHcN2AAROSfcZ3oy3Cvd6y5cY23fSquyfJaL45s9AKulvAtEfkm7jU55gurvf7WK3FNeAFvpCVAq6oewiXyi0TkYdx7/HO41y0+QSV7vRP9Gffl5xcicjXutfoy7vPynmP9O/qgwIdE5HlcbfNmjqyJbQbOFndNYBvus+D7uAFMD3t9iDtw78V34C7JOGIQSC6xUXw5xPum/i/AXFwfwL3ArbhvrLF9IriRZ0/g/pnW4b6xj407xtW44bY7gZf6ONeruFF7b8H1QfwUN7LpU4P+hx0lbzjv+bhv58txcd6K+9AJe/vcjRta/QFcDeRvuE7wWH/ft3EfVCtxA0hO9/r93uTt/zNcc9GDuP69bd5xt+NGVp3p7fdd3NBwP3F34ZolTwQe8GpMMfW45/3P3nm/Adyoqj/zthfgRveN9nOuTPBqnR/ENVO+jusnum4QDv1mXHPdX3A149jPd73tn8Y1f/8V90VtM0cOWjni9U4SfxQ3ynUDrua9Avf/81Y9umv4/Lgc11/2EvAQ8Ajwj4R9voj7UlrrxT5R3TWBp+O+6PwOl+juw33Jyflr4ew6KGOMMVnJalDGGGOykiUoY4wxWckSlDHGmKw0bEbxeVfkL8F1mtrEi8YYkxtCuAurX0ocKj9sEhQuOS3LdBDGGGOOylISZmMZTglqN8D9999PdXX1QPsaY4zJAnV1dVx66aXgfYbHG04JKgxQXV3N5MmTMx2LMcaY1BzRNWODJIwxxmQlS1DGGGOykiUoY4wxWckSlDHGmKxkCcoYY0xWsgTl6Q5HWLe1nn0HE5eYMcYYkwnDaZj5MXn+9d2sXL+PvFCQfzlvNmNHF2c6JGOMGdGsBuXpDkd6fr+8dk+GozHGGGMJyrNgxtie2xt2NHKg0Zr6jDEmkyxBeSZUljCjZhQA0WjUalHGGJNhlqDiLJnfO4ef1aKMMSazLEHFSaxFvbTGalHGGJMplqASLFkQX4tqsFqUMcZkiCWoBBMqSpgxcXTP/RVWizLGmIywBJXEkvlVPbc3Wi3KGGMywhJUElaLMsaYzLME1YdT40b0WS3KGGOGniWoPoyvKLZalDHGZJAlqH4k1qL2N1gtyhhjhoolqH6MryjmuEm9taiXbHYJY4wZMpagBrBkntWijDEmEyxBDWB8RTEz42tRa+oyGI0xxowclqB8WBxfi9rZaLUoY4wZApagfLBalDHGDD1LUD7Fz3S+cWejLQ1vjDFpZgnKp3FjDq9FvbzWalHGGJNOlqBSYLUoY4wZOpagUmC1KGOMGTqWoFJktShjjBkalqBSNG5MMTMnj+m5/5LVoowxJi0sQR2FJfN614vaZLUoY4xJC0tQR8FqUcYYk355fW0QkQl+D6Kqe/3sJyKVwL3ABcB+4AZVfaCf/QuAVUCZqk72G89QOHV+FRt3NAC9tajxFcUZjsoYY4aP/mpQdcBunz9+3Ql0AlXApcBdIrKgn/2vBXwlv6E2dvThtagVNruEMcYMqv4S1DxgvvdzCbAF+AxwOnCGd3uzt21AIlIKXAx8UVWbVXU58AhwWR/7zwA+BHzDz/Ez4dT5vX1Rm3dZX5QxxgymPpv4VFVjt0XkfuAqVX0ibpcVIrIe+Drwax/nmgOEVbU2rmwlcE4f+98B/BeQtZ/6Y0cXM2vyGDZ4TX0r1tTxzrNmZDgqY4wZHvwOklgA7EhSvgNX0/KjDGhMKGsEyhN3FJH3Anmq+jufx86YJQm1qL0HWzMYjTHGDB9+E9QK4HsiMilW4N2+1dvmRzMwKqFsFNAUX+A1Bd4C/LvP42ZUrBYV89IaW3XXGGMGg98EdTmuprNFRHaIyHZcn9Qob5sftUCeiMyOK1sErE7YbzYwHVgmInXAQ0CNiNSJyHSf5xpSS+ZXEQgEAKtFGWPMYOmzDyqeqm4BzhCRk3B9SQFXrK/6PZGqtojIQ8BNInIFcCLwHuDMhF3fAKbE3T8T+AFwMrDP7/mGkqtFjWb9dtcX9dLqOt559nEZjsoYY3KbrwQVo6qvishmoFFVo0dxvquAn+CGjh8APqmqq0VkKfCYqpapajduiDsAIlIPRFQ1q8dxL55XxYYdjUSjUTbvPsTe+lYmVJZkOixjjMlZvhKUiOQDNwMfwzX1zQE2icjXgB2qepef46hqPXBRkvJluEEUyR7zVyCrLtJN5oha1BqrRRljzLHw2wf1TeA84J+B9rjyl4ArBzuoXLVkfnVvX9TuQ+ypt74oY4w5Wn4T1L/gmuOeAeKb9t4AZg16VDmqclTRYSP6VqzO6lZJY4zJan4TVCVQn6R8FBAevHByX/yIvq11Vosyxpij5TdB/Y3Dh5NHRSQIXAc8NehR5TCrRRljzODwm6A+C3xYRJ4GCoHbgHW4IeCfS1NsOevUhFpU3YGWDEdkjDG5x1eC8ubPE+AXwN24IeJ3Aid410iZOBWjipgzxWY6N8aYY+F3mPmpwCuq+qOE8pCInKqqfqc7GjEWz6+idnsD0WiUbXVN1B1ooXpsaabDMsaYnOG3ie95YGyS8jHeNpOgojyhFmV9UcYYkxK/CSrA4cPLYyYBhwYvnOFlcVxf1LY9Tezeb31RxhjjV79NfCKyFpeYosByEYkfUh7CzfDwUPrCy20V5UXI1DGs23oQcH1R73nTzAxHZYwxuWGgPqjbcLWnHwI/5vDaUhewFXg6PaEND4vnVVO7rYFINMr2PU3s2t/MxHFJZ3UyxhgTp98Epar3AHgTxD7lTeRqUjCmvJA5UytYt9Vd57xi9R4uOscSlDHGDMRvH1QxcEFioYi8Q0TeM7ghDT+L51UR9PqiduxtYte+5gxHZIwx2c9vgvoG0JmkvN3bZvoxprwQmVbRc3+FrbprjDED8pugpgMbk5RvBmYMWjTDmNWijDEmNX4T1G5gSZLy03CLD5oBjC4rZO703lrUi3ZdlDHG9Mvvirp3AXeKSDmwDDfs/Bzg68B30xTbsHPK3CrWbTlIJBpl575mdu5rZtJ4GzBhjDHJ+J2L79vATcDXcJPEqnf7a6r6zfSFN7wk1qJsdgljjOmb3xoUqnoHcIeIjAMCqrovfWENX4vnVR9Wi9qxt4nJE8ozHZYxxmQdv31QAIjICcDZQIt3v9BbF8r4NKq0gHkzKnvur1i9h2g02SxSxhgzsvlKLiIyUUReBF4CfgtUe5tuA25PU2zD1ilze0f07drfzI69NqLPGGMS+a39/ADX7zQaaIsr/zVw4WAHNdwl1qJeWlNntShjjEngN0G9GbhZVdsTyjfjZjQ3KVo8r4pgMFaLarFalDHGJPCboMK46Y4SHQccHLxwRo7ykgLmT4/vi7JalDHGxPOboB4CbhKRIu9+VESmA7fimvnMUTglrha1+4DVoowxJp7fBHUN0AHsB0pwq+huAHYA/5We0Ia/8pIC5s/oXaj4RatFGWNMjz6vgxKRAlXtBFDVFuD9IjIHWAiUAStVdeXQhDl8LZ47gbWbDxCORKk70MK2PU1Mqx6V6bCMMSbj+rtQt01EalR1r4j8CbhEVWuB2iGKbUQo82pRr2/cD7i+qKlV5T1LxRtjzEjVXxPfIWCid/tCoCD94YxMp8ydQMjri9pT38q2PU0ZjsgYYzKvvxrUg8ByEdmOmxx2uYiEk+2oqvPTEdxIUVZSwILjxrJqg9WijDEmps8EpapXiMgDuKHk/wP8GFerMmlw8twqVm9yfVF76lvZVtfEtBrrizLGjFz9Tharqk8DT4vITOBOVW09lpOJSCVwL275+P3ADar6QJL9PgP8BzAOaAZ+BVyrqt3Hcv5sVlacf1gt6oU3djO12mpRxpiRy9ds5qp6A4A3MWwlCX1Xqup30cI7cUvHVwEnAn8UkZWqujphv0eBn6pqg5fUfotLWMN67alT5laxZnM93eEI+xra2LCjgdlTKgZ+oDHGDEO+EpQ3vPwe4CwgFLcpgOufCiV7XMIxSoGLgYWq2ozr03oEuAy4Pn5fVY1fXj4ARIBZfmLNZaXF+Zwwaxz/8PL9i6vrmDlpTM/FvMYYM5L4XQ/qZ7imtrfhln8/mqtJ5wBhb6h6zErcyrxHEJEPAncD5bjmwP88inPmnJNlAqs3HaCjK0xDUwfrttYfdjGvMcaMFH4T1AnAiaq6/hjOVQY0JpQ14hLQEby+qQdEZDbwYWDPMZw7ZxQV5nGSTOCFN3YDbkTfnKkV5IVs2S1jzMji91PvZdxovmPRDCQOSxsF9HvRj5cUVwM/PMbz54xFs8dRXOi+OzS3dbF644EMR2SMMUPPbw3qbuA2EbkNeAPoit+oqit8HKMWyBOR2XE1sUW45OMnzpk+Y815+XkhFs+rYtlrOwF4ed0e5s2opCB/wK4+Y4wZNvzWoO4HBLgLWAa8EPfzvJ8DePP5xWZFLxWRs4D3AD9P3FdErhCRCd7t+cANwFM+Yx0WFh43lvISN3lHW0c3K9fvy3BExhgztPwmqOJ+fkpSON9V3mP2Ar8APqmqq0VkqYjErzVxFvC6iLQAf/J+RtSs6aFQkFPnV/fcf7V2H+0dw/YyMGOMOYLf66A6BuNkqloPXJSkfBluEEXs/kcG43y5TqZV8A/dy8Gmdjq7wryieznrhIkDP9AYY4aB/pbbuA64Q1XbvNt9UtVbBj0yQzAY4LSF1Tz+/BYAXt+wn0Wzx1NWnJ/RuIwxZij0V4N6L24Ovjbvdl+igCWoNJk5aTQTKkrYe7CV7nCEl9fU8eZTpmQ6LGOMSbv+Jos9I9ltM7QCAVeLenTZJgDWbK7nJJnA6LLCDEdmjDHpZVd/5oCpVeVMGu+66CLRKCtW12U4ImOMST9LUDkgEAhw+sKanvu12xs40NiWwYiMMSb9LEHliJpxpczw1oeKRqO88IbVoowxw5slqBxyWlwtavOuRuoOtGQwGmOMSS9LUDlk3Jjiw9aHev713USjRzOxvDHGZD+/c/EhIufglsaYwJELFl41yHGZPpy2oJqNOxqIRKPs3NfMjr3NTKlKOiG8McbkNF81KBG5ETcX3nm4ZdgrEn7MEBlTXsi8GZU9960WZYwZrvzWoD4OfFBVf53OYIw/S+ZVsW5LPeFIlL0HW9m0s5GZk8dkOixjjBlUqfRBvZa2KExKykoKOGHW+J77L66uIxKxWpQxZnjxm6C+A3xGRGxQRZY4ee6EnvWh6g+1U7vtYIYjMsaYweW3ie884Ezg3SKyliMXLHzHYAdm+ldcmMeJc8b3zCqxYk0ds6eMIWRLwxtjhgm/Ceo1rIkv65w4ezyr1u+nvbObQy2drN584LCmP2OMyWV+14O6Id2BmNQV5IdYPG8Cy1fuAuDltXuZN72S/DxbGt4Yk/t8XwcFICJnA3O9u2tV9e+DH5JJxcKZ43itdh/NbV20tnexcv1+Fs+rynRYxhhzzHwlKBGZAjwInABs8oqPE5FVwPtUdVua4jMDyAsFWTK/mmde2Q7Aq7V7WThzLEUFKX33MMaYrOO3R/0u4AAwVVXnq+p8YBpQD/wwXcEZf+ZOr2SMtz5UR2eYV3VfhiMyxphj5zdBnQt8TlX3xgpUdQ9wrbfNZFAoGODUBdU991et30dre1c/jzDGmOznN0E1A9VJyqsAm1I7C8yeMoZxY4oB6ApHeHntngxHZIwxx8ZvR8V9wE9F5HrgRa/sdODrwM/TEZhJTWxRwz8sd12Eb2xyQ87HlNvS8MaY3OS3BnUd8GPgDqDW+/k+8BPg8+kJzaRqWnU5NWNLAYhEoix7badNJGuMyVl+r4MKAzcCN4rIBCDg9UGZLBIIBFh64iR+8/R6otEoW+sOsWX3IWZMHJ3p0IwxJmUpz4ujqnstOWWvCZUlLIhbjmPZazvpDkcyGJExxhydPmtQIrIGOFtV67359/psK/KGnZsscfrCGjbsaOyZAukf6/YeNsrPGGNyQX9NfLfTO0LvdvpJUCa7FBXmccbxNT0X776ybg8yrYLRZTZgwhiTO/pMUKp6T9ztu4cmHDNY5k2vZM3mA+ypbyXsDZh419nHZTosY4zxze+S760icsQ02SJSKSKtgx+WOVbBYIA3nTSZQCAAwJbdh9i8qzHDURljjH9+B0kU9VFeOliBmMFXVVnCfBswYYzJUf0OMxeR2Dx7UeAWEWmL2xwCTgZeTlNsZhCcvrCGjfEDJnQvp863ARPGmOw30HVQFd7vADCGw2tSXcDjwD2JD+qLiFQC9wIXAPuBG1T1gST7XQtcjpuQdj/wQ1X9tt/zmF7FhXmcvrCav/5jBwCvrN2DTLUBE8aY7NdvglLVSwBEZAvwVVU91v6mO4FO3Bx+JwJ/FJGVqro6Yb8A8GFgFTAT+LOIbFfVXx7j+Uek+TPGsmZzPXsPugETy1/byTttwIQxJsv57YP6X2B6YqGIzBOR2X4OICKlwMXAF1W1WVWXA48AlyXuq6q3qOo/VLVbVRX4PXCWz1hNAjdgYlLP/c273QwTxhiTzfwmqJ/iFitMdLy3zY85QFhVa+PKVgIL+nuQiASApUBiLcukoHps6WEDJp59dYcNmDDGZDW/CeoEYEWS8pdJnriSKQMSxzk3AuUDPO4ruDj/1+d5TB9OX1hDYUEIgEMtnbzau7yXMcZkHb8JqhWYlKR8Cq5PyY9mYFRC2Sigqa8HiMjVuL6od6pqh8/zmD6UFOVz+sKanvuvrNvLoRa/L58xxgwtvwnqYeB2EZkVK/D6nm7ztvlRC+Ql9Fktoo+mOxH5KHA9cJ6q7vB5DjOABTPGMr7CLWzYHY6w7LWdGY7IGGOS85ugrgE2Ayoiu0RkJ7AW2AJ81s8BVLUFeAi4SURKReQs4D0kWfBQRC7FLYb4VlXd5DNG40MwGOCckyb33N+8q5GtNmDCGJOF/K4H1QxcLCLzcIMaAsAbqro2xfNdhVvkcC9wAPikqq4WkaXAY6pa5u13MzAWeElEYo+9T1U/keL5TBLVY0uZN72StVvqATfDxOQJZYRCKa++YowxaeN3yXcAvISUalKKf3w9cFGS8mW4QRSx+zOO9hzGnzOOr2HTrkY6OsM0NHfwau0+Fs+rynRYxhjTw3eC8vqfLsINjMiP36aqVw1yXCbNSoryOX1BDX971XXvvbx2D3OmVjCqtCDDkRljjON3NvOLgNeBtwGfAGZ5tz+ImwLJ5KAFx41l/JjeARN/X2kDJowx2cNvp8ONwDWq+lbcsPJPALOBXwLr0xSbSbPYkhwxG3c2srXOBkwYY7KD3wQ1C3jMu90OlKlqBPgeLlmZHFUzrpS50+KW5Hh1J2GbYcIYkwX8Jqi9QOxTbAtwund7Ggn9USb3nHlCDYX5boaJ2IAJY4zJNL8J6nHgnd7tu3AX7T4L/BbXzGdyWElRPqct7F0j6pW1e2hqtRkmjDGZ5StBqeonVfWr3u2fAO8CngA+BnwqfeGZobLwuHGM8wZMdIUjLF+5K8MRGWNGugGHmYtIAa7/6ROquh5AVZ8CnkpzbGYIxZbkeOiZDQBs3NHA9j1NTKkaaC5fY4xJjwFrUKraiZs9wqYZGOYmjitj7rSKnvvPvLKd9o7uDEZkjBnJ/CadO4DrRCSlmSdM7jnzhIk9AyYOtXTy5IptRKPRDEdljBmJ/Cacs72ffxKRNbjlN3qo6jsGOzCTGSVF+bxl8RQee34LAFvrDvHKur02DZIxZsj5TVCveT9mBJg5eQwnyYSeBQ1fXF1HVWWJ9UcZY4ZUnwlKRD6sosyzAAAbMUlEQVQG/MxbKPB7wD5VtbaeEeKMhTXsOdDKrv3NRKNR/vziVv71/DmUldhcfcaYodFfH9QPgdHe7d3A+PSHY7JFMBjgwjOmUVLkrsNu6+jmsee32CwTxpgh018T3zbgShH5C279p5NE5GCyHVV1RTqCM5lVUpTPhadP4+G/bSQSjbKnvpW/r9p12Px9xhiTLv0lqGuA7wNfBaL0zsWXKAqEBjkukyUmji/jjONr+Psqd+Huqg37qR5bypypFQM80hhjjk2fTXyq+rCqTgUKcTWo6UBxkp+S9IdpMunEOeOZOWl0z/1nXt7Ogca2DEZkjBkJBhzFp6pd3lLv222QxMgUCAQ4b8lUDjTW0tDcQVc4wmPPb+H9582hIN8qz8aY9PA7F59achrZCvJDvP3M6eSF3FumoamDp1/ebhfxGmPSxqYvMr6NHV3Muaf0DpDYsKOBVev3ZzAiY8xwZgnKpESmVbJw5rie+39ftYvd+1syGJExZriyBGVStnTRRKoq3diYSDTKEy9sobW9K8NRGWOGm/5mkpjg9yCq3pw4ZkQIhYJceMZ0fvVkLe2d3TS3dfHnF7fy7qUzCQYDmQ7PGDNM9FeDqsPNINHfT2wfM8KUlxTw1tOmEgi4hLRjbzMvrq7LcFTGmOGkv2Hm84YsCpOTplWPYsn8KlZ4iemVdXuoHlvCjImjB3ikMcYMrM8Epao6lIGY3LRkXhV1B1rYVtcEwF9WbOP9589hdFlhhiMzxuQ63wsQikgIOAmYAuTHb1PVXw9yXCZHBAIBLjh1Gr/6Sy1NrZ10dIV57PktvO8ts3uumTLGmKPhK0GJyALgYWASUAC0AOVAO9AAWIIawYoK87jwjOk89Mx6wpEo+xvaePbVHbxl8dRMh2aMyWGpLPn+DDAKaMPVpGYCLwBXpyc0k0uqKktYeuKknvtrNtezZvOBDEZkjMl1fhPUKcC3VbUbCANFqroZuA74VrqCM7llwXFjmTutd5bzv/1jBzv3NWcwImNMLvOboNrobQ7cjas9AXQCNYMdlMlNgUCAc06ewtjRxQCEI1EeeXYjG7Y3ZDgyY0wu8jtI4jngzcBa4PfAbSKyBHg3sNzvyUSkErgXuADYD9ygqg8k2e9c4EvAycBBVZ3u9xwms/Lzgrz9jOk89NcNtLZ3EY5EeeLFrbS0dbFoji3KbIzxz28N6mrgSe/2l4D7gTNwfVCXp3C+O3G1rirgUuAubwBGohbgJ8C1KRzbZIkx5YW87y2zqSgvAiAajbJs5U6Wr9xps58bY3zzW4PqVtUNAKraiUtSgP8pkUSkFLgYWKiqzcByEXkEuAy4Pn5fbwn5FSJyvs/4TJYZVVrAxefO4o9/38zuA24y2ddq99HS1sV5S6baEHRjzID8fkrsTpaIRGQs/qc6mgOEVbU2rmwlkKwGZYaBosI83nPOzMNW412/vYFHnt1Ee2d3BiMzxuQCvwkqACRrm6kAWn0eowxoTChrxF1PZYapvFCQt50+nUWzevufdu1v5qFnNtDU2pnByIwx2a7fJj4R+ZN3Mwr8QkTiP1FCuPn6nvV5rmbcdVTxRgFNPh9vclQwGODsEydSWpLPc6t2AVB/qJ0Hn17Pu84+jnFjijMcoTEmGw1Ug1rp/QSANXH3VwIvAjcC/+rzXLVAnojMjitbBKxOJWCTmwKBACfLBC44bVrPkhzNbV089NcNbN9j31GMMUfqtwalqjcAiMgW4Geq2n60J1LVFhF5CLhJRK4ATgTeA5yZuK+IBHFTKuUDAREpAiLeAA2Tw+ZMraCkKI/HnttCR1eYzq4wjy7fxHmLpyDTKjMdnjEmi/gaxaeq9wCIyNnAXK94rar+PcXzXYUbPr4XOAB8UlVXi8hS4DFVLfP2exNuaqWYNuBvuGuxTI6bPKGcfz53Fo8u20RzWxeRSJQnV2yjua2Lk2VCzxpTxpiRLeDnuhQRmQI8CJwAbPKKjwNWAe9T1W1pi9AnEZkObH7qqaeYPHlypsMxPjS3dvLosk0cONRbMT9+5jiWnjjJVuY1ZoTYsWMH5513HsAMVd0Sv83vKL67cDWeqao6X1XnA9OAeuCHgxirGUHKSgp477mzmDS+rKfs9Y37efyFLXSHIxmMzBiTDfwmqHOBz6nq3liBqu7BzfRwbjoCMyNDUUEe7156HLOn9E4yu2lnIw//bSNtHXatlDEjmd8E1QxUJymvwk1LZMxRC4WCXHDaVE6Kuxa87kALDz6znsbmjgxGZozJJL9THd0H/FRErscNLwc4Hfg68PN0BGZGlkAgwFknTKSsOJ/lK3cRjUZpaOrg13+p5ZR5VSyaNY6QTY9kzIjiN0FdBxzCLVw4xitrAL4P3JyGuMwItWj2eEqL83nyxa2EI1E6usI8t2oXb2zcz5knTGTmpNE2ys+YEWKgmSSmAttVNYy7KPdGb06+gNcHZcygmzV5DKVF+Tz18jYamlwT36GWTh5/fgsTx5Vy1qJJVFWWZDZIY0zaDdRmshk4bBEfVd1rycmkW824Ui55q7D0xEkUFoR6ynftb+E3T9Xy5Itbaba5/IwZ1gZq4rO2FJMxoVCQRbPHI1MreHndHlat30/Eu25Ptx1k485GTpoznpPnTiA/LzTA0YwxucZPr7OtMGcyqqgwj7MXTeKDb5t72NId3eEIL63dw88fW8eazQeIROytasxw4meQxO8SZjE/gqq+ZZDiMaZPY8oLefuZM9i5r5nlr+1kX0MbAK3tXTz98nZe3+AGUkypshVcjBkO/CSo53HXQRmTFSaNL+P9589Btx7khTd209zWBcC+hjZ+/+xGZkwczZkn1PQsOW+MyU1+EtS342eQMCYbBAIB5k6vZObk0bxau49X1+2ly5seafOuRrbuPsTxM8exZH4VRYV+r6YwxmSTgf5zrVHfZLX8vBCnzq9m/oyxvPD6btZtrQcgEo2ycsM+1m2rZ970SuZMrWD8mGK7hsqYHGKj+MywUFacz/mnTuWEWeP4+6pd7NznWqU7OsO8VruP12r3UVFehEyrYPaUMYwuK8xwxMaYgQy0YKHNLWNyyoTKEi46Zyabdx3iuVW7aIiby+9gUzsvvLGbF97YTfXYUmRqBTMnj6akKD+DERtj+mKN82bYCQQCHDdpNNNrRrF9bxO1Ww+yaVcjXd29S3jUHWih7kALy17byZSqcmRaBTMmjrLrqYzJIpagzLAVDAaYVj2KadWj6OoOs3nXIWq3HWRbXVPPBb+RaJStdYfYWneI/FCQGZNGI1MrmFxVTsgWTTQmoyxBmREhPy/EnKkVzJlaQVtHNxt2NFC79SC7D/SuFtMVjlC77SC12w5SXJjHrMljkGkVVFWW2OAKYzLAEpQZcYoL8zh+5jiOnzmOxuYO1m9voHbbQerjlp5v6+jm9Y37eX3jfsqK85k4voyasaXUjCulclSRLUlvzBCwBGVGtNFlhSyeV8Upcyewv6Gd2u0HWb/tYM/FvwDNbV09NSuAgvwQ1WNLehJWVWWJ9V0ZkwaWoIzBDawYX1HM+IpizlhYw679zdRuO8jGHY10dIUP27ezK8y2uia21TUBEAwEGDemmJpxLmHVjC2ltNhGBhpzrCxBGZMgGAwweUI5kyeUc87JUQ40tLF7fwu7DrRQt7+Flvauw/aPRKPsPdjK3oOtrFy/D4BRpQU9NayacaVUlFuzoDGpsgRlTD9CwQATKkuYUFnCIsYTjUY51NLJbi9Z7d7fQn1TB9Ho4ZOuHGrp5FBLJ+o1C4aCAcaUFVIxqojKUUWMKS+kotz9zs+zyw2NScYSlDEpCAQCjC4rZHRZIXOnVQLQ3tnNngOt7Nrvrq3aU99Kdzhy2OPCkSgHDrVzIG4gRux45SX5jCkvpHJUERXlRVSUu0RWbHMImhHO/gOMOUZFBXlMqxnFtJpRAITDEfY1tFF3wNWw9tS3HjboIl6sRnaopbOnTyv+uJWjChnjJa3ykgLKSvIpKymgpDDPmgzNsGcJyphBFgoFqR5bSvXYUk6c48raO7tpaOrg4KEODja1c7Cpg4OH2mls6TyieTCmvbObXfu72bW/5YhtwUCA0uJ8ykvyKS0uoLwkn7KSfMpLCigtzqesOJ/iwjy7fsvkNEtQxgyBooI8qsfmUT229LDycDhCQ3MHB5s6vATWTn1TOw2HOnqWD0kmEo3S1NpJU2sncGQCA8gLBSkrdomrrNglspLCPIqL8igu7P0pKsyzWTNMVrIEZUwGhUJBxo4uZuzo4sPKo9EozW1dHDzUzsFDHTQ0d9Dc1kVzayfNbV20dXQPeOxuL/nFT5jbl8KCEMWFeZQU5lNc6N0uyqco/nZBiKKCPAoLQuSFbGCHST9LUMZkITd4ooDykgKmVh+5vTscobm1i6bWTlraumhuc7ebW10Sa2rrojPh+q3+dHSG6egM09A0cDIDVzsrzA9RWBA64ndRQV5vWZLteaGgNT0aXyxBGZOD8kJBxpQXMqa873WtOrvCLmm1ddHc2kVru6t59fy0d9Pa0U17Z7jPfrC+dIcjdIcjR1wT5kcgECA/L0hBXpCC/BD5eUHy80IU5gd7bsfKC/K9fULud6w8LxTbN0goGLCEN0xZgjJmmCrIDyVtPkwUiURp73RJq70z3JvI2rsPS2itHd09Na1IigktXjQapbMr7Gp4fYxuTEUgECAvFOhNWqEgeV6iyw8FvNvxSS1EKOj2zwsFCYUCPYkuvuyw7aEgQUuEQ25IE5SIVAL3AhcA+4EbVPWBJPsFgG8CV3hF9wKfV1Vbgt6YQRYMBigpyve9cGM0GqU7HHHJqivc87u9I0xHV/cR5T3bO11SSrxG7FhFo1G6uqN0dUdo89dCeVQCgQB5wQChUJC8kPsdCgYIhQKEgq4s6CW5UNCVhUIB8oJBgqHex8YSYdB7bDAQ8I4Te5w7TigYJBik5zi95W5bIMCwT5hDXYO6E+gEqoATgT+KyEpVXZ2w38eAi4BFQBR4EtgE3D2EsRpjknBNdCHy80KUHcXjI5Eond1hurojdHb1/u7sjtDVFTlsmyvzfneH6eyK0OXd7g5H6eoOE44MzffWaDRKVzja7+jKoRQIBAgG3ECbYMAlr9j9WDKLlcffj0+Kwfj9YscI0nM7EHt8IGHfID23q8aWUpamuSeHLEGJSClwMbBQVZuB5SLyCHAZcH3C7pcDt6rqDu+xtwJXYgnKmJwXDAYoKsijqGBwjheJuBpdV3ekz99d3RG6uyN0effDEXe/OxztvR2Jer8jhMNRr58t2tPfFhmiROhXNBolHIVwxP9gmHQIBgJ84AKhclTRoB97KGtQc4CwqtbGla0Ezkmy7wJvW/x+C9IYmzEmRwWDAQqCbgBFOkUiLpm5BBcl7CW3cDjak9Rcee/27kiESGx73LbucJSI95hINHbfPT4S2zcS7Um+kbj7sW2pDmxJl0g0SmNzR84nqDKgMaGsESj3sW8jUCYiAeuHMsZkgmviCmXN2l+RiEtu8UkrEvuJRnuSXyyxxie5SPTI/d3vw4/b5+24/arHljKtelRa/sahTFDNQOJfMQpo8rHvKKDZkpMxxjjBYIAgAciOfJkWQ3k5eC2QJyKz48oWAYkDJPDKFvnYzxhjzDA1ZDUoVW0RkYeAm0TkCtwovvcAZybZ/f+Aa0TkT7hRfP8J3DFUsRpjjMm8oZ5Q6yqgGNgL/AL4pKquFpGlItIct989wKPA68AbwB+9MmOMMSPEkF4Hpar1uOubEsuXQe8lFV5f03XejzHGmBHIpiQ2xhiTlSxBGWOMyUrDabLYEEBdXV2m4zDGGONT3Gf2EQPmh1OCqgG49NJLMx2HMcaY1NUAG+MLhlOCeglYCuwGMjs5lTHGGL9CuOT0UuKGQLbM52SMMcbEs0ESxhhjspIlKGOMMVnJEpQxxpisZAnKGGNMVrIEZYwxJitZgjLGGJOVhtN1UL6ISCVwL3ABsB+4QVUfSLJfAPgmcIVXdC/w+eG0aGIKz8W1wOXANG+/H6rqt4cy1nTy+zzE7V8ArALKVHXy0EQ5NFJ5LkTkZOA24GSgBfi6qt4+VLGmWwr/H4XA7cB7gXzg78AnVHXnEIabNiJyNfBvwPHAL1T13/rZ97PA53GrVjyIW7Gi42jPPRJrUHcCnUAVcClwl4gsSLLfx3Azry8CTgDeBXx8qIIcIn6fiwDwYaACuBC4WkQ+MGRRpp/f5yHmWtySMcORr+dCRMYBj+OWwRkLzAL+PIRxDgW/74tPA2fgPicmAg0Mr/XrdgE3Az/pbycReRtwPXAeMB04DrjxWE48ompQIlIKXAwsVNVmYLmIPAJchnti410O3KqqO7zH3gpcCdw9hCGnTSrPhareEn9XRH4PnAX8cqjiTZcU3xOIyAzgQ8A1wI+GMtZ0S/G5uAZ4QlXv9+53AGuHLNg0S/G5mIF7LvZ4j/0l8N2hjDedVPUhABFZDPTXYnA5cK+qrvb2/ypwP0n+j/waaTWoOUBYVWvjylYCyb4VLfC2DbRfrkrluejhNX0uBVanMbahlOrzcAfwX0BbugPLgFSei9OBehF5TkT2isijIjJ1SKIcGqk8F/cCZ4nIRBEpwdW2HhuCGLNNss/MKhEZe7QHHGkJqgxoTChrBMp97NsIlHkf0MNBKs9FvK/g3jf/m4aYMsH38yAi7wXyVPV3QxFYBqTynpiM+8b8aWAqsBm3SvZwkcpzUQtsA3YCh4B5wE1pjS47JfvMhIE/U/o00hJUMzAqoWwU0ORj31FA8zAaJJHKcwH0dJZ+GHjnsXR8Zhlfz4PX5HML8O9DFFcmpPKeaAN+p6ovqWo7rq/hTBEZneYYh0oqz8VdQBGuL64UeIiRWYNK9pkJ/XymDGSkJahaIE9EZseVLSJ5c9Vqb9tA++WqVJ4LROSjeB2gsX65YcLv8zAb1/G7TETqcB9CNSJSJyLThyLQIZDKe2IVEP9lLXZ7uLQwpPJcLAJ+qqr13he3O4BTvYEkI0myz8w9qnrgaA844mYz9zowo7jh4ycCfwLOjHXsxe33CVzzxfne/k8Cd6jqsBgkASk9F5cCtwLnquqw6QiP8fM8iEgeEP+BcybwA9wQ632qOiyWeEnhPfEW3DDic3EfTLcAi1V16dBGnD4pPBf/i6stfBRoxY3y/JSqThraiNPDe+/nAV/GNe1eCXSranfCfhcCPwXeglv26EFgharaIIkUXIUbo78X12b+SVVdLSJLRaQ5br97gEeB14E3gD96ZcOJ3+fiZlzzxUsi0uz9DJtEjY/nQVW7VbUu9gPUAxHv/rBITh5f7wlVfRo3WOSP3r6zgA9mIN508vv/8TmgHVgP7APegbsmarj4Aq5J93rcCNY24AsiMtX7LJgKoKqP476oPANs9X6+fCwnHnE1KGOMMblhJNagjDHG5ABLUMYYY7KSJShjjDFZyRKUMcaYrGQJyhhjTFayBGWMMSYrWYIyxgcRmSUiURFJ2/pPInKziPwlXcdPFxHJ856bs9N0/PNFpHvgPc1wM6KW2zC5SUT+CpyTUPyEql6YpvNdAVyvqrPiijcDNaR3Hahv4ha8yymq2i0iNcBRT2ljTDKWoEyuuBX4Ttz9pJPVikiBqnYO9sm92SLqBvu4CedoHniv7OTNrmHMoLIEZXJFc7IPQRE5H7ey67txi8TN9pbg/ghu5vFZuFrPz4Cb4qclEpFrgKtxq6BuxU3pchBvIUIRiU2zshSXnNYDU+IWsfww8CVgCm6xvmu8KYDi47oQN2ffRNyKsx9V1UPJ/kARuRk4XVXP9+4vB/7mHf9i3JIOl+Hmh/uR97f9HrgiNru8iOzAJfLzcXOirQbej1vf6PvABNzKqJ9T1aiIzEryd50PPK6qefFx4eaiuw7XNfAjVf1vb3se0AUsVdXlXtxnJfx5G2M1Um/hu1uBU73n9V7gG7HXRkQWeTEuAFYA/5fs+TLDn/VBmeEgCNyAW59oERDGzar9WdyH3FXAx4D/F3uAiHwSl5C+6O1zNW4+tWeB/wS24Jr0aoAXE0/o9bf8GPdBewJuTro/iMjEhLhi85edDywBPp/i33YVsBw4CZcEfwZ8C/gULvm9Hfi3hMdch5u082Rc4ngA+AwuUV3q/a0XpBjHqbh1jt7sxfR5EXlrH/u+m97nbipu5vO/A4jIeOAJ4GHgeNwEqx/xYoolu9/hkubJwA9xc0GaEchqUCZX/JeIfC7u/gdU9Q/e7QCuRhCfSP4n7vZmEbkdeF9c+ReAL8QtWb4xtrOIHMKtploXV5YYz6eBX6rqXd79/xaRtwGfwNWqYnFdo6qrvGPcS+qJ4SlV/R/v8bfiktV1qvq8V/Y74E0cPpHxA6r6W2/7ncB9uKXLVwMrReRZ7zFPpBBHK3CVqnYB67xjnIOb5f8wqlofu+097wHgk17RvwOPqer3vPsbRORGXFK9HZdwxwNXqmoTsEZETsYtMW9GGEtQJlfcjWuiitmdsP21+DsiciZuEb0FuKUQ8nADHRCRClyT27PHEM9c3EJ18Z73ymOiHL5+0G5cE1sq4h8fG6CxJqFsyQCPiQLrEsrGpxiHeskpZsC/RUQ+gFvgcomqtnrFxwPvEJGL4nYN0buelABrveQU80KKsZphwhKUyRX1qrqhj23h+BV+RWQUrsntPnr7lT6Ia2qD3kX1jmUqfz8L80USluKIknqzenxSiPZRlnjMxO39xRHxfsf/PclGEnYl3O/3bxGRBbja6qUJr1sZrk/pW0mOF4sj8XUZLosgmhRZgjLD0XxgDHCttxw5IjIltlFV60VkN66JKtkKqV24b/X9WYcbOPDDuLIzcAMJcsk+73c1sN27ffyxHND7gvAQcJuqPpqweSVwRj9fNhSYJyJlcaMaTz2WeEzusgRlhqOtQDfwHyLyIK7f5yJcTSrm68DNItKIa5qbBpR5H6hbgYkichLuQ7shyTluB54SkeeAp3EDNBYA70nPn5QeqtokIq/h+tCuww2E+MQxHvZHuGui7haRaq+sW1X340Y0Xun1jd0DdOIGgExV1W/hEnw98D8i8lVcsvzwMcZjcpSN4jPDjqruxn3I/gduBNl5uItg492JW/3zG7jRcXcBBd62Z4Ff4VYG3YerKSWeYxnwcdzy3m8A7wL+SVV3DvKfMxQ+ghvKvhI3+OOmYzzeWbja5E5cX9VuvH4kVd2CG6AxC3gON0Ly07gh9HjLiL8X15f3mrftS5gRyVbUNcYYk5WsBmWMMSYrWYIyxhiTlSxBGWOMyUqWoIwxxmQlS1DGGGOykiUoY4wxWckSlDHGmKxkCcoYY0xW+v88hIvCto1dPAAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(infected_sweep)\n", - "\n", - "decorate(xlabel='Fraction immunized',\n", - " ylabel='Total fraction infected',\n", - " title='Fraction infected vs. immunization rate',\n", - " legend=False)\n", - "\n", - "savefig('figs/chap05-fig03.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If 40% of the population is immunized, less than 4% of the population gets sick." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Logistic function" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To model the effect of a hand-washing campaign, I'll use a [generalized logistic function](https://en.wikipedia.org/wiki/Generalised_logistic_function) (GLF), which is a convenient function for modeling curves that have a generally sigmoid shape. The parameters of the GLF correspond to various features of the curve in a way that makes it easy to find a function that has the shape you want, based on data or background information about the scenario." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "def logistic(x, A=0, B=1, C=1, M=0, K=1, Q=1, nu=1):\n", - " \"\"\"Computes the generalize logistic function.\n", - " \n", - " A: controls the lower bound\n", - " B: controls the steepness of the transition \n", - " C: not all that useful, AFAIK\n", - " M: controls the location of the transition\n", - " K: controls the upper bound\n", - " Q: shift the transition left or right\n", - " nu: affects the symmetry of the transition\n", - " \n", - " returns: float or array\n", - " \"\"\"\n", - " exponent = -B * (x - M)\n", - " denom = C + Q * exp(exponent)\n", - " return A + (K-A) / denom ** (1/nu)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following array represents the range of possible spending." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 0., 60., 120., 180., 240., 300., 360., 420., 480.,\n", - " 540., 600., 660., 720., 780., 840., 900., 960., 1020.,\n", - " 1080., 1140., 1200.])" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "spending = linspace(0, 1200, 21)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`compute_factor` computes the reduction in `beta` for a given level of campaign spending.\n", - "\n", - "`M` is chosen so the transition happens around \\$500.\n", - "\n", - "`K` is the maximum reduction in `beta`, 20%.\n", - "\n", - "`B` is chosen by trial and error to yield a curve that seems feasible." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "def compute_factor(spending):\n", - " \"\"\"Reduction factor as a function of spending.\n", - " \n", - " spending: dollars from 0 to 1200\n", - " \n", - " returns: fractional reduction in beta\n", - " \"\"\"\n", - " return logistic(spending, M=500, K=0.2, B=0.01)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what it looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap05-fig04.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "percent_reduction = compute_factor(spending) * 100\n", - "\n", - "plot(spending, percent_reduction)\n", - "\n", - "decorate(xlabel='Hand-washing campaign spending (USD)',\n", - " ylabel='Percent reduction in infection rate',\n", - " title='Effect of hand washing on infection rate',\n", - " legend=False)\n", - "\n", - "savefig('figs/chap05-fig04.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Modify the parameters `M`, `K`, and `B`, and see what effect they have on the shape of the curve. Read about the [generalized logistic function on Wikipedia](https://en.wikipedia.org/wiki/Generalised_logistic_function). Modify the other parameters and see what effect they have." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Hand washing" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can model the effect of a hand-washing campaign by modifying `beta`" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "def add_hand_washing(system, spending):\n", - " \"\"\"Modifies system to model the effect of hand washing.\n", - " \n", - " system: System object\n", - " spending: campaign spending in USD\n", - " \"\"\"\n", - " factor = compute_factor(spending)\n", - " system.beta *= (1 - factor)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's start with the same values of `beta` and `gamma` we've been using." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(0.3333333333333333, 0.25)" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "tc = 3 # time between contacts in days \n", - "tr = 4 # recovery time in days\n", - "\n", - "beta = 1 / tc # contact rate in per day\n", - "gamma = 1 / tr # recovery rate in per day\n", - "\n", - "beta, gamma" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can sweep different levels of campaign spending." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.0 0.3328871432717143 0.4667702312363652\n", - "100.0 0.3321342526691939 0.46414165040064037\n", - "200.0 0.33017160845482885 0.4572170063132055\n", - "300.0 0.32538647186519215 0.4398872029120663\n", - "400.0 0.3154039052420003 0.40163064627138245\n", - "500.0 0.3 0.3370342594898199\n", - "600.0 0.28459609475799963 0.26731703056804546\n", - "700.0 0.2746135281348078 0.22184699045990752\n", - "800.0 0.26982839154517113 0.20079159841614402\n", - "900.0 0.2678657473308061 0.1923921833925878\n", - "1000.0 0.26711285672828566 0.18921320781833872\n", - "1100.0 0.26683150821044227 0.18803175228016467\n", - "1200.0 0.26672740341296003 0.1875955039953746\n" - ] - } - ], - "source": [ - "spending_array = linspace(0, 1200, 13)\n", - "\n", - "for spending in spending_array:\n", - " system = make_system(beta, gamma)\n", - " add_hand_washing(system, spending)\n", - " results = run_simulation(system, update_func)\n", - " print(spending, system.beta, calc_total_infected(results))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a function that sweeps a range of spending and stores the results in a `SweepSeries`." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "def sweep_hand_washing(spending_array):\n", - " \"\"\"Run simulations with a range of spending.\n", - " \n", - " spending_array: array of dollars from 0 to 1200\n", - " \n", - " returns: Sweep object\n", - " \"\"\"\n", - " sweep = SweepSeries()\n", - " \n", - " for spending in spending_array:\n", - " system = make_system(beta, gamma)\n", - " add_hand_washing(system, spending)\n", - " results = run_simulation(system, update_func)\n", - " sweep[spending] = calc_total_infected(results)\n", - " \n", - " return sweep" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we run it." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
0.0000000.466770
63.1578950.465418
126.3157890.462905
189.4736840.458291
252.6315790.449980
315.7894740.435540
378.9473680.411960
442.1052630.377183
505.2631580.333171
568.4210530.287633
631.5789470.249745
694.7368420.223529
757.8947370.207480
821.0526320.198306
884.2105260.193244
947.3684210.190500
1010.5263160.189027
1073.6842110.188239
1136.8421050.187819
1200.0000000.187596
\n", - "
" - ], - "text/plain": [ - "0.000000 0.466770\n", - "63.157895 0.465418\n", - "126.315789 0.462905\n", - "189.473684 0.458291\n", - "252.631579 0.449980\n", - "315.789474 0.435540\n", - "378.947368 0.411960\n", - "442.105263 0.377183\n", - "505.263158 0.333171\n", - "568.421053 0.287633\n", - "631.578947 0.249745\n", - "694.736842 0.223529\n", - "757.894737 0.207480\n", - "821.052632 0.198306\n", - "884.210526 0.193244\n", - "947.368421 0.190500\n", - "1010.526316 0.189027\n", - "1073.684211 0.188239\n", - "1136.842105 0.187819\n", - "1200.000000 0.187596\n", - "dtype: float64" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "spending_array = linspace(0, 1200, 20)\n", - "infected_sweep = sweep_hand_washing(spending_array)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's what it looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap05-fig05.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(infected_sweep)\n", - "\n", - "decorate(xlabel='Hand-washing campaign spending (USD)',\n", - " ylabel='Total fraction infected',\n", - " title='Effect of hand washing on total infections',\n", - " legend=False)\n", - "\n", - "savefig('figs/chap05-fig05.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's put it all together to make some public health spending decisions." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Optimization" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Suppose we have \\$1200 to spend on any combination of vaccines and a hand-washing campaign." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "12" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "num_students = 90\n", - "budget = 1200\n", - "price_per_dose = 100\n", - "max_doses = int(budget / price_per_dose)\n", - "dose_array = linrange(max_doses, endpoint=True)\n", - "max_doses" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can sweep through a range of doses from, 0 to `max_doses`, model the effects of immunization and the hand-washing campaign, and run simulations.\n", - "\n", - "For each scenario, we compute the fraction of students who get sick." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.0 0.9888888888888889 0.26672740341296003 0.1875955039953746\n", - "1.0 0.9777777777777779 0.26683150821044227 0.1875955039953746\n", - "2.0 0.9666666666666667 0.26711285672828566 0.1875955039953746\n", - "3.0 0.9555555555555556 0.2678657473308061 0.1875955039953746\n", - "4.0 0.9444444444444445 0.26982839154517113 0.1875955039953746\n", - "5.0 0.9333333333333333 0.2746135281348078 0.1875955039953746\n", - "6.0 0.9222222222222223 0.28459609475799963 0.1875955039953746\n", - "7.0 0.9111111111111112 0.3 0.1875955039953746\n", - "8.0 0.9 0.3154039052420003 0.1875955039953746\n", - "9.0 0.888888888888889 0.32538647186519215 0.1875955039953746\n", - "10.0 0.8777777777777778 0.33017160845482885 0.1875955039953746\n", - "11.0 0.8666666666666667 0.3321342526691939 0.1875955039953746\n", - "12.0 0.8555555555555556 0.3328871432717143 0.1875955039953746\n" - ] - } - ], - "source": [ - "for doses in dose_array:\n", - " fraction = doses / num_students\n", - " spending = budget - doses * price_per_dose\n", - " \n", - " system = make_system(beta, gamma)\n", - " add_immunization(system, fraction)\n", - " add_hand_washing(system, spending)\n", - " \n", - " results, run_simulation(system, update_func)\n", - " print(doses, system.init.S, system.beta, calc_total_infected(results))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function wraps that loop and stores the results in a `Sweep` object." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "def sweep_doses(dose_array):\n", - " \"\"\"Runs simulations with different doses and campaign spending.\n", - " \n", - " dose_array: range of values for number of vaccinations\n", - " \n", - " return: Sweep object with total number of infections \n", - " \"\"\"\n", - " sweep = SweepSeries()\n", - " \n", - " for doses in dose_array:\n", - " fraction = doses / num_students\n", - " spending = budget - doses * price_per_dose\n", - " \n", - " system = make_system(beta, gamma)\n", - " add_immunization(system, fraction)\n", - " add_hand_washing(system, spending)\n", - " \n", - " results = run_simulation(system, update_func)\n", - " sweep[doses] = calc_total_infected(results)\n", - "\n", - " return sweep" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can compute the number of infected students for each possible allocation of the budget." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
00.187596
10.174581
20.162910
30.153508
40.148565
50.152946
60.174964
70.217343
80.259071
90.278403
100.277915
110.267357
120.252797
\n", - "
" - ], - "text/plain": [ - "0 0.187596\n", - "1 0.174581\n", - "2 0.162910\n", - "3 0.153508\n", - "4 0.148565\n", - "5 0.152946\n", - "6 0.174964\n", - "7 0.217343\n", - "8 0.259071\n", - "9 0.278403\n", - "10 0.277915\n", - "11 0.267357\n", - "12 0.252797\n", - "dtype: float64" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "infected_sweep = sweep_doses(dose_array)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap05-fig06.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(infected_sweep)\n", - "\n", - "decorate(xlabel='Doses of vaccine',\n", - " ylabel='Total fraction infected',\n", - " title='Total infections vs. doses',\n", - " legend=False)\n", - "\n", - "savefig('figs/chap05-fig06.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises\n", - "\n", - "**Exercise:** Suppose the price of the vaccine drops to $50 per dose. How does that affect the optimal allocation of the spending?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Suppose we have the option to quarantine infected students. For example, a student who feels ill might be moved to an infirmary, or a private dorm room, until they are no longer infectious.\n", - "\n", - "How might you incorporate the effect of quarantine in the SIR model?" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "\"\"\"There is no unique best answer to this question,\n", - "but one simple option is to model quarantine as an\n", - "effective reduction in gamma, on the assumption that\n", - "quarantine reduces the number of infectious contacts\n", - "per infected student.\n", - "\n", - "Another option would be to add a fourth compartment\n", - "to the model to track the fraction of the population\n", - "in quarantine at each point in time. This approach\n", - "would be more complex, and it is not obvious that it\n", - "is substantially better.\n", - "\n", - "The following function could be used, like \n", - "add_immunization and add_hand_washing, to adjust the\n", - "parameters in order to model various interventions.\n", - "\n", - "In this example, `high` is the highest duration of\n", - "the infection period, with no quarantine. `low` is\n", - "the lowest duration, on the assumption that it takes\n", - "some time to identify infectious students.\n", - "\n", - "`fraction` is the fraction of infected students who \n", - "are quarantined as soon as they are identified.\n", - "\"\"\"\n", - "\n", - "def add_quarantine(system, fraction):\n", - " \"\"\"Model the effect of quarantine by adjusting gamma.\n", - " \n", - " system: System object\n", - " fraction: fraction of students quarantined\n", - " \"\"\"\n", - " # `low` represents the number of days a student \n", - " # is infectious if quarantined.\n", - " # `high` is the number of days they are infectious\n", - " # if not quarantined\n", - " low = 1\n", - " high = 4\n", - " tr = high - fraction * (high-low)\n", - " system.gamma = 1 / tr" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap13ode.ipynb b/code/soln/chap13ode.ipynb deleted file mode 100644 index c614bdc56..000000000 --- a/code/soln/chap13ode.ipynb +++ /dev/null @@ -1,862 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 13\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Code from previous chapters" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`make_system`, `plot_results`, and `calc_total_infected` are unchanged." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(beta, gamma):\n", - " \"\"\"Make a system object for the SIR model.\n", - " \n", - " beta: contact rate in days\n", - " gamma: recovery rate in days\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " init = State(S=89, I=1, R=0)\n", - " init /= np.sum(init)\n", - "\n", - " t0 = 0\n", - " t_end = 7 * 14\n", - "\n", - " return System(init=init, t0=t0, t_end=t_end,\n", - " beta=beta, gamma=gamma)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_results(S, I, R):\n", - " \"\"\"Plot the results of a SIR model.\n", - " \n", - " S: TimeSeries\n", - " I: TimeSeries\n", - " R: TimeSeries\n", - " \"\"\"\n", - " plot(S, '--', label='Susceptible')\n", - " plot(I, '-', label='Infected')\n", - " plot(R, ':', label='Recovered')\n", - " decorate(xlabel='Time (days)',\n", - " ylabel='Fraction of population')" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def calc_total_infected(results):\n", - " \"\"\"Fraction of population infected during the simulation.\n", - " \n", - " results: DataFrame with columns S, I, R\n", - " \n", - " returns: fraction of population\n", - " \"\"\"\n", - " return get_first_value(results.S) - get_last_value(results.S)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's an updated version of `run_simulation` that uses `unpack`." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \n", - " returns: TimeFrame\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " frame = TimeFrame(columns=init.index)\n", - " frame.row[t0] = init\n", - " \n", - " for t in linrange(t0, t_end):\n", - " frame.row[t+1] = update_func(frame.row[t], t, system)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Write a version of `update_func` that uses `unpack`." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "# Original\n", - "\n", - "def update_func(state, t, system):\n", - " \"\"\"Update the SIR model.\n", - " \n", - " state: State (s, i, r)\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: State (sir)\n", - " \"\"\"\n", - " s, i, r = state\n", - "\n", - " infected = system.beta * i * s \n", - " recovered = system.gamma * i\n", - " \n", - " s -= infected\n", - " i += infected - recovered\n", - " r += recovered\n", - " \n", - " return State(S=s, I=i, R=r)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test the updated code with this example." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
SIR
00.9888890.0111110.000000
10.9852300.0119920.002778
20.9812960.0129290.005776
30.9770710.0139210.009008
40.9725410.0149700.012488
\n", - "
" - ], - "text/plain": [ - " S I R\n", - "0 0.988889 0.011111 0.000000\n", - "1 0.985230 0.011992 0.002778\n", - "2 0.981296 0.012929 0.005776\n", - "3 0.977071 0.013921 0.009008\n", - "4 0.972541 0.014970 0.012488" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(0.333, 0.25)\n", - "results = run_simulation(system, update_func)\n", - "results.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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FJefPZGp42I9t/Y6XG427ijn7+/ncrD66GX8hRhJlFK8/vrrXHHFFUyfPp0LL7yQ3NxcwCzeeuutt1JWVoZSilWrVgHQ2trK3Xffzfz585k9ezbXXXcdZWVlXud9/vnnOf3005k6dSpnnXUWb731Fps2bWL16tWUlpailEIpxaZNmwAoLi7muuuuIycnh0WLFnHPPffgcByZbF9eXs4PfvADpk2bxsqVK9m1a9cAfYf6n789qI+A6UB+P8YihojgwACWzkpj4thRfPxVCVV1rQBU17eyYXsZF542Xp5LiRPWkr+dln3/xnA5+/1aFpud0AmzCR13Yr2cxx9/nNtuu42MjAzuvfdebr/9dl555RWuuuoqIiMjee6551i7dq1nVd0777yT6upqnn32WcLCwnj22We5/vrree2117Barfz973/niSee4Pbbb2fmzJmUlJTQ0tJCTk4Ot912m+d8YK5a297eztVXX83y5cu59dZbaWpq4p577uHBBx/kF7/4BWAuieF2u3nllVeoqqriV7/6Vd9884YAfxPUn4AHlFLJwHbA66GE1npzXwcmBt/o2DAuXp7F9n2VbN51EJfbYOnMVElO4htpLdg+IMkJwHA5aS3YfsIJ6vLLL/c8F7r++uu59NJLcTgchIWFERERgc1mIz4+HjBv+a1bt46NGzd6Vr391a9+xZw5c/j666+ZMWMGTz75JDfddBMXXHABAGPGjPFcq/v5AF5//XWio6P52c9+5tm2evVqrrzySu644w7y8/PZvHkz69evJz09HYCrr76aX/7ylyf09Q41/iaoVzo/9/T00ABkPPIIZbVayFEJjEuJory6mbjoEK92wzBoa3cRHCQzFoR/QjKmD2gPKiTjxJ8RZWVleV4fThw1NTUkJyf77Lt//36cTiennnqq13aHw0FxcTHjx4+nrKyMOXPm+H19rTW7du0iJyfHs80wDBwOBxUVFRQUFBAVFeVJTsCIeibm72+VCf0ahRjyosKDiOqy7PxhO/Kq+HL3IZbMTGV86sCssimGt9Bx00+4RzPQAgKO/Io8fOfA7Xb3uG9LSwthYWG8+uqrPm2xsbE+5/FHS0sL8+bN48477+zxnIZhjOg7Gn4lKK31wCxAL4aVhuZ2Nu4ox9nh5p2NB8hMjWZJTkqP61QJMdIppWhqaqKjo4PMzMwe90lKSmLz5s1M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o48+yjvvvAPAOeecwzvvvOO1IOK6detYtmwZwcHB1NbWctVVV7F06VLeeOMN7rvvPv75z3/y5z97PwV5/PHHWbVqFevWrWPq1Kls3LiRX//619x888289dZbXHvttdx6662edaOKior46U9/ysUXX8yrr75Keno6f/mL309t+tXx7qGUdn62AAcxR/Qd1o45yOHZfohLnMR6WvDwUE0Lb35eQFRYIMtmp8lIv2Hu36Vf81XZDgBmJk9jdkq2V/vG4i3sOGjeVpufNpPs0d7VsT89sIm9lfsBOHXsXCbFey/6/UHeBvJqCns89/E8/fTTPPfcc7S3t9PR0UFiYqLnGdSTTz7JnXfe6VnWfdWqVXz00Ue88cYbXHPNNTz77LMsXbqUm246MvA5O9u8/htvvEFQUBB33nknNpuNzMxMtNY8//zznH322Sxbtow77riDLVu2MGfOHFwuF++99x733HMPAC+//DILFy7k6quvBiA9PZ2bbrqJJ554gquuuspzve9973uceeaZnvdPPvkkN998M2effTYAaWlpbN68mbVr15Kdnc3f/vY3srOzuf766wG44YYb+Oyzz3r1Pesvx0xQWusrAZRSJcD9WuvmAYlKiC5cLjcffFmEYRjUNbXx6sf7mZYZx4JpSVLjT/S5Sy65hFWrVlFZWcl9993HDTfcwKhRo2hubqakpISbb77Za/mY9vZ2Ty9p3759nH/++T2eNz8/n6lTp2KzHfk3O2PGDP7+978DEB4ezuLFi3nnnXeYM2cOmzZtor29ncWLFwOQm5vLhx9+SE5Ojud4l8vlswT95MmTvd7n5uaybds2z7pRAE6nk7lz5wJQUFDgSaKHTZ8+nd27d/v3DetH/pY6+kV/ByLE0VitFqZPiOfzr8tod5r323fkVVFQVs9ps9JIT4o8zhmE8F9UVBTp6emkp6fzwAMPcNlll/Hmm296ktIjjzzCuHHjvI4JDzd79IZhHHXtM3/W3luxYgX33nsvt99+O+vWrWP58uWepd5bWlr41re+xXXXXXfMcwQHe5cXa2lp4bbbbmPBggU97tdTzENlRWy/h0kppS4HLsOsiRfYtU1rndXHcQnhYbFYmDIulvSkSD7ZUkxBubl8dlOrkzc25KPGxLBoRgohMupv2Jidkn3MW28L0maxIO3oK+ssHjuPxWPnHbV9eeYilmcu+kYx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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_results(results.S, results.I, results.R)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Sweeping beta" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make a range of values for `beta`, with constant `gamma`." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.25" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "beta_array = linspace(0.1, 1.1, 11)\n", - "gamma = 0.25" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Run the simulation once for each value of `beta` and print total infections." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.1 0.0072309016649785285\n", - "0.2 0.038410532615067994\n", - "0.30000000000000004 0.33703425948982\n", - "0.4 0.6502429153895082\n", - "0.5 0.8045061124629623\n", - "0.6 0.8862866308018508\n", - "0.7000000000000001 0.9316695082755875\n", - "0.8 0.9574278300784942\n", - "0.9 0.9720993156325133\n", - "1.0 0.9803437149675784\n", - "1.1 0.9848347293510136\n" - ] - } - ], - "source": [ - "for beta in beta_array:\n", - " system = make_system(beta, gamma)\n", - " results = run_simulation(system, update_func)\n", - " print(system.beta, calc_total_infected(results))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Wrap that loop in a function and return a `SweepSeries` object." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "def sweep_beta(beta_array, gamma):\n", - " \"\"\"Sweep a range of values for beta.\n", - " \n", - " beta_array: array of beta values\n", - " gamma: recovery rate\n", - " \n", - " returns: SweepSeries that maps from beta to total infected\n", - " \"\"\"\n", - " sweep = SweepSeries()\n", - " for beta in beta_array:\n", - " system = make_system(beta, gamma)\n", - " results = run_simulation(system, update_func)\n", - " sweep[system.beta] = calc_total_infected(results)\n", - " return sweep" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Sweep `beta` and plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
0.10.007231
0.20.038411
0.30.337034
0.40.650243
0.50.804506
0.60.886287
0.70.931670
0.80.957428
0.90.972099
1.00.980344
1.10.984835
\n", - "
" - ], - "text/plain": [ - "0.1 0.007231\n", - "0.2 0.038411\n", - "0.3 0.337034\n", - "0.4 0.650243\n", - "0.5 0.804506\n", - "0.6 0.886287\n", - "0.7 0.931670\n", - "0.8 0.957428\n", - "0.9 0.972099\n", - "1.0 0.980344\n", - "1.1 0.984835\n", - "dtype: float64" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "infected_sweep = sweep_beta(beta_array, gamma)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap06-fig01.pdf\n" - ] - }, - { - "data": { - "image/png": "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P58HH3yQL33pSzz99NMUFBREt7v77ru56aabyMrK4sorr+SKK65g4sSJPPLII/zlL3/hpptu4owzzmD48OHRfX75y19y1113UV1dzVVXXcWGDRs48cQTefTRR3nkkUe4/vrref7558nLy6O+vp5zzz2X+fPnEwqF+MlPfsK1117Lo48+etD3Nm/ePLZt23bQ52+55RYuuOCCTuubm5t55513mD9/fnTd+PHjGTt2LMuWLesyQXUUiUSoqanpdB/T888/z0knnUR5eTnnn38+X/jCF8jN1X+XnjS3hIIk1MTufY3UBMmodn8zkV52PigqyGX4kEKGDSlk2OACBhW3JSL/U9dp+j/9j4ux1O1gycrKpNyDkJebzZyjKzjOeh4Yo66ujj/96U888MADHHPMMQB89atfbXdNZNy4cXzmM5+JLt900008+eSTvPXWW8yePTu6/sorr+T4448H4JxzzuGZZ57h5z//OXl5eVx22WXcfffdvP3225x55oExea+77jqmT5/O9OnTo9Xal7/8ZQC++MUv8vDDD7Np0yamTJnCrFmzmDVrVnTfW265hZNPPplt27YxZsyYLt/ffffdR2tr60Hf/8GuJ+3Zs4dwONzp+eHDh7N79+6Dvl6sRx55hFAoxFlnnRVdd8YZZ3DeeecxatQoVqxYwfe//33279/PtddeG9drDgQNTa2+EopJRntqG6lr6P09YYOL8qKJyP8sYHhJIYUF+nga6PQXEOPN1dVJu0GupTXMm6ur40pQmzdvprW1lRkzZkTXxSYB8DceL1q0iGeeeYbq6mpCoRANDQ1s395+8sqpUw/c+1xWVsbEiRPJy/PDl2RnZ1NaWsqePXu63Wfo0KHtlgF2797NlClTqKur48477+Tvf/871dXV0W/N27dvP2iCGjt2bI/nIBFeeuklfvjDH7Jo0aJ27+m8886LPjYzsrOzueWWW7jmmmsG3Df2UDhC1a79VNc0BAmpiT37GmloOvgXiq5kZWUxdFA+w0oKYhJRIcNKCsjPy0lQ9JLplKBiHDutPKkV1LHT4rvGEU/TyP33388f//hHbrjhBiZNmkRBQQEXX3xxp8oktpkqKysrmpxi14XD7d9/7DYd92n7wG7b53vf+x5vvvkmCxYsiCaeefPmdVshHWoT37Bhw8jOzmbXrl3trh/t3r27XRNlV1577TW++tWv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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "label = 'gamma = ' + str(gamma)\n", - "plot(infected_sweep, label=label)\n", - "\n", - "decorate(xlabel='Contacts per day (beta)',\n", - " ylabel='Fraction infected')\n", - "\n", - "savefig('figs/chap06-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Sweeping gamma" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Using the same array of values for `beta`" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. , 1.1])" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "beta_array" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And now an array of values for `gamma`" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[0.2, 0.4, 0.6, 0.8]" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "gamma_array = [0.2, 0.4, 0.6, 0.8]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For each value of `gamma`, sweep `beta` and plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap06-fig02.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "for gamma in gamma_array:\n", - " infected_sweep = sweep_beta(beta_array, gamma)\n", - " label = 'γ = ' + str(gamma)\n", - " plot(infected_sweep, label=label)\n", - " \n", - "decorate(xlabel='Contacts per day (beta)',\n", - " ylabel='Fraction infected',\n", - " loc='upper left')\n", - "\n", - "savefig('figs/chap06-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "** Exercise:** Suppose the infectious period for the Freshman Plague is known to be 2 days on average, and suppose during one particularly bad year, 40% of the class is infected at some point. Estimate the time between contacts." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Update the SIR model.\n", - " \n", - " state: State (s, i, r)\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: State (sir)\n", - " \"\"\"\n", - " s, i, r = state\n", - "\n", - " infected = system.beta * i * s \n", - " recovered = system.gamma * i\n", - " \n", - " dSdt = -infected\n", - " dIdt = infected - recovered\n", - " dRdt = recovered\n", - " \n", - " return dSdt, dIdt, dRdt" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(-0.01208641975308642, 0.009308641975308642, 0.002777777777777778)" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(init, 0, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[]
nfev74
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events []\n", - "nfev 74\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(0.333, 0.25)\n", - "results, details = run_ode_solver(system, slope_func)\n", - "details" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_results(results.S, results.I, results.R)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap13soln.ipynb b/code/soln/chap13soln.ipynb deleted file mode 100644 index 40c948c08..000000000 --- a/code/soln/chap13soln.ipynb +++ /dev/null @@ -1,848 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 13\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Code from previous chapters" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`make_system`, `plot_results`, and `calc_total_infected` are unchanged." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(beta, gamma):\n", - " \"\"\"Make a system object for the SIR model.\n", - " \n", - " beta: contact rate in days\n", - " gamma: recovery rate in days\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " init = State(S=89, I=1, R=0)\n", - " init /= np.sum(init)\n", - "\n", - " t0 = 0\n", - " t_end = 7 * 14\n", - "\n", - " return System(init=init, t0=t0, t_end=t_end,\n", - " beta=beta, gamma=gamma)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_results(S, I, R):\n", - " \"\"\"Plot the results of a SIR model.\n", - " \n", - " S: TimeSeries\n", - " I: TimeSeries\n", - " R: TimeSeries\n", - " \"\"\"\n", - " plot(S, '--', label='Susceptible')\n", - " plot(I, '-', label='Infected')\n", - " plot(R, ':', label='Recovered')\n", - " decorate(xlabel='Time (days)',\n", - " ylabel='Fraction of population')" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def calc_total_infected(results):\n", - " \"\"\"Fraction of population infected during the simulation.\n", - " \n", - " results: DataFrame with columns S, I, R\n", - " \n", - " returns: fraction of population\n", - " \"\"\"\n", - " return get_first_value(results.S) - get_last_value(results.S)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's an updated version of `run_simulation` that uses `unpack`." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \n", - " returns: TimeFrame\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " frame = TimeFrame(columns=init.index)\n", - " frame.row[t0] = init\n", - " \n", - " for t in linrange(t0, t_end):\n", - " frame.row[t+1] = update_func(frame.row[t], t, system)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Write a version of `update_func` that uses `unpack`." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "# Original\n", - "\n", - "def update_func(state, t, system):\n", - " \"\"\"Update the SIR model.\n", - " \n", - " state: State (s, i, r)\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: State (sir)\n", - " \"\"\"\n", - " s, i, r = state\n", - "\n", - " infected = system.beta * i * s \n", - " recovered = system.gamma * i\n", - " \n", - " s -= infected\n", - " i += infected - recovered\n", - " r += recovered\n", - " \n", - " return State(S=s, I=i, R=r)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def update_func(state, t, system):\n", - " \"\"\"Update the SIR model.\n", - " \n", - " state: State (s, i, r)\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: State (sir)\n", - " \"\"\"\n", - " unpack(system)\n", - " s, i, r = state\n", - "\n", - " infected = beta * i * s \n", - " recovered = gamma * i\n", - " \n", - " s -= infected\n", - " i += infected - recovered\n", - " r += recovered\n", - " \n", - " return State(S=s, I=i, R=r)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test the updated code with this example." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
SIR
00.9888890.0111110.000000
10.9852300.0119920.002778
20.9812960.0129290.005776
30.9770710.0139210.009008
40.9725410.0149700.012488
\n", - "
" - ], - "text/plain": [ - " S I R\n", - "0 0.988889 0.011111 0.000000\n", - "1 0.985230 0.011992 0.002778\n", - "2 0.981296 0.012929 0.005776\n", - "3 0.977071 0.013921 0.009008\n", - "4 0.972541 0.014970 0.012488" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(0.333, 0.25)\n", - "results = run_simulation(system, update_func)\n", - "results.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_results(results.S, results.I, results.R)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Sweeping beta" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make a range of values for `beta`, with constant `gamma`." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.25" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "beta_array = linspace(0.1, 1.1, 11)\n", - "gamma = 0.25" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Run the simulation once for each value of `beta` and print total infections." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.1 0.0072309016649785285\n", - "0.2 0.038410532615067994\n", - "0.30000000000000004 0.33703425948982\n", - "0.4 0.6502429153895082\n", - "0.5 0.8045061124629623\n", - "0.6 0.8862866308018508\n", - "0.7000000000000001 0.9316695082755875\n", - "0.8 0.9574278300784942\n", - "0.9 0.9720993156325133\n", - "1.0 0.9803437149675784\n", - "1.1 0.9848347293510136\n" - ] - } - ], - "source": [ - "for beta in beta_array:\n", - " system = make_system(beta, gamma)\n", - " results = run_simulation(system, update_func)\n", - " print(system.beta, calc_total_infected(results))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Wrap that loop in a function and return a `SweepSeries` object." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "def sweep_beta(beta_array, gamma):\n", - " \"\"\"Sweep a range of values for beta.\n", - " \n", - " beta_array: array of beta values\n", - " gamma: recovery rate\n", - " \n", - " returns: SweepSeries that maps from beta to total infected\n", - " \"\"\"\n", - " sweep = SweepSeries()\n", - " for beta in beta_array:\n", - " system = make_system(beta, gamma)\n", - " results = run_simulation(system, update_func)\n", - " sweep[system.beta] = calc_total_infected(results)\n", - " return sweep" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Sweep `beta` and plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
0.10.007231
0.20.038411
0.30.337034
0.40.650243
0.50.804506
0.60.886287
0.70.931670
0.80.957428
0.90.972099
1.00.980344
1.10.984835
\n", - "
" - ], - "text/plain": [ - "0.1 0.007231\n", - "0.2 0.038411\n", - "0.3 0.337034\n", - "0.4 0.650243\n", - "0.5 0.804506\n", - "0.6 0.886287\n", - "0.7 0.931670\n", - "0.8 0.957428\n", - "0.9 0.972099\n", - "1.0 0.980344\n", - "1.1 0.984835\n", - "dtype: float64" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "infected_sweep = sweep_beta(beta_array, gamma)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap06-fig01.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "label = 'gamma = ' + str(gamma)\n", - "plot(infected_sweep, label=label)\n", - "\n", - "decorate(xlabel='Contacts per day (beta)',\n", - " ylabel='Fraction infected')\n", - "\n", - "savefig('figs/chap06-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Sweeping gamma" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Using the same array of values for `beta`" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. , 1.1])" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "beta_array" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And now an array of values for `gamma`" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[0.2, 0.4, 0.6, 0.8]" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "gamma_array = [0.2, 0.4, 0.6, 0.8]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For each value of `gamma`, sweep `beta` and plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap06-fig02.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "for gamma in gamma_array:\n", - " infected_sweep = sweep_beta(beta_array, gamma)\n", - " label = 'γ = ' + str(gamma)\n", - " plot(infected_sweep, label=label)\n", - " \n", - "decorate(xlabel='Contacts per day (beta)',\n", - " ylabel='Fraction infected',\n", - " loc='upper left')\n", - "\n", - "savefig('figs/chap06-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "** Exercise:** Suppose the infectious period for the Freshman Plague is known to be 2 days on average, and suppose during one particularly bad year, 40% of the class is infected at some point. Estimate the time between contacts." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
0.10.002736
0.20.007235
0.30.015929
0.40.038603
0.50.132438
0.60.346765
0.70.530585
0.80.661553
0.90.754595
1.00.821534
1.10.870219
\n", - "
" - ], - "text/plain": [ - "0.1 0.002736\n", - "0.2 0.007235\n", - "0.3 0.015929\n", - "0.4 0.038603\n", - "0.5 0.132438\n", - "0.6 0.346765\n", - "0.7 0.530585\n", - "0.8 0.661553\n", - "0.9 0.754595\n", - "1.0 0.821534\n", - "1.1 0.870219\n", - "dtype: float64" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "# Sweep beta with fixed gamma\n", - "gamma = 1/2\n", - "infected_sweep = sweep_beta(beta_array, gamma)" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([0.62548698])" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "# Interpolating by eye, we can see that the infection rate passes through 0.4\n", - "# when beta is between 0.6 and 0.7\n", - "# We can use the `crossings` function to interpolate more precisely\n", - "# (although we don't know about it yet :)\n", - "beta_estimate = crossings(infected_sweep, 0.4)" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1.59875429])" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "# Time between contacts is 1/beta\n", - "time_between_contacts = 1/beta_estimate" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap14soln.ipynb b/code/soln/chap14soln.ipynb deleted file mode 100644 index 6b055eda9..000000000 --- a/code/soln/chap14soln.ipynb +++ /dev/null @@ -1,828 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 14\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Code from previous chapters" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(beta, gamma):\n", - " \"\"\"Make a system object for the SIR model.\n", - " \n", - " beta: contact rate in days\n", - " gamma: recovery rate in days\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " init = State(S=89, I=1, R=0)\n", - " init /= np.sum(init)\n", - "\n", - " t0 = 0\n", - " t_end = 7 * 14\n", - "\n", - " return System(init=init, t0=t0, t_end=t_end,\n", - " beta=beta, gamma=gamma)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def update_func(state, t, system):\n", - " \"\"\"Update the SIR model.\n", - " \n", - " state: State (s, i, r)\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: State (sir)\n", - " \"\"\"\n", - " s, i, r = state\n", - "\n", - " infected = system.beta * i * s \n", - " recovered = system.gamma * i\n", - " \n", - " s -= infected\n", - " i += infected - recovered\n", - " r += recovered\n", - " \n", - " return State(S=s, I=i, R=r)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \n", - " returns: TimeFrame\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " frame = TimeFrame(columns=init.index)\n", - " frame.row[t0] = init\n", - " \n", - " for t in linrange(t0, t_end):\n", - " frame.row[t+1] = update_func(frame.row[t], t, system)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def calc_total_infected(results):\n", - " \"\"\"Fraction of population infected during the simulation.\n", - " \n", - " results: DataFrame with columns S, I, R\n", - " \n", - " returns: fraction of population\n", - " \"\"\"\n", - " return get_first_value(results.S) - get_last_value(results.S)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def sweep_beta(beta_array, gamma):\n", - " \"\"\"Sweep a range of values for beta.\n", - " \n", - " beta_array: array of beta values\n", - " gamma: recovery rate\n", - " \n", - " returns: SweepSeries that maps from beta to total infected\n", - " \"\"\"\n", - " sweep = SweepSeries()\n", - " for beta in beta_array:\n", - " system = make_system(beta, gamma)\n", - " results = run_simulation(system, update_func)\n", - " sweep[system.beta] = calc_total_infected(results)\n", - " return sweep" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## SweepFrame\n", - "\n", - "The following sweeps two parameters and stores the results in a `SweepFrame`" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "def sweep_parameters(beta_array, gamma_array):\n", - " \"\"\"Sweep a range of values for beta and gamma.\n", - " \n", - " beta_array: array of infection rates\n", - " gamma_array: array of recovery rates\n", - " \n", - " returns: SweepFrame with one row for each beta\n", - " and one column for each gamma\n", - " \"\"\"\n", - " frame = SweepFrame(columns=gamma_array)\n", - " for gamma in gamma_array:\n", - " frame[gamma] = sweep_beta(beta_array, gamma)\n", - " return frame" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the results look like." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
0.10.30.50.7
0.100.0846930.0054440.0027360.001827
0.180.7086230.0159140.0061180.003783
0.260.9007800.0553800.0116390.006427
0.340.9568880.2678640.0221150.010191
0.420.9770450.5245630.0478160.015946
\n", - "
" - ], - "text/plain": [ - " 0.1 0.3 0.5 0.7\n", - "0.10 0.084693 0.005444 0.002736 0.001827\n", - "0.18 0.708623 0.015914 0.006118 0.003783\n", - "0.26 0.900780 0.055380 0.011639 0.006427\n", - "0.34 0.956888 0.267864 0.022115 0.010191\n", - "0.42 0.977045 0.524563 0.047816 0.015946" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "beta_array = linspace(0.1, 0.9, 11)\n", - "gamma_array = linspace(0.1, 0.7, 4)\n", - "frame = sweep_parameters(beta_array, gamma_array)\n", - "frame.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's how we can plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "for gamma in gamma_array:\n", - " label = 'gamma = ' + str(gamma)\n", - " plot(frame[gamma], label=label)\n", - " \n", - "decorate(xlabel='Contacts per day (beta)',\n", - " ylabel='Fraction infected',\n", - " loc='upper left')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It's often useful to separate the code that generates results from the code that plots the results, so we can run the simulations once, save the results, and then use them for different analysis, visualization, etc." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Contact number" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After running `sweep_parameters`, we have a `SweepFrame` with one row for each value of `beta` and one column for each value of `gamma`." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(11, 4)" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "frame.shape" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following loop shows how we can loop through the columns and rows of the `SweepFrame`. With 11 rows and 4 columns, there are 44 elements." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.1 0.1 0.0846929424381071\n", - "0.18 0.1 0.7086227853695759\n", - "0.26 0.1 0.9007802517781114\n", - "0.33999999999999997 0.1 0.9568878995442757\n", - "0.42000000000000004 0.1 0.9770452570735504\n", - "0.5 0.1 0.9845958628261559\n", - "0.58 0.1 0.9874003453175401\n", - "0.66 0.1 0.9884042490643622\n", - "0.74 0.1 0.9887434214062726\n", - "0.82 0.1 0.9888495150524135\n", - "0.9 0.1 0.9888795705171926\n", - "0.1 0.3 0.0054435591223862545\n", - "0.18 0.3 0.015914069144794984\n", - "0.26 0.3 0.055379762106819386\n", - "0.33999999999999997 0.3 0.2678641677332422\n", - "0.42000000000000004 0.3 0.5245629358439001\n", - "0.5 0.3 0.6860504839161878\n", - "0.58 0.3 0.7883785563390235\n", - "0.66 0.3 0.8550657464101674\n", - "0.74 0.3 0.8994791356903035\n", - "0.82 0.3 0.9294693026191699\n", - "0.9 0.3 0.9498533103273188\n", - "0.1 0.5 0.0027357655411521797\n", - "0.18 0.5 0.006118341358324897\n", - "0.26 0.5 0.011639469321666152\n", - "0.33999999999999997 0.5 0.022114766524234164\n", - "0.42000000000000004 0.5 0.04781622666891572\n", - "0.5 0.5 0.13243803845818214\n", - "0.58 0.5 0.30326419264834004\n", - "0.66 0.5 0.4641102273186152\n", - "0.74 0.5 0.5884769725281787\n", - "0.82 0.5 0.6827496109784223\n", - "0.9 0.5 0.7545952983288148\n", - "0.1 0.7 0.001826769346999102\n", - "0.18 0.7 0.003782561608418833\n", - "0.26 0.7 0.0064266722107564345\n", - "0.33999999999999997 0.7 0.010190551933453973\n", - "0.42000000000000004 0.7 0.015945826561526877\n", - "0.5 0.7 0.025707925046422053\n", - "0.58 0.7 0.04500775311679983\n", - "0.66 0.7 0.09069406882939202\n", - "0.74 0.7 0.18979521165624091\n", - "0.82 0.7 0.3183431867354656\n", - "0.9 0.7 0.4369993744563133\n" - ] - } - ], - "source": [ - "for gamma in frame.columns:\n", - " series = frame[gamma]\n", - " for beta in series.index:\n", - " frac_infected = series[beta]\n", - " print(beta, gamma, frac_infected)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can wrap that loop in a function and plot the results. For each element of the `SweepFrame`, we have `beta`, `gamma`, and `frac_infected`, and we plot `beta/gamma` on the x-axis and `frac_infected` on the y-axis." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_sweep_frame(frame):\n", - " \"\"\"Plot the values from a SweepFrame.\n", - " \n", - " For each (beta, gamma), compute the contact number,\n", - " beta/gamma\n", - " \n", - " frame: SweepFrame with one row per beta, one column per gamma\n", - " \"\"\"\n", - " for gamma in frame.columns:\n", - " series = frame[gamma]\n", - " for beta in series.index:\n", - " frac_infected = series[beta]\n", - " plot(beta/gamma, frac_infected, 'ro')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what it looks like:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap06-fig03.pdf\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3Xt83FWd//HXZJI2TUhbKG2h3IpAPyogNwEXV6WtKMr+KKgoF3EBFRexKnhZQeWysuKiXFZZFxBQEErVFVR0Vy5tQV1dUC5quXxaSgttKW2KQNOkbZLJ/P443wmT6TT5psnM95vM+/l45NHMme/M9zNJOp8553y+52Ty+TwiIiJpU5d0ACIiIuUoQYmISCopQYmISCopQYmISCopQYmISCopQYmISCopQYmISCopQYmISCopQYmISCopQYmISCrVJx1AJZnZWOBwYA2QSzgcEZFalgV2Bf7o7lviPGBUJyhCcvpt0kGIiEivtwG/i3PgaE9QawBuv/12dtlll6RjERGpWS+++CKnnXYaRO/LcYz2BJUD2GWXXdh9992TjkVERAYx3aIiCRERSaWq9qDM7H3AucCbgfHunhng+BnADcCRwIvApe7+g0rHKSIiyat2D6oJWAh8Y6ADzawB+BWwllDscBlwg5m9o6IRiohIKlS1B+XutwGY2dExDn8PsBtwsLu3A4uj5DQXeLBiQYqISCqkuUjiCOChKDkVLCD0pEREem1YspTWhYvYvHYdjVOnMHnWTMbP2E/nHKHnLEhzkcQUYF1JW2vULiID2LBkKcuuu4EnLr2MZdfdwIYlS0fleTcsWcrKefPpamtjzM6T6GprY+W8+RU9r85Znb+lNCeofgsoRGTbknpjSeK8rQsXkW1uor65mUwmQ31zM9nmJloXLtI5R+A5i6V5iG8tsG9J22S27lWJpF61h0mK31iA3n9bFy4adefdvHYdY3ae1Kct29TE5rWVe6vQOavzNpzmHtTDwJFm1lTUNgt4KKF4RLZLEr2KzWvXkW1q6tNWjTeWJM7bOHUKuY6OPm25jg4ap1ZuNkDnrM5MS1UTlJntZGYHE/WMzOzg6GuMmR1hZk+b2W7R4b8GXgBuMrP9zews4BTgO9WMWWSokhgmSeqNJYnzTp41k1x7B93t7eTzebrb28m1dzB51kydcwSes1i1e1DHA48B34tuPxZ9TSNcI2VAA4C7dwLHEVa/fQS4CDjb3VViLiNKEr2KpN5Ykjjv+Bn7scepJ9PQ0kLn+pdoaGlhj1NPruhQps5ZnSq+TD6fr8qJkmBm04HlCxYs0Fp80qva80HLrruBrra23vkYgO72dhpaWtjnn86u2HmTKg9OsixZ0mvVqlXMnj0bYG93XxHnMWkukhDZpu19EyzMB2Wbm/rMB1XyU+HkWTNZOW8+EHpOuY4Ocu0dTJtzfEXOVzB+xn6JJIakziujT5qLJETKGkrRQRLzQUkPk4iMVOpByYgzlFLmpMpm1asQGTz1oGTEGUrRQdJlsyISn3pQkjoDzS81Tp2yVdFB3CST1HyQiAyeelCSKnHml4ZSyqz5IJGRQz0oSZU480uFJFPcy5o25/jYSUbzQSIjgxKUpErcIgYlGZHRT0N8kioqYhCRAiUoSZWk1/4SkfTQEJ9UTbnqPGCrtqHML4nI6KEEJVVRbomh5d+7iTzQOGXyVssOVXKNOhEZGTTEJ1VRbomhrg0b6N6wIbHdOkUk3ZSgpCrKrf7Q09VFvqu7T1s1d+sUkXRTgpKqKFedV9fQQKah7yizKvZEpEAJSqqiXHVew/jx1I8fr4o9ESlLRRJSFdta/QFQxZ6IlKUEJVWzrdUflJBEpBwN8YmISCqpByVDtr3br4uI9Ec9KBmSoWy/LiLSHyUoGZJyF+DqYlsRGQ5KUDIkQ9l+XUSkP0pQMiTaHkNEKkUJSoZE22OISKUoQcmQFC7AbWhpoXP9SzS0tLDHqSerik9Ehkxl5jJk2n5dRCpBPSgREUklJSgREUklJSgREUmlqs9BmdkFwFxgInAvcLa7l71oxsyOBy4FDPgb8F/AF929s0rhiohIQqragzKzM4ELgXOBowhJ6o5tHLsPISHdDuwPfAR4P/DlqgQrIiKJqnYPai5wlbvfBWBmZwHLzOwAd19ccuyhwAZ3/1Z0e7mZ/Rg4rHrhiohIUqqWoMxsLHAQcF6hzd2fNbMVwJFAaYJ6BGgxsxOAnwO7A8cC11cjXtEq5SKSrGoO8U2Kzlc639QKbLUujrs/CxwP/ADoBJ4HHnT3b1c2TAGtUi4iyatmgsoM5mAzmwZ8F/gGYVjveOBYMzu/ArFJCa1SLiJJ2+YQn5l9MO6TuPuPYxy2Hugh9JaeKmqfzNa9KoBPAsvc/RvR7b+YWQtwNXBV3Nhk+2xeu44xO0/q06ZVykWkmvqbg5pfcjtP315Qvuj7AROUu28xsz8DM4EHAcxsb2A68FCZhzQBuZK2HnTtVlU0Tp1CV1sb9c3NvW1apVxEqqm/N/uGoq/ZwJPAiYQez2TgfYTChncO4nzXAueb2RwzOwi4CVjk7ovN7Agze9rMdouO/W/gXWb2KTN7nZnNAr4G3D2I88l20irlIpK0bfag3L2392JmVwLnufv9RYf83Mw6gGsI1XkDcvebzWwqcB0wAbgPODu6u4lwQW5DdOz9URn654ErCBfq/hy4IN5Lk6EorFJeXMU3bc7xquITkaqJW2b+BmBVmfaVwIzBnNDdLwcuL9P+ACWFFO5+C3DLYJ5fts+2SsqVkEQkKXHnc5YC55lZbwKJvj8vuk9GMJWUi0gaxe1BnUeY+5ltZg8RCiTeAkwjlH/LCFZcUg70/tu6cJF6UCKSmFg9KHdfAOxHWBtvB2A88BNgRsm8lIxAm9euI9vU1KdNJeUikrTYSx25+2rgSxWMRRKiknIRSaPY1xSZ2b5mdoWZ3RlV4mFmx5nZgZULT6pBJeUikkaxEpSZvRX4C2FR1+OAwkftNwEXVSY0qZZCSXlDSwud61+ioaWFPU49WfNPIpKouEN8lwNfd/fLzKytqH0BcM7whyXVppJyEUmbuEN8BwPzyrSvpcxK5CIiIkMVtwe1mVC5V2o/wiKwMsJp7ycRSZu4PahfA/9cdKFu3sx2QmvjjQq6UFdE0ihugvoCcAjwLNAI/BRYDrQAX65MaFIt2vtJRNIo7oW6awkJ6l8JK5D/iZC0Dnf3v1UuPKkGXagrImkUaw7KzI4CHnL3G4Ebi9qzZnaUu/++UgFKZRTPOW1Zv56e7m7G7bpL7/26UFdEkha3SOK3wK5svfPtxOi+7HAGJZW15v4FrLrjR+RzOeqbd6Bu7Bg6lq8AoHGXqeQ6Osi1dzBtjpZZFJHkxE1QGfruoFswAegYvnCk0jYsWRqSE2EYr6erk9ymTYydMpmuV16hrr5eez+JSCr0m6DM7Ibo2zxwlZltKro7C7wZeKxCsUkFtC5cRL47R3aHZjJApqGBHqBnSydjp0xm/4u/knSIIiLAwD2owkfoDPA6oLPovk7gD8A3KxCXVMjmtevI7tBMvrubTH349Wfq6+lu38iEqfsnHJ2IyGv6TVDuPhPAzH4InOvuG6oSlVRM49Qp5Lu72bT6BXoIySm3ZQuZbFaLw4pIqsS9Duqccsea2UQz22F4Q5JKmjxrJplslnG7TQvJaWM7GWD3Uz6kOScRSZW4RRLzgfuAfy9pPx04Bu2qO2IUVi5vXbiITH09jQdqWSMRSae4CeotlN+scCHw1eELR6pBK5eLyEgQd4ivGegq054jLHckIiIyrOImqCeBE8u0nwj48IUjIiISxB3i+xZwi5lNJsxF5YF3A+cCH61QbCIiUsNiJSh3vyOq1rsEOC9qXgN82t1vq1BsIiJSw+L2oHD37wHfM7Ndo9trKhaViIjUvLhzUACY2RuBI4BXo9v1RZsYioiIDJu4223sCPwEmEWYf9qPsHnhDcDLwOcqFaCIiNSmuD2obxKS2b70Xb38J4RiCRERkWEVdw7qWOAEd3/WzIrblwB7DeaEZnYBMJewl9S9wNnuXnbrVjOrJ1wIfCYwFXiOsCbgfYM5p4iIjDxxe1A7AS+Vad+B8vtElWVmZwIXEsrTjyIkqTv6ecj1wBxCKbsBHyNUD4qIyCgXtwf1OGEo77qS9g8DDw/ifHOBq9z9LgAzOwtYZmYHuPvi4gPN7EDgI8AMd18eNa8YxLlERGQEi5ugLgXuNLNphI0KTzGz/YH3A++M8wRmNhY4iNeuoyIaMlwBHAksLnnIccAzwMlm9kmgHbgNuNzdczHjrmkbliyldeEiNq9dR+NULQorIiNLrCE+d7+HsKzRTMLmhRcB04H3uvtvY55rUnS+0vmmVmBKmeOnEzZJfBfwAeAC4NOoYjCWDUuWsnLefLra2hiz8yS62tpYOW8+G5YsTTo0EZFYttmDMrMLgavdfZOZTXP3ewlFDdtrsNdL1QFjgDPc/bkopj2BTwJXDCGOmtC6cBHZ5ibqm5sBev9tXbhIvSgRGRH660H9C6+tVL7SzMr1cgZjPdDD1r2lyWzdqwJYC2wpJKeIA3sMMY6asHntOrJNTX3ask1NbF5btmBSRCR1+puDehGYY2a/IPR+di4pMe+1rTLxkmO2mNmfCcOEDwKY2d6EobyHyjzk/4CxZra7u6+K2vYFnh/oXBK2du9qa+vtOQHkOjponDrUzxkiItXRX4L6FqFq7zpCKflfyxyTie7LxjzftcA1ZvYYoSLvamCRuy82syOAW4HZ7r4auAd4irD+3+eBXQnzUFfGPFdNmzxrJivnzQdCzynX0UGuvYNpc7T5sYiMDNsc4nP3awgX4c4mJKIPEQoWir+Oif6Nxd1vBi4nJL0/AG3AKdHdTYRrnRqiY7sJlXwZ4I/A94DvsvW281JGYWv3hpYWOte/RENLC3ucerLmn0RkxMjk8wNfZ2tmXyOUd3cMeHCKmNl0YPmCBQvYfffdkw5HRKRmrVq1itmzZwPs7e4r4jwm7n5QXx1CXCIiIoMWdzXzRsL1R+8irInXZ2jQ3WcMf2giIlLL4q4kcS1wEvBjYBGDWH9PRERke8RNUCcAH4xWlBAREam4uKuZdxM2KBQREamKuD2o7wIfB75YwVhkGGiBWBEZLeImqGnAB81sFmHrjc7iO939k8MdmAxeYYHYbHNTnwVidf2TiIxEcRPU/sAT0fel6x2pYCIltECsiIwmca+DelulA5Gh27x2HWN2ntSnTQvEishIFbdIQkaAxqlTyHX0XexDC8SKyEjV335Q3wW+6O4bo++3SXNQ6aAFYkVkNOlviO/AovsP7Oc4zUGlRGGB2OIqvmlzjtf8k4iMSNtMUMXzTpqDGjnGz9hPCUlERgXNQYmISCopQYmISCopQYmISCopQYmISCrFXUlCUkzr74nIaKQe1AhXWH+vq62tz/p7G5YsTTo0EZEhibuj7k7Av7HtHXXHDH9oEofW3xOR0SruEN+NwJuB64HV6OLc1ND6eyIyWsVNULOA97r77ysZjAxe49QpdLW19facQOvvicjoEHcO6hXg5UoGIttn8qyZ5No76G5vJ5/P093eTq69g8mzZiYdmojIkMRNUJcDF5iZqv5SqK6xkbanl/DKo4+Tz+W0QaGIjApxE84JwFuA583sSbbeUfe9wx2YDKx4B92Jhx7cu3q5iMhoEDdBrQd+WclAZPBUwScio1ncHXVPr3QgMniq4BOR0WxQc0pmtivwRkKZ+VPuvqYiUUksquATkdEsVpGEmY0zs5uAlcB9wP3ASjO70cwaKxmgbJsq+ERkNItbxXcFYRWJDwKTgSnAh4B3R/fFZmYXmNkLZtZhZj8zswE/7pvZYWbWZWYPDOZco11hB92GlhY6179EQ0uLKvhEZNSIO8R3EnCGu/+6qO2nZrYJuBn4dJwnMbMzgQuBjwDLgWuAO4DZ/TxmLPAD4MFBxFsztIOuiIxWcXtQE4Fny7QvAyYM4nxzgavc/S53fxw4C5hlZgf085jLgIXA7wZxHhERGeHiJqgngI+Waf9YdN+Aop7QQYRkA4C7PwusAI7cxmPeChwPXBAzThERGSXiDpldAtwVJYzfEKr43kFILCfEfI5JhIRYWgPdSpjT6sPMmoDvAx939w4zi3kaEREZDWL1oNz9buBw4HngOOAfou8Pd/dfxTxXZpCxfQO4x90fHOTjRERkFIhddODujwGnDuFc64EeQm/pqaL2yWzdqwJ4O3CAmZ0T3a4DMmbWDezj7s8NIRYREUm5qu2o6+5bgD8DvRfpmNnewHTgoTIPeT9wcNHXdcCfou9fqHC4IiKSsG32oMysA9jL3VujcvJtblLo7k0xz3ctcI2ZPUYojrgaWOTui83sCOBWYLa7r3b3ZSXxrAPa3X1xzHOJiMgI1t8Q31xgQ9H3Q95F191vNrOphN7QBMKqFGdHdzcBBjQM9TwiIjLyZfL50bt7u5lNB5YvWLCA3XffPelwRERq1qpVq5g9ezbA3u6+Is5j4q7Ft8TMdirTPtHMlgwqShERkRjiFknsS/nhwDGEIgcREZFh1W+ZuZkdVXTzcDN7ueh2lrCA7KpKBCYiIrVtoOugfkcojsgDd5e5vwM4d7iDEhERGShB7U1YAeJZ4AjCskQFncBad++pUGwiIlLD+k1QhdUazKzB3XPVCUlERCR+kcR5ZnZWaaOZnWlmnxvmmERERGKvxXcOcGaZ9iXALcCVwxaRbNOGJUtpXbiIzWvX0Th1CpNnzdRmhSIyasXtQe1GWL281OroPqmwDUuWsnLefLra2hiz8yS62tpYOW8+G5YsTTo0EZGKiJugWoH9y7QfCPxt+MKRbWlduIhscxP1zc1kMhnqm5vJNjfRunBR0qGJiFRE3AR1F3C1mR1YaDCzNxGG9u6sRGDS1+a16+jp7GLDk0/x8iOPsuHJp+jp7GLz2nI7lYiIjHxx56AuBA4BHjezVsJ1UVOA/0PbsVdF3dixtD31NHWNY6lrbKSnq4uNS5bS8obXJx2aiEhFxEpQ7r4ReJuZvRs4NGp+BLjP3UfvarOpEl0vnS/66r2GWkRk9Im9oy6Au98D3FOhWKQfPVs6aZ4xgy1r1pDbtInsuHGM22sverZ0Jh2aiEhFxE5QZjaBsPbeXoRFYnu5+9eHOS4p0Th1Cl1tbYx/4xt627rb22mYtNUi8yIio0KsBGVmhwG/JiSmZkLl3s6EtfjWAUpQFTZ51kxWzpsPQLapiVxHB7n2DqbNOT7hyEREKiNuFd+3gF8AOwGbgLcQ1ul7DDivMqFJsfEz9mOPU0+moaWFzvUv0dDSwh6nnqwLdUVk1IqboA4GvhWtx9cDjInW6ftn1HuqCq0iISK1Jm6C6iGsXg5hSK+wf3oroSclFaRVJESkFsVNUH8FDoq+fwi4wMxmA5cBepesMK0iISK1KG6Curzo+4sIPaj7gNnAZ4Y7KOlr89p1ZJua+rRlm5q0ioSIjGpxy8wXAV0A7v4sYGY2BVivDQsrr1BiXt/c3NuW6+igceqUBKMSEamsAXtQZlZPKCd/Y3G7u69TcqqOybNmkmvvoLu9nXw+T3d7O7n2DibPmpl0aCIiFTNggnL3bmBVnGOlMlRiLiK1KO4Q39XAV83sdHffUsmApLzxM/ZTQhKRmhI3QR0LHAWsNrMngPbiO939vcMdmIiI1La4CWo9YSUJERGRqoi73cbplQ5ERESkWL+FD2b2QTMb098xIiIilTBQZd4dwMTCDTNbamZ7VjYkERGRgYf4MiW3d4nxmH6Z2QXAXELiuxc42923WhLBzA4Gvgz8PdACLAa+4u73D+X8IiIyMlT12iYzOxO4EDiXUBU4kdBLK+cQ4DngA4TV1O8B7jaz11chVBERSdhAvaF89FXatr3mAle5+10AZnYWsMzMDnD3xcUHuvv3Sx57sZl9gLCr79NDiGHE0BYbIlLL4gzx/cTMClttNAK3mtmm4oPc/V0DncjMxhJWRO/d4NDdnzWzFcCRhCG8/h6fASYBrwx0rtGgsMVGtrmpzxYbWkFCRGrFQAnqlpLbtw3hXJMIQ4ql802tQJxVTz8FZIG7hxDDiFG8xQbQ+2/rwkVKUCJSE/pNUO5+5jCeq7TgIjYzOxb4BnCiu788fCGl1+a16xiz86Q+bdpiQ0RqSTWLJNYTduYt7S1NZuteVS8zexvwE+Cj7n5v5cJLl8apU8h1dPRp0xYbIlJLqpagokVm/wz07hFhZnsD0wm79G7FzI4Afgl8zt3nVyHM1NAWGyJS64Z0TdN2uBa4xsweA1YQVklf5O6Lo2R0KzDb3Veb2YHAr4HrgV+Y2S7Rc2xy91erHHfVFbbYKK7imzbneM0/iUjNqGqCcvebzWwqcB0wgbBt/NnR3U2AAQ3R7fcDOwJfiL4KbgHOqEa8SdMWGyJSy6rdg8LdLwcuL9P+AEWFFO5+CXBJteISEZF00S65IiKSSkpQIiKSSkpQIiKSSlWfg5KBaQ0+ERH1oFKnsAZfV1tbnzX4NixZmnRoIiJVpQSVMsVr8GUyGeqbm8k2N9G6cFHSoYmIVJUSVMpsXruObFNTnzatwScitUgJKmW0Bp+ISKAElTJag09EJFCCSpnCGnwNLS10rn+JhpYWbVIoIjVJZeYppDX4RESUoFJF1z+JiLxGQ3wpoeufRET6UoJKieLrn7pf3UDHc8/TvnwFz3znP5SkRKQmKUGlROH6p65XXmXjM8vo6eqirrmJbvWkRKRGKUGlROH6p00vvECmoZ66+nro7qZ+hx20koSI1CQlqJTovf6pbSNks/R0dZHvztE4bVetJCEiNUkJKiUK1z/Vj2+hp6ODujFjaN73dYyZOFErSYhITVKCSpHxM/Zj37nn0jx9Ok177UnDhAlaSUJEapaug0pYuWuf9jj15D5t0+Ycr+uhRKTmKEElaM39C1h1x4/I53LUN+9AT3c3K+fNZ49TT2affzo76fBERBKlIb6EbFiylOduvY3ujRvJdWxiy0sv0b7iOXpyOVXsiYigHlRiVt95F7n2Duoa6qEuA/k8PZs3sXnNi6HEXESkxqkHlZCNS5eF5ESGTCZDpq6OTF2W7g2vqmJPRAQlqERlG8dBTw/5njwA+Xz4XhV7IiIa4quKQqVe27Ll9GzeRHbcODLZLLnNm6hvbia3ZTP5rm4gT8vrZ6hiT0QEJaiKK6xS3pPL0fnSeshk6N7YzpgdJ9Ld1kYeqGtspK6lgYbx49nrI6cnHbKISCooQQ2z4uuacp2dtC97lnxXFwDZceNomDCenu5uejo7ad53H7peeYWxO++s/Z9EREooQW2nNfcvYM3P76brlVdomDiRHY88gs2rVrNh8RNkm5vIjG1k8+rV0N1NZuxY8p2ddG/cSCabJbtDM7lNm2h5w+upq69n/4u/kvTLERFJnaonKDO7AJgLTATuBc5297IroZrZDOAG4EjgReBSd/9BNeIs9IQ2Pruc3KZN9ORDNXh23Dh6urroeO75UHnXUE/nS3/jhTt/Rn1zM3VN4yCfZ/PKlZDNQjZLvrubTDZLPpeje+NG6hrHkh03TmvsiYj0o6pVfGZ2JnAhcC5wFCFJ3bGNYxuAXwFrgcOBy4AbzOwdlY6zMG+0afULbGldT+err7LlxTV0vbqBTWvW0LHiOejpIZPNQh5ymzaRqauju72duoYG6hoaIJ+Hnp5QSp7Pk6/LQCZDvrubns4uxuy4o9bYExHpR7V7UHOBq9z9LgAzOwtYZmYHuPvikmPfA+wGHOzu7cDiKDnNBR6sZJCF3W07nnueujEN9LRtIVNXR767m3xPD/T0QHS7buwYAPI9OcgTekv19aH3lMtBJkPdmDGhai+3mUx9PWMn78y43aZpzklEpB9VS1BmNhY4CDiv0Obuz5rZCsIQXmmCOgJ4KEpOBQsIPamK2rx2HWN2nkRu0ybqGhtD0qnLks91Q75wVD4kKwjJK5ejrnEs+a5ueoBsYyO59nby3TnqJ0wA8mTq6tjzjNPZ9Z2zK/0SRERGvGoO8U2Kzlc639QKlJuImTKIY4dVYXfb7LhxvT2ifE+OTLYe6uqgPguFi2t78uTzecjnGbfbbjTv8zoAMpkM4/aezpiddiTfuYX6piYlJxGRQajmEF+mwscPm8mzZrJy3nzG7LgjHatWQ32WfOeWMHSX7yGbraOnO0cGyHd1UddQz47veBsNY8eyee06djzkYA3fiYgMUTUT1Hqgh9ADeqqofTJb95QgFEfsW9K2rWOHVWF329aFi8ht2RKq+MZP6K3iG7PTTkCeni2dun5JRKRCqpag3H2Lmf0ZmElU5GBmewPTgYfKPORh4Hwza3L3jqht1jaOHXbjZ+ynpCMikqBqV/FdC1xjZo8BK4CrgUXuvtjMjgBuBWa7+2rg18ALwE1mdhmhkOIU4JgqxywiIgmo6nVQ7n4zcDlwHfAHoI2QdACaAAMaomM7geOAXYFHgIsIF/VWtMRcRETSoeorSbj75YQkVdr+ACWFEe7uwNFVCUxERFJF+0GJiEgqjfbFYrMAL774YtJxiIjUtKL34Wzcx4z2BLUrwGmnnZZ0HCIiEuwKLItz4GhPUH8E3gasAXIJxyIiUsuyhOT0x7gPyOTz+YGPEhERqTIVSYiISCopQYmISCopQYmISCopQYmISCopQYmISCopQYmISCopQYmISCopQYmISCopQYmISCqN9qWOYjGzC4C5wETgXsK+UxXfWn4kMLMvAycB+wEvA3cCF7r7xkQDSzEzuws4AZgZbSMjRczsUOCbwN8BW4D73P2DyUaVHmY2EbiSsB/eDsBfgC+5+28SDSwBNd+DMrMzgQuBc4GjCEnqjkSDSpejgCuAQ4FTgXcB30k0ohQzs9OB5qTjSCszewOwEPgNcDjh72t+okGlz1XAYcAc4CDgYeCXZjYh0agSUPNr8ZnZo8Dd7n5xdPt1hJV2D3T3xYkGl0JmdhJwvbvvlHQsaWNmuwG/JyxQ/BzqQW3FzH4KvOzuH0s6lrQysyeA/3T3a6PbLcAG4HB3/1OiwVVZTfegzGws4RPKwkKbuz8LrACOTCistNsZeCXpIFLqRuDr7v580oGkkZllgWOB58zsATN70cz9XM+sAAALm0lEQVTuNbMDko4tZf4AnGhmk6Kf2VnAKuDJZMOqvppOUMAkws+gdL6pFZhS/XDSLRpi+Dxwc9KxpI2ZfQJocPfrk44lxSYDTcAXCcPo7yW88d4f9RIkmAu8CqwnzNF9CTjO3TsSjSoBtZ6gMkkHMFJEvc2fAs8C30g4nFQxsz2BiwENW/Wv8H7zX+5+vbs/Cnwiav+H5MJKnc8C04F3Eubp5gO/MLMdkwwqCbWeoNYDPWzdW5rM1r2qmmVm9YT/JC3Aie7enXBIaXMosAvwjJl1m1nh57PAzG5KMK60WU/YONQLDe7eRfjQs0dSQaWJmY0DLgU+5e4L3P0xdz8P6AROTja66qvpBOXuW4A/AzMLbWa2N+HTy0MJhZUqZlYH3ArsC7xH5eVlLQDeBBxc9AWhR3VRUkGljbt3Ao8R/paA3g8/0wHN2wUN0VfpDuA91OD7tar4zM4CrgFOJxRHXA3g7rMSDCs1zOxGwlzBe4EXi+5qdffS/0QSMbM8quLbipmdCtxESN5/BD4NnAiYPvwEZvY7wvbonwH+RiiSOB94k7svSTK2aqv5C3Xd/WYzmwpcB0wA7gPOTjaqVPlo9O9jJe17ExK6SGzuPs/MphDmMScSrvE5Rsmpjw8B3wLuJlxT9yRwQq0lJ1APSkREUqrmxjRFRGRkUIISEZFUUoISEZFUUoISEZFUUoISEZFUUoISEZFUqvnroEQqxcyOBhYBe7j7qiqed3fCCikHufuqpOKoVWZ2PdDm7p9POpaRTgmqxpjZJOCfCZuh7UXYZ+ZpwlYR84ZznT0zewa4zd0vGa7njJ73RmBfdz96OJ93FLmc8HMf1mRkZh8Gfuju273Ispk9CPzU3b89fJGlzr8AS8zsu9H2PbKdNMRXQ6JP1o8C7yf8JzoUeCth6ZnPA9qXZwQws4yZNWzjvl0JKxHcUN2oBmZmkwl/bz9LOpZKcvfVhPUZP5l0LCOdelC15T+BsYQ1vV4tal9qZvOAMQDRm9/XCOsTTgaeAS5z93mFB0RrzZ0L/B1wAmETw++4+xXR/Q8A+wAXm9nF0cP2Juw0ewMwC5gGrCGslH5ptHhv4fnfSdjC4jCgC3icsCbZ6UTLL0UxAJzp7j8ofbFmdgahZ/gOwjb1rweeAP7J3R+JjjmaMsNf0YrkH3P3H5jZdGA5cBrwj4Qdc58nbBXxDPC96BzPAZ9w99+WhHKImd1JWFB2GfBZd7+v6Fz7Av8GzAbyhDXqPufufy15HccQtgPfH3gf8MvS10xITivc/Yky9213HNHP6YfRcYWf+y3ufoaZHQN8OXreLOF39QV3f7jk/HOAxwsbOprZIYS/yYOjn+eFhCV+bnT3y6JjPgOcSVhgdiPwAHCeu6+J7j+a8Ps7Dvhq9FxPAh+Jznk94YPYYuAMd3+yzM/029HzPwycEn3/78AMwuaB/xglncJi0lcCbyEs1bQMuMLdf1jyWu8i9GQ1zDcE6kHVCDPbibDg67UlyQkI2x64e3t08+vAxwn70hwA3AbcZmazSx52MfAbwpvCN4F/M7PCyvDvI6zVdyWwa/S1krAH11rgVOAN0TnOJLw5FWJ9J3AP8AghAR5JWFG9gfAGNo/wxlF43h/189LrCG8UnyG8Ub0M/DhaRXuwvsZrb6hPETbdu4WQoA6J2uaV6d1cReixHgL8H2Fvn92i1zoV+B1he5e3Ed74HHgg6nEUv44rgM8REu22Vtt/B+GNtpyhxPF74FPR8xR+7p+Jbu8A/Ef0mKOApcCvo+HkYicS3rgxsybgvwmbgx5BSCjnU36j0M8DB0aP35PwgabUvxKS5GGErSnuIPyuLi5q+37JY+p4bR+vtxI+MP0o+hmdA/w9sHv0cyvYgdA7OjaK6Qbg+0V/9wUPAVPN7A1lYpWY1IOqHfsS/kP2u2109MbxacKn1J9EzV83s8MJbwALig7/kbt/L/r+22b2SeBdwCJ3/5uZ5YCN7l68CjrAV4q+X2Fm+xCGQwo9rYuB/3H3zxYd93RRjJuAzjLPW06G0FN4NHrsRYTktg9F+xLF9B13/1n0PF8nJIIr3b3wpnsZYQjVCJ/YC77h7r+MjvkEYSO6cwg/h3MIPZ5zil7fpwkfJk4jrLRfeB3nl+mdldqbkNzL2e443P0aM3sVoPTnXnj9RY87mzCMfCxwe9TWQuiZfSE67DTC/mIfLnxginYWeKrkuf+96OZyMzsXeNTMdiv0aiKXuvvC6HmuAn4MfMDdF0RtVwJ3mtkORQvTFv42Ho+OuYHwIeDNRT3s6wl/94V4/gr8tei834k+UJ1K6MkVFHrjryt9TRKfElTtKExsD7Q68L6Eob7flLQ/CFxQ0vZ4ye3VwNSBAjGzjxM+tU4nrNZcT9/e/GGEba6HQ55Q0VYcI4Q4B5ugip+n8Cb9lzJtpb2APxS+cfduM3sYeGPUdDhwmJmVruY9DtivpO2PMWIcB2zexn3DFUcf0bDXvxB6u1MIv8smQhFOwXHAc4Uhtui8TxX35t39aTN7peS5jyb83b2RMKRW+DvZi9d+lzC4303hNebpm2y29bhJZpZ191z0Ae4i4P8RepFjCMPmxckJXvsdjEO2mxJU7VhK2PRsf6JhlgGUJrJMmbbOMo/pd9jYzE4iDAd9iZD0NgAnEYZo+jv/9uop2beq8LyFOHuif3sr08wsS/nX0VXmecq1DTR0XlwFV0folX6qzHHFQ7E5d99W4inWCuwU47jtjaOcXxJ2yz2XMIzbSRguHFN0TO/wXpF+f8dmtidhGPCHhAS4njDkdn/Jc8P2/W7K/m1Eu/yWPq7ws/omYS7tc4RefTthGHtCSTyF30FrmZcmMSlB1YhoyO1/gE+Z2XdK56GieZMxhEn/LYS5jOKJ9reX3I6jkzBpXuztwGPu3juuHxUhFHsEeDehsCHu826vddG/0whvrhDmmLa7lLqMtxANrUZzX4cT5vUA/gScAax2903DcK5H2XY15lDj6Iwemy28sUfzTG8E3uvu90Rtu1PUizSzscB7CAUJBU8CHzOzCUVDfEboJRUcTuiBfLYQk5kdNsDrr7S3A7e7+4+ieOoIxRRrS447kLArbuk+ajIISlC15ZPA/wKPRHMxjxPedN5CmBv4R3d/3My+DXzNzFqjY04ifGo8pvzTbtNy4K3RJ+EOwu6gDnzUzOYQ5mn+gVBQUexrwP+Y2TXAzYSE+XfAH9zdo+c9ycz2J7wxtBVXAA7SM4Tqu0vM7DxgZ0KRyHBulPYlM3uREPf5hOHF/4zuu5ZQlfizaA5rJaGX8B7gV+7++0Ge67+Bs8xsXJlEM9Q4lkfHHh/t+rqJUHTSCnzczJYBkwjzOMXnPgZoo2/xxu2EXtGtZvZVQiK6Mnpc4We/NPr+c2Z2O3AQYXgtSQ7MMbOfEoYKzyd8uClNUEcDv3P3DdUNb3RRFV8Nicp7DwV+DlxC+LT9e0LF3jd5bWL/y4TKtGsIvaYPEyazFzA4FxOGPpzwJrYnoez3h4SKqscIFXqXlMR5L2Fy/khCNdTDhPLuwtDLTYT5mN9Hz3vKIOMqPlc3oTR7ShTPfxBef09/jxukzxOS7uOEarE5hZJ2d19LSL7rgTsJP6vbCXMsa7bjXPcS3ixPHO443P2PhPLr66JzXOvuPYQPMPsQ5m5+QPi7KY79ROBn7t6b9N29g/A7nkr4Xd4WPW4j0fyNu/8FmEso538yir+4cCYJ5xE+0CwiDImuBv6r+AAzyxCKJq6venSjjHbUFRllzOx0wif7Q4uTQkKxZAmFBqe4+/0DHLsX4dKE49397iqEVxFm9kGia7JK5rhkkDTEJzL63AbsQhh6Wj3AsZU2iTB8+EDpHdHSSasJQ4d7EYYGnyP0AkeysYSLx5Wchkg9KBFJRLRKxKeB3Qjzk/9LWLni+UQDk9RQghIRkVRSkYSIiKSSEpSIiKSSEpSIiKSSEpSIiKSSEpSIiKTS/wdKcTgmlliIdgAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_sweep_frame(frame)\n", - "\n", - "decorate(xlabel='Contact number (beta/gamma)',\n", - " ylabel='Fraction infected',\n", - " legend=False)\n", - "\n", - "savefig('figs/chap06-fig03.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It turns out that the ratio `beta/gamma`, called the \"contact number\" is sufficient to predict the total number of infections; we don't have to know `beta` and `gamma` separately.\n", - "\n", - "We can see that in the previous plot: when we plot the fraction infected versus the contact number, the results fall close to a curve." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Analysis" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the book we figured out the relationship between $c$ and $s_{\\infty}$ analytically. Now we can compute it for a range of values:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "s_inf_array = linspace(0.0001, 0.9999, 101);" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "c_array = log(s_inf_array) / (s_inf_array - 1);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`total_infected` is the change in $s$ from the beginning to the end." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "frac_infected = 1 - s_inf_array\n", - "frac_infected_series = Series(frac_infected, index=c_array);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can plot the analytic results and compare them to the simulations." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap06-fig04.pdf\n" - ] - }, - { - "data": { - "image/png": "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IL3P9DebB5L3P0UONNO2bj3xSITO4jLcSIS2devxtOzPdYhK5Z1YLIbfr1/e8kkgECASifS+Y4Y0QeWB5P1PoR07cPw+PD4f4biD4/fj+HxE176f6xCVyks69J1f+vv3oQkqD3Td/9TaBl4v8UiEcNyDt7gYx+fDqz0opdQwpAkqDyTvf/KVlxEPBvEUFODWN+AtLMCNRqmoqsh1iEqpPLZt2zZEhG3bth3zsS677DLuvvvufojq2GmCyrGWNWtZf8+9bH34VwRGj6agtpbi8eOIFBYTj0Rxo1Ea5p2a6zCVUv3g4YcfZtq0aTz44IO5DuWI7r77bq644opchwFogsqp9OU1HK8HB3BjMdrawnj8fkonT6Lu+Em5DlUp1Q+eeOIJPvvZz7Js2bJch3JElZWVlJSU5DoMQBNUzrSsWcua799J87vvsf/1Nzjw1l+JRyIU1tVSUFlJydzTKZ8+DX9FhU4zV2oI2Lx5Mxs3bmTp0qU0Njayfv36rtcuu+wy7rjjDq6//npmz57NggULePrpp7te37NnD1/96lc544wzmD17Npdeeinvv9/95KnGxkamTZt2yPEBvvSlL3HrrbcC8Kc//YmLL76YE088kblz53LllVceEktyiM91Xe644w7OPPNMZs6cycKFC3nkkUf67WfSG01QOdCyZi0bf3I/HXv3gccBHCIHDtDyviHWGaFtVxOdiSoSXo9DYYE3twErpY7ZsmXLOPfccyksLOT8888/rBf18MMPM2XKFJYtW8bixYu59tpr2bdvHwDhcJg5c+bwwAMP8NhjjzFlyhSuuuoqOjo6Dmunvr6eefPm8cQTT3Rt279/Py+//DJLliwhGo3y1a9+lSVLlvD73/+en//858ybN6/bmJ9++mmeeuop7rrrLp555hluvfVWampq+vGn0jOtJJEDTS+sINLSgidxD4fjccABNxIhtGUL/pmzu/YtLtIqEkplYpXZzavv7SISjQ9Ie36fh9Omj2S21PW6r+u6PPnkk9x8880AXHjhhXzzm99k6dKlXTcaz5kzh89+9rMAfOUrX+GnP/0pq1ev5qyzzmLMmDF85jOf6Tret7/9bZ566inefvttTj318GvUF198MXfddRdLly7FcRx+//vfM27cOE444QT2799PW1sbixYtYuTIkQAcf/zx3ca9a9cuJk6cyJw5cwAYPXp0H35Cx057UDkQbtxNPBLBV1oK8Thu3AXHwY3FiLUHKZ578NtMcZF+h1AqE2+taRqw5AQQicZ5a01TRvu+/vrrhEIh5s6dC8App5yC67qsXLmyax8R6Xrs8/moqqrq6kFFIhHuuusuFi1axJw5czjllFNob29n586d3bZ37rnn0tzczOuvvw7Ak08+yZIlSwAYMWIEF110ER/60IdYunQpjz/+OMGUdehSnXfeeaxdu5ZFixZx22238dprr2X0efuLJqgBkpyt9+5Nt9CxZw+44Hg9+CvKcTx2YULH66X8hBk4ow5+SynW609KZeSkqbX4fQN3SvP7PJw0tTajfZctW8bevXuZOXMm06dPZ8aMGTQ2Nh4yDOfzHfpl1HEc4nGbcO+//36WLVvG0qVLeeihh1i2bBmVlZVEo9Fu2ysqKuKCCy7gySefZMuWLbz99tssXry46/U777yT+++/n4kTJ3LfffexePFimpubDzvOmDFjePbZZ/na175Ge3s7X/ziF/nOd76T0WfuD/r1fADsfH452x7+FW40hre0BG9hIfFwmHhnB96SErylJXg6fBTU1TH6wxezKXywVEiJ9qCUyshsqctouG2gdXR08Ic//IE77riDqVOndm3fuHEj3/rWt7jhhht6PcaqVau48MILOe+88wA7EeLAgQM9vueSSy7hqquuYsSIEZx66qmMGjXqkNdnzZrFrFmz+NKXvsS8efNYuXJl1/FTFRcXc8EFF3DBBRcwb948rr/+eq677rpMPvox07Nflux8fjk7n3iKjj17iXeE8RYF8I+oxI1GiYTCFI0aSbStHTdmJ0OUTTue0R++hPKpUwiuPtht1x6UUoPb888/j8/nY9GiRXi9Byc8TZkyhVtuuYXnnnuu12OMHTuWF198kQ996EMAfO9736OwsLDH98yZM4fKykoeeOABbrrppq7tW7du5dFHH2XBggXU1NTwxhtvEAqFGDdu3GHHePzxx3Fdl1mzZuHxeFi+fDkTJkzI8JMfO01Q/aRlzVq2P7aMtrXriIZCxMNhe40JIO4SCwbx+P34SkuIA7GODgKjG5hxw/WHHSsYPthtD2gPSqlB7YknnmD+/PmHJCewQ3jz588/ZJjvSL785S+zefNmLr30Uqqqqvj617/Oli1ben3f4sWLue+++zj//PO7tgUCAdasWcOjjz5KS0sLY8eO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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_sweep_frame(frame)\n", - "plot(frac_infected_series, label='Analysis')\n", - "\n", - "decorate(xlabel='Contact number (c)',\n", - " ylabel='Fraction infected')\n", - "\n", - "savefig('figs/chap06-fig04.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The agreement is generally good, except for values of `c` less than 1." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** If we didn't know about contact numbers, we might have explored other possibilities, like the difference between `beta` and `gamma`, rather than their ratio.\n", - "\n", - "Write a version of `plot_sweep_frame`, called `plot_sweep_frame_difference`, that plots the fraction infected versus the difference `beta-gamma`.\n", - "\n", - "What do the results look like, and what does that imply? " - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def plot_sweep_frame_difference(frame):\n", - " for gamma in frame.columns:\n", - " series = frame[gamma]\n", - " for beta in series.index:\n", - " frac_infected = series[beta]\n", - " plot(beta - gamma, frac_infected, 'ro')" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot_sweep_frame_difference(frame)\n", - "\n", - "decorate(xlabel='Excess infection rate (infections-recoveries per day)',\n", - " ylabel='Fraction infected',\n", - " legend=False)" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "# The results don't fall on a line, which means that if we know the difference between\n", - "# `beta` and `gamma`, but not their ratio, that's not enough to predict the fraction infected.\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Suppose you run a survey at the end of the semester and find that 26% of students had the Freshman Plague at some point.\n", - "\n", - "What is your best estimate of `c`?\n", - "\n", - "Hint: if you print `frac_infected_series`, you can read off the answer. " - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "9.211261 0.999900\n", - "4.642296 0.989902\n", - "3.987365 0.979904\n", - "3.612133 0.969906\n", - "3.350924 0.959908\n", - "3.151808 0.949910\n", - "2.991711 0.939912\n", - "2.858363 0.929914\n", - "2.744467 0.919916\n", - "2.645332 0.909918\n", - "2.557767 0.899920\n", - "2.479505 0.889922\n", - "2.408879 0.879924\n", - "2.344627 0.869926\n", - "2.285771 0.859928\n", - "2.231541 0.849930\n", - "2.181315 0.839932\n", - "2.134590 0.829934\n", - "2.090947 0.819936\n", - "2.050040 0.809938\n", - "2.011573 0.799940\n", - "1.975299 0.789942\n", - "1.941002 0.779944\n", - "1.908499 0.769946\n", - "1.877628 0.759948\n", - "1.848249 0.749950\n", - "1.820238 0.739952\n", - "1.793487 0.729954\n", - "1.767898 0.719956\n", - "1.743384 0.709958\n", - " ... \n", - "1.181034 0.290042\n", - "1.173263 0.280044\n", - "1.165630 0.270046\n", - "1.158132 0.260048\n", - "1.150765 0.250050\n", - "1.143524 0.240052\n", - "1.136407 0.230054\n", - "1.129409 0.220056\n", - "1.122527 0.210058\n", - "1.115758 0.200060\n", - "1.109099 0.190062\n", - "1.102547 0.180064\n", - "1.096099 0.170066\n", - "1.089751 0.160068\n", - "1.083503 0.150070\n", - "1.077350 0.140072\n", - "1.071291 0.130074\n", - "1.065323 0.120076\n", - "1.059444 0.110078\n", - "1.053651 0.100080\n", - "1.047943 0.090082\n", - "1.042317 0.080084\n", - "1.036772 0.070086\n", - "1.031305 0.060088\n", - "1.025914 0.050090\n", - "1.020598 0.040092\n", - "1.015356 0.030094\n", - "1.010185 0.020096\n", - "1.005083 0.010098\n", - "1.000050 0.000100\n", - "Length: 101, dtype: float64" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "frac_infected_series" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1.158096819542062" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Alternative solution\n", - "\n", - "\"\"\"We can use `np.interp` to look up `s_inf` and\n", - "estimate the corresponding value of `c`, but it only\n", - "works if the index of the series is sorted in ascending\n", - "order. So we have to use `sort_index` first.\n", - "\"\"\"\n", - "\n", - "frac_infected_series.sort_index(inplace=True)\n", - "np.interp(0.26, frac_infected_series, frac_infected_series.index)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap15soln.ipynb b/code/soln/chap15soln.ipynb deleted file mode 100644 index 07844aefc..000000000 --- a/code/soln/chap15soln.ipynb +++ /dev/null @@ -1,726 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 15\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The coffee cooling problem\n", - "\n", - "I'll use a `State` object to store the initial temperature.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
T90
\n", - "
" - ], - "text/plain": [ - "T 90\n", - "dtype: int64" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "init = State(T=90)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And a `System` object to contain the system parameters." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
initT 90\n", - "dtype: int64
volume300
r0.01
T_env22
t_end30
dt1
\n", - "
" - ], - "text/plain": [ - "init T 90\n", - "dtype: int64\n", - "volume 300\n", - "r 0.01\n", - "T_env 22\n", - "t_end 30\n", - "dt 1\n", - "dtype: object" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "coffee = System(init=init,\n", - " volume=300,\n", - " r=0.01,\n", - " T_env=22,\n", - " t_end=30,\n", - " dt=1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The update function implements Newton's law of cooling." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def update_func(state, t, system):\n", - " \"\"\"Update the thermal transfer model.\n", - " \n", - " state: State (temp)\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: State (temp)\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " T = state.T\n", - " T += -r * (T - T_env) * dt\n", - " \n", - " return State(T=T)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how it works." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
T89.32
\n", - "
" - ], - "text/plain": [ - "T 89.32\n", - "dtype: float64" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "update_func(init, 0, coffee)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a version of `run_simulation` that uses `linrange` to make an array of time steps." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " Add a TimeFrame to the System: results\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " frame = TimeFrame(columns=init.index)\n", - " frame.row[0] = init\n", - " ts = linrange(0, t_end, dt)\n", - " \n", - " for t in ts:\n", - " frame.row[t+dt] = update_func(frame.row[t], t, system)\n", - " \n", - " # store the final temperature in T_final\n", - " system.T_final = get_last_value(frame.T)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's how it works." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
T
090
189.32
288.6468
387.9803
487.3205
586.6673
686.0207
785.3804
884.7466
984.1192
1083.498
1182.883
1282.2742
1381.6714
1481.0747
1580.484
1679.8991
1779.3201
1878.7469
1978.1795
2077.6177
2177.0615
2276.5109
2375.9658
2475.4261
2574.8919
2674.3629
2773.8393
2873.3209
2972.8077
3072.2996
\n", - "
" - ], - "text/plain": [ - " T\n", - "0 90\n", - "1 89.32\n", - "2 88.6468\n", - "3 87.9803\n", - "4 87.3205\n", - "5 86.6673\n", - "6 86.0207\n", - "7 85.3804\n", - "8 84.7466\n", - "9 84.1192\n", - "10 83.498\n", - "11 82.883\n", - "12 82.2742\n", - "13 81.6714\n", - "14 81.0747\n", - "15 80.484\n", - "16 79.8991\n", - "17 79.3201\n", - "18 78.7469\n", - "19 78.1795\n", - "20 77.6177\n", - "21 77.0615\n", - "22 76.5109\n", - "23 75.9658\n", - "24 75.4261\n", - "25 74.8919\n", - "26 74.3629\n", - "27 73.8393\n", - "28 73.3209\n", - "29 72.8077\n", - "30 72.2996" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results = run_simulation(coffee, update_func)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the results look like." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results.T, label='coffee')\n", - "decorate(xlabel='Time (minutes)',\n", - " ylabel='Temperature (C)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the final temperature:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "72.2996253904031" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "coffee.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Encapsulation\n", - "\n", - "Before we go on, let's define a function to initialize `System` objects with relevant parameters:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(T_init, r, volume, t_end):\n", - " \"\"\"Makes a System object with the given parameters.\n", - "\n", - " T_init: initial temperature in degC\n", - " r: heat transfer rate, in 1/min\n", - " volume: volume of liquid in mL\n", - " t_end: end time of simulation\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " init = State(T=T_init)\n", - " \n", - " # T_final is used to store the final temperature.\n", - " # Before the simulation runs, T_final = T_init\n", - " T_final = T_init\n", - "\n", - " T_env = 22 \n", - " dt = 1\n", - " \n", - " return System(locals())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we use it:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "72.2996253904031" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "coffee = make_system(T_init=90, r=0.01, volume=300, t_end=30)\n", - "results = run_simulation(coffee, update_func)\n", - "coffee.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Exercise:** Simulate the temperature of 50 mL of milk with a starting temperature of 5 degC, in a vessel with the same insulation, for 15 minutes, and plot the results.\n", - "\n", - "By trial and error, find a values for `r` that makes the final temperature close to 20 C." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "20.00135627897414" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "milk = make_system(T_init=5, t_end=15, r=0.133, volume=50)\n", - "results = run_simulation(milk, update_func)\n", - "milk.T_final" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results.T, label='milk')\n", - "decorate(xlabel='Time (minutes)',\n", - " ylabel='Temperature (C)')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap16soln.ipynb b/code/soln/chap16soln.ipynb deleted file mode 100644 index 0fb2b33ff..000000000 --- a/code/soln/chap16soln.ipynb +++ /dev/null @@ -1,1096 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 16\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Code from previous notebooks" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "def update_func(state, t, system):\n", - " \"\"\"Update the thermal transfer model.\n", - " \n", - " state: State (temp)\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: State (temp)\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " T = state.T\n", - " T += -r * (T - T_env) * dt\n", - " \n", - " return State(T=T)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " Add a TimeFrame to the System: results\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " frame = TimeFrame(columns=init.index)\n", - " frame.row[0] = init\n", - " ts = linrange(0, t_end, dt)\n", - " \n", - " for t in ts:\n", - " frame.row[t+dt] = update_func(frame.row[t], t, system)\n", - " \n", - " # store the final temperature in T_final\n", - " system.T_final = get_last_value(frame.T)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(T_init, r, volume, t_end):\n", - " \"\"\"Makes a System object with the given parameters.\n", - "\n", - " T_init: initial temperature in degC\n", - " r: heat transfer rate, in 1/min\n", - " volume: volume of liquid in mL\n", - " t_end: end time of simulation\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " init = State(T=T_init)\n", - " \n", - " # T_final is used to store the final temperature.\n", - " # Before the simulation runs, T_final = T_init\n", - " T_final = T_init\n", - "\n", - " T_env = 22 \n", - " dt = 1\n", - " \n", - " return System(locals())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using `fsolve`\n", - "\n", - "As a simple example, let's find the roots of this function; that is, the values of `x` that make the result 0." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def func(x):\n", - " return (x-1) * (x-2) * (x-3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`modsim.py` provides `fsolve`, which does some error-checking and then runs `scipy.optimize.fsolve`. The first argument is the function whose roots we want. The second argument is an initial guess." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1.])" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fsolve(func, x0=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Usually the root we get is the one that's closest to the initial guess." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([2.])" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fsolve(func, 1.9)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([3.])" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fsolve(func, 2.9)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But not always." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([3.])" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fsolve(func, 1.5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We want to find the value of `r` that makes the final temperature 70, so we define an \"error function\" that takes `r` as a parameter and returns the difference between the final temperature and the goal." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def error_func1(r):\n", - " \"\"\"Runs a simulation and returns the `error`.\n", - " \n", - " r: heat transfer rate, in 1/min\n", - " \n", - " returns: difference between final temp and 70 C\n", - " \"\"\"\n", - " system = make_system(T_init=90, r=r, volume=300, t_end=30)\n", - " results = run_simulation(system, update_func)\n", - " return system.T_final - 70" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With `r=0.01`, we end up a little too warm." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2.2996253904030937" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "error_func1(r=0.01)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The return value from `fsolve` is an array with a single element, the estimated value of `r`." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.011543084583973956" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solution = fsolve(error_func1, 0.01)\n", - "r_coffee = solution[0]\n", - "r_coffee" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we run the simulation with the estimated value of `r`, the final temperature is 70 C, as expected." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "70.0000000000064" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "coffee = make_system(T_init=90, r=r_coffee, volume=300, t_end=30)\n", - "results = run_simulation(coffee, update_func)\n", - "coffee.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** When you call `fsolve`, it calls `error_func1` several times. To see how this works, add a print statement to `error_func1` and run `fsolve` again." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Repeat this process to estimate `r_milk`, given that it starts at 5 C and reaches 20 C after 15 minutes. \n", - "\n", - "Before you use `fsolve`, you might want to try a few values for `r_milk` and see how close you can get by trial and error. Here's an initial guess to get you started:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "18.499850754390966" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "r_milk = 0.1\n", - "milk = make_system(T_init=5, t_end=15, r=r_milk, volume=50)\n", - "results = run_simulation(milk, update_func)\n", - "milk.T_final" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def error_func2(r):\n", - " \"\"\"Runs a simulation and returns the `error`.\n", - " \n", - " r: heat transfer rate, in 1/min\n", - " \n", - " returns: difference between final temp and 20C\n", - " \"\"\"\n", - " system = make_system(T_init=5, t_end=15, r=r, volume=50)\n", - " results = run_simulation(system, update_func)\n", - " return system.T_final - 20" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "-1.500149245609034" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "error_func2(r=0.1)" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.13296078935465339" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "solution = fsolve(error_func2, 0.1)\n", - "r_milk = solution[0]" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "19.999999999999613" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "milk = make_system(T_init=5, t_end=15, r=r_milk, volume=50)\n", - "results = run_simulation(milk, update_func)\n", - "milk.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Mixing liquids" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function takes `System` objects that represent two liquids, computes the temperature of the mixture, and returns a new `System` object that represents the mixture." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "def mix(s1, s2):\n", - " \"\"\"Simulates the mixture of two liquids.\n", - " \n", - " s1: System representing coffee\n", - " s2: System representing milk\n", - " \n", - " returns: System representing the mixture\n", - " \"\"\"\n", - " assert s1.t_end == s2.t_end\n", - " \n", - " V_mix = s1.volume + s2.volume\n", - " \n", - " T_mix = (s1.volume * s1.T_final + \n", - " s2.volume * s2.T_final) / V_mix\n", - " \n", - " mixture = make_system(T_init=T_mix,\n", - " t_end=0,\n", - " r=s1.r,\n", - " volume=V_mix)\n", - " \n", - " return mixture" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Mixing at the end\n", - "\n", - "First we'll see what happens if we add the milk at the end. We'll simulate the coffee and the milk separately." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "70.0000000000064" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "coffee = make_system(T_init=90, t_end=30, r=r_coffee, volume=300)\n", - "coffee_results = run_simulation(coffee, update_func)\n", - "coffee.T_final" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "21.76470588235285" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "milk = make_system(T_init=5, t_end=30, r=r_milk, volume=50)\n", - "milk_results = run_simulation(milk, update_func)\n", - "milk.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the results look like." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap07-fig01.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(coffee_results.T, label='coffee')\n", - "plot(milk_results.T, '--', label='milk')\n", - "\n", - "decorate(xlabel='Time (minutes)',\n", - " ylabel='Temperature (C)',\n", - " loc='center left')\n", - "\n", - "savefig('figs/chap07-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what happens when we mix them." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "63.10924369748446" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "mix_last = mix(coffee, milk)\n", - "mix_last.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Mixing immediately\n", - "\n", - "Next here's what we get if we add the milk immediately." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
dt1
T_env22
T_final5
initT 5\n", - "dtype: int64
t_end0
volume50
r0.132961
T_init5
\n", - "
" - ], - "text/plain": [ - "dt 1\n", - "T_env 22\n", - "T_final 5\n", - "init T 5\n", - "dtype: int64\n", - "t_end 0\n", - "volume 50\n", - "r 0.132961\n", - "T_init 5\n", - "dtype: object" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "coffee = make_system(T_init=90, t_end=0, r=r_coffee, volume=300)\n", - "milk = make_system(T_init=5, t_end=0, r=r_milk, volume=50)" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "61.42857142857666" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "mix_first = mix(coffee, milk)\n", - "mix_first.t_end = 30\n", - "results = run_simulation(mix_first, update_func)\n", - "mix_first.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function takes `t_add`, which is the time when the milk is added, and returns the final temperature." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "def run_and_mix(t_add, t_total):\n", - " \"\"\"Simulates two liquids and them mixes them at t_add.\n", - " \n", - " t_add: time in minutes\n", - " t_total: total time to simulate, min\n", - " \n", - " returns: final temperature\n", - " \"\"\"\n", - " coffee = make_system(T_init=90, t_end=t_add, \n", - " r=r_coffee, volume=300)\n", - " coffee_results = run_simulation(coffee, update_func)\n", - "\n", - " milk = make_system(T_init=5, t_end=t_add, \n", - " r=r_milk, volume=50)\n", - " milk_results = run_simulation(milk, update_func)\n", - " \n", - " mixture = mix(coffee, milk)\n", - " mixture.t_end = t_total - t_add\n", - " results = run_simulation(mixture, update_func)\n", - "\n", - " return mixture.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can try it out with a few values." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "61.42857142857666" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run_and_mix(t_add=0, t_total=30)" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "62.90280912845778" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run_and_mix(t_add=15, t_total=30)" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "63.10924369748446" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run_and_mix(t_add=30, t_total=30)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And then sweep a range of values for `t_add`" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "sweep = SweepSeries()\n", - "for t_add in linspace(0, 30, 11):\n", - " sweep[t_add] = run_and_mix(t_add, 30)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the result looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap07-fig02.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(sweep, label='final temp', color='C2')\n", - "decorate(xlabel='Time added (min)',\n", - " ylabel='Final temperature (C)')\n", - "\n", - "savefig('figs/chap07-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Analysis" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can use the analytic result to compute temperature as a function of time. The following function is similar to `run_simulation`." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "def run_analysis(system):\n", - " \"\"\"Computes temperature using the analytic solution.\n", - " \n", - " system: System object\n", - " \n", - " returns: TimeFrame\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " T_init = init.T \n", - " ts = linrange(0, t_end, dt)\n", - " \n", - " T_array = T_env + (T_init - T_env) * exp(-r * ts)\n", - " \n", - " # to be consistent with run_simulation, we have to\n", - " # put the array into a TimeFrame\n", - " results = TimeFrame(T_array, index=ts, columns=['T'])\n", - " system.T_final = get_last_value(results.T)\n", - "\n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we run it. From the analysis (see `chap14analysis.ipynb`), we have the computed value of `r_coffee2`" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "70.56053840222036" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "r_coffee2 = 0.011610223142273859\n", - "coffee2 = make_system(T_init=90, r=r_coffee2, volume=300, t_end=30)\n", - "results = run_analysis(coffee2)\n", - "coffee2.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we can compare to the results from simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "70.0000000000064" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "coffee = make_system(T_init=90, r=r_coffee, volume=300, t_end=30)\n", - "results = run_simulation(coffee, update_func)\n", - "coffee.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "They are identical except for a small roundoff error." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "-0.5605384022139646" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "coffee.T_final - coffee2.T_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Exercise:** Suppose the coffee shop won't let me take milk in a separate container, but I keep a bottle of milk in the refrigerator at my office. In that case is it better to add the milk at the coffee shop, or wait until I get to the office?\n", - "\n", - "Hint: Think about the simplest way to represent the behavior of a refrigerator in this model. The change you make to test this variation of the problem should be very small!" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "## A refrigerator keeps the milk at a constant temperature,\n", - "## so it is like a container with r = 0." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap17soln.ipynb b/code/soln/chap17soln.ipynb deleted file mode 100644 index 51b92658e..000000000 --- a/code/soln/chap17soln.ipynb +++ /dev/null @@ -1,1600 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 17\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Data\n", - "\n", - "We have data from Pacini and Bergman (1986), \"MINMOD: a computer program to calculate insulin sensitivity and pancreatic responsivity from the frequently sampled intravenous glucose tolerance test\", *Computer Methods and Programs in Biomedicine*, 23: 113-122.." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
glucoseinsulin
time
09211
235026
4287130
625185
824051
1021649
1221145
1420541
1619635
1919230
2217230
2716327
3214230
4212422
5210515
629215
728411
827710
92828
1028111
122827
142828
162858
182907
\n", - "
" - ], - "text/plain": [ - " glucose insulin\n", - "time \n", - "0 92 11\n", - "2 350 26\n", - "4 287 130\n", - "6 251 85\n", - "8 240 51\n", - "10 216 49\n", - "12 211 45\n", - "14 205 41\n", - "16 196 35\n", - "19 192 30\n", - "22 172 30\n", - "27 163 27\n", - "32 142 30\n", - "42 124 22\n", - "52 105 15\n", - "62 92 15\n", - "72 84 11\n", - "82 77 10\n", - "92 82 8\n", - "102 81 11\n", - "122 82 7\n", - "142 82 8\n", - "162 85 8\n", - "182 90 7" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "data = pd.read_csv('data/glucose_insulin.csv', index_col='time')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the glucose time series looks like." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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QTbOtWbOnMnixYt59tln+dnPfsYdd9zBnDlzqKsLyfmhhx7aagmpwYMHd+ln6C5ttyEiBS+bE/B32WUX+vbty1tvvdVS9sYbb7R7fFlZGR988EHL882bN7N8+fKW53vssQfLli1jw4YNW51bXl5O3759WbRoUUvZ0qVLqampYffdd28pO+CAAzjvvPOYO3cuH330EfPnz2f06NEUFxezbt06dt1111ZfQ4YM6fbP3xVKUCJS8LI5AX/gwIGcdNJJzJgxg9dff53XX3+d2267DWh7qaBDDz2U+fPn89RTT7F06VKuuuqqVi2oL33pS+y1116cd955LFy4kBUrVjB37lzWrFnDwIEDOe2007juuutYsGABb775JtOmTWP8+PGMGjWKlStXcuutt7J48WJWr17NE088QX19PbvssgsDBw5k0qRJXHXVVTz55JOsXLmShQsXcsstt7B06dK0vy9t0S0+ERGyOwH/sssuY9q0aUycOJGdd96Ziy++mHPPPZd+/frxySeftDp2woQJnHXWWUyfPp3tttuOKVOmsGrVqlbHzJo1ixkzZjB58mQA9tlnH/7pn/4JgB/84AfMmDGD//qv/6KpqYkjjzySq6++GgiLuy5ZsoTf/va31NTUUF5ezowZM1oGSlxyySUMHjyYm266iXXr1jF06FDGjRuXtRZUUVfvh/ZkZrYbsLyyspKRI0fmOhwRSZO///3v7LXXXrkOo9sWLVrE6aefzgsvvMDQoUNzHc42ae/fYtWqVVRUVACMcvd347yWWlAiIlm2ePFiqqqq2GuvvXj//fe5/vrrOeKII3p8cko3JSgRkSxramri9ttvZ8WKFXz+85/n8MMP57LLLst1WHlHCUpEJMsOPvhgHn300VyHkfc0ik9ERPKSEpSI9AqFNOArX6X730AJSkR6vOLiYurr63MdRsGrr6+nuLi48wNj6lKCMrMBZqad+kQkrwwbNozVq1dTV1enllQONDc3U1dXx+rVqxk2LHXT8+7rdJCEmZ0ATAKOJtpu3cyagIXAH4B73X112iISEemiQYMGAbBmzZpWqyxI9hQXFzN8+PCWf4t0aDdBRYlpJmHL9T8A1wKrgAagDNgPOA643MzuAa5098zt8CUi0oFBgwal9Y+j5F5HLagfAVcAc6ONBlM9DGBmuwMXAVOAm9IeoYiIFKSOEtR+0TbsHXL3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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(data.glucose, 'bo', label='glucose')\n", - "decorate(xlabel='Time (min)',\n", - " ylabel='Concentration (mg/dL)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And the insulin time series." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(data.insulin, 'go', label='insulin')\n", - "decorate(xlabel='Time (min)',\n", - " ylabel='Concentration ($\\mu$U/mL)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For the book, I put them in a single figure, using `subplot`" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap08-fig01.pdf\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjgAAAI4CAYAAABndZP2AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzs3X14nGWZ8P9voClpUtqaQAsPFFqxPcVVQWxB3YfXKiggCiy6bEF5xAfkRRQRFcWfqLytyyIu8CCsuixvq6yAuuwiSlF00Ra7QFHQqxVKaQukJRFKk4amJb8/7kmYpGmbpDOZyZ3v5zjmSOa67pk57840OXNeL3dNV1cXkiRJebJdpQOQJEkqNRMcSZKUOyY4kiQpd0xwJElS7pjgSJKk3DHBkSRJuWOCI0mScscER5Ik5Y4JjiRJyh0THEmSlDtjKh1ApUTEDsBs4DlgY4XDkSRJm7c9sCvwu5TSKwN5wKhNcMiSm19XOghJkjRgBwL/PZADR3OC8xzArbfeyi677FLpWCRJ0mY8//zzzJ07Fwq/uwdiNCc4GwF22WUXdt9990rHIkmStm7AU0qcZCxJknLHBEeSJOWOCY4kScodExxJkpQ7FZ1kHBFfBU4EpgJrgPuA81JKz0fEKcC/9HnIj1NKHyx6/EzgBuAA4HngqymlG4chdElSjqxZs4ZVq1bR2dlZ6VBGpdraWiZPnsyECRNK9pyVXkX1J+B0YCkwGfhH4F+BIwr9K8j2q+nW0f1NRNQC/wk8XDjmAOCGiFiaUnqg/KEPzOJlrcxbuJzm1namNNYzZ9ZUZu7ZWOmwJEkFa9asobm5md12241x48ZRU1NT6ZBGla6uLtatW8fKlSsBSpbkVDTBSSn9W9HdpyPiG0Bx28aU0vObefj7gN2AfVNKbcAfIuJg4JNAVSQ4i5e1csu9iYa6MTRNrGNN23puuTdx0hFhkiNJVWLVqlXstttu1NfXVzqUUammpob6+np22203nn322XwkOMUiYiLwd8CDRc27RsRKssrNPOCClFJLoW9/YEEhuek2D7h4OOIdiHkLl9NQN4aGcbUAPV/nLVxugiNJVaKzs5Nx48ZVOoxRb9y4cSUdIqz4JOOImBsRa4EXgelkc3IgG776KPBe4GyyhObHEdFdO5wMrOrzdKsL7VWhubWd+rreOWR93RiaW9srFJEkqT8OS1Veqd+Daqjg/AR4iGyi8VeB64ETUkrzgfmFY34fEY8DTwOzgN8BVf9pnNJYz5q29T2VG4D2jg1MabQMKklSOVW8gpNSejmltCSldD/wt8DfRMSb+jnuGaAFmFZoambTas3ObFrVqZg5s6bS1rGBtnWddHV10bauk7aODcyZNbXSoUmScmjBggVERKXDqAoVT3D66K7KbHKtiYjYFWgClhWaHgIOiIjicshhwIKyRjgIM/ds5KQjggkNY2l5qYMJDWOdYCxJ0jCo2BBVYZn3RcCPyKouU4Gvky37XhIR5wOPAn8G/hfwD8D/FG4APwWeBb4bEReTLRM/EXjP8J3F1s3cs9GERpKkYVbJCk4XsDfZHJzFwK1kyczRKaVXgYnAd8kmG/+gcMz7U0obAVJK64GjgF3Jkp7/DzitmvbAkSSNDouXtXLdHYu46J9/y3V3LGLxstayvdZLL73EWWedxVvf+lYOP/xwHnjgASKCBQs2HcD4whe+wBe+8IVebSeffDJXX311z/2WlhbOO+88Zs+ezdve9jZOPvlkmpubAVi3bh0XXnhhT9+nPvUpWltfO7cHH3yQD37wg7z1rW/lgAMO4LTTTuvp27hxI1dddRUHHXRQz/OmlEr9z7FZFavgpJQ2AMdtof9C4MKtPEcCDiltZJIkDdxw73l26aWX8swzz3DzzTcDcNlll23T85199tlst9123HDDDUyaNImFCxeycWM2U+Tyyy/nd7/7Hddddx3jxo3jq1/9KhdccAHXX389GzZs4JxzzuHss8/mPe95D2vXrmX+/Pk9z3vNNdfw61//miuvvJKdd96ZO++8k1NPPZV7772XhoaGbYp5IKphFZUkSSPWcO55tnbtWu6++26+973vsc8++wDwqU99ilNOOWVIzzd//nwef/xx7r//fnbaaScApk+f3vNad9xxB9/+9reZNWsWkCVTRx55JEuXLmXSpEmsXbuW973vfeyyyy4AvPGNbwTglVde4Xvf+x533nkne+21FwDnnnsuP/3pT/nlL3/JUUcdNeR/g4EywZEkaRs0t7bTNLGuV1u59jxbvnw5GzZs4K/+6q962t7ylrcM+fmWLFnC9OnTe5KbYitWrKCzs5N99923p22vvfZiwoQJPPXUU8yZM4ejjjqKo48+mgMPPJCDDjqII444gvr6ep555hk6Ojr4m7/5m17P2dHRwfLly4cc72CY4EiStA2Gc8+zrq6uQR1fU1PDq6++2quteLfgrq6uzW6wN5DXuvLKK1m0aBEPPPAA3/nOd7j22mu54447aG/Pkrvbbrttk0tgTJw4cVDnMFTVtkxckqQRZTj3PNtjjz0YM2YMTzzxRE/bH/7wh80e39jYyAsvvNBzf/369SxdurTn/owZM3jqqadoaWnZ5LFTp05lzJgxPProoz1tTz75JGvWrOH1r399T9s+++zDOeecw1133cWLL77IggUL2GuvvaitrWXVqlXsueeevW6TJk0a8vkPhgmOJEnbYDj3PBs/fjxHH300l1xyCY899hiPPfYYV111FdD/pQ5mz57NggULuO+++3jyySf5yle+0quC8853vpO9996bc845h0ceeYRly5Zx11138eyzzzJ+/HiOP/54vv71r7Nw4UIef/xxLrjgAg466CCmT5/O8uXL+eY3v8miRYtYuXIlP/3pT1m3bh177LEH48eP56STTuIrX/kKP/vZz1i+fDmPPPIIV1xxBU8++WTJ/1364xCVJEnbaDj3PPviF7/IBRdcwNy5c9l111357Gc/yyc/+UnGjh3LK6+80uvYgw8+mI9+9KNceOGFbLfddpx22mmsWLGi1zFXX301l1xyCaeeeioAf/VXf8Vf//VfA/D5z3+eSy65hE984hNs3LiRAw88kIsuugjILo65ePFifvjDH7JmzRqmTp3KJZdc0jPR+HOf+xwTJ07k8ssvZ9WqVTQ1NXHAAQcMWwWnZrDjeXkREdOApfPmzWP33XevdDiSpAr54x//yN57713pMIbs0Ucf5cMf/jC/+c1vaGpqqnQ422Rz78WKFSuYM2cOwPSU0tMDeS4rOJIkjSCLFi2iubmZvffem+eee46LL76Y//2///eIT25KzQRHkqQRZOPGjVxzzTUsW7aMHXfckXe961188YtfrHRYVccER5KkEWS//fbjJz/5SaXDqHquopIkSbljgiNJGvVG64KbalLq98AER5I0qtXW1rJu3bpKhzHqrVu3jtra2q0fOEAmOJKkUW3y5MmsXLmS9vZ2KzkV0NXVRXt7OytXrmTy5Mkle14nGUuSRrUJEyYA8Oyzz/ba5VfDp7a2lilTpvS8F6VggiNJGvUmTJhQ0l+uqjyHqCRJUu6Y4EiSpNwxwZEkSbljgiNJknLHBEeSJOWOCY4kScodExxJkpQ7JjiSJCl3THAkSVLumOBIkqTcMcGRJEm5Y4IjSZJyp2IX24yIrwInAlOBNcB9wHkppecL/UcBVwDTgT8AZ6SUflf0+F2AbwOHAy8C30op/f2wnoQkSapKlazg/Ak4HdgbeD+wB/CvABHxRuBO4CZgP+A3wD0R8bqix/8AmAi8CzgLuDAiPjJs0UuSpKpVsQpOSunfiu4+HRHfALrbTgPmp5QuA4iITwEfAOYC10TEW4GDgL1SSk8Bj0bEN4FzyJIiSZI0ilXFHJyImAj8HfBgoWl/4P7u/pRSV+H+AUX9TxeSm27zgH0jYmz5I5YkSdWsoglORMyNiLVkc2imk83JAZgMrOpz+OpC+5b6tweayhOtJEkaKSpdwfkJ8DZgDtAJXF9or9nK47bWL0mSRrGKzcEBSCm9DLwMLImIBKyIiDcBzbxWrem2M69VbTbXvxFoKV/EkiRpJKh0BadYd1VmI/AQcGif/kOBBYXvHwKmRcT0ov7DgEdTSuvLGqUkSap6FangREQtcBHwI7KqzFTg68DDwBLgBmBRRHyebBjrdGA8cCtASumxiPgV8N2IOBeYBpwLnD2sJyJJkqpSpSo4XWT73/wEWEyWuPwZODql9GpK6U/A8cApwKPAgcCRKaW/FD3Hh8mGt35LtuHfpSkll4hLkqTKVHBSShuA47ZyzN3A3Vvof55sbxxJkqReKjrJeLRavKyVeQuX09zazpTGeubMmsrMPRsrHZYkSblRTZOMR4XFy1q55d7Emrb1NE2sY03bem65N7F4WWulQ5MkKTdMcIbZvIXLaagbQ8O4WmpqamgYV0tD3RjmLVxe6dAkScoNE5xh1tzaTn1d75HB+roxNLe2VygiSZLyxwRnmE1prKe9Y0OvtvaODUxprK9QRJIk5Y8JzjCbM2sqbR0baFvXSVdXF23rOmnr2MCcWVMrHZokSblhgjPMZu7ZyElHBBMaxtLyUgcTGsZy0hHhKipJkkrIZeIVMHPPRhMaSZLKyAqOJEnKHRMcSZKUOyY4kiQpd0xwJElS7pjgSJKk3DHBkSRJuWOCI0mScscER5Ik5Y4JjiRJyh0THEmSlDsmOJIkKXdMcCRJUu6Y4EiSpNwxwZEkSbljgiNJknLHBEeSJOWOCY4kScodExxJkpQ7JjiSJCl3THAkSVLujKnEi0bEl4ATgBnAX4A7gS+mlNYW+qcBS/s87KWU0qSi5xgPXAMcB3QCNwKfSyltLHf85bB4WSvzFi6nubWdKY31zJk1lZl7NlY6LEmSRqRKVXDeBXwD2A/4O+Bw4Op+jtsf2LVwm9mn71pgNvBusmTpROBLZYq3rBYva+WWexNr2tbTNLGONW3rueXexOJlrZUOTZKkEakiFZyU0lHFdyPiy8D1/Ry6OqX0fN/GiHgdMBd4T0rpoULbhcBlEXFxSunVcsRdLvMWLqehbgwN42oBer7OW7jcKo4kSUNQLXNwdgJe7Kf9VxHxbET8Z0S8uaj97UAX8KuitnnAZGB6+cIsj+bWdurreuea9XVjaG5tr1BEkiSNbBVPcCJiIvBZ4HtFzWuBc4Bjgb8B2siSnSmF/slAa5/5NquL+kaUKY31tHds6NXW3rGBKY31FYpIkqSRrSJDVN0iYgfgDuAp4PLu9pTSCxTNyYmIBcATwEeAfwBq+nm6rrIGW0ZzZk3llnsTkFVu2js20NaxgWMP3qvCkUmSNDJVrIITEWOA7wM7AsemlDZs7thCpeYxYFqhqRlojIjtiw7rrtysKn205TVzz0ZOOiKY0DCWlpc6mNAwlpOOCOffSJI0RJVaJr4dcBPwBuDg7uXhWzi+BngT8LtC08NkVZwDgV8W2g4jS276Li8fEWbu2WhCI0lSiVRqiOoG4BDgSGBsROxSaF+dUtoYEScUYvsfYAfgU8DuwG0AKaXWiLgNuDoiTgUagIuBa0faCqotcW8cSZKGplJDVKeS7W3zCPBc0W1qob8L+DLwKFmFZg/gsJTSiqLnOJOskjOPbB7PD4BLhiH2YeHeOJIkDV2l9sHpb5Jwcf8PgR9u5Zi1wEcLt9xxbxxJkoau4svE1T/3xpEkaehMcKqUe+NIkjR0JU1wIvNEKZ9ztJozayptHRtoW9dJV1cXbes6aevYwJxZU7f+YEmSRrlSV3DqgCjxc45K7o0jSdLQVXQnY22Ze+NIkjQ0zsGRJEm5Y4IjSZJyZ1BDVBHxs60cMn4bYpEkSSqJwc7BWTmAY9JQApEkSSqVQSU4KaX/U65AJEmSSsU5OJIkKXcGXMEZwPybHimlw4cWjiRJ0rYbzBBV8fybGuBY4GXgd4W22cCOwJ2lCU2SJGloBpzgFM+/iYivAz8BPpZS6iy01QLfAZ4tdZCSJEmDMdQ5OB8HLu1ObgAK3/99oU+SJKlihprg7AhM7qd9MuDlriVJUkUN9VpU/wn8c0R8Avhtoe1dwLWFPpXJ4mWtzFu4nObWdqY01jNn1lSvVyVJUh9DreCcDjwO3AesLdx+BvwJOKM0oamvxctaueXexJq29TRNrGNN23puuTexeFlrpUOTJKmqDKmCk1J6ETg2IvYC3kS2qurxlNKTpQxOvc1buJyGujE0jKsF6Pk6b+FyqziSJBUZ7LWofki2DPw/U0ovFRIak5ph0tzaTtPEul5t9XVjaG5tr1BEkiRVp8FWcB4HPg/8S0T8ErgL+HFK6blSB6ZNTWmsZ03b+p7KDUB7xwbG1m7HdXcscl6OJEkFg5qDk1L6SkppH7JhqZ8DJwPLIuI3EXF+RLyhHEEqM2fWVNo6NtC2rpOuri7a1nWy6i/ttL7U4bwcSZKKDGmScUrpyZTSFSmlvwb2AG4C3gM8ERG/j4ivRcTrSxmoYOaejZx0RDChYSwtL3UwoWEsO00cx86vq6dhXC01NTU0jKuloW4M8xYur3S4kiRVzFCXifdIKT0PfBv4dkS8Djia7DIOxwFXbOvzq7eZezb2Gn666J9/67wcSZL62OYEp1hK6S/AzYWbhsHm5uVMaXS/RUnS6DWkBCcifg509dPVBXQACbgxpfTENsSmAZgzayq33JuArHLT3rGBto4N7Bc7O/FYkjRqDXWjv5XA/sAbeW2jvyC7ovgrwAeBRyLinaUIUpvX37ycA/fZlV8ves6Jx5KkUWuoQ1TPkO2Hc1pKaQNARGwPXA+sAP4W+BfgcuDgEsSpLeg7L+e6Oxa5IaAkaVQbaoLzf4HDupMbgJTSxoj4R+AXKaWLIuKbwLz+HhwRXwJOAGYAfyFLlr6YUlpbdMxM4AbgAOB54KsppRuL+scA3wA+CtQCdwBnp5TahnhOueGGgJKk0W5bria+cz/tOwPjC9+vIUs8+vMusuRkP+DvgMOBq7s7I6KW7KKdzWTDXhcDN0REcTXoy2SVohOAd5MNmV2NmNJYT3vHhl5tTjyWJI0mQ63g3EOWcHwCWEA2ufidZFcT/6/CMW9jM5dxSCkdVXw3Ir5MNrzV7X3AbsC+hYrMHwrJzSeBByJiO+BM4HMppfsBIuKTwL0R8ZnCtbJGrc1NPD724L0qHJkkScNjW64mvhi4n2yCcRvZcNQS4BOFY1YB5w7w+XYCipOS/YEFfYab5pENVwG8vvCY+4v6HyC76OfbB3wWOdXfxOOTjgjn30iSRo2hXk28FTimcGmGNxWan0gp/bnomF8P5LkiYiLwWeB7Rc2TyRKkYqsL7RR97TmmMAeotahvVOs78Rhg8bJW5i1c7tJxSVLuDXmjv4g4AphDllBsV2gDIKX0kQE+xw5kk4OfIltx1a1mKw/dWr/6WLyslVvuTTTUjem1dNzKjiQpj4Y0RBURF5PNwzkc2IVscnHxbSDPMQb4PtmE5WOLV2SRTS7uW4nZmdcqNs2Frz3HFJapN7Jp5UdkS8S7l457zSpJUt4NtYJzGnBKSummoTy4MEn4JuANwMHFy8MLHgI+ExH1KaXutc2HkU1ohqzi8wJwKHBjoe0gssnODw8lprzb2tJxh68kSXky1ATnVeA32/C6NwCHAEcCYyNil0L76pTSRuCnwLPAdwvVogOAE8muWE5K6dWIuA64JCKWkU1y/ifg5sL1sNTHlq5Z5fCVJClvhrqK6v8BH9+G1z0V2BV4BHiu6DYVIKW0HjiqcMz/AP8f2a7JDxQ9x9eA28nm8MwrHPfJbYgp1+bMmkpbxwba1nXS1dVF27pO2jo2MGfWVIevJEm5M9QKzteBuyNiEfAY0FncmVL62JYenFLa6iThlFIiq/Jsrn8D2TL0gS5FH9W6l44XD0Mde/BezNyzkdt+ltz5WJKUK0NNcL5GthnfE2RVlv6uLK4q09/Scdjy8JUkSSPRUBOcs4GPFV8bSiOXOx9LkvJmqHNw1gP/XcpAVDnufCxJypuhVnBuIJsofEEJY1EFbW74SpKkkWioCc6uwPGF3YwXsekk49O2NTBJkqShGmqCsxfwaOH7aX36nHAsSZIqaqgX2zy01IFIkiSVylAnGUuSJFUtExxJkpQ7JjiSJCl3THAkSVLuDHUVlUaRxctae13Das6sqe6ZI0mqalZwtEWLl7Vyy72JNW3raZpYx5q29dxyb2LxstZKhyZJ0maZ4GiL5i1cTkPdGBrG1VJTU0PDuFoa6sYwb+HySocmSdJmmeBoi5pb26mv6z2SWV83hubW9gpFJEnS1pngaIumNNbT3rGhV1t7xwamNNZXKCJJkrbOBEdbNGfWVNo6NtC2rpOuri7a1nXS1rGBObOmVjo0SZI2ywRHWzRzz0ZOOiKY0DCWlpc6mNAwlpOOCFdRSZKqmsvEtVUz92wcdELj0nJJUiVZwVHJubRcklRpJjgqOZeWS5IqzQRHJefScklSpZngqORcWi5JqjQTHJWcS8slSZVmgqOSc2m5JKnSXCaushjK0nJJkkrFCo4kScodExxJkpQ7FRuiiojjgLOAWcCElFJNUd8hwC/6PGRRSmnfomN2Ab4NHA68CHwrpfT35Y5bkiRVv0rOwakH7gfuAy7dzDG7Fn3f2afvB8CrwLuA6cBNEfFcSummUgcqSZJGloolOCmlW6CnWrO5Y57vrz0i3gocBOyVUnoKeDQivgmcA5jgSJI0ylX1HJyIeDoinomI70fEHkVd+wNPF5KbbvOAfSNi7PBGKUmSqk21JjjPAacCHwA+CkwGfhER4wr9k4FVfR6zGtgeaBquICVJUnWqyn1wUkoJSN33I2Ih8AxwNPDvQM1mHipJklS1FZxeUkovA38GphWamsmqOMV2BjYCLcMXmSRJqkZVWcHpqzA09XpgWaHpIWBaRExPKS0ttB0GPJpSWl+JGLVtFi9rZd7C5TS3tjOlsZ45s6a6E7IkacgquQ9OI7AH8IbC/e49bp4ATgGeBx4HJgFfAdYC/wWQUnosIn4FfDciziWr7JwLnD18Z6BSWbyslVvuTTTUjaFpYh1r2tZzy73J61dJkoaskhWcY4B/Kbr/SOHrdKAWuBLYHXgJeBCYk1JaW3T8h4Hrgd8WjrnUPXBGpnkLl9NQN4aGcbUAPV/nLVxugiNJGpJK7oNzI3DjZrqvLdy29PjnyVZZaYRrbm2naWJdr7b6ujE0t7ZXKCJJ0kg3IiYZK9+mNNbT3rGhV1t7xwamNNZXKCJJ0khngqOKmzNrKm0dG2hb10lXVxdt6zpp69jAnFlTKx2aJGmEMsFRxc3cs5GTjggmNIyl5aUOJjSMdYKxJGmbjIhl4sq/mXs2mtBIkkrGCo4kScodExxJkpQ7JjiSJCl3THAkSVLumOBIkqTcMcGRJEm54zJxjRpesVySRg8rOBoVuq9YvqZtfa8rli9e1lrp0CRJZWCCo1Gh+IrlNTU1NIyrpaFuDPMWLq90aJKkMjDB0ajQ3NpOfV3vEVmvWC5J+WWCo1HBK5ZL0uhigqNRwSuWS9LoYoKjUcErlkvS6OIycY0aXrFckkYPKziSJCl3THAkSVLumOBIkqTcMcGRJEm5Y4IjSZJyxwRHkiTljsvEpRLxauWSVD2s4Egl4NXKJam6mOBIJeDVyiWpupjgSCXg1colqbpUbA5ORBwHnAXMAiaklGr69B8AXAu8GXgKOC+ldE9R/3jgGuA4oBO4EfhcSmnjsJyAVGRKYz1r2tbTMK62p82rlUtS5VSyglMP3A9c3rcjIpqAe4AHgf2Am4G7ImJG0WHXArOBdwMnACcCXypzzFK/vFq5JFWXiiU4KaVbUkqXAL/tp3susAb4dErpiZTSZcBDwOkAEfG6wjFnp5QeSindD1wInBURDrtp2Hm1ckmqLtW6THx/4Bcppa6itnlk1RqAtwNdwK/69E8GpgNPDkeQUjGvVi5J1aNaqx2TgVV92lYX2rv7W/vMt1ld1CdJkkaxak1waobQ39VPmyRJGoWqdYiqmU0rMTvzWlWnGWiMiO2Lqjjdx/et/Awbd7JVufkZk6SBqdYKzkPAIX3aDgMWFL5/mKyKc2Cf/lXA0nIH1x93slW5+RmTpIGr5D44jcAewBsK9/ctdD0B3ApcFBFXAdcDxwAHAB8HSCm1RsRtwNURcSrQAFwMXJtSenVYT6SgeCdboOfrvIXL/QtbJeFnTFK1qsbqciUrOMcAjwD/XLj/SOH2v1JKLcCRZBWaR4FTgONSSkuKHn8mWSVnHnAH8APgkmGJvB/uZKty8zMmqRpVa3W5YhWclNKNZLsPb65/Ptly8M31rwU+WrhVnDvZqtz8jEmqRtVaXa7WOTgjjjvZqtz8jEmqRtVaXa7WVVQjTvdOtsVjkMcevFfFxyCVH37GpHyqxvkrg1Gt1WUTnBJyJ1uV23B8xkb6D1tpJOmev9JQN6bX/JWRdKmXObOmcsu9CcgqN+0dG2jr2MCxB+9V0bhMcCT1yMMPWzBJG6g8/DuN9HOo1vkrg1Gt1WUTHEk98vDDdjiStJH+SxXykczm4RyaW9tpmljXq60a5q8MVjWOYDjJWFKPap0sOBjFSVpNTQ0N42ppqBvDvIXLS/L81bokdrDK/e80HPJwDlMa62nv2NCrrRrmr+SBCY6kHnn4YVvuJC0Pv1QhH8lsHs7B1ZHlY4Ijqcdw/LBdvKyV6+5YxEX//Fuuu2NRySsf5U7S8vBLFfKRzObhHLrnr0xoGEvLSx1MaBg7oobYqpkJjqQe5f5hOxzDO+VO0vLwSxXyUTnIwzlA9v/ujOP34aL/+07OOH4fk5sScZKxpF7KOVlwOCYxl3tFR7UuiR2sal35Mhh5OAeVjwmOpGEzXCtGypmk5emXajWufBmsPJyDysMER9KwqdYdTwfLX6pS9XMOjqRhk5c5E5KqnwmOpGHjihFJw8UhKknDyuEdScPBCo4kScodExxJkpQ7JjiSJCl3THAkSVLumOBIkqTcGc2rqLYHeP755ysdhyRJ2oKi39XbD/QxoznB2RVg7ty5lY5DkiQNzK7AkwM5cDQnOL8DDgSeAzZWOBZJkrR525MlN78b6ANqurq6yheOJElSBTjJWJIk5Y4JjiRJyh0THElrH/PwAAAgAElEQVSSlDsmOJIkKXdMcCRJUu6Y4EiSpNwxwZEkSbljgiNJknJnNO9kXBYRcQHwSWAS8DPgtJTSqspGVRoR8SXgBGAG8BfgTuCLKaW1hf5pwNI+D3sppTRpOOMstYi4Efhon+ZzU0pXFR1zFHAFMB34A3BGSmnAO25Wo4h4Gtizn64DgHrgF33aF6WU9i1zWCUVEccBZwGzgAkppZo+/QcA1wJvBp4Czksp3VPUPx64BjgO6ARuBD6XUqrq3dG3dN4RcQhwPrA/MBZYCJyfUnq46JhfAgf3edpjU0o/Km/kQzeAc97i5zkidgG+DRwOvAh8K6X09+WOe1tt5bwvAr7Sz8OuSymdWTjmaTb9OfC2lNKj5Yi3lKzglFBE/B/gi2QfpneRJTn/VtGgSutdwDeA/YC/I/uPfnU/x+1PtqX2rsDMYYuuvG7ntXPaFbihuyMi3kiW7N1E9m/zG+CeiHhdBeIspdn0PueLgZVkv/C6FffPGe4AS6AeuB+4vG9HRDQB9wAPkr2vNwN3RcSMosOuJft3ejdZ8n8i8KUyx1wKmz1v4J3AfOAosnN7Eri38O9R7B/p/f7fQ3Xb0jl329Ln+QfARLKfg2cBF0bER8oQZ6lt6byvoPc57wO8CvRNVD/d57g/lCvYUrKCU1qfBK5MKd0FEBEfA56MiDenlEbEB2JLUkpHFd+NiC8D1/dz6OqUUt4u075uC+d0GjA/pXQZQER8CvgAMJfsr/sRKaW0uvh+RLwfuDWl9GpEdB8zot/nlNIt0PMXfF9zgTXAp1NKXcATEfE+4HTgs4UEdi7wnpTSQ4XnuRC4LCIuTim9OhznMBRbOu/uz3G3iDgD+Fvgr4GfFHWtHUnv/1be6+5j+j2fiHgrcBCwV0rpKeDRiPgmcA7ZHzZVayvv9Vpgbff9iPhboBmY1+fQl0bSe93NCk6JRMQOZNnv/d1thf8IT5OV9PNoJ7JSbV+/iohnI+I/I+LNwx1UmRwTEasj4rGIuCAiiv842J/e73tX4X5u3vfC+7gPfX6YR8TTEfFMRHw/IvaoTHRlsz/wi8L72W0er72vbwe6gF/16Z9MNlSZF/XAODb9v35mRLwQEf8TEf+3AnGV3BY+z/sDTxd+pnebB+wbEWOHN8qyOpnsj5i+Q6yXFX7+/TYiPliJwIbCBKd0msj+PfvOt1lN9gMvVyJiIvBZ4HtFzWvJ/qI5FvgboI0s2Zky/BGW1H+RDckdBlwJfAb4WlH/ZPL/vn8EeCSl9Hjh/nPAqWSVqo+SnesvImJcheIrh629r5OB1j6/DFYX9eXFRcCfyYZeu90MfJhsGOdfgW9FxGnDH1rJbO3zvLnPwvZkP/tHvIh4E68NxRb7JtnP88PJhiHviIjDhzm8IXGIqnRqtn5IPhSqVXeQTbrsGddNKb1A0ZyciFgAPEH2y/EfhjnMkkkp3V509/cRsRG4LiK+VPjrPtfvfURsR5bgXdHdllJKQCo6ZiHwDHA08O/DHWOZbO197a+/q5+2ESsiTiX7xX9wSmlDd3tK6btFhy0q/MHzKYrmpo0kA/g85/r/eMFHyCZWP1bcmFL6VtHdRyJiT7L3+mfDGdxQWMEpnRfIJmf1/cttZzbN/EeswtDM94EdyVZNbNjcsYW/bB8Dpg1PdMPmYaCBbIgOsjHrPL/vc4ApbGHCfErpZbK/8qcNU0zDYWvvazPQGBHbF/V3Hz/i3/uI+BBwFfCBlNKirRz+MDl67/v5PG/us7ARaBm+yMqj8EfMXDat3vRnxLzXJjglklJ6BVgEHNrdFhHTyT4ICyoUVkkV/hPcBLwBeF/38vAtHF8DvAlYNgzhDac3A+1kSS3AQxS97wWHkpP3newvu5+llJo3d0ChlP968vVePwQc0qftMF57Xx8m+8v+wD79q9h0u4QRpTCh/F+AD6eUHhjAQ95Mjt77fj7PDwHTCj/Tux0GPJpSWj/c8ZXBoWSro24dwLEj5r12iKq0rgGuiohHyCYXf5NskuKIX0FVcAPZD/wjgbGFfSEgWzW1MSJOIPtM/Q+wA1kZc3fgtgrEWjIRcSXZEtFmsjHqK4Hriyaf3kBWpv882SqT04HxDOyHRVWLiAayOVUf79N+GvA88DjZdghfIZuD9V/DHeO2iIhGYA+ypJ2I6N735Amy9++iiLiKbLXgMWQTjD8OkFJqjYjbgKsLQzkNZEvpr63mFVSw1fM+kGxY5nPAw0X/z9emlNYW7n+C7LP+Itnqoi8CXx6+Mxi8rZzzKWzh85xSeiwifgV8NyLOJfvD9Vzg7OE7g6HZ0nkXJWcnA/f1XSlVWD32buA+sj/qjib7/B8/DKFvMxOcEkopfa8wofbbZPsl/JxsCXFenFr4+kif9ulkCV0X2Q+5acA6skTnsJTSimGKr1zeBNwNTCAbl78e6NngK6X0p4g4nmye0dfI9og4MqX0lwrEWmrHkZXhf9ynvZYs0dsdeIlsr5g5W6vqVaFjyCoV3bo/29NTSk9HxJFke92cQTbn7LiU0pKi488s9M/jtY3+Lil30CWw2fMm+2W3A/Ctwq3bV8kmHHeS/aFzDtnqqqfI9v75f+UMuAS2dM4D+Tx/mOz//m8Lx1yaUqrqJeIFWzrvpyOinuz/+Sf6eex64ENkCd8Y4E/ASSmln/RzbNWp6erK1Zw4SZIk5+BIkqT8McGRJEm5Y4IjSZJyxwRHkiTljgmOJEnKHRMcSZKUOyY4kiQpd0xwJElS7pjgSJKk3DHBkSRJuWOCI0mScscER5Ik5Y4JjiRJyh0THEmSlDsmOJIkKXdMcCRJUu6Y4EiSpNwZU+kAKiUidgBmA88BGyscjiRJ2rztgV2B36WUXhnIA0ZtgkOW3Py60kFIkqQBOxD474EcOJoTnOcAbr31VnbZZZdKxyJJkjbj+eefZ+7cuVD43T0QoznB2Qiwyy67sPvuu1c6FkmStHUDnlLiJGNJkpQ7JjiSJCl3THAkSVLumOBIkqTcGc2TjCVJ2qrOzk5WrFhBR0dHpUPJre23355Jkyax0047sd12pam9mOBUwJKWpTywdD6r2lqY3NDEwdPfwYym6ZUOS5LUjxUrVrDjjjsybdo0ampqKh1O7nR1ddHZ2UlzczMrVqxgjz32KMnzOkQ1zJa0LOX239/Ny6+spWncJF5+ZS23//5ulrQsrXRokqR+dHR00NTUZHJTJjU1NYwdO5bddtuNtra2kj2vCc4we2DpfOpr62gYW09NTQ0NY+upr63jgaXzKx2aJGkzTG7Kr1RDUz3PV9Jn01atamuhvnZcr7b62nGsamupUESSJOWPCc4wm9zQRHvnul5t7Z3rmNzQVKGIJEnKHxOcYXbw9HfQ3tlB2/p2urq6aFvfTntnBwdPf0elQ5MkjXAnn3wyV199ddlfZ8WKFUQEK1asAODOO+/ksMMOK/vrDoarqIbZjKbpfOgtR/daRXX0G9/tKipJ0ja7+uqrqa2tHfbXPfLIIznkkEOG/XW3xASnAmY0TTehkaRRYji3Bpk0aVJZnndr6urqqKurq8hrb45DVJIklclwbw1SPEQVEfzoRz/iIx/5CPvssw/HH388ixcv7jn28ccf58QTT2Tfffdl9uzZnHTSSaxZs2aT5+l22GGHceedd/b7un2HqL7whS/w+c9/niuvvJLZs2dz4IEHctNNN5X6dLfIBEeSpDKp9NYg11xzDaeccgo/+tGPaGxs5Etf+lJP3/nnn8++++7Lf/zHf3Dbbbfx/ve/v6Sv/fOf/xyA22+/nTPPPJNLL72UJUuWlPQ1tsQhKkmSymRVWwtN43oPGw3n1iBz587tqaycccYZnHjiiXR0dFBXV8dzzz3HnDlzmDp1KgAzZswo6WvvtttufOYznwFg+vTp3HjjjSxcuLDkr7M5VnAkSSqTSm8NMnPmzJ7vd955ZwBaW1sBOOmkkzj11FM588wzufXWW/nLX/5Sttfufv2WluHb880ER5KkMqn01iBjxrw2UNO9G/Orr74KwHnnnce///u/s++++/LjH/+Y9773vSxfvrzn2K6url7P1dnZOajX7ruaq7/nLCcTHEmSyqR7a5AddxhPy7oX2XGH8XzoLUdXzUramTNnctppp3H77bez0047cd999wHQ2NjICy+80HNca2trr/sjgXNwJEkqo2rcGqSjo4MrrriC9773vey66678+c9/5tlnn2XatGkAzJ49m3/6p3/iyCOP5HWvex1XXXUVY8eOrWzQg2SCI0nSKLPddtvR0tLCZz7zGVpbW5kyZQpnn302hx56KAAnnHACjz/+OGeffTbjx4/n/PPPJ6VU4agHp2Y4x8OKRcRxwFnALGBCSqmmqO8Q4Hxgf2AssBA4P6X0cNExY4BvAB8FaoE7gLNTSgO61npETAOWzps3j913370UpyRJyqE//vGP7L333pUOY1TY3L/1ihUrmDNnDsD0lNLTA3muSs7BqQfuBy7vp++dwHzgKGA28CRwb0QUTzv/MvC3wAnAu8mSofJfgEOSJFW9ig1RpZRugZ5qTd++y4rvR8QZZMnMXwM/iYjtgDOBz6WU7i8c80myJOgzKaUXyxy+JEmqYiNlFVU9MA7oTlxeD+xEVgHq9gBQA7x9eEOTJEnVZqQkOBcBfwZ+U7g/ufB1VfcBKaWNQGtRnyRJGqWqfhVVRJwKnAocnFLaUGiu2cJDJEkqqa6urp6N8lQe3RsQlkpVV3Ai4kPAVcAHUkqLirqaC18nFx27PdBIUVVHkqRtVVdXR0tLy7DuwjuadHV1sX79elauXElDQ0PJnrdqKzgR8X7gX4ATUkoP9Ol+CngBOBS4sdB2ENAFPIwkSSWy++67s2LFClavXl3pUHJrzJgxTJw4kZ122ql0z1myZxqkiGgE9gDeULi/b6HrCeBA4N+BzwEPR8Quhb61KaW1KaVXI+I64JKIWAa0Af8E3JxSKu3VwiRJo1ptbS3Tp1fXTsTaukpWcI4hq9B0e6TwdTpwMrAD8K3CrdtXySYcA3wN2JFsg7/ujf4+Wb5wJUnSSFHJfXBu5LXhpb5OKdy29PgNwLmFmyRJUo+qnmQsSZI0FCY4kiQpd0xwJElS7pjgSJKk3DHBkSRJuWOCI0mScscER5Ik5Y4JjiRJyh0THEmSlDsmOJIkKXdMcCRJUu6Y4EiSpNwxwZEkSbljgiNJknLHBEeSJOWOCY4kScodExxJkpQ7JjiSJCl3THAkSVLumOBIkqTcMcGRJEm5Y4IjSZJyxwRHkiTljgmOJEnKnTGVeuGIOA44C5gFTEgp1fTpPwC4Fngz8BRwXkrpnqL+8cA1wHFAJ3Aj8LmU0sZhOQFJklS1KlnBqQfuBy7v2xERTcA9wIPAfsDNwF0RMaPosGuB2cC7gROAE4EvlTlmSZI0AlQswUkp3ZJSugT4bT/dc4E1wKdTSk+klC4DHgJOB4iI1xWOOTul9FBK6X7gQuCsiHDYTZKkUa5ak4H9gV+klLqK2uYBBxS+fzvQBfyqT/9kYPqwRChJkqpWtSY4k4FVfdpWF9q7+1v7zLdZXdQnSZJGsWpNcGqG0N/VT5skSRqFqjXBaWbTSszOvFbVaQYaI2L7ov7u4/tWfiRJ0ihTrQnOQ8AhfdoOAxYUvn+YrIpzYJ/+VcDScgcnSZKqWyX3wWkE9gDeULi/b6HrCeBW4KKIuAq4HjiGbILxxwFSSq0RcRtwdUScCjQAFwPXppReHdYTkSRJVaeSFZxjgEeAfy7cf6Rw+18ppRbgSLIKzaPAKcBxKaUlRY8/k6ySMw+4A/gBcMmwRC5JkqpaxSo4KaUbyXYf3lz/fLLl4JvrXwt8tHCTJEnqUa1zcCRJkobMBEeSJOWOCY4kScodExxJkpQ7JjiSJCl3THAkSVLumOBIkqTcMcGRJEm5Y4IjSZJyxwRHkiTljgmOJEnKHRMcSZKUOyY4kiQpd0xwJElS7pjgSJKk3BlT6QCUWdKylAeWzmdVWwuTG5o4ePo7mNE0vdJhSZI0IlnBqQJLWpZy++/v5uVX1tI0bhIvv7KW239/N0tallY6NEmSRiQTnCrwwNL51NfW0TC2npqaGhrG1lNfW8cDS+dXOjRJkkYkE5wqsKqthfracb3a6mvHsaqtpUIRSZI0spngVIHJDU20d67r1dbeuY7JDU0VikiSpJHNBKcKHDz9HbR3dtC2vp2uri7a1rfT3tnBwdPfUenQJEkakUxwqsCMpul86C1Hs+MO42lZ9yI77jCeD73laFdRSZI0RC4TrxIzmqab0EiSVCJVneBExCTgH4GjgPHAY8AXUkq/KvQfAFwLvBl4CjgvpXRPhcKVJElVotqHqK4E3g58ANgHeAi4OyImRkQTcA/wILAfcDNwV0TMqFSwkiSpOlR7gnMA8J2U0oKU0pPAl4EdgRnAXGAN8OmU0hMppcvIEqDTKxatJEmqCtuc4ETmiVIE04/fAsdGRFNEbA98DFgBPAHsD/wipdRVdPw8sqRIkiSNYqWo4NQBUYLn6c8ngZeAF4BXgC8AR6WU2oHJwKo+x68utEuSpFGs2oeoPg1MA94NzAa+D/wkIl4H1FQwLkmSVMWqdhVVRIwDvgocklL6TaH5kYg4CvhboJlNqzU7s2lVR5IkjTLVXMGpLdw29ml/lSzuh4BD+vQdBiwoe2SSJKmqbbWCExE/28oh40sUSy8ppTUR8SBwVUR8Cmglm2Q8Dfg50AJcFBFXAdcDx5BNMP54OeKRJEkjx0CGqFYO4Ji0rYFsxoeBK4D/ABrIVk99MKW0GCAijiTb6O8Mso3+jkspLSlTLJIkaYTYaoKTUvo/wxHIZl57JXDiFvrnk20EKEmS1KOa5+BIkiQNyaBWUUXEEcAcstVLvZKjlNJHShiXJEnSkA04wYmIi4Evkl3w8nmga8uPkCRJqozBVHBOA05JKd1UrmAkSZJKYTBzcF4FfrPVoyRJkipsMAnO/8M9ZiRJ0ggwmCGqrwN3R8Qisnk4ncWdKaWPlTIwSZKkoRpMgvM14H1km+3tipOMJUlSlRpMgnM28LGU0o1likWSJKkkBjMHZz3w3+UKRJIkqVQGk+DcAJxarkAkSZJKZTBDVLsCxxd2M17EppOMTytlYJIkSUM1mARnL+DRwvfT+vQ54ViSJFWNASc4KaVDyxmIJElSqWw1wYmIHwJ3AnenlNaUPyRJkqRtM5BJxo8DnwdWR8S9EfGJiNi1zHFJkiQN2VYTnJTSV1JK+wBvAn4OnAwsi4jfRMT5EfGGcgcpSZI0GIOZg/MkcAVwRaGC8wHgOOCSiEjAXcCNKaWnyhKpJEnSAA1mH5weKaXnUkrfTikdDkwBvgG8mSzhkSRJqqiBTDI+aDNdHcCSlNJfgJsLN0mSpIobyBDVL8n2uanpp29jRPwr8ImU0oZSBiZJkjRUA0lwpm6mfRJwAHAJ8CngH0sVlCRJ0rbYaoKTUlq5ma6VwOMR0Q5cgAmOJEmqEkOaZNzHQ8DrS/A8kiRJJTGYa1FtziTgpRI8T78iYj/gH4B3Aq8AP08pfajQdwBwLdkKrqeA81JK95QrFkmSNDJsUwUnImqAc4CFpQlnk+ffG7gf+BUwG3gX8P1CXxNwD/AgsB/ZKq67ImJGOWKRJEkjx0CWid+wma6JwNuBXYB3lDKoIhcDP0wpfbWo7Y+Fr3OBNcCnU0pdwBMR8T7gdOCzZYpHkiSNAAMZotpcReQV4G7g2ymlP5UupExEbA+8F7g8In4JvBF4DPhMSukPwP7ALwrJTbd5wLtLHYskSRpZBrKK6tDhCKQfOwP1wOfIKjK/A84G7isMQ00GHunzmNWFdkmSNIoNeJJxRHwCeBj4fUppXVH7OIDithLpnh/0w5TS9YXXOp1sefrR9L/xoCRJ0qBWUV1AtunfxsLFNR8hS3g2kE00LvXk3heAjUDqbkgpdUbEU4U4mtm0WrMzsKrEcUiSpBFmwKuoUkp7AjsBR5KtWBoHXARcNZjnGcTrrSdLot7Q3RYRY4BpwDNk++8c0udhhwELSh2LJEkaWQa1D05KqRX4eeFGREwC7gSuK31oAHwT+G5E/IJsDs45ZFWdu4EdgIsi4irgeuAYsktHfLxMsUiSpBFimyovKaUXgS8Dl5YmnE2e/zayobHLgf8B9gbek1Jam1JqIasmHQg8CpwCHJdSWlKOWCRJ0sgxmEnGu6eUVvTTtRrYvXQh9ZZSuopsGKy/vvlke/FIkiT1GMwQ1TMR8QJZJeXhwm0lcBbwwzLEJkmSNCSDSXDeCrytcHsXcCbZbsYAD0bEP5ENFT2aUnq4pFFKkiQNwoATnMLuwX8gW0EFQERM57Wk523A8WSXbti+tGFKkiQN3DZdTTyltBRYSraSCoCIcCdhSZJUUeXYv8aN9iRJUkVtUwVH5bWkZSkPLJ3PqrYWJjc0cfD0dzCjaXqlw5IkqeqVvIKj0ljSspTbf383L7+ylqZxk3j5lbXc/vu7WdKytNKhSZJU9UxwqtQDS+dTX1tHw9h6ampqaBhbT31tHQ8snV/p0CRJqnoOUVWpVW0tNI2b1KutvnYcS/+ynO8s/DeHrSRJ2gIrOFVqckMT7Z3rerU1r11N67oXHbaSJGkrTHCq1MHT30F7Zwdt69vp6uqibX07z61dza47TnbYSpKkrTDBqVIzmqbzobcczY47jKdl3YvsuMN4muonMaVhp17H1deOY1VbS4WilCSpOjkHp4rNaJrea37Ndxb+Gy+/spaGsfU9be2d65jc0FSJ8CRJqlpWcEaQ/oat2js7OHj6OyodmiRJVcUEZwTpb9jqQ2852lVUkiT14RDVCNN32Arc8ViSpL6s4Ixw7ngsSdKmTHBGOHc8liRpUyY4I9yqthbqa8f1anPpuCRptDPBGeH62/HYpeOSpNHOBGeEc+m4JEmbMsEZ4Vw6LknSplwmngMuHZckqbcRUcGJiLsioisiDilqOyAiFkZER0Q8ERHvq2CIVcWl45Kk0a7qE5yIOBlo6NPWBNwDPAjsB9wM3BURM4Y/wurj0nFJ0mhX1QlOROwGXAx8vE/XXGAN8OmU0hMppcuAh4DThznEquTScUnSaFfVCQ7wHeDSlNIzfdr3B36RUuoqapsHHDBskVUxl45Lkka7qk1wIuJ0oDaldH0/3ZOBVX3aVhfaRz2XjkuSRruqTHAiYg/gK2w6NNWtZhjDGXFcOi5JGu2qdZn4fsAuwJ8jorh9XkTcCDSzabVmZzat6oxa/S0dlyRptKjWBGce8NY+bb8nq+j8DDgeOLdP/2HAgvKHJkmSql1VJjgppZeBPxS3FSo5S1NKKyPiVuCiiLgKuB44hmyC8eaGtCRJ0ihSlQnO1qSUWiLiSOBa4AzgKeC4lNKSykZW3dzdWJI0WoyYBCelVNPn/nzg7RUKZ8Tp3t24vrau1+7GTj6WJOVRVa6iUum5u7EkaTQxwRkl3N1YkjSamOCMEu5uLEkaTUxwRgl3N5YkjSYmOKOEuxtLkkaTEbOKStvO3Y0lSaOFFRxJkpQ7VnAEbHkTwOHYINBNCCVJ/3979x4tVXnecfx7gkCAGC9EDKup4aTRx1ra2FzQ2lgUDWsB1kQTYxQvTbUYpRo1xixBV0yWiTG1KlVroGqtaaK1NaYuIq0N8bLaqgRB8LJ8qly0NXKRqPFwBI54+se757DPcGbOcNh7z553fp+1WMPsvefM+579zpxnP/u9ZEkZHOmbBPCtrV39JgF8YdOauvuKeH8REZGhUAZH+k0CCPQ9ViYBrLUvqwxLvfdXFkdERIZCGRypOwlgERMEahJCERHJmgIcqTsJYBETBGoSQhERyZoCHKk7CeBQJwh8YdMabl16F9995CZuXXpX3f40moRQRESypgBH6k4COJQJAne107AmIRQRkaypk3HOWmX4c71JAHd1gsChdBrWJIQiIpIlZXBy1K7Dn9VpWEREmk0BTo7SmYyOjg7GjBjN6OHv7Rt+HSt1GhYRkWZTgJOjds1kqNOwiIg0mwKcHLVrJkOdhkVEpNnUyThHkzsP556nFwIhc9Pd8zbdPVs47uBjm1yy/KnTsIiINJMyODlSJkNERKQ5lMHJmTIZ5dEqQ/ZFRGT3KYMjbaFdh+yLiLQrBTjSFtp1yL6ISLsq7S0qM5sLnAQcCLwO/ASY4+5dqWMOAhYAhwHrgG+5+x3Fl1bKbsPmTYwdtXe/be0wZF9EpF2VOYNzBPB94OPAqcBU4MbKTjMbDvwMWA98CrgKWGBmk4svqpRduw7ZFxFpV6XN4Lj7jPRTM7sCmJ/aNg34LeBQd98MPJMEN+cDjxRXUmkF7TxkX0SkHZU5g1PtA8AbqeeTgCeS4KZiMeF2lUg/GrIvItJeSpvBSTOzvYBLgNtTm8cBG6oO3ZhsF9mJhuyLiLSP0mdwzGwkcC+wGvhealdHc0okIiIiZVfqAMfM9gDuBvYETnD3d1K717NztmY/ds7qiIiISJspbYBjZu8B7gQ+CkxLDw9PLAEOM7PRqW1TgCcKKqKIiIiUVJn74CwAjgKmAyPM7IPJ9o3uvh34N+BXwG1mdhWhc/EpwGeaUFYREREpkTIHOGclj8urtncCa919m5nNIAwdf5Iw0d8sd9cQcWmKIta60npaIiKNKW2A4+6DdiJ2dydkeUSaqrLW1ejh7+231lWWQ9GLeA8RkViUtg+OSCspYq0rraclItI4BTgiGdiweROjh4/qty3rta6KeA8RkVgowBHJQBFrXWk9LRGRxinAEcnA5M7D6e7ZwuZt3fT29rJ5WzfdPVuY3Hl4S72HiEgsFOCIZKCIta60npaISONKO4qqFWkIb3srYq2rIt5D7Xhw+h2JlJ8yOBmpDOF9a2tXvyG8L2xa0+yiiTRM7Xhw+h2JtAZlcDKSHsIL9D0+suZxXdlJyyiiHbd69kOfdZHWoAxORjSEV2KQdzuOIfuhz7pIa1CAkxEN4ZUY5N2OY5NLTqYAAAwuSURBVJisUJ91kdagW1QZmdx5OPc8vRAIV3PdPW/T3bOF4w4+tsklE2lc3u14w+ZNjB21d79teWQ/8rwNps96ubT6LU+Iow5lpAxORjSEV2KQdzsuIvuR920wfdbLI4ZbnjHUoayUwclQEUN4RfKWZzsuIvtRRCfgWD7rrZ45iKHDdwx1gHK2JWVwRKQwRWQ/1Am4MTFkDmI41zHUoaxtSRkcESlU3tmPcWPG8tbWrr4rYVAn4IHEkDmI4VzHUIeytiVlcEQkKlqzqzExZA5iONcx1KGsbUkBjohERZ2AGxPDcPcYznUMdShrW9ItKhGJTiydgPMUy3D3GM51q9ehrG1JGRwRkTYUQ+ZAyqGsbUkZHBGRNtXqmQMpjzK2JWVwREREJDoKcERERCQ6CnBEREQkOgpwREREJDrt3Ml4GMC6deuaXQ4RERGpI/W3elijr2nnAGc8wMyZM5tdDhEREWnMeGBVIwe2c4DzS+BI4FVge5PLIiIiIrUNIwQ3v2z0BR29vb35FUdERESkCdTJWERERKKjAEdERESiowBHREREoqMAR0RERKKjAEdERESiowBHREREoqMAR0RERKKjAEdERESiowBHREREotPOSzXkwswuA84H9gYeBGa5+4bmliobZjYXOAk4EHgd+Akwx927kv0TgDVVL3vT3fcuspxZM7M7gDOrNl/k7jekjpkBXAt0As8A57p7w1OKl5GZrQU+PMCuw4DRwENV21e4+6E5FytTZnYiMBv4JPB+d++o2n8YcDMwEVgNfM3dF6X2vw+4CTgR6AHuAC5191Iv/1Kv3mZ2FPB1YBIwAlgKfN3dl6WOeRiYXPVjT3D3n+Zb8qFroM5127OZfRD4ATAVeAOY5+7X5F3u3TVIva8EvjnAy25x9/OSY9ay8/fAH7r7U3mUN0vK4GTIzL4MzCE0piMIQc5dTS1Uto4Avg98HDiV8EG/cYDjJhHWDBkPHFRY6fJ1DzvqNB5YUNlhZgcTgr07Cb+b/wYWmdk+TShnlj5F/zpfBbxC+INXkd5/TNEFzMBo4BfA96p3mNlYYBHwX4Tz+kPgPjM7MHXYzYTf07GE4P8UYG7OZc5CzXoDfwQ8Dswg1G0V8O/J7yPtr+l//hdRbvXqXFGvPf8TsBfhe3A2cLmZnZFDObNWr97X0r/OHwPeBaoD1Qurjnsmr8JmSRmcbJ0PXOfu9wGY2Z8Dq8xsoru3RIOox91npJ+a2RXA/AEO3eju6wbY3srerlOnWcDj7n41gJl9FfgsMJNwdd+S3H1j+rmZ/SnwI3d/18wqx7T0eXb3f4S+K/hqM4HfABe6ey/wnJlNA84BLkkC2JnAZ9x9SfJzLgeuNrOr3P3dIuowFPXqXWnHFWZ2LvAl4I+B+1O7ulrp/A9yrivHDFgfM/sD4E+A33H31cBTZnY9cAHhwqa0BjnXXUBX5bmZfQlYDyyuOvTNVjrXFcrgZMTMRhKi319UtiUfhLWElH6MPkBI1VZ71Mx+ZWY/M7OJRRcqJ8eb2UYzW2lml5lZ+uJgEv3Pe2/yPJrznpzHj1H1ZW5ma83sZTO728wOaE7pcjMJeCg5nxWL2XFePwH0Ao9W7R9HuFUZi9HAKHb+rJ9nZq+Z2ZNm9hdNKFfm6rTnScDa5Du9YjFwqJmNKLaUuTqdcBFTfYv16uT77zEz+1wzCjYUCnCyM5bw+6zub7OR8IUXFTPbC7gEuD21uYtwRXMC8AVgMyHY2b/4EmbqAcItuSnAdcDFwLdT+8cR/3k/A1ju7s8mz18FziJkqs4k1PUhMxvVpPLlYbDzOg74ddUfg42pfbG4EniRcOu14ofAyYTbOP8AzDOzWcUXLTODtedabWEY4bu/5ZnZIey4FZt2PeH7fCrhNuS9Zja14OINiW5RZadj8EPikGSr7iV0uuy7r+vur5Hqk2NmTwDPEf44/lXBxcyMu9+Tevq0mW0HbjGzucnVfdTn3szeQwjwrq1sc3cHPHXMUuBl4Djgn4suY04GO68D7e8dYFvLMrOzCH/4J7v7O5Xt7n5b6rAVyQXPV0n1TWslDbTnqD/jiTMIHatXpje6+7zU0+Vm9mHCuX6wyMINhTI42XmN0Dmr+sptP3aO/FtWcmvmbmBPwqiJd2odm1zZrgQmFFO6wiwDxhBu0UG4Zx3zeT8G2J86Hebd/S3CVf6EgspUhMHO63pgXzMbltpfOb7lz72ZfRG4Afisu68Y5PBlRHTuB2jPtdrCdmBTcSXLR3IRM5OdszcDaZlzrQAnI+6+FVgBHF3ZZmadhIbwRJOKlankQ3An8FFgWmV4eJ3jO4BDgJcKKF6RJgLdhKAWYAmp8544mkjOO+HK7kF3X1/rgCSV/xHiOtdLgKOqtk1hx3ldRriyP7Jq/wZ2ni6hpSQdyv8eONndH2ngJROJ6NwP0J6XABOS7/SKKcBT7r6t6PLl4GjC6KgfNXBsy5xr3aLK1k3ADWa2nNC5+HpCJ8WWH0GVWED4wp8OjEjmhYAwamq7mZ1EaFNPAiMJacwPAT9uQlkzY2bXEYaIrifco74OmJ/qfLqAkKb/BmGUyTnA+2jsy6LUzGwMoU/V2VXbZwHrgGcJ0yF8k9AH64Giy7g7zGxf4ABC0I6ZVeY9eY5w/q40sxsIowWPJ3QwPhvA3X9tZj8Gbkxu5YwhDKW/ucwjqGDQeh9JuC1zKbAs9Tnvcveu5PlXCG39DcLoojnAFcXVYNcNUuc/o057dveVZvYocJuZXUS4cL0I+MviajA09eqdCs5OB35ePVIqGT12LPBzwkXdcYT2//kCir7bFOBkyN1vTzrU/oAwX8J/EIYQx+Ks5HF51fZOQkDXS/iSmwC8TQh0prj7/xVUvrwcAiwE3k+4Lz8f6Jvgy92fN7PPE/oZfZswR8R0d3+9CWXN2omENPy/Vm0fTgj0PgS8SZgr5pjBsnoldDwhU1FRadud7r7WzKYT5ro5l9Dn7ER3fyF1/HnJ/sXsmOjvO3kXOgM16034YzcSmJf8q/gWocNxD+FC5wLC6KrVhLl//jbPAmegXp0bac8nEz77jyXHfNfdSz1EPFGv3mvNbDThc/6VAV67DfgiIeDbA3geOM3d7x/g2NLp6O2Nqk+ciIiIiPrgiIiISHwU4IiIiEh0FOCIiIhIdBTgiIiISHQU4IiIiEh0FOCIiIhIdBTgiEhuzOxhM7u1yWW40cxuyuDn7HJdkpXn/2V331tEdp0m+hORXWZmg02g9ZK7TyBMIFZzvbK8mZkBXyaZxXU3DaUu84CXzOzT7v6fGZRBRBqkAEdEhmJ86v+TCDMdTwL+N9m2HcJyBgWXq9oFwAPVU9APxVDq4u7dZnYXcCGgAEekQApwRGSXpQMGM6v84d84wFo2DwMvuvvZqeergFcJy5iMICx1cAVwOTCbcOt8gbvPTf2cPZL9ZxKCq1XA37j7/FplTBaHPYWq9YJ2owwD1eVFwsKDs5Ofcz8w2903p97yPuBBM9szWaVaRAqgPjgiUrQvENb++TRwMWGhxoWEBUqPBC4B5pjZtNRrbiXcIjoH+F3Cml/XJItc1vL7wD6ElaCzKEOtuuxLWJvpVOBzhEUq05YAw5L3EpGCKIMjIkVb4+7fSP7/P2b2NeC33X16atvFwDHAIjPrBM4ADnH35ys/I+lfcz5wW4336UweX9ndMtSpy8vuflHy/+fN7G5gKmFxQgDcfbOZvQF8pM7PEZGMKcARkaKtqHq+LvlXvW1c8v9PAh3A0hDT9NmDpK9PDaOSx60ZlKGWp6qev0IIcKptSZVHRAqgAEdEitZT9by3xrbKLfTK4xFA9wDH1bIxedwH2LSbZahlW4Ov2TdVHhEpgAIcESm7J5PHA9x94S68bjkh4Pg94NHMS9UgMzsQGAksbVYZRNqRAhwRKTV3f9HMbgf+zswuBR4DxgCfAPZz92tqvG6TmS0BJtPEAIfQAfkld3+2iWUQaTsaRSUirWAWcD0wF3gOWEwYMr56kNfdApyeb9EGdRpQczi7iOSjo7d3sAlJRURak5kNB1YCl7n7T5vw/pMIc+Mc5O6/Kfr9RdqZMjgiEi137yFkesY0qQj7A6cpuBEpnjI4IiIiEh1lcERERCQ6CnBEREQkOgpwREREJDoKcERERCQ6CnBEREQkOgpwREREJDr/D0mZmqXc2jKdAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "subplot(2, 1, 1)\n", - "plot(data.glucose, 'bo', label='glucose')\n", - "decorate(ylabel='mg/dL')\n", - "\n", - "subplot(2, 1, 2)\n", - "plot(data.insulin, 'go', label='insulin')\n", - "decorate(xlabel='Time (min)',\n", - " ylabel='$\\mu$U/mL')\n", - "\n", - "savefig('figs/chap08-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Interpolation\n", - "\n", - "We have measurements of insulin concentration at discrete points in time, but we need to estimate it at intervening points. We'll use `interpolate`, which takes a `Series` and returns a function:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The return value from `interpolate` is a function." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "I = interpolate(data.insulin)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use the result, `I`, to estimate the insulin level at any point in time." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array(68.)" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "I(7)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`I` can also take an array of time and return an array of estimates:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "numpy.ndarray" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_0 = get_first_label(data)\n", - "t_end = get_last_label(data)\n", - "ts = linrange(t_0, t_end, endpoint=True)\n", - "I(ts)\n", - "type(ts)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the interpolated values look like." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap08-fig02.pdf\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzs3Xl81NW5+PHPZDJZSQIJCTshChwQUKggiAsK1WtxweJWiqAWS12rtRVxr/ciLteqLdpbta1eUVrBorZc/QmiUhcWUWRTDgESIBCyEsg+6++P78wwGSbJTJjJzJDn/Xrllcz3fGfmmSHMk3O+zznH5HK5EEIIIWJNQrQDEEIIIQKRBCWEECImSYISQggRkyRBCSGEiEmSoIQQQsQkSVBCCCFikiQoIYQQMUkSlBBCiJgkCUoIIURMkgQlhBAiJiVGO4BoUkolA+OAUsAR5XCEEOJkZgb6AF9prZuDuUOXTlAYyemzaAchhBBdyHnA58Gc2NUTVCnAm2++Se/evaMdixBCnLQOHTrEzJkzwf25G4yunqAcAL1796Z///7RjkUIIbqCoC+nSJGEEEKImCQJSgghREySBCWEECImSYISQggRk7p6kUREFFYVsaZoHeX1VeSl5zCpYAJDcgqiHZYQEed0OikpKaG+vj7aoYhOZrFYyMvLIzMzM2yPKQkqzAqrili6dQWWBDPZKVnUNtexdOsKrh11mSQpcdKrrKzEZDKhlCIhQQZougqXy0VjYyMHDhwACFuSkt+gMFtTtI5Ddfv5v93LWVX0PmmWVNIsKawpWhft0ISIuJqaGnr16iXJqYsxmUykpaXRr18/ysvLw/a48lsUZuX1VZTUFuNyOaloKONwUzVpllTK66uiHZoQEedwOLBYLNEOQ0RJamoqNpstbI8nCSrM8tJzaLQ3eW9bHc002BrJS8+JYlRCdB6TyRTtEESUhPvfXhJUmE0qmECz3YrNaQegtrmOBlsTkwomRDkyIYSIL5KgwmxITgEq9xSSzBaa7FaSEi1SICFEDJo1axaLFi3qlOcqKSlBKUVJSQkAy5cvZ/LkyRF9jpOBVPFFQFZyBsN6DgbgkiGTJDkJEYMWLVoUtetlU6dO5YILLojKc3usX7+e2bNno7WOahxtkQQVAQ7XsbUQmx1BbXsihOhk3bt3j9pzp6SkkJKSErXnjxcyxBcBDuexBNXkUzAhhGhbYVURf974NxaueYE/b/wbhVVFEXsu/yE+pRTvvvsus2fP5owzzuCqq65i586d3vbt27czY8YMRo8ezbhx47j++us5evRowMcCmDx5MsuXLw/43P5DfPPnz+e+++7j2WefZdy4cZx33nm8/vrrbcZfWlrKDTfcwKhRo5g2bRrbt29v0b57925uvvlmxo8fz9ixY/n5z3/O/v37AWM4cPbs2d7XrZTyxvr4448zZcoUzjjjDC699FLef//9NuOIJElQEdCiB2WXHpQQwfBMcq9triMntbt3knskk5S/F154gRtvvJF3332X7OxsHnzwQW/bvffey+jRo/nXv/7FkiVLuPzyy8P63KtWrQJg6dKl3HbbbSxcuJDCwsJWz583bx52u51ly5Zx77338rvf/a5Fe0NDA5dccglLlixhyZIlpKSkcM899wDQp08fb0L9/PPP+fzzz5k6dSpg9Cyfe+45VqxYwezZs5k3b17UhgFliC8CfHtQMsQnRHDWFK0jzZJCelIagPf7mqJ1nXYdd+bMmd6eza233sqMGTNoamoiJSWF0tJSpkyZwoABAwAYMmRIWJ+7X79+3gRSUFDAa6+9xsaNGwM+z+7du9mwYQMrV64kPz8fgDlz5vDII494zxk1ahSjRo3y3n7sscc4++yzOXjwIH379iUrKwuA3NzcFo99++23e3++7rrr+Pjjj1m1ahVKqfC92CBJggozl8slPSghOqC8voqc1JbXhTp7kvvQoUO9P3s+uKurq+nbty/XX389c+bM4ZxzzuGcc85h6tSp9OjRIyLP7Xn+qqrAr72oqIisrCxvcgI444wzWpxTV1fHs88+yxdffEFFRQUulwswhgb79u3bahzvvvsuixcvpqSkBKvVitVqPS6JdRYZ4gszFy7vLwJID0qIYOWl59Bga2xxrLMnuScmHvub3TPp1Ol0AvDrX/+aZcuWMXr0aN577z0uueQS7zUdk8nU4v89EPKKCv4VhYEe08PlcrU7Kfapp57iq6++4oEHHmDp0qUsW7YMALvd3up9vv76ax566CGuvPJKXn31Vd59913OPffcNu8TSZKgwsx3eA+kSEKIYE0qmECDrYl6awMul4t6a0PMTXIfOnQoc+fOZenSpfTs2ZOPPvoIgOzsbCorK73nVVdXt7gdbgUF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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(data.insulin, 'go', label='insulin data')\n", - "plot(ts, I(ts), color='green', label='interpolated')\n", - "\n", - "decorate(xlabel='Time (min)',\n", - " ylabel='Concentration ($\\mu$U/mL)')\n", - "\n", - "savefig('figs/chap08-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** [Read the documentation](https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html) of `scipy.interpolate.interp1d`. Pass a keyword argument to `interpolate` to specify one of the other kinds of interpolation, and run the code again to see what it looks like. " - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "I = interpolate(data.insulin, kind='cubic')\n", - "plot(data.insulin, 'go', label='insulin data')\n", - "plot(ts, I(ts), color='green', label='interpolated')\n", - "\n", - "decorate(xlabel='Time (min)',\n", - " ylabel='Concentration ($\\mu$U/mL)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The glucose minimal model\n", - "\n", - "I'll cheat by starting with parameters that fit the data roughly; then we'll see how to improve them." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
G0290.00000
k10.03000
k20.02000
k30.00001
\n", - "
" - ], - "text/plain": [ - "G0 290.00000\n", - "k1 0.03000\n", - "k2 0.02000\n", - "k3 0.00001\n", - "dtype: float64" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(G0 = 290,\n", - " k1 = 0.03,\n", - " k2 = 0.02,\n", - " k3 = 1e-05)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a version of `make_system` that takes the parameters and data:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params, data):\n", - " \"\"\"Makes a System object with the given parameters.\n", - " \n", - " params: sequence of G0, k1, k2, k3\n", - " data: DataFrame with `glucose` and `insulin`\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " G0, k1, k2, k3 = params\n", - " \n", - " Gb = data.glucose[0]\n", - " Ib = data.insulin[0]\n", - " \n", - " t_0 = get_first_label(data)\n", - " t_end = get_last_label(data)\n", - "\n", - " init = State(G=G0, X=0)\n", - " \n", - " return System(G0=G0, k1=k1, k2=k2, k3=k3,\n", - " init=init, Gb=Gb, Ib=Ib,\n", - " t_0=t_0, t_end=t_end, dt=2)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
G0290
k10.03
k20.02
k31e-05
initG 290.0\n", - "X 0.0\n", - "dtype: float64
Gb92
Ib11
t_00
t_end182
dt2
\n", - "
" - ], - "text/plain": [ - "G0 290\n", - "k1 0.03\n", - "k2 0.02\n", - "k3 1e-05\n", - "init G 290.0\n", - "X 0.0\n", - "dtype: float64\n", - "Gb 92\n", - "Ib 11\n", - "t_0 0\n", - "t_end 182\n", - "dt 2\n", - "dtype: object" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(params, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the update function. It uses `unpack` to make the system variables accessible without using dot notation, which makes the translation of the differential equations more readable and less error prone." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def update_func(state, t, system):\n", - " \"\"\"Updates the glucose minimal model.\n", - " \n", - " state: State object\n", - " t: time in min\n", - " system: System object\n", - " \n", - " returns: State object\n", - " \"\"\"\n", - " G, X = state\n", - " unpack(system)\n", - " \n", - " dGdt = -k1 * (G - Gb) - X*G\n", - " dXdt = k3 * (I(t) - Ib) - k2 * X\n", - " \n", - " G += dGdt * dt\n", - " X += dXdt * dt\n", - "\n", - " return State(G=G, X=X)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Before running the simulation, it is always a good idea to test the update function using the initial conditions. In this case we can veryify that the results are at least qualitatively correct." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
G278.12
X0.00
\n", - "
" - ], - "text/plain": [ - "G 278.12\n", - "X 0.00\n", - "dtype: float64" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "update_func(system.init, system.t_0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now `run_simulation` is pretty much the same as it always is." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a simulation of the system.\n", - " \n", - " system: System object\n", - " update_func: function that updates state\n", - " \n", - " returns: TimeFrame\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " frame = TimeFrame(columns=init.index)\n", - " frame.row[t_0] = init\n", - " ts = linrange(t_0, t_end, dt)\n", - " \n", - " for t in ts:\n", - " frame.row[t+dt] = update_func(frame.row[t], t, system)\n", - " \n", - " return frame" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's how we run it. `%time` is a Jupyter magic command that runs the function and reports its run time." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 212 ms, sys: 0 ns, total: 212 ms\n", - "Wall time: 211 ms\n" - ] - } - ], - "source": [ - "%time results = run_simulation(system, update_func);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The results are in a `TimeFrame object` with one column per state variable." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
GX
0290.0000000.000000
2278.1200000.000000
4266.9528000.000300
6256.2954600.002668
8245.0701400.004041
10233.9051380.004680
12223.2016510.005252
14212.9848440.005722
16203.2882070.006093
18194.1334610.006330
20185.5478350.006474
22177.5324610.006592
24170.0600940.006708
26163.0950090.006797
28156.6123480.006851
30150.5896600.006901
32144.9957780.006977
34139.7926310.007078
36134.9460720.007177
38130.4323350.007248
40126.2355960.007277
42122.3443050.007255
44118.7483720.007185
46115.4370480.007074
48112.3976350.006931
50109.6156880.006766
52107.0754840.006587
54104.7603870.006403
56102.6531340.006224
58100.7361900.006053
.........
12486.3216240.001182
12686.4582980.001049
12886.6094870.000918
13086.7739610.000792
13286.9501510.000672
13487.1362630.000561
13687.3303780.000458
13887.5305440.000365
14087.7348610.000280
14287.9415550.000205
14488.1490520.000137
14688.3560350.000075
14888.5614940.000018
15088.764694-0.000036
15288.965138-0.000086
15489.162529-0.000134
15689.356738-0.000181
15889.547769-0.000228
16089.735737-0.000274
16289.920834-0.000321
16490.103312-0.000368
16690.283457-0.000416
16890.461571-0.000465
17090.637951-0.000514
17290.812877-0.000564
17490.986591-0.000615
17691.159288-0.000666
17891.331102-0.000716
18091.502094-0.000767
18291.672241-0.000816
\n", - "

92 rows × 2 columns

\n", - "
" - ], - "text/plain": [ - " G X\n", - "0 290.000000 0.000000\n", - "2 278.120000 0.000000\n", - "4 266.952800 0.000300\n", - "6 256.295460 0.002668\n", - "8 245.070140 0.004041\n", - "10 233.905138 0.004680\n", - "12 223.201651 0.005252\n", - "14 212.984844 0.005722\n", - "16 203.288207 0.006093\n", - "18 194.133461 0.006330\n", - "20 185.547835 0.006474\n", - "22 177.532461 0.006592\n", - "24 170.060094 0.006708\n", - "26 163.095009 0.006797\n", - "28 156.612348 0.006851\n", - "30 150.589660 0.006901\n", - "32 144.995778 0.006977\n", - "34 139.792631 0.007078\n", - "36 134.946072 0.007177\n", - "38 130.432335 0.007248\n", - "40 126.235596 0.007277\n", - "42 122.344305 0.007255\n", - "44 118.748372 0.007185\n", - "46 115.437048 0.007074\n", - "48 112.397635 0.006931\n", - "50 109.615688 0.006766\n", - "52 107.075484 0.006587\n", - "54 104.760387 0.006403\n", - "56 102.653134 0.006224\n", - "58 100.736190 0.006053\n", - ".. ... ...\n", - "124 86.321624 0.001182\n", - "126 86.458298 0.001049\n", - "128 86.609487 0.000918\n", - "130 86.773961 0.000792\n", - "132 86.950151 0.000672\n", - "134 87.136263 0.000561\n", - "136 87.330378 0.000458\n", - "138 87.530544 0.000365\n", - "140 87.734861 0.000280\n", - "142 87.941555 0.000205\n", - "144 88.149052 0.000137\n", - "146 88.356035 0.000075\n", - "148 88.561494 0.000018\n", - "150 88.764694 -0.000036\n", - "152 88.965138 -0.000086\n", - "154 89.162529 -0.000134\n", - "156 89.356738 -0.000181\n", - "158 89.547769 -0.000228\n", - "160 89.735737 -0.000274\n", - "162 89.920834 -0.000321\n", - "164 90.103312 -0.000368\n", - "166 90.283457 -0.000416\n", - "168 90.461571 -0.000465\n", - "170 90.637951 -0.000514\n", - "172 90.812877 -0.000564\n", - "174 90.986591 -0.000615\n", - "176 91.159288 -0.000666\n", - "178 91.331102 -0.000716\n", - "180 91.502094 -0.000767\n", - "182 91.672241 -0.000816\n", - "\n", - "[92 rows x 2 columns]" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following plot shows the results of the simulation along with the actual glucose data." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap08-fig03.pdf\n" - ] - }, - { - "data": { - "image/png": "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detWVq1axbPPPsv06dMBWLZsGTfffDNf/vKXycnJAWD+/Plcd911ANx666385je/4d1332XRokVHFfdIUtIiIiIJs6amecQSFgB/IMSamuYjSlpmzZqFMYZzzz2XRYsWsXjxYs4999yDtmIcf/zxscdFRUVUVFTE9nU6nRQUFNDW1nbUsT/11FM8+uij7Ny5k76+Pvr6+g6aoAxl69at5OfnxxIWgFNOOQW/309dXR2zZs0CwBgTq3e73RQWFtLa2nrUcY+kVB/TIiIiaWxeZTEe98h91XjcTuZVHtkXvdvt5tFHH+X++++nqKiIH/3oR3zyk5886Aqubvf+v/MdDscByY3D4SAUiiRoTqeTcDg8oD4QCBw0ljfffJNly5Zx2WWX8Zvf/IannnqKM88885DHHImhFneLfx2D4051amkREZGEOcWUHFGrR7K4XC4++MEP8sEPfpDrr7+ehQsXUlNTc8znHT9+PG1tbQSDQVwuFwCRIZZDW7NmDZWVlbEBtuFwmB07djB+/PjYPm63m2DwgNU+YqZPn057ezvbtm2Ltba89dZbeDweystHx9pfamkREZExae3atTz88MOsX7+e+vp6nnrqKTIzMykrKzvmc8+ePZtwOMwDDzxAbW0tv/3tb6murj7o/lOnTmXz5s28+OKLbN26lXvuuYfGxsYB+0yaNIm33nqL5uZm9u3bd8A5ZsyYwZlnnsmdd97J+vXrqa6u5t577+VjH/tYbDxLulPSIiIiY1Jubi6vvfZabEbO3/72N5YvXz4ss2gKCwu57777eOqpp7jsssvYuHEjV1999UH3P/fcc/nEJz7BV7/6Va6++mq8Xi/nnTdwSbFbb72VNWvW8OEPf5ibb755yPP84Ac/oLi4mKVLl3LjjTcyf/58vva1rx3z60kVjsF9brKfMWYasK2qqoopU6YkOxwRkZT33nvvccIJJyQ7DEkhB3tP7Ny5kyVLlgBMt9ZuP5JzqaVFRERE0oKSFhEREUkLSlpEREQkLShpERERkbSgpEVERETSgpIWERERSQtKWkRERCQtKGkRERGRtKCkRURERNKCkhYREZEhrF69GmNMssMYNsuXL49dkDFdKWkRERGRA9xxxx3ccccdyQ5jAHeyAxAREYlXU9tKVXUdja3dlBZms2R+OZUVx34RQ0l/amkREZGUUVPbyoqVlo6uPory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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "subplot(2, 1, 1)\n", - "\n", - "plot(results.G, 'b-', label='simulation')\n", - "plot(data.glucose, 'bo', label='glucose data')\n", - "decorate(ylabel='mg/dL')\n", - "\n", - "subplot(2, 1, 2)\n", - "\n", - "plot(results.X, 'g-', label='remote insulin')\n", - "\n", - "decorate(xlabel='Time (min)', \n", - " ylabel='Arbitrary units')\n", - "\n", - "savefig('figs/chap08-fig03.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Under the hood" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[0;32mdef\u001b[0m \u001b[0minterpolate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mseries\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Creates an interpolation function.\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m series: Series object\u001b[0m\n", - "\u001b[0;34m options: any legal options to scipy.interpolate.interp1d\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m returns: function that maps from the index of the series to values\u001b[0m\n", - "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# TODO: add error checking for nonmonotonicity\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0msum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mseries\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0misnull\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mmsg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"\"\"The Series you passed to interpolate contains\u001b[0m\n", - "\u001b[0;34m NaN values in the index, which would result in\u001b[0m\n", - "\u001b[0;34m undefined behavior. So I'm putting a stop to that.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# make the interpolate function extrapolate past the ends of\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# the range, unless `options` already specifies a value for `fill_value`\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0munderride\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfill_value\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'extrapolate'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# call interp1d, which returns a new function object\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0minterp_func\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0minterp1d\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mseries\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mseries\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalues\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0munits\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mseries\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'units'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0munits\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mQuantity\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minterp_func\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0munits\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0minterp_func\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "%psource interpolate" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises\n", - "\n", - "**Exercise:** Our solution to the differential equations is only approximate because we used a finite step size, `dt=2` minutes.\n", - "\n", - "If we make the step size smaller, we expect the solution to be more accurate. Run the simulation with `dt=1` and compare the results. What is the largest relative error between the two solutions?" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "system2 = System(system, dt=1)\n", - "results2 = run_simulation(system2, update_func);" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot(results.G, label='dt=2')\n", - "plot(results2.G, label='dt=1')\n", - "decorate(xlabel='Time (m)', ylabel='mg/dL')" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0 0.000000\n", - "2 -0.064073\n", - "4 -0.161975\n", - "6 -0.024191\n", - "8 0.019012\n", - "10 -0.035063\n", - "12 -0.098785\n", - "14 -0.171325\n", - "16 -0.251903\n", - "18 -0.342755\n", - "20 -0.438831\n", - "22 -0.532172\n", - "24 -0.618874\n", - "26 -0.701364\n", - "28 -0.780436\n", - "30 -0.853092\n", - "32 -0.915848\n", - "34 -0.968854\n", - "36 -1.015152\n", - "38 -1.057509\n", - "40 -1.097505\n", - "42 -1.135687\n", - "44 -1.171725\n", - "46 -1.204635\n", - "48 -1.233412\n", - "50 -1.257222\n", - "52 -1.275396\n", - "54 -1.287408\n", - "56 -1.292905\n", - "58 -1.291936\n", - " ... \n", - "124 -0.426672\n", - "126 -0.409604\n", - "128 -0.392985\n", - "130 -0.376577\n", - "132 -0.360224\n", - "134 -0.343834\n", - "136 -0.327376\n", - "138 -0.310868\n", - "140 -0.294371\n", - "142 -0.277983\n", - "144 -0.261833\n", - "146 -0.246067\n", - "148 -0.230816\n", - "150 -0.216177\n", - "152 -0.202224\n", - "154 -0.189007\n", - "156 -0.176555\n", - "158 -0.164883\n", - "160 -0.153988\n", - "162 -0.143857\n", - "164 -0.134463\n", - "166 -0.125773\n", - "168 -0.117743\n", - "170 -0.110325\n", - "172 -0.103466\n", - "174 -0.097105\n", - "176 -0.091183\n", - "178 -0.085635\n", - "180 -0.080395\n", - "182 -0.075395\n", - "Name: G, Length: 92, dtype: float64" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "diff = (results.G - results2.G).dropna()\n", - "rel_diff = diff / results.G * 100" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1.2929052345260739" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "max(abs(rel_diff))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap18soln.ipynb b/code/soln/chap18soln.ipynb deleted file mode 100644 index 7cb68f300..000000000 --- a/code/soln/chap18soln.ipynb +++ /dev/null @@ -1,1962 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 18\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Code from the previous chapter\n", - "\n", - "Read the data." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "data = pd.read_csv('data/glucose_insulin.csv', index_col='time');" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Interpolate the insulin data." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "I = interpolate(data.insulin)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Initialize the parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "1e-05" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "G0 = 290\n", - "k1 = 0.03\n", - "k2 = 0.02\n", - "k3 = 1e-05" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To estimate basal levels, we'll use the concentrations at `t=0`." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "11" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "Gb = data.glucose[0]\n", - "Ib = data.insulin[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create the initial condtions." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
G290
X0
\n", - "
" - ], - "text/plain": [ - "G 290\n", - "X 0\n", - "dtype: int64" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "init = State(G=G0, X=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make the `System` object." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "182" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_0 = get_first_label(data)\n", - "t_end = get_last_label(data)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
initG 290\n", - "X 0\n", - "dtype: int64
k10.03
k20.02
k31e-05
I<scipy.interpolate.interpolate.interp1d object...
Gb92
Ib11
t_00
t_end182
dt2
\n", - "
" - ], - "text/plain": [ - "init G 290\n", - "X 0\n", - "dtype: int64\n", - "k1 0.03\n", - "k2 0.02\n", - "k3 1e-05\n", - "I \n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[]
nfev80
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events []\n", - "nfev 80\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`results` is a `TimeFrame` with one row for each time step and one column for each state variable:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
GX
0290.0000000.000000
2278.4419310.000148
4267.1622530.001767
6255.5521730.003811
8243.7788690.004825
10232.5565620.005397
12221.8794050.005891
14211.7554650.006272
16202.2053890.006538
19188.9789710.006786
22177.0257560.006967
27159.6719540.007152
32145.1405780.007301
42122.8999980.007327
52107.9281120.006677
6298.2308460.005888
7292.1053440.005019
8288.4677630.004050
9286.7177770.003143
10286.0663380.002382
12286.3913870.001222
14288.0781200.000246
16289.951526-0.000323
18291.674908-0.000798
\n", - "
" - ], - "text/plain": [ - " G X\n", - "0 290.000000 0.000000\n", - "2 278.441931 0.000148\n", - "4 267.162253 0.001767\n", - "6 255.552173 0.003811\n", - "8 243.778869 0.004825\n", - "10 232.556562 0.005397\n", - "12 221.879405 0.005891\n", - "14 211.755465 0.006272\n", - "16 202.205389 0.006538\n", - "19 188.978971 0.006786\n", - "22 177.025756 0.006967\n", - "27 159.671954 0.007152\n", - "32 145.140578 0.007301\n", - "42 122.899998 0.007327\n", - "52 107.928112 0.006677\n", - "62 98.230846 0.005888\n", - "72 92.105344 0.005019\n", - "82 88.467763 0.004050\n", - "92 86.717777 0.003143\n", - "102 86.066338 0.002382\n", - "122 86.391387 0.001222\n", - "142 88.078120 0.000246\n", - "162 89.951526 -0.000323\n", - "182 91.674908 -0.000798" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting the results from `run_simulation` and `run_ode_solver`, we can see that they are not very different." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYUAAAEFCAYAAAAMk/uQAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3Xl8XHd97//XmU2jGa0jWbJjy/L+zR6TGCdAdgNpCA2XtGkLSYFbfg8oBdqm7a+/R1vaC21aKL97afujXEq4tLnglKWlLBdK0qKEJGSzHSeO49hfJ15kO7ZGq7XMaDTb+f1xRuOxYltjW9aMpPfz8ZiH5pzvmZnPHEnn893OOY7ruoiIiAD4Kh2AiIhUDyUFEREpUlIQEZEiJQURESlSUhARkSIlBRERKVJSEBGRIiUFEREpUlIQEZEiJQURESlSUhARkaJApQMohzGmBngzcAzIVTgcEZG5wg8sAbZaayfKecGcSAp4CeHJSgchIjJH3QD8vJwN50pSOAbw0EMPsXjx4krHIiIyJ/T09HDPPfdA4RhajrmSFHIAixcvZtmyZZWORURkrim7210DzSIiUqSkICIiRUoKIiJSVPaYgjHmM8D7gA5gBPgp8PvW2h5jzIeAf5rykh9Ya/9LyevXAQ8A1wI9wGestQ+eV/QiIjKjzmageQ/wUeAA0Ab8D+B/A7cVyo/gTR2dlJp8YowJAj8Gthe2uRZ4wBhzwFr7+DlHf5b2dg/Ste0w8cEk7bEImzZ0sK4zNlsfLyJS9cpOCtbab5YsHjTGfB4oXZez1vac5uW3A0uB9dbaBPCyMeYm4JPArCSFvd2DbH7EEg0HaGkMM5JIs/kRy723GSUGEZGCc5qSaoxpBN4PPFWyeokx5nW8FkIX8EfW2oFC2UbguUJCmNQF3H8un38uurYdJhoOEK0NAhR/dm07rKQgIlJwVgPNxph7jDFjwHFgJd4YA3hdSx8EfgH4BF4S+IExximUtwG9U96ur7B+VsQHk0TCJ+fASDhAfDA5WyGIiFS9s20p/BDYgjfY/BngK8Dd1tpngWcL2+w0xuwCDgIbgK2A88a3ml3tsQgjiXSxhQCQTGVpj0UqGJWISHU5q5aCtXbUWvuqtfZR4NeAXzbGXHqK7Q4BA8CKwqo4b2wVLOKNrYcLZtOGDhKpLInxDK7rkhjPkEhl2bShY7ZCEBGpeudznsJk7f8Np08bY5YALUB3YdUW4FpjTGm1/FbgufP4/LOyrjPGvbcZGqIhBoZTNERDGmQWEZmirO6jwpTSTwPfx6vddwB/gTfF9FVjzP8NvAi8BlwE/L/A84UHwMPAUeBrxpj78aakvg94x0x9kXKs64wpCYiInEG5LQUXuARvTGEv8BBeAni3tTYPNAJfwxtw/nZhm1+01uYArLVp4A6863o/D/wZ8JHZPEdBRESmV1ZLwVqbBe46Q/mngE9N8x4WuPlsghMRkdmlax+JiEiRkoKIiBQpKYiISJGSgoiIFCkpiIhIkZKCiIgUKSmIiEiRkoKIiBQpKYiISJGSgoiIFCkpiIhIkZKCiIgUKSmIiEiRkoKIiBQpKYiISJGSgoiIFCkpiIhIkZKCiIgUKSmIiEiRkoKIiBQpKYiISJGSgoiIFCkpiIhIkZKCiIgUKSmIiEhRoJyNjDGfAd4HdAAjwE+B37fW9hTK7wD+O7ASeBn4mLV2a8nrFwP/ALwTOA78nbX2r2fwe4iIyAwot6WwB/gocAnwi8By4H8DGGMuBv4N+DpwNfA08BNjTHPJ678NNAJvBT4OfMoY84GZ+AIiIjJzymopWGu/WbJ40BjzeWBy3UeAZ621nwUwxvwO8B7gHuDvjTFXAjcCq621+4EXjTF/A/w2XiIREZEqcdZjCsaYRuD9wFOFVRuBRyfLrbVuYfnakvKDhYQwqQtYb4wJnUvQIiJyYZSdFIwx9xhjxvDGBFbijTEAtAG9UzbvK6w/U7kfaDnbgEVE5MI5m5bCD4E3AZuADPCVwnpnmtdNVy4iIlWirDEFAGvtKDAKvGqMscARY8ylQJwTrYJJizjROjhdeQ4YOJegRUTkwjjX8xQma/85YAtwy5TyW4DnCs+3ACuMMStLym8FXrTWps/x80VE5AKYtqVgjAkCnwa+j1f77wD+AtgOvAo8AOwwxvw/eF1MHwXqgIcArLUvGWOeAL5mjLkPWAHcB3xihr+LiIicp3JaCi7e+Qk/BPbiHexfA95trc1ba/cAvwR8CHgRuAF4l7V2qOQ9fhWv6+kZvJPY/spaOyvTUXuHh9kXj8/GR4mIzHnTthSstVngrmm2+RHwozOU9+CduzCreoaP8/nvPMpEJs8d163hXVevL5bt7R6ka9th4oNJ2mMRNm3oYF1nbLZDFBGpKvP62kcDI2NMZPIAPPbiQbK5HOAlhM2PWEYSaVoaw4wk0mx+xLK3e7CS4YqIVNy8TgprF7cTrvEDkExl2fKqd/5c17bDRMMBorVBHMchWhskGg7Qte1wJcMVEam4eZ0UQsEgV6xaVFx+avcBAOKDSSLhk3vOIuEA8cHkrMYnIlJt5nVSALjlClN83t0zQu/wCO2xCMlU9qTtkqks7bHIbIcnIlJV5n1S6GxdxEWLvIO968JjO/ewaUMHiVSWxHgG13VJjGdIpLJs2tBR4WhFRCpr3icFgOsuWV58vv3VY6xe1sS9txkaoiEGhlM0REPce5vR7CMRWfDKvszFXPa2dWv58bOWibTL2HiG7QcO8eY1K5QERESmWBAthXCohktXtBaXn3x5XwWjERGpXgsiKQDccuWJAecDx47TPzxWwWhERKrTgkkKqxa1095aA0DedenaubvCEYmIVJ8FkxQcx+Etl3QWl5/fe5RsNlfBiEREqs+CSQoAN1x8MeEa76rfiVSGLa8dqHBEIiLVZUElhXAwxBWrTtzv58mX959haxGRhWdBJQWATVddglO4RdDh3hGO9A+d+QUiIgvIgksKHbFWOtqjgHejiK6X9lQ2IBGRKrLgkgLA9ZevKj7fsT/ORDpTwWhERKrHgkwK165eTTTiXVJ7Ip3lyd17KxyRiEh1WJBJIeAPcM3aJcXlp3cfrFwwIiJVZEEmBYBNV16KrzDgHB8cZ+/RY5UNSESkCizYpNBa38CaZc3FZZ3hLCKygJMCwM1XrCs+39M9yMh4ooLRiIhU3oJOCld2LiNWHwYgm4VHd71S4YhERCprQScFx3G49uITN+DZag+Ty+t6SCKycC3opABw82UXE/R79xo6Ppxne/drFY5IRKRyFnxSqIuEuKzzxPWQntilpCAiC9e0t+M0xvwJcDewFhgC/g34Y2vtWKF8BTD1cqPD1tqmkveoA/4euAvIAA8Cf2itrYq+mlWti3n0uR6yGYeh/gmeWbWft1y6avoXiojMM+Xco/mtwOeB54F24AGgHvivU7bbCBwuPM9PKfsSsAF4O1AHbAaGgT8/p6hn0N7uQR5/voegL4QbSJPLwj/9aDct0Sbdw1lEFpxpk4K19o7SRWPMnwJfOcWmfdbanqkrjTHNwD3AO6y1WwrrPgV81hhzv7V2agKZVV3bDhOtDbK0tZ6DvQM4fkhMTPAfW7qVFERkwTmXMYVW4Pgp1j9hjDlqjPmxMebykvXX4F2Q9ImSdV1AG7DyHD5/RsUHk0TCAdqa6vE73vWQcjl47WhfhSMTEZl9Z5UUjDGNwB8A/1iyegz4beC9wC8DCbwE0V4obwMGp4wf9JWUVVR7LEIylSUY8NFUVwuAm4fx3Diu61Y4OhGR2VV2UjDG1ADfBfYDn5tcb63tt9Z+0Vr7vLX2aeB9eAf9DxQ2cU7xdlVztN20oYNEKktiPMPipgbyOYd83gF/hteH39AbJiIyr5WVFIwxAeBbeAPM77XWZk+3baFF8BKworAqDsSMMf6SzSZbCL1nG/BMW9cZ497bDA3REJmsS11tiIbmPIEgPGl1Ax4RWVjKmZLqA74OrAFumpyKeobtHeBSYGth1Xa81sINwM8K627FSwhTp7JWxLrOWHFQ+T+ff43vP/ciADv39XHnm5JEQ5FKhiciMmvKmZL6AHAz8C4gZIxZXFjfZ63NGWPuLrzP80AN8DvAMuCfAay1g8aYfwa+aIz5MBAF7ge+VOmZR6ey0XTw/Z/vZvB4ht6jfj63+Rl+fdObNBNJRBaEcrqPPgwsAV4AjpU8OgrlLvCnwIt4LYHlwK3W2iMl7/FbeC2GLrxxiW8Df3n+4c+8+ECC1FiQfA78AZcjvaNsfngPe7sHKx2aiMgFV855CqcaKC4t/1fgX6fZZgz4YOFR1bq2HWZJSz374ylc8qQzeVxfhq5th9VaEJF5b8Ff+2iq+GCS9uYIkVAIgHwehsZHiA8mKxyZiMiFp6QwRXssQnIiS3tzfXHd4HCGpsZyhl9EROY2JYUpJs9biNSECDgB8jnITDjE2icqHZqIyAWnpDDF5HkLsYYwtaEafH5oaM7TPdBDJpepdHgiIheU+kROYfK8hcPxlXzh+4+RzmXo63PZ07ufK5aYSocnInLBqKVwBsva6ulo8WYc5XIOj+/aq+shici8pqRwBo7jcMPlq/E53qzcA4dSHB3R9ZBEZP5SUpjGlavbaYk2AJBKOTxhd1c4IhGRC0dJYRo1QT/Xms7i8kuv9TMyccbLP4mIzFlKCmV4y6Wd1BUuijd63MeWg69UOCIRkQtDSaEMTfU1XNa5BPAu9PTMrkOanioi85KSQpluvmI1IX8QgL4+l13xfRWOSERk5ikplKmjvZ7O1hbAm576s51W01NFZN5RUiiT4zjcdOVa/I63y7qPpDkwdGSaV4mIzC1KCmfh8pWLaGtoAiA94fDYrl0VjkhEZGbpMhfT2Ns9SNe2w94ltWMR1rS10zM8iAvs2T9CfH0f7fWLKh2miMiMUEvhDPZ2D7L5EctIIk1LY5iRRJqdrx0n6HrTU5MJH0/sVWtBROYPJYUz6Np2mGg4QLQ2iOM4RGuDNERDhIgWt9lue3Uym4jMG0oKZxAfTBIJn+hhOz6a4uCxEQ71JBgdCJEadxg57uOZ/S9XMEoRkZmjpHAG7bEIyVQW8BLCa0eGSU1kaaoLUReuZWTIR2rc4ZmXD5PK6iY8IjL3KSmcweRd2BLjGV7vS+DgndG8rK2eFYubCPh9JMcc+vsdtnbr0hciMvcpKZzB5F3YGqIhRhJpwjV+1nY00VhXQyQcpLm+lmzGwXXh8Z37SevSFyIyx2lK6jQm78IGMJJIE60NFsta6+sYTo4DOeI9eV44sodrO6+oUKQiIudPLYUylXYlua5LYjxDYjxNOBCi96if/rifHzz5Ktl8rtKhioicMyWFMpV2JQ0Mp8jl8ziOw5JYA4GAQz4Hr1mHh59/qdKhioics2m7j4wxfwLcDawFhoB/A/7YWjtWss064AHgWqAH+Iy19sGS8gDweeCDQBD4LvAJa21ixr7JLCjtSvryd3fg9/mI1gbpGxllZDxJPufywye7edeGq/A5yrciMveUc+R6K94B/Wrg/cA7gS9OFhpjgsCPgTjwZuB+4AFjzE0l7/GnwK/hJZe3AxtL32MuKj2HYVlrEw4Ojg+OD+XZHd9f4ehERM7NtC0Fa+0dpYvGmD8FvlKy7nZgKbC+UPN/uZAQPgk8bozxAb8F/KG19lEAY8wngUeMMb9nrT0+Q99lVrXHIsWB5+a6MNFwmJHEOP4APLx9F5fcvkqtBRGZc87lqNUKlB7INwLPTekK6sLrSgJYVXjNoyXljwMOcM05fH5VOGngGZfW+jpc10ekzqX7cJqXj+kmPCIy95xVUjDGNAJ/APxjyeo2oHfKpn2F9ZT8LG5jrc0BgyVlc87Ugef62hDRmiAjQz76evx859FXyOfzlQ5TROSslJ0UjDE1eAPE+4HPlRQ507x0uvI5a11njI/90lW8/52GVCbPRa2NBAKQz4G1eX68TTORRGRuKSspFGYPfQuoB95rrc2WFMd5Y41/ESdaBvHCz+I2xhg/EOONLYw5afJqqkta6miI1OLzg89x+cETB8npvAURmUOmTQqFgeKvA2uA20unohZsAa41xkRK1t0KPFd4vh/oB24pKb8R7zJC288x7qpSOhOps625OBNpcNDl/q8/yae/+gxf/u4O9nYPVjhSEZEzK6el8ABwM/DrQMgYs7jw8BfKHwaOAl8zxlxmjPkN4H0Uppxaa/PAl4G/NMbcYozZCPx/wDestUMz+3Uqo/Rqqg3RGhojEbJpyGcd9h09TqyhhpFEms2PWCUGEalq5SSFDwNLgBeAYyWPDgBrbRq4o7DN88CfAR+x1j5e8h5/DnwHb0yiq7DdJ2fmK1Te1EtgLGqoJ5vz4Q+6ZDJ5eseOE60NEg0H6Np2uNLhioicVjnnKUw7UGyttXitidOVZ4H7Co95Z3Im0uS9nJe0RjnYEyTtevdYONI3TFtdE5FwgPhgssLRioicnq6SOkNKL4EBkExl2Lr3dXByTEy4HB0eoDncRHsscoZ3ERGpLJ1ye4H84vWrqAvXkM+B68Lh+CgjyRSbNnRUOjQRkdNSUrhA1nXG+M33rCcU9JPLOuTzcFFH5qTWhIhItVFSuIA2XLqYX3nHWtouyhFblOfVYwPERzT7SESql5LCBXbbNYbmqDeOkEk7/MszW3Fdt8JRiYicmpLCBRYK+vnF6y4rXuvD7h/hlaMHKxmSiMhpKSnMgo3rltPZ5o0l5F2H7z79ki5/ISJVSUlhFjiOw6/c8Cb8hfsrxPsyPLp7Z4WjEhF5IyWFWdLZ3szVa5YWl/9z22uMTehENhGpLkoKs+iut15FbSgEQCIB//rslgpHJCJyMiWFWdQQDfP2N60tLj+/u4/dx3QtJBGpHkoKs6wz1sbESJjeo376437+14+2k8llp3+hiMgsUFKYRXu7B/nmf+7lopbm4h3aDnXn+erDz1Y6NBERQElhVk3eoa2tKcqSlnrvDm0+l58/38uh/v5KhycioqQwm0rv0LairYWaUADH553p/I1Ht5LP5yscoYgsdEoKs6j0Dm0+n8OaJa24eQgEXY72J/jx8y9VOEIRWeiUFGbR1Du0BX1B6iNhInXetZB+un0frw/ognkiUjm6yc4smnqHtvZYhN+9ewPffPpZjo+myeZcvvj9p1neuIzeoXHaYxE2bejQ5bZFZNYoKcyyqXdoA7in5k38z//zHONJh96jGZKtfay9qJWRRJrNj1juvc0oMYjIrFD3URW4dFkHb75kCckxB5/PpX9kjOREmmhtkGg4QNc2neAmIrNDSaFK/NrbNoLrx/GB67rY1+O4rkskHCA+qGskicjsUFKoEjXBIJevWoRbmJU6PpHlQF8/yVSW9likssGJyIKhpFBF7r7pYhqiteRz4LrwenyU+NAomzZ0VDo0EVkglBSqyLrOGPfdvYFoJEgu6+DzQzowSltruNKhicgCoaRQZS5dtYi/+MjbWNaZJ7Yoj+tk+MpPniKb1Z3aROTCK2tKqjHmLuDjwAagwVrrlJTdDDw25SU7rLXrS7ZZDPwD8E7gOPB31tq/Pr/Q56+lsRi/fONlfLPrZVzgUO8w33pyO/fe8uZKhyYi81y5LYUI8CjwuTNss6TksWlK2beBRuCteMnlU8aYD5xdqAvL28zFbLysvbj87O5unnplXwUjEpGFoKyWgrV2MxRbBafbpudU640xVwI3AquttfuBF40xfwP8NvD1sw14IXn/9W/hyMDDvN6TwgX+9ec7Wd4ao6OtudKhicg8NWNjCsaYg8aYQ8aYbxljlpcUbQQOFhLCpC5gvTEmNFOfPx8F/AF+87YbiUa93rp0NssDDz/D2Hi6wpGJyHw1E0nhGPBh4D3AB4E24DFjTG2hvA3onfKaPsAPtMzA589rsWgDH3z7Nfj93kXzBseSPPDIU+TzboUjE5H56LyTgvX8o7V2h7X2MbzkEAPeXdjEOf2rpRyXLV3BL1y3qrgj9x0d4F+e2l7RmERkfprxKanW2lHgNWBFYVUcr7VQahGQAwZm+vPnq9uvvJor150YS3hy5wGe2q2BZxGZWTOeFArdRquA7sKqLcAKY8zKks1uBV601qpzvEyO4/Chm2/gorYaUuMOA30+/u6fX+L+B59mb7fuwSAiM6Pc8xRiwHJgTWF58hyEV4APAT3ALqAJ+G/AGPDvANbal4wxTwBfM8bch9eCuA/4xEx9iYUiFAjxzsvX8z92Po/r5vH5XV4+0MfgcJrf/C9X6vLaInLeym0p3Am8AHy1sPxC4XEREAS+gJcU/h3IApustWMlr/9VYBR4Bu8ktr+y1mo66jnYtmuQtUtjBIPgOOD4chwbGuZfHttb6dBEZB4o9zyFB4EHT1P8pcLjTK/vwRuAlvMUH0zS1lRPTY3D7sN93sXzyLFzfy+7DvRz2crWSocoInOYrn00x7THIiRTWZoi9axdGsPvBzcPjj/LQ48+z87X+iodoojMYUoKc8ymDR0kUlkS4xlaoo0sjtWBA5E6l+GJUb79xIu8YKeeFiIiUh4lhTlmXWeMe28zNERDDAynWNXWzp3vWESs1buK6lBqmO89/RJbXznlVUdERM6orDEFqS7rOmMnzTRyXZdFbU/z02dfJzHmY2D8OD/asotsLs91ly/BcXT+oIiURy2FecBxHN6+9i3ccm07dQ3e/Tz7k0M8/Pxufr7jKK6rS2KISHmUFOYJn+PjnWuv54ZrWmloPJEY/uOF3fxs+xElBhEpi5LCPOL3+bnd3MQNGxbR2OyNMQyOH6frpT10bT2ki+iJyLSUFOYZv8/PbWtv5KYNi2mOFQafx4fp2rmbR547QE6JQUTOQElhHvL7/Lxj9Q3cvOGi4qykkYkxHnt5Nz9+eh+5XL7CEYpItdLso3nK5/Nx6+q3URPYyk+3HaS/108iM87jr+zmaN8ouZxD39A47bEImzZ06LpJIgKopTCv+Rwf13du5M63XMKixVkAjo+meXR7N7v299NcX8NIIs3mR6yutCoigJLCvOc4DtcsvZJfuf5q2pfmSI45OE6eofFRXn19gNqaANFwgK5thysdqohUASWFBeLStnV86KbrCQTA8QG4DIyNsvNgD67rEh9MVjpEEakCSgoLyPKmpVy9poNoxF9cN5YaZ+eBHtKZHKl0toLRiUg1UFJYYO64bjVLGlpoqPPmGORzkM7mGEwP8E8/3sGBo8MVjlBEKklJYYFZ1xnjA7dfylWdy2ltjBCOuDQ05/GHMuzqOcDmn77Iw88cJJnKVDpUEakATUldgEovqLdv4CA/eGEL3fsdslmHnrE+HtuV4GDPcW6+ejlmebMuqCeygKilsMCtblnBb7ztNq7bGCHW4p3oNpZOsjt+gO89uZsf/fwAo8l0haMUkdmiloLQVNvIr1z5C2xt2cFT1nLkYIB0OseRkWOM7B3lUO8wN17VweWrW9RqEJnnlBQE8C6NcV3H1SxrXEJX7Fn2H0gzEPczMjGG7U1y/Jkx9h5azK1v7qC5PlzpcEXkAlFSkJMsa1jCr115B882b2f7gf0cORgklcpzdDTO8X3DHOob4sarOlm/rg2/T60GkflGSUHeoCYQ4qaV17E61snPYs+x/0CK3p4AyUyKfQOHGHxmmD3dS3nnxpUsaq6tdLgiMoM00CyntaxxCb96xR3c9mbDussyRCJ5XGBwfJjn9u/hy//nOZ7ccYTUhE56E5kv1FKQMwr6g1y77E2sa1nFz9u2smNvP/GjfhIJl77eQV5+dRsNkTA3r1/Obdep5SAy1ykpSFmaaxt5t9nE5e1H+N6z29nyXA43Dz5/ntHxJD98ei87u4+xYe0yrlrbxuqljfj9aoiKzDVKClI2x3FY2dxBODHAqosGGUgMk0i65LKQz+U5FB9mglF2HznGkqYYV6xexOWrWqiLhCoduoiUqaykYIy5C/g4sAFosNY6U8qvBb4EXA7sB37fWvuTkvI64O+Bu4AM8CDwh9ba3Ax8B5llvUPjLGlqZnFTE0Pjw8SHhxhLuIwnfWTyWeKJfvqSgxwe7OXZXU2Y5a1cuaaVi1qjOs9BpMqV21KIAI8CPwX+qrTAGNMC/AT4BvAB4D3A94wxV1hrXy1s9iW8hPJ2oA7YDAwDf36+X0BmX3sswkgiTbQ2SEukmebaRo4NDZJoHGHR0iwDvX4ymTwD48cZGD/OsdFeduxvZHlrjCvWLMIsbyYU9E//QSIy68pKCtbazQDGmJtPUXwPMAL8rrXWBV4xxtwOfBT4A2NMc2Gbd1hrtxTe51PAZ40x91trdcPgOWbThg42P2IBiIQDjKdyBJ0ov/mu9TjRIXYc282hYwkGev2MjfoYSycZSyc5NtqHPXaU1romrlrdzhWrW2lu0IlwItVkJsYUNgKPFRLCpC68VgHANYALPDGlvA1YCeybgRhkFq3rjHHvbYaubYeJDyZpj0V4702rCxfZa+Xi1jUcWv46u3r38lpPDwO9fob6fWTzWfqTQ/Qnh+gePMrPXqrn4o521q9ZTEd7PbU1GuISqbSZ+C9sA16Ysq6vsH6yfHDK+EFfSZmSwhxUeqXVqRzHobNpGZ1NyxhZPsorfa+yu/cAx45lGOj1k0o5jGcnGM9OEN/Tz9Z9+6gP1dHZFmPN0laWtdVxUWsdwYBmL4nMtplICtONHJ6q3D3FOpmHGsL1XNdxNRuXrufQqqPY/td45XAPfXGHkSE/LpDMpEhmUsQP9LPj0EHqaqI0hKOsam9h+eIGlrXV0dYcwafLaohccDORFOKcaBVMWgT0lpTHjDH+ktbC5Pa9yILg8/lY0byMFc3LuGnlBAeHjrDr6AFeOTjA6LBDMuHDdWEil2YimWYgOUT30FEi+2qJhmppqo2yekkLHYvr6Wirp6m+RjOZRC6AmUgKW4D7pqy7FXiu8Hw7XmvhBuBnJeW9wIEZ+PzT2ts9eFK/96YNHaft8pDZEw7UcPGi1fiSzRxNH2R4dIhozQQ1TaP4HBgdcUiN+8i7ecbSCcbSCeJj/bzWf5jaPbVEgmFa6utYt7SV5YsbWdZWT11tsNJfS2ReKPc8hRiwHFhTWF5fKHoFeAj4tDHmb4GvAHcC1wL/F4C1dtAY88/AF40xHwaiwP3Aly6sy7AxAAAM2UlEQVTkzKO93YNsfsQSDQdoaQwzkkiz+RHLvbcZJYYqUPr76VwUI5nKMjbQyC3Xt+CLjrCv/yjx/nHGRnyMDvvIZBxyJUmiNzHAnp5uagI1hAM1NEdraW9uYGmsidamCM31YZrra4jWBtWikFnlui6ZXIaJTJaJTIaJbIbU5PNMlolslnQ2SzqbI11YzmRz3iOXI53NkcnmyWRzDA5liPfmiQRrWbesdVYqtuW2FO4E/qlkeXJgeaW19qAx5l145yJ8DO/ktbtKzlEA+K1CeRcnTl77y/OIe1pd2w4TDQeIFmqQkz+7th1WUqgCp/v9vLJngo/90kau73QZnhjl6Eic10d6ONDXS/9AhrERh7FRH7mcgwukshOkshMcT41wYCAOQMgfpMYfoiYQIlJTQ1tjHe3N9bQ11dFcX0NzQ5jGaEiX4ViA8vm8d5BOZ0hlMoxn0icO2Oks6WyGiUyOVDZLOpMtHKCz3sE6M3ngzpPN5kjn8mSzebL5PNmsSzaXJ5vLk8+75PPnXxFJjTuMDPnw+VzGglkWj9XPSsW23PMUHsQ7kJ+u/Fm8qaenKx8DPlh4zIr4YJKWxpPnwEfCAeKDydkKQc5gut+P4zg0hRtoCjdwadta3NVekugZ7ePYaJz98QGO9iUZG3ZIjjnk3RP/hOlchnQuw2g6AUk4NAQchIDjIxgIEfQFqAkEaaoL09oYoa2pjvbGeuojYWprgoRr/IRDAWqCfg1uzyLXdcnl3WLtOZVOM55Oe88LtexUJkM6kyWVKdS2M1kmCgfudMY7YGdKatqZwoE6m3O9A3huNua4zMzfTHLMwedz8fkhUhMgGgmBk7ngFdt5OzG89KzbSclUlvZYpIJRyaSz/f2UJomLF63mllXewb8/MUA8McDrA4McHTxO33CS1DhMpBxS4w653Il/0KybJ5tJMQ4wAX0JeDV+4jN8jkPAFyDg8xd/hkMBIjVBIuEQ0XCIuuLPGurCYSLhILU1AcI1AUIBHz6fQ8Dvw+9z8PmcOd915bpeDTiTzZ90wJ7sGklnTyynCwfmYtdIoVskXdI1ks3lSWdzZLP54nI259W0c/k8OTePOw/mJvocB//k34Lf+xkI+Aj6fQT8PoIBH0G/n0DARyjgJ1h4hAJ+gn4/oUCAf/9ZDw11QUKBALG6KDA7Fdt5mxSmnnWbTGVJpLK896bVFY5MYGZ+PyF/kLGhEM9uyxIfDNAeW857r1lKW5uP4+MjDCSH6BsdIT40Qt9IklQSUimHiXGHdPqNB+u86xZbGUXjZ47B5zj4cPD5/PgcH05h2XF8xQODv5AkJg8Qft+JpBEoLE8+B8B1wOHEwdF1cb0f3lxu14sVF1xcXNctlrmuS7647JYsF57nS5bxnk8uT/7M5fPkci7ZvEs+5+K6efKTn1P2b6d6eb8Th2DAR8DvEPD7C88nD9A+EmMux+JZxsfzNNQFWbcqyrK2KKFggJpAwPs5+QgEqQkECIdChIMBQoEgwYDvvCsEBw/kK1KxnbdJ4cxn3UqlzcTv51STCb71H68W+lxXsKZlRXFb13VJpJOMpMcYnRhjKDFK7/AY/cNJBkfHGRlLk8m4ZLOQyzrez9z0/9R51yWPCzldrWUm+BwXZ/KgXVK7Dga856HCATwUDBAKBAgGfIUDs3cwrgn6qQkGqQkGCAcDhIMhwqEgNcEgtcEg4eD0Y0mTf1dLmwNElngVltePZLnlstmdvVipiu28TQpw5rNupfLO9/dzNpMJHMehriZKXU0U6tuh9eT3mpwxksyMk8iMk8pOkEiPMzI+zmgyxVhqgkQqTTKVZjydI5XOkprIkcu6jAz7OD7gJ52GYBCidXlCYeZcN0hq3BufyWYcAkGXSJ1LuNb1+rV94POB48Nr3fi9WnbQ7y/WuoN+f6G27R2sgwE/Ib+/WKsOBQKEAt5BOxwIFtZ7B/DaYKhQ4w6y78gIj05WFhpmfyp5tUxSqVTFdl4nBZnfZnIygeM4hAIhQoEQTbWNZb3GdV12H+zjoUf2snipn5oah2QqSzKV4fYblrBsSZhMNsdENku22H9e+Jn3+tJzeZdcLs+x+ASv7k8xlsgRjfpZs7KG1pYgjlMYtnQm4yw8cHB83mrH8eE4eN1WPgcHx+vWchx8Pl+he8vrovIVu7V8+B1/8fnr8XH+8+k4Fy0JEAkHmMi4TKRz3P32VVyyohW/z4/f8RW7yC6Uvd2DPFThqeTVNEmlEhVbJQWZsyo9mcBxHB7ffoyGSE0xhtoghAMZdtsJbr784rLeZ2/3INu2WhbVRehs9boJjh3OsunS2TsQ/vy5HbQ11he/R30tJMYzPPfSAFevXTYrMUB11NIr/XdVaZqoLXPWpg0dJFJZEuMZb8xgPEMilWXTho6y32Nv9yBf/u4OPv3VZ/jyd3ewt3vwrGKIDyaJhE+uW51trbL0QOg4DtHaINFwgK5th88qlvMxE99jvsQxE39Xc5mSgsxZk32uDdEQA8MpGqKhs+pmmBxQHEmkT+qqOJvE0B6LkExlT1p3trXKajgQzsT3mC9xnO/f1Vyn7iOZ086nz3UmuipmYoZINXRXVMsU7mqJYyFPUlFLQRasmaihz0Stshq6K6qldlwtcSxkainIgjVTNfTzrVVWyzk11VI7rpY4FiolBVmwqqWrAnQglOqh7iNZsNRVIfJGainIgqYausjJ1FIQEZEiJQURESlSUhARkSIlBRERKZorA81+gJ6enkrHISIyZ5QcM/3lvmauJIUlAPfcc0+l4xARmYuWAPvK2XCuJIWtwA3AMSBX4VhEROYKP15C2FruCxx3rt0eSkRELhgNNIuISJGSgoiIFCkpiIhIkZKCiIgUKSmIiEiRkoKIiBQpKYiISJGSgoiIFM2VM5rPmTHmj4BPAk3AfwAfsdb2Vjaq6maMeRD44JTV91lr/7ZkmzuA/w6sBF4GPmatLfusyfnMGHMX8HFgA9BgrXWmlF8LfAm4HNgP/L619icl5XXA3wN3ARngQeAPrbUL8mz+M+1PY8zNwGNTXrLDWru+ZJvFwD8A7wSOA39nrf3rCx33XDWvWwrGmP8K/DHeH9Rb8RLDNysa1NzxHbzT4ycfD0wWGGMuBv4N+DpwNfA08BNjTHMF4qxGEeBR4HNTC4wxLcBPgKfw9t03gO8ZY9aWbPYl4M3A24G7gfcBf3KBY65mp92fJUr/VjdNKfs20Ih3DPg48CljzAcuQJzzwnxvKXwS+IK19nsAxpjfAPYZYy631r5c2dCq3ri19nSXpf0I8Ky19rMAxpjfAd4D3INXw13QrLWboViLneoeYAT4XWutC7xijLkd+CjwB4XEeg/wDmvtlsL7fAr4rDHmfmttfja+QzWZZn9ObnPKv1VjzJXAjcBqa+1+4EVjzN8Av41XqZEp5m1LwRhTA1yFV8MAoPBHcRC4tkJhzSV3GmP6jDEvGWP+yBhTWoHYyMn71S0sa79ObyPwWGGfTerixL67BnCBJ6aUt+F11ckpGGMOGmMOGWO+ZYxZXlK0EThY+N+f1AWsN8aEZjfKuWHeJgWgBe/7TR0/6MP7B5PT+3fg/cCtwBeA3wP+vKS8De3XczXdvmsDBqeMH/SVlMnJjgEfxmupfhBvHz1mjKktlJ9uf/vxjhEyxXzuPnKm30ROxVr7nZLFncaYHPBlY8yfFGq42rfnbrp9d6pyXcr4NKy1FrCTy8aYbcAh4N3Av6C/1bM2n1sK/UCeN9auFvHGmoOc2XYgCrQWluNov56r6fZdHIgZY0rvlDW5vfbvNKy1o8BrwIrCqtPt7xwwMHuRzR3zNilYayeAHcAtk+uMMSvx/lieq1BYc9XlQBIv0QJsoWS/FtyC9ms5tgA3T1l3Kyf23Xa82u0NU8p7gQMXOri5rtBttAroLqzaAqwo/O9PuhV40Vqbnu345oJ5fZOdwmyjvwV+HW+A+W8ArLW3VjCsqmeM+QLeNL443rTJLwLfttb+XqH8YryE+2fAD/FmztwLrLXWDlUk6CpijIkBy/Hm1X8VeFOh6BWgHngVb+bLV4A7gc8AV1hrXy28/uuF13wYr4W2GfiKtbZ0XGfBmGZ/fgjoAXbhTTn/b3gTTC6z1o4VXv84XsvgPrxK4TeAT1hrNfvoFOZtSwHAWvuPwGfxTlx5BhjFm/MtZ3Yp8CO8vtq/xjt4/dFkobV2D/BLeP+QL+LVat+lhFB0J/AC3gGMwvMXgIustQPAu/D22Yt4+/CuyYRQ8Ft4LYYu4Lt4CfovZyXy6nTa/QkE8SZD7MKbIJEFNk0mhIJfxfvffwbvWPBXSginN69bCiIicnbmdUtBRETOjpKCiIgUKSmIiEiRkoKIiBQpKYiISJGSgoiIFCkpiIhIkZKCiIgUKSmIiEjR/w8ui9LgHEi/KAAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results.G, 'g-')\n", - "plot(results2.G, 'b-')\n", - "plot(data.glucose, 'bo')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The differences in `G` are less than 1%." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0 0\n", - "2 -0.115619\n", - "4 -0.0783993\n", - "6 0.290855\n", - "8 0.52969\n", - "10 0.579892\n", - "12 0.59593\n", - "14 0.580565\n", - "16 0.535504\n", - "22 0.282925\n", - "32 -0.122467\n", - "42 -0.454298\n", - "52 -0.734567\n", - "62 -0.798479\n", - "72 -0.700541\n", - "82 -0.499145\n", - "92 -0.458979\n", - "102 -0.350362\n", - "122 -0.155287\n", - "142 -0.0781028\n", - "162 0.0185836\n", - "182 0.0544144\n", - "Name: G, dtype: object" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "diff = results.G - results2.G\n", - "percent_diff = diff / results2.G * 100\n", - "percent_diff.dropna()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Optimization" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's find the parameters that yield the best fit for the data. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll use these values as an initial estimate and iteratively improve them." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
G0290.00000
k10.03000
k20.02000
k30.00001
\n", - "
" - ], - "text/plain": [ - "G0 290.00000\n", - "k1 0.03000\n", - "k2 0.02000\n", - "k3 0.00001\n", - "dtype: float64" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(G0 = 290,\n", - " k1 = 0.03,\n", - " k2 = 0.02,\n", - " k3 = 1e-05)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`make_system` takes the parameters and actual data and returns a `System` object." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params, data):\n", - " \"\"\"Makes a System object with the given parameters.\n", - " \n", - " params: sequence of G0, k1, k2, k3\n", - " data: DataFrame with `glucose` and `insulin`\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " G0, k1, k2, k3 = params\n", - " \n", - " Gb = data.glucose[0]\n", - " Ib = data.insulin[0]\n", - " \n", - " t_0 = get_first_label(data)\n", - " t_end = get_last_label(data)\n", - "\n", - " init = State(G=G0, X=0)\n", - " \n", - " return System(G0=G0, k1=k1, k2=k2, k3=k3,\n", - " init=init, Gb=Gb, Ib=Ib,\n", - " t_0=t_0, t_end=t_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
G0290
k10.03
k20.02
k31e-05
initG 290.0\n", - "X 0.0\n", - "dtype: float64
Gb92
Ib11
t_00
t_end182
\n", - "
" - ], - "text/plain": [ - "G0 290\n", - "k1 0.03\n", - "k2 0.02\n", - "k3 1e-05\n", - "init G 290.0\n", - "X 0.0\n", - "dtype: float64\n", - "Gb 92\n", - "Ib 11\n", - "t_0 0\n", - "t_end 182\n", - "dtype: object" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(params, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`error_func` takes the parameters and actual data, makes a `System` object, and runs `odeint`, then compares the results to the data. It returns an array of errors." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def error_func(params, data):\n", - " \"\"\"Computes an array of errors to be minimized.\n", - " \n", - " params: sequence of parameters\n", - " data: DataFrame of values to be matched\n", - " \n", - " returns: array of errors\n", - " \"\"\"\n", - " print(params)\n", - " \n", - " # make a System with the given parameters\n", - " system = make_system(params, data)\n", - " \n", - " # solve the ODE\n", - " results, details = run_ode_solver(system, slope_func, t_eval=data.index)\n", - " \n", - " # compute the difference between the model\n", - " # results and actual data\n", - " errors = results.G - data.glucose\n", - " return errors" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When we call `error_func`, we provide a sequence of parameters as a single object." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how that works:" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "G0 290.00000\n", - "k1 0.03000\n", - "k2 0.02000\n", - "k3 0.00001\n", - "dtype: float64\n" - ] - }, - { - "data": { - "text/plain": [ - "0 198.000000\n", - "2 -71.558069\n", - "4 -19.837747\n", - "6 4.552173\n", - "8 3.778869\n", - "10 16.556562\n", - "12 10.879405\n", - "14 6.755465\n", - "16 6.205389\n", - "19 -3.021029\n", - "22 5.025756\n", - "27 -3.328046\n", - "32 3.140578\n", - "42 -1.100002\n", - "52 2.928112\n", - "62 6.230846\n", - "72 8.105344\n", - "82 11.467763\n", - "92 4.717777\n", - "102 5.066338\n", - "122 4.391387\n", - "142 6.078120\n", - "162 4.951526\n", - "182 1.674908\n", - "dtype: float64" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "error_func(params, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`fit_leastsq` is a wrapper for `scipy.optimize.leastsq`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we call it." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[2.9e+02 3.0e-02 2.0e-02 1.0e-05]\n", - "[2.9e+02 3.0e-02 2.0e-02 1.0e-05]\n", - "[2.9e+02 3.0e-02 2.0e-02 1.0e-05]\n", - "[2.90000004e+02 3.00000000e-02 2.00000000e-02 1.00000000e-05]\n", - "[2.90000000e+02 3.00000004e-02 2.00000000e-02 1.00000000e-05]\n", - "[2.90000000e+02 3.00000000e-02 2.00000003e-02 1.00000000e-05]\n", - "[2.90000000e+02 3.00000000e-02 2.00000000e-02 1.00000001e-05]\n", - "[ 2.43781445e+02 1.61703647e-02 -6.49036599e-03 7.89648343e-06]\n", - "[2.64044392e+02 2.45499485e-02 1.38130607e-02 9.55517964e-06]\n", - "[2.64044396e+02 2.45499485e-02 1.38130607e-02 9.55517964e-06]\n", - "[2.64044392e+02 2.45499489e-02 1.38130607e-02 9.55517964e-06]\n", - "[2.64044392e+02 2.45499485e-02 1.38130609e-02 9.55517964e-06]\n", - "[2.64044392e+02 2.45499485e-02 1.38130607e-02 9.55517978e-06]\n", - "[2.48636533e+02 1.78648756e-02 3.64812583e-03 8.31669768e-06]\n", - "[2.50264165e+02 1.87408371e-02 9.93907437e-03 9.09094288e-06]\n", - "[2.50264169e+02 1.87408371e-02 9.93907437e-03 9.09094288e-06]\n", - "[2.50264165e+02 1.87408374e-02 9.93907437e-03 9.09094288e-06]\n", - "[2.50264165e+02 1.87408371e-02 9.93907452e-03 9.09094288e-06]\n", - "[2.50264165e+02 1.87408371e-02 9.93907437e-03 9.09094301e-06]\n", - "[2.52120010e+02 2.00244940e-02 6.21404563e-03 9.23760328e-06]\n", - "[2.52120014e+02 2.00244940e-02 6.21404563e-03 9.23760328e-06]\n", - "[2.52120010e+02 2.00244943e-02 6.21404563e-03 9.23760328e-06]\n", - "[2.52120010e+02 2.00244940e-02 6.21404572e-03 9.23760328e-06]\n", - "[2.52120010e+02 2.00244940e-02 6.21404563e-03 9.23760341e-06]\n", - "[2.51545351e+02 1.98080313e-02 1.57093375e-03 9.18079777e-06]\n", - "[2.51828156e+02 2.00684913e-02 6.18246731e-03 9.21691948e-06]\n", - "[2.52021343e+02 2.00354769e-02 6.37459848e-03 9.22775951e-06]\n", - "[2.52093423e+02 2.00218709e-02 6.29608020e-03 9.23305346e-06]\n", - "[2.52093427e+02 2.00218709e-02 6.29608020e-03 9.23305346e-06]\n", - "[2.52093423e+02 2.00218712e-02 6.29608020e-03 9.23305346e-06]\n", - "[2.52093423e+02 2.00218709e-02 6.29608029e-03 9.23305346e-06]\n", - "[2.52093423e+02 2.00218709e-02 6.29608020e-03 9.23305360e-06]\n", - "[2.52082575e+02 2.00040624e-02 6.41672141e-03 9.23333369e-06]\n", - "[2.52082579e+02 2.00040624e-02 6.41672141e-03 9.23333369e-06]\n", - "[2.52082575e+02 2.00040627e-02 6.41672141e-03 9.23333369e-06]\n", - "[2.52082575e+02 2.00040624e-02 6.41672150e-03 9.23333369e-06]\n", - "[2.52082575e+02 2.00040624e-02 6.41672141e-03 9.23333382e-06]\n", - "[2.52055059e+02 1.99850879e-02 6.48381520e-03 9.23299273e-06]\n", - "[2.52075539e+02 1.99936972e-02 6.44821917e-03 9.23317869e-06]\n", - "[2.52081338e+02 2.00002970e-02 6.42721881e-03 9.23328278e-06]\n", - "[2.52081342e+02 2.00002970e-02 6.42721881e-03 9.23328278e-06]\n", - "[2.52081338e+02 2.00002973e-02 6.42721881e-03 9.23328278e-06]\n", - "[2.52081338e+02 2.00002970e-02 6.42721891e-03 9.23328278e-06]\n", - "[2.52081338e+02 2.00002970e-02 6.42721881e-03 9.23328291e-06]\n", - "[2.52076826e+02 1.99909811e-02 6.44966243e-03 9.23319060e-06]\n", - "[2.52076830e+02 1.99909811e-02 6.44966243e-03 9.23319060e-06]\n", - "[2.52076826e+02 1.99909814e-02 6.44966243e-03 9.23319060e-06]\n", - "[2.52076826e+02 1.99909811e-02 6.44966252e-03 9.23319060e-06]\n", - "[2.52076826e+02 1.99909811e-02 6.44966243e-03 9.23319073e-06]\n", - "[2.52048752e+02 1.99560788e-02 6.51555288e-03 9.23303909e-06]\n", - "[2.52073810e+02 1.99826513e-02 6.46305186e-03 9.23315789e-06]\n", - "[2.52076593e+02 1.99900927e-02 6.45103659e-03 9.23318720e-06]\n", - "[2.52076804e+02 1.99908917e-02 6.44980021e-03 9.23319026e-06]\n", - "[2.52076807e+02 1.99908917e-02 6.44980021e-03 9.23319026e-06]\n", - "[2.52076804e+02 1.99908920e-02 6.44980021e-03 9.23319026e-06]\n", - "[2.52076804e+02 1.99908917e-02 6.44980031e-03 9.23319026e-06]\n", - "[2.52076804e+02 1.99908917e-02 6.44980021e-03 9.23319039e-06]\n", - "[2.52076758e+02 1.99907121e-02 6.45007558e-03 9.23318957e-06]\n", - "[2.52076762e+02 1.99907121e-02 6.45007558e-03 9.23318957e-06]\n", - "[2.52076758e+02 1.99907124e-02 6.45007558e-03 9.23318957e-06]\n", - "[2.52076758e+02 1.99907121e-02 6.45007567e-03 9.23318957e-06]\n", - "[2.52076758e+02 1.99907121e-02 6.45007558e-03 9.23318971e-06]\n", - "[2.52076666e+02 1.99903500e-02 6.45062541e-03 9.23318821e-06]\n", - "[2.52076749e+02 1.99906758e-02 6.45013062e-03 9.23318944e-06]\n", - "[2.52076753e+02 1.99906758e-02 6.45013062e-03 9.23318944e-06]\n", - "[2.52076749e+02 1.99906761e-02 6.45013062e-03 9.23318944e-06]\n", - "[2.52076749e+02 1.99906758e-02 6.45013071e-03 9.23318944e-06]\n", - "[2.52076749e+02 1.99906758e-02 6.45013062e-03 9.23318957e-06]\n", - "[2.52076731e+02 1.99906031e-02 6.45024067e-03 9.23318916e-06]\n", - "[2.52076735e+02 1.99906031e-02 6.45024067e-03 9.23318916e-06]\n", - "[2.52076731e+02 1.99906034e-02 6.45024067e-03 9.23318916e-06]\n", - "[2.52076731e+02 1.99906031e-02 6.45024077e-03 9.23318916e-06]\n", - "[2.52076731e+02 1.99906031e-02 6.45024067e-03 9.23318930e-06]\n", - "[2.52076694e+02 1.99904571e-02 6.45046063e-03 9.23318862e-06]\n" - ] - } - ], - "source": [ - "best_params, fit_details = fit_leastsq(error_func, params, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The first return value is a `Params` object with the best parameters:" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
G0252.076731
k10.019991
k20.006450
k30.000009
\n", - "
" - ], - "text/plain": [ - "G0 252.076731\n", - "k1 0.019991\n", - "k2 0.006450\n", - "k3 0.000009\n", - "dtype: float64" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "best_params" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The second return value is a `ModSimSeries` object with information about the results." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
fvec[160.0767308095254, -104.21961159052165, -47.5...
nfev72
fjac[[-8599507368.377098, 33.25989348427639, -78.1...
ipvt[4, 2, 3, 1]
qtf[-8.018186132608008, 0.003428006849176768, -6....
cov_x[[0.2749513460215024, 9.112638841837913e-05, -...
mesgThe relative error between two consecutive ite...
ier2
\n", - "
" - ], - "text/plain": [ - "fvec [160.0767308095254, -104.21961159052165, -47.5...\n", - "nfev 72\n", - "fjac [[-8599507368.377098, 33.25989348427639, -78.1...\n", - "ipvt [4, 2, 3, 1]\n", - "qtf [-8.018186132608008, 0.003428006849176768, -6....\n", - "cov_x [[0.2749513460215024, 9.112638841837913e-05, -...\n", - "mesg The relative error between two consecutive ite...\n", - "ier 2\n", - "dtype: object" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fit_details" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
fvec[160.0767308095254, -104.21961159052165, -47.5...
nfev72
fjac[[-8599507368.377098, 33.25989348427639, -78.1...
ipvt[4, 2, 3, 1]
qtf[-8.018186132608008, 0.003428006849176768, -6....
cov_x[[0.2749513460215024, 9.112638841837913e-05, -...
mesgThe relative error between two consecutive ite...
ier2
\n", - "
" - ], - "text/plain": [ - "fvec [160.0767308095254, -104.21961159052165, -47.5...\n", - "nfev 72\n", - "fjac [[-8599507368.377098, 33.25989348427639, -78.1...\n", - "ipvt [4, 2, 3, 1]\n", - "qtf [-8.018186132608008, 0.003428006849176768, -6....\n", - "cov_x [[0.2749513460215024, 9.112638841837913e-05, -...\n", - "mesg The relative error between two consecutive ite...\n", - "ier 2\n", - "dtype: object" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fit_details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we have `best_params`, we can use it to make a `System` object and run it." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'The solver successfully reached the end of the integration interval.'" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(best_params, data)\n", - "results, details = run_ode_solver(system, slope_func, t_eval=data.index)\n", - "details.message" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the results, along with the data. The first few points of the model don't fit the data, but we don't expect them to." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap08-fig04.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results.G, label='simulation')\n", - "plot(data.glucose, 'bo', label='glucose data')\n", - "\n", - "decorate(xlabel='Time (min)',\n", - " ylabel='Concentration (mg/dL)')\n", - "\n", - "savefig('figs/chap08-fig04.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Interpreting parameters\n", - "\n", - "Based on the parameters of the model, we can estimate glucose effectiveness and insulin sensitivity." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def indices(params):\n", - " \"\"\"Compute glucose effectiveness and insulin sensitivity.\n", - " \n", - " params: sequence of G0, k1, k2, k3\n", - " data: DataFrame with `glucose` and `insulin`\n", - " \n", - " returns: State object containing S_G and S_I\n", - " \"\"\"\n", - " G0, k1, k2, k3 = params\n", - " return State(S_G=k1, S_I=k3/k2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the results." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
S_G0.019991
S_I0.001431
\n", - "
" - ], - "text/plain": [ - "S_G 0.019991\n", - "S_I 0.001431\n", - "dtype: float64" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "indices(best_params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Under the hood\n", - "\n", - "Here's the source code for `run_ode_solver` and `fit_leastsq`, if you'd like to know how they work." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[0;32mdef\u001b[0m \u001b[0mrun_ode_solver\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msystem\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mslope_func\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Computes a numerical solution to a differential equation.\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m `system` must contain `init` with initial conditions,\u001b[0m\n", - "\u001b[0;34m `t_0` with the start time, and `t_end` with the end time.\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m It can contain any other parameters required by the slope function.\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m `options` can be any legal options of `scipy.integrate.solve_ivp`\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m system: System object\u001b[0m\n", - "\u001b[0;34m slope_func: function that computes slopes\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m returns: TimeFrame\u001b[0m\n", - "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# make sure `system` contains `init`\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msystem\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'init'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mmsg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"\"\"It looks like `system` does not contain `init`\u001b[0m\n", - "\u001b[0;34m as a system variable. `init` should be a State\u001b[0m\n", - "\u001b[0;34m object that specifies the initial condition:\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# make sure `system` contains `t_end`\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msystem\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m't_end'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mmsg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"\"\"It looks like `system` does not contain `t_end`\u001b[0m\n", - "\u001b[0;34m as a system variable. `t_end` should be the\u001b[0m\n", - "\u001b[0;34m final time:\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# make the system parameters available as globals\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0munpack\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msystem\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# the default value for t_0 is 0\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mt_0\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msystem\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m't_0'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# try running the slope function with the initial conditions\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# try:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# slope_func(init, t_0, system)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# except Exception as e:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# msg = \"\"\"Before running scipy.integrate.solve_ivp, I tried\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# running the slope function you provided with the\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# initial conditions in `system` and `t=t_0` and I got\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# the following error:\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# logger.error(msg)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# raise(e)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# wrap the slope function to reverse the arguments and add `system`\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mf\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mt\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mslope_func\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mt\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msystem\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mwrap_event\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mevent\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Wrap the event functions.\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m Make events terminal by default.\u001b[0m\n", - "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mwrapped\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mt\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mevent\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mt\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msystem\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mwrapped\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mterminal\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mevent\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'terminal'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mwrapped\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdirection\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mevent\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'direction'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mwrapped\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# wrap the event functions so they take the right arguments\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mevents\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0moptions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'events'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mevents\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mwrap_event\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mevent\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mevent\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mevents\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mevents\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwrap_event\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mevents\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# remove dimensions from the initial conditions.\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# we need this because otherwise `init` gets copied into the\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# results array along with its units\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0my_0\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mmagnitude\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0minit\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# run the solver\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0munits_off\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mbunch\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msolve_ivp\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mt_0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mt_end\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my_0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mevents\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mevents\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# separate the results from the details\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbunch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'y'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mt\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbunch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m't'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mdetails\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mModSimSeries\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbunch\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# pack the results into a TimeFrame\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mresults\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mTimeFrame\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtranspose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mindex\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcolumns\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0minit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresults\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdetails\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "%psource run_ode_solver" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[0;32mdef\u001b[0m \u001b[0mfit_leastsq\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merror_func\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Find the parameters that yield the best fit for the data.\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m `params` can be a sequence, array, or Series\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m Whatever arguments are provided are passed along to `error_func`\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m error_func: function that computes a sequence of errors\u001b[0m\n", - "\u001b[0;34m params: initial guess for the best parameters\u001b[0m\n", - "\u001b[0;34m data: the data to be fit; will be passed to min_fun\u001b[0m\n", - "\u001b[0;34m options: any other arguments are passed to leastsq\u001b[0m\n", - "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# if any of the params are quantities, strip the units\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mx0\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mmagnitude\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# override `full_output` so we get a message if something goes wrong\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0moptions\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'full_output'\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# run leastsq\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0munits_off\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mbest_params\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcov_x\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minfodict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmesg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mier\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mleastsq\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merror_func\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mx0\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mx0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mdetails\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mModSimSeries\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minfodict\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mdetails\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcov_x\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcov_x\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmesg\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmesg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mier\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mier\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# if we got a Params object, we should return a Params object\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mParams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mbest_params\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mParams\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mSeries\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbest_params\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# return the best parameters and details\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mbest_params\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdetails\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "%psource fit_leastsq" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Exercise:** Since we don't expect the first few points to agree, it's probably better not to make them part of the optimization process. We can ignore them by leaving them out of the `Series` returned by `error_func`. Modify the last line of `error_func` to return `errors.loc[8:]`, which includes only the elements of the `Series` from `t=8` and up.\n", - "\n", - "Does that improve the quality of the fit? Does it change the best parameters by much?\n", - "\n", - "Note: You can read more about this use of `loc` [in the Pandas documentation](https://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-integer)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** How sensitive are the results to the starting guess for the parameters. If you try different values for the starting guess, do we get the same values for the best parameters?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Related reading:** You might be interested in this article about [people making a DIY artificial pancreas](https://www.bloomberg.com/news/features/2018-08-08/the-250-biohack-that-s-revolutionizing-life-with-diabetes)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap20soln.ipynb b/code/soln/chap20soln.ipynb deleted file mode 100644 index e7f2130e0..000000000 --- a/code/soln/chap20soln.ipynb +++ /dev/null @@ -1,1739 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 20\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Dropping pennies\n", - "\n", - "I'll start by getting the units we need from Pint." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "second" - ], - "text/latex": [ - "$second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "m = UNITS.meter\n", - "s = UNITS.second" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And defining the initial state." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
y381 meter
v0.0 meter / second
\n", - "
" - ], - "text/plain": [ - "y 381 meter\n", - "v 0.0 meter / second\n", - "dtype: object" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "init = State(y=381 * m, \n", - " v=0 * m/s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Acceleration due to gravity is about 9.8 m / s$^2$." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "9.8 meter/second2" - ], - "text/latex": [ - "$9.8 \\frac{meter}{second^{2}}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "g = 9.8 * m/s**2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When we call `odeint`, we need an array of timestamps where we want to compute the solution.\n", - "\n", - "I'll start with a duration of 10 seconds." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "10 second" - ], - "text/latex": [ - "$10 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_end = 10 * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we make a `System` object." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
inity 381 meter\n", - "v 0.0 meter / secon...
g9.8 meter / second ** 2
t_end10 second
\n", - "
" - ], - "text/plain": [ - "init y 381 meter\n", - "v 0.0 meter / secon...\n", - "g 9.8 meter / second ** 2\n", - "t_end 10 second\n", - "dtype: object" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = System(init=init, g=g, t_end=t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And define the slope function." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Compute derivatives of the state.\n", - " \n", - " state: position, velocity\n", - " t: time\n", - " system: System object containing `g`\n", - " \n", - " returns: derivatives of y and v\n", - " \"\"\"\n", - " y, v = state\n", - " unpack(system) \n", - "\n", - " dydt = v\n", - " dvdt = -g\n", - " \n", - " return dydt, dvdt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It's always a good idea to test the slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.0 meter / second\n", - "-9.8 meter / second ** 2\n" - ] - } - ], - "source": [ - "dydt, dvdt = slope_func(init, 0, system)\n", - "print(dydt)\n", - "print(dvdt)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we're ready to call `run_ode_solver`" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'The solver successfully reached the end of the integration interval.'" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results, details = run_ode_solver(system, slope_func, max_step=0.5*s)\n", - "details.message" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the results:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
yv
0.000000381.0000000.000000
0.000102381.000000-0.001000
0.001122380.999994-0.011000
0.011327380.999371-0.111000
0.113367380.937025-1.110997
0.613367379.156526-6.010997
1.113367374.926028-10.910997
1.613367368.245529-15.810997
2.113367359.115031-20.710997
2.613367347.534532-25.610997
3.113367333.504034-30.510997
3.613367317.023535-35.410997
4.113367298.093036-40.310997
4.613367276.712538-45.210997
5.113367252.882039-50.110997
5.613367226.601541-55.010997
6.113367197.871042-59.910997
6.613367166.690544-64.810997
7.113367133.060045-69.710997
7.61336796.979547-74.610997
8.11336758.449048-79.510997
8.61336717.468550-84.410997
9.113367-25.961949-89.310997
9.613367-71.842448-94.210997
10.000000-109.000000-98.000000
\n", - "
" - ], - "text/plain": [ - " y v\n", - "0.000000 381.000000 0.000000\n", - "0.000102 381.000000 -0.001000\n", - "0.001122 380.999994 -0.011000\n", - "0.011327 380.999371 -0.111000\n", - "0.113367 380.937025 -1.110997\n", - "0.613367 379.156526 -6.010997\n", - "1.113367 374.926028 -10.910997\n", - "1.613367 368.245529 -15.810997\n", - "2.113367 359.115031 -20.710997\n", - "2.613367 347.534532 -25.610997\n", - "3.113367 333.504034 -30.510997\n", - "3.613367 317.023535 -35.410997\n", - "4.113367 298.093036 -40.310997\n", - "4.613367 276.712538 -45.210997\n", - "5.113367 252.882039 -50.110997\n", - "5.613367 226.601541 -55.010997\n", - "6.113367 197.871042 -59.910997\n", - "6.613367 166.690544 -64.810997\n", - "7.113367 133.060045 -69.710997\n", - "7.613367 96.979547 -74.610997\n", - "8.113367 58.449048 -79.510997\n", - "8.613367 17.468550 -84.410997\n", - "9.113367 -25.961949 -89.310997\n", - "9.613367 -71.842448 -94.210997\n", - "10.000000 -109.000000 -98.000000" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's position as a function of time:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap09-fig01.pdf\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3Xl8m9Wd7/GPLMvxvm9x7NjOdkIIgSSQDQhJWJoABQrttJRpp9N2yqtTOrTT3t47bedO29tl2pnb9ralLd2m02WmK9BACVCyhwQCISwh5CTeEjtOHK/xEseWLd0/HtlWTBLboM3y9/166WXpnEfSzxDrp/M85/yOy+/3IyIiEmsSoh2AiIjI+ShBiYhITFKCEhGRmKQEJSIiMUkJSkREYpISlIiIxCQlKBERiUlKUCIiEpOUoEREJCYpQYmISExKjHYA42GMeRi4A1hrrd0WaFsOPAAsBGqAT1lrNwU9Jx34HnAn4AV+DnzGWjs4zvecBlwFnADG9RwREbkgNzAdeN5a2zeeJ8R8gjLGvA9IG9WWB2wCfgm8H7gdeNgYc5m19kjgsAeAK4EbgHTgV8Bp4EvjfOurgJ1v+RcQEZFg1wK7xnNgTCcoY8wM4Ms4v9DRoK57gE7gE9ZaP3DQGLMBuBf4tDEmJ3DMjdbavYHX+jzwNWPMl621vnG8/QmAX//61xQXF4fsdxIRmYpOnjzJPffcA4HP1vGI6QQF/AT4qrX2mDEmuH0ZsDWQnIZsxhktASwF/MCOUf2FQCVQPY73HgQoLi6mtLT0zUUvIiKjjfuSScxOkjDG3At4rLUPnqe7EDg1qq050D7U3zbqelNzUJ+IiMS4mBxBGWNmAv8CrLrAIa4xXuJ8/dr4SkRkEonJBAUsAYqBqlGn9jYbY34ONPHGkVABI6OqJiDXGOMOGkUNHT965CUiIjEoVhPUZmDRqLZXgQ8DTwF3AZ8c1b8OeC5w/0WcUdS1wLag/lNAbejDFRGRUIvJBGWt7QIOBLcFRlK11trjxphfA18wxnwbeBC4DViOk8Cw1rYZY/4L+K4x5kM409S/DDwwzhl8IiJh4/P5aGhooKenJ9qhhIzH46GwsJDMzMyQvWZMJqixWGtbjTE346x1+ijOQt07g9ZAAfx9oH8zIwt1vxKJ+Px+P509/fj94ApcDUt0J+BJdG4u11iX0EQknrW0tOByuTDGkJAQs3PVxs3v99Pb28vx48cBQpakJk2Csta6Rj1+Fmc6+YWO7wb+JnCLGL/fzyPbqzne3H3BYzzuBBIDySrJ48YTlLw8iQkkJrqH7ycFjklL8ZCe4iE9NYkkJTmRSa2jo4OKioq4SE4ALpeL1NRUZsyYQWNj49RLUJNFb98AjS0XH7Z7B314B330jqvYxxt5EhNIT0kiPdVJWhmpSUEJzENaiodpHreSmEiMGhwcxOPxRDuMkEtJScHr9Ybs9ZSgQiw12cPKy6ZzqK4Nn8+Pz+/Mbh8Y9OMdGMQ78NYvgXkHfLR3naW96+wFjwlOYlnp08jLTCYvK5ncrGSSk/S/XSTa4vELZKh/J31ShcESU8gSc/71wH6/n4FBH94BH/1e56d30Elc3gEfA4Gf3gEf/YGE1tc/QHfvAN29/XSf8TIwOHaSC05i9U1d5/Slp3jIy0ohN8tJWnmZKeRmTsPtjo/TDSISH5SgIszlcuFJdONJdJOaPPHn+/1++voH6e71Orcz/YGfgcfjSGJDzz16snO4LcHlIjtjGrmBkVZeVgp5WclkpiXF5Tc9EYl9SlCTjMvlInlaIsnTEsnPTjnvMX6/nz7vIN1nvHSd6ae9s4+2zl5aT5+lrfMsg743FtXw+f20dTr9VQ0j7Z7EBIrz0ijJT6OkIJ2i3FQSNdISkQhQgopDLpeL5KREkpOcJFZZMtLn8/np6O6j9bSTsJxbL509/ed9Le+Aj/qmruHThAkJLopyUpkeSFjFeam6piUiYaFPlikmIcFFbmYyuZnJzC0bafcODNLW+cbE1ds3cM7zfT4/J1p7ONHaw4v2FC6Xi7ysZEry05yklZ9OWkr8zU4SmYoefPBBnn76aX7/+98Pt7W2trJ69Wr+8Ic/cMkll4T1/ZWgBABPopui3FSKclPPae/s6edESzeNLT00Nve8Yeag3++npaOXlo5eXqlqASA7fdpwsiotSicjNSliv4eIhM4dd9zBt7/9bWpra6msrATgscceY9asWWFPTqAEJWPITEsiMy0XU54LwJmzXk609NDY0sOJlh6aO3rx+8+9ptXR3UdHdx+v17UBOKcZp2dSWZJFQU6KJl2IjLLfnmLvwZMhWYYyFk9iAssWFLP4AjONgxUVFbFy5Uo2btzI/fffD8DGjRu57bbbwh0moAQlE5Sa7GF2aTazS7MB6PcOOqf8AiOspraeN0zCGBphPf96E2nJHipKnGRVWpiuCRciwEuHmyOSnMC5rvzS4eZxJShwRlHf+c53uP/++6mpqeHgwYN8//vfD3OUDiUoeUuSPG7KizMpL3ZKmwwO+mhqP0Njcw/Hm7s53tyNLyhh9Zz18lpNK6/VtOJxJ1BalEFlSSYV0zNJTda1K5marphXENER1BXzCsZ9/I033sgXvvAFXnzxRXbs2MGKFSsoKioKY4QjlKAkpNzuBEry0ynJT+fKS4ro9w5yrKmLusbT1J3o4mz/yKQL76CP2sbT1DaexuVyUZSbSsX0TCpLMsnNTNapQJkyFpvCcY9oIi0lJYX169ezceNGdu3axX333Rex91aCkrBK8riZU5rNnNJsfD4/J1t7qD3RSW3jaTq6RooR+v1O38nWHp49cILMtCQqS7KYNzOHQl23EomqO++8kw984AN4PB5uvPHGiL2vEpRETEKCi5KCdEoK0rl6UQntXWepa+yktrGTE60950y26Ozp5+Ujzbx8pJmcjGRMeQ7zy3NI14xAkYhbunQphYWFLF26lLS0tIi9rxKURE1ORjI5JpnFppCzfQPUneykrrGTY01d9HsHh49r7zrLswdO8NxrJ5lRkM78ihxmz8jCk+iOYvQiU0d/fz/d3d3cfvvtEX1fJSiJCcnTEplfnsv88lwGB300NHdTVd9BVUPH8IVjv99Pw6kuGk51sd2dwOzSLEx5LjMK0klI0ClAkXBoaWnhl7/8JVlZWVx99dURfW8lKIk5bnfC8MzA1YtnUHP8NPZoO/WnuodPA3oHfRw62s6ho+2kp3iYNzOH+RW55Ga+iQq8InJBV199NYWFhXzjG9+I+LVgJSiJaZ5EN6bcWSjc3evl8NF2Dh1to61zpKJFd6+XF+0pXrSnKMxJZX5FDnNKszVtXSQErLVRe28lKJk00lM8LJlfyGJTQHNHL/ZoO4ePtZ9TL/BU+xlOtZ9h10uNVJZksmhuASX5aZoFKDIJKUHJpONyuSjMSaUwJ5VVi0o4drKTQ0fbqWs8PVzFwuf3U338NNXHT5OfncKiOfnMm5mjyhUSM/x+f9x9cRpd9uytUoKSSc2d4KKyJIvKkizO9g9QVd+BPdrOidae4WNaOnrZ8kI9e149wYLKPC6bk0+6Kq5LFLndbrxeL0lJ8bVsore3F48ndH9bSlASN5KTElk4O5+Fs/Np6zzLK0easUfb8QZ2F+7tG2DfoSb221PMLs3i8rkFFOWmxt23WIl92dnZNDU1MWPGDBISJv+o3u/309vby/Hjx0NaBkkJSuJSbmYya5aWseKy6RysbeNAdcvwpow+v58j9R0cqe+gKDeVRXPymVOajVun/yRC8vPzaWhoiOoEhFDzeDwUFRWRmZkZstdUgpK4lpyUyBJTyBVzC6htPM0rVS0cb+4e7m9qO8Nf9h7jmVdOsHB2Hgtn5Wn2n4RdQkICM2fOjHYYMU8JSqaEhATX8DYhze29vFLVzOFj7cOTKs6c9bL3tZPse72JuWXZLJpbQGFO6hivKiLhpAQlU05BTgrXXzWTlUGn/7p7vQAM+vzDC4DLijJYtqCY6fmRqz0mIiOUoGTKSk32cOUlRSw2hVQ3dPBKVQsng2b/1Td1Ud/URWlhBssWFFFSkB7FaEWmHiUomfLcCS7mzcxh3swcmtrO8PKRZo7Udwyv6Riq/1damM5VC4qZoUQlEhFKUCJBinJTuWl5OcsWFPPC600cPtaObzhRddNwqooZBeksu1SJSiTcNK9W5DyyM6Zxw7KZvPdt87mkIpeEoLVSx5u7eXhbFQ9traK+qSvkq+dFxBGzIyhjzBeBu4EyoBN4GviUtfZkoP8W4N+BSuAA8FFr7fNBzy8GfgjcBHQA/89a+/WI/hIy6WVnTOP6q2Zy5SVF7DvUxKG6kRFVY0s3f9rRTUl+GlctKKa0MF2LfkVCKJZHUIeAe4FLgLcDM4H/BDDGzAceAn4BLAF2A5uMMTlBz/8tkAWsAj4GfN4Y8/6IRS9xJSt9GuuunMk96+ezoDLvnBFVY0sPf9pRzUNbqzh2slMjKpEQidkRlLX2v4Me1hljvgEMtX0EeNZa+zUAY8z9wO3APcD3jDGLgNXAbGttDfCSMeZbwD/gJDWRN8VJVGXDI6rX69rwBdZSnWjtYePOGorz0lh+aTFlRRlRjlZkcovlEdQwY0wW8F7gmUDTMmDLUL+11h94vDyovy6QnIZsBq4wxsRXdUaJisy0JNYuLeN9Gy5h4ay8c3b0PdnqjKg27qym9XRvFKMUmdxiOkEZY+4xxnTjXEOqxLkmBVAInBp1eHOg/WL9biAvPNHKVJSRmsSapWW8f8MlXDY7H3dQojp2sovf/OUw2/bVc+asN4pRikxOMZ2ggI3AYuB6wAs8GGgf60q0rlRLRKWnJnHdklLet+ESFlTmDU+W8Pv9HKhp5VdPHOKF15sYCFRWF5Gxxew1KABrbRfQBRwxxligwRizAGhiZLQ0pICRUdOF+geB1vBFLFNdemoS664sY9GcfJ55pZH6pi4A+r2DPHvgBK/VtLLysunMLcvWjD+RMcT6CCrY0F/zILAXWDuqfy3wXOD+XqDCGFMZ1L8OeMla2x/WKEWA/OwUbrt2Fm+/Zha5mcnD7V1n+nnquaP8YcsRGlu6L/IKIhKTIyhjjAf4AvAIzqioDPg/wIvAEeBHwMvGmP+JcxrwXiAd+DWAtfYVY8wO4KfGmE8CFcAngfsi+ovIlOZyuSifnklZUQYHa1t57rWT9PYNAM42Hw9trWJ2aTarLptOVvq0KEcrEntidQTlx1n/tBE4jJN4qoBbrbU+a+0h4C7gA8BLwLXAzdba9qDXeDfO6cE9OAt2v2qt1RRzibiEBBcLZ+fz1xsuYen8wnMmUlQ3dPDrJw+x6+XjnO0fiGKUIrHHpUWF52eMqQBqN2/eTGlpabTDkTjS2dPPnldPcKS+/Zz25KRErlpQxMJRswFF4kFDQwPXX389QKW1tm48z4nVEZRI3MpMS+JtK8p557q5FOeN7DV1tn+AnS8d57+fOsTRk51RjFAkNihBiURJcV4ad62dw/oVFWSmjawf7+jq49GdNTz57FGtn5IpLSYnSYhMFS6Xizll2VSWZPJyVQv7Xm+izzsIwJH6do41dbLqshIWVOZqWrpMORpBicQAtzuBJaaQe9bPx8wcqXnc1z/I1n31PLS1SmWTZMpRghKJIanJHm5cXs7tq2efM/X8RGsPv/3LYfa82oh3QNUoZGpQghKJQWVFGdx9k+HKS4qGC9H6/H72HTqlSRQyZShBicSoRHcCKxZO5z03GkryR2b7dfb0axKFTAlKUCIxLjczmXesmcO6K8uYluQebj9S386vnzjEgeoWbZIocUkJSmQScLlcLKjM4563jZpE4R1k24sNmkQhcUkJSmQSCZ5Eka1JFBLnlKBEJqGyogzec4FJFL/5i+VES0+UIxR565SgRCapcydRpA+3n+7u46FtVex5tZFBbZAok5gSlMgk50yimO1MovA4kyj8gdHU77cc0bUpmbSUoETiwNAkirtvMpQWZgy3t3T08runD/PioVP4fJrpJ5OLEpRIHElPTeL21bO49ooZJLqdP+9Bn5/drzbyyPYqTnf3RTlCkfFTghKJMy6Xi8vnFvDuG+ZRmJM63N7Y0sNv/mJ5raZV66ZkUlCCEolTOZnJ3LVuLssuLSYhUAndO+Bj6756/vxMrapQSMxTghKJY+4EF8sWFPPOdXPJyUgebq870cl/PWmpauiIYnQiFzeh/aCMMWnATCAFaLbW1oclKhEJqcLcVN594zz2vHqCl480A84Ovk/sqcPMzOHaxTNITtL2cBJbxvwXaYxJB94P/DVwJeAGXIDfGNMMPA780Fq7N5yBishbk+hO4NorZlAxPZPNzx+ju9c5xWePtXO8uZvrr5pJWVHGGK8iEjkXPcVnjPkYUAfcCzwF3A5cARhgFfB5IAnYbIz5szFmVlijFZG3bKgKxfzykZp+3b1e/rSjmh37G1QqSWLGWCOotwMbrLXPX6D/OeAngVHWR4ENwAMhjE9EwiA5KZEblpVTUZLFtn0NnO0fAOCVqhaON/ewfmX5OdesRKLhognKWrt+PC9ire0G/i0kEYlIxMwpzaYkP42tL9RTe8LZBLH1tLO4d+3SMuYFVU4XiTTN4hOZ4lKTPdx8dSVrlpTiThiZjv7Uc0fZuq+eAdXzkyiZ6Cy+twHXA4WMSm7W2veHMC4RiSCXy8XC2fkU5abx5LN1dAQqTrxW00pT2xnWr6ggO2PaGK8iElrjHkEZY74MbAJuAoqBglE3EZnkCnJS+Ksb5jG3LHu4raWjl99tPsyR+vYoRiZT0URGUB8BPmCt/UW4ghGR6EvyuLlpeTklBenseuk4gz4//d5Bnnz2KI3NPVx9eclwnT+RcJrIvzIfsDtcgYhI7HC5XFw2O5+71s0lMy1puP3V6hb+uPWIis5KREwkQX0f+HC4AhGR2FOYk8q7bzTMLh055dfc3stvnz6sMkkSdhM5xfd/gMeMMS8DrwDnVJq01n4wVEEZYz4HvAuYC7QDDwGfDUxnHzpmHvAjYDlwEviitfbnQf2JwDeAvwE8wB+B+6y12gtbZAKmedysX1HOq9Vp7Hq5EV/glN8Te+pYNCefqxeV4NYpPwmDifyr+hLOQlw3MB0oG3ULpVU4yWUJ8F6ciRnfHeo0xniAPwNNwFXAl4EfGWOuC3qNfwbeg5PobgCWBb+GiIyfy+Vi0ZwC3rn23FN+r1S18NA27TMl4TGREdR9wAeDRynhYq29JfihMeafgQeD2jYAM4ArAiOiA4Hk9HFguzEmAfh74DPW2i0AxpiPA08aY/7RWqtzEyJvQmFuKn91wzy2vlBP9fHTADS1neF3mw9z/ZUzmTUjK8oRSjyZyAiqH9gVrkDGkA8EJ5VlwHOjTtdtxjndBzAr8JwtQf3bcYrcLg1jnCJxLzkpkfUrK7j28hkkBBb29vUP8vjuWna9fFxby0vITCRB/Qj4ULgCuRBjTBbwaeBnQc2FwKlRhzYH2gn6OXyMtXYQaAvqE5E3yeVycfm8Au4adcrvpcPNPLqrhrN9A1GMTuLFRE7xTQfuClSTeJk3TpL4SCgDAzDGTMOZ3FAD/GtQl2uMp47VLyIhUJSbyl9dP4/NL9RT2+ic8qtv6uL3W45w86oK8rJSohyhTGYTGUHNBl4CTgMVODPshm5zQh1YYBbeb4AM4B3W2uCvZE28cSRUwMiIqSnwc/gYY4wbyOWNIy8ReQuSpyVy86oKll1aPNx2uruPP2w5Mpy0RN6McY+grLVrwxlIsMAkh1/gJL7rgqeXB+wF/tEYk2qtPRNoW4ez/Qc4I64WYC3w80DbasAPvBjG0EWmJJfL2Vo+LzOZp58/hnfAh3fAx+O761h+aTFL5xficunEhkxMrO7x/CNgDXAzkGSMGfpq1hy4lvQE0Aj8NFAjcDlwN3AjgLXWZ4z5AfAVY8xRoAf4DvBLa60KiomEyezSbLLSp/H47lo6e/rx+/08e+AErad7WXflTDyJWi8l4zfWjrrfMcaMqxCsMeY2Y8zdoQmLD+Fc89oPnAi6lQFYa/uBWwLH7AP+N/ARa+32oNf4EvA7nGtYmwPHfTxE8YnIBeRnp/Cu6+cxoyB9uO1IfQcPbT1C15n+KEYmk81YIygXUG2MeRx4BOfU2nFrbZ8xJge4FLgOeB8wiFO14S2z1o55LsBaa3FGWRfqHwA+GbiJSASlTEvkttWz2fXScV6tbgGgucPZCHHDqgpK8tPHeAWRMUZQ1tqP41RqaMHZyv0IcMYYMxho2w7cCnwVuNxa+0J4wxWRycKd4OK6JaWsXVpGQuD6U2/fAI9sr+a1mtYoRyeTwZjXoAIjlfsClRguAyqBFJx1R/uttW3hDVFEJrNLZ+WRkzGNTXvq6O0bwOfzs3VfPa2ne7n68hnDu/iKjDaRWXx+nCKxr4QvHBGJRyUF6bzr+nk8vruWlo5ewKnj19bZx/oV5SRPi9X5WhJNmlIjIhGRmZbEXWvnnLN1R8OpLn63+TCtp3ujGJnEKiUoEYkYT6KzdcfyoEW9nT39WtQr56UEJSIR5XK5uGpBMRtWVgyvi/IO+PjzM7W8eOgUfr+KzYpDCUpEomJ2aTbvHLWl/O5XG9mxXxXRxaEEJSJRk5flLOoNXhf1anULm/bU4R3wRS8wiQkTmjpjjJmNU/OuiFHJzVr7pRDGJSJTRMq0RG5fPYunn6/nSL1Tiay28TSPbK/ilqsrSU32RDlCiZZxJyhjzF8D/wGcxakWHjwG9+OUFhIRmTC3O4Gbls8kPdXDfutsONDUdoaHtlZx6zWzyM6YFuUIJRomMoL6IvDvwOcDBVtFRELG5XJx9aISMlI97HypEb/fT0d3H3/ceoRbrq6kOC8t2iFKhE3kGlQx8GMlJxEJp0VzCli/opxEt/PxNFQeSdPQp56JJKjNwOJwBSIiMmR2aTZ3XDeb5CTnJM/AoLO31FDhWZkaJnKK75fA140xpThbvp9TN99auzuUgYnI1Facl8Zd6+bw6M6a4b2ltr/YQPeZflYsnK4NEKeAiSSo3wZ+fus8fX7A/dbDEREZkZORzDvXzeXPz9TS1OZsnr3v0Cm6z3hZd2UZbrdWysSziSSoyrBFISJyAanJHu64bjZPPXuU2hOdANhj7fScHWDDqgqmefTdOF5NpJr50XAGIiJyIZ5ENxtWVbJjfwMHAntJNZzq4qEtR3j7tbNIT00a4xVkMproQt15wGdwdtL1A68B37DWHglDbCIiwxICGyCmpybx7IETALR2nuUPgSSVl5US5Qgl1MZ9AtcYcyPwKs5Mvmdxtn9fArxqjLk+POGJiIxwuVxceUkRNyybObxLb3evlz9uraK+qSvK0UmoTWQE9VXgB9baTwQ3GmP+H/A1YFkoAxMRuZD55bmkJXvYtKeOfu8g/d5BHttVw03Ly8/Zb0omt4lMgVkI/OA87d/H2QpeRCRiyooyuHPNHNJTnFp9gz4/Tz57lENH26IcmYTKRBJUF1B2nvZyoDM04YiIjF9+dgp3rZs7XKvP5/fz9N5jvFqlBb3xYCKn+B4GfmSM+SiwM9C2GmcE9VCoAxMRGY+M1CTuXDOHjTtraOlwto7fvr8B74CPJfMLoxydvBUTGUF9CtgHbMIZTXUBfwaeB/5H6EMTERmf1GQPd6yeTVFu6nDb7lcb2fPqCe3QO4lNZB1UN/CuwJ5QCwLNr1lra8ISmYjIBCRPS+T21bN5fHctDae6Adh3qAnvwCDXXjFDpZEmoQmtgwKw1lYD1WGIRUTkLUnyuLn1mlk8saeOukDViVeqWvAO+Fi7tIyEBCWpyeSiCcoY81ngW9ba3sD9C7LWfjWkkYmIvAmJ7gQ2rKzg6eePcaS+A4DX69roH/Bx07KZqt83iYw1gvo74EGgN3D/Qvw466RERKLO7U7gxmXleBITOFjrTDuvbujgzwODbFhZiSdRSWoyuGiCstZWnu++iEisS0hwsXZpGZ5ENy8faQbg2MkuHt1Zw63XVJKkIrMxbyKljt5vjJl2nvYkY8z7QxuWiMhb53K5uObyEpYtKB5ua2zp5k87qjnbNxDFyGQ8JjJJ4j+AJ4BTo9ozAn2/CFVQAMaYO4GPAVcCmdZa16j+5cADOBUuaoBPWWs3BfWnA98D7gS8wM+Bz2jLepGpxeVysezSYhITE9j9SiMATW1neHhbFbetnk1aoBKFxJ6JnIh14VxrGq0IZ01UqKUCW4B/Hd1hjMnDWY/1DE7B2l8CDxtj5gYd9gBwFXAD8C7gbuBzYYhTRCaBJaaQtUvLhqebt3ae5eFtVXT29I/xTImWMUdQxpgtgbt+nCQQ/H/TDcwHdoU6MGvtrwLvv+Y83ffglFf6hLXWDxw0xmwA7gU+bYzJCRxzo7V2b+B1Pg98zRjzZWutL9Txikjsu3RWHp7EBJ7eewyf309Hdx8PbT3C7dfNJicjOdrhySjjGUENrXtyAXVBj6uBA8C/Ae8LU3wXsgzYGkhOQzYDywP3l+Ik1B2j+gvRzsAiU9q8mTlsWFWBO2Fku46HtlYNl0mS2DHmCMpa+3cAxpgG4N+ttT1hj2pshcD+UW3Ngfah/rZR15uag/q00FhkCqssyeLWa2bx+DO1eAd99PYN8Kcd1dxx3WxtfBhDxn0Nylr7xRhJTuCM5ibar4JcIjKsrCiD21bPHp5u3ts3wCPbq2k9rZFUrBirksRTwLustacD9y/IWntTSCO7uCZGRktDChiZYdgE5Bpj3EGjqKHjR89CFJEpanp+GrddO4uNO2vo9w4OJ6l3rJlDbqauSUXbWCOo44Av6P7FbpG0F1gzqm0d8Fzg/os4o6hrR/WfAmrDHZyITB7FeU6SGj2Sau88G+XIxBWrpeiNMbnATJx1UD8GFge6DuKsvTqCs/bqQeA24IvAZdbaI4Hn/yLwnA8BacCvgAettV8a5/tXALWbN2+mtLQ0RL+ViMSqEy09bNxZjXfA+U6emuzhHWs0uy9UGhoauP766wEqrbV143nOmy5IZYxxGWMuNcZkvNnXGMNtOBMhfhx4vD9wK7HWtgI344yQXgI+ANw5lJwC/h5nJLUZ+CPwW+ArYYpVRCa56flpvP3aWcN1+s6c9fLItmo6uvqiHNnUNe4RlDHm34GII/vYAAAUHUlEQVTXrbU/Nca4cD741+As0l1vrd0TtiijQCMokampsbmbR3fW4B10RlLpKR7uuG7O8Lby8uaEewT1LuC1wP23AYuAlTin2VTJXETiQklBOrdeOwtPYFuO7l4vj2yv4nS3RlKRNpEEVQQ0BO6vB35vrX0O+A5wRagDExGJlhkF6dxyTSWJQUnq4W1KUpE2kQTVDgyVBF4HbAvcd+GUPBIRiRulhRnccvW5SeqR7dWq3RdBE0lQTwA/Nsb8DKdc0JOB9ktxSiCJiMSVsiInSQ2VReo6088j21VgNlImkqD+AdgJ5AJ3WWs7Au1Lgd+FOjARkVhQVpTBzUFJqrPHSVLdZ5Skwm3c+0FZa7twktTo9s+HNCIRkRhTXpzJzasqeXx3LYM+P509/Ty0rYo718whPTUp2uHFrYlsWIgxJhF4D85pPT9ONfPfWWu1NaWIxLXy6ZlsCCQpXyBJPbK9mjvWzCFdmx6GxUS2fJ8NvA78CGcW383AT4DXjDGzwhOeiEjsqJieyYaVFSQETvd1dPfxyLYqunu9UY4sPk3kGtS3gHqgwlq72Fp7Bc5kicZAn4hI3KssyXKSlCsoSW2v4sxZJalQm0iCWgt8ylo7XA3cWtsEfDrQJyIyJVSWZPG2FeUjSaqrj0d31tDnHRzjmTIRE63Fd766SNo+XUSmnNml2dy0ohxXIEk1d/Ty5121DAzqIzFUJpKgdgD/ZozJGWoIVBz/BudurS4iMiXMKc1m7dKRWp2NLd08saeOQV9s7hIx2UxkFt8ngb8A9caYgzijqUuBFuDGMMQmIhLzFlTm0dc/yDOvNAJQd6KTzc8f48ZlM4dHV/LmTGTL98OAAe7HWbC7C2dd1PxR21yIiEwpi00hS+cXDT8+fKydHfuPE6v77U0W4x5BGWOmAW5r7U/DGI+IyKS0YmExff0DHKhpBeDV6hamJblZsXB6lCObvMYcQRljco0xjwLdQKcx5pnAXkkiIhLgcrlYvbiUuWXDl+l54fUmXjp86iLPkosZzym+rwDLgX8B/gfOths/DGdQIiKTUUKCixuuKqO8OHO4bdfLjbxe2xbFqCav8SSoDcCHrLVftdZ+E2cr9huMMartISIyitudwPqVFZTkpw23bdlXT3VDx0WeJeczngQ1A9g39MBaexDoB3RiVUTkPDyJCdx8dSUF2SkA+P1+nnruKPVNXVGObHIZT4JyA6NreAyiTQpFRC4oOSmRt187i+z0aQAM+vw8vruWk609UY5s8hjvLL7fG2OCNz9JBn5hjOkdarDW3hTSyEREJrnUZA+3rZ7NQ1uP0N3rxTvg49FdNdy5Zg55WSnRDi/mjWcE9Z9ALXA86PYroGpUm4iIjJKZlsRtq2eTMs0ZD/T1D7JxRw2nu/uiHFnsG3MEZa3920gEIiISr3Izk3n7NbN4ZEc1/d5Bes56+dOOau5aO5c07SV1QRMtFisiIm9CYW4qt4zaOn7jjmrO9mm/1wtRghIRiZAZBemsD9pLqrXzLI89U4t3QNt0nI8SlIhIBFWWZLHuqrLhxydbe3hiz1F8qoD+BkpQIiIRNr88l9WLZww/Pnqyk+37G1RcdhQlKBGRKFg0p+CcCuiv1bSy75Dq9gVTghIRiZIVC4sxM0eKyz574AT2qOr2DZnIhoWTkjHmn4CPA9nAU8BHrLX6miIiUedyuVh3ZRk9Z700nOoGYPML9aQmeygryohydNEX1yMoY8zfAp8FPgaswklS/x3VoEREggwVl83LTAbA5/OzaU8drad7L/7EKSCuExTOyOmb1tqHrbUvAR8E1hljFkY5LhGRYUN1+9IDi3b7vYM8urOG7jP9YzwzvsVtggrsAHw5sGWozVpbA9Th7G8lIhIz0lOTuPWaWSR5nDrc3b1eHnumln7v1F0jFbcJCsjD+f1GX29qBgojH46IyMXlZ6ewfkX58ELelo5eNu2pY3CKrpGK5wTlinYAIiITNbM4k7VLRxby1jd1sfWF+im5RiqeE1QL4OONo6UC3jiqEhGJGZdU5rLs0uLhx4eOtvH8waYoRhQdcZugrLV9wMvA2qE2Y0wlUAE8F6WwRETG5apLilhQmTv8eO/BkxysbY1iRJEX7+ugvgd82xizH2dyxLeArdbaA1GNSkRkDC6Xi+uWlNHd6+XYSWer+G37GkhL8VBenBnl6CIjbkdQANbanwFfA34I7AG6gLujGpSIyDi5E1ysX1FBQbaz+67P7+eJPXU0t0+NNVLxPoLCWvs1nCQlIjLpJHnc3HLNLP645QhdZ/rxDvh4bFcN77x+LhmpSdEOL6ziegQlIhIP0lM83HpNJdMCa6R6znp5dGcNZ/vje7NDJSgRkUkgLyuFm6+uJCGwI29b51k27a5jcNAX5cjCRwlKRGSSmFGQzg1XzRx+fLy5m81xvEZKCUpEZBKZNzOHlZdNH358+Fg7L7wen2uklKBERCaZJaaQhbPyhh8/99pJqho6ohhReChBiYhMMi6Xi2sXl1JaOLJn1NN7j3Gq/UwUowo9JSgRkUnIWSNVTnb6NAAGBn08/kwtPb3eKEcWOkpQIiKTVPK0RG65emT6eXevl8d31zIQJzP7lKBERCaxnMxk3raiHFdgi46mtjNsiZOZfUpQIiKT3MziTK65vGT48eFj7ew7NPk3bVCCEhGJA4vm5HNp0My+Zw+coHqSz+xTghIRiQMul4vVi0uZUZA+3Pb03mOTurCsEpSISJxwJ7jYsLKCrMDMPu+gjz8/U8OZs5NzZp8SlIhIHBma2Zd0zsy+ukk5s08JSkQkzuRmJvO25SMz+0629rBt3+Sb2acEJSISh8qnZ3L1opGafYeOtrPfNkcxoolTghIRiVOXzy1gQWXu8OM9B05Q23g6ihFNjBKUiEiccrlcXLe4lJJ8Z2af3+/nqeeO0tIxOWb2KUGJiMQxtzuBDasqyExztof3Dvh4fHftpJjZpwQlIhLnUgIz+zyJzkd+Z0//pNiNVwlKRGQKyMtK4aagmX0nWnvY9mJDTM/sU4ISEZkiKkuyztmN9/W6Nl4+Ersz+5SgRESmkMXzCrikYmRm3+5XTtBwqiuKEV2YEpSIyBTicrlYs6SU4rw0AHx+P08+e5SuM/1RjuyNlKBERKYYtzuB9SsrSE32ANDbN8CmGCyHpAQlIjIFpad4WL+ynITApIlT7WfYHmOTJpSgRESmqJL8dK65YmSjw9fr2jhQ3RrFiM6lBCUiMoVdNjuf+eU5w493vnScEy09UYxohBKUiMgU5nK5WLO0jIKcFMCZNLFpTx3dvdGvNKEEJSIyxSW6E9iwspKUaYkAnDnr5Yk90a80kRjVd78AY8ydwMeAK4FMa63rPMcsBx4AFgI1wKestZuC+tOB7wF3Al7g58BnrLWDYf8FREQmmcy0JG5aXs7GnTX4/X5Otvaw86XjrFlaFrWYYnUElQpsAf71fJ3GmDxgE/AMsAT4JfCwMWZu0GEPAFcBNwDvAu4GPhfGmEVEJrWyooxzKk0cqGnlYG30Jk3EZIKy1v7KWvsVYM8FDrkH6AQ+Ya09aK39GrAXuBfAGJMTOOY+a+1ea+0W4PPAx4wxMfk7i4jEgsXzCphblj38ePuLDTS1nYlKLJP1w3oZsNVaGzxhfzOwPHB/KeAHdozqLwQqIxKhiMgk5HK5WHdlGXlZzqSJQZ+fTVHanmOyJqhC4NSotuZA+1B/26jrTc1BfSIicgGeRDc3r6pgWpIbgO5eL08+exSfL7KLeCM6ScIY83Pgby5yyH9aaz8wjpd6w6SJcfTHzvJoEZEYl5U+jZuWlfPYM7X4/X6ON3ez+9VGrrl8RsRiiPQI6n5g+kVu94/zdZp440iogJFRVROQa4xxB/UPHT965CUiIudRPj2T5ZcWDz9+6XAz9U2Rq3we0RGUtfY0cDoEL7UX+OSotnXAc4H7L+KMoq4FtgX1nwJqQ/D+IiJTwtL5hTS1naG20fnoPtHSQ1lRRkTeO1bXQeUCM4E5gcdXBLoOWmv7gV8DXzDGfBt4ELgNZ4LEhwGstW3GmP8CvmuM+RCQBnwZeMBaG1vlekVEYpjL5eLGZTN55pVGzvYNsGBWXsTeOyYTFE7C+Y+gx/sDPyuBOmttqzHmZpy1Th/FWah7p7X2SNBz/j7Qv5mRhbpfCXPcIiJxJ8njZm0UFuzGZIKy1v4cJ6Fc7JhncaaTX6i/G2dCxsUmZYiISIyarNPMRUQkzilBiYhITFKCEhGRmKQEJSIiMUkJSkREYlJMzuKLEW6AkydPRjsOEZFJL+iz1H2x44IpQV3YdIB77rkn2nGIiMST6UD1eA5Ugrqw53FKJZ0AtAuviMhb48ZJTs+P9wkuv19FvkVEJPZokoSIiMQkJSgREYlJSlAiIhKTlKBERCQmKUGJiEhMUoISEZGYpAQlIiIxSQlKRERikhKUiIjEJJU6CgNjzD8BHweygaeAj1hrT0U3qvAxxnwOeBcwF2gHHgI+a63tjmpgEWaMeRi4A1hrrd0W5XDCzhizBPg3YCXQB/zFWvtX0Y0qvIwx2cD/BW4B0oFXgP9lrd0R1cBCyBhzJ/Ax4Eog01rrGtW/HHgAWAjUAJ+y1m4KRywaQYWYMeZvgc/i/A9ehZOk/juqQYXfKuAbwBLgvcBNwHejGlGEGWPeB6RFO45IMcZcAmwBdgBX4fwb+E1Ug4qMbwJLgduBy4G9wGPGmKyoRhVaqTj/b/91dIcxJg/YBDyD8/f+S+BhY8zccASiEVTofRz4prX2YQBjzAeBamPMQmvtgeiGFh7W2luCHxpj/hl4MFrxRJoxZgbwZZziwkejHE6kfBn4g7X2i0Ftr0crmAhaDvzAWvscQODf+v04Zw9eiGZgoWKt/RWAMWbNebrvATqBT1hr/cBBY8wG4F7g06GORSOoEDLGTMP5VrVlqM1aWwPU4fzDnirygY5oBxFBPwG+aq09Fu1AIsEY4wbWA0eNMduMMSeNMU8ZYxZGO7YI2AO8wxiTF/jv8EGgATgY3bAiZhmwNZCchmwmTJ9vSlChlYfz33T09aZmoDDy4URe4FTHp4GfRTuWSDDG3At4rLVTZsQIFOCcBvoMzunrm3E+pJ82xmREM7AI+DhwGmjBue72v4BbrLVnohpV5BQSwc83JajQco19SPwKjCD/iHPh9A3nr+ONMWYm8C/Ah6MdS4QNfW78wVr7oLX2RZxTPAnArdELKyI+AVQAN+Bce/sNsNEYkxPNoCIoop9xSlCh1QL4eOO3iQLe+K0jrhhjEnH+WDOAd1hrB6IcUiQsAYqBKmPMgDFm6HfebIz5aRTjCrcWnE087VCDtdaL88WkLFpBhZsxJgX4InCftXaztXa/tfaTQD/wnuhGFzFNRPDzTQkqhKy1fcDLwNqhNmNMJc43rueiFFbYGWMSgF8Ac4ANU2h6+WZgEXBF0A2cEdX/jlZQ4Wat7Qf24/z/Boa/oFQA8XwdzhO4jd5h28fU+SzdC6wZ1baOMH2+aUfdEAvM2vs28D6cyRHfArDWrotiWGFljPkJznWIm4GTQV3N1trRf8xxzRjjZwqsgzLGvBf4KU4yfh74B+AdgInnLyjGmF04W5ffD7ThTJL4R2CRtfZwNGMLFWNMLjATZx3Uj4HFga6DOGdIjuB8IX0QuA1nVHmZtfZIqGOZKlk/Yqy1PwO+BvwQZ8ZPF3B3VIMKvw8B03G+VZ8IusXt6Z6pzlr7X8A/4Vxr3AdcAtwYz8kp4N04XzwfBV7CuRZ1R7wkp4DbcP6Wfxx4vD9wK7HWtuJ8Eb0W5/f/AHBnOJITaAQlIiIxSiMoERGJSUpQIiISk5SgREQkJilBiYhITFKCEhGRmKQEJSIiMUkJSiQKAlXAfxLlGL5rjPneOI/NCFQtvzzccYkM0X5QIiEUqCRxMUettRXAnUDU6hUaYwzwtwSVK7oYa22XMeabOLvJ3hDO2ESGaAQlElrTg263B9qWBbVdBWCtbbPWdkYlQsc/AI9ba0+OeeSInwPXTZF9nyQGaAQlEkLBH/jGmLbA3ebRicAYsw2ostZ+OOhxNU6JqI8AScADwD8Dnwc+hvOF8kfW2s8FvU5ioP9vcBJgNfCdi+1PFSjuezdw36j2a4Cv4xTABac6+WestU8GfrdTxpjdwF/j7IMkElYaQYnEjnfiVMu+BqcA6WeBx4B0nNpnnwY+G9hie8hPcE4X3otTD+9LwNeNMR+6yPtcBuTgVKYGhnfJ3YhTlXpJ4PYFYPRGfM8RVK1fJJw0ghKJHbXW2v8ZuH/YGPMpoMxae3NQ2z8C1wObAlu5vB9YYK09NPQagetLH8epNn4+lYGfx4PaMnGS1sagwp/nKwDaAMya6C8m8mYoQYnEjpdHPT7JuduXDLUNbRh3Jc4Opy84OWlYIm/csyhYSuBn31CDtbY9MKvwSWPMFmA78LC11o567tmg54uElU7xicQO76jH/gu0Df3dDv1cxbmbJi5k5DrS+TQHfp6zTbm19u+ApcBfgOuAA8aYe0c9Nzfo+SJhpRGUyOS1L/BzprX2sQk8bz9OorsU2BHcYa09ABwAvmmM+SHOhI3gCReXAS+86YhFJkAJSmSSstZWGWN+BvzYGPMZnA0y03BGQQXW2q9f4Hmtxpi9OKOkHQDGmDnA3+FsxFcPlOBMzHhx6HnGGBewGmdmoUjY6RSfyOT2EeBbwOdwtuTejDPlvGaM5/0AeF/Q4x5gLvAb4DDwR2A3505FX4Mzo/B3IYhbZEzaUVdkCjLGeIBXgH+y1j4yzuc8Dmy/0MhMJNQ0ghKZgqy1XpyRVtp4jjfGZOCcQvx2OOMSCaYRlIiIxCSNoEREJCYpQYmISExSghIRkZikBCUiIjFJCUpERGKSEpSIiMSk/w/tiQ+WftI/igAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_position(results):\n", - " plot(results.y, label='y')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Position (m)')\n", - "\n", - "plot_position(results)\n", - "savefig('figs/chap09-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Onto the sidewalk\n", - "\n", - "To figure out when the penny hit the sidewalk, we can use `crossings`, which finds the times where a `Series` passes through a given value." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([8.81788535])" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_crossings = crossings(results.y, 0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For this example there should be just one crossing, the time when the penny hits the sidewalk." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "8.817885349720552 second" - ], - "text/latex": [ - "$8.817885349720552 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_sidewalk = t_crossings[0] * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can compare that to the exact result. Without air resistance, we have\n", - "\n", - "$v = -g t$\n", - "\n", - "and\n", - "\n", - "$y = 381 - g t^2 / 2$\n", - "\n", - "Setting $y=0$ and solving for $t$ yields\n", - "\n", - "$t = \\sqrt{\\frac{2 y_{init}}{g}}$" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "8.817885349720552 second" - ], - "text/latex": [ - "$8.817885349720552 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "sqrt(2 * init.y / g)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The estimate is accurate to about 10 decimal places." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Events\n", - "\n", - "Instead of running the simulation until the penny goes through the sidewalk, it would be better to detect the point where the penny hits the sidewalk and stop. `run_ode_solver` provides exactly the tool we need, **event functions**.\n", - "\n", - "Here's an event function that returns the height of the penny above the sidewalk:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def event_func(state, t, system):\n", - " \"\"\"Return the height of the penny above the sidewalk.\n", - " \"\"\"\n", - " y, v = state\n", - " return y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's how we pass it to `run_ode_solver`. The solver should run until the event function returns 0, and then terminate." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[[8.817885349720566]]
nfev38
njev0
nlu0
status1
messageA termination event occurred.
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events [[8.817885349720566]]\n", - "nfev 38\n", - "njev 0\n", - "nlu 0\n", - "status 1\n", - "message A termination event occurred.\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results, details = run_ode_solver(system, slope_func, events=event_func)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The message from the solver indicates the solver stopped because the event we wanted to detect happened.\n", - "\n", - "Here are the results:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
yv
0.0000003.810000e+020.000000
0.0001023.810000e+02-0.001000
0.0011223.810000e+02-0.011000
0.0113273.809994e+02-0.111000
0.1133673.809370e+02-1.110997
1.1337733.747013e+02-11.110971
8.8178855.684342e-14-86.415276
\n", - "
" - ], - "text/plain": [ - " y v\n", - "0.000000 3.810000e+02 0.000000\n", - "0.000102 3.810000e+02 -0.001000\n", - "0.001122 3.810000e+02 -0.011000\n", - "0.011327 3.809994e+02 -0.111000\n", - "0.113367 3.809370e+02 -1.110997\n", - "1.133773 3.747013e+02 -11.110971\n", - "8.817885 5.684342e-14 -86.415276" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With the `events` option, the solver returns the actual time steps it computed, which are not necessarily equally spaced. \n", - "\n", - "The last time step is when the event occurred:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "8.817885349720566 second" - ], - "text/latex": [ - "$8.817885349720566 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_sidewalk = get_last_label(results) * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Unfortunately, `run_ode_solver` does not carry the units through the computation, so we have to put them back at the end.\n", - "\n", - "We could also get the time of the event from `details`, but it's a minor nuisance because it comes packed in an array:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "8.817885349720566 second" - ], - "text/latex": [ - "$8.817885349720566 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "details.t_events[0][0] * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result is accurate to about 15 decimal places.\n", - "\n", - "We can also check the velocity of the penny when it hits the sidewalk:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "-86.41527642726155 meter/second" - ], - "text/latex": [ - "$-86.41527642726155 \\frac{meter}{second}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v_sidewalk = get_last_value(results.v) * m / s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And convert to kilometers per hour." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "-311.0949951381416 kilometer/hour" - ], - "text/latex": [ - "$-311.0949951381416 \\frac{kilometer}{hour}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "km = UNITS.kilometer\n", - "h = UNITS.hour\n", - "v_sidewalk.to(km / h)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If there were no air resistance, the penny would hit the sidewalk (or someone's head) at more than 300 km/h.\n", - "\n", - "So it's a good thing there is air resistance." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Under the hood\n", - "\n", - "Here is the source code for `crossings` so you can see what's happening under the hood:" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[0;32mdef\u001b[0m \u001b[0mcrossings\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mseries\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Find the labels where the series passes through value.\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m The labels in series must be increasing numerical values.\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m series: Series\u001b[0m\n", - "\u001b[0;34m value: number\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m returns: sequence of labels\u001b[0m\n", - "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0minterp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mInterpolatedUnivariateSpline\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mseries\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mseries\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0minterp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mroots\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "%psource crossings" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The [documentation of InterpolatedUnivariateSpline is here](https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.InterpolatedUnivariateSpline.html).\n", - "\n", - "And you can read the [documentation of `scipy.integrate.solve_ivp`](https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.solve_ivp.html) to learn more about how `run_ode_solver` works." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises\n", - "\n", - "**Exercise:** Here's a question from the web site [Ask an Astronomer](http://curious.astro.cornell.edu/about-us/39-our-solar-system/the-earth/other-catastrophes/57-how-long-would-it-take-the-earth-to-fall-into-the-sun-intermediate):\n", - "\n", - "\"If the Earth suddenly stopped orbiting the Sun, I know eventually it would be pulled in by the Sun's gravity and hit it. How long would it take the Earth to hit the Sun? I imagine it would go slowly at first and then pick up speed.\"\n", - "\n", - "Use `run_ode_solver` to answer this question.\n", - "\n", - "Here are some suggestions about how to proceed:\n", - "\n", - "1. Look up the Law of Universal Gravitation and any constants you need. I suggest you work entirely in SI units: meters, kilograms, and Newtons.\n", - "\n", - "2. When the distance between the Earth and the Sun gets small, this system behaves badly, so you should use an event function to stop when the surface of Earth reaches the surface of the Sun.\n", - "\n", - "3. Express your answer in days, and plot the results as millions of kilometers versus days.\n", - "\n", - "If you read the reply by Dave Rothstein, you will see other ways to solve the problem, and a good discussion of the modeling decisions behind them.\n", - "\n", - "You might also be interested to know that [it's actually not that easy to get to the Sun](https://www.theatlantic.com/science/archive/2018/08/parker-solar-probe-launch-nasa/567197/)." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "astronomical_unit" - ], - "text/latex": [ - "$astronomical_unit$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "N = UNITS.newton\n", - "kg = UNITS.kilogram\n", - "m = UNITS.meter\n", - "AU = UNITS.astronomical_unit" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
r149597870691.0 meter
v0.0 meter / second
\n", - "
" - ], - "text/plain": [ - "r 149597870691.0 meter\n", - "v 0.0 meter / second\n", - "dtype: object" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "r_0 = (1 * AU).to_base_units()\n", - "v_0 = 0 * m / s\n", - "init = State(r=r_0,\n", - " v=v_0)" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
initr 149597870691.0 meter\n", - "v 0.0 meter / s...
G6.674e-11 meter ** 2 * newton / kilogram ** 2
m11.989e+30 kilogram
r_final701879000.0 meter
m25.972e+24 kilogram
t_00 second
t_end10000000.0 second
\n", - "
" - ], - "text/plain": [ - "init r 149597870691.0 meter\n", - "v 0.0 meter / s...\n", - "G 6.674e-11 meter ** 2 * newton / kilogram ** 2\n", - "m1 1.989e+30 kilogram\n", - "r_final 701879000.0 meter\n", - "m2 5.972e+24 kilogram\n", - "t_0 0 second\n", - "t_end 10000000.0 second\n", - "dtype: object" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "r_earth = 6.371e6 * m\n", - "r_sun = 695.508e6 * m\n", - "\n", - "system = System(init=init,\n", - " G=6.674e-11 * N / kg**2 * m**2,\n", - " m1=1.989e30 * kg,\n", - " r_final=r_sun + r_earth,\n", - " m2=5.972e24 * kg,\n", - " t_0=0 * s,\n", - " t_end=1e7 * s)" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def universal_gravitation(state, system):\n", - " \"\"\"Computes gravitational force.\n", - " \n", - " state: State object with distance r\n", - " system: System object with m1, m2, and G\n", - " \"\"\"\n", - " r, v = state\n", - " unpack(system)\n", - " \n", - " force = G * m1 * m2 / r**2\n", - " return force" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "3.5423376937972604e+22 newton" - ], - "text/latex": [ - "$3.5423376937972604e+22 newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "universal_gravitation(init, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def slope_func(state, t, system):\n", - " \"\"\"Compute derivatives of the state.\n", - " \n", - " state: position, velocity\n", - " t: time\n", - " system: System object containing `g`\n", - " \n", - " returns: derivatives of y and v\n", - " \"\"\"\n", - " y, v = state\n", - " unpack(system) \n", - "\n", - " force = universal_gravitation(state, system)\n", - " dydt = v\n", - " dvdt = -force / m2\n", - " \n", - " return dydt, dvdt" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " )" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "slope_func(init, 0, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def event_func(state, t, system):\n", - " r, v = state\n", - " return r - system.r_final" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "148895991691.0 meter" - ], - "text/latex": [ - "$148895991691.0 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "event_func(init, 0, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[[5577323.2573304195]]
nfev230
njev0
nlu0
status1
messageA termination event occurred.
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events [[5577323.2573304195]]\n", - "nfev 230\n", - "njev 0\n", - "nlu 0\n", - "status 1\n", - "message A termination event occurred.\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "results, details = run_ode_solver(system, slope_func, events=event_func)\n", - "details" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[5577323.25733042] second" - ], - "text/latex": [ - "$[5577323.25733042] second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "t_event = details.t_events[0] * s" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[64.55235252] day" - ], - "text/latex": [ - "$[64.55235252] day$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "t_event.to(UNITS.day)" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "ts = linspace(t_0, t_event, 201)\n", - "results, details = run_ode_solver(system, slope_func, events=event_func, t_eval=ts)" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "results.index /= 60 * 60 * 24" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "results.r /= 1e9" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot(results.r, label='r')\n", - "\n", - "decorate(xlabel='Time (day)',\n", - " ylabel='Distance from sun (million km)')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap21soln.ipynb b/code/soln/chap21soln.ipynb deleted file mode 100644 index 022a1ea53..000000000 --- a/code/soln/chap21soln.ipynb +++ /dev/null @@ -1,2780 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 21\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### With air resistance" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next we'll add air resistance using the [drag equation](https://en.wikipedia.org/wiki/Drag_equation)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "I'll start by getting the units we'll need from Pint." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "kilogram" - ], - "text/latex": [ - "$kilogram$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now I'll create a `Params` object to contain the quantities we need. Using a Params object is convenient for grouping the system parameters in a way that's easy to read (and double-check)." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
height381 meter
v_init0.0 meter / second
g9.8 meter / second ** 2
mass0.0025 kilogram
diameter0.019 meter
rho1.2 kilogram / meter ** 3
v_term18.0 meter / second
\n", - "
" - ], - "text/plain": [ - "height 381 meter\n", - "v_init 0.0 meter / second\n", - "g 9.8 meter / second ** 2\n", - "mass 0.0025 kilogram\n", - "diameter 0.019 meter\n", - "rho 1.2 kilogram / meter ** 3\n", - "v_term 18.0 meter / second\n", - "dtype: object" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(height = 381 * m,\n", - " v_init = 0 * m / s,\n", - " g = 9.8 * m/s**2,\n", - " mass = 2.5e-3 * kg,\n", - " diameter = 19e-3 * m,\n", - " rho = 1.2 * kg/m**3,\n", - " v_term = 18 * m / s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can pass the `Params` object `make_system` which computes some additional parameters and defines `init`.\n", - "\n", - "`make_system` uses the given radius to compute `area` and the given `v_term` to compute the drag coefficient `C_d`." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Makes a System object for the given conditions.\n", - " \n", - " params: Params object\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " area = np.pi * (diameter/2)**2\n", - " C_d = 2 * mass * g / (rho * area * v_term**2)\n", - " init = State(y=height, v=v_init)\n", - " t_end = 30 * s\n", - " \n", - " return System(params, area=area, C_d=C_d, \n", - " init=init, t_end=t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's make a `System`" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
height381 meter
v_init0.0 meter / second
g9.8 meter / second ** 2
mass0.0025 kilogram
diameter0.019 meter
rho1.2 kilogram / meter ** 3
v_term18.0 meter / second
area0.0002835287369864788 meter ** 2
C_d0.4445009981135434 dimensionless
inity 381 meter\n", - "v 0.0 meter / secon...
t_end30 second
\n", - "
" - ], - "text/plain": [ - "height 381 meter\n", - "v_init 0.0 meter / second\n", - "g 9.8 meter / second ** 2\n", - "mass 0.0025 kilogram\n", - "diameter 0.019 meter\n", - "rho 1.2 kilogram / meter ** 3\n", - "v_term 18.0 meter / second\n", - "area 0.0002835287369864788 meter ** 2\n", - "C_d 0.4445009981135434 dimensionless\n", - "init y 381 meter\n", - "v 0.0 meter / secon...\n", - "t_end 30 second\n", - "dtype: object" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the slope function, including acceleration due to gravity and drag." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Compute derivatives of the state.\n", - " \n", - " state: position, velocity\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: derivatives of y and v\n", - " \"\"\"\n", - " y, v = state\n", - " unpack(system)\n", - " \n", - " f_drag = rho * v**2 * C_d * area / 2\n", - " a_drag = f_drag / mass\n", - " \n", - " dydt = v\n", - " dvdt = -g + a_drag\n", - " \n", - " return dydt, dvdt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As always, let's test the slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(, )" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use the same event function as in the previous chapter." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func(state, t, system):\n", - " \"\"\"Return the height of the penny above the sidewalk.\n", - " \"\"\"\n", - " y, v = state\n", - " return y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And then run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'A termination event occurred.'" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results, details = run_ode_solver(system, slope_func, events=event_func, max_step=0.5*s)\n", - "details.message" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the results." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
yv
0.0000003.810000e+020.000000
0.0001023.810000e+02-0.001000
0.0011223.810000e+02-0.011000
0.0113273.809994e+02-0.110998
0.1133673.809371e+02-1.109588
0.6133673.791898e+02-5.797088
1.1133673.752652e+02-9.745614
1.6133673.696115e+02-12.701005
2.1133673.627205e+02-14.723219
2.6133673.550087e+02-16.023394
3.1133673.467797e+02-16.826181
3.6133673.382354e+02-17.309535
4.1133673.295027e+02-17.596161
4.6133673.206587e+02-17.764595
5.1133673.117496e+02-17.863049
5.6133673.028025e+02-17.920417
6.1133672.938332e+02-17.953785
6.6133672.848510e+02-17.973173
7.1133672.758613e+02-17.984431
7.6133672.668673e+02-17.990966
8.1133672.578708e+02-17.994758
8.6133672.488728e+02-17.996958
9.1133672.398740e+02-17.998235
9.6133672.308747e+02-17.998976
10.1133672.218751e+02-17.999406
10.6133672.128753e+02-17.999655
11.1133672.038754e+02-17.999800
11.6133671.948755e+02-17.999884
12.1133671.858756e+02-17.999933
12.6133671.768756e+02-17.999961
13.1133671.678756e+02-17.999977
13.6133671.588756e+02-17.999987
14.1133671.498756e+02-17.999992
14.6133671.408756e+02-17.999996
15.1133671.318756e+02-17.999997
15.6133671.228756e+02-17.999999
16.1133671.138756e+02-17.999999
16.6133671.048756e+02-17.999999
17.1133679.587563e+01-18.000000
17.6133678.687563e+01-18.000000
18.1133677.787563e+01-18.000000
18.6133676.887563e+01-18.000000
19.1133675.987563e+01-18.000000
19.6133675.087563e+01-18.000000
20.1133674.187563e+01-18.000000
20.6133673.287563e+01-18.000000
21.1133672.387563e+01-18.000000
21.6133671.487563e+01-18.000000
22.1133675.875630e+00-18.000000
22.4397911.421085e-14-18.000000
\n", - "
" - ], - "text/plain": [ - " y v\n", - "0.000000 3.810000e+02 0.000000\n", - "0.000102 3.810000e+02 -0.001000\n", - "0.001122 3.810000e+02 -0.011000\n", - "0.011327 3.809994e+02 -0.110998\n", - "0.113367 3.809371e+02 -1.109588\n", - "0.613367 3.791898e+02 -5.797088\n", - "1.113367 3.752652e+02 -9.745614\n", - "1.613367 3.696115e+02 -12.701005\n", - "2.113367 3.627205e+02 -14.723219\n", - "2.613367 3.550087e+02 -16.023394\n", - "3.113367 3.467797e+02 -16.826181\n", - "3.613367 3.382354e+02 -17.309535\n", - "4.113367 3.295027e+02 -17.596161\n", - "4.613367 3.206587e+02 -17.764595\n", - "5.113367 3.117496e+02 -17.863049\n", - "5.613367 3.028025e+02 -17.920417\n", - "6.113367 2.938332e+02 -17.953785\n", - "6.613367 2.848510e+02 -17.973173\n", - "7.113367 2.758613e+02 -17.984431\n", - "7.613367 2.668673e+02 -17.990966\n", - "8.113367 2.578708e+02 -17.994758\n", - "8.613367 2.488728e+02 -17.996958\n", - "9.113367 2.398740e+02 -17.998235\n", - "9.613367 2.308747e+02 -17.998976\n", - "10.113367 2.218751e+02 -17.999406\n", - "10.613367 2.128753e+02 -17.999655\n", - "11.113367 2.038754e+02 -17.999800\n", - "11.613367 1.948755e+02 -17.999884\n", - "12.113367 1.858756e+02 -17.999933\n", - "12.613367 1.768756e+02 -17.999961\n", - "13.113367 1.678756e+02 -17.999977\n", - "13.613367 1.588756e+02 -17.999987\n", - "14.113367 1.498756e+02 -17.999992\n", - "14.613367 1.408756e+02 -17.999996\n", - "15.113367 1.318756e+02 -17.999997\n", - "15.613367 1.228756e+02 -17.999999\n", - "16.113367 1.138756e+02 -17.999999\n", - "16.613367 1.048756e+02 -17.999999\n", - "17.113367 9.587563e+01 -18.000000\n", - "17.613367 8.687563e+01 -18.000000\n", - "18.113367 7.787563e+01 -18.000000\n", - "18.613367 6.887563e+01 -18.000000\n", - "19.113367 5.987563e+01 -18.000000\n", - "19.613367 5.087563e+01 -18.000000\n", - "20.113367 4.187563e+01 -18.000000\n", - "20.613367 3.287563e+01 -18.000000\n", - "21.113367 2.387563e+01 -18.000000\n", - "21.613367 1.487563e+01 -18.000000\n", - "22.113367 5.875630e+00 -18.000000\n", - "22.439791 1.421085e-14 -18.000000" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The final height is close to 0, as expected.\n", - "\n", - "Interestingly, the final velocity is not exactly terminal velocity, which suggests that there are some numerical errors.\n", - "\n", - "We can get the flight time from `results`." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "22.4397909658943" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_sidewalk = get_last_label(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the plot of position as a function of time." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap09-fig02.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_position(results):\n", - " plot(results.y)\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Position (m)')\n", - " \n", - "plot_position(results)\n", - "savefig('figs/chap09-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And velocity as a function of time:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_velocity(results):\n", - " plot(results.v, color='C1', label='v')\n", - " \n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Velocity (m/s)')\n", - " \n", - "plot_velocity(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "From an initial velocity of 0, the penny accelerates downward until it reaches terminal velocity; after that, velocity is constant." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Run the simulation with an initial velocity, downward, that exceeds the penny's terminal velocity. Hint: You can create a new `Params` object based on an existing one, like this:\n", - "\n", - "`params = Params(params, v_init = -30 * m / s)`\n", - "\n", - "What do you expect to happen? Plot velocity and position as a function of time, and see if they are consistent with your prediction." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'A termination event occurred.'" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "params = Params(params, v_init = -30 * m / s)\n", - "system = make_system(params)\n", - "results, details = run_ode_solver(system, slope_func, events=event_func, max_step=0.5*s)\n", - "details.message" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_position(results)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot_velocity(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Suppose we drop a quarter from the Empire State Building and find that its flight time is 19.1 seconds. Use this measurement to estimate the terminal velocity.\n", - "\n", - "1. You can get the relevant dimensions of a quarter from https://en.wikipedia.org/wiki/Quarter_(United_States_coin).\n", - "\n", - "2. Create a `Params` object with the system parameters. We don't know `v_term`, so we'll start with the inital guess `v_term = 18 * m / s`.\n", - "\n", - "3. Use `make_system` to create a `System` object.\n", - "\n", - "4. Call `run_ode_solver` to simulate the system. How does the flight time of the simulation compare to the measurement?\n", - "\n", - "5. Try a few different values of `t_term` and see if you can get the simulated flight time close to 19.1 seconds.\n", - "\n", - "6. Optionally, write an error function and use `fsolve` to improve your estimate.\n", - "\n", - "7. Use your best estimate of `v_term` to compute `C_d`.\n", - "\n", - "Note: I fabricated the observed flight time, so don't take the results of this exercise too seriously." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
height381 meter
v_init-30.0 meter / second
g9.8 meter / second ** 2
mass0.00567 kilogram
diameter0.02426 meter
rho1.2 kilogram / meter ** 3
v_term18.0 meter / second
flight_time19.1 second
\n", - "
" - ], - "text/plain": [ - "height 381 meter\n", - "v_init -30.0 meter / second\n", - "g 9.8 meter / second ** 2\n", - "mass 0.00567 kilogram\n", - "diameter 0.02426 meter\n", - "rho 1.2 kilogram / meter ** 3\n", - "v_term 18.0 meter / second\n", - "flight_time 19.1 second\n", - "dtype: object" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "# Here's a `Params` object with the dimensions of a quarter,\n", - "# the observed flight time and our initial guess for `v_term`\n", - "\n", - "params = Params(params,\n", - " mass = 5.67e-3 * kg,\n", - " diameter = 24.26e-3 * m,\n", - " v_term = 18 * m / s,\n", - " flight_time = 19.1 * s)" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
height381 meter
v_init-30.0 meter / second
g9.8 meter / second ** 2
mass0.00567 kilogram
diameter0.02426 meter
rho1.2 kilogram / meter ** 3
v_term18.0 meter / second
flight_time19.1 second
area0.000462244204111976 meter ** 2
C_d0.6183600157463346 dimensionless
inity 381 meter\n", - "v -30.0 meter / s...
t_end30 second
\n", - "
" - ], - "text/plain": [ - "height 381 meter\n", - "v_init -30.0 meter / second\n", - "g 9.8 meter / second ** 2\n", - "mass 0.00567 kilogram\n", - "diameter 0.02426 meter\n", - "rho 1.2 kilogram / meter ** 3\n", - "v_term 18.0 meter / second\n", - "flight_time 19.1 second\n", - "area 0.000462244204111976 meter ** 2\n", - "C_d 0.6183600157463346 dimensionless\n", - "init y 381 meter\n", - "v -30.0 meter / s...\n", - "t_end 30 second\n", - "dtype: object" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "# Now we can make a `System` object\n", - "\n", - "system = make_system(params)" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[[20.63532743663445]]
nfev74
njev0
nlu0
status1
messageA termination event occurred.
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events [[20.63532743663445]]\n", - "nfev 74\n", - "njev 0\n", - "nlu 0\n", - "status 1\n", - "message A termination event occurred.\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "# And run the simulation\n", - "\n", - "results, details = run_ode_solver(system, slope_func, events=event_func)\n", - "details" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "20.63532743663445 second" - ], - "text/latex": [ - "$20.63532743663445 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "# And get the flight time\n", - "\n", - "flight_time = get_last_label(results) * s" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "# The flight time is a little long, so we could increase `v_term` and try again.\n", - "\n", - "# Or we could write an error function\n", - "\n", - "def error_func(v_term, params):\n", - " \"\"\"Final height as a function of C_d.\n", - " \n", - " C_d: drag coefficient\n", - " params: Params object\n", - " \n", - " returns: height in m\n", - " \"\"\"\n", - " params = Params(params, v_term=v_term)\n", - " system = make_system(params)\n", - " results, details = run_ode_solver(system, slope_func, events=event_func)\n", - " flight_time = get_last_label(results) * s\n", - " return flight_time - params.flight_time" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "1.53532743663445 second" - ], - "text/latex": [ - "$1.53532743663445 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "# We can test the error function like this\n", - "\n", - "guess = 18 * m / s\n", - "error_func(guess, params)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "19.459701937716446 meter/second" - ], - "text/latex": [ - "$19.459701937716446 \\frac{meter}{second}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "# Now we can use `fsolve` to find the value of `v_term` that yields the measured flight time.\n", - "\n", - "solution = fsolve(error_func, guess, params)\n", - "v_term_solution = solution[0] * m/s" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.5290711032106973 dimensionless" - ], - "text/latex": [ - "$0.5290711032106973 dimensionless$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "# Plugging in the estimated value, we can use `make_system` to compute `C_d`\n", - "\n", - "params = Params(params, v_term=v_term_solution)\n", - "system = make_system(params)\n", - "system.C_d" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Bungee jumping" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Suppose you want to set the world record for the highest \"bungee dunk\", [as shown in this video](https://www.youtube.com/watch?v=UBf7WC19lpw). Since the record is 70 m, let's design a jump for 80 m.\n", - "\n", - "We'll make the following modeling assumptions:\n", - "\n", - "1. Initially the bungee cord hangs from a crane with the attachment point 80 m above a cup of tea.\n", - "\n", - "2. Until the cord is fully extended, it applies no force to the jumper. It turns out this might not be a good assumption; we will revisit it.\n", - "\n", - "3. After the cord is fully extended, it obeys [Hooke's Law](https://en.wikipedia.org/wiki/Hooke%27s_law); that is, it applies a force to the jumper proportional to the extension of the cord beyond its resting length.\n", - "\n", - "4. The jumper is subject to drag force proportional to the square of their velocity, in the opposite of their direction of motion.\n", - "\n", - "Our objective is to choose the length of the cord, `L`, and its spring constant, `k`, so that the jumper falls all the way to the tea cup, but no farther! \n", - "\n", - "First I'll create a `Param` object to contain the quantities we'll need:\n", - "\n", - "1. Let's assume that the jumper's mass is 75 kg.\n", - "\n", - "2. With a terminal velocity of 60 m/s.\n", - "\n", - "3. The length of the bungee cord is `L = 40 m`.\n", - "\n", - "4. The spring constant of the cord is `k = 20 N / m` when the cord is stretched, and 0 when it's compressed.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "newton" - ], - "text/latex": [ - "$newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram\n", - "N = UNITS.newton" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
y_attach80 meter
v_init0.0 meter / second
g9.8 meter / second ** 2
mass75 kilogram
area1 meter ** 2
rho1.2 kilogram / meter ** 3
v_term60.0 meter / second
L25 meter
k40.0 newton / meter
\n", - "
" - ], - "text/plain": [ - "y_attach 80 meter\n", - "v_init 0.0 meter / second\n", - "g 9.8 meter / second ** 2\n", - "mass 75 kilogram\n", - "area 1 meter ** 2\n", - "rho 1.2 kilogram / meter ** 3\n", - "v_term 60.0 meter / second\n", - "L 25 meter\n", - "k 40.0 newton / meter\n", - "dtype: object" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(y_attach = 80 * m,\n", - " v_init = 0 * m / s,\n", - " g = 9.8 * m/s**2,\n", - " mass = 75 * kg,\n", - " area = 1 * m**2,\n", - " rho = 1.2 * kg/m**3,\n", - " v_term = 60 * m / s,\n", - " L = 25 * m,\n", - " k = 40 * N / m)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now here's a version of `make_system` that takes a `Params` object as a parameter.\n", - "\n", - "`make_system` uses the given value of `v_term` to compute the drag coefficient `C_d`." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Makes a System object for the given params.\n", - " \n", - " params: Params object\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " C_d = 2 * mass * g / (rho * area * v_term**2)\n", - " init = State(y=y_attach, v=v_init)\n", - " t_end = 20 * s\n", - "\n", - " return System(params, C_d=C_d, \n", - " init=init, t_end=t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's make a `System`" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
y_attach80 meter
v_init0.0 meter / second
g9.8 meter / second ** 2
mass75 kilogram
area1 meter ** 2
rho1.2 kilogram / meter ** 3
v_term60.0 meter / second
L25 meter
k40.0 newton / meter
C_d0.3402777777777778 dimensionless
inity 80 meter\n", - "v 0.0 meter / secon...
t_end20 second
\n", - "
" - ], - "text/plain": [ - "y_attach 80 meter\n", - "v_init 0.0 meter / second\n", - "g 9.8 meter / second ** 2\n", - "mass 75 kilogram\n", - "area 1 meter ** 2\n", - "rho 1.2 kilogram / meter ** 3\n", - "v_term 60.0 meter / second\n", - "L 25 meter\n", - "k 40.0 newton / meter\n", - "C_d 0.3402777777777778 dimensionless\n", - "init y 80 meter\n", - "v 0.0 meter / secon...\n", - "t_end 20 second\n", - "dtype: object" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`spring_force` computes the force of the cord on the jumper:" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def spring_force(y, system):\n", - " \"\"\"Computes the force of the bungee cord on the jumper:\n", - " \n", - " y: height of the jumper\n", - " \n", - " Uses these variables from system|\n", - " y_attach: height of the attachment point\n", - " L: resting length of the cord\n", - " k: spring constant of the cord\n", - " \n", - " returns: force in N\n", - " \"\"\"\n", - " unpack(system)\n", - " distance_fallen = y_attach - y\n", - " if distance_fallen <= L:\n", - " return 0 * N\n", - " \n", - " extension = distance_fallen - L\n", - " f_spring = k * extension\n", - " return f_spring" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The spring force is 0 until the cord is fully extended. When it is extended 1 m, the spring force is 40 N. " - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0 newton" - ], - "text/latex": [ - "$0 newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "spring_force(80*m, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0 newton" - ], - "text/latex": [ - "$0 newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "spring_force(55*m, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "40.0 newton" - ], - "text/latex": [ - "$40.0 newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "spring_force(54*m, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`drag_force` computes drag as a function of velocity:" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "def drag_force(v, system):\n", - " \"\"\"Computes drag force in the opposite direction of `v`.\n", - " \n", - " v: velocity\n", - " system: System object\n", - "\n", - " returns: drag force\n", - " \"\"\"\n", - " unpack(system)\n", - " f_drag = -np.sign(v) * rho * v**2 * C_d * area / 2\n", - " return f_drag" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the drag force at 60 meters per second." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "735.0 kilogram meter/second2" - ], - "text/latex": [ - "$735.0 \\frac{kilogram \\cdot meter}{second^{2}}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v = -60 * m/s\n", - "f_drag = drag_force(v, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Acceleration due to drag at 60 m/s is approximately g, which confirms that 60 m/s is terminal velocity." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "9.8 meter/second2" - ], - "text/latex": [ - "$9.8 \\frac{meter}{second^{2}}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a_drag = f_drag / system.mass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now here's the slope function:" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Compute derivatives of the state.\n", - " \n", - " state: position, velocity\n", - " t: time\n", - " system: System object containing g, rho,\n", - " C_d, area, and mass\n", - " \n", - " returns: derivatives of y and v\n", - " \"\"\"\n", - " y, v = state\n", - " unpack(system)\n", - " \n", - " a_drag = drag_force(v, system) / mass\n", - " a_spring = spring_force(y, system) / mass\n", - " dvdt = -g + a_drag + a_spring\n", - " \n", - " return v, dvdt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As always, let's test the slope function with the initial params." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(, )" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And then run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[]
nfev428
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events []\n", - "nfev 428\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results, details = run_ode_solver(system, slope_func, max_step=0.3*s)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the plot of position as a function of time." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_position(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After reaching the lowest point, the jumper springs back almost to almost 70 m, and oscillates several times. That looks like more osciallation that we expect from an actual jump, which suggests that there some dissipation of energy in the real world that is not captured in our model. To improve the model, that might be a good thing to investigate.\n", - "\n", - "But since we are primarily interested in the initial descent, the model might be good enough for now.\n", - "\n", - "We can use `min` to find the lowest point:" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "5.3987270041625965" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "min(results.y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "At the lowest point, the jumper is still too high, so we'll need to increase `L` or decrease `k`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's velocity as a function of time:" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_velocity(results)" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap09-fig03.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "subplot(1, 2, 1)\n", - "plot_position(results)\n", - "\n", - "subplot(1, 2, 2)\n", - "plot_velocity(results)\n", - "\n", - "savefig('figs/chap09-fig03.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Although we compute acceleration inside the slope function, we don't get acceleration as a result from `run_ode_solver`.\n", - "\n", - "We can approximate it by computing the numerical derivative of `ys`:" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "a = gradient(results.v)\n", - "plot(a)\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Acceleration (m/$s^2$)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we can compute the maximum acceleration the jumper experiences:" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "16.51379192670322 meter/second2" - ], - "text/latex": [ - "$16.51379192670322 \\frac{meter}{second^{2}}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "max_acceleration = max(a) * m/s**2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Relative to the acceleration of gravity, the jumper \"pulls\" about \"1.7 g's\"." - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "1.6850808088472673 dimensionless" - ], - "text/latex": [ - "$1.6850808088472673 dimensionless$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 45, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "max_acceleration / g" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Under the hood\n", - "\n", - "The gradient function in `modsim.py` adapts the NumPy function of the same name so it works with `Series` objects.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[0;32mdef\u001b[0m \u001b[0mgradient\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mseries\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Computes the numerical derivative of a series.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0ma\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgradient\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mseries\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mseries\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mTimeSeries\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mseries\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "%psource gradient" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Solving for length\n", - "\n", - "Assuming that `k` is fixed, let's find the length `L` that makes the minimum altitude of the jumper exactly 0." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The metric we are interested in is the lowest point of the first oscillation. For both efficiency and accuracy, it is better to stop the simulation when we reach this point, rather than run past it and the compute the minimum.\n", - "\n", - "Here's an event function that stops the simulation when velocity is 0." - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func(state, t, system):\n", - " \"\"\"Return velocity.\n", - " \"\"\"\n", - " y, v = state\n", - " return v" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As usual, we should test it with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.0 meter/second" - ], - "text/latex": [ - "$0.0 \\frac{meter}{second}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 48, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "event_func(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we see that we have a problem. Since the event function returns 0 under the initial conditions, the simulation would stop immediately. We can solve that problem by specifying the direction of the event function:" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": {}, - "outputs": [], - "source": [ - "event_func.direction = +1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When direction is positive, it only stops the simulation if the velocity is 0 and increasing, which is what we want." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can test it an confirm that it stops at the bottom of the jump." - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "results, details = run_ode_solver(system, slope_func, events=event_func, max_step=0.3*s)\n", - "plot_position(results)" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "5.3898136492074435" - ] - }, - "execution_count": 51, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "min(results.y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Write an error function that takes `L` and `params` as arguments, simulates a bungee jump, and returns the lowest point.\n", - "\n", - "Test the error function with a guess of 25 m and confirm that the return value is about 5 meters.\n", - "\n", - "Use `fsolve` with your error function to find the value of `L` that yields a perfect bungee dunk.\n", - "\n", - "Run a simulation with the result from `fsolve` and confirm that it works.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def error_func(L, params):\n", - " \"\"\"Minimum height as a function of length.\n", - " \n", - " L: length in m\n", - " params: Params object\n", - " \n", - " returns: height in m\n", - " \"\"\"\n", - " params = Params(params, L=L)\n", - " system = make_system(params)\n", - "\n", - " results, details = run_ode_solver(system, slope_func, events=event_func)\n", - " # plot_position(results)\n", - " min_height = get_last_value(results.y)\n", - " return min_height" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 192 ms, sys: 4 ms, total: 196 ms\n", - "Wall time: 194 ms\n" - ] - }, - { - "data": { - "text/plain": [ - "5.175384394959764" - ] - }, - "execution_count": 53, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "guess = 25 * m\n", - "%time error_func(guess, params)" - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([28.72414596])" - ] - }, - "execution_count": 54, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "solution = fsolve(error_func, guess, params)" - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "-8.613249002920043e-14 meter" - ], - "text/latex": [ - "$-8.613249002920043e-14 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 55, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "L = solution[0] * m\n", - "params = Params(params, L=L)\n", - "system = make_system(params)\n", - "\n", - "results, details = run_ode_solver(system, slope_func, events=event_func)\n", - "plot_position(results)\n", - "min_height = get_last_value(results.y) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Optional exercise:** Search for the combination of length and spring constant that yields minimum height 0 while minimizing peak acceleration." - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def sweep_k(ks, params):\n", - " for k in ks:\n", - " guess = 25 * m\n", - " params = Params(params, k=k)\n", - " solution = fsolve(error_func, guess, params)\n", - " L = solution[0] * m\n", - " params = Params(params, L=L)\n", - " system = make_system(params)\n", - " results, details = run_ode_solver(system, slope_func, max_step=0.3*s)\n", - " a = gradient(results.v)\n", - " g_max = max(a) * m/s**2 / g\n", - " print(k, L, g_max)" - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "20.0 newton / meter 6.352990374690812 meter 0.9998064320155012 dimensionless\n", - "24.0 newton / meter 13.180953422406455 meter 1.177336817793974 dimensionless\n", - "28.0 newton / meter 18.455086554496408 meter 1.3365853841835644 dimensionless\n", - "32.0 newton / meter 22.533355245033626 meter 1.490103634668971 dimensionless\n", - "36.0 newton / meter 25.926930287896166 meter 1.6232439622424328 dimensionless\n", - "40.0 newton / meter 28.724145955383136 meter 1.7615136049084956 dimensionless\n", - "44.0 newton / meter 31.400654112514374 meter 1.8930296739199899 dimensionless\n", - "48.0 newton / meter 33.59456931139941 meter 2.0115350545146273 dimensionless\n", - "52.0 newton / meter 35.31522982941949 meter 2.11670192317478 dimensionless\n", - "56.0 newton / meter 37.10367893145362 meter 2.2244874391614267 dimensionless\n", - "60.0 newton / meter 38.74613046240288 meter 2.3406778605267418 dimensionless\n" - ] - } - ], - "source": [ - "# Solution\n", - "\n", - "ks = np.linspace(20, 60, 11) * N / m\n", - "sweep_k(ks, params)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap22soln.ipynb b/code/soln/chap22soln.ipynb deleted file mode 100644 index 8329fd193..000000000 --- a/code/soln/chap22soln.ipynb +++ /dev/null @@ -1,2557 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 22\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Vectors" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `Vector` object represents a vector quantity. In the context of mechanics, vector quantities include position, velocity, acceleration, and force, all of which might be in 2D or 3D.\n", - "\n", - "You can define a `Vector` object without units, but if it represents a physical quantity, you will often want to attach units to it.\n", - "\n", - "I'll start by grabbing the units we'll need." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "kilogram" - ], - "text/latex": [ - "$kilogram$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a two dimensional `Vector` in meters." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "[3. 4.] meter" - ], - "text/latex": [ - "$[3. 4.] meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A = Vector(3, 4) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can access the elements by name." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "3.0 meter" - ], - "text/latex": [ - "$3.0 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A.x" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "4.0 meter" - ], - "text/latex": [ - "$4.0 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A.y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The magnitude is the length of the vector." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "5.0 meter" - ], - "text/latex": [ - "$5.0 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A.mag" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The angle is the number of radians between the vector and the positive x axis." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.9272952180016122 radian" - ], - "text/latex": [ - "$0.9272952180016122 radian$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A.angle" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we make another `Vector` with the same units," - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "[1. 2.] meter" - ], - "text/latex": [ - "$[1. 2.] meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "B = Vector(1, 2) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can add `Vector` objects like this" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[4. 6.] meter" - ], - "text/latex": [ - "$[4. 6.] meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A + B" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And subtract like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[2. 2.] meter" - ], - "text/latex": [ - "$[2. 2.] meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A - B" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can compute the Euclidean distance between two Vectors." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "2.8284271247461903 meter" - ], - "text/latex": [ - "$2.8284271247461903 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A.dist(B)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And the difference in angle" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "-0.17985349979247822 radian" - ], - "text/latex": [ - "$-0.17985349979247822 radian$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A.diff_angle(B)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we are given the magnitude and angle of a vector, what we have is the representation of the vector in polar coordinates." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "0.9272952180016122 radian" - ], - "text/latex": [ - "$0.9272952180016122 radian$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "mag = A.mag\n", - "angle = A.angle" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use `pol2cart` to convert from polar to Cartesian coordinates, and then use the Cartesian coordinates to make a `Vector` object.\n", - "\n", - "In this example, the `Vector` we get should have the same components as `A`." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[3. 4.] meter" - ], - "text/latex": [ - "$[3. 4.] meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x, y = pol2cart(angle, mag)\n", - "Vector(x, y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another way to represent the direction of `A` is a unit vector, which is a vector with magnitude 1 that points in the same direction as `A`. You can compute a unit vector by dividing a vector by its magnitude:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[0.6 0.8] dimensionless" - ], - "text/latex": [ - "$[0.6 0.8] dimensionless$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A / A.mag" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Or by using the `hat` function, so named because unit vectors are conventionally decorated with a hat, like this: $\\hat{A}$:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[0.6 0.8] dimensionless" - ], - "text/latex": [ - "$[0.6 0.8] dimensionless$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A.hat()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Create a `Vector` named `a_grav` that represents acceleration due to gravity, with x component 0 and y component $-9.8$ meters / second$^2$." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[ 0. -9.8] meter/second2" - ], - "text/latex": [ - "$[ 0. -9.8] \\frac{meter}{second^{2}}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "a_grav = Vector(0, -9.8) * m / s**2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Degrees and radians\n", - "\n", - "Pint provides units to represent degree and radians." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "radian" - ], - "text/latex": [ - "$radian$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "degree = UNITS.degree\n", - "radian = UNITS.radian" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you have an angle in degrees," - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "45 degree" - ], - "text/latex": [ - "$45 degree$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "angle = 45 * degree\n", - "angle" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can convert to radians." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.7853981633974483 radian" - ], - "text/latex": [ - "$0.7853981633974483 radian$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "angle_rad = angle.to(radian)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If it's already in radians, `to` does the right thing." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.7853981633974483 radian" - ], - "text/latex": [ - "$0.7853981633974483 radian$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "angle_rad.to(radian)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also convert from radians to degrees." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "45.0 degree" - ], - "text/latex": [ - "$45.0 degree$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "angle_rad.to(degree)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As an alterative, you can use `np.deg2rad`, which works with Pint quantities, but it also works with simple numbers and NumPy arrays:" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.7853981633974483 radian" - ], - "text/latex": [ - "$0.7853981633974483 radian$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.deg2rad(angle)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Create a `Vector` named `a_force` that represents acceleration due to a force of 0.5 Newton applied to an object with mass 0.3 kilograms, in a direction 45 degrees up from the positive x-axis.\n", - "\n", - "Add `a_force` to `a_grav` from the previous exercise. If that addition succeeds, that means that the units are compatible. Confirm that the total acceleration seems to make sense." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[1.1785113 1.1785113] newton/kilogram" - ], - "text/latex": [ - "$[1.1785113 1.1785113] \\frac{newton}{kilogram}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "N = UNITS.newton\n", - "mag = 0.5 * N\n", - "angle = 45 * degree\n", - "theta = angle.to(radian)\n", - "x, y = pol2cart(theta, mag)\n", - "force = Vector(x, y)\n", - "\n", - "mass = 0.3 * kg\n", - "a_force = force / mass\n", - "a_force" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[ 1.1785113 -8.6214887] newton/kilogram" - ], - "text/latex": [ - "$[ 1.1785113 -8.6214887] \\frac{newton}{kilogram}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "a_force + a_grav" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Baseball" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a `Params` object that contains parameters for the flight of a baseball." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
x0 meter
y1 meter
g9.8 meter / second ** 2
mass0.145 kilogram
diameter0.073 meter
rho1.2 kilogram / meter ** 3
C_d0.33
angle45 degree
velocity40.0 meter / second
t_end10 second
\n", - "
" - ], - "text/plain": [ - "x 0 meter\n", - "y 1 meter\n", - "g 9.8 meter / second ** 2\n", - "mass 0.145 kilogram\n", - "diameter 0.073 meter\n", - "rho 1.2 kilogram / meter ** 3\n", - "C_d 0.33\n", - "angle 45 degree\n", - "velocity 40.0 meter / second\n", - "t_end 10 second\n", - "dtype: object" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(x = 0 * m, \n", - " y = 1 * m,\n", - " g = 9.8 * m/s**2,\n", - " mass = 145e-3 * kg,\n", - " diameter = 73e-3 * m,\n", - " rho = 1.2 * kg/m**3,\n", - " C_d = 0.33,\n", - " angle = 45 * degree,\n", - " velocity = 40 * m / s,\n", - " t_end = 10 * s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the function that uses the `Params` object to make a `System` object." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Make a system object.\n", - " \n", - " params: Params object with angle, velocity, x, y,\n", - " diameter, duration, g, mass, rho, and C_d\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " # convert angle to degrees\n", - " theta = np.deg2rad(angle)\n", - " \n", - " # compute x and y components of velocity\n", - " vx, vy = pol2cart(theta, velocity)\n", - " \n", - " # make the initial state\n", - " init = State(x=x, y=y, vx=vx, vy=vy)\n", - " \n", - " # compute area from diameter\n", - " area = np.pi * (diameter/2)**2\n", - " \n", - " return System(params, init=init, area=area)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we use it:" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
x0 meter
y1 meter
g9.8 meter / second ** 2
mass0.145 kilogram
diameter0.073 meter
rho1.2 kilogram / meter ** 3
C_d0.33
angle45 degree
velocity40.0 meter / second
t_end10 second
initx 0 meter\n", - "y ...
area0.004185386812745002 meter ** 2
\n", - "
" - ], - "text/plain": [ - "x 0 meter\n", - "y 1 meter\n", - "g 9.8 meter / second ** 2\n", - "mass 0.145 kilogram\n", - "diameter 0.073 meter\n", - "rho 1.2 kilogram / meter ** 3\n", - "C_d 0.33\n", - "angle 45 degree\n", - "velocity 40.0 meter / second\n", - "t_end 10 second\n", - "init x 0 meter\n", - "y ...\n", - "area 0.004185386812745002 meter ** 2\n", - "dtype: object" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a function that computes drag force using vectors:" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "def drag_force(v, system):\n", - " \"\"\"Computes drag force in the opposite direction of `v`.\n", - " \n", - " v: velocity Vector\n", - " system: System object with rho, C_d, area\n", - " \n", - " returns: Vector drag force\n", - " \"\"\"\n", - " unpack(system)\n", - " mag = -rho * v.mag**2 * C_d * area / 2\n", - " direction = v.hat()\n", - " f_drag = direction * mag\n", - " return f_drag" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can test it like this." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[-0.11719681 -0.11719681] kilogram meter/second2" - ], - "text/latex": [ - "$[-0.11719681 -0.11719681] \\frac{kilogram \\cdot meter}{second^{2}}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v_test = Vector(10, 10) * m/s\n", - "drag_force(v_test, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the slope function that computes acceleration due to gravity and drag." - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes derivatives of the state variables.\n", - " \n", - " state: State (x, y, x velocity, y velocity)\n", - " t: time\n", - " system: System object with g, rho, C_d, area, mass\n", - " \n", - " returns: sequence (vx, vy, ax, ay)\n", - " \"\"\"\n", - " x, y, vx, vy = state\n", - " unpack(system)\n", - "\n", - " v = Vector(vx, vy) \n", - " a_drag = drag_force(v, system) / mass\n", - " a_grav = Vector(0, -g)\n", - " \n", - " a = a_grav + a_drag\n", - " \n", - " return vx, vy, a.x, a.y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Always test the slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " ,\n", - " ,\n", - " )" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use an event function to stop the simulation when the ball hits the ground:" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func(state, t, system):\n", - " \"\"\"Stop when the y coordinate is 0.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: y coordinate\n", - " \"\"\"\n", - " x, y, vx, vy = state\n", - " return y" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "1 meter" - ], - "text/latex": [ - "$1 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "event_func(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can call `run_ode_solver`" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[[5.00486551596594]]
nfev176
njev0
nlu0
status1
messageA termination event occurred.
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events [[5.00486551596594]]\n", - "nfev 176\n", - "njev 0\n", - "nlu 0\n", - "status 1\n", - "message A termination event occurred.\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results, details = run_ode_solver(system, slope_func, events=event_func, max_step=0.2*s)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The final label tells us the flight time." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "5.00486551596594 second" - ], - "text/latex": [ - "$5.00486551596594 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "flight_time = get_last_label(results) * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The final value of `x` tells us the how far the ball landed from home plate:" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "99.31065562230891 meter" - ], - "text/latex": [ - "$99.31065562230891 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x_dist = get_last_value(results.x) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Visualizing the results\n", - "\n", - "The simplest way to visualize the results is to plot x and y as functions of time." - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap10-fig01.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results.x, label='x')\n", - "plot(results.y, label='y')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Position (m)')\n", - "\n", - "savefig('figs/chap10-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can plot the velocities the same way." - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results.vx, label='vx')\n", - "plot(results.vy, label='vy')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Velocity (m/s)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The x velocity slows down due to drag.\n", - "\n", - "The y velocity drops quickly while drag and gravity are in the same direction, then more slowly after the ball starts to fall.\n", - "\n", - "Another way to visualize the results is to plot y versus x. The result is the trajectory of the ball through its plane of motion." - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap10-fig02.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_trajectory(results):\n", - " plot(results.x, results.y, label='trajectory')\n", - "\n", - " decorate(xlabel='x position (m)',\n", - " ylabel='y position (m)')\n", - "\n", - "plot_trajectory(results)\n", - "savefig('figs/chap10-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Under the hood\n", - "\n", - "`Vector` is a function that returns a `ModSimVector` object." - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "modsim.ModSimVector" - ] - }, - "execution_count": 46, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v = Vector(3, 4)\n", - "type(v)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `ModSimVector` is a specialized kind of Pint `Quantity`." - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 47, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "isinstance(v, Quantity)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There's one gotcha you might run into with Vectors and Quantities. If you multiply a `Vector` and a `Quantity`, you get a `Vector`:" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[3. 4.] meter" - ], - "text/latex": [ - "$[3. 4.] meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 48, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v1 = v * m" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "modsim.ModSimVector" - ] - }, - "execution_count": 49, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(v1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But if you multiply a `Quantity` and a `Vector`, you get a `Quantity`:" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[3. 4.] meter" - ], - "text/latex": [ - "$[3. 4.] meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 50, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v2 = m * v" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "pint.unit.build_quantity_class..Quantity" - ] - }, - "execution_count": 51, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(v2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With a `Vector` you can get the coordinates using dot notation, as well as `mag`, `mag2`, and `angle`:" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " ,\n", - " ,\n", - " )" - ] - }, - "execution_count": 52, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v1.x, v1.y, v1.mag, v1.angle" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With a `Quantity`, you can't. But you can use indexing to get the coordinates:" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(, )" - ] - }, - "execution_count": 53, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v2[0], v2[1]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And you can use vector functions to get the magnitude and angle." - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(, )" - ] - }, - "execution_count": 54, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "vector_mag(v2), vector_angle(v2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And often you can avoid the whole issue by doing the multiplication with the `Vector` on the left." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Run the simulation for a few different launch angles and visualize the results. Are they consistent with your expectations?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** The baseball stadium in Denver, Colorado is 1,580 meters above sea level, where the density of air is about 1.0 kg / meter$^3$. How much farther would a ball hit with the same velocity and launch angle travel?\n", - "\n", - "Hint: create a new `Params` object like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
x0 meter
y1 meter
g9.8 meter / second ** 2
mass0.145 kilogram
diameter0.073 meter
rho1.0 kilogram / meter ** 3
C_d0.33
angle45 degree
velocity40.0 meter / second
t_end10 second
\n", - "
" - ], - "text/plain": [ - "x 0 meter\n", - "y 1 meter\n", - "g 9.8 meter / second ** 2\n", - "mass 0.145 kilogram\n", - "diameter 0.073 meter\n", - "rho 1.0 kilogram / meter ** 3\n", - "C_d 0.33\n", - "angle 45 degree\n", - "velocity 40.0 meter / second\n", - "t_end 10 second\n", - "dtype: object" - ] - }, - "execution_count": 55, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params2 = Params(params, rho=1*kg/m**3)" - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "105.78838005859801 meter" - ], - "text/latex": [ - "$105.78838005859801 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 56, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "system2 = make_system(params2)\n", - "results2, details2 = run_ode_solver(system2, slope_func, events=event_func)\n", - "x_dist2 = get_last_value(results2.x) * m" - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "6.477724436289094 meter" - ], - "text/latex": [ - "$6.477724436289094 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 57, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "x_dist2 - x_dist" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** The model so far is based on the assumption that coefficient of drag does not depend on velocity, but in reality it does. The following figure, from Adair, [*The Physics of Baseball*](https://books.google.com/books/about/The_Physics_of_Baseball.html?id=4xE4Ngpk_2EC), shows coefficient of drag as a function of velocity.\n", - "\n", - "\n", - "\n", - "\n", - "I used [an online graph digitizer](https://automeris.io/WebPlotDigitizer/) to extract the data and save it in a CSV file. Here's how we can read it:" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Velocity in mphDrag coefficient
0.0261460.0584860.49965
8.87150919.8450000.49878
17.64735139.4760000.49704
22.43291450.1810000.48225
26.88230360.1340000.45004
30.63699268.5330000.40914
32.97769473.7690000.38042
34.60447277.4080000.36562
37.49726883.8790000.34822
40.46024990.5070000.33081
43.49252297.2900000.31427
46.733562104.5400000.30035
50.886563113.8300000.28816
54.047136120.9000000.28381
56.926074127.3400000.28033
60.086646134.4100000.28207
\n", - "
" - ], - "text/plain": [ - " Velocity in mph Drag coefficient\n", - "0.026146 0.058486 0.49965\n", - "8.871509 19.845000 0.49878\n", - "17.647351 39.476000 0.49704\n", - "22.432914 50.181000 0.48225\n", - "26.882303 60.134000 0.45004\n", - "30.636992 68.533000 0.40914\n", - "32.977694 73.769000 0.38042\n", - "34.604472 77.408000 0.36562\n", - "37.497268 83.879000 0.34822\n", - "40.460249 90.507000 0.33081\n", - "43.492522 97.290000 0.31427\n", - "46.733562 104.540000 0.30035\n", - "50.886563 113.830000 0.28816\n", - "54.047136 120.900000 0.28381\n", - "56.926074 127.340000 0.28033\n", - "60.086646 134.410000 0.28207" - ] - }, - "execution_count": 58, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "baseball_drag = pd.read_csv('data/baseball_drag.csv')\n", - "baseball_drag.index = Quantity(baseball_drag['Velocity in mph'].values, UNITS.mph).to(m/s)\n", - "baseball_drag" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Modify the model to include the dependence of `C_d` on velocity, and see how much it affects the results. Hint: use `interpolate`." - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "drag_interp = interpolate(baseball_drag['Drag coefficient'])\n", - "vs = linspace(0, 60, 101)\n", - "cds = drag_interp(vs)\n", - "plot(vs, cds)\n", - "decorate(xlabel='Velocity (m/s)', ylabel='C_d')" - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def drag_force(v, system):\n", - " \"\"\"Computes drag force in the opposite direction of `v`.\n", - " \n", - " v: velocity\n", - " system: System object with rho, C_d, area\n", - " \n", - " returns: Vector drag force\n", - " \"\"\"\n", - " unpack(system)\n", - " C_d = drag_interp(v.mag)\n", - " mag = -rho * v.mag**2 * C_d * area / 2\n", - " direction = v.hat()\n", - " f_drag = direction * mag\n", - " return f_drag" - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[[4.919407063460681]]
nfev44
njev0
nlu0
status1
messageA termination event occurred.
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events [[4.919407063460681]]\n", - "nfev 44\n", - "njev 0\n", - "nlu 0\n", - "status 1\n", - "message A termination event occurred.\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 63, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "system = System(system, drag_interp=drag_interp)\n", - "results, details = run_ode_solver(system, slope_func, events=event_func)\n", - "details" - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
xyvxvy
0.0000000.0000001.000000e+0028.28427128.284271
0.0000610.0017311.001731e+0028.28387128.283271
0.0006730.0190441.019042e+0028.27987128.273273
0.0067950.1920381.191812e+0028.23992028.173382
0.0680101.9086292.886082e+0027.84514227.183821
0.68016417.8411961.668401e+0124.32235318.129987
2.22207750.1491083.015913e+0118.2360420.350431
4.91940790.2066477.105427e-1511.640440-20.837214
\n", - "
" - ], - "text/plain": [ - " x y vx vy\n", - "0.000000 0.000000 1.000000e+00 28.284271 28.284271\n", - "0.000061 0.001731 1.001731e+00 28.283871 28.283271\n", - "0.000673 0.019044 1.019042e+00 28.279871 28.273273\n", - "0.006795 0.192038 1.191812e+00 28.239920 28.173382\n", - "0.068010 1.908629 2.886082e+00 27.845142 27.183821\n", - "0.680164 17.841196 1.668401e+01 24.322353 18.129987\n", - "2.222077 50.149108 3.015913e+01 18.236042 0.350431\n", - "4.919407 90.206647 7.105427e-15 11.640440 -20.837214" - ] - }, - "execution_count": 64, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "results" - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "90.20664656623309 meter" - ], - "text/latex": [ - "$90.20664656623309 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 65, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "x_dist = get_last_value(results.x) * m" - ] - }, - { - "cell_type": "code", - "execution_count": 66, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(18.23940861149146, 40.0)" - ] - }, - "execution_count": 66, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "vs = np.hypot(results.vx, results.vy)\n", - "interval = min(vs), max(vs)" - ] - }, - { - "cell_type": "code", - "execution_count": 67, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([0.49521022, 0.33351435])" - ] - }, - "execution_count": 67, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "drag_interp(interval)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap23soln.ipynb b/code/soln/chap23soln.ipynb deleted file mode 100644 index 026d64dab..000000000 --- a/code/soln/chap23soln.ipynb +++ /dev/null @@ -1,1196 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 23\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Code from the previous chapter" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "degree" - ], - "text/latex": [ - "$degree$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram\n", - "degree = UNITS.degree" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
x0 meter
y1 meter
g9.8 meter / second ** 2
mass0.145 kilogram
diameter0.073 meter
rho1.2 kilogram / meter ** 3
C_d0.3
angle45 degree
velocity40.0 meter / second
t_end20 second
\n", - "
" - ], - "text/plain": [ - "x 0 meter\n", - "y 1 meter\n", - "g 9.8 meter / second ** 2\n", - "mass 0.145 kilogram\n", - "diameter 0.073 meter\n", - "rho 1.2 kilogram / meter ** 3\n", - "C_d 0.3\n", - "angle 45 degree\n", - "velocity 40.0 meter / second\n", - "t_end 20 second\n", - "dtype: object" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(x = 0 * m, \n", - " y = 1 * m,\n", - " g = 9.8 * m/s**2,\n", - " mass = 145e-3 * kg,\n", - " diameter = 73e-3 * m,\n", - " rho = 1.2 * kg/m**3,\n", - " C_d = 0.3,\n", - " angle = 45 * degree,\n", - " velocity = 40 * m / s,\n", - " t_end = 20 * s)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Make a system object.\n", - " \n", - " params: Params object with angle, velocity, x, y,\n", - " diameter, duration, g, mass, rho, and C_d\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " # convert angle to degrees\n", - " theta = np.deg2rad(angle)\n", - " \n", - " # compute x and y components of velocity\n", - " vx, vy = pol2cart(theta, velocity)\n", - " \n", - " # make the initial state\n", - " init = State(x=x, y=y, vx=vx, vy=vy)\n", - " \n", - " # compute area from diameter\n", - " area = np.pi * (diameter/2)**2\n", - " \n", - " return System(params, init=init, area=area)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def drag_force(V, system):\n", - " \"\"\"Computes drag force in the opposite direction of `V`.\n", - " \n", - " V: velocity\n", - " system: System object with rho, C_d, area\n", - " \n", - " returns: Vector drag force\n", - " \"\"\"\n", - " unpack(system)\n", - " mag = -rho * V.mag**2 * C_d * area / 2\n", - " direction = V.hat()\n", - " f_drag = mag * direction\n", - " return f_drag" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes derivatives of the state variables.\n", - " \n", - " state: State (x, y, x velocity, y velocity)\n", - " t: time\n", - " system: System object with g, rho, C_d, area, mass\n", - " \n", - " returns: sequence (vx, vy, ax, ay)\n", - " \"\"\"\n", - " x, y, vx, vy = state\n", - " unpack(system)\n", - "\n", - " V = Vector(vx, vy) \n", - " a_drag = drag_force(V, system) / mass\n", - " a_grav = Vector(0, -g)\n", - " \n", - " a = a_grav + a_drag\n", - " \n", - " return vx, vy, a.x, a.y" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func(state, t, system):\n", - " \"\"\"Stop when the y coordinate is 0.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: y coordinate\n", - " \"\"\"\n", - " x, y, vx, vy = state\n", - " return y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Optimal launch angle\n", - "\n", - "To find the launch angle that maximizes distance from home plate, we need a function that takes launch angle and returns range." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def range_func(angle, params): \n", - " \"\"\"Computes range for a given launch angle.\n", - " \n", - " angle: launch angle in degrees\n", - " params: Params object\n", - " \n", - " returns: distance in meters\n", - " \"\"\"\n", - " params = Params(params, angle=angle)\n", - " system = make_system(params)\n", - " results, details = run_ode_solver(system, slope_func, events=event_func)\n", - " x_dist = get_last_value(results.x) * m\n", - " return x_dist" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's test `range_func`." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 68 ms, sys: 0 ns, total: 68 ms\n", - "Wall time: 65.7 ms\n" - ] - }, - { - "data": { - "text/html": [ - "102.72776151763685 meter" - ], - "text/latex": [ - "$102.72776151763685 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "%time range_func(45, params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And sweep through a range of angles." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "20.0 79.96823513701818 meter\n", - "23.0 86.29628649188565 meter\n", - "26.0 91.59647908800758 meter\n", - "29.0 95.89089380357919 meter\n", - "32.0 99.20335822576222 meter\n", - "35.0 101.5566800797347 meter\n", - "38.0 102.9717388091764 meter\n", - "41.0 103.4674081317783 meter\n", - "44.0 103.06092247917795 meter\n", - "47.0 101.76845068606522 meter\n", - "50.0 99.60572853320409 meter\n", - "53.0 96.5886733164578 meter\n", - "56.0 92.7339915489423 meter\n", - "59.0 88.05990483905569 meter\n", - "62.0 82.58716276454999 meter\n", - "65.0 76.34016117578491 meter\n", - "68.0 69.34714056465752 meter\n", - "71.0 61.63878192638944 meter\n", - "74.0 53.25610154962806 meter\n", - "77.0 44.24668067783074 meter\n", - "80.0 34.67021301943231 meter\n" - ] - } - ], - "source": [ - "angles = linspace(20, 80, 21)\n", - "sweep = SweepSeries()\n", - "\n", - "for angle in angles:\n", - " x_dist = range_func(angle, params)\n", - " print(angle, x_dist)\n", - " sweep[angle] = x_dist" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting the `Sweep` object, it looks like the peak is between 40 and 45 degrees." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap10-fig03.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(sweep, color='C2')\n", - "decorate(xlabel='Launch angle (degree)',\n", - " ylabel='Range (m)',\n", - " title='Range as a function of launch angle',\n", - " legend=False)\n", - "\n", - "savefig('figs/chap10-fig03.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use `max_bounded` to search for the peak efficiently." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 624 ms, sys: 0 ns, total: 624 ms\n", - "Wall time: 624 ms\n" - ] - } - ], - "source": [ - "%time res = max_bounded(range_func, [0, 90], params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`res` is an `ModSimSeries` object with detailed results:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
fun103.46837822354604 meter
status0
successTrue
messageSolution found.
x41.1391
nfev9
\n", - "
" - ], - "text/plain": [ - "fun 103.46837822354604 meter\n", - "status 0\n", - "success True\n", - "message Solution found.\n", - "x 41.1391\n", - "nfev 9\n", - "dtype: object" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "res" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`x` is the optimal angle and `fun` the optional range." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "41.139142795603355 degree" - ], - "text/latex": [ - "$41.139142795603355 degree$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "optimal_angle = res.x * degree" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "103.46837822354604 meter" - ], - "text/latex": [ - "$103.46837822354604 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "max_x_dist = res.fun" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Under the hood\n", - "\n", - "Read the source code for `max_bounded` and `min_bounded`, below.\n", - "\n", - "Add a print statement to `range_func` that prints `angle`. Then run `max_bounded` again so you can see how many times it calls `range_func` and what the arguments are." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[0;32mdef\u001b[0m \u001b[0mmax_bounded\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmax_func\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbounds\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Finds the input value that maximizes `max_func`.\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m Wrapper for https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize_scalar.html\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m min_func: computes the function to be maximized\u001b[0m\n", - "\u001b[0;34m bounds: sequence of two values, lower and upper bounds of the\u001b[0m\n", - "\u001b[0;34m range to be searched\u001b[0m\n", - "\u001b[0;34m args: any additional positional arguments are passed to max_func\u001b[0m\n", - "\u001b[0;34m options: any keyword arguments are passed as options to minimize_scalar\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m returns: ModSimSeries object\u001b[0m\n", - "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mmin_func\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0mmax_func\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mres\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmin_bounded\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmin_func\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbounds\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# we have to negate the function value before returning res\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mres\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfun\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0mres\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfun\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mres\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "%psource max_bounded" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[0;32mdef\u001b[0m \u001b[0mmin_bounded\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmin_func\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbounds\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Finds the input value that minimizes `min_func`.\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m Wrapper for https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize_scalar.html\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m min_func: computes the function to be minimized\u001b[0m\n", - "\u001b[0;34m bounds: sequence of two values, lower and upper bounds of the\u001b[0m\n", - "\u001b[0;34m range to be searched\u001b[0m\n", - "\u001b[0;34m args: any additional positional arguments are passed to min_func\u001b[0m\n", - "\u001b[0;34m options: any keyword arguments are passed as options to minimize_scalar\u001b[0m\n", - "\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m returns: ModSimSeries object\u001b[0m\n", - "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# try:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# print(bounds[0])\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# min_func(bounds[0], *args)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# except Exception as e:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# msg = \"\"\"Before running scipy.integrate.min_bounded, I tried\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# running the slope function you provided with the\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# initial conditions in system and t=0, and I got\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# the following error:\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# logger.error(msg)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# raise(e)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0munderride\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mxatol\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1e-3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;31m# TODO: Do we need to remove units from bounds?\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0munits_off\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mres\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mminimize_scalar\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmin_func\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mbracket\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mbounds\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mbounds\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mbounds\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'bounded'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0moptions\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mres\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msuccess\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mmsg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"\"\"scipy.optimize.minimize_scalar did not succeed.\u001b[0m\n", - "\u001b[0;34m The message it returned is %s\"\"\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mres\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmessage\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mModSimSeries\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mres\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "%psource min_bounded" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The Manny Ramirez problem\n", - "\n", - "Finally, let's solve the Manny Ramirez problem:\n", - "\n", - "*What is the minimum effort required to hit a home run in Fenway Park?*\n", - "\n", - "Fenway Park is a baseball stadium in Boston, Massachusetts. One of its most famous features is the \"Green Monster\", which is a wall in left field that is unusually close to home plate, only 310 feet along the left field line. To compensate for the short distance, the wall is unusually high, at 37 feet.\n", - "\n", - "Although the problem asks for a minimum, it is not an optimization problem. Rather, we want to solve for the initial velocity that just barely gets the ball to the top of the wall, given that it is launched at the optimal angle.\n", - "\n", - "And we have to be careful about what we mean by \"optimal\". For this problem, we don't want the longest range, we want the maximum height at the point where it reaches the wall.\n", - "\n", - "If you are ready to solve the problem on your own, go ahead. Otherwise I will walk you through the process with an outline and some starter code.\n", - "\n", - "As a first step, write a function called `height_func` that takes a launch angle and a params as parameters, simulates the flights of a baseball, and returns the height of the baseball when it reaches a point 94.5 meters (310 feet) from home plate." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def event_func(state, t, system):\n", - " \"\"\"Computes derivatives of the state variables.\n", - " \n", - " state: State (x, y, x velocity, y velocity)\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: height\n", - " \"\"\"\n", - " x, y, vx, vy = state\n", - " # TODO: add 94.5 as a system variable\n", - " return x - 94.5 * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Always test the slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "-94.5 meter" - ], - "text/latex": [ - "$-94.5 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "system = make_system(params)\n", - "event_func(system.init, 0, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def height_func(angle, params):\n", - " \"\"\"Computes the height of the ball at the wall.\n", - " \n", - " angle: launch angle in degrees\n", - " params: Params object\n", - " \n", - " returns: height in meters\n", - " \"\"\" \n", - " params = Params(params, angle=angle)\n", - " system = make_system(params)\n", - " results, details = run_ode_solver(system, slope_func, events=event_func)\n", - " height = get_last_value(results.y) * m\n", - " \n", - " return height" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your function with a launch angle of 45 degrees:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "11.033479404035585 meter" - ], - "text/latex": [ - "$11.033479404035585 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "height_func(45 * degree, params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now use `max_bounded` to find the optimal angle. Is it higher or lower than the angle that maximizes range?" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
fun11.04782805563806 meter
status0
successTrue
messageSolution found.
x44.5406
nfev12
\n", - "
" - ], - "text/plain": [ - "fun 11.04782805563806 meter\n", - "status 0\n", - "success True\n", - "message Solution found.\n", - "x 44.5406\n", - "nfev 12\n", - "dtype: object" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "res = max_bounded(height_func, [0, 90], params)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "44.54061679801752 degree" - ], - "text/latex": [ - "$44.54061679801752 degree$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "optimal_angle = res.x * degree" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "11.04782805563806 meter" - ], - "text/latex": [ - "$11.04782805563806 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "optimal_height = res.fun" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With initial velocity 40 m/s and an optimal launch angle, the ball clears the Green Monster with a little room to spare.\n", - "\n", - "Which means we can get over the wall with a lower initial velocity." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Finding the minimum velocity\n", - "\n", - "Even though we are finding the \"minimum\" velocity, we are not really solving a minimization problem. Rather, we want to find the velocity that makes the height at the wall exactly 11 m, given given that it's launched at the optimal angle. And that's a job for `fsolve`.\n", - "\n", - "Write an error function that takes a velocity and a `Params` object as parameters. It should use `max_bounded` to find the highest possible height of the ball at the wall, for the given velocity. Then it should return the difference between that optimal height and 11 meters." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def error_func(velocity, params):\n", - " \"\"\"Returns the optimal height at the wall minus the target height.\n", - " \n", - " velocity: initial velocity in m/s\n", - " params: Params object\n", - " \n", - " returns: height difference in meters\n", - " \"\"\"\n", - " params = Params(params, velocity=velocity)\n", - " res = max_bounded(height_func, [0, 90], params)\n", - " return res.fun - 11 * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your error function before you call `fsolve`." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.04782805563806036 meter" - ], - "text/latex": [ - "$0.04782805563806036 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "error_func(40 * m/s, params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then use `fsolve` to find the answer to the problem, the minimum velocity that gets the ball out of the park." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([39.98806408])" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "res = fsolve(error_func, 40 * m/s, params)" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "39.988064078851565 meter/second" - ], - "text/latex": [ - "$39.988064078851565 \\frac{meter}{second}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "min_velocity = res[0] * m/s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And just to check, run `error_func` with the value you found." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "2.051180558737542e-09 meter" - ], - "text/latex": [ - "$2.051180558737542e-09 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "error_func(min_velocity, params)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap24soln.ipynb b/code/soln/chap24soln.ipynb deleted file mode 100644 index 79f25dd2f..000000000 --- a/code/soln/chap24soln.ipynb +++ /dev/null @@ -1,1215 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 24\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Rolling paper\n", - "\n", - "We'll start by loading the units we need." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "second" - ], - "text/latex": [ - "$second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "radian = UNITS.radian\n", - "m = UNITS.meter\n", - "s = UNITS.second" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And creating a `Params` object with the system parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
Rmin0.02 meter
Rmax0.055 meter
L47 meter
t_end130 second
\n", - "
" - ], - "text/plain": [ - "Rmin 0.02 meter\n", - "Rmax 0.055 meter\n", - "L 47 meter\n", - "t_end 130 second\n", - "dtype: object" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(Rmin = 0.02 * m,\n", - " Rmax = 0.055 * m,\n", - " L = 47 * m,\n", - " t_end = 130 * s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function estimates the parameter `k`, which is the increase in the radius of the roll for each radian of rotation. " - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def estimate_k(params):\n", - " \"\"\"Estimates the parameter `k`.\n", - " \n", - " params: Params with Rmin, Rmax, and L\n", - " \n", - " returns: k in meters per radian\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " Ravg = (Rmax + Rmin) / 2\n", - " Cavg = 2 * pi * Ravg\n", - " revs = L / Cavg\n", - " rads = 2 * pi * revs\n", - " k = (Rmax - Rmin) / rads\n", - " return k" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As usual, `make_system` takes a `Params` object and returns a `System` object." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Make a system object.\n", - " \n", - " params: Params with Rmin, Rmax, and L\n", - " \n", - " returns: System with init, k, and ts\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " init = State(theta = 0 * radian,\n", - " y = 0 * m,\n", - " r = Rmin)\n", - " \n", - " k = estimate_k(params)\n", - " \n", - " return System(init=init, k=k, t_end=t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `make_system`" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
inittheta 0 radian\n", - "y 0 meter\n", - "r ...
k2.7925531914893616e-05 meter
t_end130 second
\n", - "
" - ], - "text/plain": [ - "init theta 0 radian\n", - "y 0 meter\n", - "r ...\n", - "k 2.7925531914893616e-05 meter\n", - "t_end 130 second\n", - "dtype: object" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(params)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
theta0 radian
y0 meter
r0.02 meter
\n", - "
" - ], - "text/plain": [ - "theta 0 radian\n", - "y 0 meter\n", - "r 0.02 meter\n", - "dtype: object" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.init" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can write a slope function based on the differential equations\n", - "\n", - "$\\omega = \\frac{d\\theta}{dt} = 10$\n", - "\n", - "$\\frac{dy}{dt} = r \\frac{d\\theta}{dt}$\n", - "\n", - "$\\frac{dr}{dt} = k \\frac{d\\theta}{dt}$\n" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object with theta, y, r\n", - " t: time\n", - " system: System object with r, k\n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " theta, y, r = state\n", - " unpack(system)\n", - " \n", - " omega = 10 * radian / s\n", - " dydt = r * omega\n", - " drdt = k * omega\n", - " \n", - " return omega, dydt, drdt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `slope_func`" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " ,\n", - " )" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(system.init, 0*s, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll use an event function to stop when `y=L`." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func(state, t, system):\n", - " \"\"\"Detects when we've rolled length `L`.\n", - " \n", - " state: State object with theta, y, r\n", - " t: time\n", - " system: System object with r, k\n", - " \n", - " returns: difference between `y` and `L`\n", - " \"\"\"\n", - " theta, y, r = state\n", - " unpack(system)\n", - " \n", - " return y - L" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[[125.33333333333341]]
nfev782
njev0
nlu0
status1
messageA termination event occurred.
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events [[125.33333333333341]]\n", - "nfev 782\n", - "njev 0\n", - "nlu 0\n", - "status 1\n", - "message A termination event occurred.\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results, details = run_ode_solver(system, slope_func, events=event_func, max_step=1*s)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And look at the results." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
thetayr
122.0579791220.57978945.2135340.054085
123.0579791230.57978945.7557840.054365
124.0579791240.57978946.3008260.054644
125.0579791250.57978946.8486610.054923
125.3333331253.33333347.0000000.055000
\n", - "
" - ], - "text/plain": [ - " theta y r\n", - "122.057979 1220.579789 45.213534 0.054085\n", - "123.057979 1230.579789 45.755784 0.054365\n", - "124.057979 1240.579789 46.300826 0.054644\n", - "125.057979 1250.579789 46.848661 0.054923\n", - "125.333333 1253.333333 47.000000 0.055000" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The final value of `y` is 47 meters, as expected." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "47.0 meter" - ], - "text/latex": [ - "$47.0 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "unrolled = get_last_value(results.y) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The final value of radius is `R_max`." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.054999999999999945 meter" - ], - "text/latex": [ - "$0.054999999999999945 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "radius = get_last_value(results.r) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The total number of rotations is close to 200, which seems credible." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "199.4741953418423" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "rotation = get_last_value(results.theta) / 2 / np.pi" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The elapsed time is plausible." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "125.33333333333341 second" - ], - "text/latex": [ - "$125.33333333333341 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_final = get_last_label(results) * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Plotting" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `theta`" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_theta(results):\n", - " plot(results.theta, color='C0', label='theta')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Angle (rad)')\n", - " \n", - "plot_theta(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `y`" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_y(results):\n", - " plot(results.y, color='C1', label='y')\n", - "\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Length (m)')\n", - " \n", - "plot_y(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `r`" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_r(results):\n", - " plot(results.r, color='C2', label='r')\n", - "\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Radius (mm)')\n", - " \n", - "plot_r(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also see the relationship between `y` and `r`, which I derive analytically in the book." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results.r, results.y, color='C3')\n", - "\n", - "decorate(xlabel='Radius (mm)',\n", - " ylabel='Length (m)',\n", - " legend=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the figure from the book." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap11-fig01.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_three(results):\n", - " subplot(3, 1, 1)\n", - " plot_theta(results)\n", - "\n", - " subplot(3, 1, 2)\n", - " plot_y(results)\n", - "\n", - " subplot(3, 1, 3)\n", - " plot_r(results)\n", - "\n", - "plot_three(results)\n", - "savefig('figs/chap11-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises\n", - "\n", - "**Exercise:** Since we keep `omega` constant, the linear velocity of the paper increases with radius. Use `gradient` to estimate the derivative of `results.y`. What is the peak linear velocity?" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3Xl8XXWd//FX2nTf13RJoC20n5buTdmKLS0goKMsogxaGVxwAR3AUWZAHUV0HAd1VBZBHQRx2FRQcADhB3RjE7rvn9KW0qRtmu5tumW7vz++J+3NbZLetLlLkvfz8ciD3O/55tzv5ab3k3PO+3y/ObFYDBERkWzTJtMDEBERqYsKlIiIZCUVKBERyUoqUCIikpVyMz2AdDGzDsCZwBagKsPDERGRoC0wEHjH3Q/Hb2g1BYpQnOZlehAiIlKnqcBr8Q2tqUBtAXj00UcZMGBApsciIiJASUkJM2fOhOgzOl5rKlBVAAMGDCA/Pz/TYxERkdqOufSikISIiGQlFSgREclKKlAiIpKVVKBERCQrpTUkYWa9gQeBi4HtwO3u/lgd/e4AvgXEZ+LHufv6aHsMOADUzHT7hLtfn8Khi4hImqU7xXcfUA7kAROA58xsibuvqKPvk+7+6Qb2Nd7d16ZikCIiknlpK1Bm1gW4Chjj7mXAa2b2LHAtcFu6xiEiIidvT9lhlq/fQUVFFZNH5dG1c/smf450HkGNAKrcfU1c2xLg/Hr6f9TMdhJu3rrX3e9P2D7XzNoAbwD/4u4bmnrAIiJSW3V1jMVrtvH2yhIqq6oBOFxRzSXnnNrkz5XOkERXYE9C2x6gWx19/wCMAvoBXwC+Y2afjNt+PjAEGAlsBv7PzFrTTcciImlXuvMAf3xlDW8s23ykOAH069UpJc+Xzg/1MqB7Qlt3YF9iR3dfGffwDTP7BfBx4PFo+9xoW7mZ3QzsJRS0ZU096Gx02223kZeXx9e+9rVG/dwFF1zAD37wA6ZMmZKikYlIS1RRWcXbK7ay+N1txK/C3rdnJ6aMHcgpAxI/2ptGOgvUGiDXzIa7+7tR23igroBEohiQcxLbJcE999zD+++/z09+8pNMD0VEstjGkr3MXljM3v3lR9py27bhrDMGMH5EP9q2Sd1Hb9oKlLvvN7OngTvN7HpCiu9y4Jg/583scmAusJswC/lNwDejbaOBdoSjpU7AD4BNwKo0vAwRkVbh4OFKXlu8Cd+4q1Z7fv9uzCjMp0fXDikfQ7pv1L2RUFRKCafrbnD3FWY21czK4vpdA6wlnP57BPgvd/9dtC0PeJJwWm894VrUR9y9Ij0vIf1WrlzJlVdeycSJE7nllls4fDjcHvaRj3yEV1999Ui/iooKzj77bFatCrX6L3/5CzNmzODss8/m/vuPZkzmzp3Lr371K1544QUmTpzIZZddlt4XJCJZKxaLsfr9nTz6t9W1ilPH9rlcOPkULp82LC3FCdJ8H5S77wSuqKN9HiFEUfP4k4l94ra9ClhKBggs8lLeXllCRWX18TufoHa54fB4ovU/bt/y8nK+8pWvcN111zFz5kxeeeUVvv71r3P99ddz+eWX8+yzz3LBBRcAMGfOHPr378+oUaNYu3Yt3/ve9/j1r3/N+PHj+elPf0pJSQkA06ZN40tf+pJO8YlILXvKDjNnYTEbt9aOBgwv6MXUCYPo3LFdWsejqY4SLF6zLaXFCaCisprFa7Yl1XfJkiVUVFRw3XXX0a5dOy699FLGjh0LwGWXXcacOXMoKwsHn88+++yRo6G//e1vTJ8+nTPPPJP27dtz880306aN3m4ROVZ1dYyFXsrjL3mt4tStc3s++oFhXHLOqWkvTqACdYwJI/rRLje1/1va5bZhwoh+SfUtLS0lLy+PnJyjFyIHDRoEQF5eHpMmTeLFF19k7969zJ0790iBKi0trbUwY+fOnenZs2cTvgoRaQlKdx3gj6+u4Y2lR6PjOTk5jB/ej09dYpw6MDUJvWTo3qEEE61/Uqfe0qVfv35s3bqVWCx2pEht3ryZgoICAK688kr++Mc/UlVVxYQJE8jLywOgf//+rFu37sh+Dh48yO7du488ji94ItL6VFRW8/bKEpas2UZ1QnR8RmEBeb07Z3B0gY6gstyECRPIzc3lkUceobKykpdeeolly47e7nXRRRexcuVKHnnkEa644ujlvUsuuYTZs2czf/58ysvLufvuu6muPnrqsk+fPmzatKlWm4i0DkVb9/H4S6tZ5KVHilNu2zacO3Ygn7hwRFYUJ1CBynrt27fnnnvu4c9//jNnnnkmzz//PB/84AePbO/YsSMXX3wxxcXFtdqHDx/Od77zHb7xjW8wdepUunfvXuuU36WXXgrA2WefzZVXXpm+FyQiGXPwcCUvv/0+z8xdV+u+pvz+3bjmg0bhyLyU3tfUWDnxdwW3ZGY2BHjvlVdeIT8/P9PDaVL33nsvGzZsUCJPROoUi8VYs3EXry3ZzMHDlUfaO7RvywfGDWbkkF4ZO+1fXFzMhRdeCDA0cU5VXYNq5nbv3s1TTz3FXXfdlemhiEgW2ru/nNkLi9hYkh3R8cZQgWrG/vCHP/DDH/6Qyy67jDPPPDPTwxGRLFJdHWPp2m38fXkJFXETu3br3J7pk/Izms5LlgpUM3b11Vdz9dVXZ3oYIpJltu06yKwFRZTuOnCkLScnh3Gn9+WcMQNol9s2g6NLngqUiEgLUVFZzTsrS1icEB3v06MTF0zOjuh4Y6hAiYi0AEVb9zFrQVGtdF7bNjmcGU2rlk3pvGSpQImINGOHDlfy2pLNrH5/Z632/P5dmT6pgJ7d0jOxayqoQImINEOxWIx3i3Yzb/GmY6Lj540bxKghvZv9jDEqUCIizUz90fGeTJ0wOKuj442hAiUi0kzUFx3v2qkd50/KZ+igHhkcXdNTgRIRaQa27z7Iq/OPjY6PPa0P54wZSPt2zSM63hgqUCIiWayyqpq3V9QRHe/ekRmTCxjQp0sGR5daKlAiIlmqaOs+Zi8sZk/Z4SNtR6LjI/rRtm3Lnu9bBUpEJMscOlzJ60s3s2pD7ej44H5dmV6YT69uHTM0svRSgRIRyRINRcenjB3EGUObf3S8MdJaoMysN/AgcDGwHbjd3R+ro98dwLeAw3HN49x9fbR9QrSfUcAq4PPuvji1oxcRSZ29+8uZs7CY90v21mo/Pb8n0ya2nOh4Y6T7COo+oBzIAyYAz5nZEndfUUffJ93904mNZtYeeAb4OfBL4EvAM2Y23N3LE/uLiGSz6uoYy9Zu560VW6iobPnR8cZIW4Eysy7AVcAYdy8DXjOzZ4FrgdsasavphHH/3N1jwN1m9g3gAuBvTTtqEZHU2b47zDq+dWfriY43RjqPoEYAVe6+Jq5tCXB+Pf0/amY7gS3Ave5+f9Q+GlgaFacaS6N2FSgRyXqVVWHW8UXe+qLjjZHOAtUV2JPQtgfoVkffPwC/BrYCZwNPmdlud3+8kfsREckqRVv3MWdhMbtbaXS8MdJZoMqAxCUcuwP7Eju6+8q4h2+Y2S+AjwOPN2Y/IiLZor7o+KC+XZkxufVExxsjnaV6DZBrZsPj2sYDdQUkEsWAmmzlCmCcmcVnLccluR8RkbSKxWKs2biLR19cXas4dWjXlhmFBVw5/TQVp3qk7QjK3feb2dPAnWZ2PSHFdzkwJbGvmV0OzAV2A2cCNwHfjDbPBqqAm8zsAeALUfurKX0BIiKNtO9AiI5v2FI7On5afk+mTRhMl06tLzreGOmOmd8I/BYoBXYAN7j7CjObCrzg7l2jftdE/ToAxcB/ufvvANy93MyuAP4H+BHhPqgrFDEXkWyh6HjTSGuBcvedwBV1tM8jhB9qHn/yOPtZBBQ2+QBFRE7Sjj1h1vHE6PiYYX04d6yi442hqY5ERJpAiI5vZZGX1oqO9+7ekRmFBQzsq+h4Y6lAiYicpE3bypg1v+iY6PjkUXlMsv6Kjp+gRhUoM+sOdAJ2uHvl8fqLiLRkh8oreWPpZla+V0d0vDCfXt2VzjsZDRYoM+sMXE24B2kKUHNlL2Zmq4GXgd9F14RERFqFWCzG2uLdzFu8mQOHKo60t2/XliljBzJ6WJ9WNet4qtRboMzsX4FbCfcXvUCYnLUEOAT0AkYSitYLZrYUuNndV6V8xCIiGVQWRcffS4yOD+7B1In5dFV0vMk0dAQ1BJjs7u/Xs/0d4Pdm9hXCUdZ4QuRbRKTFqa6OsXz9dt5cdmx0fNrEfIYNVnS8qdVboNz9xmR24O7VwBNNNiIRkSyzY89BZi0opmTH/lrtY07ry7ljB9JB0fGUSCokEYUjcPe90eMhhPuZ1rj78ykbnYhIBlVWVTN/1VYWrlZ0PBOSTfE9AzwK/E+0Ku7bhNkgCszsDnf/WaoGKCKSCZu2lTFrQRG799WOjheOyqNQ0fG0SPb/8Hjg9ej7fwTWufsY4JPAV1IxMBGRTDhUXsmsBUX8efbaWsVpYJ8u/OMHjbPOGKDilCbJHkG1B2rm7bgY+HP0/XJgcFMPSkQk3WKxGOs27WHuok2KjmeJZAvUIuCGaIn2i4FvRe2nANtSMTARkXQpO1DOnEWbeG9z7bVQFR3PrGQL1M3A74EbgF/ELSj4CY6e+hMRaVZisRjL1+3gzeVbKK+oOtLepWM7pk0czGn5PTM4OjneTBLd3H2fuy8ERtfR5d8IazOJiDQr9UbHh/XhnLED6dheU5Vm2vHega1m9hbwHPCcu6+O3+juh1I2MhGRFKisqmbBqq0s8FKqq49Gx3t168iMwnwG9evawE9LOh2vQA0CLgE+DPyrme0HnicUrFfd/XBDPywikk02byvj1YToeJs2OUwemUfhSEXHs02DBcrddwNPAk+aWQ5h+fV/AO4E/mhmcwjF6nl335DisYqInJBD5ZW8uWwLK9bvqNU+sE8Xphfm06dHpwyNTBqS9ElWd48RbtB9G/iumeUBHyIcXf3AzP7D3X+ammGKiDReTXR83qJN7E+Ijp87ZiBjTlN0PJud8FVAd98KPAw8bGZtgd5NNSgRkZNVdrAizDqeEB0fOqgH508cTNfO7TM0MklW0gXKzD4ETAP6UXsGipi7fx7dDyUiWaC+6Hjnmuj44B46amomkp0s9mfAl4FZhDn4YnGbk36no3n8HiTc7LsduN3dH2ugf3tgKdDV3fPj2mOEmS1qxvGEu1+f7DhEpGXaufcQs+YXsSUhOj56WB/OVXS82Un23foMcLm7v3SSz3cfUA7kAROA58xsibuvqKf/rYSCWFfuc7y7rz3J8YhIC1BVVc2C1aXMX721VnS8Z7cOXFBYoOh4M5VsgSoBtpzME5lZF+AqYIy7lwGvRVMnXQvcVkf/ocCngX8BfnMyzy0iLdfm7WXMml/Mrn1Hb8ts0yaHQutP4ag8chUdb7aSLVD/DNxlZrcCq9z9RGaPGAFUufuauLYlwPn19L8H+CZwsJ7tc82sDfAG8C+KuYu0Locrqnhz6WaWJ0THB/TpwgxFx1uEZAvUeqAnoaBgZrU2unsyy0l2BfYktO0BuiV2NLMrgVx3/7OZTa9jX+cDbwGdgR8A/2dmE9y9MolxiEgzt654N3MVHW/xki1QTxKOZD7JsSGJZJUB3RPaugP74huiU4F3Ee6vqpO7z42+LTezm4G9wChg2QmMS0SaibKDFcxdVMz6TYqOtwbJFqgzgIkJp+caaw2Qa2bD3f3dqG08kBiQGA4MAeZFR2rtgR5mVgKcU8+pvBiNSBOKSPMSi8VYvn4Hby5TdLw1SbZAvQyMIxSZE+Lu+83saeBOM7uekOK7HJiS0HU5UBD3eApwLzAJ2GZmo4F2hKOlToRTfJuAVSc6NhHJXoqOt17JvrNzgF+Y2bnASqAifqO7P5Lkfm4Efks4TbgDuMHdV5jZVOAFd+8aXUcqqfkBM9sJVLt7SfQ4D7gfyAf2E0ISH3H3CkSkxWgoOj6jsIDBio63eI1J8ZUDH4u+4sWApAqUu+8ErqijfR513+uEu88mFKOax68CVldfEWkZtmzfz6wFRezcGxcdz8lh0sj+TFZ0vNVIqkC5+9BUD0RERNFxiaeTtyKSFdYV72be4k2UHTx6tr5dbhvOHTuQMcP60qaNQhCtTb0FyszuAe5y96KGdhCtE/UJoI27P9HE4xORFq7sYAXzFhWzLjE6PrA750/KV3S8FWvoCGoTsNDMlhNW0V1ImO7oMOGm3ZHAecCVhOTdTakdqoi0JLFYjBXrd/BGXdHxCYM5LV/R8dau3gLl7j8ys3sJN+d+HPg2tWd9WAO8Alzm7u+kdJQi0qLs2nuIWQuK2Ly9dnT8jKF9mDJO0XEJjrfkexlhotbfAJhZL6AjsMPdy1M/PBFpSaqqqlngpSxYtZWq+Oh41w7MmKzouNTWqD9T3H1XqgYiIi1bfdHxidafM89QdFyOpeNoEUmp8ooq3ly2heXrdxCLHT1qyuvdmRmFBfTtqei41E0FSkRS5r3Ne5izsPiY6Pg5YwYy9jRFx6VhKlAi0uT2H6xg7uJNrCveXat9SBQd76bouCQhqQJlZm1PcJFCEWlFYrEYK9/byRtLN3M4LjreqUMu0yYO5vT8noqOS9KSPYLaYmaPAb939wWpHJCINE+79h1i1vxiNm8vq9V+xtDeTBk7iI4ddMJGGifZ35gbgJnA62a2Hvg98Ki7b0zZyESkWaiqqmahlzK/juj49MJ88vsfs2i2SFKSnSz2KeApM+sJ/COhWN1pZvMIM5k/5e77GtqHiLQ8JTv2M2t+ETsUHZcUaOx9ULuBX5nZw4SpjX4ATAfuNbPHgW+7+9amHqSIZJfyiireWr6FZesUHZfUaVSBMrPpwKeBqwiLDn6fcLovD/gu8BwwuWmHKCLZpN7o+OiBjD1d0XFpOsmm+H5EmJOvO/BHwgq2r8d1ed/MvgKsa/ohikg2OHCogrmLNrE2ITp+6oAQHe/eRdFxaVrJHkFNAP4N+Iu7H6qnTzFwQZOMSkSyxpHo+LLNHC6vHR2fOmEwwwsUHZfUSLZAPQb82d0PxzeaWXvgGnd/xN0rgTlNPUARyZxd+w4xe0Exm7bVjo6PGtKb88YpOi6plexv10PA3wjXneJ1i7Y9ksxOzKw38CBwMbAduN3dH2ugf3tgKdDV3fPj2idE+xkFrAI+7+6Lk3wtInIcVVXVLFqzjXdWltSKjvfo2oHpk/IpyFN0XFIv2QxoDhCLb4hW0j0b2NGI57sPKCeEKmYC95vZ6Ab630pCUYyK1jPA/wK9gN8Bz0TtInKSSnbs5w8vr+Gt5VuOFKc2OTlMsv588mJTcZK0afAIysyqCYUpBpSYWV3dfpjME5lZF0L6b0y0ztRrZvYscC1wWx39hxISg/9CtB5VZHo07p+7ewy428y+Qbj+9bdkxiIix6ovOt6/V4iO9+ul6Lik1/FO8c0gHD29SiguO+O2VQAb3b04yecaAVS5+5q4tiXA+fX0vwf4JnAwoX00sDQqTjWWRu0qUCInoM7oeNs2nD1mAONO76fouGTE8VbUnQNHjmY2JhSFxuoK7Elo20PtZeSJnu9KINfd/xzde3VC+xGRhh04VMG8xZt4t6h2dPyUAd2YPqlA0XHJqHoLlJlNA96I0nmnAqfWc4oPd5+bxHOVEe6jitcdqDVFUnQq8C7gwyezHxGpXywWY9WGnby+VNFxyV4NHUHNBgYQQgqzG+gXA9om8VxrgFwzG+7u70Zt44EVCf2GA0OAeVFBbA/0MLMS4Jyo/9fNLCfuiG4cIYAhIsexe99hZi0oOiY6PvLU3nxgvKLjkj3q/U109zZ1fX+i3H2/mT1NmGT2esLNv5cDUxK6LgcK4h5PAe4FJgHbgM1AFXCTmT0AfCHq9+rJjlGkJauqjrHIS4+Jjnfv0p4ZhQVK50nWSfefSjcCvyUcle0AbnD3FWY2FXjB3btGpxRLan7AzHYC1e5e01ZlZlcA/wP8iHAf1BXuXp7OFyLSnJTs2M+sBcXs2HM0c9QmJ4cJI/px5hkDaJerWccl+yQ7F9/vgIXu/ouE9puAie7+2WT24+47gSvqaJ9HCD/U9TOzgfyEtkVAYTLPKdKalVdU8fflJSxdt13RcWl2kj2C+hDwkzraXwW+1XTDEZGmsmHLXmYvKFJ0XJqtZAtUV8J1n0TVKN4tklUaio6fPzGfHl07ZGhkIo2TbIH6O2HZ939OaP8KsKBJRyQiJ6Sh6PgHxg9ixCm9FB2XZiXZAvUN4GUzOweYR4iWTwNOJ0z8KiIZtHvfYWYvLKK4NDE63ovzxg+mk6Lj0gwl9Vvr7gvMbDjhCGo0Yfqj54F73X1bCscnIg2oiY7PX7WVyqrqI+3du7Rn+qR8ThmQeE+7SPOR9J9V7r6dsKy7iGSBrTsPMGtBEdt3H42O50TR8bPOyKNdbjL3z4tkr6QLlJkNItzHNDJqWgU84O6bUjEwEalbRWUVby0vYena2tHxfj07MWNyAf17dc7g6ESaTlJ355nZhcBa4EJgY/R1EfCumV2UuuGJSLz3t+zlsRedJe9uO1Kcctu2Ycq4QXziwhEqTtKiJHsE9RPgx+5e6xSfmd0ZbZvQ1AMTkaNCdHwz7xbtqtVekNeN6ZMUHZeWKdkCNQq4uo723wP/2nTDEZF4sViM1Rt28drSTbWi4x3b5/KBCYMwRcelBUu2QG0knN57N6H9g0BRk45IRICa6HgxxaW1V5KxU3px3vhBdO7YLkMjE0mPZAvU94CHzOwDwFtR2znAJ4DPp2JgIq1VVXWMJWu28fbKkmOi4+dPyudURcellUj2PqhHzWwdcBPwOcJ9UKuB6e7+ZgrHJ9KqlO48wKt1RceH9+Os0YqOS+vSmPug3uLo0ZOINKGKyir+vqKEJe/WER0vLKB/b6XzpPVpaMn3U5LdibtvbJrhiLQ+75fsZc7CYvbuP7qkWW7bNpw1egAThmvWcWm9GjqC2kCYcy9Rzb+WWPR9sku+i0icA4cqeG3JZtZsVHRcpC4NFaihaRuFSCsSi8Xw93fx2pLNHCqvPNKu6LhIbfUWKHd/P50DEWkN9pSF6HjRVkXHRY6nMXPxXQl8ERgCXOzuRWb2RWCDu7+UovGJtAjV1TEWKzou0ihJFSgz+zJwB/AL4Hyg5s+8CuB2QAVKpB6l0azj2xKi4+OH9+Xs0QMUHRepR7JHULcAn3P3583sm3Htfwd+nOyTmVlv4EHCIofbgdvd/bE6+t1CuOeqL1AGPAnc6u6V0fYNQB5Hl6F/w921cKJklYrKKt5esZXFcRO7gqLjIslKtkCdAqyoo70a6NSI57sPKCcUlwnAc2a2xN0T9/1X4GF33x0VtT8RCtZ/x/X5qLu/3IjnFkmbjSV7mV1XdPyMAYwf0Y+2io6LHFeyBWoF4dTeIwnt1wALk9mBmXUBrgLGuHsZ8JqZPQtcC9wW39fd18U9zCEUwtOTHKtIxhw4VMHrSzbjCdHx/P7dmFGo6LhIYyRboG4H/mRmI6Ofuc7MRgCXE07XJWMEUOXua+LalhAK3zHM7FPAA0A3wunAryd0edTM2gCLCKf/liQ5DpEmF4vF8I27eG3xsdHx88YNYuQQRcdFGiupBQujU2lnE07NLSNMEgvwAXd/Lcnn6grsSWjbQyhAdT3nY+7enVDYHgC2xm2eSUgTngrMAl40s55JjkOkSe0pO8yz89bz8tsbaxWn4QW9+NQlxqihvVWcRE5AY+bic05u5vIyIDFL2x3YV0ff+Od918xWAL8EPha1vR7X5T/N7DpgKuHalUhaVFfHWPzuNt5eUTs63q1ze6ZPyufUgYqOi5yMZGPm7wKPA39w9+Un+FxrgFwzG+7uNetKjafu8EWiXOC0BrbXTLskkhalu6Lo+K7a0fFxp/flnDGKjos0hWSPoH5EOK13m5mtBf4APOnuq5J9Inffb2ZPA3ea2fWEFN/lwJTEvtH2Z9291MzOIFwDezHadgpQALxDOEX5z4Q4+uuJ+xFpahWV1by9soQla7ZRHRcd7xtFx/MUHRdpMsleg3rQ3S8FBhCi3ucCS8xsmZl9uxHPdyMhll5KOCK7wd1XmNlUMyuL63cesMzM9gPPR1819191A+4HdgGbgEuBD7n7jkaMQ6TRirbu4/GXVrPIS48Up9y2bTh37EA+ceEIFSeRJpYTfwNhY5hZIfAQMNrds/58hpkNAd575ZVXyM/Pz/RwpBk5eLiS15dsYvX7x0bHp0/Kp2c3RcdFTlRxcTEXXnghwFB33xC/LemQBICZFQAfB64GzgQWALc2zTBFskssFmPNxjDr+MHDR9N5Hdq35QPjBis6LpJiyYYkbiEUpbMI9y49CXwysdqJtBR7yg4zZ1ExG0tqh0yHF/Ri6gTNOi6SDskeQX2GEIy4NmGWB5EWpbo6xpIoOl4RFx3v2qkd0wsLGKLouEjaJFWg3H1Cqgcikmnbdh1k1oIiSncdONKWk5PDuNP6cvaYAbRvl/WXWkValEZdgxJpieqLjvfp0YkZhfkM6NMlg6MTab1UoKRVK9q6j1kLimrNOt62TQ5nnjGAidZfs46LZJAKlLRKhw5X8lod0fHB/boyvTCfXt06ZmhkIlLjuAXKzNoBnyXM7FCS+iGJpE5D0fHzxg1i1BBN7CqSLY5boNy9wsx+hpZ1l2Zu7/5yZi8sqiM63pOpEwYrOi6SZZI9xff/COs2bUjdUERSo7o6xtK12/j78mOj4+dPymfooB4ZHJ2I1CfZAjUP+LGZnQssBg7Eb3T3xJV2RbJCfdHxsaf14ZwxAxUdF8liyRaorwL7gUuir3gxjl0KXiSjKiqreWdlCYsTo+PdOzJjcoGi4yLNQLI36g5N9UBEmkrR1n3MXljMnrLDR9qORMdH9KNt26Qm8ReRDFPMXFqMEB3fzOr3d9ZqV3RcpHlKukCZ2WjgSsJigbXiTu7+uSYel0jSYrEY7xbtZt7iTcdEx6eMHcQZQxUdF2mOkp3N/GrCdab/B1xMiJyfBgwGnk7Z6ESOY+/+cuYsLOb9kr212k/P78m0iYqOizRnyR5B/TvwVXf/HzPbB9zs7uvN7G5CeEIkraqrYyxbu523VmyholLRcZGWKNkCNQx4Ofr+EGHZdYB7gdett7H5AAAVi0lEQVSB25t4XCL12r47RMe37lR0XKQlS7ZAlQB9CDfqvgecR1i48HRAkShJi8qqEB1f5IqOi7QGyRaoZ4FLCUu83wP8xsw+A4wGfpOaoYkcVbR1H3MWFrNb0XGRViPZ+6C+Fvf9781sPXAOsM7d/5Lsk5lZb+BBQtBiO3C7uz9WR79bgJuAvkAZYYn5W929Mto+BHgIOBvYSLg+9nLifqT5O3S4kteXbmbVhtrR8UF9uzJjsqLjIi3ZCd0H5e6vE649NdZ9QDmQB0wAnjOzJe6+IqHfX4GH3X13VNT+RChY/x1tfxx4E/hw9PUnMxvu7ttOYEySheqNjrdry5Rxio6LtAbJxsxzgFuALwJDgNFRiu924P26joLq2EcX4CpgjLuXAa+Z2bPAtcBt8X3dfV3cwxygmnC9CzMbAUwCLnb3g8BT0RHXVcADybweyW77DpQze8Gx0fHT8nsybcJgunRSdFykNWhMzHwmcAfhFF2NdcC/AMctUMAIoMrd18S1LSHMkn4MM/sUoeB0I5wO/Hq0aTSw3t3j10xYErVLM6bouIjES7ZAfQb4jLvPNbNfx7UvBkYmuY+uwJ6Etj0cjazXEh2VPWZmw4F/ArYeZz+DkxyHZKEdew7y6vxjo+NjhvXh3LGKjou0RskWqAFAUR3tHUk+Zl4GdE9o6w7sq6PvEe7+rpmtAH4JfOxE9yPZKUTHt7LIS2tFx3t378iMwgIG9lV0XKS1Sra4/B24Iu5xzSfJDcBrSe5jDZAbHRHVGA8kBiTqkkuYWomo/zAziz/ySnY/kkU2bSvjiZecBau3HilObdvkcNboAfzjRSNUnERauWSPoL4OvGhmZwLtgW+Z2RmAAdOS2YG77zezp4E7zex6QorvcmBKYt9o+7PuXho9z+3Ai9F+1pjZYuC7ZvZt4EPAOEJIQpqBQ+WVvLF0MyvfS4yOd2F6YQG9uys6LiJJHkG5+0JCMVoFPAMMAuYA4+uIiDfkRqATUEqIit/g7ivMbKqZlcX1Ow9YZmb7geejr2/Gbb8GmAzsAn4EfFwR8+wXouO7eOxFr1Wc2rdry/RJ+Vw5/XQVJxE5IicWd96/JYtu7n3vlVdeIT8/P9PDaXXKDoRZx9/bkhAdH9yDqRPz6arouEirVFxczIUXXggw1N03xG9rzHpQvQlHLf1JOPJydy35LnWqro6xfP123lx2bHR82sR8hg1WdFxE6pbsjbofBx4m3DS7jaMhCaLvVaDkGHVFxwHGnNaXc8cOpIOi4yLSgGSPoO4iTDP0PXevSuF4pAWorKpm/qqtLFyt6LiInLhkC1Rfwtx4Kk7SoE3bypi1oIjd+47OOt6mTQ6TR+VRaP0167iIJC3ZAvUEYVLWe1M4FmnGQnR8Cyvf21GrfWCfLsyYrOi4iDResgVqO+H+pYuAZUBF/EZ3v7OpBybNQywWY13xHuYu3sSBQ0d/Ldq3a8uUsQMZPayPZh0XkROSbIE6lzAhaw/gAwnbYoAKVCuk6LiIpFKyCxbOSPVApPmoLzrepWM7pk0czGn5PTM4OhFpKU5owUJpvXbsOcisBcWU7Nhfq33MsD6cM3YgHdvrV0pEmka9nyZm9irwsWhV21nUvvepFne/IBWDk+xxJDrupVRXH/1V6NWtIzMK8xnUr2sGRyciLVFDf+7OISzPDjA79UORbLV5Wxmv1hUdH5lH4UhFx0UkNeotUO7+vbq+j2dmHQkr5UoLdKi8kjeXbWHF+mOj49ML8+nTo1OGRiYircHJXjAwYCGgOWtakFgsxrpNe5i76Njo+LljBjLmNEXHRST1dEVbaik7UM6cRZt4b/OeWu1DB/Xg/ImD6dq5fYZGJiKtjQqUAOGoafm6Hby5fAvlFUdntOpcEx0f3ENHTSKSVipQUm90fPSwPpyr6LiIZEiDnzxm9p3j/PzAJhyLpFlVFB1fkBAd79mtAxcUFig6LiIZdbw/jZOZQWJuUwxE0qu+6Hih9adwVB65io6LSIY1WKA0xVHLU190fECfLsxQdFxEsoguLrQi64p3M3fRJvYrOi4izUBaC5SZ9QYeBC4mLOFxu7s/Vke/W4HrgFOjfr909x/Hbd8A5AE1cbM33P3ilA6+GSs7WMHcRcWs36TouIg0H+k+grqPMH1SHjABeM7Mlrj7ioR+OcA/AUuB04CXzKzI3Z+I6/NRd385HYNurmKxGMvX7+DNZYqOi0jzk7YCZWZdgKuAMe5eBrxmZs8C1wK3xfd197viH5rZM8B5hJV9JQk79x5i1vwitig6LiLNVDo/pUYAVe6+Jq5tCXB+Qz9kZjnAVOBXCZseNbM2wCLgVndf0pSDba6qqqpZsLqU+au3HhMdn1FYwGBFx0WkmUhngeoK7Elo2wN0O87P3QG0AR6Ka5tJmAMwB7gZeNHMRrr77qYZavO0eXsZsxcUs3PvoSNtbXJymDSyP5MVHReRZiadBaoM6J7Q1h3YV98PmNlXCdeiprr7kRt23P31uG7/aWbXEY6y/tp0w20+DldU8ebSzSxPiI7n9e7MBZMLFB0XkWYpnQVqDZBrZsPd/d2obTyQGJAAwMw+R7g2Nc3di4+z7xjhaKrVWVe8m3mLN1F28Gh0vF1uG84dO5Axw/rSpk2r/N8iIi1A2gqUu+83s6eBO83sekKK73JgSmJfM5sJ/BCY4e7rE7adAhQA7xBO/f0z0Bd4PXE/LVnZwQrmLSpmXWJ0fGB3zp+Ur+i4iDR76Y5y3Qj8FigFdgA3uPsKM5sKvODuNVfwfwD0Ad4xs5qf/V93/zLhmtX9hPj5IWAx8CF3r31+q4WKxWKsWL+DN+qIjk+dMIjT83sqOi4iLUJaC5S77wSuqKN9HiFEUfN4aAP7WAGMS8kAs9zOvYeYvaCIzdtrR8fPGNqbKeMGKTouIi2KPtGagaqqahZ4KQtWbaUqPjretQMzJis6LiItkwpUltuyfT+zFhQdEx2faP058wxFx0Wk5VKBylLlFVW8uWwLy9fvIBY7etSU17szMwoL6NtT0XERadlUoLLQe5v3MGdh8THR8XPGDGTsaYqOi0jroAKVRfYfrGDu4k2sK649IcaQKDreTdFxEWlFVKCyQCwWY+V7O3lj6WYOx0XHO3XIZdrEwYqOi0irpAKVYbv2HmLWgmI2by+r1X7G0N5MGTuIjh30FolI66RPvwypqqpmoZcyv47o+PTCfPL7H28OXRGRlk0FKgNKduxn1vwidig6LiJSLxWoNFJ0XEQkeSpQaVJvdHz0QMaerui4iEgiFagUqy86fuqAEB3v3kXRcRGRuqhApUhD0fGpEwYzvEDRcRGRhqhApcCufYeYNf/Y6PioIb05b5yi4yIiydAnZROqqqpm0ZptvLOypFZ0vEfXDkyflE9BnqLjIiLJUoFqIvVFxyeM6MdZowcoOi4i0kgqUCepvKKKt5ZvYdm62tHx/r1CdLxfL0XHRUROhArUSagzOt62DWePGcC40/spOi4ichJUoE7AgUMVzF20ibUJ0fFTBnRj+qQCRcdFRJpAWguUmfUGHgQuBrYDt7v7Y3X0uxW4Djg16vdLd/9x3PYhwEPA2cBG4Kvu/nKqx38kOr5sM4fLFR0XEUmldF+5vw8oB/KAmcD9Zja6jn45wD8BvYBLga+a2TVx2x8HFgF9gG8BfzKzfqkc+K59h/jLnHXMWlBUqziNPLU3My8ZyYhTeqk4iYg0obQdQZlZF+AqYIy7lwGvmdmzwLXAbfF93f2u+Idm9gxwHvCEmY0AJgEXu/tB4CkzuyXa9wOpGPv23Qf506vvUllVfaSte5f2zCgsUHRcRCRF0nmKbwRQ5e5r4tqWAOc39ENmlgNMBX4VNY0G1rv7voT91HUk1iTWb9pzpDi1yclh/Ih+nHXGANrlKjouIpIq6SxQXYE9CW17gOMdgtxBOBX50HH2M/gkx1cvO7UXxaVldGjXhrNGD1R0XEQkDdJZoMqA7glt3YF9dfQFwMy+SrgWNdXdD5/ofk5Wj64d+NiM01O1exERqUM6z1GtAXLNbHhc23hgRV2dzexzhGtTF7p7cdymFcAwM4s/8qp3PyIi0jylrUC5+37gaeBOM+tiZucBlwO/T+xrZjOBHwIfdPf1CftZAywGvmtmHc3sSmAc8FSqX4OIiKRPum/UvRH4LVAK7ABucPcVZjYVeMHdu0b9fkCIkL9jZjU/+7/u/uXo+2uAh4FdhPugPu7u29LzEkREJB3SWqDcfSdwRR3t8wjhh5rHQ4+znw3A9CYenoiIZBHlpEVEJCupQImISFZSgRIRkazUmmYzbwtQUlKS6XGIiEgk7jO5beK21lSgBgLMnDkz0+MQEZFjDQTWxTe0pgL1DmFOvy1A1XH6iohIerQlFKd3EjfkxC9TLiIiki0UkhARkaykAiUiIllJBUpERLKSCpSIiGQlFSgREclKKlAiIpKVWtN9UCfMzHoDDwIXA9uB2939scyOKjlm1gH4JXAR0BtYC3zT3V+Itl8I3AecAvwd+Iy7v5+h4TZKtPjlMuBP7v7pqO1TwH8CfYH/B3wumkU/q5nZNcB3Ce9DCeF9mNcc3x8zG0L4nTsXOAz8CbjF3SvNbALh39IoYBXweXdfnKmx1iVayfszwFjgcXf/TNy2et+P6N/a/cDHgQPAXe7+32kdfB3qez1mdg7wfaCQcG/obOAmd98Sbc8BfgRcH+3qQeDf3D1t9ybpCCo59wHlQB4wE7jfzEZndkhJywWKgPOBHsC/A38wsyFm1pewiOS/E4rXfODJTA30BNxH3M190XvyK+Bawnt1gPBBmdXM7IPAfwGfBboB04D1zfj9+SVhzbeBwATC796NZtYeeAb4X6AX8Dvgmag9m2wmrEn32/jGJN6PO4DhwKnADOBfzezSNIz3eOp8PYT34NfAEMKY9wEPxW3/ImF5pPGERWE/AnwpxWOtRUdQx2FmXYCrgDHuXga8ZmbPEj4Eb8vo4JIQrWR8R1zT/5nZe4S/mvoAK9z9jwBmdgew3cxGuvvqdI+1MaIjjt3AG8DpUfNM4K/uPjfq8+/AKjPr5u77MjPSpHwPuNPd34oebwIwsy/SPN+focC97n4IKDGzvwGjCWu45QI/j/4Kv9vMvgFcAPwtU4NN5O5PA5jZZCA/btPHaPj9+Cfgs+6+C9hlZr8hHLlk9LXV93pqzqLUMLN7gTlxTdcBP3X34mj7T4EvAA+kesw1dAR1fCOAqmip+RpLCP/gmh0zyyO8phWE17CkZltUzNaR5a/NzLoDdwJfT9iU+HrWEY58R6RvdI1jZm2ByUA/M1trZsVmdq+ZdaKZvj/AL4BrzKyzmQ0GPkT4kB4NLE04RbSU7H89Nep9P8ysFzAofjvN73NiGuFzoUat10sGXo8K1PF1BfYktO0hnIppVsysHfAo8LvoL77m+tq+Dzzo7kUJ7c3x9eQB7QjXLaYSTolNBL5N83w9EP4KHw3sBYoJp8L+QvN9PTUaGn/XuMeJ27KemY0DvgPcGtec+Hr3AF2ja1NpoQJ1fGVA94S27oTztc2GmbUBfk84ovhq1NzsXlt0kf0i4Gd1bG52rwc4GP33Hnff4u7bgf8GPkwzfD3R79mLhGs1XQhhlV6Ea2zN7vUkaGj8ZXGPE7dlNTM7HXgBuNnd58VtSny93YEyhSSyyxogN0qM1RhP7UPhrBb9xfMg4a/1q9y9Itq0gvBaavp1AU4ju1/bdMJF3Y1mVgJ8A7jKzBZy7OsZBnQgvIdZKbpeUQzU9Y++Ob4/vYECwjWow+6+g3Dh/cOEcY9L+At8HNn9euLV+35E7+OW+O00g88JMzsVeBn4vrv/PmFzrddLBl6PZjNPgpk9QfgAuZ5wCuZ5YIq7Z/UvXw0ze4Aw7ouioEdNez9C7PxzwHOEi/Xnu/s5GRloEsysM7X/qvsGoWDdAPQH3gT+AVhISPTluvs1aR5mo5jZnYTrNP8AVADPEiK/d9PM3h8AM1tPSIf9hHCa6CFCovKzwLuEI8QHCBfcbwWGu3t5ZkZ7LDPLJYQ5vksIFXwBqCQcCdb7fpjZjwjR+isIfwzOIoQmMhqSaOD15AFzgQfc/cd1/NyXgZsJZyxihNs27nF3hSSyzI1AJ0J09nHghmZUnE4lREMnEBJVZdHXTHffRkgo/gewCzgbyOoPc3c/4O4lNV+E0xCH3H1b9J58mXCdrZRw/v/GDA43Wd8nxOXXEO4NWgT8R3N8fyIfAy4FthE+0CuBr0VF6ApC2m034YP+imwqTpFvE0693gZ8Ovr+20m8H98lhCbeJ1yH+3Gmi1OkztdD+IN7GPDduM+Fsrif+xXwV8K9hssJRflX6Ry4jqBERCQr6QhKRESykgqUiIhkJRUoERHJSipQIiKSlVSgREQkK6lAiYhIVtJs5iJpZGYPA8SvMZSm5+1GuMfqnJrZqRvo+2/AEHe/IS2DE6mHCpRIEzGz491UOJRwZ34m3Ay8dLziFPkl8J6Z/Ze7b0jtsETqp1N8Ik1nYNzXTwnTLsW3Fbn7HndPnBE7paIJXL9EmCz4uKK1s54nzPQgkjE6ghJpItHUSwBEU8aUx7dF7Q9HfT8TPd4A3ENYdfYiwmm4q4kW/QMGAA8TpgqKRT/TD/g5Ye6+SsJaSzc1sLT9OYR55ObGjWMY4UhpStS0Cpjp7mujx88Tll/4TiP+F4g0KR1BiWTeNwjzB04iTKr6KGES1Wuiry8TZgOv8SegirB+1HSOLp9enynAEnevimu7F9gOnElYMPFuoDpu+wJglJn1OdEXJXKydAQlknlPuvuTcGTZ7SeACe6+JGqbRVjt9Dkzm0ZY4v6CmoJjZl8ANpnZgMQjtsgphKUg4hUAj7m7R48TlyQpieu346RencgJUoESybz4mfFLo/+uTGjrF30/lnDab4+ZJe5nGEcLS7yOwOGEtl8CD5rZtcBLhCIZH6A4FP23UzIvQCQVdIpPJPMq4r6PAcQtKlnTVvNvtSuwmrB8SvzXcMIyHXXZAfSMb3D3+4FRhGtNHwJWm9mUuC69ov9ub+RrEWkyOoISaV6WEBZo3B0tD5+MpYTgRS3uvh74BfALM3uecL3rjWjzSMJy5etPdsAiJ0oFSqR5eYlwSvBpM/smsJlwTepj7v7len5mNnBq/DUqM/sZYQG6tYTrTOMIR1M1pgCzE4IVImmlU3wizYi7VxNWq30X+DOhWN1NWOG1vp/ZQihsn4hrbkdYln01YZXox4D747Z/goaTgSIppxV1RVoBM5tKWK57dM39VA30PRd4KOqrIyjJGB1BibQC7j4PuI8wo8Xx9AI+r+IkmaYjKBERyUo6ghIRkaykAiUiIllJBUpERLKSCpSIiGQlFSgREclKKlAiIpKV/j+4ovuE9+o3dAAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "dydt = gradient(results.y)\n", - "plot(dydt, label='dydt')\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Linear velocity (m/s)')" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.5496155290513908 meter/second" - ], - "text/latex": [ - "$0.5496155290513908 \\frac{meter}{second}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "linear_velocity = get_last_value(dydt) * m/s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now suppose the peak velocity is the limit; that is, we can't move the paper any faster than that.\n", - "\n", - "Nevertheless, we might be able to speed up the process by keeping the linear velocity at the maximum all the time.\n", - "\n", - "Write a slope function that keeps the linear velocity, `dydt`, constant, and computes the angular velocity, `omega`, accordingly.\n", - "\n", - "Run the simulation and see how much faster we could finish rolling the paper." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def slope_func(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object with theta, y, r\n", - " t: time\n", - " system: System object with r, k\n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " theta, y, r = state\n", - " unpack(system)\n", - " \n", - " dydt = 0.55 * radian / s\n", - " omega = dydt / r\n", - " drdt = k * omega\n", - " \n", - " return omega, dydt, drdt" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[[85.45454545454554]]
nfev548
njev0
nlu0
status1
messageA termination event occurred.
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events [[85.45454545454554]]\n", - "nfev 548\n", - "njev 0\n", - "nlu 0\n", - "status 1\n", - "message A termination event occurred.\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "results, details = run_ode_solver(system, slope_func, events=event_func, max_step=1*s)\n", - "details" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "85.45454545454554 second" - ], - "text/latex": [ - "$85.45454545454554 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "t_final = get_last_label(results) * s" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot_three(results)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/chap25soln.ipynb b/code/soln/chap25soln.ipynb deleted file mode 100644 index 1738bcb4c..000000000 --- a/code/soln/chap25soln.ipynb +++ /dev/null @@ -1,2241 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 25\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Teapots and Turntables" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Tables in Chinese restaurants often have a rotating tray or turntable that makes it easy for customers to share dishes. These turntables are supported by low-friction bearings that allow them to turn easily and glide. However, they can be heavy, especially when they are loaded with food, so they have a high moment of inertia.\n", - "\n", - "Suppose I am sitting at a table with a pot of tea on the turntable directly in front of me, and the person sitting directly opposite asks me to pass the tea. I push on the edge of the turntable with 1 Newton of force until it has turned 0.5 radians, then let go. The turntable glides until it comes to a stop 1.5 radians from the starting position. How much force should I apply for a second push so the teapot glides to a stop directly opposite me?\n", - "\n", - "The following figure shows the scenario, where `F` is the force I apply to the turntable at the perimeter, perpendicular to the moment arm, `r`, and `tau` is the resulting torque. The blue circle near the bottom is the teapot.\n", - "\n", - "![](diagrams/teapot.png)\n", - "\n", - "We'll answer this question in these steps:\n", - "\n", - "1. We'll use the results from the first push to estimate the coefficient of friction for the turntable.\n", - "\n", - "2. We'll use that coefficient of friction to estimate the force needed to rotate the turntable through the remaining angle.\n", - "\n", - "Our simulation will use the following parameters:\n", - "\n", - "1. The radius of the turntable is 0.5 meters, and its weight is 7 kg.\n", - "\n", - "2. The teapot weights 0.3 kg, and it sits 0.4 meters from the center of the turntable.\n", - "\n", - "As usual, I'll get units from Pint." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "newton" - ], - "text/latex": [ - "$newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "radian = UNITS.radian\n", - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram\n", - "N = UNITS.newton" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And store the parameters in a `Params` object." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
radius_disk0.5 meter
mass_disk7 kilogram
radius_pot0.4 meter
mass_pot0.3 kilogram
force1 newton
torque_friction0.2 meter * newton
theta_end0.5 radian
\n", - "
" - ], - "text/plain": [ - "radius_disk 0.5 meter\n", - "mass_disk 7 kilogram\n", - "radius_pot 0.4 meter\n", - "mass_pot 0.3 kilogram\n", - "force 1 newton\n", - "torque_friction 0.2 meter * newton\n", - "theta_end 0.5 radian\n", - "dtype: object" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(radius_disk=0.5*m,\n", - " mass_disk=7*kg,\n", - " radius_pot=0.4*m,\n", - " mass_pot=0.3*kg,\n", - " force=1*N,\n", - " torque_friction=0.2*N*m,\n", - " theta_end=0.5*radian)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`make_system` creates the initial state, `init`, and computes the total moment of inertia for the turntable and the teapot." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Make a system object.\n", - " \n", - " params: Params object\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " init = State(theta=0, omega=0)\n", - " \n", - " I_disk = mass_disk * radius_disk**2 / 2\n", - " I_pot = mass_pot * radius_pot**2\n", - " \n", - " return System(params, init=init, t_end=20*s,\n", - " I=I_disk+I_pot)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the `System` object we'll use for the first phase of the simulation, while I am pushing the turntable." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
radius_disk0.5 meter
mass_disk7 kilogram
radius_pot0.4 meter
mass_pot0.3 kilogram
force1 newton
torque_friction0.2 meter * newton
theta_end0.5 radian
inittheta 0\n", - "omega 0\n", - "dtype: int64
I0.923 kilogram * meter ** 2
t_end20 second
\n", - "
" - ], - "text/plain": [ - "radius_disk 0.5 meter\n", - "mass_disk 7 kilogram\n", - "radius_pot 0.4 meter\n", - "mass_pot 0.3 kilogram\n", - "force 1 newton\n", - "torque_friction 0.2 meter * newton\n", - "theta_end 0.5 radian\n", - "init theta 0\n", - "omega 0\n", - "dtype: int64\n", - "I 0.923 kilogram * meter ** 2\n", - "t_end 20 second\n", - "dtype: object" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system1 = make_system(params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Simulation\n", - "\n", - "When I stop pushing on the turntable, the angular acceleration changes abruptly. We could implement the slope function with an `if` statement that checks the value of `theta` and sets `force` accordingly. And for a coarse model like this one, that might be fine. But we will get more accurate results if we simulate the system in two phases:\n", - "\n", - "1. During the first phase, force is constant, and we run until `theta` is 0.5 radians.\n", - "\n", - "2. During the second phase, force is 0, and we run until `omega` is 0.\n", - "\n", - "Then we can combine the results of the two phases into a single `TimeFrame`.\n", - "\n", - "Here's the slope function we'll use:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object \n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " theta, omega = state\n", - " unpack(system)\n", - " \n", - " torque = radius_disk * force - torque_friction\n", - " alpha = torque / I\n", - " \n", - " return omega, alpha " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As always, we'll test the slope function before running the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(0, )" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(system1.init, 0*s, system1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's an event function that stops the simulation when `theta` reaches `theta_end`." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func1(state, t, system):\n", - " \"\"\"Stops when theta reaches theta_end.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object \n", - " \n", - " returns: difference from target\n", - " \"\"\"\n", - " theta, omega = state\n", - " unpack(system)\n", - " return theta - theta_end " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run the first phase." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[[1.7540429489230491]]
nfev128
njev0
nlu0
status1
messageA termination event occurred.
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events [[1.7540429489230491]]\n", - "nfev 128\n", - "njev 0\n", - "nlu 0\n", - "status 1\n", - "message A termination event occurred.\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results1, details1 = run_ode_solver(system1, slope_func,\n", - " events=event_func1, max_step=0.1*s)\n", - "details1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And look at the results." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
thetaomega
1.4111000.3235970.458646
1.5111000.3710870.491148
1.6111000.4218270.523651
1.7111000.4758170.556154
1.7540430.5000000.570111
\n", - "
" - ], - "text/plain": [ - " theta omega\n", - "1.411100 0.323597 0.458646\n", - "1.511100 0.371087 0.491148\n", - "1.611100 0.421827 0.523651\n", - "1.711100 0.475817 0.556154\n", - "1.754043 0.500000 0.570111" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results1.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Phase 2\n", - "\n", - "Before we run the second phase, we have to extract the final time and state of the first phase." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "1.7540429489230491 second" - ], - "text/latex": [ - "$1.7540429489230491 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_0 = get_last_label(results1) * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And make an initial `State` object for Phase 2." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
theta0.49999999999999994 radian
omega0.570111467688965 radian / second
\n", - "
" - ], - "text/plain": [ - "theta 0.49999999999999994 radian\n", - "omega 0.570111467688965 radian / second\n", - "dtype: object" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "theta, omega = get_last_value(results1)\n", - "init2 = State(theta=theta*radian, omega=omega*radian/s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And a new `System` object with `force=0`." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
radius_disk0.5 meter
mass_disk7 kilogram
radius_pot0.4 meter
mass_pot0.3 kilogram
force0
torque_friction0.2 meter * newton
theta_end0.5 radian
inittheta 0.49999999999999994 radian\n", - "ome...
I0.923 kilogram * meter ** 2
t_end20 second
t_01.7540429489230491 second
\n", - "
" - ], - "text/plain": [ - "radius_disk 0.5 meter\n", - "mass_disk 7 kilogram\n", - "radius_pot 0.4 meter\n", - "mass_pot 0.3 kilogram\n", - "force 0\n", - "torque_friction 0.2 meter * newton\n", - "theta_end 0.5 radian\n", - "init theta 0.49999999999999994 radian\n", - "ome...\n", - "I 0.923 kilogram * meter ** 2\n", - "t_end 20 second\n", - "t_0 1.7540429489230491 second\n", - "dtype: object" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system2 = System(system1, t_0=t_0, init=init2, force=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's an event function that stops when angular velocity is 0." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func2(state, t, system):\n", - " \"\"\"Stops when omega is 0.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object \n", - " \n", - " returns: omega\n", - " \"\"\"\n", - " theta, omega = state\n", - " return omega" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run the second phase." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[[4.385107372307622]]
nfev164
njev0
nlu0
status1
messageA termination event occurred.
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events [[4.385107372307622]]\n", - "nfev 164\n", - "njev 0\n", - "nlu 0\n", - "status 1\n", - "message A termination event occurred.\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results2, details2 = run_ode_solver(system2, slope_func,\n", - " events=event_func2, max_step=0.1*s)\n", - "details2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And check the results." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
thetaomega
4.0540431.2381257.173660e-02
4.1540431.2442165.006813e-02
4.2540431.2481392.839966e-02
4.3540431.2498956.731186e-03
4.3851071.250000-5.377643e-17
\n", - "
" - ], - "text/plain": [ - " theta omega\n", - "4.054043 1.238125 7.173660e-02\n", - "4.154043 1.244216 5.006813e-02\n", - "4.254043 1.248139 2.839966e-02\n", - "4.354043 1.249895 6.731186e-03\n", - "4.385107 1.250000 -5.377643e-17" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results2.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Pandas provides `combine_first`, which combines `results1` and `results2`." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
thetaomega
4.0540431.2381257.173660e-02
4.1540431.2442165.006813e-02
4.2540431.2481392.839966e-02
4.3540431.2498956.731186e-03
4.3851071.250000-5.377643e-17
\n", - "
" - ], - "text/plain": [ - " theta omega\n", - "4.054043 1.238125 7.173660e-02\n", - "4.154043 1.244216 5.006813e-02\n", - "4.254043 1.248139 2.839966e-02\n", - "4.354043 1.249895 6.731186e-03\n", - "4.385107 1.250000 -5.377643e-17" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results = results1.combine_first(results2)\n", - "results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can plot `theta` for both phases." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_theta(results):\n", - " plot(results.theta, label='theta')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Angle (rad)')\n", - " \n", - "plot_theta(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And `omega`." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_omega(results):\n", - " plot(results.omega, label='omega', color='C1')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Angular velocity (rad/s)')\n", - " \n", - "plot_omega(results)" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap25-fig01.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "subplot(2, 1, 1)\n", - "plot_theta(results)\n", - "subplot(2, 1, 2)\n", - "plot_omega(results)\n", - "savefig('figs/chap25-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Estimating friction\n", - "\n", - "Let's take the code from the previous section and wrap it in a function." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "def run_two_phases(force, torque_friction, params):\n", - " \"\"\"Run both phases.\n", - " \n", - " force: force applied to the turntable\n", - " torque_friction: friction due to torque\n", - " params: Params object\n", - " \n", - " returns: TimeFrame of simulation results\n", - " \"\"\"\n", - " # put the specified parameters into the Params object\n", - " params = Params(params, force=force, torque_friction=torque_friction)\n", - "\n", - " # run phase 1\n", - " system1 = make_system(params)\n", - " results1, details1 = run_ode_solver(system1, slope_func, \n", - " events=event_func1, max_step=0.1*s)\n", - "\n", - " # get the final state from phase 1\n", - " t_0 = get_last_label(results1) * s\n", - " theta, omega = get_last_value(results1)\n", - " init2 = State(theta=theta, omega=omega)\n", - " \n", - " # run phase 2\n", - " system2 = System(system1, t_0=t_0, init=init2, force=0)\n", - " results2, details2 = run_ode_solver(system2, slope_func, \n", - " events=event_func2, max_step=0.1*s)\n", - " \n", - " # combine and return the results\n", - " results = results1.combine_first(results2)\n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's test it with the same parameters." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
thetaomega
4.0540431.2381257.173660e-02
4.1540431.2442165.006813e-02
4.2540431.2481392.839966e-02
4.3540431.2498956.731186e-03
4.3851071.250000-5.377643e-17
\n", - "
" - ], - "text/plain": [ - " theta omega\n", - "4.054043 1.238125 7.173660e-02\n", - "4.154043 1.244216 5.006813e-02\n", - "4.254043 1.248139 2.839966e-02\n", - "4.354043 1.249895 6.731186e-03\n", - "4.385107 1.250000 -5.377643e-17" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "force = 1*N\n", - "torque_friction = 0.2*N*m\n", - "results = run_two_phases(force, torque_friction, params)\n", - "results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And check the results." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1.2499999999999993" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "theta_final = get_last_value(results.theta)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To estimate the coefficient of friction, we'll use `fsolve`, which doesn't always work well with units.\n", - "\n", - "So for the rest of this example, we'll run without units. Here's a version of the `Params` object with no units." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
radius_disk0.5
mass_disk7
radius_pot0.4
mass_pot0.3
force1
torque_friction0.2
theta_end0.5
\n", - "
" - ], - "text/plain": [ - "radius_disk 0.5\n", - "mass_disk 7\n", - "radius_pot 0.4\n", - "mass_pot 0.3\n", - "force 1\n", - "torque_friction 0.2\n", - "theta_end 0.5\n", - "dtype: object" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params_nodim = remove_units(params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the error function we'll use with `fsolve`. It takes a hypothetical value for `torque_friction` and returns the difference between `theta_final` and the observed result of the first push." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "def error_func1(torque_friction, params):\n", - " \"\"\"Error function for fsolve.\n", - " \n", - " torque_friction: hypothetical value\n", - " params: Params object\n", - " \n", - " returns: offset from target value\n", - " \"\"\"\n", - " force = 1\n", - " results = run_two_phases(force, torque_friction, params)\n", - " theta_final = get_last_value(results.theta)\n", - " print(torque_friction, theta_final)\n", - " return theta_final - 1.5" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing the error function." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.2 1.2499999999999993\n" - ] - }, - { - "data": { - "text/plain": [ - "-0.25000000000000067" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "guess = 0.2\n", - "error_func1(guess, params_nodim)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And running `fsolve`." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.2 1.2499999999999993\n", - "[0.2] 1.2499999999999993\n", - "[0.2] 1.2499999999999993\n", - "[0.2] 1.2499999999999993\n", - "[0.2] 1.2499999813735485\n", - "[0.16] 1.5625000139706928\n", - "[0.168] 1.4880952355608708\n", - "[0.16672] 1.4995201533449898\n", - "[0.16666624] 1.5000038400123035\n", - "[0.16666667] 1.499999998771198\n", - "[0.16666667] 1.4999999999999973\n" - ] - }, - { - "data": { - "text/plain": [ - "array([0.16666667])" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "res = fsolve(error_func1, guess, params_nodim)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result is an array, so we'll extract the first (and only) element." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.166666666666667" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "torque_friction = res[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And do a test run with the estimated value." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1.4999999999999973" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "force = 1\n", - "results = run_two_phases(force, torque_friction, params_nodim)\n", - "theta_final = get_last_value(results.theta)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Looks good.\n", - "\n", - "### Exercises\n", - "\n", - "Now finish off the example by estimating the force that delivers the teapot to the desired position.\n", - "\n", - "Write an error function that takes `force` and `params` and returns the offset from the desired angle." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def error_func2(force, params):\n", - " \"\"\"Error function for fsolve.\n", - " \n", - " force: hypothetical value\n", - " params: Params object\n", - " \n", - " returns: offset from target value\n", - " \"\"\"\n", - " # notice that this function uses the global value of torque_friction\n", - " results = run_two_phases(force, torque_friction, params)\n", - " theta_final = get_last_value(results.theta)\n", - " print(force, theta_final)\n", - " remaining_angle = np.pi - 1.5\n", - " return theta_final - remaining_angle" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test the error function with `force=1`" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1 1.4999999999999973\n" - ] - }, - { - "data": { - "text/plain": [ - "-0.14159265358979578" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "guess = 1\n", - "error_func2(guess, params_nodim)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And run `fsolve` to find the desired force." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1 1.4999999999999973\n", - "[1] 1.4999999999999973\n", - "[1.] 1.4999999999999973\n", - "[1.] 1.4999999999999973\n", - "[1.00000001] 1.500000022351739\n", - "[1.0943951] 1.6415926549966982\n", - "[1.0943951] 1.6415926535897938\n" - ] - }, - { - "data": { - "text/plain": [ - "array([1.0943951])" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "res = fsolve(error_func2, guess, params_nodim)" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1.6415926535897938" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "force = res[0]\n", - "results = run_two_phases(force, torque_friction, params_nodim)\n", - "theta_final = get_last_value(results.theta)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Now suppose my friend pours 0.1 kg of tea and puts the teapot back on the turntable at distance 0.3 meters from the center. If I ask for the tea back, how much force should they apply, over an arc of 0.5 radians, to make the teapot glide to a stop back in front of me? You can assume that torque due to friction is proportional to the total mass of the teapot and the turntable." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
radius_disk0.5
mass_disk7
radius_pot0.4
mass_pot0.3
force1
torque_friction0.2
theta_end0.5
\n", - "
" - ], - "text/plain": [ - "radius_disk 0.5\n", - "mass_disk 7\n", - "radius_pot 0.4\n", - "mass_pot 0.3\n", - "force 1\n", - "torque_friction 0.2\n", - "theta_end 0.5\n", - "dtype: object" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "params_nodim" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "7.3" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "mass_before = params_nodim.mass_pot + params_nodim.mass_disk" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
radius_disk0.5
mass_disk7
radius_pot0.3
mass_pot0.2
force1
torque_friction0.2
theta_end0.5
\n", - "
" - ], - "text/plain": [ - "radius_disk 0.5\n", - "mass_disk 7\n", - "radius_pot 0.3\n", - "mass_pot 0.2\n", - "force 1\n", - "torque_friction 0.2\n", - "theta_end 0.5\n", - "dtype: object" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "params_nodim2 = Params(params_nodim, mass_pot=0.2, radius_pot=0.3)" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "7.2" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "mass_after = params_nodim2.mass_pot + params_nodim2.mass_disk" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.166666666666667" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "torque_friction" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.16438356164383594" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "torque_friction2 = torque_friction * mass_after / mass_before" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
thetaomega
5.9276383.0296656.647228e-02
6.0276383.0353924.806427e-02
6.1276383.0392782.965626e-02
6.2276383.0413231.124824e-02
6.2887433.0416673.295975e-17
\n", - "
" - ], - "text/plain": [ - " theta omega\n", - "5.927638 3.029665 6.647228e-02\n", - "6.027638 3.035392 4.806427e-02\n", - "6.127638 3.039278 2.965626e-02\n", - "6.227638 3.041323 1.124824e-02\n", - "6.288743 3.041667 3.295975e-17" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "guess = 2\n", - "results = run_two_phases(guess, torque_friction2, params_nodim2)\n", - "results.tail()" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "subplot(2, 1, 1)\n", - "plot_theta(results)\n", - "subplot(2, 1, 2)\n", - "plot_omega(results)" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "# Note: this is so similar to error_func2, it would be better\n", - "# to generalize it, but for expediency, I will make a modified\n", - "# verison.\n", - "\n", - "def error_func3(force, params):\n", - " \"\"\"Error function for fsolve.\n", - " \n", - " force: hypothetical value\n", - " params: Params object\n", - " \n", - " returns: offset from target value\n", - " \"\"\"\n", - " # notice that this function uses the global value of torque_friction2\n", - " results = run_two_phases(force, torque_friction2, params)\n", - " theta_final = get_last_value(results.theta)\n", - " print(force, theta_final)\n", - " remaining_angle = np.pi\n", - " return theta_final - remaining_angle" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2 3.041666666666659\n", - "[2] 3.041666666666659\n", - "[2.] 3.041666666666659\n", - "[2.] 3.041666666666659\n", - "[2.00000003] 3.041666711991027\n", - "[2.06570475] 3.1415926480419096\n", - "[2.06570476] 3.1415926535897944\n" - ] - }, - { - "data": { - "text/plain": [ - "array([2.06570476])" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "res = fsolve(error_func3, guess, params_nodim2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "**Exercise:** Run these simulations with different values for the weight and size of the turntable, or weight and location of the teapot. What effect do they have on the results?\n", - "\n", - "If you do some analysis, you might find that you can answer the original question without doing any simulation." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/diagrams/RC_Divider.png b/code/soln/diagrams/RC_Divider.png deleted file mode 100644 index 83c136163..000000000 Binary files a/code/soln/diagrams/RC_Divider.png and /dev/null differ diff --git a/code/soln/diagrams/RC_Divider.svg b/code/soln/diagrams/RC_Divider.svg deleted file mode 100644 index 16834bdc9..000000000 --- a/code/soln/diagrams/RC_Divider.svg +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - R - C - V - in - V - out - - diff --git a/code/soln/diagrams/queue.png b/code/soln/diagrams/queue.png deleted file mode 100644 index 25f5d200a..000000000 Binary files a/code/soln/diagrams/queue.png and /dev/null differ diff --git a/code/soln/diagrams/spider-man.png b/code/soln/diagrams/spider-man.png deleted file mode 100644 index 81f8f591f..000000000 Binary files a/code/soln/diagrams/spider-man.png and /dev/null differ diff --git a/code/soln/diagrams/throwingaxe1.png b/code/soln/diagrams/throwingaxe1.png deleted file mode 100644 index 12f81a3fc..000000000 Binary files a/code/soln/diagrams/throwingaxe1.png and /dev/null differ diff --git a/code/soln/diagrams/throwingaxe2.png b/code/soln/diagrams/throwingaxe2.png deleted file mode 100644 index dd34730b9..000000000 Binary files a/code/soln/diagrams/throwingaxe2.png and /dev/null differ diff --git a/code/soln/duck_soln.ipynb b/code/soln/duck_soln.ipynb deleted file mode 100644 index 93dcf3907..000000000 --- a/code/soln/duck_soln.ipynb +++ /dev/null @@ -1,206 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Copyright 2018 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## The duck problem\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
density_duck0.3
density_water1.0
r5.0
\n", - "
" - ], - "text/plain": [ - "density_duck 0.3\n", - "density_water 1.0\n", - "r 5.0\n", - "dtype: float64" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = System (\n", - " density_duck = 0.3, # g / cm^3 \n", - " density_water = 1, # g / cm^3\n", - " r = 5, # cm\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def error_func(d, system):\n", - " print(d)\n", - " unpack(system)\n", - " \n", - " volume_duck = 4 / 3 * pi * r**3\n", - " mass_duck = volume_duck * density_duck\n", - " volume_water = pi / 3 * (3 * r * d**2 - d**3)\n", - " mass_water = volume_water * density_water\n", - " return mass_duck - mass_water" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3\n" - ] - }, - { - "data": { - "text/plain": [ - "43.982297150257125" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "error_func(3, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3\n", - "[3]\n", - "[3.]\n", - "[3.]\n", - "[3.00000004]\n", - "[3.66666666]\n", - "[3.63105175]\n", - "[3.63257187]\n", - "[3.63257491]\n", - "[3.63257491]\n" - ] - }, - { - "data": { - "text/plain": [ - "array([3.63257491])" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fsolve(error_func, 3, system)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/filter_soln.ipynb b/code/soln/filter_soln.ipynb deleted file mode 100644 index 0787d2c71..000000000 --- a/code/soln/filter_soln.ipynb +++ /dev/null @@ -1,1685 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Low pass filter\n", - "\n", - "The following circuit diagram (from [Wikipedia](https://en.wikipedia.org/wiki/File:RC_Divider.svg)) shows a low-pass filter built with one resistor and one capacitor. \n", - "\n", - "![Low-pass filter circuit diagram](diagrams/RC_Divider.png)\n", - "\n", - "A \"filter\" is a circuit takes a signal, $V_{in}$, as input and produces a signal, $V_{out}$, as output. In this context, a \"signal\" is a voltage that changes over time.\n", - "\n", - "A filter is \"low-pass\" if it allows low-frequency signals to pass from $V_{in}$ to $V_{out}$ unchanged, but it reduces the amplitude of high-frequency signals.\n", - "\n", - "By applying the laws of circuit analysis, we can derive a differential equation that describes the behavior of this system. By solving the differential equation, we can predict the effect of this circuit on any input signal.\n", - "\n", - "Suppose we are given $V_{in}$ and $V_{out}$ at a particular instant in time. By Ohm's law, which is a simple model of the behavior of resistors, the instantaneous current through the resistor is:\n", - "\n", - "$ I_R = (V_{in} - V_{out}) / R $\n", - "\n", - "where $R$ is resistance in ohms.\n", - "\n", - "Assuming that no current flows through the output of the circuit, Kirchhoff's current law implies that the current through the capacitor is:\n", - "\n", - "$ I_C = I_R $\n", - "\n", - "According to a simple model of the behavior of capacitors, current through the capacitor causes a change in the voltage across the capacitor:\n", - "\n", - "$ I_C = C \\frac{d V_{out}}{dt} $\n", - "\n", - "where $C$ is capacitance in farads (F).\n", - "\n", - "Combining these equations yields a differential equation for $V_{out}$:\n", - "\n", - "$ \\frac{d }{dt} V_{out} = \\frac{V_{in} - V_{out}}{R C} $\n", - "\n", - "Follow the instructions blow to simulate the low-pass filter for input signals like this:\n", - "\n", - "$ V_{in}(t) = A \\cos (2 \\pi f t) $\n", - "\n", - "where $A$ is the amplitude of the input signal, say 5 V, and $f$ is the frequency of the signal in Hz." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Params and System objects\n", - "\n", - "Here's a `Params` object to contain the quantities we need. I've chosen values for `R1` and `C1` that might be typical for a circuit that works with audio signal." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "second" - ], - "text/latex": [ - "$second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ohm = UNITS.ohm\n", - "farad = UNITS.farad\n", - "volt = UNITS.volt\n", - "Hz = UNITS.Hz\n", - "second = UNITS.second" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
R11000000.0 ohm
C11e-09 farad
A5 volt
f1000 hertz
\n", - "
" - ], - "text/plain": [ - "R1 1000000.0 ohm\n", - "C1 1e-09 farad\n", - "A 5 volt\n", - "f 1000 hertz\n", - "dtype: object" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(\n", - " R1 = 1e6 * ohm,\n", - " C1 = 1e-9 * farad,\n", - " A = 5 * volt,\n", - " f = 1000 * Hz)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Normally I would recommend running this computation with units, but for this example it turns out to be too slow.\n", - "\n", - "Here's a `Params` object with no units." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
R11.000000e+06
C11.000000e-09
A5.000000e+00
f1.000000e+03
\n", - "
" - ], - "text/plain": [ - "R1 1.000000e+06\n", - "C1 1.000000e-09\n", - "A 5.000000e+00\n", - "f 1.000000e+03\n", - "dtype: float64" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(\n", - " R1 = 1e6, # ohm\n", - " C1 = 1e-9, # farad\n", - " A = 5, # volt\n", - " f = 1000, # Hz\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can pass the `Params` object `make_system` which computes some additional parameters and defines `init`.\n", - "\n", - "* `omega` is the frequency of the input signal in radians/second.\n", - "\n", - "* `tau` is the time constant for this circuit, which is the time it takes to get from an initial startup phase to \n", - "\n", - "* `cutoff` is the cutoff frequency for this circuit (in Hz), which marks the transition from low frequency signals, which pass through the filter unchanged, to high frequency signals, which are attenuated.\n", - "\n", - "* `t_end` is chosen so we run the simulation for 4 cycles of the input signal." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Makes a System object for the given conditions.\n", - " \n", - " params: Params object\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " init = State(V_out = 0)\n", - " omega = 2 * np.pi * f\n", - " tau = R1 * C1\n", - " cutoff = 1 / R1 / C1 / 2 / np.pi\n", - " t_end = 4 / f\n", - " ts = linspace(0, t_end, 401)\n", - " \n", - " return System(R1=R1, C1=C1, A=A, f=f,\n", - " init=init, t_end=t_end, ts=ts,\n", - " omega=omega, tau=tau, cutoff=cutoff)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's make a `System`" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
R11e+06
C11e-09
A5
f1000
initV_out 0\n", - "dtype: int64
t_end0.004
ts[0.0, 1e-05, 2e-05, 3.0000000000000004e-05, 4e...
omega6283.19
tau0.001
cutoff159.155
\n", - "
" - ], - "text/plain": [ - "R1 1e+06\n", - "C1 1e-09\n", - "A 5\n", - "f 1000\n", - "init V_out 0\n", - "dtype: int64\n", - "t_end 0.004\n", - "ts [0.0, 1e-05, 2e-05, 3.0000000000000004e-05, 4e...\n", - "omega 6283.19\n", - "tau 0.001\n", - "cutoff 159.155\n", - "dtype: object" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Write a slope function that takes as an input a `State` object that contains `V_out`, and returns the derivative of `V_out`." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def slope_func(state, t, system):\n", - " \"\"\"Compute derivatives of the state.\n", - " \n", - " state: position, velocity\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: derivatives of y and v\n", - " \"\"\"\n", - " V_out, = state\n", - " \n", - " # If we're running we units, we have to apply them here\n", - " #V_out = require_units(V_out, volt)\n", - " #t = require_units(t, second)\n", - " \n", - " unpack(system)\n", - " \n", - " V_in = A * np.cos(omega * t)\n", - " \n", - " V_R1 = V_in - V_out\n", - " \n", - " I_R1 = V_R1 / R1\n", - " I_C1 = I_R1\n", - "\n", - " dV_out = I_C1 / C1\n", - " \n", - " return dV_out" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test the slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "5000.0" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And then run the simulation. I suggest using `t_eval=ts` to make sure we have enough data points to plot and analyze the results. " - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[]
nfev110
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events []\n", - "nfev 110\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results, details = run_ode_solver(system, slope_func, t_eval=system.ts)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a function you can use to plot `V_out` as a function of time." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_results(results):\n", - " xs = results.V_out.index\n", - " ys = results.V_out.values\n", - "\n", - " t_end = get_last_label(results)\n", - " if t_end < 10:\n", - " xs *= 1000\n", - " xlabel = 'Time (ms)'\n", - " else:\n", - " xlabel = 'Time (s)'\n", - " \n", - " plot(xs, ys)\n", - " decorate(xlabel=xlabel,\n", - " ylabel='$V_{out}$ (volt)',\n", - " legend=False)\n", - " \n", - "plot_results(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If things have gone according to plan, the amplitude of the output signal should be about 0.8 V.\n", - "\n", - "Also, you might notice that it takes a few cycles for the signal to get to the full amplitude. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Sweeping frequency\n", - "\n", - "Plot `V_out` looks like for a range of frequencies:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "fs = [1, 10, 100, 1000, 10000, 100000]\n", - "\n", - "for i, f in enumerate(fs):\n", - " system = make_system(Params(params, f=f))\n", - " results, details = run_ode_solver(system, slope_func, t_eval=system.ts)\n", - " subplot(3, 2, i+1)\n", - " plot_results(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "At low frequencies, notice that there is an initial \"transient\" before the output gets to a steady-state sinusoidal output. The duration of this transient is a small multiple of the time constant, `tau`, which is 1 ms." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Estimating the output ratio\n", - "\n", - "Let's compare the amplitudes of the input and output signals. Below the cutoff frequency, we expect them to be about the same. Above the cutoff, we expect the amplitude of the output signal to be smaller.\n", - "\n", - "We'll start with a signal at the cutoff frequency, `f=1000` Hz." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "system = make_system(Params(params, f=1000))\n", - "results, details = run_ode_solver(system, slope_func, t_eval=system.ts)\n", - "V_out = results.V_out\n", - "plot_results(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function computes `V_in` as a `TimeSeries`:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "def compute_vin(results, system):\n", - " \"\"\"Computes V_in as a TimeSeries.\n", - " \n", - " results: TimeFrame with simulation results\n", - " system: System object with A and omega\n", - " \n", - " returns: TimeSeries\n", - " \"\"\"\n", - " unpack(system)\n", - " V_in = A * np.cos(omega * results.index)\n", - " return TimeSeries(V_in, results.index, name='V_in')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the input and output look like. Notice that the output is not just smaller; it is also \"out of phase\"; that is, the peaks of the output are shifted to the right, relative to the peaks of the input." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "V_in = compute_vin(results, system)\n", - "\n", - "plot(V_out)\n", - "plot(V_in)\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='V (volt)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function estimates the amplitude of a signal by computing half the distance between the min and max." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "def estimate_A(series):\n", - " \"\"\"Estimate amplitude.\n", - " \n", - " series: TimeSeries\n", - " \n", - " returns: amplitude in volts\n", - " \"\"\"\n", - " return (series.max() - series.min()) / 2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The amplitude of `V_in` should be near 5 (but not exact because we evaluated it at a finite number of points)." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "5.0" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A_in = estimate_A(V_in)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The amplitude of `V_out` should be lower." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.8129812004177154" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A_out = estimate_A(V_out)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the ratio between them." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.16259624008354306" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ratio = A_out / A_in" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Encapsulate the code we have so far in a function that takes two TimeSeries objects and returns the ratio between their amplitudes." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def estimate_ratio(V1, V2):\n", - " \"\"\"Estimate the ratio of amplitudes.\n", - " \n", - " V1: TimeSeries\n", - " V2: TimeSeries\n", - " \n", - " returns: amplitude ratio\n", - " \"\"\"\n", - " a1 = estimate_A(V1)\n", - " a2 = estimate_A(V2)\n", - " return a1 / a2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And test your function." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.16259624008354306" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "estimate_ratio(V_out, V_in)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Estimating phase offset\n", - "\n", - "The delay between the peak of the input and the peak of the output is call a \"phase shift\" or \"phase offset\", usually measured in fractions of a cycle, degrees, or radians.\n", - "\n", - "To estimate the phase offset between two signals, we can use cross-correlation. Here's what the cross-correlation looks like between `V_out` and `V_in`:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "corr = np.correlate(V_out, V_in, mode='same')\n", - "corr = TimeSeries(corr, V_in.index)\n", - "plot(corr, color='C5')\n", - "decorate(xlabel='Lag (s)',\n", - " ylabel='Correlation')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The location of the peak in the cross correlation is the estimated shift between the two signals, in seconds." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.00222" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "peak_time = corr.idxmax()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can express the phase offset as a multiple of the period of the input signal:" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.001" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "period = 1 / system.f" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2.22" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "peak_time / period" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We don't care about whole period offsets, only the fractional part, which we can get using `modf`:" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.2200000000000002" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "frac, whole = np.modf(peak_time / period)\n", - "frac" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, we can convert from a fraction of a cycle to degrees:" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "79.20000000000007" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "frac * 360" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Encapsulate this code in a function that takes two `TimeSeries` objects and a `System` object, and returns the phase offset in degrees.\n", - "\n", - "Note: by convention, if the output is shifted to the right, the phase offset is negative." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def estimate_offset(V1, V2, system):\n", - " \"\"\"Estimate phase offset.\n", - " \n", - " V1: TimeSeries\n", - " V2: TimeSeries\n", - " system: System object with f\n", - " \n", - " returns: amplitude ratio\n", - " \"\"\"\n", - " corr = np.correlate(V1, V2, mode='same')\n", - " corr = TimeSeries(corr, V1.index)\n", - " peak = corr.idxmax()\n", - " period = 1 / system.f\n", - " frac, whole = np.modf(peak / period)\n", - " return -frac * 360" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your function." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "-79.20000000000007" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "estimate_offset(V_out, V_in, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Sweeping frequency again\n", - "\n", - "**Exercise:** Write a function that takes as parameters an array of input frequencies and a `Params` object.\n", - "\n", - "For each input frequency it should run a simulation and use the results to estimate the output ratio (dimensionless) and phase offset (in degrees).\n", - "\n", - "It should return two `SweepSeries` objects, one for the ratios and one for the offsets." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def sweep_frequency(fs, params):\n", - " ratios = SweepSeries()\n", - " offsets = SweepSeries()\n", - "\n", - " for i, f in enumerate(fs):\n", - " system = make_system(Params(params, f=f))\n", - " results, details = run_ode_solver(system, slope_func, t_eval=system.ts)\n", - " V_out = results.V_out\n", - " V_in = compute_vin(results, system)\n", - " ratios[f] = estimate_ratio(V_out, V_in)\n", - " offsets[f] = estimate_offset(V_out, V_in, system)\n", - " return ratios, offsets" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Run your function with these frequencies." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1.00000000e+00, 3.16227766e+00, 1.00000000e+01, 3.16227766e+01,\n", - " 1.00000000e+02, 3.16227766e+02, 1.00000000e+03, 3.16227766e+03,\n", - " 1.00000000e+04])" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fs = 10 ** linspace(0, 4, 9)" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "ratios, offsets = sweep_frequency(fs, params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can plot output ratios like this:" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3XmcXFWZ//FP9ZJOd9ZOk30hCZCHNYR9RGVJRn4IDgrCKCgqUVGB6DjOKAFHxQ10ZpCRjCYKyuAuIzjDOAJKFGVUFllCWB6QdPad7Emnt6rfH+dWpbp6q66urbu/79erX11176l7z61O6qlz7jnPiSUSCURERMpNRakrICIi0hUFKBERKUsKUCIiUpYUoEREpCwpQImISFlSgBIRkbKkACUiImVJAUpERMqSApSIiJQlBSgRESlLVaWuQDGYWQ1wGrAJaC9xdUREhqJKYDLwhLs3Z/OCIRGgCMHp96WuhIiI8Ebg0WwKDpUAtQngBz/4AZMmTSp1XUREhpzNmzfzrne9C6LP42wMlQDVDjBp0iSmTZtW6rqIiAxlWd9m0SAJEREpSwpQIiJSlhSgRESkLClAiYhIWSrJIAkzuwS4FjgVGO3usV7KzwG+BZwBbAZucve7Cl1PEcndnj172Lp1K62traWuihRBdXU1EyZMYPTo0Xk7ZqlG8dUBy4FfA1/uqaCZVQO/AJ4izGc6A/iWmTW6+yOFrihAIpGgLd5GdWV1MU4nMuDt2bOHLVu2MHXqVGpra4nFevwOKgNcIpGgqamJDRs2AOQtSJUkQLn79wHM7Jwsir8ZmArMc/f9wEozOxtYBBQ8QB1sa+bnLz7I3uZ9nDPrdRzVMKvQpxQZ8LZu3crUqVOpq6srdVWkCGKxGHV1dUydOpWNGzfmLUANhHtQpwOPRcEp6WFCS6rg9jTvY8/BvSQSCf649s8kEolinFZkQGttbaW2trbU1ZAiq62tzWuX7kAIUBOArRnbtkXbC+6w2nqqKkND82BbM3ub9xXjtCIDnrr1hp58/80HQoAq6b/yiooKJo44LPV824EdJayNiMjQMRAC1BY6t5bG07lVVTDjRzSkHm/b/1qxTisiUnS33347V155ZamrAQyMAPU4cIaZpd9tnQ88VqwKjKsdm3q8R118IpJh/fr1mBnr16/P+7Hnz5/Pvffem/fjAlx55ZXcfvvtHbYtXLiw07ZSKdU8qHHADODI6Pm8aNcLwDzgbmCBu28AHgA2Anea2RcJgyMuB95UrPrWVQ9PPT7YdrBYpxURyUlLSwvDhg3L6bUjRozIc21yV6oW1EXA08C3o+dPRz9TCHOkDKgGcPcW4ELCQld/Bj4DXF2sOVAAw9MCVFNrVutsicgA0tTUxKc//WlOO+00TjrpJD72sY+xY8eh+81dtTTSWzYLFixI/TazVNn58+dz55138sEPfpC5c+fy5je/mSeffDJ1jK66066//nquv/761Hk3bNjA4sWLMbNuu97uvfde5s+fz89//nPmz5/P/PnzAVi2bBnnn38+J554Iueddx533313h/M8/vjjLFmyBDNLvSazTr29N4VUqnlQdwF3dbN7NRkDI9zdgXMKWaeeDK+qST0+2KYAJTLY3HLLLTzxxBN885vfpLa2lptuuonFixezbNmyrF5/zz33cNlll3HPPfcwefLkDvO/li1bxic+8Qmuv/56fvjDH3LNNdewfPlyRo4c2etxb7/9dt761reycOFCLrjgAqqru08WsH37du6//36+8Y1vUFlZCcCwYcP44he/yMSJE1m5ciU33HADhx9+OGeffTY33ngjq1ev5qSTTmLhwoWp1+T7vemPobIeVL+kB6jmtmbiiTgVsYFw+06kfKzY/CJPblxBW3tbwc9VVVnFqVPmMnfSMb2W3bdvHz/72c9YunQpp556KgA333wzF1xwAY2Njcya1fvk/HHjxqV+jx8/vsO+s846i3e84x0A3HDDDSxfvpz777+fyy+/vNfjjh07lsrKSkaNGtXpuJmam5v50pe+1GFR1quuuir1ePr06TzxxBM88MADnH322YwaNYrq6mrq6uq6PXY+3pv+0KdsFipiFdR0CFItJayNyMC0YsuLRQlOAG3tbazY8mJWZdevX09rayvz5s1LbTviiCMYPXo0q1at6ndd5s6dm3pcWVnJcccdR2NjY7+Pm2ncuHGdVgx/5JFHuOKKKzjzzDM56aST+OlPf8rmzZuzPmah35veKEBlKb0V1aSBEiJ9NnfiMalJ74VWVVnF3Im9t56ArLLDxGKxTuWyzZjQ0+TV/hw3U2bmjnXr1nHttdfyute9jmXLlnHfffdxySWX0NaW/ZeEUmfOURdflmqra9gdxSXdhxLpu7mTjsmqy63Ypk+fTlVVFc888wxveMMbAHj11VfZs2cPs2fPBkLrZPv27anX7Nixo8Pz5L2heDze6fgrVqxIPY7H47zwwgu8/vWvTx1327ZtHcq//PLLHHfccannVVVVtLdnvUp6yvPPP8+IESNYtGhRalsymWu2x87mvSkktaCy1GGgRKtaUCKDxciRI3n729/OF77wBZ588kmef/55Fi9ezFlnnZW6x3Laaafx4IMP8qc//Ql358Ybb+wwjLuhoYGamhr+8Ic/sGPHDpqamlL7HnnkEe655x5WrVrFl7/8Zfbu3cvf/M3fAHDqqaeyZs0afvKTn7B69WpuvfXWTkFkypQpPPXUU2zbto29e/dmfV0zZsxg9+7d3HfffaxZs4alS5fy9NNPdzr2ihUr2LJlC7t3787pvSkkBagsDa9KG2quFpTIoPKpT32KU045hQ9/+MO8+93vZtKkSXzlK19J7b/ssstYsGAB1113HR/60Id4y1veQkPDoQwzVVVVfOpTn2LJkiWceeaZ3HHHHal9V199NQ888ABvfetbefTRR1myZElqBN+cOXO4/vrruf3227n00kuJx+O86U0dp3hed911PPPMM5xzzjlcc801WV/Tsccey8c//nG++tWvcvHFF7N27VquuOKKDmWuuuoqdu3axYIFC7j44otzem8KKVbqPsZiMLOZQOPDDz/MtGnTcjrG4+uf4ZlNzwNwytS5nDLlhPxVUGSQefHFFznmmPLrziu2+fPnc91113HJJZeUuipF093ffv369cn5YrPcfXU2x1ILKku1HSbrqotPRKTQFKCypMm6IiLFpVF8WaqtSs/HpwAlIr1bvnx5qaswoKkFlaXh1ektKHXxiYgUmgJUljpM1NU9KJFeDYUBWNJRvv/mClBZGp7Rxaf/fCLdq66u7jAXSIaGpqamHhPa9pUCVJaqKipTaVoSiQQt7bmlIxEZCiZMmMCGDRs4cOCAvswNAYlEggMHDrBhwwYmTMhcAD13GiTRB7VVw9nbHlbUbWo7SE1VbguCiQx2o0ePBmDjxo0555aTgaW6upqJEyem/vb5oADVB8OratgbLfmukXwiPRs9enReP6xk6FEXXx+kT9Y9qJV1RUQKSgGqDzpO1tVIPhGRQlKA6gOtCSUiUjwKUH3QoYtP96BERApKAaoPOk7WVYASESkkBag+0D0oEZHiUYDq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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(ratios, color='C2', label='output ratio')\n", - "decorate(xlabel='Frequency (Hz)',\n", - " ylabel='$V_{out} / V_{in}$')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But it is useful and conventional to plot ratios on a log-log scale. The vertical gray line shows the cutoff frequency." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_ratios(ratios, system):\n", - " \"\"\"Plot output ratios.\n", - " \"\"\"\n", - " plt.axvline(system.cutoff, color='gray', alpha=0.4)\n", - " plot(ratios, color='C2', label='output ratio')\n", - " decorate(xlabel='Frequency (Hz)',\n", - " ylabel='$V_{out} / V_{in}$',\n", - " xscale='log', yscale='log')" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_ratios(ratios, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This plot shows the cutoff behavior more clearly. Below the cutoff, the output ratio is close to 1. Above the cutoff, it drops off linearly, on a log scale, which indicates that output ratios for high frequencies are practically 0.\n", - "\n", - "Here's the plot for phase offset, on a log-x scale:" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_offsets(offsets, system):\n", - " \"\"\"Plot phase offsets.\n", - " \"\"\"\n", - " plt.axvline(system.cutoff, color='gray', alpha=0.4)\n", - " plot(offsets, color='C9')\n", - " decorate(xlabel='Frequency (Hz)',\n", - " ylabel='Phase offset (degree)',\n", - " xscale='log')" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_offsets(offsets, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For low frequencies, the phase offset is near 0. For high frequencies, it approaches 90 degrees." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Analysis\n", - "\n", - "By analysis we can show that the output ratio for this signal is\n", - "\n", - "$A = \\frac{1}{\\sqrt{1 + (R C \\omega)^2}}$ \n", - "\n", - "where $\\omega = 2 \\pi f$, and the phase offset is\n", - "\n", - "$ \\phi = \\arctan (- R C \\omega)$ \n", - "\n", - "**Exercise:** Write functions that take an array of input frequencies and returns $A(f)$ and $\\phi(f)$ as `SweepSeries` objects. Plot these objects and compare them with the results from the previous section.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def output_ratios(fs, system):\n", - " unpack(system)\n", - " omegas = 2 * np.pi * fs\n", - " rco = R1 * C1 * omegas\n", - " A = 1 / np.sqrt(1 + rco**2)\n", - " return SweepSeries(A, fs)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your function:" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
1.0000000.999980
3.1622780.999803
10.0000000.998032
31.6227770.980827
100.0000000.846733
316.2277660.449565
1000.0000000.157177
3162.2776600.050266
10000.0000000.015913
\n", - "
" - ], - "text/plain": [ - "1.000000 0.999980\n", - "3.162278 0.999803\n", - "10.000000 0.998032\n", - "31.622777 0.980827\n", - "100.000000 0.846733\n", - "316.227766 0.449565\n", - "1000.000000 0.157177\n", - "3162.277660 0.050266\n", - "10000.000000 0.015913\n", - "dtype: float64" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A = output_ratios(fs, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def phase_offsets(fs, system):\n", - " omegas = 2 * np.pi * fs\n", - " rco = R1 * C1 * omegas\n", - " phi = np.arctan(-rco) * 180 / np.pi\n", - " return SweepSeries(phi, fs)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your function:" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
1.000000-0.359995
3.162278-1.138270
10.000000-3.595274
31.622777-11.237841
100.000000-32.141908
316.227766-63.284248
1000.000000-80.956939
3162.277660-87.118780
10000.000000-89.088186
\n", - "
" - ], - "text/plain": [ - "1.000000 -0.359995\n", - "3.162278 -1.138270\n", - "10.000000 -3.595274\n", - "31.622777 -11.237841\n", - "100.000000 -32.141908\n", - "316.227766 -63.284248\n", - "1000.000000 -80.956939\n", - "3162.277660 -87.118780\n", - "10000.000000 -89.088186\n", - "dtype: float64" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "phi = phase_offsets(fs, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot the theoretical results along with the simulation results and see if they agree." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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vj7Lczi1VSUThOM4BPyBs7dKEiEvPffM67OAs43qU3iWdUBDSyPHakopq6+ARB+t4Xbq2xt7O610R1u2AIFggIb2RhraGzlUdwSA6poaejq7yUrMYO7085g+pbA3vOL8wdM+p8hYe/DBB9m6dSu/+MUvvC5l/AWUtbbTGPM27n1OGwCMMUXALGCLh6XJBJeRkk5GSjqLZr3XjAxHwjR1tlDX1sAe9lLeUElte/0pnVcAuru66A51U9FSBfV+jrSW9q5LiU+mp6mL9PhUCjKmUpQ/i6zkDHXKGCeOHz/OunXreOmll0Z8bMO1a9dy7733csstt4zocQHuvPNOVq5cyX333de77O677+bOO+8c8ecajpgMKGNMFm7nhvOij09cGNprre0CHsK9AXcHUAL8K/CytXaPB+XKJOZzfGQkpJGRkMbsrPd6NXaFuqlvb6Au+lXbWk9ZuCy61iEueNLtBZ2tHKs6RiQShqMwrXYawWCQzIR0spMyyUrMINjjpzA7n9QEXSaUs+vq6jrlFpfBOnH7RyyI1bdnNwE7gB9HH++IfhUAWGt/BnwbeBjYDDQDt419mSKnF+cPMjUlh/k553PZjBXcNP9a/uGDX+B/rruHW5feyMXTl3FedhFZSW4rKdTT44YTbhd9n99POBymtq2ed2uK2XT0TX655fd8988P8q2nf8B/73+RTUff5FDdEcLhsMf/2vGvvb2dr3zlK6xYsYKlS5fyt3/7t9TVvdfL88477+TBBx/st8/atWt54oknAFi3bl3vd2NM77Zr167lpz/9KZ/61KdYvHgx73vf+9i+fXvvMR588MFTWiv3338/999/f+/zlpaW8sADD2CMOWPL5oknnmDt2rU89dRTrF27lrVr1wLwyCOPcP3113PhhRdy7bXX8uijj/Z7nq1bt/LQQw9hjOnd5+SaBjo3oykmW1DW2p8DPx9gm2/jhpTIuODz+cjJmEJORv/u66FwiOrmWg6kHaKisYrmnlbi4xJp7dONvrvrvc+3uglR1lxJWXMleyohN2UK62ZfRmp8ypj9Wyaa73znO2zbto0f/ehHJCYm8vWvf50HHniARx55ZFD7P/bYY9x666089thj5Ofn97v/65FHHuELX/gC999/P7/+9a/57Gc/y/r163tvZD+bBx98kA9+8IPcfffd3HDDDQSDZ/6ssqamhqeffpof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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(A, ':', color='gray')\n", - "plot_ratios(ratios, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(phi, ':', color='gray')\n", - "plot_offsets(offsets, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For the phase offsets, there are small differences between the theoretical results and our estimates, but that is probably because it is not easy to estimate phase offsets precisely from numerical results." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Consider modifying this notebook to model a [first order high-pass filter](https://en.wikipedia.org/wiki/High-pass_filter#First-order_continuous-time_implementation), a [two-stage second-order low-pass filter](https://www.electronics-tutorials.ws/filter/filter_2.html), or a [passive band-pass filter](https://www.electronics-tutorials.ws/filter/filter_4.html)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/insulin_soln.ipynb b/code/soln/insulin_soln.ipynb deleted file mode 100644 index 73435f39b..000000000 --- a/code/soln/insulin_soln.ipynb +++ /dev/null @@ -1,1151 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Insulin minimal model\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Data\n", - "\n", - "We have data from Pacini and Bergman (1986), \"MINMOD: a computer program to calculate insulin sensitivity and pancreatic responsivity from the frequently sampled intravenous glucose tolerance test\", *Computer Methods and Programs in Biomedicine*, 23: 113-122.." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "data = pd.read_csv('data/glucose_insulin.csv', index_col='time');" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The return value from `interpolate` is a function." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The insulin minimal model\n", - "\n", - "In addition to the glucose minimal mode, Pacini and Bergman present an insulin minimal model, in which the concentration of insulin, $I$, is governed by this differential equation:\n", - "\n", - "$ \\frac{dI}{dt} = -k I(t) + \\gamma (G(t) - G_T) t $" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Write a version of `make_system` that takes the parameters of this model, `I0`, `k`, `gamma`, and `G_T` as parameters, along with a `DataFrame` containing the measurements, and returns a `System` object suitable for use with `run_simulation` or `run_odeint`.\n", - "\n", - "Use it to make a `System` object with the following parameters:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
I0360.000
k0.250
gamma0.004
G_T80.000
\n", - "
" - ], - "text/plain": [ - "I0 360.000\n", - "k 0.250\n", - "gamma 0.004\n", - "G_T 80.000\n", - "dtype: float64" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(I0 = 360,\n", - " k = 0.25,\n", - " gamma = 0.004,\n", - " G_T = 80)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def make_system(params, data):\n", - " I0, k, gamma, G_T = params\n", - " init = State(I=I0)\n", - " \n", - " t_0 = get_first_label(data)\n", - " t_end = get_last_label(data)\n", - "\n", - " system = System(k=k, gamma=gamma, G_T=G_T,\n", - " init=init, t_0=t_0, t_end=t_end,\n", - " G=interpolate(data.glucose))\n", - "\n", - " return system" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
k0.25
gamma0.004
G_T80
initI 360.0\n", - "dtype: float64
t_00
t_end182
G<scipy.interpolate.interpolate.interp1d object...
\n", - "
" - ], - "text/plain": [ - "k 0.25\n", - "gamma 0.004\n", - "G_T 80\n", - "init I 360.0\n", - "dtype: float64\n", - "t_0 0\n", - "t_end 182\n", - "G \n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[]
nfev314
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events []\n", - "nfev 314\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "results, details = run_ode_solver(system, slope_func, t_eval=data.index)\n", - "details" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
I
1021.947892
1223.421845
1424.419436
16211.157260
18225.705778
\n", - "
" - ], - "text/plain": [ - " I\n", - "102 1.947892\n", - "122 3.421845\n", - "142 4.419436\n", - "162 11.157260\n", - "182 25.705778" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "results.tail()" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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29+oqqqicy3AHTt2MHny5F7jWLZsGY888ghbt26lra2Ntra2vZJYsvS3i29bH18K7LuDtGdrgS/hjp36DDAd+G8AETkIeMo77+G4g4GfEZH434Df4g4aPha4Gncs1iUDiGNAugzUDdtcfMb4aV7ZMTS1t9DY1kQ0GqWxrYmm9hbmlR0zZDEEg3v+j8dWL4hEALjuuut48sknOfTQQ/nDH/7AqaeeGrun4zgO3ReDbW9P7DMiFAp1ed7TOTtFo9FYfL256667eP3117n55ptZunQpTz75JAAdHb3/cf3mm2+yePFizjrrLH71q1+xbNkyjj/++H2+xk/9akGp6heScXFVfSLu6WYRuRvo3HYl8Kqq3gkgIl8DzgQuBH4iIocAJwAHqOpG3Fks7gOuYWDJMmEhm83cmKSZWVzGebPP6FLFd8ZBJyetim8gZs2axaxZs7jyyis5/fTTef755/nCF75AUVERO3fujB1XU1PT5bnfysrK2LVrF1u2bGH69OkAvPvuu12Oefvtt/m3f/s35s2bF3serzMZh8NhMjLceUZXrVrFrFmzuPjiiwE3EW7ZssXXluK+pM09KBEpAP4d+Ju36Wjguc79qhoVkReAucBPvP2bveTUaQVws4hkqmpbsmO2VXWNSa6ZxWVplZA6tbS0cM8993DqqacyadIk1q9fz/bt25kxYwYARx11FD/+8Y9j3X4//OEPyczMTFo8Bx54IEcddRS33HILt9xyCzU1NfziF7/ocsy0adN4+umnOeaYY9i1axf33HNPl/2d3Xwvv/wyhxxyCPn5+UyfPp3169fz4osvMn36dB577DEqKiqS9nN0N5DZzP8VWACU0q2LUFUT7l4TkQuBB4E84DXcuf3wzl/Z7fAqYE4f+zOAYmBHorEkKmT3oIwZlQKBANXV1Vx77bXU1NQwYcIEFi5cyEknnQTAueeey5o1a1i4cCH5+flcf/31Sa98u/vuu7nxxhv5/Oc/T1lZGV//+tf5+te/Htu/aNEiFi1axNlnn83UqVO59dZbueyyy2L7J02axJe//GUWLVpEbW0td955J5/73Oc477zzuP766wkEAnz+85/nlFNOSerPEc/prU+zJyLyXdwJY98FyoEuL1bVTycagIiMASbiFkp8CyhX1XNFZB1wr6o+EHfs3cAcVf1XEbkF+Kyqzo3b/zFgDTBZVftM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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot(results.I, 'g-', label='simulation')\n", - "plot(data.insulin, 'go', label='insulin data')\n", - "\n", - "decorate(xlabel='Time (min)',\n", - " ylabel='Concentration ($\\mu$U/mL)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Write an error function that takes a sequence of parameters as an argument, along with the `DataFrame` containing the measurements. It should make a `System` object with the given parameters, run it, and compute the difference between the results of the simulation and the measured values. Test your error function by calling it with the parameters from the previous exercise.\n", - "\n", - "Hint: As we did in a previous exercise, you might want to drop the errors for times prior to `t=8`." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def error_func(params, data):\n", - " \"\"\"Computes an array of errors to be minimized.\n", - " \n", - " params: sequence of parameters\n", - " actual: array of values to be matched\n", - " \n", - " returns: array of errors\n", - " \"\"\"\n", - " print(params)\n", - " \n", - " # make a System with the given parameters\n", - " system = make_system(params, data)\n", - "\n", - " # solve the ODE\n", - " results, details = run_ode_solver(system, slope_func, t_eval=data.index)\n", - "\n", - " # compute the difference between the model\n", - " # results and actual data\n", - " errors = results.I - data.insulin\n", - " return TimeSeries(errors.loc[8:])" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "I0 360.000\n", - "k 0.250\n", - "gamma 0.004\n", - "G_T 80.000\n", - "dtype: float64\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
810.679284
10-3.230156
12-7.936756
14-8.007716
16-3.570965
191.815372
222.570822
277.101640
323.815164
428.932695
529.598985
620.776848
72-3.215782
82-10.348094
92-7.387625
102-9.052108
122-3.578155
142-3.580564
1623.157260
18218.705778
\n", - "
" - ], - "text/plain": [ - "8 10.679284\n", - "10 -3.230156\n", - "12 -7.936756\n", - "14 -8.007716\n", - "16 -3.570965\n", - "19 1.815372\n", - "22 2.570822\n", - "27 7.101640\n", - "32 3.815164\n", - "42 8.932695\n", - "52 9.598985\n", - "62 0.776848\n", - "72 -3.215782\n", - "82 -10.348094\n", - "92 -7.387625\n", - "102 -9.052108\n", - "122 -3.578155\n", - "142 -3.580564\n", - "162 3.157260\n", - "182 18.705778\n", - "dtype: float64" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "error_func(params, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Use `fit_leastsq` to find the parameters that best fit the data. Make a `System` object with those parameters, run it, and plot the results along with the measurements." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[3.6e+02 2.5e-01 4.0e-03 8.0e+01]\n", - "[3.6e+02 2.5e-01 4.0e-03 8.0e+01]\n", - "[3.6e+02 2.5e-01 4.0e-03 8.0e+01]\n", - "[3.60000005e+02 2.50000000e-01 4.00000000e-03 8.00000000e+01]\n", - "[3.60000000e+02 2.50000004e-01 4.00000000e-03 8.00000000e+01]\n", - "[3.60000000e+02 2.50000000e-01 4.00000006e-03 8.00000000e+01]\n", - "[3.60000000e+02 2.50000000e-01 4.00000000e-03 8.00000012e+01]\n", - "[3.22633290e+02 2.49583030e-01 3.78613755e-03 8.06552415e+01]\n", - "[3.22633295e+02 2.49583030e-01 3.78613755e-03 8.06552415e+01]\n", - "[3.22633290e+02 2.49583034e-01 3.78613755e-03 8.06552415e+01]\n", - "[3.22633290e+02 2.49583030e-01 3.78613761e-03 8.06552415e+01]\n", - "[3.22633290e+02 2.49583030e-01 3.78613755e-03 8.06552427e+01]\n", - "[3.08623795e+02 2.49614641e-01 3.97466190e-03 8.00584655e+01]\n", - "[3.08623795e+02 2.49614641e-01 3.97466190e-03 8.00584655e+01]\n", - "[3.19566993e+02 2.49611490e-01 3.88853846e-03 8.03632613e+01]\n", - "[3.23708229e+02 2.49609012e-01 3.81722890e-03 8.05362319e+01]\n", - "[3.24030778e+02 2.49601708e-01 3.77475245e-03 8.06261820e+01]\n", - "[3.24030783e+02 2.49601708e-01 3.77475245e-03 8.06261820e+01]\n", - "[3.24030778e+02 2.49601712e-01 3.77475245e-03 8.06261820e+01]\n", - "[3.24030778e+02 2.49601708e-01 3.77475250e-03 8.06261820e+01]\n", - "[3.24030778e+02 2.49601708e-01 3.77475245e-03 8.06261832e+01]\n", - "[3.25854440e+02 2.49601794e-01 3.75658463e-03 8.06140853e+01]\n", - "[3.25854445e+02 2.49601794e-01 3.75658463e-03 8.06140853e+01]\n", - "[3.25854440e+02 2.49601797e-01 3.75658463e-03 8.06140853e+01]\n", - "[3.25854440e+02 2.49601794e-01 3.75658468e-03 8.06140853e+01]\n", - "[3.25854440e+02 2.49601794e-01 3.75658463e-03 8.06140865e+01]\n", - "[3.27642608e+02 2.49603500e-01 3.74263717e-03 8.05986043e+01]\n", - "[3.27642612e+02 2.49603500e-01 3.74263717e-03 8.05986043e+01]\n", - "[3.27642608e+02 2.49603503e-01 3.74263717e-03 8.05986043e+01]\n", - "[3.27642608e+02 2.49603500e-01 3.74263723e-03 8.05986043e+01]\n", - "[3.27642608e+02 2.49603500e-01 3.74263717e-03 8.05986055e+01]\n", - "[3.26863109e+02 2.49605667e-01 3.75078997e-03 8.06057216e+01]\n", - "[3.27304683e+02 2.49604337e-01 3.74623093e-03 8.06014527e+01]\n", - "[3.27506605e+02 2.49603828e-01 3.74408894e-03 8.05997299e+01]\n", - "[3.27592938e+02 2.49603618e-01 3.74316805e-03 8.05990128e+01]\n", - "[3.27626784e+02 2.49603537e-01 3.74280637e-03 8.05987341e+01]\n", - "[3.27626789e+02 2.49603537e-01 3.74280637e-03 8.05987341e+01]\n", - "[3.27626784e+02 2.49603541e-01 3.74280637e-03 8.05987341e+01]\n", - "[3.27626784e+02 2.49603537e-01 3.74280643e-03 8.05987341e+01]\n", - "[3.27626784e+02 2.49603537e-01 3.74280637e-03 8.05987353e+01]\n", - "[3.27641682e+02 2.49603506e-01 3.74264454e-03 8.05986372e+01]\n", - "[3.27633912e+02 2.49603523e-01 3.74272894e-03 8.05986878e+01]\n", - "[3.27633917e+02 2.49603523e-01 3.74272894e-03 8.05986878e+01]\n", - "[3.27633912e+02 2.49603526e-01 3.74272894e-03 8.05986878e+01]\n", - "[3.27633912e+02 2.49603523e-01 3.74272900e-03 8.05986878e+01]\n", - "[3.27633912e+02 2.49603523e-01 3.74272894e-03 8.05986890e+01]\n", - "[3.27637457e+02 2.49603517e-01 3.74269029e-03 8.05986633e+01]\n", - "[3.27634917e+02 2.49603521e-01 3.74271799e-03 8.05986808e+01]\n", - "[3.27634922e+02 2.49603521e-01 3.74271799e-03 8.05986808e+01]\n", - "[3.27634917e+02 2.49603525e-01 3.74271799e-03 8.05986808e+01]\n", - "[3.27634917e+02 2.49603521e-01 3.74271804e-03 8.05986808e+01]\n", - "[3.27634917e+02 2.49603521e-01 3.74271799e-03 8.05986820e+01]\n", - "[3.27636818e+02 2.49603516e-01 3.74269717e-03 8.05986672e+01]\n", - "The relative error between two consecutive iterates is at most 0.000000\n" - ] - } - ], - "source": [ - "# Solution\n", - "\n", - "best_params, details = fit_leastsq(error_func, params, data)\n", - "print(details.mesg)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
k0.249604
gamma0.00374272
G_T80.5987
initI 327.634917\n", - "dtype: float64
t_00
t_end182
G<scipy.interpolate.interpolate.interp1d object...
\n", - "
" - ], - "text/plain": [ - "k 0.249604\n", - "gamma 0.00374272\n", - "G_T 80.5987\n", - "init I 327.634917\n", - "dtype: float64\n", - "t_0 0\n", - "t_end 182\n", - "G \n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[]
nfev320
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events []\n", - "nfev 320\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "results, details = run_ode_solver(system, slope_func, t_eval=data.index)\n", - "details" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot(results.I, 'g-', label='simulation')\n", - "plot(data.insulin, 'go', label='insulin data')\n", - "\n", - "decorate(xlabel='Time (min)',\n", - " ylabel='Concentration ($\\mu$U/mL)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Using the best parameters, estimate the sensitivity to glucose of the first and second phase pancreatic responsivity:\n", - "\n", - "$ \\phi_1 = \\frac{I_{max} - I_b}{k (G_0 - G_b)} $\n", - "\n", - "$ \\phi_2 = \\gamma \\times 10^4 $\n", - "\n", - "For $G_0$, use the best estimate from the glucose model, 290. For $G_b$ and $I_b$, use the inital measurements from the data.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "I0, k, gamma, G_T = best_params" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(130, 11)" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "I_max = data.insulin.max()\n", - "Ib = data.insulin[0]\n", - "I_max, Ib" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(289, 92)" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "# The value of G0 is the best estimate from the glucose model\n", - "G0 = 289\n", - "Gb = data.glucose[0]\n", - "G0, Gb" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2.4200817021371024" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "phi_1 = (I_max - Ib) / k / (G0 - Gb)\n", - "phi_1" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "37.42717987843228" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "phi_2 = gamma * 1e4\n", - "phi_2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/interest.ipynb b/code/soln/interest.ipynb deleted file mode 100644 index 54fda7dbd..000000000 --- a/code/soln/interest.ipynb +++ /dev/null @@ -1,381 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Case study\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *\n", - "\n", - "from pandas import read_html" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exponential, geometric, and polynomial growth" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Suppose there are two banks across the street from each other, The First Geometric Bank (FGB) and Exponential Savings and Loan (ESL). They offer the same interest rate on checking accounts, 3%, but at FGB, they compute and pay interest at the end of each year, and at ESL they compound interest continuously.\n", - "\n", - "If you deposit $p_0$ dollars at FGB at the beginning of Year 0, the balanace of your account at the end of Year $n$ is\n", - "\n", - "$ x_n = p_0 (1 + \\alpha)^n $\n", - "\n", - "where $\\alpha = 0.03$. At ESL, your balance at any time $t$ would be\n", - "\n", - "$ x(t) = p_0 \\exp(\\alpha t) $\n", - "\n", - "If you deposit \\$1000 at each back at the beginning of Year 0, how much would you have in each account after 10 years?\n", - "\n", - "Is there an interest rate FGB could pay so that your balance at the end of each year would be the same at both banks? What is it?\n", - "\n", - "Hint: `modsim` provides a function called `exp`, which is a wrapper for the NumPy function `exp`." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "0.03" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "p_0 = 1000\n", - "alpha = 0.03" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11.])" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "ts = linrange(11)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1000. , 1030. , 1060.9 , 1092.727 ,\n", - " 1125.50881 , 1159.2740743 , 1194.05229653, 1229.87386542,\n", - " 1266.77008139, 1304.77318383, 1343.91637934, 1384.23387072])" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "geometric = p_0 * (1 + alpha) ** ts" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1000. , 1030.45453395, 1061.83654655, 1094.17428371,\n", - " 1127.49685158, 1161.83424273, 1197.21736312, 1233.67805996,\n", - " 1271.24915032, 1309.96445073, 1349.85880758, 1390.96812846])" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "exponential = p_0 * exp(alpha * ts)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.030454533953516938" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "alpha2 = exp(alpha) - 1" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1000. , 1030.45453395, 1061.83654655, 1094.17428371,\n", - " 1127.49685158, 1161.83424273, 1197.21736312, 1233.67805996,\n", - " 1271.24915032, 1309.96445073, 1349.85880758, 1390.96812846])" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "geometric = p_0 * (1 + alpha2) ** ts" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot(ts, exponential, '-', label='Exponential')\n", - "plot(ts, geometric, 's', label='Geometric')\n", - "\n", - "decorate(xlabel='Time (years)',\n", - " ylabel='Value (dollars)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Suppose a new bank opens called the Polynomial Credit Union (PCU). In order to compete with First Geometric Bank and Exponential Savings and Loan, PCU offers a parabolic savings account where the balance is a polynomial function of time:\n", - "\n", - "$ x(t) = p_0 + \\beta_1 t + \\beta_2 t^2 $\n", - "\n", - "As a special deal, they offer an account with $\\beta_1 = 30$ and $\\beta_2 = 0.5$, with those parameters guaranteed for life.\n", - "\n", - "Suppose you deposit \\$1000 at all three banks at the beginning of Year 0. How much would you have in each account at the end of Year 10? How about Year 20? And Year 100?" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "number_of_years = 100\n", - "ts = linrange(number_of_years+1)\n", - "geometric = p_0 * (1 + alpha2) ** ts\n", - "exponential = p_0 * exp(alpha * ts)\n", - "None" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "beta1 = 30\n", - "beta2 = 0.5\n", - "parabolic = p_0 + beta1 * ts + beta2 * ts**2\n", - "None" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def plot_results():\n", - " plot(ts, exponential, '-', label='Exponential')\n", - " plot(ts, geometric, 's', label='Geometric')\n", - " plot(ts, parabolic, 'o', label='Parabolic')\n", - " \n", - " decorate(xlabel='Time (years)',\n", - " ylabel='Value (dollars)')" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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x+PJ2mf1p2g02i+7oAshutbYEiQWN4OTAfg8gzO0lyRDfMNIS0sLDz/8MPPmzePkk0+msLAQwzA46KCDeOCBB/B4PITDYe677z6OO+44jj76aG6//XaCwfYTMp977jm+/OUvM2PGDObMmdPh7CelFEuWLOErX/kKU6dOTR25fu6553L44Ydz3XXXEQ63L+p78803OfXUU5k2bRrnnXce69evB+Cmm25i+/btzJkzh6lTp/Lkk0+ydetWlFI8//zzHHfccVxyySWpa9GoOdzQ2NjIj370I770pS8xffp0vvOd7wzQb1aI4S+eSFDbEKCESkqoZEL5ARw0dgJXTr8g9TPQJEANIx988AHhcDi58WKnFi5cyKZNm3jxxRf529/+Rm1tLYsWLQLg3Xff5YEHHuChhx7i73//O3vttRc/+MEPOtRfvnw5S5cu5bnnnmPx4sXcdttt/PSnP+Xtt99mw4YNqRNz165dy6233sqdd97Je++9x7nnnst3vvMdwuEwCxcupKqqiscee4wPPvigww7nK1as4JVXXuGpp57are0//OEPCQQCvPzyy7zzzjvMnj07C781IQRAfXMwdfCqy2kwqiRvkFskAWpYaWhooKysrMPQ2Hnnnce0adM49NBDef/993n++ee59dZbKS0tpbCwkKuuuioVVF566SXOPPNMDj74YDweDz/4wQ9YvXo1W7duTT3viiuuoLCwkAkTJjBx4kSOOeYY9t57b4qKipg5cyYfffQRYPbEzj33XKZMmZI60t3tdrN69epuv8M111xDfn4+eXkd/+eora1l2bJl3HHHHZSUlOB2u5kxY0a2fnVCjGixeIKdDYHU+7Fl+ThyYL5aBvmHkdLSUhoaGjrM3/z+978HYObMmdTV1REIBDjjjDNSdRKJBPG4+bem2tpaDj744FRZQUEBpaWl1NTUpI4dsR/T7vV6d3tfV1cHmBl4L774Ir/73e9S5ZFIhNra2m6/w7hx4zq9vmPHDkpKSigpKen5FyGE6JWdjf7U3JPX7aSsePB7TyABaliZOnUqHo+HN954g5NOOmm38rKyMvLy8nj55Zc7PbZ97NixbNu2LfXe7/fT2NiY0RHv6SorK5kzZw5XX311r+p1lWU4btw4mpqaaG5uprg4/QBlIURvJRO3wtEYnzZuInkyYKHLjUHp4DXMRob4hpHi4mK++93v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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot_results()" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAZkAAAEYCAYAAACOSYuzAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvNQv5yAAAIABJREFUeJzt3XecVPW9//HX9LLLFpAqGKLCV2wRg2BQIUFjNCaKGixRY4uKetUkdoINjYqYmMSQYIlpcjUmco2JpunPQu61oZQE4QsWUNhlYWHr9Pb748wOZ2bbDMzM7sx8no/HPtg9Z8p3dpb97Pt8myWRSCCEEEIUgnWgGyCEEKJ8SZERQghRMFJkhBBCFIwUGSGEEAVjH+gGFIJSygUcBTQCsQFujhBClDMbMBp4R2sdyjxZlkUGo8AsH+hGCCFEBTkO+FfmwXItMo0AS5cuZdSoUQPdFiGEKFvbtm3jvPPOg+Tv3UzlWmRiAKNGjWLs2LED3RYhhKgEPXZNSMe/EEKIgpEiI4QQomCkyAghhCgYKTJCCCEKRoqMEEKIginX0WVCCCH68Og7S3s9d/lR5+XteSTJCCFEhYonEjS3BWho7iQSixfkOSTJCCFEhTCnl39v24AvGCEW79pTbDxj9qnO+3NKkRFCiAoSTyRo2uWnwx/GvGWly1mYciBFRhTV5MmTef755xk3blyft9uyZQvHH388a9euxW6XH1Mh9pQ5vazZtgF/Mr2E8ePEiwXwuu0MrXEX5Pnlf+8gM2vWLJqbm7HZbKljp59+OrfffvsAtmrPXHDBBZx66qnMmTMndWzlypUD2CIhKlMsnqBpl48OfzjtuMNupcrtwGqxYCnQc0uRGYSWLFnC9OnTB7oZQogSlZ5eNL5AlHjClF4s4LBaqfY4C1ZcusjoshJxxx13cO2116a+XrRoERdeeCGJRIK33nqLGTNmsGTJEqZNm8asWbN4/vnnU7ft6Ojgpptu4uijj+ZLX/oSP//5z4nHjZEky5Yt49xzz2XhwoUcddRRzJo1i9deey3tvvPmzePYY4/luOOO46GHHiIWi/V734ceeogVK1awYMECJk+ezIIFCwBQSrF582YAXn31VWbPns2RRx7JzJkzefjhhwv7TRSigsTiCbbu6KTDHyGe2N374rRbqa1yMcxbx8HDD2RS8uPyo85LfeRTxSeZlXo7b7+/jUi0MMP3wIikUw8exWQ1Yo8f45ZbbmH27NksW7aM/fbbjz/+8Y/86U9/wmIx/g5pbm6mpaWF5cuXs2rVKi6//HIOPfRQ9t9/f+6++246Ojp46aWXaG1t5dJLL2X48OGpy1hr1qzh9NNP58033+T3v/893//+91m+fDkWi4Wbb76ZffbZh3/84x8EAgGuuOIKRo8ezTnnnNPnfb/73e/y3nvvdbtcZubxeFi4cCETJkxgw4YNXHLJJUyaNIkTTjhhj79PQlQqc3pZ3ajxB9PTizWZXqqKkF7MKj7JrNqwo6AFBiASjbNqw46sb3/11VczZcqU1MczzzyDx+Nh0aJF3H///dx4443cdttt3fbKue6663A6nUydOpWZM2fy17/+lVgsxosvvsj1119PdXU1Y8eO5eKLL05LOmPGjOGss87CZrNx+umns2PHDpqbm2lubub1119n3rx5eL1ehg0bxkUXXcQLL7zQ732zMW3aNJRSWK1WDjroIE455RTefvvtrL9PQoh00XiCT7d30hnonl5qiphezCo+yRwxcXhRkswRE4dnffvFixf32Cdz+OGHM3bsWHbt2sXJJ5+cdq6mpgav15v6esyYMWzfvp2WlhYikQhjxoxJO9fU1JT6ep999kl97vF4APD7/bS1tRGNRjn22GNT5+PxOKNHj+73vtlYvXo1Dz74IBs3biQSiRAOhznppJOyuq8QYnd6SQBrGjX+YIR4AlN6saT6XgZKxReZyWrEXl3GKqalS5cSiUQYMWIEjz/+OFdccUXqXHt7O36/P1VoGhsbmTBhAvX19TgcDhoaGjjwwANT50aOHNnv840aNQqn08mbb75ZkGHE119/Peeffz6PP/44LpeLH/zgB7S0tOT9eYQoZ9FYnIZmH52BSNpxl8OG12XHYnExafiBqeOFTC09qfjLZaXi448/5sc//jGLFi3igQce4PHHH2fdunVpt3n44YcJh8OsWLGCV199lZNOOgmbzcZJJ53EQw89RGdnJ1u3buVXv/oVp556ar/POWLECI455hjuv/9+Ojs7icfjfPLJJ1lf0tpnn3349NNPez3v8/mora3F5XKxZs0a/vKXv2T1uEJUqkffWZr6eOSdpaxq0Lz9yft86tucuo3VYsFhM4Ymd/XZDqSKTzKD0dy5c9PmyUyfPp2mpiYu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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot_results()\n", - "plt.yscale('log')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.4" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/jump_soln.ipynb b/code/soln/jump_soln.ipynb deleted file mode 100644 index 633ebbaf3..000000000 --- a/code/soln/jump_soln.ipynb +++ /dev/null @@ -1,1666 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Bungee dunk example, taking into account the mass of the bungee cord\n", - "\n", - "Copyright 2018 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Bungee jumping" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In Chapter 21, we simulated a bungee jump with a model that took into account gravity, air resistance, and the spring force of the bungee cord, but we ignored the weight of the cord.\n", - "\n", - "It is tempting to say that the weight of the cord doesn't matter, because it falls along with the jumper. But that intuition is incorrect, as explained by [Heck, Uylings, and Kędzierska](http://iopscience.iop.org/article/10.1088/0031-9120/45/1/007). As the cord falls, it transfers energy to the jumper. They derive a differential equation that relates the acceleration of the jumper to position and velocity:\n", - "\n", - "$a = g + \\frac{\\mu v^2/2}{\\mu(L+y) + 2L}$ \n", - "\n", - "where $a$ is the net acceleration of the number, $g$ is acceleration due to gravity, $v$ is the velocity of the jumper, $y$ is the position of the jumper relative to the starting point (usually negative), $L$ is the length of the cord, and $\\mu$ is the mass ratio of the cord and jumper.\n", - "\n", - "If you don't believe this model is correct, [this video might convince you](https://www.youtube.com/watch?v=X-QFAB0gEtE).\n", - "\n", - "Following the example in Chapter 21, we'll model the jump with the following modeling assumptions:\n", - "\n", - "1. Initially the bungee cord hangs from a crane with the attachment point 80 m above a cup of tea.\n", - "\n", - "2. Until the cord is fully extended, it applies a force to the jumper as explained above.\n", - "\n", - "3. After the cord is fully extended, it obeys [Hooke's Law](https://en.wikipedia.org/wiki/Hooke%27s_law); that is, it applies a force to the jumper proportional to the extension of the cord beyond its resting length.\n", - "\n", - "4. The jumper is subject to drag force proportional to the square of their velocity, in the opposite of their direction of motion.\n", - "\n", - "First I'll create a `Param` object to contain the quantities we'll need:\n", - "\n", - "1. Let's assume that the jumper's mass is 75 kg and the cord's mass is also 75 kg, so `mu=1`.\n", - "\n", - "2. The jumpers's frontal area is 1 square meter, and terminal velocity is 60 m/s. I'll use these values to back out the coefficient of drag.\n", - "\n", - "3. The length of the bungee cord is `L = 25 m`.\n", - "\n", - "4. The spring constant of the cord is `k = 40 N / m` when the cord is stretched, and 0 when it's compressed.\n", - "\n", - "I adopt the coordinate system and most of the variable names from [Heck, Uylings, and Kędzierska](http://iopscience.iop.org/article/10.1088/0031-9120/45/1/007).\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "newton" - ], - "text/latex": [ - "$newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram\n", - "N = UNITS.newton" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
v_init0.0 meter / second
g9.8 meter / second ** 2
M75 kilogram
m_cord75 kilogram
area1 meter ** 2
rho1.2 kilogram / meter ** 3
v_term60.0 meter / second
L25 meter
k40.0 newton / meter
\n", - "
" - ], - "text/plain": [ - "v_init 0.0 meter / second\n", - "g 9.8 meter / second ** 2\n", - "M 75 kilogram\n", - "m_cord 75 kilogram\n", - "area 1 meter ** 2\n", - "rho 1.2 kilogram / meter ** 3\n", - "v_term 60.0 meter / second\n", - "L 25 meter\n", - "k 40.0 newton / meter\n", - "dtype: object" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(v_init = 0 * m / s,\n", - " g = 9.8 * m/s**2,\n", - " M = 75 * kg, # mass of jumper\n", - " m_cord = 75 * kg, # mass of cord\n", - " area = 1 * m**2, # frontal area of jumper\n", - " rho = 1.2 * kg/m**3, # density of air\n", - " v_term = 60 * m / s, # terminal velocity of jumper\n", - " L = 25 * m, # length of cord\n", - " k = 40 * N / m) # spring constant of cord" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now here's a version of `make_system` that takes a `Params` object as a parameter.\n", - "\n", - "`make_system` uses the given value of `v_term` to compute the drag coefficient `C_d`.\n", - "\n", - "It also computes `mu` and the initial `State` object." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Makes a System object for the given params.\n", - " \n", - " params: Params object\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " # back out the coefficient of drag\n", - " C_d = 2 * M * g / (rho * area * v_term**2)\n", - " \n", - " mu = m_cord / M\n", - " init = State(y=0*m, v=v_init)\n", - " t_end = 10 * s\n", - "\n", - " return System(params, C_d=C_d, mu=mu,\n", - " init=init, t_end=t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's make a `System`" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
v_init0.0 meter / second
g9.8 meter / second ** 2
M75 kilogram
m_cord75 kilogram
area1 meter ** 2
rho1.2 kilogram / meter ** 3
v_term60.0 meter / second
L25 meter
k40.0 newton / meter
C_d0.3402777777777778 dimensionless
mu1.0 dimensionless
inity 0 meter\n", - "v 0.0 meter / secon...
t_end10 second
\n", - "
" - ], - "text/plain": [ - "v_init 0.0 meter / second\n", - "g 9.8 meter / second ** 2\n", - "M 75 kilogram\n", - "m_cord 75 kilogram\n", - "area 1 meter ** 2\n", - "rho 1.2 kilogram / meter ** 3\n", - "v_term 60.0 meter / second\n", - "L 25 meter\n", - "k 40.0 newton / meter\n", - "C_d 0.3402777777777778 dimensionless\n", - "mu 1.0 dimensionless\n", - "init y 0 meter\n", - "v 0.0 meter / secon...\n", - "t_end 10 second\n", - "dtype: object" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`drag_force` computes drag as a function of velocity:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def drag_force(v, system):\n", - " \"\"\"Computes drag force in the opposite direction of `v`.\n", - " \n", - " v: velocity\n", - " \n", - " returns: drag force in N\n", - " \"\"\"\n", - " unpack(system)\n", - " f_drag = -np.sign(v) * rho * v**2 * C_d * area / 2\n", - " return f_drag" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's drag force at 20 m/s." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "-81.66666666666667 kilogram meter/second2" - ], - "text/latex": [ - "$-81.66666666666667 \\frac{kilogram \\cdot meter}{second^{2}}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "drag_force(20 * m/s, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function computes the acceleration of the jumper due to tension in the cord.\n", - "\n", - "$a_{cord} = \\frac{\\mu v^2/2}{\\mu(L+y) + 2L}$ " - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def cord_acc(y, v, system):\n", - " \"\"\"Computes acceleration due to the force of \n", - " the bungee cord on the jumper.\n", - " \n", - " y: height of the jumper\n", - " v: velocity of the jumpter\n", - " \n", - " returns: acceleration in m/s\n", - " \"\"\"\n", - " # Fill this in\n", - " return 0" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def cord_acc(y, v, system):\n", - " \"\"\"Computes acceleration due to the force of \n", - " the bungee cord on the jumper.\n", - " \n", - " y: height of the jumper\n", - " v: velocity of the jumpter\n", - " \n", - " returns: acceleration in m/s\n", - " \"\"\"\n", - " unpack(system)\n", - " a_cord = -v**2 / 2 / (2*L/mu + (L+y))\n", - " return a_cord" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's acceleration due to tension in the cord if we're going 20 m/s after falling 20 m." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "-3.6363636363636362 meter/second2" - ], - "text/latex": [ - "$-3.6363636363636362 \\frac{meter}{second^{2}}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "y = -20 * m\n", - "v = -20 * m/s\n", - "cord_acc(y, v, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now here's the slope function:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "def slope_func1(state, t, system):\n", - " \"\"\"Compute derivatives of the state.\n", - " \n", - " state: position, velocity\n", - " t: time\n", - " system: System object containing g, rho,\n", - " C_d, area, and mass\n", - " \n", - " returns: derivatives of y and v\n", - " \"\"\"\n", - " y, v = state\n", - " unpack(system)\n", - " \n", - " # Fill this in\n", - " dvdt = -g\n", - " \n", - " return v, dvdt" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def slope_func1(state, t, system):\n", - " \"\"\"Compute derivatives of the state.\n", - " \n", - " state: position, velocity\n", - " t: time\n", - " system: System object containing g, rho,\n", - " C_d, area, and mass\n", - " \n", - " returns: derivatives of y and v\n", - " \"\"\"\n", - " y, v = state\n", - " unpack(system)\n", - " \n", - " a_drag = drag_force(v, system) / M\n", - " a_cord = cord_acc(y, v, system)\n", - " dvdt = -g + a_cord + a_drag\n", - " \n", - " return v, dvdt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As always, let's test the slope function with the initial params." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(, )" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func1(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll need an event function to stop the simulation when we get to the end of the cord." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func(state, t, system):\n", - " \"\"\"Run until y=-L.\n", - " \n", - " state: position, velocity\n", - " t: time\n", - " system: System object containing g, rho,\n", - " C_d, area, and mass\n", - " \n", - " returns: difference between y and -L\n", - " \"\"\"\n", - " y, v = state\n", - " unpack(system)\n", - "\n", - " # Fill this in\n", - " return 1" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def event_func(state, t, system):\n", - " \"\"\"Run until y=-L.\n", - " \n", - " state: position, velocity\n", - " t: time\n", - " system: System object containing g, rho,\n", - " C_d, area, and mass\n", - " \n", - " returns: difference between y and -L\n", - " \"\"\"\n", - " y, v = state\n", - " unpack(system)\n", - " return y + L" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can test it with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "25 meter" - ], - "text/latex": [ - "$25 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "event_func(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And then run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'A termination event occurred.'" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results, details = run_ode_solver(system, slope_func1, \n", - " events=event_func, max_step=0.1)\n", - "details.message" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how long it takes to drop 25 meters." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2.211816442174678" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_final = get_last_label(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the plot of position as a function of time." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_position(results, **options):\n", - " plot(results.y, **options)\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Position (m)')\n", - " \n", - "plot_position(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's velocity as a function of time:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_velocity(results):\n", - " plot(results.v, color='C1', label='v')\n", - " \n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Velocity (m/s)')\n", - " \n", - "plot_velocity(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Velocity when we reach the end of the cord." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "-23.875662149063114 meter/second" - ], - "text/latex": [ - "$-23.875662149063114 \\frac{meter}{second}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "min(results.v) * m/s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Sweeping cord weight\n", - "\n", - "Now let's see how velocity at the crossover point depends on the weight of the cord." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "def sweep_m_cord(m_cord_array, params):\n", - " sweep = SweepSeries()\n", - "\n", - " for m_cord in m_cord_array:\n", - " system = make_system(Params(params, m_cord=m_cord))\n", - " results, details = run_ode_solver(system, slope_func1, \n", - " events=event_func)\n", - " min_velocity = min(results.v) * m/s\n", - " sweep[m_cord.magnitude] = min_velocity\n", - " \n", - " return sweep" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "m_cord_array = linspace(1, 201, 51) * kg\n", - "sweep = sweep_m_cord(m_cord_array, params);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what it looks like. As expected, a heavier cord gets the jumper going faster.\n", - "\n", - "There's a hitch near 25 kg that seems to be due to numerical error." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(sweep)\n", - "\n", - "decorate(xlabel='Mass of cord (kg)',\n", - " ylabel='Fastest downward velocity (m/s)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Phase 2\n", - "\n", - "Once the jumper falls past the length of the cord, acceleration due to energy transfer from the cord stops abruptly. As the cord stretches, it starts to exert a spring force. So let's simulate this second phase." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`spring_force` computes the force of the cord on the jumper:" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "def spring_force(y, system):\n", - " \"\"\"Computes the force of the bungee cord on the jumper:\n", - " \n", - " y: height of the jumper\n", - " \n", - " Uses these variables from system:\n", - " y_attach: height of the attachment point\n", - " L: resting length of the cord\n", - " k: spring constant of the cord\n", - " \n", - " returns: force in N\n", - " \"\"\"\n", - " unpack(system)\n", - " distance_fallen = -y\n", - " extension = distance_fallen - L\n", - " f_spring = k * extension\n", - " return f_spring" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The spring force is 0 until the cord is fully extended. When it is extended 1 m, the spring force is 40 N. " - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.0 newton" - ], - "text/latex": [ - "$0.0 newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "spring_force(-25*m, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "40.0 newton" - ], - "text/latex": [ - "$40.0 newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "spring_force(-26*m, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The slope function for Phase 2 includes the spring force, and drops the acceleration due to the cord." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "def slope_func2(state, t, system):\n", - " \"\"\"Compute derivatives of the state.\n", - " \n", - " state: position, velocity\n", - " t: time\n", - " system: System object containing g, rho,\n", - " C_d, area, and mass\n", - " \n", - " returns: derivatives of y and v\n", - " \"\"\"\n", - " y, v = state\n", - " unpack(system)\n", - " \n", - " a_drag = drag_force(v, system) / M\n", - " a_spring = spring_force(y, system) / M\n", - " dvdt = -g + a_drag + a_spring\n", - " \n", - " return v, dvdt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "I'll run Phase 1 again so we can get the final state." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
v_init0.0 meter / second
g9.8 meter / second ** 2
M75 kilogram
m_cord75 kilogram
area1 meter ** 2
rho1.2 kilogram / meter ** 3
v_term60.0 meter / second
L25 meter
k40.0 newton / meter
C_d0.3402777777777778 dimensionless
mu1.0 dimensionless
inity 0 meter\n", - "v 0.0 meter / secon...
t_end10 second
\n", - "
" - ], - "text/plain": [ - "v_init 0.0 meter / second\n", - "g 9.8 meter / second ** 2\n", - "M 75 kilogram\n", - "m_cord 75 kilogram\n", - "area 1 meter ** 2\n", - "rho 1.2 kilogram / meter ** 3\n", - "v_term 60.0 meter / second\n", - "L 25 meter\n", - "k 40.0 newton / meter\n", - "C_d 0.3402777777777778 dimensionless\n", - "mu 1.0 dimensionless\n", - "init y 0 meter\n", - "v 0.0 meter / secon...\n", - "t_end 10 second\n", - "dtype: object" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system1 = make_system(params)" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "A termination event occurred.\n" - ] - } - ], - "source": [ - "event_func.direction=-1\n", - "results1, details1 = run_ode_solver(system1, slope_func1, \n", - " events=event_func, max_step=0.1)\n", - "print(details1.message)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now I need the final time, position, and velocity from Phase 1." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2.211816442174678" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_final = get_last_label(results1)" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
y-25.000000
v-23.875662
\n", - "
" - ], - "text/plain": [ - "y -25.000000\n", - "v -23.875662\n", - "Name: 2.211816442174678, dtype: float64" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "init2 = results1.row[t_final]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And that gives me the starting conditions for Phase 2." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
v_init0.0 meter / second
g9.8 meter / second ** 2
M75 kilogram
m_cord75 kilogram
area1 meter ** 2
rho1.2 kilogram / meter ** 3
v_term60.0 meter / second
L25 meter
k40.0 newton / meter
C_d0.3402777777777778 dimensionless
mu1.0 dimensionless
inity -25.000000\n", - "v -23.875662\n", - "Name: 2.21181644...
t_end12.2118
t_02.21182
\n", - "
" - ], - "text/plain": [ - "v_init 0.0 meter / second\n", - "g 9.8 meter / second ** 2\n", - "M 75 kilogram\n", - "m_cord 75 kilogram\n", - "area 1 meter ** 2\n", - "rho 1.2 kilogram / meter ** 3\n", - "v_term 60.0 meter / second\n", - "L 25 meter\n", - "k 40.0 newton / meter\n", - "C_d 0.3402777777777778 dimensionless\n", - "mu 1.0 dimensionless\n", - "init y -25.000000\n", - "v -23.875662\n", - "Name: 2.21181644...\n", - "t_end 12.2118\n", - "t_0 2.21182\n", - "dtype: object" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system2 = System(system1, t_0=t_final, t_end=t_final+10, init=init2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we run Phase 2, setting the direction of the event function so it doesn't stop the simulation immediately. " - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "A termination event occurred.\n" - ] - }, - { - "data": { - "text/plain": [ - "8.104199118651167" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "event_func.direction=+1\n", - "results2, details2 = run_ode_solver(system2, slope_func2, \n", - " events=event_func, max_step=0.1)\n", - "print(details2.message)\n", - "t_final = get_last_label(results2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can plot the results on the same axes." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_position(results1, label='Phase 1')\n", - "plot_position(results2, label='Phase 2')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And get the lowest position from Phase 2." - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "-77.04862516095774 meter" - ], - "text/latex": [ - "$-77.04862516095774 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "min(results2.y) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To see how big the effect of the cord is, I'll collect the previous code in a function." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [], - "source": [ - "def simulate_system2(params):\n", - " \n", - " system1 = make_system(params)\n", - " event_func.direction=-1\n", - " results1, details1 = run_ode_solver(system1, slope_func1, events=event_func, max_step=0.1)\n", - "\n", - " t_final = get_last_label(results1)\n", - " init2 = results1.row[t_final]\n", - " \n", - " system2 = System(system1, t_0=t_final, t_end=t_final+10, init=init2)\n", - " event_func.direction=+1\n", - " results2, details2 = run_ode_solver(system2, slope_func2, events=event_func, max_step=0.1)\n", - " t_final = get_last_label(results2)\n", - " return TimeFrame(pd.concat([results1, results2]))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run both phases and get the results in a single `TimeFrame`." - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [], - "source": [ - "results = simulate_system2(params);" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_position(results)" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [], - "source": [ - "params_no_cord = Params(params, m_cord=1*kg)\n", - "results_no_cord = simulate_system2(params_no_cord);" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzs3XlYnfWZ+P/3WeHAYd+3AEnIk4WQELIvahLjVq1Lbau1LmPq9LIdp+0v4/xS+3XG6ea0emnnq22tY62t9lfbWrUudU00ZicrSSB5IIEAYQ/7gcPZf388cIBsQATOAe7XdXFdPMs53MaE+3w+z+dz3zqfz4cQQggRbPSBDkAIIYS4EElQQgghgpIkKCGEEEFJEpQQQoigJAlKCCFEUJIEJYQQIihJghJCCBGUJEEJIYQISpKghBBCBCVJUEIIIYKSMdABBIqiKCHAEqAO8AQ4HCGEmOwMQAqwT1VVx3BeMGUTFFpy2h7oIIQQYopZA+wYzo1TOUHVAfzxj38kOTk50LEIIcSkVl9fz1133QW9v3uHYyonKA9AcnIy6enpgY5FCCGmimE/UpFFEkIIIYKSJCghhBBBSRKUEEKIoCQJSgghRFCa8IskFEX5PvAQEA18CPyzqqqNgY1KiODV0dFBY2MjLpcr0KGIScRkMpGYmEhkZOSoveeETlCKovwT8AhwD1AB/AL4E7B+rH92R0sLXq8Po9mE2WzGYDJhMBjG+scK8bl0dHTQ0NBAWloaFosFnU4X6JDEJODz+bDb7dTU1ACMWpKa0AkKbeT0lKqqbwAoinI/cEpRlFxVVY+N1Q/d88Zf6akpPf+CKQR9SDgGSzimsAjCouOJTEgkNiWZ8MhI+WUgAq6xsZG0tDTCwsICHYqYRHQ6HWFhYaSlpVFbWysJqrdU0QLge33nVFUtVxTlNLAMGJME5fF46KmvuPBFlwOvy4HX1oIL6K6Es0A5gCkUc2wSEckZJGRkkZCRLiMuMe5cLhcWiyXQYYhJymKxjOrU8YRNUEAc2iKPc583NQGJY/VDDQYDKYvX0lC8H5/Lgc/rBo8bvEPsPXP14GyopLmhkuaiHZwwmAhJzCAuaxbpikKY1TpWIQsxiIzkxVgZ7b9bEzlBBexfmbJkCcqSJYPOeTweujo66Wpvp7u9je6OdrpbmnC0N+PtatWS2KAXuHDUlVNbV07t7g8wxqWQmJNLVm4e5tCQcfyvEUKI4DSRE9RZwIs2Wjo+4HwC54+qxpzBYCAyJprImGggc9A1r9dLS0MDjadP015XjeNsDTi7B9zhw91cS21zLbX7thKWOp303EWkTJ8un3aFmEA2b94MwH//938HOJLheeaZZygsLOTll18OdCgXNGETlKqqDkVRioC1wDYARVGygSxgbwBDO49eryc+JYX4lBRgBV6vl+a6empLT9BefRJvR1P/zR433dWllFaXcioijqTcxUxfsBCjccL+rxJCjIJnnnmGZ5999rzzd955J4899hgA69at86+k6/Pmm28yZ86c8Qhx1E3033rPAr9QFOUQcBp4GvhkLFfwjQa9Xk9CWioJaanAOjpaWqk4cpi28mJ83e3++zydzdTu/oC6A58Rp+SjLF+BKSQ0cIELIQBwOp2YzeZx/Zn3338/d9xxh/+4ubmZW2+9lauvvnrQfY888gg33HCD/zgmJmbcYhxtE7qShKqqLwKPA88Bu4FO4M6ABnUZImNjWHDVWtbc9y1m3fh1QjPmgK5/hZ/Paefs0V3sfulZird/gtvRE8BohRgfd999N08++SSbN29m4cKFXHvttRQVFVFSUsJtt91Gfn4+mzZtwuEYVu87jh07xt13301eXh7Lli3j4Ycf9l+rqqpi48aN5OXlsXLlSp566im8Xq//+rp16/jtb3/Lgw8+SF5eHn/7298AePnll1m5ciUFBQU8/vjjg14z2sLDw0lISPB/7dmzh7i4OFasWDHovoiIiEH3jWT2ZdeuXSxZsoSPP/4YgPb2dr797W+Tl5fHNddcw7Zt21AUhb17x2eSaqKPoFBV9XG0JDXh6fV6UrMySc3KxNbZSdn+QtpKi9C57AB4XQ6ainbRXHKAxLzlzFq6HL1hwv8vFOKiXn31VTZt2sQ3v/lNnn76aTZv3kxSUhI/+MEP0Ov1fOtb3+K1117r6zN0US0tLdx3333ceOONPProo3i9Xvbs2QNoz4gffPBBMjMzee2116irq+P73/8+UVFRbNy40f8ezz//PA8//DCPPPIIZrOZwsJCfvazn/Hoo4+yePFiXn75ZT766COuvfbai8bx3HPP8Zvf/Oai1wsKCnjhhReG9Wfz1ltvcdNNN523XeWpp57i5z//OZmZmTzwwAPnjbAuZtu2bWza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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_position(results, label='m_cord = 75 kg')\n", - "plot_position(results_no_cord, label='m_cord = 1 kg')" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "-74.63749938066776 meter" - ], - "text/latex": [ - "$-74.63749938066776 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "min(results_no_cord.y) * m" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "-2.4111257802899786" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "diff = min(results.y) - min(results_no_cord.y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/kitten_soln.ipynb b/code/soln/kitten_soln.ipynb deleted file mode 100644 index 18e2fc8f6..000000000 --- a/code/soln/kitten_soln.ipynb +++ /dev/null @@ -1,888 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Case study.\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Unrolling\n", - "\n", - "Let's simulate a kitten unrolling toilet paper. As reference material, see [this video](http://modsimpy.com/kitten).\n", - "\n", - "The interactions of the kitten and the paper roll are complex. To keep things simple, let's assume that the kitten pulls down on the free end of the roll with constant force. Also, we will neglect the friction between the roll and the axle. \n", - "\n", - "![](diagrams/kitten.png)\n", - "\n", - "This figure shows the paper roll with $r$, $F$, and $\\tau$. As a vector quantity, the direction of $\\tau$ is into the page, but we only care about its magnitude for now." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll start by loading the units we need." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "newton" - ], - "text/latex": [ - "$newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "radian = UNITS.radian\n", - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram\n", - "N = UNITS.newton" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And a few more parameters in the `Params` object." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
Rmin0.02 meter
Rmax0.055 meter
Mcore0.015 kilogram
Mroll0.215 kilogram
L47 meter
tension0.0002 newton
t_end120 second
\n", - "
" - ], - "text/plain": [ - "Rmin 0.02 meter\n", - "Rmax 0.055 meter\n", - "Mcore 0.015 kilogram\n", - "Mroll 0.215 kilogram\n", - "L 47 meter\n", - "tension 0.0002 newton\n", - "t_end 120 second\n", - "dtype: object" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(Rmin = 0.02 * m,\n", - " Rmax = 0.055 * m,\n", - " Mcore = 15e-3 * kg,\n", - " Mroll = 215e-3 * kg,\n", - " L = 47 * m,\n", - " tension = 2e-4 * N,\n", - " t_end = 120 * s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`make_system` computes `rho_h`, which we'll need to compute moment of inertia, and `k`, which we'll use to compute `r`." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Make a system object.\n", - " \n", - " params: Params with Rmin, Rmax, Mcore, Mroll,\n", - " L, tension, and t_end\n", - " \n", - " returns: System with init, k, rho_h, Rmin, Rmax,\n", - " Mcore, Mroll, ts\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " init = State(theta = 0 * radian,\n", - " omega = 0 * radian/s,\n", - " y = L)\n", - " \n", - " area = pi * (Rmax**2 - Rmin**2)\n", - " rho_h = Mroll / area\n", - " k = (Rmax**2 - Rmin**2) / 2 / L / radian \n", - " \n", - " return System(init=init, k=k, rho_h=rho_h,\n", - " Rmin=Rmin, Rmax=Rmax,\n", - " Mcore=Mcore, Mroll=Mroll, \n", - " t_end=t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `make_system`" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
inittheta 0 radian\n", - "omega 0.0 radi...
k2.7925531914893616e-05 meter / radian
rho_h26.07109543981524 kilogram / meter ** 2
Rmin0.02 meter
Rmax0.055 meter
Mcore0.015 kilogram
Mroll0.215 kilogram
t_end120 second
\n", - "
" - ], - "text/plain": [ - "init theta 0 radian\n", - "omega 0.0 radi...\n", - "k 2.7925531914893616e-05 meter / radian\n", - "rho_h 26.07109543981524 kilogram / meter ** 2\n", - "Rmin 0.02 meter\n", - "Rmax 0.055 meter\n", - "Mcore 0.015 kilogram\n", - "Mroll 0.215 kilogram\n", - "t_end 120 second\n", - "dtype: object" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(params)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
theta0 radian
omega0.0 radian / second
y47 meter
\n", - "
" - ], - "text/plain": [ - "theta 0 radian\n", - "omega 0.0 radian / second\n", - "y 47 meter\n", - "dtype: object" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.init" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we compute `I` as a function of `r`:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def moment_of_inertia(r, system):\n", - " \"\"\"Moment of inertia for a roll of toilet paper.\n", - " \n", - " r: current radius of roll in meters\n", - " system: System object with Mcore, rho, Rmin, Rmax\n", - " \n", - " returns: moment of inertia in kg m**2\n", - " \"\"\"\n", - " unpack(system)\n", - " Icore = Mcore * Rmin**2 \n", - " Iroll = pi * rho_h / 2 * (r**4 - Rmin**4)\n", - " return Icore + Iroll" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When `r` is `Rmin`, `I` is small." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "6e-06 kilogram meter2" - ], - "text/latex": [ - "$6e-06 kilogram \\cdot meter^{2}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "moment_of_inertia(system.Rmin, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As `r` increases, so does `I`." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.00037418750000000006 kilogram meter2" - ], - "text/latex": [ - "$0.00037418750000000006 kilogram \\cdot meter^{2}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "moment_of_inertia(system.Rmax, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "Write a slope function we can use to simulate this system. Here are some suggestions and hints:\n", - "\n", - "* `r` is no longer part of the `State` object. Instead, we compute `r` at each time step, based on the current value of `y`, using\n", - "\n", - "$y = \\frac{1}{2k} (r^2 - R_{min}^2)$\n", - "\n", - "* Angular velocity, `omega`, is no longer constant. Instead, we compute torque, `tau`, and angular acceleration, `alpha`, at each time step.\n", - "\n", - "* I changed the definition of `theta` so positive values correspond to clockwise rotation, so `dydt = -r * omega`; that is, positive values of `omega` yield decreasing values of `y`, the amount of paper still on the roll.\n", - "\n", - "* Your slope function should return `omega`, `alpha`, and `dydt`, which are the derivatives of `theta`, `omega`, and `y`, respectively.\n", - "\n", - "* Because `r` changes over time, we have to compute moment of inertia, `I`, at each time step.\n", - "\n", - "That last point might be more of a problem than I have made it seem. In the same way that $F = m a$ only applies when $m$ is constant, $\\tau = I \\alpha$ only applies when $I$ is constant. When $I$ varies, we usually have to use a more general version of Newton's law. However, I believe that in this example, mass and moment of inertia vary together in a way that makes the simple approach work out. Not all of my collegues are convinced." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def slope_func(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object with theta, omega, y\n", - " t: time\n", - " system: System object with Rmin, k, Mcore, rho_h, tension\n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " theta, omega, y = state\n", - " unpack(system)\n", - " \n", - " r = sqrt(2*k*y + Rmin**2)\n", - " I = moment_of_inertia(r, system)\n", - " tau = r * tension\n", - " alpha = tau / I\n", - " dydt = -r * omega\n", - " \n", - " return omega, alpha, dydt " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test `slope_func` with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " ,\n", - " )" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "slope_func(system.init, 0*s, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[]
nfev92
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events []\n", - "nfev 92\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "results, details = run_ode_solver(system, slope_func, max_step=10*s)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And look at the results." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
thetaomegay
83.775812106.0191582.60317941.325889
93.775812133.8123482.95813339.890335
103.775812165.2405993.33074338.293013
113.775812200.4981803.72467136.533897
120.000000224.4783293.98256535.357283
\n", - "
" - ], - "text/plain": [ - " theta omega y\n", - "83.775812 106.019158 2.603179 41.325889\n", - "93.775812 133.812348 2.958133 39.890335\n", - "103.775812 165.240599 3.330743 38.293013\n", - "113.775812 200.498180 3.724671 36.533897\n", - "120.000000 224.478329 3.982565 35.357283" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Check the results to see if they seem plausible:\n", - "\n", - "* The final value of `theta` should be about 220 radians.\n", - "\n", - "* The final value of `omega` should be near 4 radians/second, which is less one revolution per second, so that seems plausible.\n", - "\n", - "* The final value of `y` should be about 35 meters of paper left on the roll, which means the kitten pulls off 12 meters in two minutes. That doesn't seem impossible, although it is based on a level of consistency and focus that is unlikely in a kitten.\n", - "\n", - "* Angular velocity, `omega`, should increase almost linearly at first, as constant force yields almost constant torque. Then, as the radius decreases, the lever arm decreases, yielding lower torque, but moment of inertia decreases even more, yielding higher angular acceleration." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot `theta`" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3Xl8XNV99/HPaJe1WJstyZsWYx+v2I5tjG1siCGUJSE025M8pBRok5DWaZ6+2jRtQwIlNGnShqQ0hDRNGtKUpDzNQwqEnQQwtsF4N3g5XiXLlmVZ+77Off64o9FIluSRpdmk7/v10ku6597R/K7Hmt+ce885P4/jOIiIiESbuEgHICIiMhQlKBERiUpKUCIiEpWUoEREJColRDqAUDHGJAOrgXNAb4TDERGRocUDhcBOa21n4I4Jm6Bwk9ObkQ5CRESCsgHYGtgwkRPUOYAnnniCgoKCSMciIiJDqKqq4o477gDfe3agiZygegEKCgqYNWtWpGMREZGRXXQrRoMkREQkKilBiYhIVFKCEhGRqKQEJSIiUUkJSkREopISlIiIRCUlKBERGbVer8OeI9Vs3X+Wzu7QLNYzkedBiYhIiGzbf5YDx2sASElKYNXC/HF/DvWgRERkVI6ervcnJ4CsjOSQPI8SVJQ7c+YMxhh6enoiHYqICHVNHby2u8K/PXfmVObOnBqS51KCikKbNm1i+/btY/49Sm4iMp66unt5YXsZ3T1eALLSk9m0eg4ejyckz6cEJSIil+Q4Dq/trqC+uQOAhPg4bl5XTHJifMieUwkqynzpS1+isrKSe++9lxUrVvDCCy8A8Oyzz3LdddexZs0aHnvsMf/xXq+XH/3oR9xwww2sWbOGL37xizQ0NADw6U9/GoDVq1ezYsUK9u7dy+nTp7nzzjtZs2YNa9as4S/+4i9oamoK/4mKSEx590QNxyoa/NvXrZxF7tTUkD6nx3GckD5BpBhjioFTv/3tby+5mvleW807h6r83dZQSEyI46pFBaww0y957KZNm3jooYdYt24dZ86c4frrr+fjH/849913H2VlZXzsYx/j6aefZu7cuTz++OM8//zzPPLII+Tk5PDQQw/R0tLCww8/7H/swYMHSUhwB2yWl5dz5swZVq9eTUtLC1/4whdYtGgRX/nKV0J27iIS26pqW3nq9eN4vW6+WFKay3UrZ4/L7+57nwJKrLVlgfvUgwL2Hb0Q0uQE0N3jZd/RC5f9+M2bN5OSksKCBQtYsGABR44cAeDJJ5/kz//8zykoKCApKYnNmzfz0ksvDXvfqaioiPXr15OUlEROTg533303O3fuvOy4RGRia+vo5sW3yvzJaXr2FK5ZPjMsz615UMDy+dPC0oNaPn/aZT8+Ly/P/3NqaiptbW0AVFZW8qd/+qfExfV/1oiLi6O2tnbI31NbW8tDDz3Erl27aG1txXEcMjMzLzsuEZm4vF6HV945TUt7NwDJSfHctLaYhPjw9G2UoIAVZnpQl96iUUFBAd/4xjdYuXLlRfvOnj17Udt3vvMdPB4PzzzzDNnZ2bz66qs8+OCD4QhVRGLMO4eqqDjf7N++8aoiMtOSwvb8usQXhfLy8qioqLj0gcCnPvUpvve97/mTUV1dHa+++ioAOTk5xMXFDfhdra2tTJkyhczMTM6fP8+Pf/zj8T8BEYl5Zeea2HX4vH979cJ8igrDe7VFCSoKffazn+Wxxx5j1apVvPTSSyMee+edd7Jp0ybuueceVqxYwSc+8QkOHDgAuJcC7733Xj71qU+xatUq9u3bx+bNmzl06BCrVq3is5/9LDfeeGM4TklEYkhTaxevvFPu356dn8HqRQVhj0Oj+ERExK+n18tTrx2nut69z52emsgnbpjPlJTEkDyfRvGJiEhQ3tx31p+c4uI83LS2OGTJ6VKUoEREBIAj5XUcPNk/AviaZTMoyE2LWDxKUCIiQk1DO6/vPuPfnjc7m6Vz80Z4ROgpQYmITHKd3b28+FYZPb3uXNCczBQ2rZoVskVgg6UEJSIyiTmOw+92nqahpRNwFxW4aW0xiQmhWwQ2WEpQIiKT2N6jFzhxttG/vWnVbHIyUyIYUT8lKBGRSaryQgtvv3vOv33lFXnMm50dwYgGCttSR8aYZOAHwA1ADnAc+Ftr7Qu+/dcDjwJzgB3AXdba8oDHPgZ8DGgDvm2tfThcsYuITDSt7d28+HY5Xt9c2ILcNNZfOSPCUQ0Uzh5UAlABXAtMBb4K/F9jTLExJg94yteWA+wCngx47APAPKAIeD/wV8aYm8IXuojIxOH1Orz0djltHe4isKnJCdx0dRHxYVoENlhh60FZa1txE02f3xhjTgErgVzgoLX2vwGMMQ8ANcaYBdbaI8CdwN3W2nqg3hjzb8BdwIvhil9EZKLYuv8slTUtAHg8Hm5cU0T6lPAtAhusiKVLY0w+MB84CCwG9vft8yWzE8BiY0w2MCNwv+/nxeGLVkRkYnj3eA0Hjtf4t9csLmB2fkYEIxpeRBKUMSYReAL4ma+HlA40DjqsEcjw7WPQ/r59IiISpNNVTby5r78Mz9xZWaxcEL2lhsKeoIwxccDPgS5gs6+5BRi8jnsm0Ozbx6D9fftERCQIdU0dAwZFTM+ewg2r50R8Mu5IwpqgjDEe4CdAPvBRa223b9dBYFnAcWnAXNz7UvXAucD9vp8PhiVoEZEY19bRzW+2nqSruxdwVyi/dX0JiQnRNShisHBX1H0MWAjcYK1tD2j/NfCPxpiPAs8BXwMO+C7/AfwHcJ8xZhducvsMcHf4whYRiU29vV5efKuMptYuwF0p4tb1paSlRmaF8tEI5zyoIuBzQCdQZYzp2/U5a+0TvuT0feA/cedBfTLg4ffjJrdyoB34lrVWI/hEREbgOA6v7a6gsqYV6B+xNy07NcKRBSecw8zLgWEvdlprXwUWDLOvE7jH9yUiIkHYY6s5Ul7v3167tJCSGVMjGNHoRPcFSBERuSwnzjTwVsAyRotKclgxf1oEIxo9JSgRkQmmuq6NV9457d+eOS2da1dEvnzGaClBiYhMIC3t3Ty//ZS/tlNWejI3ry2OumWMghF7EYuIyJC6e3p5bttJWtrdGTzJSfHcur6ElORwD9geH0pQIiITgOM4vPrOaS7UuzN44jwebrq6mOwoqe10OZSgREQmgLffOzeg8OC175sVtWvsBUsJSkQkxh0pq2P3kWr/9rJ501hcmhvBiMaHEpSISAyrvNDC73ZX+LeLCzOjrvDg5VKCEhGJUY0tnTy/vQyv110ANndqKjeuKSIuLraGkw9HCUpEJAZ1dPXwm62n6OjqAdyquLeuLyEpMT7CkY0fJSgRkRjTV7K9vrkDgPg4D7euLyEzLfqq4o6FEpSISAxxHIct+85Scb6/JN71q+dQkJsWwahCQwlKRCSGvHuihvdO9Jdsv2pRAfPnZEcwotBRghIRiRHl55p4c1+lf3ve7GxWL8qPYEShpQQlIhIDahvbeWlHOY6vZHt+zhSuXz075haAHQ0lKBGRKNfW0c1z205dVLI9IQYXgB2NiX12IiIxrqfXywvbB5Zs/+A1pUxJif6S7WOlBCUiEqUcx+G1XRWcqx1Ysj0vKzZKto+VEpSISJTafaQae7q/ZPv6K2OrZPtYKUGJiESh4xUNvP1ef8n2xaW5LJsXWyXbx0oJSkQkypyva+PVnf0l22dNz2BjDJZsHyslKBGRKNLS1sVz2wJKtmckc9PaIuInyAKwo6EEJSISJdyS7ado6+gv2f7B9aWkJMVmyfaxUoISEYkCXq/DyztOc6Ghv2T7zWuLycpIjnBkkaMEJSISYY7jsP3dSk5VDizZPmt6bJdsHyslKBGRCNt9pJp9Ry/4t1eY6ROiZPtYKUGJiETQgeMXBgwnL505lbVLCiMYUfRQghIRiZDDp+rYsvesf3vW9IwJVbJ9rJSgREQi4PiZBn63u8K/XZCbxq3riyf8ArCjoX8JEZEwKz/XxMsBpTOmZaXywWtKSEyIj3Bk0UUJSkQkjM5eaOGFt8rwet3klJWRzIc2TN65TiNRghIRCZPzdW0DVonITEvi9o1zJ0XpjMuhBCUiEga1je08++ZJf9HBtJREPrxxLulTkiIcWfRSghIRCbGG5k6e3nKSjq4eAFKSErhtYylT0yfvKhHBUIISEQmhlrYunnnzhH99vaTEeG7bUEru1MlRdHAslKBEREKkraOb/9lywl+uPSE+jg+uL2F6zpQIRxYbwjpsxBizGbgLWAr80lp7l6+9GDgFtAYc/i1r7dd9+5OBx4CPAW3At621D4ctcBGRUero6uGZN0/S0NwJQFych5vXFTNjWnqEI4sd4R7XWAk8BPweMFT/Nsta2zNE+wPAPKAIKABeM8Ycsta+GKpARUQuV3dPL7/Zeooa38rkHo+HG9cUUVSQGeHIYktYL/FZa5+y1v4PUDvKh94JfN1aW2+tPQz8G25PTEQkqvT0enluWxlVtf0XhK5fNZsrZmVFMKrYFG0zw8qNMQ7wCvAla22NMSYbmAHsDzhuP3B7JAIUERlOr9fhpbfKOFPd7G/buGImC4pzIhdUDAsqQRljpgHrcC+xpQI1uElij7XWOw5x1ACrgX1ALvAo8ATupcC+C7aNAcc3ApO7UIqIRBWv1+HVd05z6lyTv+3qJYVcecW0CEYV20ZMUMaYTwH3Ahtw7x9VAR1ANlAKtBhjfgF811pbdrlBWGtbgF2+zfO+wRTnjDGZQIuvPdP33H0/NyMiEgUcx+GNvWc4VlHvb3ufmc7KBdMjGFXsG/YelDHmXeCPgMeBQmvtLGvtKmvtNdbaxbg9mA8C3cA2XzIbL47vu8daWw+cA5YF7F8GHBzH5xMRuSyO47DtQCUHT/bfWl8yN4+1SwvxeFQ2YyxG6kH9kbX2neF2+kbb7QB2GGPuB+Zc6smMMQm+54wH4o0xKUAPsBJoAI7h9s4eAV631vZd1vsP4D5jzC4gH/gMcPelnk9EJNR2Hj4/oBrugqJsrl0xU8lpHAyboEZKTkMc2wocDuLQ+4D7A7Y/DfwdYIFvANOBJtxBEoE9svtx50GVA+24c6Q0xFxEImrf0WreOVjl3547cyqbVs1RchonwyYoY8zGYH+JtXZLkMc9gDunaSi/HOFxncA9vi8RkYg7eLKWrfsr/duz81UNd7yNdInv9UHb/vtCg7bBvWQnIjIpHKuo5/U9Z/zbhblp3LKumHhVwx1Xw/5rWmvj+r6Am4HdwE3AVN/XTbgj724NR6AiItHgVGUjr+w43V8NNzuVD24oVTXcEAh2ou4/A39ord0R0PaKMaYZ+DnuMkQiIhPamepmXnyrDK8vOeVkpvCha0pJTlRyCoVg+6NzgKEm5HqBmeMXjohIdKqqbeW5bafo9ZVqz0xL4jZVww2pYHtQLwA/NcZ8AdiJe//pKtzh4C+EKDYRkahQ09DOs1tP0t3jfk5PT/VVw01VcgqlYHtQd+Mmppdwlxlq8v28G3cyr4jIhFTf3MHTW07Q2eWWak9NTuC2jXNVDTcMgupBWWubgLuNMX+Gu8SRBzhhrdVyQyIyYTW1dvH0Gydo73SrACUlxvOhDaXkZKZEOLLJYVSrmfsS0v5LHigiEuNa2rt5ZssJWtrdUu2J8XF86JpSpmerGm64BJ2gjDE34Va0nQ0MuPBqrd00znGJiERMY0snTweUao+P83DL+hIK89IiHNnkEtQ9KN/giCdxlxm6Dndpoi7cNfTeDlVwIiLhVtvYzv977bg/OcX5quHOzleFn3ALdpDEZuAea+0XcBPTP1lrbwK+BajYiYhMCFW1rTz1+nHaOtzLegnxcdy6voS5qoYbEcEmqFn012tqxV1JAtxe1cfHOygRkXCrON88YLReUmI8t20opagwM8KRTV7BJqjTuGXXAY7Sv7zRevqLCIqIxKSTZxv5TcA8p9TkBG7fOJcZ09Iv8UgJpWAT1M+ANb6fvwl8xRhTDfw78N1QBCYiEg5Hyut48a0y/woR6amJfOS6K5ieo9F6kRbsPKh/CPj5BWPMAtwBEiestftCFZyISCgdOH6BLXvP+rez0pO5beNcMtOSIhiV9LlkgjLGJAEngQ9Yaw8DWGvLgLKQRiYiEiKO47Dz8PkBxQbzslK5bUOp1taLIpe8xGet7QK6GeWkXhGRaOQ4DtsOVA5ITgW5adx+rRZ+jTbB3oP6GvBtY0xhKIMREQklr9fhd7sq2Hf0gr9tdn4GH95YSkqSPoNHm2BfkQeBfOCMMeYC0Ba401pbOt6BiYiMp95eLy/vKOfE2UZ/29yZU7lxTZEq4UapYBPUA6EMQkQklLp7enl+exkV5/vXt15YnMP7V84mLs4TwchkJMGO4vtZqAMREQmFjq4entt6inO1rf62ZfOmcc2yGXg8Sk7RbNh+rTFmxnD7hjm+YOzhiIiMn7aObv7njRMDktNViwuUnGLESD2oHcaYl4CfA1ustc7gA4wx03GXOvo88CjwWEiiFBEZpabWLp7ZcoKGlk5/24blM1k2T8uHxoqREtRC4Iu4CSrLGLMfOAd0AlnAAqAIeBm411q7NcSxiogEpb7JrYLbV8vJ4/Fw/erZLCjKiXBkMhrDJihrbQvw98aYbwBrfV9zgBSgAngCeN1aWzXc7xARCbfq+jaeffOkvwpufJxbLkMrkseeSw6S8F3a2+77EhGJWpUXWvjNtlN0dbsrkicmxHHLuhLVcopRmpkmIhNC+bkmXnirjJ5ed0Xy5KR4PnRNKQW5qoIbq5SgRCTmHauo55Udp/E67liuKSmJfHhjKblTUyMcmYyFEpSIxLSDJ2t5fc8ZHF9yykxL4rYNc8nKSI5wZDJWSlAiErP2HKlm+7uV/u2czBRu2ziX9FQt+joRjCpB+eY9lQJ7rbWdlzpeRCQUHMfh7ffOsftItb9tevYUPrShlNRkfe6eKIJ6JY0x2bhVdT8IOMA84KQx5jGgzlr7ldCFKCLSz3Ec3thzhvdO1vrbZk5L59b1JSQlxkcwMhlvwS7h+wjgAWYxcCXzXwO/P95BiYgMpdfr8PKO0wOSU0lhJh/aUKrkNAEFm6BuAr5sra0c1H4MdzUJEZGQ6ujq4bltJzlWUe9vmz8nm5vWlZCgchkTUrAXa4c7bgbQMk6xiIgMqb65g+e2naKhuf/W95K5eVy7YqYWfZ3Agv3Y8TzwJWNM3/8ExxiTA3wDeCYkkYmI4E7A/dVvjw1ITqsX5is5TQLB9qC+CPwKOA2k4ialYuAg8KWQRCYik5rjOOw9eoG33j3nn+OUEB/H9atnM292doSjk3AItmBhDXCdMWYjsARIB/YDLw9VhmM4xpjNwF3AUuCX1tq7AvZdj1uyYw6wA7jLWlvu25eMW8rjY7iDNL5trX042OcVkdjS0+vltV0V2NP995vSUxO5ZX0J07OnRDAyCadRTRiw1m4Btozh+SqBh4Dfw+2JAWCMyQOeAv4YeBb4OvAkcLXvkAdwh7YXAQXAa8aYQ9baF8cQi4hEoZb2bl7Yforzdf0Dhgtz07h5XTFTUjQBdzIZNkEZY74W7C+x1j4Y5HFP+X73Ktwh630+Ahy01v63b/8DQI0xZoG19ghwJ3C3tbYeqDfG/BtuT0wJSmQCOV/XxvPbTtHa0e1vW1SSw7UrZhGvkXqTzkg9qPcH+TscIKgENYLFuJcMAbDWthpjTgCLjTHncUcL7g84fj9w+xifU0SiyJHyOl7bVUGv171rEOfxcM3yGSydm6fBEJPUSAULg01Q4yEduDCorRHI8O3r2x68T0RinNfr8NZ759hr+5ctSk6K56ari1XHaZKLlkWrWoDMQW2ZQDP986wygY5B+0QkhnV09fDyjnJOV/X/OedkpnDLuhKtRi5Br8X3Gu6lvMEcoBM4AfyntXbHZcZxEPjDgOdLA+bi3peqN8acA5YBr/gOWeZ7jIjEqKEm35YUZvKBNUVatkiA4HtQO4DPA/uAXb62lcAK4KeAAbYZYz5irR124q4xJsH3nPFAvDEmBejBXdPvH40xHwWeA74GHPANkAD4D+A+Y8wuIB/4DHB30GcpIlGlvKqJl98up9NXmh1g5YJ8rl5SoPtN4hdsgpoBfMNa+63ARmPMXwFLrLU3G2P+Bnc4+EgrS9wH3B+w/Wng76y1D/iS0/eB/8RNiJ8MOO5+3HlQ5UA78C0NMReJPY7jsO/oBbYPmny7adVs5s/R5FsZKNgE9fsMPVLv18BXfT//NzBi2Q1r7QO4SWyofa8CC4bZ1wnc4/sSkRjU0+vl9d0VHCkfNPl2XQnTczT5Vi4W7MSCOuDDQ7Tf5tsHkIYGLojIEFrau/n168cHJKfC3DQ+ccN8JScZVrA9qL8Cfm6MuRXYjTs4YiWwAfgD3zHXoIVjRWSQ83VtvLD9FC3tmnwroxPsWnxPGmP24C5FNB+3eOEu4HPW2uO+Yx4NWZQiEpNseR2/GzT5dv2yGVx5hSbfyqUFPQ/KWnsM+HIIYxGRCUKTb2U8BJ2gfPWfVgHTGXTvylr7H+Mcl4jEqM7uXl5+u5zyqiZ/mybfyuUIdqLux4DHcS/tXWDgpF0Hd56SiExymnwr4ynYHtS3gYdx5yz1XupgEZl8hpt8u2ZxAXFxut8koxdsgsoDHldyEpHBHMdh/7ELbDugybcyvoId4/lfwC2hDEREYk93j5ff7qxg6/5Kf3JKT03kI9ddoeQkYxZsD6oGeNAYcwPwLtAduDPYgoUiMnFU17Xx8jvlA+43FeSmcYsq38o4CTZBrcUtEjgVd0JuoPEoWCgiMcLrddhjq3nnYBVep3+81MLiHK57nybfyvgJdqJuOIsXikiUamrt4tV3TlNZ0+JvS0yIY8PymSwsztHkWxlXl12w0Fcq4/eBO621N49fSCISjY6erueNPWcGjNIryE3jA1fNYWq65jfJ+Bt1gjLGXAfcCXwUd3HYZ8c5JhGJIh1dPbyx5yzHKvoXeo3zeFi1KJ9VC/I1hFxCJtiJugY3Kd2BO/JvBvAJa+1TIYxNRCLs7IUWXn3nNM1tXf62zLQkblxTREFuWgQjk8lgxARljNmMm5hmA7/CLTC4DegCjozwUBGJYb29Xt45VMUee8E/fBzcgRAbls/UqhASFpfqQX0P+C7wFWut/yOU26ESkYmovqmDl98p50J9u78tOSme96+czRWzsiIYmUw2l0pQm3HrPZ0xxvwad8LuGyGPSkTCznEc3jtRy7YDlfT0ev3ts/MzuH71HNJTNbdJwmvECQvW2h9aa9cD64Aq4MfAWd/jlhhjdHdUZAJo6+jmuW2neGPvGX9yio/zcM2yGdy2oVTJSSIiqBl11trj1tr7rbVzgf8F/BT4V6DKGPPjUAYoIqF1qrKRX75sKTvXXx4jNzOFj18/n+Xzp2tuk0TMqIeZW2u3AFuMMX+KOw/q0+MelYiEXHePl20HKnnvRM2A9mXzprF2aSEJWhFCIuyyJ+paaztx70n91/iFIyLhMNQ6emkpiVy/ejZzCjIjGJlIv8tOUCISe7xeh71Hq9nx3sB19ObOnMp1K2eTmqy3BIke+t8oMkloHT2JNUpQIpPAUOvo5edM4QNXFZGVoXX0JDopQYlMYB1dPWzZe5ajp/vX0fN4PKxemM/KhfnEax09iWJKUCITlNbRk1inBCUywWgdPZkolKBEJpAz1c1s2XuWuqYOf5vW0ZNYpQQlMgG0tHWx7UAlxyoaBrTPmp7BDVdpHT2JTUpQIjGst9fL/mM17DxcRXdP/wKviQlxXL24kCvn5Wn4uMQsJSiRGFVx3r2cV9/cMaB93uxs1i+boV6TxDwlKJEY09zWxdb9lZw4M/ByXm5mChvfN4uZ09IjFJnI+FKCEokRvb1e9h69wO7D5+kOqNeUlBjPVYvyWXrFNM1rkglFCUokBpRXNfHm3rM0tHQOaDdzsll35QzSdDlPJiAlKJEo1tTaxdb9Zzl5tnFAe15WKhuXz2SGLufJBBZVCcoY8zpwNdDjazprrTW+ff8b+CaQB7wC3GOtrYtEnCKh1tPrZa+tZveR6gHl15MT41mzpIAlpXnE6XKeTHBRlaB8NltrB1TpNcYsxq3geyuwB/gR8APgk+EPTyS0ys418ea+szQOupy3sDiHtUsLmZKiy3kyOURjghrKHcCzvmq+GGO+Chw2xmRYa5sjG5rI+Ghs6WTrvrOcCii9DjAtK5WNK2ZRmKf182RyicYE9U1jzD8AFviKtfZ1YDGwve8Aa+0JY0wXMB/YHZEoRcZJT6+XPUeq2X3kPL3e/rXzkpPiuXpxIYtLc3U5TyalaEtQXwYOAV24l++eNcYsB9KBxkHHNgIZ4Q1PZPw4juO/nNfU2jVg36KSXK5eUqDLeTKpRVWCstbuCNj8mTHmU8AtQAuQOejwTECX9yQmNTR38ua+s5RXDbycNz17Cte+bxb5OVMiFJlI9IiqBDUEB/AAB4FlfY3GmFIgGTgaobhELkt3j5fdR86z11YPuJyXkpTA2qWFLCzO0eU8EZ+oSVDGmCxgDfAG7jDz/wVsBP4PbpxvGWM24I7iexB4SgMkJFY4jsPJs41s3V85oICgx+NhcUkOVy8pJCU5av4cRaJCNP1FJAIPAQuAXuAIcLu11gIYY+4FngBygVeBuyMUp8ioVNW2suNgFRXnB36eys+ZwrUrZjFdl/NEhhQ1CcpaewFYPcL+XwC/CF9EImNTWdPCzkPnL0pMqcn9l/NUCkNkeFGToEQmAsdxqKxpZeehKs5UtwzY5/F4WFKay5olBaQk6U9P5FL0VyIyDhzH4Ux1CzsPVVFZ0zpgn8fjwczJYuWCfLIzUyIUoUjsUYISGQPHcThd1czOw+epqh2YmOI8HkxRNisX5JOVkRyhCEVilxKUyGXom2S789B5quvbBuyLi/OwsDiH95npTE1XYhK5XEpQIqPQN1x81+HzXGhoH7AvPs7DopJc3rdgOhlTkiIUocjEoQQlEgSv1+HE2QZ2HTpPbVPHgH0J8XEsLsllxYLppKtwoMi4UYISGYHX63Csop7dR6qpGyIxLZmby4r501XRViQElKBEhuD1Ohw9Xc+uw+cvKrOemBDH0rl5LJ8/TYu5ioSQEpRIgN4SBT0iAAAPLUlEQVReL9aXmAavMJ6UGM+VV+SxbN40UrUskUjI6a9MBDcxHS6rY/eR6gFr5YFbl2nZvGlceUWeJtiKhJH+2mRS6+n1cuhULXuOVNPS3j1gX0pSAsvnT2PpFXkkJ8ZHKEKRyUsJSialru5eDp+qY4+tprVjYGJKTU5gxfzpLJmbS5ISk0jEKEHJpOE4DlW1bRwuq+VYRQPdPd4B+6ekJLJi/jSWzM0lMUGJSSTSlKBkwmvr6MaW13O4rO6ioeIA6amJrDDTWVyaS0J8XAQiFJGhKEHJhOT1OlScb+ZQWR2nzjbidZyLjsnJTGHp3DwWluQoMYlEISUomVAaWzo5UlbH4bK6iwY9gDuHad7sbBaV5JCfM0X1mESimBKUxLyeXi8nzzZy6FQdZ6qbhzymIDeNRSU5zJudpftLIjFCCUpiVk1DO4dO1WJP19PZ1XvR/tTkBBYU5bCwJIcc1WESiTlKUBJTOrt7OXa6nkOn6i4qcwFuccA5+RksLMmhpDCTeN1bEolZSlAS9RzH4VxNK4dO1XL8TCM9vd6LjslMS2JhcQ4Li3NIV6kLkQlBCUqiVltHN0fK6jlUVktDc+dF++PjPJTOzGJRSQ6zpqdrwIPIBKMEJVHF63Uor2ri0Kk6ys81DTk8PC8rlYXFOZg52aRo0VaRCUt/3RJxvb1eztW2Ul7VzNHy+ouWHgJ3JfH5s7NYVJLLtOxU9ZZEJgElKImIlrYuyquaOV3VREV1C13dF4/CA5iRl86i0hzmzswiMUEDHkQmEyUoCYter0NVbSvl55oor2qmtrF92GOnpCSyoCibhSU5ZGdoeLjIZKUEJSHT0t7N6aomys+N3EsCdxTenIJMigszmZ2fQXycLuGJTHZKUDJuer0O52tbKa9ye0k1DcP3kuLiPMyclk5RQQZzCjLJzkjWfSURGUAJSsbE30uqaubM+WY6R+glZUxJoqggg6LCTGZNT9eSQyIyIiUoGRWv16GqrpXyc+4AhwuX6CXNyEtnTkEGxYXqJYnI6ChBySW1dXRTfq6Z8qomKqqbh1z3rk96aiJFhZkUFbi9JFWkFZHLpQQlA3T39FLT0EFNYzs1De1U17WN3EvyeJgxLY05BZkUFWSQk5miXpKIjAslqEnKcRxaO3qoaXAT0YWGdmob2mls7cIZYvWGQH29pDn5GczOz1AvSURCQglqEuj1OjQ0d/iSkPu9pqGdjq6eoB4f5/FQmJdGUUEmRYXqJYlIeChBTTAdXT3UNnb4e0Y1De3UNXXQ6x25V9QnzuMhOyOZ3KxU8rJSyZuaQn5uGsnqJYlImClBxSjHcWhu6+5PRL57Rk2tXUH/jqTEePKmppCXlUru1FSmZaWSMzWFBNVQEpEooAQVhbxeh46uHto6emjvdL/aOrr9Pzc0d1Hb2D7inKPBMqYk+XtEeb7eUWZaki7ViUjUipkEZYzJAX4C3AjUAH9jrf1FZKMKjuM49PR6ByWcgYkncF9HV+8lByoMJy7OQ05mCnm+HlFediq5mSkqSyEiMSeW3rUeBbqAfGA58JwxZr+19mConrCjqwev18Hrdej1Ongdx7cNXseh1+v17+vs6h3Qy2nv6KEtIBkNVQV2rJKT4t0klJVK3lT3e3ZGssqci8iEEBMJyhiTBnwUWGKtbQG2GmOeAf4A+Ovxfj7HcXjhrTJOnm0c718dtJSkBKakJJCa3P/d/TmRKSkJTMtKJS01UZfoRGTCiokEBcwHeq21RwPa9gPXhuLJ2jt7xj05JcTHDUg0QyWevraUpATitJq3iExysZKg0oHBGaMRyAjFk01JSWTlgnyOlNXhdRzi4zzExXmI8/i+x3nctoDtlKT4AYlmcAJKTIhTb0dEZBRiJUG1AJmD2jKB5lA94dqlhaxdWhiqXy8iIpcQK3fTjwIJxph5AW3LgJANkBARkciKiQRlrW0FngIeNMakGWPWAx8Gfh7ZyEREJFRiIkH5/AmQClQDvwQ+H8oh5iIiElmxcg8Ka20dcHuk4xARkfCIpR6UiIhMIkpQIiISlZSgREQkKsXMPajLEA9QVVUV6ThERGQYAe/RFxWdm8gJqhDgjjvuiHQcIiJyaYXAicCGiZygdgIbgHNA8IWTREQknOJxk9POwTs8l1t3SEREJJQ0SEJERKKSEpSIiEQlJSgREYlKSlAiIhKVlKBERCQqKUGJiEhUmsjzoC6bMSYH+AlwI1AD/I219heRjeryGGOSgR8ANwA5wHHgb621L/j2Xw88CswBdgB3WWvLIxTuZfEVsnwX+JW19tO+tv8NfBPIA14B7vGtiB9TjDGfBO7HfX2qcF+fN2P5dTPGFOP+n1wLdAK/Av6PtbbHGLMc929vIXAY+CNr7b5IxXopxpjNwF3AUuCX1tq7AvYN+xr5/i4fAz4GtAHfttY+HNbgL2G4czPGXA18HViJO8f0deDPrLXnfPs9wD8Af+z7VT8BvmytHfWcJvWghvYo0AXkA3cAjxljFkc2pMuWAFQA1wJTga8C/9cYU2yMycMtBPlV3OS1C3gyUoGOwaMETPLzvVb/CvwB7mvYhvuGGFOMMR8AvgXcDWQAG4GTE+B1+wFuXbdCYDnu/80/McYkAU8D/wlkAz8Dnva1R6tK4CHg3wMbg3iNHgDmAUXA+4G/MsbcFIZ4R2PIc8N9bX4EFOPG3wz8NGD/Z3FLIy0DrgQ+CHzucgJQD2oQY0wa8FFgibW2BdhqjHkG983uryMa3GXwVSN+IKDpN8aYU7iffnKBg9ba/wYwxjwA1BhjFlhrj4Q71svh62E0ANuBK3zNdwDPWmu3+I75KnDYGJNhrW2OTKSX5e+AB621b/u2zwIYYz5LbL9uJcD3rbUdQJUx5kVgMXAd7nvS93yfth8xxvwlsAl4MVLBjsRa+xSAMWYVMCtg10cY+TW6E7jbWlsP1Btj/g23txI15zncufVdfeljjPk+8EZA0x8C37HWnvHt/w7wGeCHo41BPaiLzQd6rbVHA9r24/4BxTxjTD7uOR7EPaf9fft8yewEMXKuxphM4EHgLwbtGnxeJ3B7xPPDF93YGGPigVXANGPMcWPMGWPM940xqcT46wb8M/BJY8wUY8xM4GbcN+bFwIFBl4IOEDvnFWjY18gYkw3MCNxPbL/HbMR9P+kz4NwZw7kpQV0sHWgc1NaIe4klphljEoEngJ/5PsXF+rl+HfiJtbZiUHusnxe4lyYTce9RbMC9FLYCuI/YP783cN+wmoAzuJe//ofYP69AI51LesD24H0xxRhzJfA14EsBzYPPvRFI992bGhUlqIu1AJmD2jJxr7PGLGNMHPBz3J7EZl9zzJ6r72b6DcB3h9gds+cVoN33/V+steestTXAw8AtxPD5+f4fvoR7fyYNdxBLNu69tpg9ryGMdC4tAduD98UMY8wVwAvAF621bwbsGnzumUCLBkmMj6NAgm9kWJ9lDOzCxhTfJ5ef4H4q/6i1ttu36yDuufUdlwbMJTbO9Trcm7SnjTFVwF8CHzXG7OHi8yoFknFf25jguzdxBhjqjzqWX7ccYDbuPahOa20t7g32W3Djv3LQJ+0riY3zGmzY18j32p4L3E+MvccYY4qAV4GvW2t/Pmj3gHNnDOem1cyHYIz5L9w3hj/GvbTyPLDOWhsz/4ECGWN+iHseN/gGfvS1T8Mddn4P8BzuTflrrbVXRyTQUTDGTGHgp7S/xE1YnwemA28BtwJ7cEf0JVhrPxnmMMfEGPMg7v2ZW4Fu4BncIb2PEKOvG4Ax5iTuKLB/wr0c9FPckZZ3A8dwe4o/xL2x/iVgnrW2KzLRjswYk4A7sON+3IEEnwF6cHuFw75Gxph/wB1mfzvuB8fXcAdNRM0giRHOLR/YAvzQWvuPQzzuXuCLuFc4HNxpHv9irdUgiXHyJ0Aq7lDYXwKfj+HkVIQ7xHM57oipFt/XHdbaC7gjFv8eqAfWADHxJm6tbbPWVvV94V5W6LDWXvC9Vvfi3m+rxr22/ycRDPdyfR13+PxR3DlBe4G/j+XXzecjwE3ABdw38R7gz31J6HbcEW4NuG/ut0drcvK5D/dy7F8Dn/b9fF8Qr9H9uIMmynHvyf1jNCUnnyHPDfeDeylwf8D7SUvA4/4VeBZ3buJ7uAn6Xy8nAPWgREQkKqkHJSIiUUkJSkREopISlIiIRCUlKBERiUpKUCIiEpWUoEREJCppNXORMDDGPA4QWC8oTM+bgTuH6uq+1aVHOPbLQLG19vNhCU7kEpSgRMbIGHOpyYQluDPrI+GLwMuXSk4+PwBOGWO+Za0tC21YIpemS3wiY1cY8PUd3GWWAtsqrLWN1trBq1uHlG9h1s/hLhJ8Sb5aWc/jruAgEnHqQYmMkW+pJQB8S750Bbb52h/3HXuXb7sM+BfcarI34F6G+wS+Yn5AAfA47hJAju8x04Dv4a7N14NbQ+nPRihlfzXumnBbAuIoxe0prfM1HQbusNYe920/j1s+4Wuj+CcQCQn1oEQi5y9x1wt8H+5iqU/gLo76Sd/XvbirfPf5FdCLWx/qOvrLog9nHbDfWtsb0PZ9oAZYjVsQ8RHAG7B/N7DQGJN7uSclMl7UgxKJnCettU+Cv2z2fwHLrbX7fW2v4VYrfc4YsxG3pP2mvoRjjPkMcNYYUzC4x+YzB7esQ6DZwC+stda3PbgESVXAcbVjOjuRMVKCEomcwBXyq33fDw1qm+b7eSnuZb9GY8zg31NKf2IJlAJ0Dmr7AfATY8wfAC/jJsnAARQdvu+pwZyASCjpEp9I5HQH/OwABBST7Gvr+xtNB47glk0J/JqHW4ZjKLVAVmCDtfYxYCHuvaabgSPGmHUBh2T7vteM8lxExp16UCKxYT9uQcYGX/n3YBzAHXgxgLX2JPDPwD8bY57Hvd+13bd7AW7p8ZNjDVhkrJSgRGLDy7iXBJ8yxvwtUIl7T+oj1tp7h3nM60BR4D0qY8x3cQvIHce9z3Qlbm+qzzrg9UEDK0QiQpf4RGKAtdaLW4X2GPBr3GT1CG611uEecw43sX08oDkRt9z6Edxq0b8AHgvY/3FGHhkoEjaqqCsygRljNuCW217cN59qhGPXAj/1HaselEScelAiE5i19k3gUdwVLS4lG/gjJSeJFupBiYhIVFIPSkREopISlIiIRCUlKBERiUpKUCIiEpWUoEREJCopQYmISFT6/5oxLZ2sM5YBAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_theta(results):\n", - " plot(results.theta, color='C0', label='theta')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Angle (rad)')\n", - " \n", - "plot_theta(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot `omega`" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3Xl8lPW5//9XNkIgEBISQAEBBS8ru4JsggvYVj2/r57axVO72Fbbam3P1h49ra1LT2vrOfacLlZrS91ttdUu1qWuCEIAFUUFvRABBSVkI4EkZJ/fH/ckzMQkTCCTmUnez8cjD+b+3Hfmvm4GcuVz39fn80kLhUKIiIgkm/REByAiItIZJSgREUlKSlAiIpKUlKBERCQpZSY6gN5iZtnAXGA30JLgcEREJDYZwFHAC+7eELmj3yQoguS0KtFBiIjIYVkMPB/Z0J8S1G6Ae++9lzFjxiQ6FhERiUFJSQkXXXQRhH+GR+pPCaoFYMyYMYwbNy7RsYiISM984NGMiiRERCQpKUGJiEhSUoISEZGkpAQlIiJJKSFFEmY2BXgN+KO7f6aT/WnAj4BLwk3LgSvdXTPbiogMEImq4rsZeKGb/V8GzgdmAiHgSWAbcGv8QxMRkWTQ57f4zOxCoAp4upvDPg/c5O673P094Cbg4j4IT0REYtBcW0v5mmIqX3iRUEt8Ju/p0x6UmQ0HrgeWAl/q5tCpwMaI7Y3hNhERSbAD779Pyd+fpOXAAQCyiwoZOnFir5+nr3tQ3weWu/vOQxyXC1RHbFcDueFnUyIikgChUIiqVzby/l8ebk9OaWlpZI0YEZfz9VkPysxmAcuA2TEcXgMMj9geDtSoSEJEJDFaGxspfWYFNdu2tbdl5OQw+qylDEr1BAWcDkwE3jUzCHpJGWZ2oruf1OHYTQQFEuvD2zPDbSIi0scaK/dS8vjfaayqam8bPHo0Yz5yFpm5uXE7b18mqNuA30dsf5MgYV3WybF3Af9mZo8SVPH9O/DzeAcoIiLR9r+1lbJnn6O1uam9LW/aVAoXLSQtIyOu5+6zZ1DuXufuJW1fBLfx6t29zMwWm1lNxOG/Ah4mGCv1OvBIuK1fevvtt/nsZz/LnDlzOPfcc3n66aDA8aqrruLaa6/lkksuYfbs2Vx44YWUlZXxgx/8gLlz5/LRj36UzZs3t7/Pnj17+PrXv878+fM588wzueuuu9r31dfXc+WVVzJ37lzOPvtsfv3rX7NkyZL2/bfddhvLli1j9uzZnHPOOTz55JN99xcgIkkn1NJC+fOr2fPkU+3JKS0jg9FLz6RoyeK4JydI4Gzm7n5txOtVBLf82rZDwH+Ev3pd1SsbqVz/YtRvBL0tPTOLglPmMGLWzG6Pa2pq4qtf/SoXXHABy5cv56WXXuLyyy/nwQcfBOCxxx5j+fLlTJkyhUsvvZRPfepTfOMb3+Cqq67iZz/7GTfccAN33303ra2tXHbZZZx55pncdNNN7Nmzh4svvphJkyaxePFifvGLX/Dee+/x1FNPceDAAS699NKoOMaPH8+9995LUVERjz/+ON/61rd44oknGDVqVNz+jkQkOTXX1lLy9yepLylpb8vKy2PMRz9M9siRfRbHgJzqqOqVjXFNTgCtzU1UvbLxkMdt3LiRuro6vvzlLzNo0CAWLFjAGWecwSOPPALAWWedxbRp08jOzuass84iOzub888/n4yMDM455xzeeOMNAF577TUqKyu54oorGDRoEOPHj+eTn/wkjz76KBAkuq985Svk5eUxZswYPve5z0XFcfbZZzN69GjS09M555xzmDBhAq+++mov/62ISLI78P777Hzgj1HJaejEiYz7+Mf6NDlB/1oPKmYjZs3skx7UoXpPAKWlpYwZM4b09IO/Kxx99NHs2bMHgJER/yAGDx5MYWFh1HZdXR0A7733HqWlpcyZM6d9f0tLS/t2aWkpRx11VPu+jos6/vnPf+b222/nvffeA6Curo69e/fGfL0iktpCoRDVG1+longtoVBbwXQaI+efwojZs0hL6/tRPgM2QcWSPPrCqFGjKCkpobW1tT1J7d69m4kTJ7Yni1gcddRRjBs3jieeeKLT/UVFRZSUlDB58mQgWMWyzXvvvcfVV1/NHXfcwezZs8nIyOC88847gqsSkVTSXQn5kAQuADsgb/ElkxkzZpCTk8NvfvMbmpqaWLduHc888wznnHNOj98nNzeX2267jfr6elpaWtiyZUv7bbqzzz6bX/3qV1RXV7Nnzx7uueee9u89cOAAaWlpFBQUAPDggw/y1ltv9d5FikjSaqzcy64/PhSVnAaPGsX4T1yQ0OQESlAJN2jQIG655RZWrlzJ/Pnzue6667jxxhs57rjjevQ+GRkZ3HLLLbz55pssXbqU+fPnc/XVV1NTExRHfu1rX2PMmDEsXbqUiy++mI985CMMGjQIgMmTJ/PFL36RCy+8kIULF7JlyxZOOqnj0DQR6W/2v7WVXX98KGp8U960qYz9x/PiOr4pVmkH7zWmNjObCGx/+umnGZfgrJ8K7rvvPh599NGonpSIDAyhlhYqitdS9epr7W1pGRmMOv00htnxfRrLrl27WLp0KcAkd98RuW9APoMaiEpLS9m5cyezZ89mx44d3H777Vx00UWJDktE+ljTvv3seerphJeQx0IJaoBoamrimmuuYdeuXQwbNoxzzz2XT3/604kOS0T6UM227ZQ+s4LWxob2tqETJzJq6RlkZGcnMLLOKUENEGPHjuVvf/tbosMQkQQItbRQvrqY6tdfb29LS0ujYF7iSshjoQQlItKPNVZVs+eJJ2koL29vy8zNZfRZy8g5akw335l4SlAiIv3U/i1vUbZiZdSkBEMnTWTUGaeTMXhw4gKLkRKUiEg/09rURPmq1ex78832trT0DEYunE/e9GlJe0uvIyUoEZF+pKGikj1PPEljxFRlWXl5jPnwMrKLihIYWc8pQYmI9AOhUIj9b7xJ2arnCbW0tLcPmzKZotOWkB4emJ9KlKBERFJca2MjpStWUrN1a3tbWkYGRUsWM+wES5lbeh0pQYmIpLD60lL2PPEUTfv2tbcNKihg9FnLyB5ZkMDIjlyfJigzuwdYCgwFSoAb3f03nRx3MbAcOBDR/A/uvqIPwhQRSXqhUIjqV18LlsdobW1vH37ihyhctJD0rKwERtc7+roHdQPwJXdvMLMTgBVm9rK7v9TJscXufmofxycikvRa6uspfWYFtTt2tLelZ2VRdPppDJsyOXGB9bI+TVDuviliMxT+Og7oLEGJiEgHB3aXsOeJJ2murW1vyy4sZPSHz2LQiLwERtb7+vwZlJn9ErgYyAFeBh7t4tDZZlYOVAJ3Aze4e3OfBCkikmRCoRBVG16mcv0LRK5CMWLGdEYumE9aRkYCo4uPPk9Q7n65mX0dWACcDjR0cthKYBrwDjAVuB9oJrhFKCIyoDTX1VH61DPU7drV3pY+KJvRS89g6KSJCYsr3hJSxefuLcDzZvYZ4DLgZx32b4vYfM3Mrge+hRKUiAwwdTt3seepp2k5cLBmbPCYMYw+aylZw4YlMLL4O2SCMrNJwAXAImACwa25cmAj8BTwtyO49ZZJ8AzqUEJAahbyi4gchlBrK5UvvMjel14m+BEIkEb+SbMomDunX97S66jLBGVmc4EfENyKe56gkOFpoB7IB04g6NH82sx+Avyfux/o4u0ws1HAmcDfCMrHlwH/BHxgUSIzOxvY4O57wtV+3wX+cDgXKCKSappraih54qmoRQUzcnIYvWwpQ8YPnBXDu+tB/Rb4MXC+u9d1dZCZTQe+BnwduLGb9wsR3M67FUgneL70L+7+FzM7BtgMnOju7xKMlbrDzHKBPcA9wA9jvioRkRRVu2MHpU8/S0vDwcfzOWPHMvqspWQOGZLAyPpeWmQ1SCozs4nA9qeffppx4wbObxgi0j+EWlqoWLuOqo2vRrSmUXDKHPJPmk1aenrCYounXbt2sXTpUoBJ7r4jct9hFUmYWQYwHXjX3SuPOEIRkQGssaqaPU8+RUNZWXtb5tChjP7wMnKOOiqBkSVWTCnZzG41s0vDrzMJnkltAHaa2YfjGJ+ISL8VCoWo3rSZXQ/8ISo5DZ0wgfGf+sSATk4Qew/qPOC28OvzgdHhr88TFFI80fuhiYj0X811Byh7dgW177zT3paWns7IBfPJmzE9ZWcg702x3tQcAbSl93OB+929DHgA+FA8AhMR6a9qd+xg5/0PRCWnQfn5jLvgY4yYOUPJKSzWHtQOYKGZ/RU4G/hUuH0k0GWFn4iIHNTa1ET56jXs2/xGVHve9OmMXDCP9EytgBQp1r+N64E7CcYvvUYwFRHAWQTz6YmISDfq9+xhz1PP0FRd3d6WOWQIo848gyHHjE9gZMkrpgTl7r8zs2eBo4GN7t5Wm74C+GucYhMRSXmhlhb2vrSBvS9tiJrkNffYYyk6fQkZgwcnMLrk1m2CMrN1wCPAI+E1m0oi97v7+jjGJiKS0hqrqil96mnqS0vb29Kzsihaspjc46foWdMhHKoH9V/AOcCDZpYNPEawPMYT7r6v2+8UERmgQqEQ+zZvpmJ1Ma3NB6cqzTnqKEYtPZOs4f17ktfe0m2CcveHgYcBzGwaQbL6OnCnma0nSFaPuPvmeAcqIpIKmuvqKHv2uQ+UjxecMpcRs2b22xkh4iHmkhF3fx14HbjRzPKAjxAkrH83s3rg3939wfiEKSKS/Gq376B0xXNRS2MMys9n9LKlZBcVJjCy1HRYNY3uXk0wBuoBaJ/5XERkQOqqfHzEjOkUzFf5+OHqbrmNJTG+R8jdV/VSPCIiKaW+pIQ9Tz8bXT4+dGhQPj6AlsaIh+7S+ooO2wdXzIreBuj/K2eJiETosnx88nEULVms8vFe0GWCcvf2J3lm9hHg+8B3gLXh5vkEVX7XxDNAEZFk01hVRelTz6h8PM5ivTH6U+Dz7r4uou1JM9sP3A1M6fXIRESSTJfl40cfzagzz1D5eC+LNUEdA7R20t4KjI31ZGZ2D8FquUMJBv3e6O6/6eLYfwWuBHKAB4HL3L2hs2NFROItKB9fQe0777a3paWnUzDvlGCCV5WP97pYE9RjwO1m9nXgBYLnT6cAPwvvi9UNwJfcvcHMTgBWmNnL4Vkq2oVvKV4FnAm8D/wJuC7cJiLSp2q376D02RW01Ne3tw0qKGD00jNVPh5HsSaoLxDc5vs7BwsiWoD7gH+J9WTuviliMxT+Og54qcOhnweWtx1vZt8H7kUJSkT6UGtTE+WrVrPvzTej2lU+3jdinSx2H/AFM/sGcCxBJd/b7r6/pyc0s18CFxPcunuZYDaKjqYCf4nY3giMNrOR7l7R03OKiPRUfUlJMPv4voOzuql8vG/1KP2HE9LGIzmhu18evlW4ADgd6Oy5Ui5QHbHd9noYoAQlInHT2tzM3hdeZO/LG4kcTaPy8b4Xc4Iys48CHwfGA1mR+9z9zJ6c1N1bgOfN7DPAZQTPsiLVAMMjttte97jHJiISq/o9eyh9ZgWNe/e2t6l8PHFiKjsJ93juJ1iw8HTAgUbgZA6OizocmQTPoDraBMyM2J4J7NHtPRGJh9bmZiqK17LrwT9HJaecsWMZ/6lPMsyOV3JKgFh7UFcAX3T3B83sYuB/3P1tM/s2MCmWNzCzUQRVeX8jSHTLgH8CPt3J4XcBd5jZvcBu4GrgjhhjFRGJWae9pswsRi6az/ATT1RiSqBYE9Q44MXw61ogL/z6foIKvEtjeI8Qwe28Wwl6bu8A/+LufzGzY4DNwInu/q67P25mNwLPcnAclGasEJFe09WzppyxYxl1xukadJsEYk1Q7xIs9/4OsAU4F9gALALqu/m+du5eBpzWxb53CQojItt+AvwkxvhERGKmXlNqiDVB3QnMA4oJBtv+KfxcqoBgfj4RkaSnXlNqiXUc1I8iXj8WngXiZIKxUK/EKzgRkd5Sv2cPpU8/S2NVVXubek3J7ZAJyswGAduAs9z9DQB33wHsiGtkIiK9QL2m1HXIBOXujWbWFMuxIiLJRL2m1BZr0vkecKOZfdHdd8czIBGRI6VeU/8Qa4K6HhgN7DKzMqAucqe7H9vbgYmIHA71mvqPWBPUtfEMQkTkSKnX1P/EWsV3Z7wDERE5XOo19U9dzsVnZkf35I3MbMyRhyMiErvW5mbK1xQHc+hFJKecsWMZf+EnyZs6VckphXXXg1pnZn8H7gZWunuo4wHh+fU+QTCF0c3ALXGJUkSkg/qSkmA2CPWa+q3uEtSHgH8mSFAjzGwjwcStDcAI4ARgAvAE8FV3fz7OsYqI0NrcTOX6F6h65VX0rKl/6zJBuXsN8AMz+yHB4oILgGOAwcBOgiXYV7h7SV8EKiKiXtPAEstA3RCwJvwlItLnWpuaqHzhRfWaBhjNDiEiSa1u5y7KVjxH0/6DC2qr1zQwKEGJSFJqqa+nYk0x+970qHb1mgYOJSgRSSqhUIiarW9T/vxqWg4caG9PH5RN4aIFDDvB1GsaIPosQZlZNvBLgqXeC4CtwLfd/bFOjr0YWE6wNHybf3D3FfGPVEQSpWn/fspXrqL2nXej2nMnH0fhqYvIHDIkQZFJIsSUoMzsG8B97l5+hOfaSbCq7rvAOcADZjY9vHxHR8XufuoRnE9EUkSotZXq1zdRuXY9rc1N7e2ZQ4dSdNpihk6cmLjgJGFi7UF9CfgfM3sCuAv4i7s39ORE7l5L9Jx+fzOz7QQLH+7oyXuJSP/RUFFJ2bMrqC8tjWhNI2/aiYycP4/0QYMSFpskVqxz8c00s5nARcBNwG1m9kfgbnd/7nBObGajgeOBTV0cMtvMyoFKgsHCN7h78+GcS0SST2tzM3tfepmqDRsIhQ6Wjg/Kz6fo9NPIOUqzpw10MT+DcveNwEYzuxI4A/gM8HczKwHuAX7t7u/E8l5mlkUw0PdOd3+zk0NWAtOAd4CpwP1AM3BDrPGKSPI68P77lD77HE3V1e1taenp5J98EvknzSYtIyOB0UmyOJwiiXHAKcA8gmmPngVmA25mV7n7/3X3zWaWTtAjagSu6OwYd98WsfmamV0PfAslKJGU1tLQQEXxOvZt3hzVPnjMGEadfhqDCvITFJkko1iLJIYDnyToNS0EVhAkiwfd/UD4mPMInk91maDMLI2gOm80cI67N3V1bAchQHWlIimsZts2yletprm2tr0tPSuLkQvmM3yqBtzKB8Xag9pDUMhwF/AZd9/VyTHPAhsO8T63EExCu6wtsXXGzM4GNrj7HjM7Afgu8IcYYxWRJNJcU0vZquep3b49qn3oxIkULTmVzNzcBEUmyS7WBHWGu6/t7gB330fwbKpTZjYB+ArBbcESM2vb9RVgFbAZONHd3wWWAneYWS5BcrwH+GGMsYpIEgiFQuzb/AYVxWtpbWxsb8/IyaFoyWKGHjtJvSbpVqwJ6j4zm+vuFZGNZjaCoKdz7KHeIFxA0d2/xtyIY78JfDPG2EQkyTRWVVH27HMc2L07qn34CScwctECMrKzExSZpJJYE9REoLOymiFAj1beFZH+K9TSwt6XX2HvixsItba0t2fl5THq9NPIGasfFxK7bhOUmX0v/DIEfNPMaiJ2ZxBU8r0Wp9hEJIXU79lD6bPP0VhZ2d6WlpbGiNmzyJ9zMumZmvpTeuZQ/2LanimlAYsISsPbNBGMU/rXOMQlIimitamJynXrqXr1dSLXasouKmLUGaeTXTgyccFJSus2Qbn7GQBmdjvwz+FCCBERAGrfeZey51bSXHPw5kp6ZiYFp8wlb8Z00tLTExidpLpYpzr6QrwDEZHU0VxbS/nqYmq2bo1qHzJ+PEWnLSZr+PAERSb9SZcJysyeAT7m7lXh111y9zN7PTIRSTqh1lb2bdpMxdp1tDYdHGefkZ1N4amLyD1+ikrHpdd014N6joPPnA5rQlgR6T/qS0spe24VDWVlUe3Djp/CyIULyRySk6DIpL/qMkG5+3WdvRaRgaWloYHKdS9Q/fomIosgsvLyKDptMUPGjUtccNKvxToX3+lAq7uv7NC+BKBju4ikvral1ytWr6G5rq69PS09g/w5J5E/e5ZmHZe4inVgwk+Bb3fSPgT4ETCr1yISkYRrrKqmfNUq6nZGT7s5ZPw4ChcvZtCIvARFJgNJrAlqCsFceR15eJ+I9ANdzQSROWQIIxctJHfycSqCkD4Ta4KqIFg4cHuH9ulA9QcPF5FUU7drF2XPrYpaRDBYen0qBfPmav486XOxJqg7gZvNLEQw8zjAEuAX4X0ikqKa6+qoWFPM/i1vRbVnFxVRdNpiBo8alaDIZKCLNUFdQzDd0R+BQeG2BoJnU9+NQ1wiEmfBchibqSheF7UcRnpWFiPnzwsWEdRMEJJAsc4k0QJ8x8y+D0wmSFZbu1t0UESSV0NZOWXPraS+tDSqPXfyZAoXLSBz6NAERSZyUE+nF277dUpLsIukoNbGRirXv/CBiV2zhg+naMlihhwzPnHBiXQQ6ziobOC/CVa/zQo3N5rZr4FvuXt9jO/xS2AZUABsBb7t7o91cfy/AlcCOcCDwGXu3hBLvCISLRQKUbttO+XPr6a5tra9PS09g/yTZjHipNlaDkOSTqw3mH8KfBT4f8AIIA84H/hweF8sMoGdwGnh7/8u8ICZTex4oJl9BLiKYOn3icCxgGazEDkMTfv2sfuRxyj5+xNRySln7FjGX/gJCk6Zq+QkSSnWf5UfB85z99URbY+b2ZeAvxL0rLrl7rXAtRFNfzOz7cDJwI4Oh38eWO7umwDCz77uJUhaIhKDUEsLVa9spPLFlwi1HBzTlJGTQ+GiheROmawxTZLUYk1Q2UBNJ+21PXiPKGY2Gjge2NTJ7qnAXyK2NwKjzWyku1cczvlEBpID771P2cpVNO7dG9GaRt60EymYd4rGNElKiDW5PE4wDurz7v42gJlNBn4O/L2nJzWzLIIe0Z3u/mYnh+QSPQC47fUwgkHDItKJ5roD4TFNW6LaswsLKTptCYNHa0yTpI5YE9RlwN3AW2ZWGW7LB54ELu/JCc0sPfxejcAVXRxWA0SueNb2en9PziUyUIRaW9n3xhtUFK+ntfFgLVF6ZhYF8+aSN32axjRJyol1HFQ5cLaZGWAEJeZvurv35GRmlgYsB0YD57h7UxeHbgJmAg+Et2cCe3R7T+SD6ktKKFv5PA3l5VHtucceS+Gpi8jM1ZgmSU09en4UTkg9Skod3AJ8CFh2iEG+dwF3mNm9wG7gauCOIzivSL/TXHeAyrXr2Pdm9F3yrGHDKFyymKETjklQZCK9o7sl338b65u4+xcPdYyZTSCo9msASoLOGITbVhHMln6iu7/r7o+b2Y3AsxwcB3VNrPGI9Gfty66veyHqdl5aRgb5J81mxOxZKhuXfqG7f8W9Wn/q7u8c4j1zOxz/E+AnvRmDSKo7sLuE8lUfvJ03dOJECk9dSNbw4V18p0jq6W7J9y/0ZSAi0rXmujoqitey36Or87KGD6dw8am6nSf9Uo/uA5jZKQSzOjzs7rVmNhyod/fGQ3yriByGUGsr1a9vonL9C1EzjqdlZJB/8kmMmDVTt/Ok34p1Lr7xBDNGTAEGEwyw3Qb8kOC23dfiFaDIQHXg/fcpX7Wahoro4tWhkyZSuGgRWcOHJSYwkT4S669eNwMvAacAlRHtfyAoGxeRXhIsILj2A4Nts/LyKFq8iCHH6HaeDAyxJqjFwCnu3hRRfQfwDjC216MSGYBCLS0Hb+c1HRwimJ6Z2X47Ly0jI4ERivStWBNUEx2q7MKmAOWdtItIDxx4733KVj1PY2VlVHvusccyctECsobpdp4MPLEmqD8APzCzT4a3Q+FZJX4C/D4ukYkMAM01tVQUF7P/ra1R7Vl5ecECguPHJSgykcSLNUF9E/gVUAYMAjYQzI/3EPCd+IQm0n+FWlqofu11Kte/SGtz5O28LPLnnMSImTN0O08GvFjn4jsAfM7MvgecSHC7b2NP5+ITEajb9R7lq57vsBQG5E4+jsKFC8jM7exuusjAE2uZ+aeBv7j7Dj64uKCIxKC5pobyNcXUbH07qn1Qfj6FixcxZJxu54lEivUW3w3Ar83scYJnTo+4e138whLpP0ItLVRtfJW9L2744O28uSczYsZ03c4T6USst/gmmNkC4BPATcDtZvYIwXIYj7h7fRxjFElZdTt3Bbfzqqqi2nMnTw7fztNSGCJdiXmOFHcvBoqBfwsnq08C/wv8FsiLT3giqalp3z7KVxdTu317VPugggKKFp9KztijExSZSOro8SReZpZBUMGXR1As0dWigyIDTmtTE3s3vEzVyxsJtba0t6dnZVEwd06wsq1u54nEJNYiiXRgGUGv6XwgnWBuvosIln0XGdBCoRA1W9+mYk0xzbW1UfuGHX88IxfMI3OobueJ9ESsPajS8LEPA18AHu9muXaRAaWhrJyyVc9TX1IS1Z5dVETR4kUMHjMmQZGJpLZYE9QlwGPu3nDII7thZlcAFwPTgd+5+8VdHHcxwSS0kcvC/4O7rziS84v0pua6A1SuX8++zW8Cofb2jJwcRs6fx7ATjLS0Xl33U2RAibWK78+9dL73gf8CPkKwlHt3it391F46r0ivCbW0UL1pczALROSS62lp5M2YQf6ck8jIzk5ghCL9Q5+udObuDwGY2RxAoxIl5dTt3EX586s/MAvEkPHjKVy8iEEjRiQoMpH+J5mX4pxtZuUE60/dDdzg7s0JjkkGqKBsfA2123dEtWfl5VG4aAFDJkzQ7TyRXpasCWolMI1gvampwP1AM8GMFiJ9prWpib0vbaDqlVejy8Y1qatI3B0yQZnZIILl3c9y9zfiHxK4+7aIzdfM7HrgWyhBSR8JhULUvLWViuK1KhsXSZBDJih3bzSzpliOjaMQoPsn0icaysooW7X6A2Xjg0eNonDxIgaPHp2gyEQGlliTzveAG83si+6++3BPZmaZ4XNmABlmNhho7vhsyczOBja4+x4zOwH4LsGiiSJxo7JxkeQSa4K6HhgN7DKzMiBqJnN3PzbG97kauCZi+zPAdWb2W2AzcKK7vwssBe4ws1xgD3AP8MMYzyHSI6EnCzpCAAAWuElEQVSWFqpf30TlCy/S2tjY3p6Wnk7ejOkUzDmZ9EGDEhihyMAUa4K6tjdO5u7XdvNeuRHHfZNgFV+RuOqybPyYYyg8daHKxkUSKNaBunfGOxCRvtRUXR3MNr5jR1R7Vl4ehacuYuiEYxITmIi061Hhg5mlAWOArMj28G05kaTXZdl4Vhb5c7R4oEgyiXU285HALwlmMu/se/Q/WpJaKBSiZstbVKxd94Gy8eEnGAXz55E5ZEiCohORzsTag/o5QZHEImAF8I9AIfAd4NtxiUyklxzYXULF6jXUl5ZGtatsXCS5xZqglgEfdfcNZtYKvOPuT5rZXoIKv7/GLUKRw9S0bz8Va9dRs3VrVHtGTg4jF8xnmB2vsnGRJBZrgsoC2sqcyoCjgS3AWwRTEokkjfZVbV/ZSKjl4HOmtPQMRsyeSf5Js0nPyurmHUQkGcSaoF4BTga2A6uB75rZEIK1nTw+oYn0THfPmXInH8fI+fPJGj4sQdGJSE/FmqC+zcFxSlcCdxJM4Po28KU4xCXSIwd2l1D+/Goaysqi2rOLiig8dSE5Rx2VoMhE5HDFOg6qOOL1buDDcYtIpAe6fc6k6YlEUlqyLrch0i09ZxLp/7pMUOFqvVBX+yO5u8ZBSZ8IhULs9y1Url1Hc13UlJB6ziTSz3TXgzqjz6IQiYGeM4kMLF0mKHd/ri8DEelK8JxpLTVb345qzxwyhIL58zSeSaSfinWqoyXd7Xf3lb0TjshBes4kMrDFWiSxoov2tmdUegYlvUbPmUQEYi8zT4/cDq+MOwP4MfCDOMQlA9SB3bspf36NnjOJyOGVmYeXaN9gZt8G7gI+FMv3mdkVBLNPTAd+5+4Xd3PsvxIMCs4BHgQuc/eGw4lXkp+eM4lIR0c6DqqVYF6+WL0P/BfwEYLE0ykz+whwFXBm+Hv+BFwXbpN+pP0508sbo9ZnSsvIYMQsPWcSGchiLZL4XIemtoULLwGeivVk7v5Q+P3mAOO6OfTzwHJ33xQ+/vvAvShB9Ruh1lb2+xYq1q6j5cCBqH16ziQiEHsP6roO260Es5r/Cfhhr0YUmAr8JWJ7IzDazEa6e0Uczid9qG7XLipWF9NQEf1RBs+ZFpFz1JgERSYiySTWIolJ8Q6kg1ygOmK77fUwQAkqRTVWVVGxupjad96Jas8cOpSCeafoOZOIREnWufhqgOER222v9ycgFjlCLfX1VK5/kX2bNhEKHZw9Kz0zkxGzZzFi1kw9ZxKRD4j1GdSzdD4vXwhoIFh24x53X9dLcW0CZgIPhLdnAnt0ey+1hFpaqH7tdSpf3EBrY2QBZhrDTzieglNOITN3aMLiE5HkFmsPah1wGcHChS+G204GZgO3AwasNrOPuXuXy7+Hx09lEgzszTCzwUBzuGw90l3AHWZ2L7AbuBq4I8ZYJcFCoRC127dTsWYtTfv2Re3LOfpoChctILuoKEHRiUiqiDVBHQ380N1/HNloZv8BTHP3s83sP4FrgS4TFEGiuSZi+zPAdWb2W2AzcKK7v+vuj5vZjcCzHBwHdc0H3k2STn1pKRWrizmwe3dUe1ZeHoULFzBk4gQ9ZxKRmMSaoP4RuL6T9j8B3w2//gPwne7exN2vJUhincmN3HD3nwA/iTE+SbDmmhoq1q5n/5YtUe3pg7IpmHsyedOmkpahGbFEJHaxJqhK4Dzgpg7t/y+8D2AoKmIYcIKBtq9Q9cor0RO6pqWRN30a+XNOJmPw4ARGKCKpKtYE9R/A3WZ2LvASQXHEycBi4LPhY06l+9t70o90N9B26KSJjFwwn0EjRiQmOBHpF2IdB3W/mW0gmDnieIKZJF4EvuLuW8PH3By3KCWpdDnQtrCQwkULyRnbk9mvREQ6F/M4KHd/i2DyVhmgNNBWRPpSzAnKzAqAOcAoIGr5DXe/q5fjkiSigbYikgixDtT9OME4pDSCOfgiB+2GCMYtST+jgbYikkix9qBuJCj5vs7dWw51sKQ2DbQVkWQQa4IqBO5Qcur/NNBWRJJFrAnq98A5wC/iGIskUNO+/VSuX8/+LW9FtWugrYgkSqwJqhy43syWAa8BTZE73b2zWSYkBbQ0NFC14RWqNr4avaKtBtqKSILFmqAWECwamEcwIDdSiM6nQZIkFmppYd/mN6h84UVa6uuj9g2dNCk80DYvQdGJiMQ+UPeMeAcifSMUClG34x3K1xTTVF0dtW/wqFGMXDifnKM10FZEEu+wFywML5Xxj8Dn3P3s3gtJ4qW+tJSKNWs58P77Ue1Zw4ZRMH8euZOPUwGEiCSNHicoMzsd+BxwAcHksA/3ckzSy7qcaTwri/yTTyJvxnTSM5N1cWURGahiHahrBEnpIoJZJI4GPunuD8UxNjlCrY2N7N3wclAA0WGm8eFTp1Iw92QycnISGKGISNe6TVBmdgVBYhoP/JFggcHVQCPwZtyjk8MSam1l3xtvULn+xQ/OND5xIiMXaqZxEUl+h+pB/R/wv8B33L2xrTHoUPVceD6/5cCHCUrX/9Pd7+vkuGsJFj+MnF9nhrtvO6wTDxChUIi6d96hYs1aGquqovZlFxVRuHCBZhoXkZRxqAR1BcF6T7vM7E8EA3afO4Lz3UzQ+xoNzAIeMbON7r6pk2Pvd/fPHMG5BpSGsnLK1xRz4L33otozhw5l5Px55B4/RQUQIpJSuk1Q7n4rcKuZTSZIVL8BhhA8h5pmZm+4e6i792hjZkMJCiumuXsN8LyZ/TX8vlcdwTUMaM01NVSsW89+f4vIOXxVACEiqS7WcVBbgWuAa8xsCcFzqV8BPzezh939khje5nigxd0jS8k2Aqd1cfz/Z2aVwG7gF+5+SyyxDhStjY3sffkVql7Z2EkBxInkz5lD5hAVQIhI6urxr9buvhJYaWZfIxgHFettuFygukNbNTCsk2MfAG4D9gDzgAfNrMrdf9fTePubbgsgJkwICiDy8xMUnYhI7znsez/u3kDwTOr3MX5LDTC8Q9twgrFUHd97c8TmGjP7KfBxYMAmqFAoRN27O6lYU0zj3r1R+7ILCxm5cAFDxo1NUHQiIr2vLx9ObAEyzWxKePl4gJlAZwUSHYUIFksckBrKK6hYU0zdrl1R7SqAEJH+rM8SlLvXmtlDBLOiX0JQxXcesLDjsWZ2HrASqALmAt8Avt1XsSaL5poaKte/wL43txBVAJGZRf7Js4MCCC21LiL9VF+Xd10O/BYoBSqAy9x9k5ktBh5z99zwcReGj8sGdgE/dvc7+zjWhOmqAALSGH7ihyg4ZQ6ZQ4YkLD4Rkb7QpwnK3SuB8ztpX0VQRNG2/U99GVeyCLW2HlwCo0MBxJBjjqFw4QIGFagAQkQGBg2QSQJtS2BUFHcyA0RhISMXzmfIuHEJik5EJDGUoBKsqyUwVAAhIgOdElSCNO3bT+W6dex/a2tUu2aAEBEJ6CdgH2tpaGDvSxuofvV1Qq0fXAIjf87JmgFCRAQlqD4Tammh+vVN7H3xJVoaGqL2DZ00iZEL5mkJDBGRCEpQcRYKhajdtp2K4rU07dsXtW/wqFGMXDifnKO1BIaISEdKUHFUX1JC+epi6vfsiWrPGjaMgvnzyJ18nAogRES6oAQVB41V1VSuXUfNtuj1FdMHZVMw5yTypk8jLSMjQdGJiKQGJahe1FJfz94XX6L69U2EWlvb29PS08mbPo38k08iY/DgBEYoIpI6lKB6QWtzM9Wvvc7elzbQ2tgYtS938nGMnD+PrOEdJ3IXEZHuKEEdgVAoRM1bW6lct56m/dGrhgweM4bChfMZPGZMgqITEUltSlCH6cB771O+ppiGsrKo9qy8PEYumMfQSZNUACEicgSUoHqoce9eKorXUbtjR1R7RnY2+XPnkDf1RBVAiIj0AiWoGDXX1bH3hZfYt3kzodDBtZnS0jMYMXM6I06aTUZ2dgIjFBHpX5SgDqG1qYmqja9SteEVWpubovYNO34KBaecQtbwYQmKTkSk/1KC6kKotZX9voXK9S/QXFsbtS9n7NGMXDCfwaNGJSg6EZH+r08TlJkVAMuBDwPlwH+6+32dHJcG/Ai4JNy0HLjS3UMdj42Hund3UlG8loaKiqj2Qfn5jFwwnyETjlEBhIhInPV1D+pmoBEYDcwCHjGzje6+qcNxXyZYeXcmEAKeBLYBt8YzuIbyCiqK11K3c2dUe0ZODgWnzGX4h04gLT09niGIiEhYnyUoMxsKXABMc/ca4Hkz+yvwWeCqDod/HrjJ3XeFv/cm4FLimKBqtm2n5PEnCPJhID0zkxGzZjJi9izSs7LidWoREelEX/agjgda3H1LRNtG4LROjp0a3hd53NQ4xkbN1q0cTE5pDD/xBArmziFz6NB4nlZERLrQlwkqF6ju0FYNdFYC1/HYaiDXzNLi9RxqxIzpNFXvI2v4MPLnzCF7ZEE8TiMiIjHqywRVA3SckG44sD+GY4cDNfEskhg8ZgzjP3FBvN5eRER6qC+f+G8BMs1sSkTbTKBjgQThtpkxHCciIv1Un/Wg3L3WzB4CrjezSwiq+M4DFnZy+F3Av5nZowQPhv4d+HlfxSoiIonX1zXTlwM5QCnwO+Ayd99kZovNrCbiuF8BDwOvAa8Dj4TbRERkgOjTcVDuXkkwvqlj+yqCwoi27RDwH+EvEREZgDTqVEREkpISlIiIJCUlKBERSUr9aTbzDICSkpJExyEiIjGK+Jn9gZVe+1OCOgrgoosuSnQcIiLSc0cBb0c29KcE9QKwGNgNtCQ4FhERiU0GQXJ6oeOOtMjly0VERJKFiiRERCQpKUGJiEhSUoISEZGkpAQlIiJJSQlKRESSkhKUiIgkpf40DuqImFkBsBz4MFAO/Ke735fYqA6PmWUDvwSWAQXAVuDb7v5YeP9S4GbgGGAdcLG7v5OgcA9bePHL14A/uvtnwm2fBm4ACoEngS+GZ9FPKWZ2IXANwWdUQvAZrUr1z87MJhL821wANAB/BP7F3ZvNbBbB/8EPAW8AX3L3VxIV66GY2RXAxcB04HfufnHEvi4/p/D/z1uAjwN1wI3u/pM+DT4GXV2fmc0Hvg+cTDDmdAXwDXffHd6fBvwIuCT8VsuBKw9nRXT1oA66GWgERgMXAbeY2dTEhnTYMoGdwGlAHvBd4AEzm2hmhcBD4bYC4EXg/kQFeoRuJmJwX/jz+hXwWYLPsY7gh2FKMbOzgB8DXwCGAUuAbf3ks/slwXpwRxEsWnoacLmZDQL+AtwD5AN3An8Jtyer94H/An4b2RjD53QtMAWYAJwB/IeZfbQP4u2pTq+P4PO5DZhIcA37gdsj9n+ZYFmlmcAM4B+ArxxOAOpBAWY2FLgAmObuNcDzZvZXgh90VyU0uMPg7rUE/wna/M3MthP8xjMS2OTufwAws2uBcjM7wd3f7OtYD1e4h1EFrAEmh5svAh5295XhY74LvGFmw9x9f2IiPSzXAde7+9rw9nsAZvZlUv+zmwT8wt3rgRIzexyYCpxO8PPo/8K/af/MzL4JnAk8nqhgu+PuDwGY2RxgXMSuj9H95/Q54AvuvhfYa2a/JuipJNV1dnV9bXdi2pjZL4DnIpo+D9zk7rvC+28CLgVu7WkM6kEFjgda3H1LRNtGgv84Kc/MRhNc4yaCa9rYti+czN4mha7VzIYD1wP/3mFXx2t7m6BXfHzfRXdkzCwDmAMUmdlWM9tlZr8wsxz6wWcH/BS40MyGmNlY4GyCH8xTgVc73AZ6ldS6tjZdfk5mlg8cHbmf1P9Zs4TgZ0ubqOvnCK5PCSqQC1R3aKsmuL2S0swsC7gXuDP821t/uNbvA8vdfWeH9v5wbaOBLILnE4sJboPNBq6mf1zfcwQ/rPYBuwhuf/2Z/nFtbbq7ltyI7Y77Uo6ZzQC+B3wrornj9VcDueFnUz2iBBWoAYZ3aBtOcG81ZZlZOnA3QS/iinBzSl9r+EH6MuB/O9md0tcWdiD858/dfbe7lwM/Ac4hxa8v/O/x7wTPZ4YSFLLkEzxvS+lr66C7a6mJ2O64L6WY2WTgMeCf3X1VxK6O1z8cqFGRxOHbAmSGq8LazCS625pSwr+tLCf4jfwCd28K79pEcG1txw0FjiN1rvV0goez75pZCfBN4AIz28AHr+1YIJvg800J4ecSu4DO/jOn+mdXAIwneAbV4O4VBA/XzyG4hhkdfsueQepcW6QuP6fw57s7cj8p+LPGzCYATwHfd/e7O+yOun6O4Po0m3mYmf2e4IfCJQS3VR4FFrp7Sv3DaWNmtxJcx7Jw4UdbexFB2fkXgUcIHsif5u7zExJoD5nZEKJ/O/smQcK6DBgFFAPnAhsIKvoy3f3CPg7ziJjZ9QTPZs4FmoC/EpTy/owU/uwAzGwbQQXY/xDcCrqdoNryC8BbBL3FWwkeqn8LmOLujYmJtntmlklQ2HENQRHBpUAzQa+wy8/JzH5EUGZ/PsEvkM8SFE0kVZFEN9c3GlgJ3Oru/93J930V+GeCOx0hguEeP3d3FUkcgcuBHIIS2N8Bl6VwcppAUNY5i6BSqib8dZG7lxFULP4A2AvMA1LmB7i717l7SdsXwe2EencvC39eXyV45lZKcF//8gSGe7i+T1A+v4VgPNDLwA9S/bML+xjwUaCM4Id4M/Cv4SR0PkGFWxXBD/fzkzU5hV1NcEv2KuAz4ddXx/A5XUNQNPEOwTO5/0625BTW6fUR/BJ/LHBNxM+Wmojv+xXwMMEYxdcJkvSvDicA9aBERCQpqQclIiJJSQlKRESSkhKUiIgkJSUoERFJSkpQIiKSlJSgREQkKWk2c5E4MrM7ACLXCuqj8w4jGEM1v21W6W6OvRKY6O6X9UlwIjFSghI5TGZ2qEGEkwhG1CfCPwNPHCo5hf0S2G5mP3b3HfENSyR2usUncviOivi6iWCapci2ne5e7e4dZ7aOq/CkrF8hmCj4kMJrZT1KMHuDSNJQD0rkMIWnWgIgPNVLY2RbuP2O8LEXh7d3AD8nWEl2GcFtuE8SXsgPGAPcQTD9Tyj8PUXA/xHMzddMsH7SN7pZyn4+wXxwKyPiOJagp7Qw3PQGcJG7bw1vP0qwbML3evBXIBJX6kGJ9L1vEswXeBLBRKn3EkyMemH466sEM3y3+SPQQrA+1OkcXBK9KwuBje7eEtH2C6AcmEuwIOLPgNaI/S8BHzKzkYd7USK9TT0okb53v7vfD+3LZf8emOXuG8NtzxKsUvqImS0hWNL+zLaEY2aXAu+Z2ZiOPbawYwiWdIg0HrjP3T283XEJkpKI4yqO6OpEeokSlEjfi5wlvzT85+YObUXh19MJbvtVm1nH9zmWg4kl0mCgoUPbL4HlZvZZ4AmCJBlZQFEf/jMnlgsQ6Qu6xSfS95oiXocAIhaUbGtr+7+ZC7xJsHRK5NcUgmU4OlMBjIhscPdbgA8RPGs6G3jTzBZGHJIf/rO8h9ciEjfqQYkkt40ECzJWhZd/j8WrBIUXUdx9G/BT4Kdm9ijB86414d0nECw7vu1IAxbpLUpQIsntCYJbgg+Z2beB9wmeSX3M3b/axfesACZEPqMys/8lWDhuK8FzphkEvak2C4EVHQorRBJKt/hEkpi7txKsQPsW8CeCZPUzgpVau/qe3QSJ7RMRzVkES62/SbBi9H3ALRH7P0H3lYEifU4r6or0Q2a2mGCZ7alt46m6OXYBcHv4WPWgJGmoByXSD7n7KuBmghktDiUf+JKSkyQb9aBERCQpqQclIiJJSQlKRESSkhKUiIgkJSUoERFJSkpQIiKSlJSgREQkKf3/44Bt549J63oAAAAASUVORK5CYII=\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_omega(results):\n", - " plot(results.omega, color='C2', label='omega')\n", - "\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Angular velocity (rad/s)')\n", - " \n", - "plot_omega(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot `y`" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_y(results):\n", - " plot(results.y, color='C1', label='y')\n", - "\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Length (m)')\n", - " \n", - "plot_y(results)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/modsim.py b/code/soln/modsim.py deleted file mode 120000 index d15694c2a..000000000 --- a/code/soln/modsim.py +++ /dev/null @@ -1 +0,0 @@ -../modsim.py \ No newline at end of file diff --git a/code/soln/oem_soln.ipynb b/code/soln/oem_soln.ipynb deleted file mode 100644 index 87e9e2bf7..000000000 --- a/code/soln/oem_soln.ipynb +++ /dev/null @@ -1,2161 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Case study.\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Electric car" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[Olin Electric Motorsports](https://www.olinelectricmotorsports.com/) is a club at Olin College that designs and builds electric cars, and participates in the [Formula SAE Electric](https://www.sae.org/attend/student-events/formula-sae-electric) competition.\n", - "\n", - "The goal of this case study is to use simulation to guide the design of a car intended to accelerate from standing to 100 kph as quickly as possible. The [world record for this event](https://www.youtube.com/watch?annotation_id=annotation_2297602723&feature=iv&src_vid=I-NCH8ct24U&v=n2XiCYA3C9s), using a car that meets the competition requirements, is 1.513 seconds.\n", - "\n", - "We'll start with a simple model that takes into account the characteristics of the motor and vehicle:\n", - "\n", - "* The motor is an [Emrax 228 high voltage axial flux synchronous permanent magnet motor](http://emrax.com/products/emrax-228/); according to the [data sheet](http://emrax.com/wp-content/uploads/2017/01/emrax_228_technical_data_4.5.pdf), its maximum torque is 240 Nm, at 0 rpm. But maximum torque decreases with motor speed; at 5000 rpm, maximum torque is 216 Nm.\n", - "\n", - "* The motor is connected to the drive axle with a chain drive with speed ratio 13:60 or 1:4.6; that is, the axle rotates once for each 4.6 rotations of the motor.\n", - "\n", - "* The radius of the tires is 0.26 meters.\n", - "\n", - "* The weight of the vehicle, including driver, is 300 kg.\n", - "\n", - "To start, we will assume no slipping between the tires and the road surface, no air resistance, and no rolling resistance. Then we will relax these assumptions one at a time.\n", - "\n", - "* First we'll add drag, assuming that the frontal area of the vehicle is 0.6 square meters, with coefficient of drag 0.6.\n", - "\n", - "* Next we'll add rolling resistance, assuming a coefficient of 0.2.\n", - "\n", - "* Finally we'll compute the peak acceleration to see if the \"no slip\" assumption is credible.\n", - "\n", - "We'll use this model to estimate the potential benefit of possible design improvements, including decreasing drag and rolling resistance, or increasing the speed ratio.\n", - "\n", - "I'll start by loading the units we need." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "revolutions_per_minute" - ], - "text/latex": [ - "$revolutions_per_minute$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "radian = UNITS.radian\n", - "m = UNITS.meter\n", - "s = UNITS.second\n", - "minute = UNITS.minute\n", - "hour = UNITS.hour\n", - "km = UNITS.kilometer\n", - "kg = UNITS.kilogram\n", - "N = UNITS.newton\n", - "rpm = UNITS.rpm" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And store the parameters in a `Params` object." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
r_wheel0.26 meter
speed_ratio0.216667
C_rr0.2
C_d0.5
area0.6 meter ** 2
rho1.2 kilogram / meter ** 3
mass300 kilogram
\n", - "
" - ], - "text/plain": [ - "r_wheel 0.26 meter\n", - "speed_ratio 0.216667\n", - "C_rr 0.2\n", - "C_d 0.5\n", - "area 0.6 meter ** 2\n", - "rho 1.2 kilogram / meter ** 3\n", - "mass 300 kilogram\n", - "dtype: object" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(r_wheel=0.26 * m,\n", - " speed_ratio=13/60,\n", - " C_rr=0.2,\n", - " C_d=0.5,\n", - " area=0.6*m**2,\n", - " rho=1.2*kg/m**3,\n", - " mass=300*kg)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`make_system` creates the initial state, `init`, and constructs an `interp1d` object that represents torque as a function of motor speed." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Make a system object.\n", - " \n", - " params: Params object\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " init = State(x=0*m, v=0*m/s)\n", - " \n", - " rpms = [0, 2000, 5000]\n", - " torques = [240, 240, 216]\n", - " interpolate_torque = interpolate(Series(torques, rpms))\n", - " \n", - " return System(params, init=init,\n", - " interpolate_torque=interpolate_torque,\n", - " t_end=3*s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `make_system`" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
r_wheel0.26 meter
speed_ratio0.216667
C_rr0.2
C_d0.5
area0.6 meter ** 2
rho1.2 kilogram / meter ** 3
mass300 kilogram
initx 0 meter\n", - "v 0.0 meter / secon...
interpolate_torque<scipy.interpolate.interpolate.interp1d object...
t_end3 second
\n", - "
" - ], - "text/plain": [ - "r_wheel 0.26 meter\n", - "speed_ratio 0.216667\n", - "C_rr 0.2\n", - "C_d 0.5\n", - "area 0.6 meter ** 2\n", - "rho 1.2 kilogram / meter ** 3\n", - "mass 300 kilogram\n", - "init x 0 meter\n", - "v 0.0 meter / secon...\n", - "interpolate_torque \n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
x0 meter
v0.0 meter / second
\n", - "
" - ], - "text/plain": [ - "x 0 meter\n", - "v 0.0 meter / second\n", - "dtype: object" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.init" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Torque and speed\n", - "\n", - "The relationship between torque and motor speed is taken from the [Emrax 228 data sheet](http://emrax.com/wp-content/uploads/2017/01/emrax_228_technical_data_4.5.pdf). The following functions reproduce the red dotted line that represents peak torque, which can only be sustained for a few seconds before the motor overheats." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.interpolate_torque" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def compute_torque(omega, system):\n", - " \"\"\"Maximum peak torque as a function of motor speed.\n", - " \n", - " omega: motor speed in radian/s\n", - " system: System object\n", - " \n", - " returns: torque in Nm\n", - " \"\"\"\n", - " unpack(system)\n", - " x = omega.to(rpm)\n", - " return system.interpolate_torque(x) * N * m" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "240.0 meter newton" - ], - "text/latex": [ - "$240.0 meter \\cdot newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "compute_torque(0*radian/s, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "216.0 meter newton" - ], - "text/latex": [ - "$216.0 meter \\cdot newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "omega = (5000 * rpm).to(radian/s)\n", - "compute_torque(omega, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot the whole curve." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "xs = linspace(0, 5000, 21) * rpm\n", - "taus = [compute_torque(x, system) for x in xs]\n", - "plot(xs, taus)\n", - "decorate(xlabel='Motor speed (rpm)',\n", - " ylabel='Available torque (N m)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Simulation\n", - "\n", - "Here's the slope function that computes the maximum possible acceleration of the car as a function of it current speed." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object \n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " x, v = state\n", - " unpack(system)\n", - " \n", - " # use velocity, v, to compute angular velocity of the wheel\n", - " omega2 = v / r_wheel * radian\n", - " \n", - " # use the speed ratio to compute motor speed\n", - " omega1 = omega2 / speed_ratio\n", - " \n", - " # look up motor speed to get maximum torque at the motor\n", - " tau1 = compute_torque(omega1, system)\n", - " \n", - " # compute the corresponding torque at the axle\n", - " tau2 = tau1 / speed_ratio\n", - " \n", - " # compute the force of the wheel on the ground\n", - " F = tau2 / r_wheel\n", - " \n", - " # compute acceleration\n", - " a = F/mass\n", - " \n", - " return v, a " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `slope_func` at linear velocity 10 m/s." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
x0 meter
v10.0 meter / second
\n", - "
" - ], - "text/plain": [ - "x 0 meter\n", - "v 10.0 meter / second\n", - "dtype: object" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "test_state = State(x=0*m, v=10*m/s)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " )" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(test_state, 0*s, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[]
nfev86
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events []\n", - "nfev 86\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results, details = run_ode_solver(system, slope_func, max_step=0.3*s)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And look at the results." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
xv
1.911125.69910026.493616
2.211134.22850030.353567
2.511143.90205934.121706
2.811154.69256137.800219
3.000062.04808140.071431
\n", - "
" - ], - "text/plain": [ - " x v\n", - "1.9111 25.699100 26.493616\n", - "2.2111 34.228500 30.353567\n", - "2.5111 43.902059 34.121706\n", - "2.8111 54.692561 37.800219\n", - "3.0000 62.048081 40.071431" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After 3 seconds, the vehicle could be at 40 meters per second, in theory, which is 144 kph." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "40.07143084364128 meter/second" - ], - "text/latex": [ - "$40.07143084364128 \\frac{meter}{second}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v_final = get_last_value(results.v) * m/s" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "144.25715103710863 kilometer/hour" - ], - "text/latex": [ - "$144.25715103710863 \\frac{kilometer}{hour}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v_final.to(km/hour)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `x`" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_position(results):\n", - " plot(results.x, label='x')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Position (m)')\n", - " \n", - "plot_position(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `v`" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_velocity(results):\n", - " plot(results.v, label='v')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Velocity (m/s)')\n", - " \n", - "plot_velocity(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Stopping at 100 kph\n", - "\n", - "We'll use an event function to stop the simulation when we reach 100 kph." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func(state, t, system):\n", - " \"\"\"Stops when we get to 100 km/hour.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object \n", - " \n", - " returns: difference from 100 km/hour\n", - " \"\"\"\n", - " x, v = state\n", - " unpack(system)\n", - " \n", - " v = v * m/s\n", - " \n", - " return v.to(km/hour) - 100 * km/hour " - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[[2.010128295248225]]
nfev86
njev0
nlu0
status1
messageA termination event occurred.
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events [[2.010128295248225]]\n", - "nfev 86\n", - "njev 0\n", - "nlu 0\n", - "status 1\n", - "message A termination event occurred.\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results, details = run_ode_solver(system, slope_func, max_step=0.2*s, events=event_func)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the results look like." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/chap11-fig02.pdf\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjgAAAI4CAYAAABndZP2AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzs3Xl8nGd97/3PzGjfd8m2Fu+X4z0ksROHkARMIIG2KdAUnpSSh9KH0ienK7ygaVpStpYu57RAoIdTCrQn5QBteFpCEpIADglOnNV7dFneJNnWvu/SzNzPH/dIHi2WNbY098zo+369/LJ8Xbfk3+TWyN/c1+ZzHAcRERGRVOL3ugARERGRxaaAIyIiIilHAUdERERSjgKOiIiIpJw0rwu4UsaYTOAGoAUIeVyOiIiIxF8AWAG8bK0di+5I2oCDG26e87oIERER8dwtwPPRDckccFoAHnnkEaqqqryuRUREROKstbWVe++9FyKZIFoyB5wQQFVVFdXV1V7XIiIiIt6ZNVVFk4xFREQk5SjgiIiISMpRwBEREZGUo4AjIiIiKUcBR0RERFKOAo6IiIikHAUcERERWRKdvSM0NPcQCjtx/7uTeR8cERERSUCO4/C67WD/kQsA7No8xq4t8d2UVwFHREREFk047PDzg+c5eqpzqi0UDse9DgUcERERWRQTwRBPvdjImZb+qbZV5Xm8aVNl3GtRwBEREZGrNjw6wWPPn6G9Z3iqbWNtMW+7voZAIP5TfhVwRERE5Kp094/y2POn6R8an2q7blMlN26twufzeVKTVlHFWVNTE7t27eLYsWMAtLW1sXv3bg4cOOBxZSIiIrE73zHIf/ysYSrc+H0+br+uhpu2rfAs3MAyeYLzum3npeOtTASXbpJTepqfXZuruNZUzHtdbW0tH//4x/n4xz/Oo48+ygMPPMB73vMedu/evWS1iYiILIUTTT0883IT4cgy8PQ0P++8cTV1Kwo8rmyZPME5eKJjScMNwEQwzMETHQu69p577qGuro577rmH9vZ2/vAP/3BJaxMREVlMjuPwyhttPHWgcSrc5GSl86u3rU+IcAPLJODs3FhOetrSvtT0ND87N5Yv+Pp77rmHEydO8MEPfpCMjIwlrExERGTxhMMO+147x4tHW6baSgqyeN9bN1BRnONhZdMtiyGqa03FZYeO4mloaIgvfOELvO997+PLX/4yd9xxB0VFRV6XJSIiMq/xiRA/frGRxtaLy8CrK/J4502rycpIrEixLJ7gJJrPf/7zbNmyhc9//vPcdtttfPrTn/a6JBERkXkNjkzwg30np4UbU1vML715bcKFG1DAibtnnnmG5557jr/4i78A4FOf+hTHjx/nv/7rvzyuTEREZG5dfSP8+09O0NE7MtV2/TWV7N1V68keNwuReJErxe3du5e9e/dO/Tk3N5enn37aw4pEREQurbltgCdfOMvYRAhwl4Hfdl01m9eUelvYZSjgiIiIyJxsYzc/eaV5+jLwm1ZTV5UYK6Xmo4AjIiIi0ziOw6v17dNWSuVlp/Oum9dSXpztYWULp4AjIiIiU0Jhh2dfa+b4me6pttKCLH7plrXk5STPtiYKOCIiIgK4y8CffPEsTa0DU23VFfncuWc1mekBDyuLnQKOiIiIMDgywWPPn6YzaqXUproSbr+uOmFXSs1HAUdERGSZ6+ob4YfPnWZwZGKqbdfmKm7YXOnpgZlXQwFHRERkGWtuG+CJF84yHrUM/PbrarhmTYm3hV0lBRwREZFlyHEcjp7q4rmD5wk77jLwjPQAd960mprKfI+ru3oKOCIiIstMKBTm2dfPc/xM11RbXnY6737zWsqKkmMZ+OUo4IiIiCwjw6MTPLH/LC1dQ1NtFcU53LVndVItA7+cuAUcY0wm8FVgL1ACnAQesNY+YYxZDZwBhqI+5YvW2s/Gqz4REZFU19Y9zBP7z0ybTGxqi7n9+hrSknCl1Hzi+QQnDWgGbgWagLuA7xljtkVdU2StDcaxJhERkWWhvrGbn73STChy7ILP52PPthXs3FietCul5hO3gGOtHQIeimp6zBhzBrgOeDVedYiIiCwn4bDD/iMXOHiiY6otMyPAO3bXUZsEZ0pdKc/m4BhjKoGNwLGo5kZjjAM8DXzCWtvpSXEiIiIpYHQsyI8PNNLcdnFn4pKCLO7as4ai/EwPK1t6ngy4GWPSgUeAb1tr64FO4AagDveJTn6kX0RERK5AV98I3/vJiWnhZs3KQt731g0pH27Agyc4xhg/8K/AOHA/gLV2EHglckmbMeZ+oMUYU2Ct7Y93jSIiIsns1Llennm5iYlgeKot2XcmjlVcA44xxgd8A6gE7rLWTlziUify+/K4CyIiIovAcRxefqONl461TrWlp/nZe0Mt66qLPKws/uL9BOdrwDXAXmvt1GlexpjdQC/QABQDXwL2WWv74lyfiIhIUhqfCPHMy02cPn/xn86C3AzedfMaSgtTY/O+WMRzH5w64KPAGNBqjJns+igQBr4AVAD9uJOMPxCv2kRERJJZ3+AYj//iDF39o1Nt1RX5vPPGOrIyl+eevvFcJt7I/ENO34lXLSIiIqmiuW2AJ188y9h4aKpt58Zy9mxbid+/fGd6LM9YJyIikuQcx+FQQwe/ONyCEzksM+B3TwLftDq5TwJfDAo4IiIiSSYYCrPv1WbqG3um2vKy07lzzxoqS3I8rCxxKOCIiIgkkcHhcR7ff5b2nuGptqrSXO68aTW52ekeVpZYFHBERESSREvnEE+8cJbh0Yu7rGxeU8Kt11YTSLHDMq+WAo6IiEgSOHa6i2dfP0c4clim3+fjzTtXsm1d2bLZvC8WCjgiIiIJLBR2eP7geY6cung8Y1ZGGu+8qY7qinwPK0tsCjgiIiIJanh0gh+/2Mj5jsGptrKibO7as4aC3AwPK0t8CjgiIiIJqKNnhMf3n2FgeHyqbX11EW+7oYb0tICHlSUHBRwREZEE09Dcw09ebiYYcg/L9Pl87N5SxXWbKjTfZoEUcERERBJEOOxw4Fgrr9a3TbVlpAd4+65a1qws9LCy5KOAIyIikgDGJkI89WIjja39U21F+Zm8a88aiguyPKwsOSngiIiIeKxnYJQf/eIMvQNjU211VQW8fXctWRn6p/pK6L+aiIiIhxpb+vnxgUbGJy4elvkmU8GNW1cs68Myr5YCjoiIiAccx+HV+nYOHGudOiwzLeDnrdfXsLG22OPqkp8CjoiISJxNBEP89JVmGpp7p9ryczK4a88ayouzPawsdSjgiIiIxFH/0DhP7D9DR+/IVNvKsjzeeVMdOVk6LHOxKOCIiIjEyfmOQZ584SwjY8Gptq3ryrhlx0odlrnIFHBERESWmOM4HD3VxXMHzxOOzLfx+33cem01W9aWelxdalLAERERWUKhUJhnXz/H8TPdU23ZmWnctWcNK8pyPawstSngiIiILJHh0Qke33+W1q6hqbaK4hzu2rOavBwdlrmUFHBERESWQFv3ME/sP8PgyMRUm6kt5vbra0jTfJslp4AjIiKyyOobu/nZK82Ewu58G5/Px55tK9i5sVyHZcaJAo6IiMgiCYcd9h+5wMETHVNtmRkB3rG7jtqqAg8rW34UcERERBbB6FiQHx9opLltYKqtpCCLu/asoSg/08PKlicFHBERkavU2TvC4/vP0D80PtW2ZmUhb99VS0Z6wMPKli8FHBERkSs0ub/N84fOT823Adi1uYobNldqvo2HFHBERESuwOhYkJ+80syZC31TbelpfvbeUMu66iIPKxNQwBEREYnZ+Y5Bnj7QOG0JeFlRNnfsrqOkIMvDymRS3AKOMSYT+CqwFygBTgIPWGufiPS/DXgYqAUOAPdZaxvjVZ+IiMjlhMIOLx9v5dX6dhzn4pDUjvXl3LR9hfa3SSDxfIKTBjQDtwJNwF3A94wx24BB4FHgI8APgc8C3wVujGN9IiIil9Q/NM5TBxqn7UqclZHG226oYc3KQg8rk7nELeBYa4eAh6KaHjPGnAGuA0qBY9ba7wMYYx4COo0xm6y19fGqUUREZC4NzT3se/UcYxOhqbbqinz27qolLzvdw8rkUjx7lmaMqQQ2AseALcChyb5IGDoVaRcREfHERDDET19p4scvNk6FG7/Px03bVvDLt6xVuElgnkwyNsakA48A37bW1htj8oCOGZf1AflxL05ERATo6BnhxwfO0jswNtVWkJvBHbvrqCrVKeCJLu4BxxjjB/4VGAfujzQPAjP3sC4ABhAREYkjx3E43NDJ/iMXpu1ts6GmmNuuqyZTG/clhbgGHGOMD/gGUAncZa2dXF93DPhQ1HW5wLpIu4iISFwMj07wk5ebaWztn2pLT/Nz67XVmLpibdyXROL9BOdrwDXAXmvtSFT7D4C/Mca8F/gR8OfAYU0wFhGReGluG+Dpl5oYHr24t015sbu3TXG+9rZJNvHcB6cO+CgwBrQaYya7PmqtfSQSbr4C/G/cfXDeH6/aRERk+QqFHQ4cbeH1Ex3T9ra5dmMFN26tIqC9bZLSggOOMeZ64GagDsgGOnFXPu2z1nZe7vMjm/Zd8tmetfYZYNNC6xEREblafYNjPHWgkbbu4am27Mw09u6qpa5q5tRQSSbzBhxjTA7wB8BvA2W4gaYVGMXdcfj/AqqNMU8Cf2WtfWFpyxUREVkctrGbZ18/z3jU3ja1le7eNjlZWv6d7C73BMcCPwY+DDxrrQ3PvMAYUwX8GvBPxpivWGu/tvhlioiILI7xiRA/f/0c9Y09U21+v48bt67g2o3lmkicIi4XcG6y1p6b7wJrbSvwZWPMV4CqRatMRERkkbV3D/PUgUZ6By/ubVOUl8kdu+uoKMnxsDJZbPMGnMuFmxnXOkDLVVckIiKyyBzH4fUTHbx4tIVw1N42m+qKecu11WRob5uUE9MqKmPMZuAWoJwZxzxYaz+ziHWJiIgsiuHRCZ55qYmmtot7x2akB7j12lWYuhIPK5OlFMsqqk8CX8Cdl9MOOFHdDqCAIyIiCaWxpZ9nXm5iZCw41VZZksMdu+sozMv0sDJZarE8wflj4DettY8sVTEiIiKLIRQK88LRFg6emH7M4ZtMBbu3aG+b5SCWgDMMvLpUhYiIiCyGnoFRnjrQSEfPxQ3zc7LSefuuWmoqdYbzchFLhH0A+IwxpnCpihEREblSjuPwxpluvvfMiWnhpq6qgPe/faPCzTITyxOcHwMfATqMMW3ARHSntXbtYhYmIiKyUGMTIfa9eo6G5ot72wT8PvZsW8n2DWXa22YZiiXgfA9YATzI7EnGIiIinmjtGuKpA430D41PtRXlZ/KO3aspL872sDLxUiwB5yZgl7X26FIVIyIislDhsMNrtp2XjrUSjjokc/OaEm7ZuYr0NO1ts5zFEnBext2pWAFHREQ8NTji7m1zrn363ja3X1fNhppiDyuTRBFLwPln4OHIkQzHmT0H5+eLWZiIiMhczlzo4ycvNzM6fnFvm6rSXO7YXUdBboaHlUkiiSXgfDPy+z/M0ecAehYoIiJLJhgKs//wBQ6f7Jxq8/l8XLepgl2bq/D7NZFYLlpwwLHWalckERHxRHe/u7dNZ+/F5d952em8fXcdq8rzPKxMElVMZ1GJiIjEk+M4HD/TzXMHzxMMhafa16ws5G3X15CVqX/GZG7zfmcYY/4Y+Edr7dDlvpAxZhdQYq19crGKExGR5Wt0PMjPXj3HqXO9U20Bv48371jF1nWl2ttG5nW56LseOGOM+RHwOPAa0AKMAUXAJuBm4B4gF/jw0pUqIiLLRUunu7fNwPDFvW1KCrJ4x411lBZqbxu5vHkDjrX2Y8aYv8bdwfhzwAamb/A3DPwc+CLwqLU2tFSFiohI6guHHV6tb+Ol4204UXvbbF1bys07VpGepumgsjCXHby01p4B/hT4U2NMKVALZAFdwCmFGhERWQy9A2P89JVmLnQOTrVlZgR463U1rKsu8rAySUYxzc6y1nbhBhsREZFFEQo7vG7befl4K6Hwxac2K8vyuGN3LXk52ttGYqfp5yIi4pm27mF+9mrztOXffp+P6zdXcv2mSu1tI1dMAUdEROJuIhjiwLFWDjV0TptrU16Uze3X11BRnONhdZIKFHBERCSuGlv7efa1c9NO/04L+Nm1pYqdG8r11EYWhQKOiIjExchYkOcPnsc29Uxrr6nM57Y3VVOYl+lRZZKKYgo4xpgAsA6oAKat1dNhmyIiMhfHcTjR1MNzBy9MOyAzKyONN+9Yiakr1qZ9sugWHHCMMW8B/jdQPUf3gg7bNMbcD9wHbAO+Y629L9K+GjgDRO+Y/EVr7WcXWp+IiCSe/qFx9r3WTFPrwLT2DTXF3LJzJTlZ6R5VJqkulic4XwOeAv7MWttyhX/fBdwNA98BzLUVZZG1NjhHu4iIJJFw2OHwyQ4OHG1lIuoMqbzsdG6/roa6FQUeVifLQSwBZzXwy1cRbrDWPgpgjLmeuZ8EiYhIkuvsHeGnrzTT3jM81ebz+di+rozdW6vISL/sA3+RqxZLwHkC2A2cWqJaABqNMQ7wNPAJa23nEv5dIiKyiIKhMC8fb+N12044aul3aUEWt19fQ1VprofVyXITS8B5HvhbY8wNwFFgIrrTWvsvV1FHJ3ADcBAoBR4GHsEdyhIRkQR3rn2Afa+eo3dwbKot4Pdxw+Yqrt1YTiCgM6QkvmIJOL+Pe4r43ZFf0RzgigOOtXYQeCXyx7bIZOQWY0yBtbb/Sr+uiIgsrdHxIPsPX+D4me5p7SvL8rj9+mqK87M8qkyWuwUHHGvtmqUsZIbJZ5taNygikoAcx+HU+T5+/vp5hkcvPtDPTA+wZ/tKNq8p0dJv8dQVbfRnjMmDqScvsXxeWuTvDAABY0wWEASuA3qBBqAY+BKwz1rbdyX1iYjI0hkcHufZ189z5sL0H9HrVhVyy7XV5GVr6bd4L9aN/v4I+CNgReTPLcD/AP67tdaZ73MjHgQ+HfXn3wD+ArDAF3A3EOzHnWT8gVhqExGRpeU4DkdPdfHC0RbGJ0JT7blZ6bzl2lWsqy7ysDqR6WLZ6O/zwG/hBpQXIs17gIdwJwY/cLmvYa19KHL9XL6z0FpERCS+uvtH+dkrzbR0DU1r37q2lBu3rSArQyf/SGKJ5Tvyw8B91tono9oOG2MagW+ygIAjIiLJJRQK86pt55U32giHLz6oL8rP5K3X1bCyPM/D6kQuLZaAUwg0ztHeBOQvTjkiIpIoWjqH+NmrzXT3j061+X0+3rSpguuvqSRNS78lgcUScJ4DPmeMuc9aOwBgjCnAPXrh+aUoTkRE4m98IsQLR1o4eroLJ2rDvsqSHN56fQ2lhXOdtCOSWGIJOB8DfghcMMbYSJvBfarzK4tdmIiIxN+ZC308+9o5BkcuLv1OT/Nz07YVbF1bht+vpd+SHGLZB+e0MWYrcAdusPEB9cBTC1xBJSIiCWp4dIKfv36ek+d6p7XXVRVw23XV5OdkeFSZyJWJadp7JMj8OPJLRESSnOM4vHG2m18cvsDY+MWl39mZadyycxUbaoq0YZ8kpXkDjjHmz4G/tdYORz6+JGvtZxa1MhERWVK9A2Pse62Zc+3T92y9ZnUJN29fSVamln5L8rrcd+/tuLsKD0c+vhQHUMAREUkCobDDwRPtvHy8jWAoPNVekJvB7dfVUFOphbGS/OYNONba2+f6WEREklN79zA/fbWZzt6RqTa/z8eOjeXs2lxFepqWfktqiGUn438Gfn9yiXhUey7wZWvthxe7OBERWRwTwRAHjrVyqKFz2tLv8qJsbr++horiHA+rE1l8sQywfgj4FDAwoz0H+CDuTsciIpJgGlv7efa1c/QPjU+1pQX87NpSxc4N5Vr6LSnpsgHHGPOWyIc+4CZjTE9UdwB3bs65JahNRESuwshYkF8cOk99Y8+09prKfG57UzWFeZkeVSay9BbyBGdf5HcH+MGMviDuRn9/vIg1iYjIVXAchxNNPTx/6AIjY8Gp9syMALfsWIWpK9bSb0l5lw041lo/gDHmDHCDtbZzyasSEZEr0t49zC8OX+B8x/Sl3xtqirll50pystI9qkwkvmLZyXjNUhYiIiJXrn9onBeOtNDQPH04Ki87nduvq6FuRYFHlYl4Qxv9iYgksdHxIK++0c7hkx2EwhdXR/l9PratL2P3lioy0gMeVijiDW30JyKShEKhMEdOdfLyG23TjlgAWLeqkBu3raA4P8uj6kS8p43+RESSiOM4NDT38uLRlmnLvgGqSnO5eftKVpTlelSdSOK44oNGjDGFuE91Gqy1xxavJBERmcuFjkF+cfgCbd3D09oL8zK5aesK1lUXanWUSEQsOxn/EHjaWvslY0wO8CpQCuQYYz5srX1kqYoUEVnOevpH2X+khTMX+qa1Z2WkccM1lWxdV0ogoCMWRKLF8gTnRuBPIh+/DxgBKoB7Iu0KOCIii2h4dIKXjrdx/HQX4ajjFQJ+Hzs2lPOmTRVkZejEb5G5xPLOyAX6Ix/fCfy7tXbCGPMc8L8WvTIRkWVqIhjmUEMHr9a3MREMT+sztcXs3rqCgtwMj6oTSQ6xBJx64FeNMd8H3gn890h7FdC72IWJiCw34bBDfWM3Lx1rZXBkYlpfdUU+e7av0KGYIgsUS8D5JPB93GDzXWvty5H2XwZeWuzCRESWC8dxaGobYP/hFrr6Rqb1lRZksWf7Smqr8jWBWCQGsexk/LQxphQotNZ2R3X9E+4+OSIiEqOOnhH2H7lAc9vAtPacrHR2b6nimtUlOu1b5ArENDvNWhsyxpQYY/ZEmuqttSeXoC4RkZQ2ODzOi0dbsE29OFETiNPT/FxrKrh2YznpadqBWORKxbJMvBT4FvAuLs65KTTGPA7cZ63tWvzyRERSy9hEiNfq2zjU0EkwdHECsc/nY8uaEnZtqdKBmCKLIJYnOF8FyoBrrLUWwBizCfhmpO/XL/cFjDH3A/cB24DvWGvvi+p7G/AwUAscwA1NjTHUJyKSsEJhh2OnO3n5eBsjY8FpfWtWFHDT9pWUFOhoBZHFEkvAuRN4y2S4AbDW1htj/l9g3wK/xgXgc8A7gOzJRmNMGfAo8BHgh8Bnge/i7r0jIpK0HMfh9Pk+XjjSQu/g2LS+iuIcbt6xklXleR5VJ5K6Ygk4QaJCSZTsSN9lWWsfBTDGXA9UR3W9Bzhmrf1+pP8hoNMYs8laWx9DjSIiCaO1a4hfHLpAS9fQtPaC3Axu3LqCDTVFWhklskRiCTj/AfxzZJjpQKTtRuDLkb6rsQU4NPkHa+2QMeZUpF0BR0SSSu/AGC8cbeHUuelbhGVmBLhuUyXb15eRpqMVRJZULAHn93D3wHk86vOCuHNw/vgq68gDOma09QH5V/l1RUTiZmQsyCvH2zhyupNw+OLKKL/fx/b1ZVy/qZKsTB2tIBIPseyDMwJ8zBjzCWAt4ANOWWsHF6GOQaBgRlsBMDDHtSIiCSUYCnO4oZNX6tsYnwhN69tQU8SNW1dQmJfpUXUiy9OCAo4xZi2wF8gAfm6tPbzIdRwDPhT19+UC6yLtIiIJyXEcbFMPLx5pmXW0wsqyPPZsX0FVaa5H1Yksb5cNOMaYO4D/BELABJBvjPlda+3XY/3LjDFpkb8zAASMMVm4w1w/AP7GGPNe4EfAnwOHNcFYRBJVc9sA+w9foKN3+tEKRfmZ3Lx9JatXFGgCsYiHFjLL7bO4G/wVWmuLgQeBL1zh3/cgMAJ8CviNyMcPWms7gPcCnwd6gN3A+6/w7xARWTJdfSP88LnT/OfPT00LN9mZadx6bTUfuGMTa1YWKtyIeGwhQ1SbgXuttZMDy38HfNYYU2GtbY/lL7PWPgQ8dIm+Z4BNsXw9EZF4GRyZ4KVjLbxxtmfa0QppAT87N5bzJlNBRrqOVhBJFAsJODm4K5oAsNZOGGNGcVc+xRRwRESSzUQwxOu2g9dtOxMzjlbYVFfM7q0ryMvW0QoiiWYhAccHfMIYE71aKgP4PWPM1Kni1trPLHZxIiJeCYbCvHG2m5ePtzE8On0CcW1VPnu2raSsaK69T0UkESwk4PwcuGFG235gR9SfHUABR0SS3uh4kKOnujh8snNWsCkryubm7SupqdQWXSKJ7rIBx1p7WxzqEBHx1ODwOAcbOjh2uouJYHhaX152OjduXcHG2mL8fk0eFkkG2lJTRJa1rr4RXrftnGjqJRw1eRjcYLNzYzlb1+loBZFko4AjIsuO4zi0dA7xmm3nbEv/rP7Sgiyu3VTBhuoiAgo2IklJAUdElo1w2OHMhT5es+20dQ/P6l9ZlsebNlVQV5WvfWxEkpwCjoikvGAojG3s4XXbTu/g2LQ+n8/H2pUFXGsqdKyCSApRwBGRlDXfiqiA38em1SXs3FhOcX6WRxWKyFJRwBGRlDPfiqjM9ABb15WxY0MZOVnaoE8kVSngiEjKWMiKqM1rSnWkgsgyoIAjIklNK6JEZC4KOCKSlLQiSkTmo4AjIklFK6JEZCEUcEQkKWhFlIjEQgFHRBKaVkSJyJVQwBGRhKQVUSJyNRRwRCRhaEWUiCwWBRwR8ZxWRInIYlPAERHPTK2IOtFO74BWRInI4lHAEZG404ooEVlqCjgiEjdaESUi8aKAIyJLTiuiRCTeFHBEZEmMT4Q4ea6X+rM9XOgcnNWvFVEispQUcERk0YTDDuc7Bqk/282p830EQ+FZ12hFlIjEgwKOiFy1nv5R6hu7sY09DI5MzOr3+3ys0YooEYkjBRwRuSKjY0Eamnupb+yec+8agNLCbDbVFWPqijVxWETiSgFHRBYsFHZoau2nvrGHsxf6CIWdWddkZ6axsbaYTXUllBVlaRhKRDyRUAHHGLMPuBEIRprOW2uNdxWJiOM4dPa6Q1AnmnoYGQvOuibg97F6ZSGb6oqprSog4FeoERFvJVTAibjfWvtPXhchstwNj05gG3uob+yhq29kzmsqS3LYVFfChpoisjIT8ceJiCxX+okkIlOCoTBnLvRRf7aH5raBWXvWgLtvjakrxtSVUFKgnYZFJDElYsD5S2PMXwEW+FNr7T6P6xFJaY7j0NY9TP3ZbhrO9TI2Hpp1TVrAz7pVhWxMlvJxAAAgAElEQVRaXcKq8jz8GoISkQSXaAHnk8BxYBx4P/BDY8xOa+0pb8sSST0Dw+PuENTZbnoHx+a8ZmVZHptWF7O+uki7DItIUkmogGOtPRD1x28bYz4A3AV82aOSRFLKRDDEqXN91Df2cL5jEGeOIaiC3Aw2rS7B1BZTmJfpQZUiIlcvoQLOHBxAz8JFroLjOJxrH8Q2ursLzzzkEiAjPcD66iI2rS5mRWmulnaLSNJLmIBjjCkCdgPP4i4T/3XgLcAfeFmXSLLqHRib2l14YHh8Vr/P56OmMo9NdSWsWVlIeprOgxKR1JEwAQdIBz4HbAJCQD1wt7XWelqVSBIZHQ9ysrmX+sYeWruG5rympCCLTXUlbKwrJi9buwuLSGpKmIBjre0AbvC6DpFkEw47NLcNUN/Yzenzc+8unJWRxsbaIjbVlVBenK0hKBFJeQkTcEQkNp29I9jGHmxTD8Ojcx9wWbeigE11xaxeUUAgoCEoEVk+FHBEksjw6AQNTe4Blx29c+8uXFGcg6krZkNNkQ64FJFlSwFHJIE5jkPv4BjNbQM0tgxccnfh3Kx0NtYVs6mumNLCbA8qFRFJLAo4IglmfCLE+Y5BGlv6aWoboH9o9goocHcXXrOykE2ri6mpyNfuwiIiURRwRDw2eVp3Y2s/zW0DtHQOzfmUZtKK0lw2rS5hXXUhWRl6C4uIzEU/HUU8MDw6wbn2QZpa+2lsHWBkLHjJa9PT/FRX5FNblU9tZb52FxYRWQAFHJE4CIUd2rqGaGoboKl1gI7ekTmPSZhUXpTtBpqqAqpKcrQCSkQkRgo4Ikukf2ic5rYBmlr7aW4fZHxi9indk7Iz06ipvPiURqufRESujgKOyCIJhsKc7xikqdV9StMzMHrJa/0+H1WlOdRWFVBbma/N90REFpkCjsgVchyHnoExmlr7aWod4ELnEMHQ7IMsJ+XnZEw9oVlVkacJwiIiS0g/YUViMDoejEwOdoeeBkdm7yA8KS3gZ2V5LnWVBdRU5VOcn6mnNCIicaKAIzIPx3Fo7xmZekrT1j087xLukoIsaqvyqanMZ1V5HmmaHCwi4gkFHJEZhkcnaGodoLHV3Tl4dPzSS7gz0wNUV7rDTrVV+eTnZMSxUhERuRQFHFn2QqEwrd3DUzsHd17ijCcAn89HRXE2NZX51FUVUFmSox2ERUQSkAKOLEt9g2PuPJq2Ac61DzARvPTk4JysdGor86itKqCmMp/sTL1tREQSnX5SS8obGQvS2TtCR+8IXb0jtHUP0zs4dsnr/X4fK0pzqYsEmrKiLE0OFhFJMgo4kjIcx6F/aJyO3hE6o37Nt9JpUkFuBnVVBdRWuZODM9IDcahYRESWigKOJKVgKEx33yidfVFhpm903t2Co6UH/FRX5FFTlU9tZQFF+TrfSUQklSjgSMIbHQu6w0uRMNPRM0LPwNi8y7WjpQX8lBZmUVaUTVlRNuWRXzrfSUQkdSngSMKYHGKKHl7qWOAQ06TszDTKI0Fm8ldRXqZWOomILDMKOOKJUChMd//YtCDT2Tey4CEmgKK8zGlPZUqLssnNStOEYBERUcCRpTc6HqSrb5TOnhE6eofp7Bulu290wUNMAb+P0sKLQaasKJvSwixNBBYRkUtSwJFF4zgOA8MT057KdPWN0D80vuCvkZWRFhVk3HkzxflZGmISEZGYKODIFQmGwvQOjF0cXooMMY2NL3yIqTAyxFQeeSJTXpRNbna6hphEROSqKeDIFMdxGJsIMTwaZGhkgpEx9/fh0SBDo+7vw6MTDI1OxBRkAn4fJZEAU1aUTVmhO18mU0NMIiKyRBRwloFw2HHDSlRImQwxbmC52BYMXfrIgoXIzAhMW8VUXpRNUX4WAQ0xiYhIHCngJLGJYPhiWBmNhJWRi09ZRkaDDI0GGRkL4ixwQu9C+X0+8nLSpwWZsqJs8jTEJCIiCSChAo4xpgT4BnAH0An8ibX237ytKr4cx2FsPDT1tGXa0FBUeBkeDca0pHqh0tP85Galk5OVRk5WOrlZ6WRnpblt2WlTfdmZWo4tIiKJK6ECDvAwMA5UAjuBHxljDllrj3lblstxHMJhh1DYIRgKEw47BEMOoXCYUHiuPrc9FJrsCxOMfDzZFwyFpw0bDY9OEAov7tMWcDfAy4mEk9yo8HIxtLh9WnotIiKpIGECjjEmF3gvsNVaOwg8b4z5L+CDwKfiWcu59gGee/08g6PuDrqO425MtxTB42r4/T5yMtPIzU6PBJY0crLTZzyBcZ+26FgCERFZThIm4AAbgZC19kRU2yHg1ngXcuhEB139o/H+a6dkpAciT1rSo566TB8iys1KJzMjoGEiERGROSRSwMkD+ma09QH58S5kY10x5zuH5pzj4vf7CPh9BPx+0gI+/H4faQE/Ab8v0ucnEPCR5vfhD/hJ8/sIBHz4/f7I57nXu5/nXp+dlRYVaNJIT9MwkYiIyNVIpIAzCBTMaCsABuJdyIaaYtasLCQYjCyZ9jEVYvTEREREJPEl0sSME0CaMWZDVNsOwJMJxmkBP1mZae6vjDTSAn6FGxERkSSRMAHHWjsEPAp8xhiTa4y5GfgV4F+9rUxERESSTcIEnIjfBbKBduA7wMcSZYm4iIiIJI9EmoODtbYbuNvrOkRERCS5JdoTHBEREZGrpoAjIiIiKUcBR0RERFJOQs3BiVEAoLW11es6RERExANRGWDWDrnJHHBWANx7771e1yEiIiLeWgGcim5I5oDzMnAL0ALMPlNBREREUl0AN9y8PLPD5ziJdUK2iIiIyNXSJGMRERFJOQo4IiIiknIUcERERCTlKOCIiIhIylHAERERkZSjgCMiIiIpJ5n3wYmZMaYE+AZwB9AJ/Im19t/muM4H/BXwkUjTN4BPWmudSP/OSNs1wBvAb1lrDy79K1iYGF7nJ4APAXWR675qrf2bqP6zQCUX9xnab629Y0mLj1EMr/Uh4E+Bsajm7dba05H+VLmnT+DuDzUpA7DW2m2R/rMk6D01xtwP3AdsA75jrb1vnmv/EPgkkA38B/Axa+1YpG818E1gN9AE3G+tfWYpa4/VQl+rMeZDwO8BG4B+4N+AB6y1wUj/PuBGIBj5lPPWWrOUtccihtd5H+7390hU87uttfsi/atJ4Hsaw+v8R+A3oprSgXFrbX6kfx+JfT8zga8Ce4ES4CTu9+MTl7je0/fpsgo4wMPAOO4P+J3Aj4wxh6y1x2Zc9/8AdwM7AAd4GjgN/KMxJgP4T+DvcW/0R4H/NMZssNaOx+dlXNZCX6cP+E3gMLAOeMoY02yt/T9R1/xSIv0gmcNCXyvAd621vzGzMZXuqbX2zug/R35g/nTG10rUe3oB+BzwDtwfiHMyxrwD+BTw1sjn/AD4i0gbwHeAF4C7Ir/+PXIvO5au9Jgt6LUCOcAfAAeAcuC/gI/j/g/YpPuttf+0RHVerYW+ToAXrLVvvkRfot/TBb1Oa+3vAL8z+WdjzLeA8IzLEvl+pgHNwK24oeQu4HvGmG3W2rPRFybC+3TZDFEZY3KB9wJ/Zq0dtNY+j/vD4oNzXP4h4O+steesteeBv8NN5wC34d7kv7fWjllrv4QbFN66xC9hQWJ5ndbav7bWvmatDVprLe4/8jfHt+IrF+M9nc9tpMg9nfF5q3Gf5vzrkhe5CKy1j1pr/z+g6zKXfgj4hrX2mLW2B/gskfenMWYj8Cbg09baEWvtfwBHcP/7JYyFvlZr7destc9Za8cjP4seIYneozHc00tKhnt6Ja8z6n397SUrbJFZa4estQ9Za89aa8PW2seAM8B1c1zu+ft02QQcYCMQstaeiGo7BGyZ49otkb65rtsCHJ4croo4fImv44VYXueUyLDcLcDMJx+PGGM6jDFPGWN2LG6pVy3W1/pLxphuY8wxY8zHotpT8p7iPp17zlp7ZkZ7It/ThZjr/VlpjCmN9J221g7M6E+Ue3m13sLs9+hfGmM6jTG/MMbc5kFNi+XayOs4YYz5M2PM5AhDqt7T9wIdwM9ntCfN/TTGVOL+fJrribnn79PlFHDygL4ZbX1A/gKu7QPyIiEglq/jhSut7yHc74dvRrXdC6zGnaPzM+DHxpiiRalyccTyWr+HO7+mHPht4M+NMR+4gq/jhSut7zeBb81oS/R7uhBzvT/B/e+R6Pfyihlj/m/geuBvo5o/CawFVgFfB35ojFnnQXlX6+fAVqAC9x/+DwCfiPSl6j39EPAvM/7HKmnupzEmHfeJ4rettfVzXOL5+3Q5zcEZBApmtBUAAwu4tgAYtNY6xphYvo4XYq4vMkHuN4FbJieAAVhrfxF12V9GJjzeAvxw8cq9Kgt+rdba41F/3G+M+QfgfbjjwKl4T98MVAH/Ht2eBPd0IeZ6f4L73yPR7+UVMcbcjTvvZq+1tnOy3Vp7IOqyb0dC+13Al+Nc4lWZnOwfccQY8xncgPOXpOA9NcbU4M5j+e3o9mS5n8YYP+7Q9zhw/yUu8/x9upye4JwA0owxG6LadjD3o7Vjkb65rjsGbI88zZm0/RJfxwuxvE6MMR/GnfT1Nmvtuct8bQd3bkqiiOm1zhD9WlLqnkZ8CHjUWjt4ma+daPd0IeZ6f7ZZa7sifWuNMfkz+hPlXsbMGPNO4H/hTg4/cpnLk/F+zmXm+zOl7inu/1DunxHs5pJw9zPyc/IbuAse3mutnbjEpZ6/T5fNExxr7ZAx5lHgM8aYj+CuRPkVYM8cl/8L8EfGmMdxv8H+mIsJeh/uEtvfiyz5m0zgM1eqeCKW12mMuRf4AnD7zDeaMaYWqME9gt4P/DegDPjFzK/jlRhf66/gPgbvBW7AXXr7QKR7HylyTwGMMdnArwHvmdGe0Pc0MuciDQgAAWNMFhCcXBId5V+AbxljHgFagAeJDMVZa08YYw4CnzbGPAjciRtWE2ZCKiz8tRpj3oo7DPCr1tqXZvQV4S6xfRZ3WfGv487R+YOlfwULE8PrvBN4zVrbZozZBPwZ8H1Ijnsaw/fupN8EvjjjayT8/Yz4Gu5w/15r7cg813n+Pl1OT3AAfhd3CV877tDEx6y1x4wxt0SGnib9T9xH9keAo8CPIm1Elg3fjfsN2gt8GLg7gZYTw8Jf5+eAUuBlY8xg5Nc/Rvrycb+Re4DzwDuBOyPpO5Es9LW+H3fPhgHcN94XrbXfhpS7p+C+lj7cOTbREv2ePoi7D8qncPcKGQEeNMbURr43awGstU8Cf437+hojvz4d9XXejztXpQd3WOd9CbSceNKCXivuP/SFwONR79HJPUfScd/DHbh7I/033O9bG88XchkLfZ1vAw4bY4aAx4FHcf/na1Ki39OFvk6MMTcB1UQCXJSEv5/GmDrcbTR2Aq1R35P3JuL71Oc4zuWvEhEREUkiy+0JjoiIiCwDCjgiIiKSchRwREREJOUo4IiIiEjKUcARERGRlKOAIyIiIilHAUdERERSzrLZyVhEEocx5lsA1tr74vz35gNvADde7mgSY8wngdXW2o/Nd52IJCYFHBFZVMaYy+0eugb4/XjUMoffB55awLlrAF8FzhhjvmitPbu0ZYnIYtMQlYgsthVRv/4OeGFGW7O1ts9a2xfPoiInIH8U9xTky7LWDuAeG/DhpaxLRJaGnuCIyKKy1rZOfhw5J2s8ui3S/q3ItfdF/nwW90DbW4G9uMNI9+A+7fkKUIV7UN8fWmudyOeUA38PvAv3cMIngd+z1nZforQbgWLcQ1cn61iL+6Rm8uDSN4B7rbUnI39+HPjzyC8RSSJ6giMiieLjuCdnvwkYjnz8CdxD+d4P/A5wV9T1/457CvwtwG244eXb83z9PcAha20oqu0ruAcb3oB78N+XgHBU/6vANcaY0it9USLiDT3BEZFE8V1r7XcBjDFfAf4PsNNaeyjS9jPgLcCPjDFvAdYDb50MLMaY3wbOG2OqZj4xiqgFWma01QD/FnVi84kZ/a1R1yXKqesisgAKOCKSKI5Ffdwe+f34jLbyyMfbcIet+owxM7/OWi4Gk2hZwNiMtq8C3zDGfBB4CjdkRU9AHo38nr2QFyAiiUNDVCKSKCaiPnYArLUz2yZ/ZuUB9cDOGb82AK9f4ut3AUXRDdbarwHX4M61uROoN8bsibqkOPJ7Z4yvRUQ8pic4IpKMDgGrgV5r7ULDx2HcicvTWGtPA/8A/IMx5nHc+T77I92bgAHg9NUWLCLxpYAjIsnoKdwhrUeNMQ8AF3Dn5LzHWvs7l/icfUBd9BwdY8z/AH4EnMSdZ7Md92nOpD3AvhkTk0UkCWiISkSSjrU2DLwTaAB+gBt2vgT0zPM5LbjB6NeimtOBr+MOd30H+Dfga1H9v8b8K7NEJEH5HOdym46KiKQGY8wtwP8EtkzupzPPtTcB34xcqyc4IklGT3BEZNmw1j4HPIy7o/LlFAO/pXAjkpz0BEdERERSjp7giIiISMpRwBEREZGUo4AjIiIiKUcBR0RERFKOAo6IiIikHAUcERERSTkKOCIiIpJyFHBEREQk5STtYZvGmEzgBqAF0E6jIiIiy08Ad2fyl621Y9EdSRtwcMPNc14XISIiIp67BXg+uiGZA04LwCOPPEJVVZXXtYiIiEictba2cu+990IkE0RL5oATAqiqqqK6utrrWkRERMQ7s6aqaJKxiIiIpBwFHBEREUk5CjgiIiKSchRwREREJOUo4IiIiEjKUcARERGRlJPMy8RFREQkATmOQ3PbAMfOdDM2HuTWa6spLsiKaw0KOCIiIrIoxidC2MYeDp3soHfg4skJr59o563X18a1FgUcERERuSq9A2McOdnJG43djE9M33PP7/OxZmVh3GtSwBEREZGYOY5DU+sAh0920tjaP6s/Iz3ANXUlbF1fSnF+fIenQAFHREREYjA2EaL+TDdHTnXSOzg2q78oP5Md68sxdcVkpAc8qNClgCMiIiKX1dM/yuGTndQ3djMRDE/r8/l8rK7KZ9v6Mmoq8/H5fB5VeZECTpx9/etf5+jRo3zpS1+aavvc5z4HwIMPPuhVWSIiIrM4jsPZln6OnOykqW1gVn9meoBr1pSwbV0ZhXmZHlR4acsi4Lxu23npeOusxLmY0tP87NpcxbWmYt7r3vWud/Hwww8zODhIXl4eoVCIJ598kq985StLVpuIiEgsRseD1J/t5vDJTvqHxmf1lxRksX19GaaumPQ074ah5rMsAs7BEx1LGm4AJoJhDp7ouGzAWbVqFZs3b+aZZ57h7rvv5sUXXyQrK4udO3cuaX0iIiKX090/yuGGDmxjDxOhOYahVhSwfX0Z1RV5CTEMNZ9lEXB2biyPyxOcnRvLF3Ttu9/9bh577DHuvvtuHnvsMd797ncvWV0iIiLzCYcdGlv7OdTQybn2OYahMgJsXlPKtnVlFORmeFDhlVkWAedaU3HZJyvxdOedd/LFL36R1tZWnn76ab773e96XZKIiCwzo+NB3oishpprGKq0MJvt68vYWFtMelryney0LAJOoikpKWHXrl38yZ/8CdXV1axbt87rkkREZJnojqyGsme75xyGWruqkO3ry1hZlpvww1DzUcDxyLvf/W4++clP8olPfMLrUkREJMU5jkNj6wCHGzrmXA2VlZHGlrUlbF1XRn5O8gxDzUcBxyN33303d999t9dliIhIChufCPHG2W6OnJx7U76yoovDUGmB5BuGmo8CjoiISIqZ72won8/H2pUFbN9QnvTDUPNRwBEREUkBjuNwrn2Qww0dnG0dwHGcaf2Z6ZHVUOuTazXUlVLAERERSWITwRC2sYfDJzvp7h+d1V+cn8X2DWVsSuBN+ZaCAo6IiEgS6h8a58ipTo6f6WJsPDSrf3JTvkQ5GyreFHBERESShOM4XOgc4nBDB6cv9M8ahspID3BNXQnb1pdRlJ9YZ0PFmwKOiIhIgguGwpxocoehOntHZvUX5WWybX0Z16wuISN9+QxDzSduAccYkwl8FdgLlAAngQestU8YY1YDZ4ChqE/5orX2s/GqT0REJNEMjkxw5KQ7DDUyFpzVX1OZz/b1ZaxeUbAsh6HmE88nOGlAM3Ar0ATcBXzPGLMt6poia+3sOygiIrJMOI5DW/cwhxo6OHWuj/CMYaj0gB9TV8z2DeWUFGR5VGXii1vAsdYOAQ9FNT1mjDkDXAe8Gq86REREElEoFObkuV4ONXTS3jM8q78gN4Ot68rYvKaErAzNMLkcz/4LGWMqgY3AsajmRmOMAzwNfMJa2+lJcSIiInEyPDrB0dNdHD3VxfDoxKz+VeV5bF9fxpqVhfj9GoZaKE8CjjEmHXgE+La1tt4YkwfcABwESoGHI/3v8KI+ERGRpdbePczhkx2caO4lHJ4+DBXw+9xhqPXllBVle1Rhcot7wDHG+IF/BcaB+wGstYPAK5FL2owx9wMtxpgCa21/vGsUERFZCqGww+nzvRxu6KSla2hWf152OlvXlbFlbSnZmRqGuhpx/a9njPEB3wAqgbustbOfxbkmo6yexYmISNIbGQty7HQXR091Mjgy+5++qtJcdmwoY+2qIgIahloU8Y6HXwOuAfZaa6cW8htjdgO9QANQDHwJ2Get7YtzfSIiIoums3fEHYZq6iUYCk/r8/t9bKguYvuGcipLcjyqMHXFcx+cOuCjwBjQaoyZ7PooEAa+AFQA/biTjD8Qr9pEREQWSzjscOZCH4dPdnK+Y3BWf3ZmGtvWlbF1XSk5WekeVLg8xHOZeCPzDzl9J161iIiILLbR8SDHz3Rz9FQn/UPjs/orinPYvqGMDdVFBAJ+DypcXjSDSURE5Cr0DIxyuKGT+rPdTMwchvL5WFddyPb15VSV5mi34ThSwBEREYmR4zicax/k4IkOGltnL/bNykhjy9pStq0rJS8nw4MKRQFHRERkgYKhMLaxh8MNHXT1j87qLy3MZseGMjbWFpOmYShPKeCIiIhcxtDIBEdOdXLs9OxDL30+H6ur8tmxsZxV5XkahkoQCjgiIiKX0N4zzOGGuXcbTk/zc83qEravL6coP9OjCuVSFHBERESiTC7zPtTQyYXO2cu883My2L6+jGt06GVC050REREBxidCvHGmm0MnO+Zc5r2iNJcdG8tZq0Mvk4ICjoiILGt9g2McPtnJG2e7GZ8ITevz+3ysrylih3YbTjoKOCIisuw4jsOFziEONXRw5kI/jjN9fs3UMu/1ZeRla7fhZKSAIyIiy0YoFKbhXC+HTnTQ0Tsyq7+kIIsdG8rZWFtMepqWeSczBRwREUl5w6MTHDvdxZFTXQyPzj7Nu7Yqnx0byqmtzNcy7xShgCMiIimrq2+EQw0d2MYeQjOWeacF/GyqK2b7hnJKCrI8qlCWigKOiIikFMdxONvSz6GGTs61D8zqz8tOZ+u6MrauLSUrU/8MpirdWRERSQkTwRD1Z3s41NBB7+DYrP7Kkhx2bChnXXURAS3zTnkKOCIiktQGhsc5fLKT42e6GBufvszb5/OxdlUhOzfoNO/lRgFHRESSUmuXu8z71Lk+wjOWeWemB9i8tpRt68ooyNVp3suRAo6IiCSNUNjh1LleDjV00NY9PKu/KC+T7RvKuGZ1CelpAQ8qlEShgCMiIglvdCzIsTNdHDnZyeDI7GXe1RX57NhQxuoVBRqGEkABR0REElhP/yiHGjqob+whGApP6wv4fZi6YravL6esKNujCiVRKeCIiEhCcRyH5rYBDjV00tjaP6s/JyudretK2bq2lJwsHaMgc1PAERGRhBAMhbGN7jLv7v7RWf1lRdns3FDOhpoiAgEdoyDzU8ARERFPDY9OcPRUF0dOdTIyFpzW5/P5WL2igJ0by1lZlqv5NbJgCjgiIuKJ+Y5RSE/zs3l1Kds3lFGYl+lRhZLMFHBERCRuJufXHGzooKl19jEK+TkZbF9fxua1pWSma5m3XDkFHBERWXLBUJgTTT0cOtFB1xzzaypLcti5sZx1q4rw6xgFWQQKOCIismSGRyc4drqLwyfnnl+zdmUBOzdW6BgFWXQKOCIisui6I/vX2Dn2r9H8GokHBRwREVkUjuNwrn2Qgyc65ty/Ji87ne0bytm8poSsDP3zI0srbt9hxphM4KvAXqAEOAk8YK19ItL/NuBhoBY4ANxnrW2MV30iInJlQqEwDed6OXiig87ekVn95cXZXLuxgnXVRQQ0v0biJJ4ROg1oBm4FmoC7gO8ZY7YBg8CjwEeAHwKfBb4L3BjH+kREJAajY0GOnnbPhxoanX4+1OT+NdduLGeF9q8RD8Qt4Fhrh4CHopoeM8acAa4DSoFj1trvAxhjHgI6jTGbrLX18apRREQur2dglEMNndSf7Z41vyYt4Oea1SXs2FBOUb7m14h3PBsENcZUAhuBY8DHgEOTfdbaIWPMKWALoIAjIuIxx3G40DnEwRMdnG3px3Gmb8yXm5X+/7d35/FxXvW9xz+SLFuyJVnreIlX2dJPcRxbcuBVSptCKRdI2l5SKAUaIIFCgd5A6W15QSlLCpT1UpaGBAopSSAJtJC00AQK3JKWFkq5lmzHdvSTbXmJ19Fuy7b2uX88j8JIo2VkazSLvu/Xy69I5zya+T15ZkY/nef8zuH6rdVsr62iaJnm10j6peVVaGaFwIPA/e7eamYlQMekw/qA0gUPTkREnjE6FuPw0z3sOdRBR0/i/Jrq8mIa62uoW6f9oSSzLHiCY2b5wFeBIeCOsLkfKJt0aBmQuMyliIik3MDQCAfbu9l3uIP+y8MJ/ZvWlLGzroZ1kRLNr5GMtKAJjpnlAfcCq4Cb3X38XXMAuC3uuBXAlrBdREQWSF//IHsPdfDUsW6GRxLn1zRsrGBnXQ0VZUVpilAkObMmOGZWDLwY+BVgI1AMdBLMmfmhu++fw/PdA1wLvNDd48c6HwU+aWYvBx4D3g/s0wRjEZHUi8VinO26xJ62KO2nE+fXLC8qZMfWaq6rrc6RGCQAACAASURBVKJY82skS0z7SjWztcB7gVuBKLAbOA0MABXA7wAfMrNDwEfHK6BmeLyNwJuBQeCsmY13vdndHwyTm7uArxGsg/OqqzgvERGZxdhYjCOngvVrznVfSuivKiuisT5C3YZylmh+jWSZmVLx/wa+DDzL3Q9NdUC4eN/NwP8ys03u/snpHixctG/aG7Xu/kOgIamoRUTkig0Oj3Iw3B/qwqWhhP4Nq0tpqo9ofo1ktZkSnAZ375/ph919kOD20qPhvBkREclQff2D7DvcycGjXQnzawry87Bwfk3VyuI0RSgyf6ZNcGZLbsysJP6YcCE/ERHJMGe7LtLS1kH7qb6E+TXFy5Zw/ZZqtm+pYnlRYZoiFJl/Sc0WM7O/AI64+9fD778BvMLMzgD/0913pzBGERGZo7GxGO2n+9jT1sHZrsS/PyvLithZV4NtrND8GslJyU6H/0PCSb9m9hLg+QRVVa8EPhV+LyIiaTY8MsrB9m72Hu7g/MXE+TXrV5XSWFfDhtWlml8jOS3ZBGcVcDL8+reBr7v7T83sHNCSkshERCRp/ZeHefJwB/vbuxgcGp3Ql5+fh20I5tdUl2t+jSwOySY4ZwgqnJ4GbuIXKxAvB0an+yEREUmtrr7L7GnrwE/0MDY2cX5N0dIlbN9SxfVbqllRrPk1srgkm+DcBfy9mZ0EhoEfhO03AnNZ6E9ERK5SLBbjZLSflrYoJ84m7mhTXrKMnfU1NGyspHCJ5tfI4pRUguPunzKznwDrge/HbbFwimAxQBERSbHRsRhHTvbS4lE6ehM3vlxTtYLG+ho2r11Jfr7m18jiNmOCY2ZfAx4HvufuPwV+Gt/v7t9OYWwiIgIMDY9y8GgXe9oSN77My8ujdm0ZjfUR1lRrOTKRcbON4OwGXg98ycz2EOwT9bi770l5ZCIii1z/pSH2Hu7kQHsXQ8MTpzsuKcjn2k2V7Kyrobx0WZoiFMlcMyY47v5p4NPhKsX/g2Bbhu+Eu4J/l2B05wezLQooIiLJ6+y9zJ62KG0nehmbYmG+HVur2b6lWhtfiswg2Tk4F4F/DP9hZo0Eyc6fAg+G83P+yt1/lKpARURyWSwW4+lzF2hp6+Dpc1NMHC5dRlN9RAvziSTpitL/8BbVHuAjZlYBvARQDaKIyByNjo5x6GQvLd5BV1/ixOG11SU0WQ2b1pRpYT6ROUg6wTGzfGAzUAPE//kQc/eH5zswEZFcNjA0wsH2bvYdnnri8NZ1K2msj7CqcnmaIhTJbsnuRfUC4CvAOmDynxAxoGCe4xIRyUnnLw6x73AHB9oTd/QuXJLPtk1V7KirZmWJJg6LXI1kR3C+CHwH+AgQJUhqREQkSdGeS7R4B0dOJk4cXl5UGEwcrq2iSBOHReZFsu+kauDT7n46lcGIiOSSWCzGibMXaGmLcjKaWGxaWVZEU32E+g3lFGjisMi8SjbB+TLwe8BHUxiLiEhOGB0dw0/0sKetg+7zAwn96yKlNNVrR2+RVEo2wXk38D0zuwU4SLAf1TPc/Q/nOzARkWwzMDjC/vYu9h3u5NLAxInD+Xl5bF1fTmN9DZEKTRwWSbVkE5y/JthY898JysF1k1hEJNTXP8jeQx08dbSb4dHEicPX1Vaxs66G0uVL0xShyOKTbKLyBuAWd/9eKoMREckm57ov0eJRjpzqIzZp4nBJcSE76mrYtrmSoqX6m1BkoSX7rjtDsHO4iMiiFovFOHbmPC3ewenOxInD1eXFNNbXULdOE4dF0inZBOdtwMfN7E+BVndXmbiILCojo2P48R5a2qL0XhhM6N+wqpQmi7AuUqKJwyIZINkE558JVi9+MRAzswk3md1dN5ZFJCddHhzhySOdPHm4k8uDIxP68vPyqN9QTmN9hOry4jRFKCJTSTbBeUlKoxARyTB9/YO0tHXQeqybkUkTh5cWFrC9toodW6sp0cRhkYyU7G7i/zfVgYiIZIKZJg6XLl/Kzrpqtm2uYmmhdqgRyWTTJjhm1hjuGj4rMysCNrh727xFJiKyQGKxGCfOXaDFp15xuKaimKb6CFvWlVOQr/k1ItlgphGch8zsBPBV4Lvu3h3faWZ5QBPBCse3EiwGqARHRLLG6FiMQ0/30OIddPVdTujfsLqUpnpNHBbJRjMlONuB1wFvBx4Ik50zwCBQDtQBI8DXgV9398OzPZmZ3QHcDlwPPOzut4ftm4CjwMW4wz/u7h+a2+mIiMxuaHiUg0e72NPWQf/lxBWH69aX02SaOCySzaZNcNx9DLgPuM/M1gPPBTYARUAXsA/4ubsn1ktO7zTwYYJqrKk+OcrdfWSKdhGRq3ZpYJi9hzrZ397J4NDohL7CJfls2xysOFy2QhOHRbJdspOMnwa+cbVP5u6PAJjZs4B1V/t4IiLJ6LkwwJ6wImp0bOLE4eJlS9hZV8P22iqKlmnFYZFckWnv5uNmFgN+ALzT3TvTHZCIZK+zXRdp8Sjtp88nVESVlyyjySLYxgqWaMVhkZyTKQlOJ/BsYA9QBXweeJDgVpaISNJm20phVeVydlmEzWtXkq+KKJGclREJjrv3A/8v/PZcOBn5jJmVufv5NIYmIllidHSMthO9tLRF6T4/kNC/aU0ZuyzCmuoVqogSWQQyIsGZwvhYsj6FRGRGg8OjHGjvYt+hqSui6jdU0GQ1VK1URZTIYpJUgmNmnwAecPf9V/NkZrYkfM4CoCBcIHAEuAHoBQ4BFcDngCfcve9qnk9Eclf/5WH2Hepgf3sXQ8MTK6KWFhZw3eYqdtZpKwWRxSrZEZzrgWYzOwg8ADzk7mev4PneC3wg7vvXAH8JOPARIAKcJ5hk/OoreHwRyXHd5wfY0xal9XgPY5MqopYXFdJYV8O22kqKlmbqALWILIRky8RvMrMa4PcJVi3+mJn9K0Gy86i7Jy4BOvXj3AncOU33w8k8hogsTmc6L9LsUY6eThzYLS9dxi6LYBsqKFBFlIgwhzk47t4BfBb4rJnVA68H/g74gpk9Atzj7j9LTZgishjFYjGOnj5Ps0c523UxoX9N1Qp2NUTYtKZME4dFZII5j+Ga2XMIbi29kmDrhq8R3Fr6oZnd7e7vmt8QRWSxGRkdw4/30NIWpfdC4mLpm9eufKYiSkRkKslOMt5KkNTcCqwCvgW8wt2fiDvmQeC7gBIcEbkiA0MjHGjvYu+hTi4NTKyIKsjPwzZW0FgfobKsKE0Riki2SHYEpxX4EfBB4FvufmmKY3YDj8xXYCKyePRfGmLPoQ4OtHcxPDI2oW9pYQHba6vYUVdDSXFhmiIUkWyTbIKzOdyPalrufhF47dWHJCKLRVffZVo8StuJXsYmbaVQUlzIjnCPqKWFBWmKUESyVbIJzhEzuyacaPwMM6sEzrq7FpoQkaTEYjFOd16kuTXK8bOJC5VXlhXRVB+hfkO5KqJE5Iolm+BMd1wJwUJ9IiIzGhuL0X66jxaPcq478S732uoSmqxGFVEiMi9mTHDM7G/DL2PAX5tZ/Ho3BUAT8PMUxSYiOWBkdIzWY93saeugt39iRVReXh61a8tosgirq1QRJSLzZ7YRnPHNW/KAyWULw8A/AV+a76BEJPsNDI2w/0gXew91cHlw4kBvQX4eDZsqaayvoaJUFVEiMv9mTHDc/bUAZnYM+Fg4kVhEZFr9l4fZG1ZETd4jatnSArbXVrOzrprlRaqIEpHUSXarhvelOhARyW49FwZo8Sh+vIfRscSKqMb6GrZtVkWUiCyMaRMcM2sDnuPu3WZ2iGAezpTcvT4VwYlI5jvXfYnm1nO0nz5PbFKpd2VZEbssQt16VUSJyMKaaQTn48D4LamPLUAsIpIlYrEYJ85doMWjnIz2J/SvrlrBDdojSkTSaNoEx93vneprEVm8xsZiHD7ZS4tH6ei9nNC/aU3ZM3tEKbERkXRKdi+qlwMD7v7YpPbfAgrd/dFUBCcimWFkdIynjnXT4lHOXxya0Jefl0fd+nJ2NUSoWlk8zSOIiCysZBf6+zBwxxTt/cDdgBIckRw0U6n3koJ8tm2upLE+QtkKLWYuIpkl2QRnE9A+RftxYPO8RSMiGWG81Hv/kc6EzS+Lli5hx9Zqtm+pUqm3iGSsZBOc08AvAUcntf8ycG5eIxKRtOk5P0BLW5TW4z2MTVHq3VQfYVttJYVLVOotIpkt2QTn88BdZrYS+DFByfjzCG5dfSJFsYnIAlGpt4jkmmQX+hvfh+p9wNqw+QzwAXf/fKqCE5HUGS/1bm6NcqojsdR7TdUKdqnUW0SyVLIjOLj7PcA9ZlYO5Ll7T+rCEpFUSarUuyHC2uqSNEQnIjI/kk5wAMxsE9AQft3q7sdSEJOIpMDI6BhPHe2mpU2l3iKS+5JdB6cC+DvgpcAFgt3FV5jZt4E/cPfu1IUoIldjtlLv6zZXsbO+RqXeIpJTkh3BuRu4Brje3Q8AmNl24F6CCcivTk14InKl+i8Ps7etg/3t05d6X7+1muJlcxrIFRHJCsl+sv0m8Lzx5AbA3feb2R8BP0pJZCJyRVTqLSKSfIIzAiybon1p2CciaXa26yItHp2y1LuqrIimhgh16ysoyFdFlIjkvmQTnEeBe8MRm5+Fbc8B7kLbNIikjUq9RUSmlmyC8zbgs8APgPFx7VHgAeAdKYhLRGYwXurd7FE6pyj13rymjCaVeovIIpbsQn+XgDeZ2Z8BWwiqqA67e99cnszM7gBuB64HHnb32+P6foNgwvIGglGi2939+FweXyTXzVbqXb+hnCZTqbeIyJzKJ8KEpvkqnu80wfYOLwae+QQ2s2rgEeCNwHeADwHfILgNJrLoqdRbRGRupk1wzOwHBHtOzcrdX5TkcY+Ej/0sYF1c18uAA+7+D2H/nUCnmTW4e2syjy2Si1TqLSJyZWb6VPyvBYsCrgP2jn/j7hfN7EjYrgRHFh2VeouIXJ1pExx3f98CxlECdExq6wNKFzAGkbSbaVdvlXqLiCQv6XFtMysGbgJqgS+5e5+ZbQF65mGrhn6gbFJbGcG2ECI5LRaL8fS5CzR7lJNRlXqLiMyHZPei2gb8CzBEUOX0CMEIy5uAVcDrrzKOA8Btcc+3gqBa68C0PyGS5cbGYhw51Utzq3b1FhGZb8mO4PwN8FV3f4+ZxY+qfBt4KNknM7Ml4XMWAAVmVkSwEvKjwCfN7OXAY8D7gX2aYCy5aGR0jNZj3TS7dvUWEUmVZBOcZxOM1kx2Glg9h+d7L/CBuO9fA/ylu98ZJjd3AV8jWAfnVXN4XJGMN17qve9wJ5cGhif0LSnIZ9vmShrrIyr1FhGZB8kmOOcJbkW1T2rfSZDkJMXd7wTunKbvh0BDso8lki36Lw+z91AHB9q7GBoendC3bGkBO7fWsH1LFcuLCtMUoYhI7kk2wXkA+JSZvYZgbZyicOXhzwBfTlVwItms98JgUOp9rJvRKUq9G+truK62SqXeIiIpkGyC8z6C1YX3A0XAkwRzZ+4BPpKa0ESyU7T7Ers9SvupvoRS74rSInZZhPoN5RQU5KcpQhGR3JfsXlSjwHvM7ENAHcG6NQfdvTeVwYlki1gsxsloP7tbo5yMJq5usLpqBTeo1FtEZMHMmOCY2eMEt6f+0d0H3P0ysG9BIhPJAmNjMdpP9bHbz9HRk1jqvXF1GTc0RFhTvUKJjYjIApptBOcc8AXgi2b2LeB+d/+31IclktlGRsfw4z20eJTe/sEJfXnjpd4Wobpcpd4iIukwY4Lj7q83s7cCtwC3Aj8ws9MEpdxfdXdfgBhFMsbg8CgHjnSx51DHlKXe126qpLG+hpUly9IUoYiIQBJzcNx9APg68HUzqwZ+nyDZ+XMz200wqvP51IYpkl6XBoJS7yePTF3qff2WanZsrVapt4hIhkh6LyoAd+8EPgd8LlyY70vh90pwJCf1XhhkT1uUp6Yp9d5ZF5R6Ly1UqbeISCaZU4JjZmsIRnBeA+wA/gu4PwVxiaRVtOcSLR7l8MnEUu/y0mXssgi2oUKl3iIiGWrWBCfc+PJlBEnNC4ATBHNwftfdj6Q2PJGFM17q3exRnj6XWOq9qnI5uyxC7TUrVRElIpLhZisT/xrwUoJF/b4JvMDdf7wQgYkslLGxGO2n+2hujRLtuZTQv2F1KbsswjU1JUpsRESyxGwjOBXAGwnWwRmc5ViRrDI6Okbr8R5a2qL0Xkgs9d66Lij1rqlQqbeISLaZrUz8NxcqEJGFMjQ8yv72Lva2dXBxUql3QX4e126qpMkiKvUWEclic5pkLJLNglLvTvYf6WRwcql3YQHbt1Szs06l3iIiuUAJjuS8vv5BWto6eOpoV0Kp94qiQnbW17Bdpd4iIjlFCY7krI6eyzR7lMMnexNLvUuW0WQRGjaq1FtEJBcpwZGcEovFON15kd2t5zhxNrHUO1KxnF0NEWrXriQ/XxVRIiK5SgmO5IRYLNjVu9mjnOtOLPVevyoo9V4XUam3iMhioARHstro6Bh+oodmn67UeyVN9REilcvTFKGIiKSDEhzJSkPDoxxo72LvoQ76L09d6t1YH6G8VKXeIiKLkRIcySqXBobZd7iTJ490Mjg0sdR7aWEB22ur2FlXw4pilXqLiCxmSnAkK/T1D7KnrYOnjnUzMjo2oW95USGNdTVct6WKZSr1FhERlOBIhuvsDUu9n+5lbFKp98qScFfvjRUsUam3iIjEUYIjGScWi3Gm8yK7W6McP3s+ob+mopgbbBW116jUW0REpqYERzJGLBbj2Jnz7G6NcrbrYkL/ukgpNzSo1FtERGanBEfSbnR0jLYTvbS0Rek+PzChLy8vj9prVnKDqdRbRESSpwRH0mZ4JCj13tM2dal3w6ZKGutrqCgtSlOEIiKSrZTgyIK7NDDMk4c72TdNqfd1Yal3iUq9RUTkCinBkQVz/uIQe9qiHDyaWOpdvGwJO+tq2L6liqKlelmKiMjVyajfJGb2BPAcYCRsOuXulr6IZD509V2muTXKoSlKvctWLGWXRWjYVKlSbxERmTcZleCE7nD3L6c7CLl6pzv7aW6NcuzMFKXe5cXsaoiw5ZpylXqLiMi8y8QER7LYeKl3c2uUM1OWepewyyKsX1WqUm8REUmZTExwPmpmHwMc+At3fyLN8UgSRsdiHHq6h+bWaUq915axq2EVq1TqLSIiCyDTEpx3AQeBIeBVwHfMrNHdj6Q3LJnO8MgoB9u7aWmLJpR65+fn0bCxgiaLqNRbREQWVEYlOO7+s7hv7zezVwM3A3+TppBkGpcHR4JS78OdDAyNTOgrXJLP9i3VKvUWEZG0yagEZwoxQBM1MsiFS0Ps8Q4OHu1iWKXeIiKSoTLmt5CZlQO/BPwbQZn4K4FfA96Rzrgk0NV3mRaP0nZi6lLvpvoI125WqbeIiGSGjElwgELgw0ADMAq0Are4u6c1qkXuTOdFmlvPcXSKUu/q8mJ2WYSt61TqLSIimSVjEhx37wCene44JCj1Pn72As2t5zjdmVjqfU1NCbsaImxQqbeIiGSojElwJP1Gx2IcDku9uyaVegPUXrOSXRZhddWKNEQnIiKSPCU4wvDIGE8d66LFO7hwaWhCX35+HrYhKPWuLFOpt4iIZAclOIvYwOAI+4508uThTi4PJpZ6X1dbRWNdDSXLl6YpQhERkSujBGcR6r80xJ5DHRxo72J4ZJpS79oqipbp5SEiItlJv8EWke7zA7R4FD/eM2Wpd2N9DdduqqJwiUq9RUQkuynBWQTOdl1kd2uUo6f7EvqqVhazy2rYur6CApV6i4hIjlCCk6NisRgnzl5gd2uU0539Cf1rq0u4oSHChtUq9RYRkdyjBCfHjI3FOHyyl2aP0tl7OaF/89qg1HtNtUq9RUQkdynByRHDI2O0Hgt29T5/cVKpd14e9RsqaLIaqlYWpylCERGRhaMEJ8sNDI2w/0gXew91JJZ6F+SzrbaKxvoaSlXqLSIii4gSnCzVf3mYvW0d7G/vTCj1Llq6hB111ezYUq1SbxERWZT02y/L9JwfoKUtSuvxHsbGJpZ6lxQX0mQRtm2upHBJQZoiFBERST8lOFniXPclmlvP0X76PLFJa9hUlRXR1BChTqXeIiIigBKcjBaLxThx7gItHuVkNLHUe03VCnY1RNi0pkyl3iIiInGU4GSg8VLvFo/SMVWp95oymhoirK0uSUN0IiIimU8JTgYZGQ1KvZt9ulLvcposolJvERGRWSjByQAzlXovKchn2+ZKGusjlK1QqbeIiEgylOCkUf/lYfaGu3oPDY9O6Fu2tICdW2vYvqWK5UWFaYpQREQkOynBSYOeCwO0eAd+vJvRqUq96yNsq1Wpt4iIyJVSgrOAznVfotmjtJ/qSyj1riwrYpdFqFtfTkFBfpoiFBERyQ1KcFIsFotxMtrP7tYoJ6MXEvpXV63gBpV6i4iIzCslOCkyNhbjyKlgV++OnsRS742ry7ihIdjVW4mNiIjI/FKCM8/GS733tHXQ2z84oS8/L4+t68vZZRGqy1XqLSIikipKcObJ4PAoB450sedQB5cGhif0LSnI59pNlTTW17CyZFmaIhQREVk8lOBcpYthqff+aUq9r99SzY6t1Sr1FhERWUBKcK5Q74XBYFfvY1OXejfW17BtcxVLC1XqLSIistCU4MxRNCz1PjJFqXd56TJusFXUb1Cpt4iISDopwUnCeKl3s0d5+lxiqfeqyuXssgi116xURZSIiEgGyKgEx8wqgXuBFwGdwJ+7+0PpimdsLEb76T6aW6NEey4l9G9YXcoNDatYq1JvERGRjJJRCQ7weWAIWAU0Ao+Z2V53P7CQQcRiMQ4e7abFowml3nl5eWxdF5R611So1FtERCQTZUyCY2YrgJcD2929H/gPM/s28Frg3QsZy38fOMvPnzo3oU2l3iIiItkjYxIcoB4Ydfe2uLa9wPMWOpDe/qFnvl62tIDttdXsrFOpt4iISLbIpASnBOib1NYHlC50IDc2rmVF8RJKly/l2k2VKvUWERHJMpmU4PQDZZPayoDEsqUUW15UyK/uvGahn1ZERETmSSYt1tIGLDGzuri2ncCCTjAWERGR7JcxCY67XwQeAT5oZivM7FeAlwJfTW9kIiIikm0yJsEJ/RFQDESBh4G3LnSJuIiIiGS/TJqDg7t3A7ekOw4RERHJbpk2giMiIiJy1ZTgiIiISM5RgiMiIiI5J6Pm4MxRAcDZs2fTHYeIiIikQVwOkLAibzYnOGsAbr311nTHISIiIum1BjgS35DNCc7PgRuBM8BommMRERGRhVdAkNz8fHJHXiwWW/hwRERERFJIk4xFREQk5yjBERERkZyjBEdERERyjhIcERERyTlKcERERCTnKMERERGRnJPN6+DMmZlVAvcCLwI6gT9394emOC4P+BjwxrDpXuBd7h4L+xvDtmuBp4A/cPc9qT+D5MzhPN8J3AZsDI+7290/Gdd/DFjFL9YZ+om7vyilwc/RHM71TuAvgMG45h3u3h7258o1/S7B+lDjlgLu7teH/cfI0GtqZncAtwPXAw+7++0zHPsnwLuAYuBbwFvdfTDs2wR8Bfgl4ARwh7v/MJWxz1Wy52pmtwFvB+qA88BDwHvcfSTsfwJ4DjAS/sgpd7dUxj4XczjP2wle35fjmn/L3Z8I+zeRwdd0Duf5BeA1cU2FwJC7l4b9T5DZ13MZcDfwQqASOEzwevzuNMen9X26qBIc4PPAEMEHfCPwmJntdfcDk477Q+AWYCcQA34AtANfMLOlwD8BnyG40G8G/snM6tx9aGFOY1bJnmce8DpgH7AF+L6ZPe3uX4875rcz6YNkCsmeK8A33P01kxtz6Zq6+03x34cfmP866bEy9ZqeBj4MvJjgA3FKZvZi4N3AC8KfeRT4y7AN4GHgp8DN4b9vhteyI3Whz1lS5wosB94B/AyoAb4N/BnBH2Dj7nD3L6cozquV7HkC/NTdf3Wavky/pkmdp7u/BXjL+Pdmdh8wNumwTL6eS4CngecRJCU3A39vZte7+7H4AzPhfbpoblGZ2Qrg5cD73L3f3f+D4MPitVMcfhvwKXc/6e6ngE8RZOcAzye4yJ9x90F3/xxBovCCFJ9CUuZynu7+CXdvdvcRd3eCX/K/srARX7k5XtOZPJ8cuaaTfm4TwWjOV1Me5Dxw90fc/R+BrlkOvQ24190PuHsP8CHC96eZ1QO7gA+4+2V3/xbwJMH/v4yR7Lm6+z3u/mN3Hwo/ix4ki96jc7im08qGa3ol5xn3vr4/ZYHNM3e/6O53uvsxdx9z938GjgI3THF42t+niybBAeqBUXdvi2vbC1w3xbHXhX1THXcdsG/8dlVo3zSPkw5zOc9nhLflbgQmj3w8aGYdZvZ9M9s5v6Fetbme62+bWbeZHTCzt8a15+Q1JRid+7G7H53UnsnXNBlTvT9XmVlV2Nfu7hcm9WfKtbxav0bie/SjZtZpZv9pZs9PQ0zzpSk8jzYze5+Zjd9hyNVr+nKgA/j3Se1Zcz3NbBXB59NUI+Zpf58upgSnBOib1NYHlCZxbB9QEiYBc3mcdLjS+O4keD18Ja7tVmATwRydHwH/Ymbl8xLl/JjLuf49wfyaGuBNwPvN7NVX8DjpcKXxvQ64b1Jbpl/TZEz1/oTg/0emX8srZmavB54F/J+45ncBtcA1wN8C3zGzLWkI72r9O7AdiBD84n818M6wL1ev6W3AA5P+sMqa62lmhQQjive7e+sUh6T9fbqY5uD0A2WT2sqAC0kcWwb0u3vMzObyOOkw5/jCCXKvA24cnwAG4O7/GXfYR8MJjzcC35m/cK9K0ufq7gfjvv2JmX0W+F2C+8C5eE1/FVgNfDO+PQuuaTKmen9C8P8j06/lFTGzWwjm3bzQ3TvH2939Z3GH3R8m7TcDf7PAIV6V8cn+oSfN7IMECc5HycFrambrCeaxvCm+PVuup5nlE9z6HgLumOawtL9PXgTvoQAABbtJREFUF9MIThuwxMzq4tp2MvXQ2oGwb6rjDgA7wtGccTumeZx0mMt5YmZvIJj09RvufnKWx44RzE3JFHM610nizyWnrmnoNuARd++f5bEz7ZomY6r35zl37wr7as2sdFJ/plzLOTOzlwBfIpgc/uQsh2fj9ZzK5PdnTl1Tgj8ofzIpsZtKxl3P8HPyXoKCh5e7+/A0h6b9fbpoRnDc/aKZPQJ80MzeSFCJ8lLguVMc/gDwv83scYIX2J/yiwz6CYIS27eHJX/jGfjkSpW0mMt5mtmtwEeAX5/8RjOzDcB6gi3o84G3AdXAf05+nHSZ47m+lGAYvBd4NkHp7XvC7ifIkWsKYGbFwCuAl01qz+hrGs65WAIUAAVmVgSMjJdEx3kAuM/MHgTOAO8lvBXn7m1mtgf4gJm9F7iJIFnNmAmpkPy5mtkLCG4D/I67//ekvnKCEtt/IygrfiXBHJ13pP4MkjOH87wJaHb3c2bWALwP+AfIjms6h9fuuNcBH5/0GBl/PUP3ENzuf6G7X57huLS/TxfTCA7AHxGU8EUJbk281d0PmNmN4a2ncV8kGLJ/EtgPPBa2EZYN30LwAu0F3gDckkHlxJD8eX4YqAJ+bmb94b8vhH2lBC/kHuAU8BLgpjD7ziTJnuurCNZsuEDwxvu4u98POXdNITiXPoI5NvEy/Zq+l2AdlHcTrBVyGXivmW0IX5sbANz9e8AnCM7vePjvA3GP8yqCuSo9BLd1fjeDyonHJXWuBL/oVwKPx71Hx9ccKSR4D3cQrI30NoLXrS/kicwi2fP8DWCfmV0EHgceIfjja1ymX9NkzxMz+2VgHWECFyfjr6eZbSRYRqMROBv3mrw1E9+nebFYbPajRERERLLIYhvBERERkUVACY6IiIjkHCU4IiIiknOU4IiIiEjOUYIjIiIiOUcJjoiIiOScRbPQn4hkDjO7D8Ddb1/g5y0FngKeM9vK3Wb2LmCTu791puNEJDMpwRGReWVmsy2utRn444WIZQp/DHw/iW1JAO4GjprZx939WGrDEpH5pltUIjLf1sT9+xTw00ltT7t7n7tP3k04pcINAt9MsEngrNz9AsGqum9IZVwikhoawRGReeXuZ8e/DreRGIpvC9vvC4+9Pfz+GMF+b88DXkhwG+n3CEZ77iLYGf0+4E/cPRb+TA3wGeA3Cfbu+R7wdnfvnia05wAVBHuSjcdRSzBSM76v11PAre5+OPz+ceD94T8RySIawRGRTPFnBBtL7gIuhV+/k2DPmlcBbwFujjv+mwSbpN4IPJ8gebl/hsd/LrDX3Ufj2u4i2Pfn2QT74nwOGIvr3w1ca2ZVV3pSIpIeGsERkUzxDXf/BoCZ3QV8HWh0971h248Idld+zMx+DdgKvGA8YTGzNwGnzGz15BGj0AaCXY3jrQceitvQsG1S/9m44zJlU1IRSYISHBHJFAfivo6G/z04qa0m/Pp6gttWfWY2+XFq+UViEq8IGJzUdjdwr5m9Fvg+QZIVPwF5IPxvcTInICKZQ7eoRCRTDMd9HQNw98lt459ZJUAr0DjpXx3QMs3jdwHl8Q3ufg9wLcFcm5uAVjN7btwhFeF/O+d4LiKSZhrBEZFstBfYBPS6e7LJxz6CicsTuHs78Fngs2b2OMF8n5+E3Q3ABaD9agMWkYWlBEdEstH3CW5pPWJm7wFOE8zJeZm7v2Wan3kC2Bg/R8fMPg08BhwmmGezg2A0Z9xzgScmTUwWkSygW1QiknXcfQx4CXAIeJQg2fkc0DPDz5whSIxeEddcCPwtwe2uh4GHgHvi+l/BzJVZIpKh8mKx2RYdFRHJDWZ2I/BF4Lrx9XRmOPaXga+Ex2oERyTLaARHRBYNd/8x8HmCFZVnUwH8gZIbkeykERwRERHJORrBERERkZyjBEdERERyjhIcERERyTlKcERERCTnKMERERGRnKMER0RERHLO/wfCi0rbxgeD0QAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "subplot(2, 1, 1)\n", - "plot_position(results)\n", - "\n", - "subplot(2, 1, 2)\n", - "plot_velocity(results)\n", - "\n", - "savefig('figs/chap11-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "According to this model, we should be able to make this run in just over 2 seconds." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "2.010128295248225 second" - ], - "text/latex": [ - "$2.010128295248225 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_final = get_last_label(results) * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "At the end of the run, the car has gone about 28 meters." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
x28.38597981005175 meter
v27.77777777777778 meter / second
\n", - "
" - ], - "text/plain": [ - "x 28.38597981005175 meter\n", - "v 27.77777777777778 meter / second\n", - "dtype: object" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x, v = get_last_value(results)\n", - "state = State(x=x*m, v=v*m/s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we send the final state back to the slope function, we can see that the final acceleration is about 13 $m/s^2$, which is about 1.3 times the acceleration of gravity." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "100.0 kilometer/hour" - ], - "text/latex": [ - "$100.0 \\frac{kilometer}{hour}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v, a = slope_func(state, 0, system)\n", - "v.to(km/hour)" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "12.918946212080874 newton/kilogram" - ], - "text/latex": [ - "$12.918946212080874 \\frac{newton}{kilogram}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "1.3182598175592728 dimensionless" - ], - "text/latex": [ - "$1.3182598175592728 dimensionless$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "g = 9.8 * m/s**2\n", - "(a / g).to(UNITS.dimensionless)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It's not easy for a vehicle to accelerate faster than `g`, because that implies a coefficient of friction between the wheels and the road surface that's greater than 1. But racing tires on dry asphalt can do that; the OEM team at Olin has tested their tires and found a peak coefficient near 1.5.\n", - "\n", - "So it's possible that our no slip assumption is valid, but only under ideal conditions, where weight is distributed equally on four tires, and all tires are driving." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** How much time do we lose because maximum torque decreases as motor speed increases? Run the model again with no drop off in torque and see how much time it saves." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Drag" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this section we'll see how much effect drag has on the results.\n", - "\n", - "Here's a function to compute drag force, as we saw in Chapter 21." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "def drag_force(v, system):\n", - " \"\"\"Computes drag force in the opposite direction of `v`.\n", - " \n", - " v: velocity\n", - " system: System object\n", - " \n", - " returns: drag force\n", - " \"\"\"\n", - " unpack(system)\n", - " f_drag = -np.sign(v) * rho * v**2 * C_d * area / 2\n", - " return f_drag" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can test it with a velocity of 20 m/s." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "-72.0 kilogram meter/second2" - ], - "text/latex": [ - "$-72.0 \\frac{kilogram \\cdot meter}{second^{2}}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "drag_force(20 * m/s, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the resulting acceleration of the vehicle due to drag.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "-0.24 meter/second2" - ], - "text/latex": [ - "$-0.24 \\frac{meter}{second^{2}}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "drag_force(20 * m/s, system) / system.mass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see that the effect of drag is not huge, compared to the acceleration we computed in the previous section, but it is not negligible.\n", - "\n", - "Here's a modified slope function that takes drag into account." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "def slope_func2(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object \n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " x, v = state\n", - " unpack(system)\n", - " \n", - " omega2 = v / r_wheel * radian\n", - " omega1 = omega2 / speed_ratio\n", - " tau1 = compute_torque(omega1, system)\n", - " tau2 = tau1 / speed_ratio\n", - " F = tau2 / r_wheel\n", - " a_motor = F / mass\n", - " a_drag = drag_force(v, system) / mass\n", - " \n", - " a = a_motor + a_drag\n", - " return v, a " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the next run." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[[2.0339198094626973]]
nfev38
njev0
nlu0
status1
messageA termination event occurred.
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events [[2.0339198094626973]]\n", - "nfev 38\n", - "njev 0\n", - "nlu 0\n", - "status 1\n", - "message A termination event occurred.\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results2, details = run_ode_solver(system, slope_func2, events=event_func)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The time to reach 100 kph is a bit higher." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "2.0339198094626973 second" - ], - "text/latex": [ - "$2.0339198094626973 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_final2 = get_last_label(results2) * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But the total effect of drag is only about 2/100 seconds." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.023791514214472453 second" - ], - "text/latex": [ - "$0.023791514214472453 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_final2 - t_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "That's not huge, which suggests we might not be able to save much time by decreasing the frontal area, or coefficient of drag, of the car." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Rolling resistance" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next we'll consider [rolling resistance](https://en.wikipedia.org/wiki/Rolling_resistance), which the force that resists the motion of the car as it rolls on tires. The cofficient of rolling resistance, `C_rr`, is the ratio of rolling resistance to the normal force between the car and the ground (in that way it is similar to a coefficient of friction).\n", - "\n", - "The following function computes rolling resistance." - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "def rolling_resistance(system):\n", - " \"\"\"Computes force due to rolling resistance.\n", - " \n", - " system: System object\n", - " \n", - " returns: force\n", - " \"\"\"\n", - " unpack(system)\n", - " return -C_rr * mass * N/kg" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The acceleration due to rolling resistance is 0.2 (it is not a coincidence that it equals `C_rr`)." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "-60.0 newton" - ], - "text/latex": [ - "$-60.0 newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "rolling_resistance(system)" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "-0.2 newton/kilogram" - ], - "text/latex": [ - "$-0.2 \\frac{newton}{kilogram}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "rolling_resistance(system) / system.mass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a modified slope function that includes drag and rolling resistance." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "def slope_func3(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object \n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " x, v = state\n", - " unpack(system)\n", - " \n", - " omega2 = v / r_wheel * radian\n", - " omega1 = omega2 / speed_ratio\n", - " tau1 = compute_torque(omega1, system)\n", - " tau2 = tau1 / speed_ratio\n", - " F = tau2 / r_wheel\n", - " a_motor = F / mass\n", - " a_drag = drag_force(v, system) / mass\n", - " a_roll = rolling_resistance(system) / mass\n", - " \n", - " a = a_motor + a_drag + a_roll\n", - " return v, a " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the run." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[[2.0640384588032687]]
nfev38
njev0
nlu0
status1
messageA termination event occurred.
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events [[2.0640384588032687]]\n", - "nfev 38\n", - "njev 0\n", - "nlu 0\n", - "status 1\n", - "message A termination event occurred.\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results3, details = run_ode_solver(system, slope_func3, events=event_func)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The final time is a little higher, but the total cost of rolling resistance is only 3/100 seconds." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "2.0640384588032687 second" - ], - "text/latex": [ - "$2.0640384588032687 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_final3 = get_last_label(results3) * s" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.030118649340571402 second" - ], - "text/latex": [ - "$0.030118649340571402 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_final3 - t_final2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "So, again, there is probably not much to be gained by decreasing rolling resistance.\n", - "\n", - "In fact, it is hard to decrease rolling resistance without also decreasing traction, so that might not help at all." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Optimal gear ratio" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The gear ratio 13:60 is intended to maximize the acceleration of the car without causing the tires to slip. In this section, we'll consider other gear ratios and estimate their effects on acceleration and time to reach 100 kph.\n", - "\n", - "Here's a function that takes a speed ratio as a parameter and returns time to reach 100 kph." - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [], - "source": [ - "def time_to_speed(speed_ratio, params):\n", - " \"\"\"Computes times to reach 100 kph.\n", - " \n", - " speed_ratio: ratio of wheel speed to motor speed\n", - " params: Params object\n", - " \n", - " returns: time to reach 100 kph, in seconds\n", - " \"\"\"\n", - " params = Params(params, speed_ratio=speed_ratio)\n", - " system = make_system(params)\n", - " results, details = run_ode_solver(system, slope_func3, events=event_func)\n", - " t_final = get_last_label(results) * s\n", - " a_initial = slope_func(system.init, 0, system)\n", - " return t_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can test it with the default ratio:" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "2.0640384588032687 second" - ], - "text/latex": [ - "$2.0640384588032687 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "time_to_speed(13/60, params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can try it with different numbers of teeth on the motor gear (assuming that the axle gear has 60 teeth):" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "8.0 1.3224382966899866 second\n", - "9.0 1.4657155218053697 second\n", - "10.0 1.6153479581562389 second\n", - "11.0 1.7650168438034508 second\n", - "12.0 1.9144943634203977 second\n", - "13.0 2.0640384588032687 second\n", - "14.0 2.216930792405196 second\n", - "15.0 2.3701147392940345 second\n", - "16.0 2.5248555545397595 second\n", - "17.0 2.679328836624321 second\n" - ] - } - ], - "source": [ - "for teeth in linrange(8, 18):\n", - " print(teeth, time_to_speed(teeth/60, params))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Wow! The speed ratio has a big effect on the results. At first glance, it looks like we could break the world record (1.513 seconds) just by decreasing the number of teeth.\n", - "\n", - "But before we try it, let's see what effect that has on peak acceleration." - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [], - "source": [ - "def initial_acceleration(speed_ratio, params):\n", - " \"\"\"Maximum acceleration as a function of speed ratio.\n", - " \n", - " speed_ratio: ratio of wheel speed to motor speed\n", - " params: Params object\n", - " \n", - " returns: peak acceleration, in m/s^2\n", - " \"\"\"\n", - " params = Params(params, speed_ratio=speed_ratio)\n", - " system = make_system(params)\n", - " a_initial = slope_func(system.init, 0, system)[1] * m/s**2\n", - " return a_initial" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the results:" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "8.0 23.076923076923077 meter * newton / kilogram / second ** 2\n", - "9.0 20.51282051282051 meter * newton / kilogram / second ** 2\n", - "10.0 18.46153846153846 meter * newton / kilogram / second ** 2\n", - "11.0 16.783216783216787 meter * newton / kilogram / second ** 2\n", - "12.0 15.384615384615385 meter * newton / kilogram / second ** 2\n", - "13.0 14.201183431952662 meter * newton / kilogram / second ** 2\n", - "14.0 13.186813186813184 meter * newton / kilogram / second ** 2\n", - "15.0 12.307692307692308 meter * newton / kilogram / second ** 2\n", - "16.0 11.538461538461538 meter * newton / kilogram / second ** 2\n", - "17.0 10.85972850678733 meter * newton / kilogram / second ** 2\n" - ] - } - ], - "source": [ - "for teeth in linrange(8, 18):\n", - " print(teeth, initial_acceleration(teeth/60, params))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As we decrease the speed ratio, the peak acceleration increases. With 8 teeth on the motor gear, we could break the world record, but only if we can accelerate at 2.3 times the acceleration of gravity, which is impossible without very sticky ties and a vehicle that generates a lot of downforce." - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2.354081632653061" - ] - }, - "execution_count": 48, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "23.07 / 9.8" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "These results suggest that the most promising way to improve the performance of the car (for this event) would be to improve traction." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/old_notebooks/chap11soln.ipynb b/code/soln/old_notebooks/chap11soln.ipynb deleted file mode 100644 index 3386111c0..000000000 --- a/code/soln/old_notebooks/chap11soln.ipynb +++ /dev/null @@ -1,1971 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 11: Rotation\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# If you want the figures to appear in the notebook, \n", - "# and you want to interact with them, use\n", - "# %matplotlib notebook\n", - "\n", - "# If you want the figures to appear in the notebook, \n", - "# and you don't want to interact with them, use\n", - "# %matplotlib inline\n", - "\n", - "# If you want the figures to appear in separate windows, use\n", - "# %matplotlib qt5\n", - "\n", - "# tempo switch from one to another, you have to select Kernel->Restart\n", - "\n", - "%matplotlib inline\n", - "\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Rolling paper\n", - "\n", - "We'll start by loading the units we need." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "radian = UNITS.radian\n", - "m = UNITS.meter\n", - "s = UNITS.second" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And creating a `Condition` object with the system parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "condition = Condition(Rmin = 0.02 * m,\n", - " Rmax = 0.055 * m,\n", - " L = 47 * m,\n", - " duration = 130 * s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function estimates the parameter `k`, which is the increase in the radius of the roll for each radian of rotation. " - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def estimate_k(condition):\n", - " \"\"\"Estimates the parameter `k`.\n", - " \n", - " condition: Condition with Rmin, Rmax, and L\n", - " \n", - " returns: k in meters per radian\n", - " \"\"\"\n", - " unpack(condition)\n", - " \n", - " Ravg = (Rmax + Rmin) / 2\n", - " Cavg = 2 * pi * Ravg\n", - " revs = L / Cavg\n", - " rads = 2 * pi * revs\n", - " k = (Rmax - Rmin) / rads\n", - " return k" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As usual, `make_system` takes a `Condition` object and returns a `System` object." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(condition):\n", - " \"\"\"Make a system object.\n", - " \n", - " condition: Condition with Rmin, Rmax, and L\n", - " \n", - " returns: System with init, k, and ts\n", - " \"\"\"\n", - " unpack(condition)\n", - " \n", - " init = State(theta = 0 * radian,\n", - " y = 0 * m,\n", - " r = Rmin)\n", - " \n", - " k = estimate_k(condition)\n", - " ts = linspace(0, duration, 101)\n", - " \n", - " return System(init=init, k=k, ts=ts)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `make_system`" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
inittheta 0 radian\n", - "y 0 meter\n", - "r ...
k2.7925531914893616e-05 meter
ts[0.0 second, 1.3 second, 2.6 second, 3.9000000...
\n", - "
" - ], - "text/plain": [ - "init theta 0 radian\n", - "y 0 meter\n", - "r ...\n", - "k 2.7925531914893616e-05 meter\n", - "ts [0.0 second, 1.3 second, 2.6 second, 3.9000000...\n", - "dtype: object" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(condition)\n", - "system" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
theta0 radian
y0 meter
r0.02 meter
\n", - "
" - ], - "text/plain": [ - "theta 0 radian\n", - "y 0 meter\n", - "r 0.02 meter\n", - "dtype: object" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.init" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can write a slope function based on the differential equations\n", - "\n", - "$\\omega = \\frac{d\\theta}{dt} = 10$\n", - "\n", - "$\\frac{dy}{dt} = r \\frac{d\\theta}{dt}$\n", - "\n", - "$\\frac{dr}{dt} = k \\frac{d\\theta}{dt}$\n" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object with theta, y, r\n", - " t: time\n", - " system: System object with r, k\n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " theta, y, r = state\n", - " unpack(system)\n", - " \n", - " omega = 10 * radian / s\n", - " dydt = r * omega\n", - " drdt = k * omega\n", - " \n", - " return omega, dydt, drdt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `slope_func`" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " ,\n", - " )" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(system.init, 0*s, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "run_odeint(system, slope_func)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And look at the results." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
thetayr
124.81248.046.7070640.054851
126.11261.047.4224870.055214
127.41274.048.1426300.055577
128.71287.048.8674930.055940
130.01300.049.5970740.056303
\n", - "
" - ], - "text/plain": [ - " theta y r\n", - "124.8 1248.0 46.707064 0.054851\n", - "126.1 1261.0 47.422487 0.055214\n", - "127.4 1274.0 48.142630 0.055577\n", - "128.7 1287.0 48.867493 0.055940\n", - "130.0 1300.0 49.597074 0.056303" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Extracting one time series per variable (and converting `r` to radians):" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "thetas = system.results.theta\n", - "ys = system.results.y\n", - "rs = system.results.r * 1000" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `theta`" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\niIiIyMV/ADzWqGdBjP47AAAAAElFTkSuQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(thetas, label='theta')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Angle (rad)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `y`" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(ys, color='green', label='y')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Length (m)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `r`" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\nTMz/A7U6MNoeEemZAAAAAElFTkSuQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(rs, color='red', label='r')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Radius (mm)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also see the relationship between `y` and `r`, which I derive analytically in the book." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\nRU5ErkJggg==\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(rs, ys, color='purple')\n", - "\n", - "decorate(xlabel='Radius (mm)',\n", - " ylabel='Length (m)',\n", - " legend=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the figure from the book." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file chap11-fig01.pdf\n" - ] - }, - { - "data": { - "image/png": "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\nYII=\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "subplot(3, 1, 1)\n", - "plot(thetas, label='theta')\n", - "decorate(ylabel='Angle (rad)')\n", - "\n", - "subplot(3, 1, 2)\n", - "plot(ys, color='green', label='y')\n", - "decorate(ylabel='Length (m)')\n", - "\n", - "subplot(3, 1, 3)\n", - "plot(rs, color='red', label='r')\n", - "\n", - "decorate(xlabel='Time(s)',\n", - " ylabel='Radius (mm)')\n", - "\n", - "savefig('chap11-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use interpolation to find the time when `y` is 47 meters." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array(125.33333334940457)" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "T = interp_inverse(ys, kind='cubic')\n", - "t_end = T(47)\n", - "t_end" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "At that point `r` is 55 mm, which is `Rmax`, as expected." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array(55.00000000448797)" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "R = interpolate(rs, kind='cubic')\n", - "R(t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The total amount of rotation is 1253 rad." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array(1253.3333334940455)" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "THETA = interpolate(thetas, kind='cubic')\n", - "THETA(t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Unrolling" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For unrolling the paper, we need more units:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "kg = UNITS.kilogram\n", - "N = UNITS.newton" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And a few more parameters in the `Condition` object." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "condition = Condition(Rmin = 0.02 * m,\n", - " Rmax = 0.055 * m,\n", - " Mcore = 15e-3 * kg,\n", - " Mroll = 215e-3 * kg,\n", - " L = 47 * m,\n", - " tension = 2e-4 * N,\n", - " duration = 180 * s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`make_system` computes `rho_h`, which we'll need to compute moment of inertia, and `k`, which we'll use to compute `r`." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(condition):\n", - " \"\"\"Make a system object.\n", - " \n", - " condition: Condition with Rmin, Rmax, Mcore, Mroll,\n", - " L, tension, and duration\n", - " \n", - " returns: System with init, k, rho_h, Rmin, Rmax,\n", - " Mcore, Mroll, ts\n", - " \"\"\"\n", - " unpack(condition)\n", - " \n", - " init = State(theta = 0 * radian,\n", - " omega = 0 * radian/s,\n", - " y = L)\n", - " \n", - " area = pi * (Rmax**2 - Rmin**2)\n", - " rho_h = Mroll / area\n", - " k = (Rmax**2 - Rmin**2) / 2 / L / radian \n", - " ts = linspace(0, duration, 101)\n", - " \n", - " return System(init=init, k=k, rho_h=rho_h,\n", - " Rmin=Rmin, Rmax=Rmax,\n", - " Mcore=Mcore, Mroll=Mroll, \n", - " ts=ts)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `make_system`" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
inittheta 0 radian\n", - "omega 0.0 radi...
k2.7925531914893616e-05 meter / radian
rho_h26.07109543981524 kilogram / meter ** 2
Rmin0.02 meter
Rmax0.055 meter
Mcore0.015 kilogram
Mroll0.215 kilogram
ts[0.0 second, 1.8 second, 3.6 second, 5.4 secon...
\n", - "
" - ], - "text/plain": [ - "init theta 0 radian\n", - "omega 0.0 radi...\n", - "k 2.7925531914893616e-05 meter / radian\n", - "rho_h 26.07109543981524 kilogram / meter ** 2\n", - "Rmin 0.02 meter\n", - "Rmax 0.055 meter\n", - "Mcore 0.015 kilogram\n", - "Mroll 0.215 kilogram\n", - "ts [0.0 second, 1.8 second, 3.6 second, 5.4 secon...\n", - "dtype: object" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(condition)\n", - "system" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
theta0 radian
omega0.0 radian / second
y47 meter
\n", - "
" - ], - "text/plain": [ - "theta 0 radian\n", - "omega 0.0 radian / second\n", - "y 47 meter\n", - "dtype: object" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.init" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we compute `I` as a function of `r`:" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def moment_of_inertia(r, system):\n", - " \"\"\"Moment of inertia for a roll of toilet paper.\n", - " \n", - " r: current radius of roll in meters\n", - " system: System object with Mcore, rho, Rmin, Rmax\n", - " \n", - " returns: moment of inertia in kg m**2\n", - " \"\"\"\n", - " unpack(system)\n", - " Icore = Mcore * Rmin**2 \n", - " Iroll = pi * rho_h / 2 * (r**4 - Rmin**4)\n", - " return Icore + Iroll" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When `r` is `Rmin`, `I` is small." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "6e-06 kilogram meter2" - ], - "text/latex": [ - "$6e-06 kilogram \\cdot meter^{2}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "moment_of_inertia(system.Rmin, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As `r` increases, so does `I`." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.00037418750000000006 kilogram meter2" - ], - "text/latex": [ - "$0.00037418750000000006 kilogram \\cdot meter^{2}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "moment_of_inertia(system.Rmax, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the slope function." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object with theta, omega, y\n", - " t: time\n", - " system: System object with Rmin, k, Mcore, rho_h, tension\n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " theta, omega, y = state\n", - " unpack(system)\n", - " \n", - " r = sqrt(2*k*y + Rmin**2)\n", - " I = moment_of_inertia(r, system)\n", - " tau = r * tension\n", - " alpha = tau / I\n", - " dydt = -r * omega\n", - " \n", - " return omega, alpha, dydt " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `slope_func`" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " ,\n", - " )" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(system.init, 0*s, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "run_odeint(system, slope_func)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And look at the results." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
thetaomegay
172.8503.3769986.83022222.852269
174.6515.7882196.96058022.346268
176.4528.4371507.09437621.835001
178.2541.3301517.23180221.318468
180.0554.4739407.37306620.796665
\n", - "
" - ], - "text/plain": [ - " theta omega y\n", - "172.8 503.376998 6.830222 22.852269\n", - "174.6 515.788219 6.960580 22.346268\n", - "176.4 528.437150 7.094376 21.835001\n", - "178.2 541.330151 7.231802 21.318468\n", - "180.0 554.473940 7.373066 20.796665" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Extrating the time series" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "thetas = system.results.theta\n", - "omegas = system.results.omega\n", - "ys = system.results.y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `theta`" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n8e2336K1tRVJSUlYvnw5pk+fjg0bNuDNN9+EVCq1+5qHHnoIv/vd75xUcd9lZWWhuroaYrH937S5\nubmIj4/Hrl27sHnzZpSWlkKpVCInJwfLli1zy+GfR44cweLFizu1m81m/OIXv0B0dLTbv7ZqtRrP\nPvssDh8+jP379yMmJsa273qvpVqtxrp161BUVARBEDB+/Hg899xziI2NddblXFdP17t9+3Zs374d\nV65cgVwuxy9+8Qs8/vjjEIvFuHz5MmbPng2pVGq3kJhSqcSBAweccSnX1d21OvI7aTi9tnPnzkVl\nZaXdsYIgwGQy4dy5c4P72goebtWqVcKdd94pFBcXCwaDQfj73/8ujB07Vrh06ZKzSxsQ99xzj7B4\n8WKhpqZGMBgMwquvvipMmDBBqKqqEt544w3h/vvvd3aJA+aWW24RPv744y73HTp0SBgzZoywe/du\noa2tTTh79qwwa9YsYcOGDUNc5eCpqakRpkyZIhw6dMjtX9u9e/cK06dPF1asWCEkJycLarXatu96\nr6XRaBTmzp0r/P73vxfq6+uFhoYGYdWqVUJ2drZgNBqddUk96ul6//73vwuTJk0SDh06JJjNZuHo\n0aNCenq68N577wmCIAhqtbrT17iynq71eu/b4fbadmX58uXCqlWrBEEY3NfWo2/7NzQ0YOfOnXji\niScQHx8Pb29vLFiwAImJidixY4ezy+u3pqYmJCYm4tlnn4VSqYS3tzeWLFmC1tZWFBUVObu8IbVt\n2zbMnDkTOTk5kMlkSElJwYMPPoitW7fCarU6u7wB8cc//hE5OTmYMmWKs0vpN51Oh+3bt+Ouu+7q\ntO96r2V+fj7KysqwevVqhIaGIigoCCtXroRarUZeXp4Trub6erpeo9GI3//+95gyZQokEgkmTZqE\nadOm4eDBg06otP96utbrGW6v7X/at28fjhw5gtWrVw96XR4d/qdPn4bJZMK4cePs2tPS0nDixAkn\nVTVwAgMD8eKLLyIxMdHWplarAQCRkZEA2ud+XrRoEaZOnYqsrCy8/PLLMBgMTql3IHzxxRe47bbb\nMGnSJNx9993Yt28fAKCwsBBpaWl2x6alpUGn06G0tNQJlQ6sAwcO4NixY3jmmWdsbe782s6bNw/x\n8fFd7rvea1lYWAiVSgW5XG7bHxISgtjYWJf9ue7pen/zm99g/vz5tm1BEFBRUYGoqCi749avX49b\n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\nce3jAAAAAElFTkSuQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(thetas, label='theta')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Angle (rad)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `omega`" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(omegas, color='orange', label='omega')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Angular velocity (rad/s)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plotting `y`" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(ys, color='green', label='y')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Length (m)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the figure from the book." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file chap11-fig02.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n5ncrRpMFo9mKyWTFaLbYfv+tzWiyUGeyUGe0fTearBd0T/vF8PZSE+Cjwd/37FeAr4YAPy/bd18N\n3l5quV1OCCHaiDYX/uXl5Xz11Ve89tprdO3aFYCxY8eyePFiFi9ezMyZMy94m1arwonT5ZRWGOCc\nHLUqCopiC2uF374rtvUVRcGqKFitCharbV2rxYpFUbBYFCxWBYvVitWiYLJYsVgUzFYrZrO1xY+6\nL4an2gM/H098vT3x8/bE10eDn4/nb18a/H/73d9Xg6d0qhNCiHalzYX//v37MZlM9O3b1669X79+\n7N69+6K2eehUKeu361qiPLfQeHrgrVHj6+2Jt5cn3l5qfLzU+Hh54uutxsfb87efz3yp0XjKLHNC\nCNFRtbnwLy0tBSAkJMSuPTQ0lJKSkovappPPmjdKo/bA09MDT7Xty0vjgcbT47d2NV4aD7w0ajSe\nHnh52n720qjxPue7t5ftZ7VcSxdCCHEB2lz4N+dirzlf1iUML40HZRV152zM9s3jt216qFSgArVK\nhcrD9rtKpULtocLjty+1hwoPle1nT7UHag8VavVvP6s98PRQ2b6rVXJ9XAghhNu0ufAPDw8HQK/X\nExUVVd9eVlZGREREo4+xWGxTtubn5ze5XV8V+AZfYnFnbnW3gMUEjk0UK4QQQrS8M5l3JgPP1ebC\nPzk5GS8vL3bt2sV1111X3/7rr78ycuTIRh9TVFQEwPjx411SoxBCCNFaFBUV0blzZ7u2Nhf+gYGB\n3HbbbbzxxhskJSURHR3NZ599Rm5uLmPHjm30McnJySxatAitVotaLR3dhBBCtH8Wi4WioiKSk5Mb\nLFMpzr5J3AmMRiMvvfQSX3/9NdXV1fTq1Ytp06YxcOBAd5cmhBBCtHptMvyFEEIIcfFk9BYhhBCi\ng5HwF0IIIToYCX8hhBCig5HwF0IIITqYNnerX0tr79MDl5SU8Morr/DTTz9RU1ND9+7dmTp1KsOG\nDeONN97grbfeQqPR2D3mvvvu469//aubKr54aWlpFBQU4OFh/5l25cqVdO3alVWrVvH++++TlZWF\nVqtl9OjRTJkypU3e/rlt2zYmTpzYoN1sNnPLLbcQGxvb5vetTqdj5syZbN26lXXr1hEXF1e/7Hz7\nUqfTkZmZyZ49e1AUhf79+zNr1izi4+Pd9XLOq7nXu2jRIhYtWkReXh6hoaHccsstTJ48GQ8PD3Jy\nckhPT0ej0diNHKrValm/fr07Xsp5NfVaHfk/qT3t2+uuu47Tp0/brasoCiaTicOHDzt33yodXEZG\nhvKnP/1JOXHihGIwGJTPP/9cSU5OVo4fP+7u0lrE7bffrkycOFEpLCxUDAaD8sorrygDBgxQ8vPz\nlddff12588473V1iixk5cqSydOnSRpdt2bJF6dOnj7J69Wqlrq5OOXTokHL11Vcrb7zxhourdJ7C\nwkJl8ODBypYtW9r8vv3222+VYcOGKdOmTVOSkpIUnU5Xv+x8+9JoNCrXXXed8uSTTyolJSVKeXm5\nkpGRoVx77bWK0Wh010tqVnOv9/PPP1cGDhyobNmyRTGbzcr27duVlJQU5cMPP1QURVF0Ol2Dx7Rm\nzb3W871v29u+bczUqVOVjIwMRVGcu2879Gn/M9MDP/roo3Tt2hVvb2/Gjh1LYmIiixcvdnd5l6yy\nspLExERmzpyJVqvF29ubBx54gJqaGvbs2ePu8lzq008/ZcSIEYwePRovLy969uzJPffcwyeffILV\naj3/BtqAZ555htGjRzN48GB3l3LJ9Ho9ixYt4uabb26w7Hz7cuPGjZw6dYoZM2YQFhZGUFAQ06dP\nR6fTsWHDBje8mvNr7vUajUaefPJJBg8ejFqtZuDAgQwdOpTNmze7odJL19xrPZ/2tm9/7/vvv2fb\ntm3MmDHD6XV16PB3xvTArUlgYCDPP/88iYmJ9W06nW3q4ujoaMA29vO9997LkCFDSEtL48UXX8Rg\nMLil3pawZs0arr/+egYOHMitt97K999/D8CuXbvo16+f3br9+vVDr9eTlZXlhkpb1vr16/n11195\n4okn6tva8r4dM2YMXbt2bXTZ+fblrl27SEhIIDQ0tH55SEgI8fHxrfbvurnXe9ddd3HHHXfU/64o\nCrm5ucTExNitN3/+fEaOHMmQIUO47777OHr0qFNrvljNvVZo/n3b3vbtuQwGA3PmzGH69OkEBQXZ\nLXPGvu3Q4e+M6YFbs6qqKmbMmEF6ejp9+/YlMjKShIQEHn/8cTZu3MiLL77IV199xbx589xd6kVJ\nSkqiW7dufPrpp2zYsIFrrrmGyZMns2vXLkpLSwkOtp+56cx/IGfeB22V1Wpl/vz5PPjggwQEBAC0\nu317rvPty7KysgbLz6zTHv6u33rrLU6fPl3f58PLy4vk5GSGDBnCmjVrWLlyJT4+Ptx7771UVla6\nudoLc773bXvetx9//DEhISHccMMN9W3O3LcdOvyb096m3M3NzeUvf/kL4eHhvPLKKwDccccdvP/+\n+/Tt2xeNRsMVV1zBgw8+yLJlyzCbzW6u+MItWLCg/nRgQEAADz/8ML169eI///mPu0tzqm+//ZaC\nggK7iava275tKW3579pisZCZmcknn3zCwoUL6zuNRUZGsnTpUu644w58fHyIiori+eefp6SkhHXr\n1rm56gtzKe/btrxvjUYj77//Pg899JDd63Dmvu3Q4X/u9MDnam564LZoz549jBkzhoEDB7Jw4UL8\n/PyaXLdz584YjUbKyspcWKHzJCQkUFBQQERERKP7GWw9Z9uylStXkpaWhre3d7PrtZd9e759GR4e\n3mD5mXXa6t+1wWDg4Ycf5ueff+aLL74gJSWl2fWDg4MJCQmhsLDQRRU6z7nv2/a4bwF+/PFHDAZD\nkzPTnqul9m2HDv9zpwc+16+//sqgQYPcVFXLOnLkCA888AAPPvggs2fPtruF5p133uF///uf3frH\njx/Hz8+vzf0h6XQ6nn32WSoqKuzaT5w4QefOnUlJSWlwTXDHjh1otVoSEhJcWWqLqqqq4scff2TU\nqFF27e1p3/7e+fZlSkoKOp3O7jRwcXEx2dnZbfLv2mKxMHnyZGpra/niiy/o0qWL3fJffvmFf/7z\nn3ZtZy5/tLX39vnet+1t356xZs0aUlNTGxyYOXPfdujwP3d64JMnT1JbW8v777/f7PTAbYnFYiEj\nI4MxY8Zwzz33NFiu1+t5+umn2bt3L2azmW3btvGvf/2Le++9t82dQouIiGDdunU8++yzlJWVUVNT\nw5tvvsnJkye58847ufvuu9m4cSOrV6/GaDSyd+9ePvjggzb5Ws918OBBTCYTvXr1smtvT/v29863\nL4cPH0737t3JzMykrKyM0tJS5s6dS1JSEqmpqe4u/4J98sknnDp1igULFhAYGNhgeVBQEAsXLuTD\nDz+krq6OoqIiZs2aRefOnUlLS3NDxRfvfO/b9rZvz9i1axe9e/du0O7MfdvhZ/Vrz9MDb9++nfHj\nxzcYIALg5ptv5umnn+att95i1apVFBYWotVq64OyLQ58c/z4cV5++WV27dpFbW0tvXv3Zvr06QwY\nMACwXRt//fXXycrKIiIigrFjxza4xtbWfP311zz++OPs2rULX1/f+naj0dim9+2ZwU+U3wY8OfMe\n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\n61i1ahW2trY8/PDDFBQUtHC01+ZKv7ft+dx+8cUXuLq6ctttt5m2Nee57dDJvzGXd65oD1JSUrj/\n/vvx8PBg0aJFAEyePJmlS5fSu3dvrKysGDhwII8//jgrVqygsrKyhSO+dh9//LGpOdDR0ZHp06cT\nGhrKt99+29KhNauff/6Z9PT0WgtXtbdzay5t+e/aaDQyf/58vvzyS5YsWWLqNObt7c3y5cuZPHky\ntra2+Pj4sGDBArKzs9m0aVMLR31tmvJ725bPbXl5OUuXLuXPf/5zrc/RnOe2Qyd/Dw8PQH0mc7nc\n3Fw8PT1bIqRmceTIEWJiYujfvz9LlizB3t6+wbpdunShvLyc3NxcDSNsPoGBgaSnp+Pp6VnveQa1\n52xbtmrVKkaPHo2NjU2j9drLub3SufTw8KhTXlOnrf5dl5aWMn36dHbu3Mk333xDVFRUo/VdXFxw\ndXUlIyNDowibz+W/t+3x3AJs27aN0tLSBpelv5y5zm2HTv4RERFYW1sTGxtba/vBgwcZMGBAC0Vl\nXgkJCTz22GM8/vjj/P3vf681hOajjz5iy5YtteonJiZib2/f5v6QkpKSePXVV8nPz6+1/cyZM3Tp\n0oWoqKg6zwQPHDiAl5cXgYGBWoZqVoWFhWzbto2xY8fW2t6ezu3vXelcRkVFkZSUVKsZOCsriwsX\nLrTJv2uj0chTTz1FSUkJ33zzDV27dq1V/ttvv/Huu+/W2lbz+KOt/W5f6fe2vZ3bGuvWrWPYsGF1\nbsya89x26OTv5OTE3Xffzfvvv8/Zs2cpKSlh6dKlpKSkcN9997V0eE1mNBqZPXs2MTExPPTQQ3XK\nDQYDL7/8MkePHqWyspJ9+/bx73//m4cffrjNNaF5enqyadMmXn31VXJzcykuLuaf//wnZ8+eZerU\nqfzxj39kx44drF27lvLyco4ePcqnn37aJj/r5eLi4qioqCA0NLTW9vZ0bn/vSudy+PDhhISEMH/+\nfHJzc8nJyeH111+nR48eDBs2rKXDv2Zffvkl58+f5+OPP8bJyalOubOzM0uWLOGzzz6jrKyMzMxM\nXnjhBbp06cLo0aNbIOLrd6Xf2/Z2bmvExsYSFhZWZ3tzntsOP71veXk5b775JmvWrKGoqIjQ0FCe\nf/55+vfv39KhNdn+/fuZMmVKnQkiACZOnMjLL7/MBx98wE8//URGRgZeXl6mRNkWJ75JTEzkrbfe\n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\nbD7HAAAAAElFTkSuQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "subplot(3, 1, 1)\n", - "plot(thetas, label='theta')\n", - "decorate(ylabel='Angle (rad)')\n", - "\n", - "subplot(3, 1, 2)\n", - "plot(omegas, color='orange', label='omega')\n", - "decorate(ylabel='Angular velocity (rad/s)')\n", - "\n", - "subplot(3, 1, 3)\n", - "plot(ys, color='green', label='y')\n", - "\n", - "decorate(xlabel='Time(s)',\n", - " ylabel='Length (m)')\n", - "\n", - "savefig('chap11-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Yo-yo" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Simulate the descent of a yo-yo. How long does it take to reach the end of the string?\n", - "\n", - "I provide a `Condition` object with the system parameters:\n", - "\n", - "* `Rmin` is the radius of the axle. `Rmax` is the radius of the axle plus rolled string.\n", - "\n", - "* `Rout` is the radius of the yo-yo body. `mass` is the total mass of the yo-yo, ignoring the string. \n", - "\n", - "* `L` is the length of the string.\n", - "\n", - "* `g` is the acceleration of gravity." - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "condition = Condition(Rmin = 8e-3 * m,\n", - " Rmax = 16e-3 * m,\n", - " Rout = 35e-3 * m,\n", - " mass = 50e-3 * kg,\n", - " L = 1 * m,\n", - " g = 9.8 * m / s**2,\n", - " duration = 1 * s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a `make_system` function that computes `I` and `k` based on the system parameters.\n", - "\n", - "I estimated `I` by modeling the yo-yo as a solid cylinder with uniform density ([see here](https://en.wikipedia.org/wiki/List_of_moments_of_inertia)). In reality, the distribution of weight in a yo-yo is often designed to achieve desired effects. But we'll keep it simple." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(condition):\n", - " \"\"\"Make a system object.\n", - " \n", - " condition: Condition with Rmin, Rmax, Rout, \n", - " mass, L, g, duration\n", - " \n", - " returns: System with init, k, Rmin, Rmax, mass,\n", - " I, g, ts\n", - " \"\"\"\n", - " unpack(condition)\n", - " \n", - " init = State(theta = 0 * radian,\n", - " omega = 0 * radian/s,\n", - " y = L,\n", - " v = 0 * m / s)\n", - " \n", - " I = mass * Rout**2 / 2\n", - " k = (Rmax**2 - Rmin**2) / 2 / L / radian \n", - " ts = linspace(0, duration, 101)\n", - " \n", - " return System(init=init, k=k,\n", - " Rmin=Rmin, Rmax=Rmax,\n", - " mass=mass, I=I, g=g,\n", - " ts=ts)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `make_system`" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
inittheta 0 radian\n", - "omega 0.0 radi...
k9.6e-05 meter / radian
Rmin0.008 meter
Rmax0.016 meter
mass0.05 kilogram
I3.0625000000000006e-05 kilogram * meter ** 2
g9.8 meter / second ** 2
ts[0.0 second, 0.01 second, 0.02 second, 0.03 se...
\n", - "
" - ], - "text/plain": [ - "init theta 0 radian\n", - "omega 0.0 radi...\n", - "k 9.6e-05 meter / radian\n", - "Rmin 0.008 meter\n", - "Rmax 0.016 meter\n", - "mass 0.05 kilogram\n", - "I 3.0625000000000006e-05 kilogram * meter ** 2\n", - "g 9.8 meter / second ** 2\n", - "ts [0.0 second, 0.01 second, 0.02 second, 0.03 se...\n", - "dtype: object" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(condition)\n", - "system" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
theta0 radian
omega0.0 radian / second
y1 meter
v0.0 meter / second
\n", - "
" - ], - "text/plain": [ - "theta 0 radian\n", - "omega 0.0 radian / second\n", - "y 1 meter\n", - "v 0.0 meter / second\n", - "dtype: object" - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.init" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Write a slope function for this system, using these results from the book:\n", - "\n", - "$ r = \\sqrt{2 k y + R_{min}^2} $ \n", - "\n", - "$ T = m g I / I^* $\n", - "\n", - "$ a = -m g r^2 / I^* $\n", - "\n", - "$ \\alpha = m g r / I^* $\n", - "\n", - "where $I^*$ is the augmented moment of inertia, $I + m r^2$.\n", - "\n", - "Hint: If `y` is less than 0, it means you have reached the end of the string, so the equation for `r` is no longer valid. In this case, the simplest thing to do it return the sequence of derivatives `0, 0, 0, 0`" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def slope_func(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object with theta, omega, y, v\n", - " t: time\n", - " system: System object with Rmin, k, I, mass\n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " theta, omega, y, v = state\n", - " unpack(system)\n", - " \n", - " if y < 0 * m:\n", - " return 0, 0, 0, 0\n", - " \n", - " r = sqrt(2*k*y + Rmin**2)\n", - " alpha = mass * g * r / (I + mass * r**2)\n", - " a = -r * alpha\n", - " \n", - " return omega, alpha, v, a " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " ,\n", - " ,\n", - " )" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(system.init, 0*s, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": { - "collapsed": true, - "scrolled": false - }, - "outputs": [], - "source": [ - "run_odeint(system, slope_func)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Check the final conditions. If things have gone according to plan, the final value of `y` should be close to 0." - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
thetaomegayv
0.9667.11465144.630635-1.143965e-08-1.994692
0.9767.11465144.630635-1.143965e-08-1.994692
0.9867.11465144.630635-1.143965e-08-1.994692
0.9967.11465144.630635-1.143965e-08-1.994692
1.0067.11465144.630635-1.143965e-08-1.994692
\n", - "
" - ], - "text/plain": [ - " theta omega y v\n", - "0.96 67.11465 144.630635 -1.143965e-08 -1.994692\n", - "0.97 67.11465 144.630635 -1.143965e-08 -1.994692\n", - "0.98 67.11465 144.630635 -1.143965e-08 -1.994692\n", - "0.99 67.11465 144.630635 -1.143965e-08 -1.994692\n", - "1.00 67.11465 144.630635 -1.143965e-08 -1.994692" - ] - }, - "execution_count": 45, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "thetas = system.results.theta\n", - "ys = system.results.y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`theta` should increase and accelerate." - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(thetas, label='theta')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Angle (rad)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`y` should decrease and accelerate down." - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(ys, color='green', label='y')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Length (m)')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.1" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/code/soln/orbit_demo.ipynb b/code/soln/orbit_demo.ipynb deleted file mode 100644 index d95a1ff72..000000000 --- a/code/soln/orbit_demo.ipynb +++ /dev/null @@ -1,717 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Earth orbit\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "year" - ], - "text/latex": [ - "$year$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Here are the units we'll need\n", - "\n", - "s = UNITS.second\n", - "N = UNITS.newton\n", - "kg = UNITS.kilogram\n", - "m = UNITS.meter\n", - "year = UNITS.year" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
x147000000000.0 meter
y0 meter
vx0.0 meter / second
vy-30300.0 meter / second
\n", - "
" - ], - "text/plain": [ - "x 147000000000.0 meter\n", - "y 0 meter\n", - "vx 0.0 meter / second\n", - "vy -30300.0 meter / second\n", - "dtype: object" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# And an inition condition (with everything in SI units)\n", - "\n", - "# distance from the sun to the earth at perihelion\n", - "x_0 = 147e9 * m\n", - "\n", - "# initial velocity\n", - "vy_0 = -30300 * m/s\n", - "\n", - "init = State(x = x_0,\n", - " y = 0 * m,\n", - " vx = 0 * m / s,\n", - " vy = vy_0)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
initx 147000000000.0 meter\n", - "y ...
G6.674e-11 meter ** 2 * newton / kilogram ** 2
m11.989e+30 kilogram
r_final701879000.0 meter
m25.972e+24 kilogram
t_00 second
t_end31556925.9747 second
\n", - "
" - ], - "text/plain": [ - "init x 147000000000.0 meter\n", - "y ...\n", - "G 6.674e-11 meter ** 2 * newton / kilogram ** 2\n", - "m1 1.989e+30 kilogram\n", - "r_final 701879000.0 meter\n", - "m2 5.972e+24 kilogram\n", - "t_0 0 second\n", - "t_end 31556925.9747 second\n", - "dtype: object" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Making a system object\n", - "\n", - "r_earth = 6.371e6 * m\n", - "r_sun = 695.508e6 * m\n", - "\n", - "t_end = (1 * year).to_base_units()\n", - "\n", - "system = System(init=init,\n", - " G=6.674e-11 * N / kg**2 * m**2,\n", - " m1=1.989e30 * kg,\n", - " r_final=r_sun + r_earth,\n", - " m2=5.972e24 * kg,\n", - " t_0=0 * s,\n", - " t_end=t_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "# Here's a function that computes the force of gravity\n", - "\n", - "def universal_gravitation(state, system):\n", - " \"\"\"Computes gravitational force.\n", - " \n", - " state: State object with distance r\n", - " system: System object with m1, m2, and G\n", - " \n", - " returns: Vector\n", - " \"\"\"\n", - " x, y, vx, vy = state\n", - " unpack(system)\n", - " \n", - " v = Vector(x, y)\n", - " \n", - " # make sure the result is a vector, either\n", - " # by forcing it, or by putting v.hat() on\n", - " # the left\n", - " \n", - " force = -G * m1 * m2 / v.mag2 * v.hat()\n", - " return Vector(force)\n", - "\n", - " force = -v.hat() * G * m1 * m2 / v.mag2\n", - " return force" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[-3.6686486e+22 -0.0000000e+00] newton" - ], - "text/latex": [ - "$[-3.6686486e+22 -0.0000000e+00] newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "universal_gravitation(init, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# The slope function\n", - "\n", - "def slope_func(state, t, system):\n", - " \"\"\"Compute derivatives of the state.\n", - " \n", - " state: position, velocity\n", - " t: time\n", - " system: System object containing `g`\n", - " \n", - " returns: derivatives of y and v\n", - " \"\"\"\n", - " x, y, vx, vy = state\n", - " unpack(system) \n", - "\n", - " F = universal_gravitation(state, system)\n", - " A = F / m2\n", - " \n", - " return vx, vy, A.x, A.y" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " ,\n", - " ,\n", - " )" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Always test the slope function!\n", - "\n", - "slope_func(init, 0, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "# Here's an event function that stops the simulation\n", - "# before the collision\n", - "\n", - "def event_func(state, t, system):\n", - " x, y, vx, vy = state\n", - " v = Vector(x, y)\n", - " return v.mag - system.r_final" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "146298121000.0 meter" - ], - "text/latex": [ - "$146298121000.0 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Always test the event function!\n", - "\n", - "event_func(init, 0, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[[]]
nfev152
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events [[]]\n", - "nfev 152\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Finally we can run the simulation\n", - "\n", - "results, details = run_ode_solver(system, slope_func, events=event_func)\n", - "details" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "# Scaling the time steps to days\n", - "\n", - "results.index /= 60 * 60 * 24" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "# Scaling the distances to million km\n", - "\n", - "results.x /= 1e9\n", - "results.y /= 1e9" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results.x, results.y, label='trajectory')\n", - "\n", - "decorate(xlabel='x distance (million km)',\n", - " ylabel='y distance (million km)')" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[[]]
nfev137
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events [[]]\n", - "nfev 137\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Before plotting, we run the simulation again with `t_eval`\n", - "\n", - "results, details = run_ode_solver(system, slope_func, events=event_func, method='RK23')\n", - "details" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "# Scaling the time steps to days\n", - "\n", - "results.index /= 60 * 60 * 24" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "# Scaling the distances to million km\n", - "\n", - "results.x /= 1e9\n", - "results.y /= 1e9" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results.x, results.y, label='trajectory')\n", - "\n", - "decorate(xlabel='x distance (million km)',\n", - " ylabel='y distance (million km)')" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 1.47262303e+02, 7.08483175e-02, 1.32544305e+01, -3.02734326e+04])" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "get_last_value(results)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/queue_soln.ipynb b/code/soln/queue_soln.ipynb deleted file mode 100644 index fc059d2ef..000000000 --- a/code/soln/queue_soln.ipynb +++ /dev/null @@ -1,1331 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Case Study: Queueing theory\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *\n", - "\n", - "# set the random number generator\n", - "np.random.seed(7)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## One queue or two?\n", - "\n", - "This notebook presents a solution to an exercise from *Modeling and Simulation in Python*. It uses features from the first four chapters to answer a question related to queueing theory, which is the study of systems that involve waiting in lines, also known as \"queues\".\n", - "\n", - "Suppose you are designing the checkout area for a new store. There is room for two checkout counters and a waiting area for customers. You can make two lines, one for each counter, or one line that serves both counters.\n", - "\n", - "In theory, you might expect a single line to be better, but it has some practical drawbacks: in order to maintain a single line, you would have to install rope barriers, and customers might be put off by what seems to be a longer line, even if it moves faster.\n", - "\n", - "So you'd like to check whether the single line is really better and by how much. Simulation can help answer this question.\n", - "\n", - "As we did in the bikeshare model, we'll assume that a customer is equally likely to arrive during any timestep. I'll denote this probability using the Greek letter lambda, $\\lambda$, or the variable name `lam`. The value of $\\lambda$ probably varies from day to day, so we'll have to consider a range of possibilities.\n", - "\n", - "Based on data from other stores, you know that it takes 5 minutes for a customer to check out, on average. But checkout times are highly variable: most customers take less than 5 minutes, but some take substantially more. A simple way to model this variability is to assume that when a customer is checking out, they have the same probability of finishing up during each time step. I'll denote this probability using the Greek letter mu, $\\mu$, or the variable name `mu`.\n", - "\n", - "If we choose $\\mu=1/5$, the average number of time steps for each checkout will be 5 minutes, which is consistent with the data." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### One server, one queue\n", - "\n", - "Write a function called `make_system` that takes `lam` and `mu` as parameters and returns a `System` object with variables `lam`, `mu`, and `duration`. Set `duration`, which is the number of time steps to simulate, to 10 hours, expressed in minutes. " - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def make_system(lam, mu):\n", - " \"\"\"Make a System object.\n", - " \n", - " lam: arrival rate, per minute\n", - " mu: service completion rate, per minute\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " # duration is 10 hours, expressed in minutes\n", - " return System(lam=lam, mu=mu, duration=10*60)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test this function by creating a `System` object with `lam=1/8` and `mu=1/5`." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
lam0.125
mu0.200
duration600.000
\n", - "
" - ], - "text/plain": [ - "lam 0.125\n", - "mu 0.200\n", - "duration 600.000\n", - "dtype: float64" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "interarrival_time = 8\n", - "service_time = 5\n", - "\n", - "lam = 1 / interarrival_time\n", - "mu = 1 / service_time\n", - "\n", - "system = make_system(lam, mu)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Write an update function that takes as parameters `x`, which is the total number of customer in the store, including the one checking out; `t`, which is the number of minutes that have elapsed in the simulation, and `system`, which is a `System` object.\n", - "\n", - "If there's a customer checking out, it should use `flip` to decide whether they are done. And it should use `flip` to decide if a new customer has arrived.\n", - "\n", - "It should return the total number of customers at the end of the time step.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def update_func1(x, t, system):\n", - " \"\"\"Simulate one time step.\n", - " \n", - " x: number of people in the shop\n", - " t: time step\n", - " system: System object\n", - " \"\"\"\n", - " # if there's a customer in service, check if they're done\n", - " if x > 0:\n", - " if flip(system.mu):\n", - " x -= 1\n", - " \n", - " # check for an arrival\n", - " if flip(system.lam):\n", - " x += 1\n", - " \n", - " return x" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your function by calling it with `x=1`, `t=0`, and the `System` object you created. If you run it a few times, you should see different results." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "update_func1(1, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run the simulation. Here's a version of `run_simulation` that creates a `TimeSeries` with the total number of customers in the store, including the one checking out." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Simulate a queueing system.\n", - " \n", - " system: System object\n", - " update_func: function object\n", - " \"\"\"\n", - " x = 0\n", - " results = TimeSeries()\n", - " results[0] = x\n", - " \n", - " for t in linrange(0, system.duration):\n", - " x = update_func(x, t, system)\n", - " results[t+1] = x\n", - "\n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Call `run_simulation` with your update function and plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "results = run_simulation(system, update_func1)\n", - "plot(results)\n", - "decorate(xlabel='Time (min)', ylabel='Customers')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After the simulation, we can compute `L`, which is the average number of customers in the system, and `W`, which is the average time customers spend in the store. `L` and `W` are related by Little's Law:\n", - "\n", - "$L = \\lambda W$\n", - "\n", - "Where $\\lambda$ is the arrival rate. Here's a function that computes them." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def compute_metrics(results, system):\n", - " \"\"\"Compute average number of customers and wait time.\n", - " \n", - " results: TimeSeries of queue lengths\n", - " system: System object\n", - " \n", - " returns: L, W\n", - " \"\"\"\n", - " L = results.mean()\n", - " W = L / system.lam\n", - " return L, W" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Call `compute_metrics` with the results from your simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(0.8552412645590682, 6.841930116472546)" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "compute_metrics(results, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Parameter sweep\n", - "\n", - "Since we don't know the actual value of $\\lambda$, we can sweep through a range of possibilities, from 10% to 80% of the completion rate, $\\mu$. (If customers arrive faster than the completion rate, the queue grows without bound. In that case the metrics `L` and `W` just depend on how long the store is open.)\n", - "\n", - "Create an array of values for `lam`." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([0.02 , 0.0214, 0.0228, 0.0242, 0.0256, 0.027 , 0.0284, 0.0298,\n", - " 0.0312, 0.0326, 0.034 , 0.0354, 0.0368, 0.0382, 0.0396, 0.041 ,\n", - " 0.0424, 0.0438, 0.0452, 0.0466, 0.048 , 0.0494, 0.0508, 0.0522,\n", - " 0.0536, 0.055 , 0.0564, 0.0578, 0.0592, 0.0606, 0.062 , 0.0634,\n", - " 0.0648, 0.0662, 0.0676, 0.069 , 0.0704, 0.0718, 0.0732, 0.0746,\n", - " 0.076 , 0.0774, 0.0788, 0.0802, 0.0816, 0.083 , 0.0844, 0.0858,\n", - " 0.0872, 0.0886, 0.09 , 0.0914, 0.0928, 0.0942, 0.0956, 0.097 ,\n", - " 0.0984, 0.0998, 0.1012, 0.1026, 0.104 , 0.1054, 0.1068, 0.1082,\n", - " 0.1096, 0.111 , 0.1124, 0.1138, 0.1152, 0.1166, 0.118 , 0.1194,\n", - " 0.1208, 0.1222, 0.1236, 0.125 , 0.1264, 0.1278, 0.1292, 0.1306,\n", - " 0.132 , 0.1334, 0.1348, 0.1362, 0.1376, 0.139 , 0.1404, 0.1418,\n", - " 0.1432, 0.1446, 0.146 , 0.1474, 0.1488, 0.1502, 0.1516, 0.153 ,\n", - " 0.1544, 0.1558, 0.1572, 0.1586, 0.16 ])" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "num_vals = 101\n", - "lam_array = linspace(0.1*mu, 0.8*mu, num_vals)\n", - "lam_array" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Write a function that takes an array of values for `lam`, a single value for `mu`, and an update function.\n", - "\n", - "For each value of `lam`, it should run a simulation, compute `L` and `W`, and store the value of `W` in a `SweepSeries`.\n", - "\n", - "It should return the `SweepSeries`." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def sweep_lam(lam_array, mu, update_func):\n", - " \"\"\"Run simulations with a range of values for `lam`\n", - " \n", - " lam_array: array of values for `lam`\n", - " mu: probability of finishing a checkout\n", - " update_func: passed along to run_simulation\n", - " \n", - " returns: SweepSeries of average wait time vs lam\n", - " \"\"\"\n", - " sweep = SweepSeries()\n", - " \n", - " for lam in lam_array:\n", - " system = make_system(lam, mu)\n", - " results = run_simulation(system, update_func)\n", - " L, W = compute_metrics(results, system)\n", - " sweep[lam] = W\n", - " \n", - " return sweep" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Call your function to generate a `SweepSeries`, and plot it." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
0.02004.658902
0.02145.209386
0.02283.065067
0.02425.706742
0.02568.059484
0.02709.613607
0.02843.280917
0.02984.075981
0.03126.932890
0.03265.563325
0.03406.704512
0.035410.340581
0.03687.279534
0.03824.225070
0.03964.453856
0.04103.449535
0.04245.376260
0.04387.483722
0.04524.491040
0.04666.641292
0.04803.986412
0.04945.119672
0.05086.386993
0.05228.861349
0.05366.394815
0.05506.534564
0.056410.060065
0.05784.001405
0.05927.110896
0.06063.459581
......
0.119414.687971
0.12086.942072
0.122212.472393
0.12369.827203
0.125027.873544
0.126410.978538
0.12787.121673
0.129212.543593
0.130617.976675
0.13208.042152
0.133413.545640
0.134814.701018
0.136236.649637
0.137628.150756
0.139013.957553
0.14045.119672
0.141817.166969
0.14329.527882
0.144613.750711
0.14607.783831
0.147410.249765
0.14886.988800
0.150210.058690
0.151613.071881
0.153012.800017
0.154410.776512
0.155823.281694
0.157210.351704
0.15869.242687
0.160023.356905
\n", - "

101 rows × 1 columns

\n", - "
" - ], - "text/plain": [ - "0.0200 4.658902\n", - "0.0214 5.209386\n", - "0.0228 3.065067\n", - "0.0242 5.706742\n", - "0.0256 8.059484\n", - "0.0270 9.613607\n", - "0.0284 3.280917\n", - "0.0298 4.075981\n", - "0.0312 6.932890\n", - "0.0326 5.563325\n", - "0.0340 6.704512\n", - "0.0354 10.340581\n", - "0.0368 7.279534\n", - "0.0382 4.225070\n", - "0.0396 4.453856\n", - "0.0410 3.449535\n", - "0.0424 5.376260\n", - "0.0438 7.483722\n", - "0.0452 4.491040\n", - "0.0466 6.641292\n", - "0.0480 3.986412\n", - "0.0494 5.119672\n", - "0.0508 6.386993\n", - "0.0522 8.861349\n", - "0.0536 6.394815\n", - "0.0550 6.534564\n", - "0.0564 10.060065\n", - "0.0578 4.001405\n", - "0.0592 7.110896\n", - "0.0606 3.459581\n", - " ... \n", - "0.1194 14.687971\n", - "0.1208 6.942072\n", - "0.1222 12.472393\n", - "0.1236 9.827203\n", - "0.1250 27.873544\n", - "0.1264 10.978538\n", - "0.1278 7.121673\n", - "0.1292 12.543593\n", - "0.1306 17.976675\n", - "0.1320 8.042152\n", - "0.1334 13.545640\n", - "0.1348 14.701018\n", - "0.1362 36.649637\n", - "0.1376 28.150756\n", - "0.1390 13.957553\n", - "0.1404 5.119672\n", - "0.1418 17.166969\n", - "0.1432 9.527882\n", - "0.1446 13.750711\n", - "0.1460 7.783831\n", - "0.1474 10.249765\n", - "0.1488 6.988800\n", - "0.1502 10.058690\n", - "0.1516 13.071881\n", - "0.1530 12.800017\n", - "0.1544 10.776512\n", - "0.1558 23.281694\n", - "0.1572 10.351704\n", - "0.1586 9.242687\n", - "0.1600 23.356905\n", - "Length: 101, dtype: float64" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "sweep = sweep_lam(lam_array, mu, update_func1)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot(sweep, 'bo')\n", - "\n", - "decorate(xlabel='Arrival late, lambda (per min)',\n", - " ylabel='Average time in system',\n", - " title='Single server, single queue')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we imagine that this range of values represents arrival rates on different days, we can use the average value of `W`, for a range of values of `lam`, to compare different queueing strategies." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "# W_avg = sweep.mean()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Analysis\n", - "\n", - "The model I chose for this system is a common model in queueing theory, in part because many of its properties can be derived analytically.\n", - "\n", - "In particular, we can derive the average time in the store as a function of $\\mu$ and $\\lambda$:\n", - "\n", - "$W = 1 / (\\mu - \\lambda)$\n", - "\n", - "The following function plots the theoretical value of $W$ as a function of $\\lambda$." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_W(lam_array, mu):\n", - " \"\"\"Plot the theoretical mean wait time.\n", - " \n", - " lam_array: array of values for `lam`\n", - " mu: probability of finishing a checkout\n", - " \"\"\"\n", - " W = 1 / (mu - lam_array)\n", - " plot(lam_array, W, 'g-')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use this function to plot the theoretical results, then plot your simulation results again on the same graph. How do they compare?" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot_W(lam_array, mu)\n", - "plot(sweep, 'bo')\n", - "\n", - "decorate(xlabel='Arrival late, lambda (per min)',\n", - " ylabel='Average time in system',\n", - " title='Single server, single queue')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Multiple servers\n", - "\n", - "Now let's try the other two queueing strategies:\n", - "\n", - "1. One queue with two checkout counters.\n", - "2. Two queues, one for each counter.\n", - "\n", - "The following figure shows the three scenarios:\n", - "\n", - "![](diagrams/queue.png)\n", - "\n", - "Write an update function for one queue with two servers." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def update_func2(x, t, system):\n", - " \"\"\"Simulate a single queue with two servers.\n", - " \n", - " system: System object\n", - " \"\"\"\n", - " # if both servers are busy, check whether the\n", - " # second is complete\n", - " if x > 1 and flip(system.mu):\n", - " x -= 1\n", - " \n", - " # check whether the first is complete\n", - " if x > 0 and flip(system.mu):\n", - " x -= 1\n", - " \n", - " # check for an arrival\n", - " if flip(system.lam):\n", - " x += 1\n", - " \n", - " return x" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use this update function to simulate the system, plot the results, and print the metrics." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(0.8552412645590682, 6.841930116472546)" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "system = make_system(lam, mu)\n", - "run_simulation(system, update_func2)\n", - "plot(results)\n", - "decorate(xlabel='Time (min)', ylabel='Customers')\n", - "compute_metrics(results, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Since we have two checkout counters now, we can consider values for $\\lambda$ that exceed $\\mu$.\n", - "\n", - "Create a new array of values for `lam` from 10% to 160% of `mu`." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([0.02 , 0.023, 0.026, 0.029, 0.032, 0.035, 0.038, 0.041, 0.044,\n", - " 0.047, 0.05 , 0.053, 0.056, 0.059, 0.062, 0.065, 0.068, 0.071,\n", - " 0.074, 0.077, 0.08 , 0.083, 0.086, 0.089, 0.092, 0.095, 0.098,\n", - " 0.101, 0.104, 0.107, 0.11 , 0.113, 0.116, 0.119, 0.122, 0.125,\n", - " 0.128, 0.131, 0.134, 0.137, 0.14 , 0.143, 0.146, 0.149, 0.152,\n", - " 0.155, 0.158, 0.161, 0.164, 0.167, 0.17 , 0.173, 0.176, 0.179,\n", - " 0.182, 0.185, 0.188, 0.191, 0.194, 0.197, 0.2 , 0.203, 0.206,\n", - " 0.209, 0.212, 0.215, 0.218, 0.221, 0.224, 0.227, 0.23 , 0.233,\n", - " 0.236, 0.239, 0.242, 0.245, 0.248, 0.251, 0.254, 0.257, 0.26 ,\n", - " 0.263, 0.266, 0.269, 0.272, 0.275, 0.278, 0.281, 0.284, 0.287,\n", - " 0.29 , 0.293, 0.296, 0.299, 0.302, 0.305, 0.308, 0.311, 0.314,\n", - " 0.317, 0.32 ])" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "lam_array = linspace(0.1*mu, 1.6*mu, num_vals)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use your sweep function to simulate the two server, one queue scenario with a range of values for `lam`.\n", - "\n", - "Plot the results and print the average value of `W` across all values of `lam`." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Average of averages = 6.346761276675128 minutes\n" - ] - } - ], - "source": [ - "# Solution\n", - "\n", - "sweep = sweep_lam(lam_array, mu, update_func2)\n", - "W_avg = sweep.mean()\n", - "print('Average of averages = ', W_avg, 'minutes')" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot(sweep, 'bo')\n", - "decorate(xlabel='Arrival late, lambda (per min)',\n", - " ylabel='Average time in system',\n", - " title='Multiple server, single queue')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Multiple queues\n", - "\n", - "To simulate the scenario with two separate queues, we need two state variables to keep track of customers in each queue.\n", - "\n", - "Write an update function that takes `x1`, `x2`, `t`, and `system` as parameters and returns `x1` and `x2` as return values. f you are not sure how to return more than one return value, see `compute_metrics`.\n", - "\n", - "When a customer arrives, which queue do they join?" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def update_func3(x1, x2, t, system):\n", - " \"\"\"Simulate two queues with one server each.\n", - " \n", - " x1: number of customers in queue 1\n", - " x2: number of customers in queue 2\n", - " t: time step\n", - " system: System object\n", - " \"\"\"\n", - " # if the first servers is busy, check it it's done\n", - " if x1 > 0 and flip(system.mu):\n", - " x1 -= 1\n", - " \n", - " # if the second queue is busy, check if it's done\n", - " if x2 > 0 and flip(system.mu):\n", - " x2 -= 1\n", - " \n", - " # check for an arrival\n", - " if flip(system.lam):\n", - " # join whichever queue is shorter\n", - " if x1 < x2:\n", - " x1 += 1\n", - " else:\n", - " x2 += 1\n", - " \n", - " return x1, x2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Write a version of `run_simulation` that works with this update function." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def run_simulation(system, update_func):\n", - " \"\"\"Simulate a queueing system.\n", - " \n", - " system: System object\n", - " update_func: function object\n", - " \"\"\"\n", - " x1, x2 = 0, 0\n", - " results = TimeSeries()\n", - " results[0] = x1 + x2\n", - " \n", - " for t in linrange(0, system.duration):\n", - " x1, x2 = update_func(x1, x2, t, system)\n", - " results[t+1] = x1 + x2\n", - "\n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your functions by running a simulation with a single value of `lam`." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(0.8552412645590682, 6.841930116472546)" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJztvXe8ZUd15/s759zb997u263OQd3qoFZ3KaEAGGSQQEgCDCYNYJLsecJhGDD2MGOcZrCtwYEx5vFsY/A4wIDJBuMHwsIYY2QUAKHUil2tzjnfDrdvOmHPH/uEqtpVe1ftdM7ZZ30/n/70uTtUrZ1q1Vq1alXJ8zwQBEEQRK9R7rYABEEQBKGDFBRBEATRk5CCIgiCIHoSUlAEQRBETzLUbQHCYIyNAPgJAEcA1LssDkEQBJE+FQBrAPyYcz4r7uhpBQVfOd3bbSEIgiCIzLkJwH3ihl5XUEcA4POf/zxWr17dbVkIgiCIlDl69Chuv/12oNnei/S6gqoDwOrVq7Fu3bpuy0IQBEFkR2AYh4IkCIIgiJ6EFBRBEATRk5CCIgiCIHoSUlAEQRBET0IKiiAIguhJco3iY4zdA+AGALXmpkOcc5anDARBEER/0I0w8/dyzv+2C/USBaLe8PDw9mM4MTGNKzctxaaLL+q2SARBpAy5+Ii+ZM/hs3jwqaPYc/gsvvWDvZirUiYsgiga3VBQH2KMnWSM3c8Yu7kL9RMF4NyFufbvRsPD9Gwt5GiCIPqRvBXUbwK4FMBaAH8N4C7G2OacZSAKQKMhrwTdoJWhCaJw5DoGxTn/kfDnZxhjbwfwagAfy1MOoniQfiKI4tHtMSgPQKnLMhB9iGpBeaShCKJw5GZBMcYWA3ghgH+HH2b+VgAvAfC+vGQgioPq0ms0uiQIQRCZkaeLbxjAHwC4HH7W2u0A3sA55znKQBQE1WIiC4ogikduCopzfgL+AoQEkRjFw0dBEgRRQLo9BkUQsQhaUF0ShCCIzCAFRfQlFGZOEMWHFBTRl6j6SFVYBEH0P6SgiL5EtZgoSIIgigcpKKIvoTEogig+pKCIvkSd90RjUARRPEhBEX0JWVAEUXxIQRF9Cc2DIojiQwqK6EsokwRBFB9SUERfoiokCjMniOJBCoroS4LZzLskCEEQmUEKiuhLaAyKIIoPKSiiL6EoPoIoPqSgiL6ELCiCKD6koIi+hKL4CKL4kIIi+pKAgqIVdQmicJCCIvoS1cVXJwuKIAoHKSiiLwm49Eg/EUThIAVF9CW0YCFBFB9SUERfoqojUlAEUTxIQRF9SSCTBKU6IojCQQqK6EtUi4n0E0EUD1JQRF8SiJEgFx9BFA5SUERfEshmTgqKIAoHKSiiL6Fs5gRRfEhBEX1JIBcfDUIRROEgBUX0JZSLjyCKDykooi+hKD6CKD6koIi+hKL4CKL4kIIi+hJ1Yi4pKIIoHqSgiL6EXHwEUXy6oqAYY1sYYzOMsc91o36i/yEXH0EUn25ZUB8H8OMu1V1IPM/D6XMzqNaKv3Lf1Ew1YEHVyYTqaWr1Bk5MTGNqptptUYg+YijvChljbwNwBsADAC7Lu/6i8o17d+PAsfMYmVfBW27diovGR7otUiZs33ca3/3xgcD23YfOol5voFIhr3WvMTldxZf+hWNmroZyqYRbf+ISsA1Luy0W0Qfk+jUzxhYB+CCAX8uz3qIzOTWHA8fOAwBm5+rYc/hslyXKDr5vwujOO3zyQs7SEDbsO3IOM3M1AP7YId830WWJiH4h7+7m7wP4JOc82AUmYlOrD467q143Xxtlk+hNanXZ7Vzk95NIl9wUFGPsOgC3Afj/8qpzUAhmVeiSIDkgjj296WVbsHHNIu0+oncIJPYlBUVYkucY1M0ANgLYzxgDgHEAFcbYlZzz5+YoR+EIhlwXtwEQG7tSCSiVSsK+bkhERKHqI3pMhC15Kqi/BvAl4e/3w1dY785RhkKiNsxF7qGKyrdcKqFc0u8jegf1fSzy+0mkS24KinM+BWCq9TdjbBLADOf8RF4yFBW1YS5yOy1eW6lUQrksWlAFvvACQc+JsCX3MPMWnPM7u1V30QhYUAVuAMQUR+UyAHQUFPXMe5OABUWPibCEJo0UgEFaeqKhWFDitKfiXnV/E7Tw6UkRdpCCKgCB1WULnEwiLEiCLKjeJBDFRwqKsIQUVAFQP/ciNwBqkARF8fU+gSg+ek6EJaSgCkDAgipwCyBea4mi+PqCQXJBE+lCCqoADOrSE+WyakENyIX3GarLmVyxhC2koArAIC09IabJKZd8N1+LIo+99TOD2oEikkMKqgAMkgtFnQdVEt5gcvH1JoP0fhLpQgqqAKguk3qBLQlKddR/DFIqLiJdSEEVgOD3XtwGIJjqqKTdR/QOQRd0d+Qg+g9SUAUg0EMtsJM/kOqIovh6nuA8PXpOhB2koApAcCJklwTJAdmCAkpiLr4iX3gfoz4W6kgQtpCCKgANZcypyIPQQQtKdPF1QSAikkFar4xIF1JQBcDDYAxCe56nCZKQ9xO9hy7VET0rwgZSUAUgmEmiS4JkjHiZrTRHFMXX++gsW3pWhA2koArAoEzUVa0nABQk0QfognaK+o4S6UIKqgAEo/i6JEjGeEqIOQBasLAPUF3QAI0XEnaQgioAg7KcgZjmqBW9V6IFC3seXYeJOhOEDaSgCkBwOYOCfvxSBJ//v2RB5SwOYYfufSxqJ4pIF1JQBWBQLCg1iwQgR/GRBdWb6BRUQV9RImVIQRWAQUkloy73DijZzIt64X2OPoqPnhURDSmoAqBaTEXNqCAHSfj/SxZUMS+779G7+LogCNF3kIIqAKprq6gfv3idrbEnWrCw99G5nMkdS9hACqoADM48qM7vkibMnNq83kT3Ohb1HSXSZcj2QMbY6wCc45zf0/z7VwH8PAAO4Fc458czkZCIZFDW29FN1BU8fNQr71F0LueivqNEurhYUP8LwAgAMMauA/AhAJ8HsBTAn6UvGmFLIIqvoA21LopPtKAKGx3S59Qpio+IiYuC2ghge/P3zwD4R875nwD4bwBuS1kuwgGvoJkjVMRGraWYxCg+XUNI9ADk4iNi4qKgzgNY1vz9cgDfav6eBjCWplCEG6q7pF5UC0rMJNH6nwyonkcfJNEFQYi+w3oMCsA3APwNY+xhAFsB/FNz+3UAdqUtGGHPoKy3I14WRfH1D5QsloiLiwX1XgBfAjAD4OWc8zPN7esA/EXaghH2BOZBFfTjlywoXRQf9cp7Et3rSEEShA1WFhRjbB6ATwH4Hc75bnEf5/xPsxCMsEf91ov68UdN1C2qYu53dO8jPSrCBisLinM+B+CVtscT+TI4CxZqLChpyfeCXnifQxYUERcXhfN/ALwrK0GI+Kgu/qJ+/LqJurSibu9DmSSIuLgESawE8EbG2GsBPAZgStzJOf/5qAIYY58DcCuABQCOAvgw5/xvHWQgNAxKkERD4+IjC6r30Wczp2dFROOioBoAvir8XTIdGMKHAPwC53yWMXY5gHsYY49yzh+OUVYiZuZqeGzHCZw5Pxt63LqV4yiXSzg7OYfrt67A6IjLLQsycX4G2549iXlDZVy3dQXmjw7HLsvzPDyx6yR2HjwT2O55nmRdFAG9BdXZdvLMdCGvu9/RWUsT52axfnUXhCH6CuvWlnP+zqSVcc6fEv70mv82A8hdQT2+8yQeeuZY5HFi439huorbXrA+Ub3//shBHDw+CQCo1Rt4yfXrYpd18Pgkvv/oIe2+hgdUCtZO64IkpEwSAA4cO4/1qxflKRYRgc5YevDpo7h264r8hSH6CuegB8bYCxhjb2OMLWj+vagZ5Wd7/icYY1Pws1IcAXC3qwxpEGU56di+73TielvKCQAOCb/jMHF+xriviC4UXTbzxeMj0jFnJt2fK5EtOtfr8BDFWxHRuCSLvQT+ZN0tAEbhT9bdDeCP4Lv7ftmmHM75exhjvwLgJwHcDKArLYrYgF+7ZQVWL5sv7X9q92kcPH4+b7GcEFMcjY8NY3K62tlXRAUl+/gA+Irqyk1L8fQev/MwKGmf+gXTe0gxEoQNLt2Yj8N3xS2Bn96oxVfgh6Bbwzmvc87vgz/J990u56aF+IGsWb4AWy5ZIv1bPG5tFHYNscG+7JLFmDdcaf9dxHRHUi4+YZxJvG4KlOgtTNF6RexAEenjoqBuAvDHnPOqsn0fgLUx6x+CPwaVO5K7SDOoro5t9CJq0IB0GQX8/nVRfACFmvcypsdBHQnCBpeQtCqAcc32LQBORp3MGFsJ4BYA34Rvgd0G4O0A3uEgQ3po1hYS6YdIMHX5iaKHXIvuO/H5FP26+xmzBZWzIERf4mJBfQXAHzLGWkrKY4wxAB+Fn6MvCg++O+8ggAkAHwHwPs751x1kSI26F2FB9ZmCKpXkRruAHj5ZIQtvrvioSEH1Fro1vAD9IoYEoeJiQb0fwF8BOAFgHoBHACwC8DUA/yPqZM75CQAvjSFjJmjG2yX6QD9J/pNyuQR57b7iNQDyirqCBSVdeJ4SEVGIr2GlXEKj7m+gjgRhg8s8qGkA/5Ex9rsAroTv7tvGOedZCZclpsYubFuvUVfG0eTM3sVrAExBEtKihQW87n5GmrtWKQH11vYuCUT0Fc5pETjnewHsTV2SnBGXZtAFRPRBjISiZIsfLCAvtwHt7yJajv1MQ7KgymhpqEZBs50Q6eIyD2oegF8A8BIAK6CMX3HOb0lXtGzxIsagShloqEDOvMTldX6rUXxFbKg9iGNQnYstumLuZ9TsH6VSqb3N8/rElU50DRcL6lPwl3r/BwAcfe7tVwMMVLIIkkg767gadl30aLaGMYpPOKaA193PqNk/SqVOJ8JXVKShCDMuCup18FfS/VFWwuSJaTwjbFvyOuXGM+k4kTqOJloVRWyn7eZBFfDC+xj1OyuXSmigFSgBVAznEQTgFma+AwXq7ugWvxPJwvWQduPZUD5+UeRiBkkYovgKHl7fz8jpqWi8kHDDxYL6RQAfZoz9OYBn4E/cbcM535+mYFnjSa6H4P4sLCg1wixdC0oelynip2+M4iuTBdWriM+jokaa0rMiInBRUPPg5877BuT2r9T8u6+sdVE3aC2oLJItB8agEhYnTVwtyRN1C2hKmMYNqVfeu0jjhuUSRDufHhURhYuC+gyAxwHcAeA4+ryTrlofKlmEv6o9xqSNqRR2jQGeqFtwxdzPhFr5BXxHiXRxUVCXAHgt53xnVsLkiSkFS9i25HWaZYiD5PIqq6mOivfxq2NuLWQLKkeBiEiC+SLFfV0QiOgrXHPxvSwrQfJGt3y4SB5BEkkbUzXQo+jzgWSXZmd70RVzPxOcq0fWLmGPiwV1FMAfM8ZeBeApBIMkPpimYFmjjt+oZLHchvpBJk2YqVoURZ8PpLo0W1AUX++iToin8ULCBRcFdQOAbfAXLLxR2ecB6CsFJTfuwf1ZxNOr32NyF99gpTqSXZomF18BL7yPqSvpqYo+mZxIF5dksYVx7wFqXrd8LKi0XXyeMku/6EESpig+aeCdTKieRY00LeArSqSMc7JYxth8AJc2/9zNOZ9KV6R8EPO6dSuKL2nCTDVUvvjZzGmibr+hJvgteieKSBeXZLGjAD4M4F0Ahpub5xhjfwPg1znnMxnIlxmRS75nkupIvy1uVQGLouDuEykDvTQRSjimgNfdz6hRfCWpE9UNiYh+wiWK708B/BT8nHyLAVwE4A0AXgHgz9IXLVu6EcWns2qS9CJ1ec50+4qCZEEJb27Rr7ufUb8z+VnRwyLCcXHxvRnA6znn9wvb/pkx9gvws0u8K1XJMsaUeLSzTa+hkrnkgh9kw/Nip+BQM0UXPorPItVREV2b/Yz6nZUK/o4S6eJiQY0AmNRsv4AYY1ndJq4FlaQBNLn44pcnhl0rUXwFdJ9QqqP+I5Bxn6xdwgEXBfXPAD7OGNvc2sAYuwzAxwB8O23BssRrBie0cAmSSPJN6RrPJApP6p2WS9Lk1UL2Tq2CJAp43X1M6ERdelZEBC6Wz7sBfBbAs4yx081tSwB8B8Avpy1YlgSWqXAIM28kWMTG5OKLS9jHX0RLQlTmFVpRty8ILKopdqLIHUtE4DIP6iSAVzHGtgK4HL5XaTvnnGclXFZEWU+AeQwqLYUSts2+PPMkyCI21KYM9DTw3ruIrubgPCh6VkQ4LmHmvwvgI5zzHfAXL2xtH4MfZt43mSTU9Cs6TIoryTelU25JPtKgJSjsK2Dv1NSxkAfecxSIiEQdN6TEvoQLLmNQvwdgXLN9AYDfTUecfJB64gZXXlgUX+x6Na1nkgY1fMHC4n39NlF81CvvLdTOYIXGoAgHIi0oxtj65s8SgHXNCbstKgBug78+VN/gNaJdfKYgibR76MksKDlIQs4UnUisnsTOgqJGr5dQM+6j4G5oIl1sXHx74QeveQB+rOwrAZgG8P50xcqWqLWgAP0y8EAyhaIu+a7K4krYgoVFbKhNrllasLB38ZTsH7TkO+GCjYLaBF8R7QbwAgAnhH1VAMc45/UMZMsMk6tIxGhBJZoHpRuDil2cxCAMQJuCJLLIm0ikg2RBlSkXH+FGpILinO9r/nQZr+ppbKL4jBN1U47iSzQPSsonWPwoPtMaXpLlSBZUT0ELFhJJcIni+yX41tI3mn//vwB+AX5E3zv6aSl4NUWQDnOQRIJ6U7agVIui6GMxskuzgzz5M0eBiEjClnynR0VE4WIV/SaAswDAGLsJvnL6JQDPAvjz9EXLjqg0R2HbEyV31QQuJLPI1BDeYrv4xCuiBQv7g2CYOVlQhD0uCmot/HEowM9i/mXO+VcA/E/4q+32DVYTdY2ZJOLXm/48KLV3WmxLQl1bqAUNvPcw4nhvuaRMCeiCPERf4ZLq6BSASwAcAPAqAB9obi/DIvkPY2wEwCfgh6UvBbATwH/nnH/LReA0sIriM07UTTdIIrVUR0ouviJaEqbnVvSxt36m3pCfmfhZUWeCiMJFQX0WwBcYYxz+WlAtxfJCANst6zoA4KUA9gN4NYC/Z4w9h3O+10GOxDSU9Cs6TC4+Xai4LecuzAW2XZiuOpczM1vDbLUeCJIQP/803See5+H8VDWg9BYtmAcAmJyuYnxsuF1npVJGtVbH1EwNlUoZ42PDgTIB/9o9wLg/KEfntymKr9HwcHZyFiPDFYyOZJdkf3JqDvWGh/mjwxgeyi5+yPM8TE5XsWB02PiuulKtNTA1U0WlXML4/HmplGkibDL59EwNZydnsWjBvPYznKvWUW94GNM8O9N7ODxUxvxRu3coDeoND5NTc5g/OoThoXiJOT3Pw7kLcxgZrmDecAXnp/y2QbwXJhoNDw3Pw1ClMHFrRlxy8f02Y+xRAOsA/CfO+XRzlwfgjyzOvwDgTmHTNxljewA8D/5cq1yYq9bx5X/tpA80vQqmd+QfvvcsfvF1Vzs1fp7n4a77dmP/0fOBfd/+4T4cOz2FG69da1XW4ztP4L7HDgd6n+qS72l1Tj3Pw9e+txNHTl0I7Gs1ItOzNcwfHW43HDdcvQb3P34Yc1V/9gFbvwQvf+EG6dx/e+gAnt5zCgCw5ZIleOUN8n6TLC0kF5/wu1Zv4LPfegblUgk3Xncxrrlshd2FOvCtH+zFroNnAAAjwxW89qZLsXrZgtTr8TwP/3jPLhw+OYlFC+bhrS9nGBmOu3qYz9FTF3DXvbsx23w2m9ctxqt+cmMK0up5hHfm8KtRfA8+fRQPPn0USxeN4i23bcWh45P45x/uRb3u4Yar1+C5l69sH9toePiH7z2LY6enAnWUSiW88KrVeP4VqzK7jhbnLszhq//2LKZmqhgeKuOnfnIjNqxe5FRGrd7AV/51B06dCy5CvnzxGH7mli2oGJTP1EwVf/+vO1CtN/C6mzZj1dL5sa6jX3BSwZzzv+ecf1QIPQfn/O845193rZgxtgrAVgBPuZ6bBFVJjI3qFc1wpWzsGe89es6pzrOTc1rl1OKJnSety3pq16mAchqZVwnm4ktJQ52YmNYqJ8BXTNOzNQD+h9P6+3sPH2grJwDg+ycw0zwO8Hvwz+w93f772QMT7XLCMEVflssljMyTG+6G5+GpXaciy3RlaqbaVk4AMFutY/u+idTrAYCTZ2Zw+KS/BNu5C3PYd8TtvdPB9020lRMA7Dp4BlMz7la8DZNTssdg/ugQ5mu+t9PnZnDw+CS27TyBaq2BhufhgScOS8ccn5jSKifAV+RP7rL/hpIg3q9qrYHtwntsy6ETk1rlBAAnz0zj8En99wYA9z52GJPTVczO1XHXvbuNxxUF12SxRlySxTLGhgF8HsBnOOc27sHUqNXlKIcXXLlae1ylUsZLrluHx3YcD7xMtZpbpIRa5+LxEZTLJZxulltveNYr9VaFssbHhjE6MoTnspWZTdQVZa+US1gwNozp2RqqrvdAUC71RiMgX70eXV6Yi++l16/DI/w4ZmZrmGy6TasWZbqiu24b2eOgvjf1evJnqpYJ6K8pDdT7v3ntYlRrdRw/PYUTZ6YxPVNrH1OrNzAza57vL8o4VCm3FV3LbZ7Fs9ah3r9ajGcS1X7onlGLU2en279n5qI7df2Oi5P+ZcrfwwBY8/9HAFgpKMZYGf541hyA9zrUnwqiZXHFxqWhrpkrNi3FFZuWAgDueeRgu5fm2vaLx69YPIa3vpwBAD7x1W1teRoeUHEcYnjjy7a0x4EANeWPW1kmxPu1ZvkCvOGll+G7P94vWUBWCOVoJyxb3FN1bSGRreuXYOv6JTh3YQ5/d/fTbrI5oF8yJZvBfrXcNKziLLOZhJW7ZOEohod8r8SrXrQJAPDtH+7FswfOGOUysWb5Arz+JZsxW63jb/7/JwJ1ZYlaT5xnT6Eh9riMQakKCoyxeQD+EsA2mzIYYyUAnwSwCsCrOefZ+BZCsJkDpSNJnrtAwsz2b7TfVv9Fj5ZHDYwQKWUQxae7X3FSC4nBJXFXFnaNvsxino1O9qw67+p7lsYz1cmah4LVx3fYB/XophhIk35zmlehPpM4nYaoa00SiFU0EoWBcM7nAHwYwG9bnvKXAK4A8FohyCJXwnrhYSRxnxkH92MsE2HKRweoS587iRhSX3jDYIt4eTrZbJYHkW6Rxfy1LL5zfYOUVQMf/nfMUgNbsgr3Fq143bI2YhxAlAS6zkk3lo9Xv9M41UZ960WcIhKXNOJwL4eFomOMbQDwLgCzAI4yxlq73sU5/3wKcljhGayZKJI0/qaev1h/veHBJlA2bJJxFkESjUZ4w2CLKLd2XSyLmyr2kisWWjKLDz3tfIphBHrrKdSjKyM7F5/5XfW32VtQUZZ8Xm26KmacZxIlK+mnDi5BEp9SNpUArAZwC4CPRp3fjPzretppmzx8OpKk0zG5FSU3lWWRYW6ukiykk4wmdLLHmY8jyh13HMTkKhXJOqtE2otOutSVxvXoZM1Dwepcsi4KRuf5UN3utoFGSUjDgop6jjSBuYOLBaU++Qb8saePcc7vTk+kbJFdZPbnJemtyUpRX7/tS6lmjxARrYq0Gh1dwxCnDZBdfPHcTDbjh1lnldA6+HJzL6VRhqZzkLxYQ12d37rn5TKuq/N8tOZVtfZ5Xrx304U0Og2RFlQBFxuNi82Kuivhu+bexzk/q+xbBOBXGWMPcs7zmYiQENOid1FUEvTMTT3/OG7D0CCJDMagdEtcuNy3tjxSkISunmg5bMYPs05Gqregsmni1cHydCyoeO7VOER9a1HjuqJFJAdJyIFGrVNtA42SoN6qOJ0TsqDssQmS+HUAG1XlBACc83MANqCPVtSNG8UnvvfOH7RweFn5uDpy2VpQZjdXFlm95WISjEFJZeobI1s51IwEIlkviJdvmLbqTkpeUZ5h8nVJqQT3S9ZuI9x9Ju4Rn3HeCZLTCP2nIAl7bBTUT8MPDTfxfwC8Ph1xsiduFF8S15GpznIMl1zYasBZR/G1oq7iRPGJ16eboxU9SB7e2HX25R/Fl50FEv53HPK0AEV045bitIiG5wUDEAyBNd1caiWVKL4IFx5ZUB1sFNRG+MldTRyCb0X1BWIkmC701YTc+Du6+Ax1uo5rec2B4M758v5MLCgxVLjl+4+hoUR54oyDhClmEfUepN1oxXFPxiWNOTcqucofYUGp73+YxShb0J3fSb7LOKidqzjzr+pRFhSNQbWxUVBn4C+zYeIyNBcy7AdsGzqVLKL4XMuMcnPJVl46H6tu/CzWGJTYG45hhbhYUFkGSsQN8IiD2lClEmaeo/xRUXyqcgkGIESXlfcCiIEUXbFMqPDdZEF1sFFQ34G/mq6J32we0xfEDZJQ/eUuGF18jr2/qJn5mQdJpBTFp7tUl4HjqOeWpdsnzzGcQCaJFMrsVqojmyi+wPUaso+YOnl5EPiuYty76IhF9zKLik2Y+Z0AHmKMPQB/vtOO5nYG4H3wJ+r+RCbSZYD0crjESCj+chdMH5c8BhVdTtQ8oDiZKeLUGSdIIsqCio7i6/yOqr9cKqHebDkanhe9mqYDaS866VJXVhZUHgpW26ES39eGF3gHpLlz4hpuwvPPYmpFGKmkOqIoPmsiLSjO+R4ANwKYAfBlAI82/30ZflaImzjnfZP3XXz2FScXX/zGXzxc/FBL0jFuLj7toLPwO62PVdcwJE51pM1mkI6LD1AavrRdfLlmYlD/zmYMKo8wc70FJVv8YQEIUkfJ0GrlksIuhU5D1LgVRfF1sJqoyzl/BsAtjLFlADY3N+/inKe/4E7GyAEL9uclCpKwsaAsyowadJYsKCcJQ+rUNAxxMklEBUlEJgsVOxYR9UsToFNutbQWSFaZGELGZOKikzW7XHzhCkp1xYbNMTJZ0Fl4DcJQn0mcKqOeI+mnDk65+JoKqe+UkkjcXHxJsgiZLB/XKL7o1DHCsWlZUJqGoRRjMmTSibou15NlZJeuuFgD5RZkkc1cm6g3KwtQ+B01ZqqL4rMJksg9ii/EDWlL1HOkbOYdir+ovULcKL44c5Z0x4s1uk4qjR50Tv9j1TUMSXPxxYkk02W0MJFlElGtnH3l4uuOBaV7ZlFBEtLcOYOLN++EsXnk4iMXX4eBU1AuYxki0viO4/vTMDSursspQArbAAAgAElEQVQFuETxpfWO6xqGLKL4oj5Klyi+LLNJ6CI48wjTBjIMksho3o3coQruV91zAYUsaH7PoOyyzhyikkaqI5eAoEFnoBVUXAsq2XpQSVx8nd82Pv1U0Lgns5kHFSFGxLWLZLkmVLdy2fl/Jy8zz0wSkS5pZcHCwJibZEF1tkvzoBwjYZOSRqqj6PFW0lAtBk5B1aXMCPbnJYniM31cZcfQdZcgibQaTdEf3lkPyr0csZeuzyTh4OJzqD9t5dGteURAfuHsWZQbOS1CI4dsdZtcfPpjsoIWLMyXgVNQokngMpaSJM+d8eNyWPIakBvxpOvr2KKTPfE8qBgLFkatzioiN3xpK6jgttxcfClUoxuAz0zBGuYutVCDesICEIxRfN0OkojxUKJOoRiJDgOnoKJCX01kkerINdjAddA5DXSyx3HxSSHDEfVEnR9VvdRopR1mnmMDHwxpTiNIIrqetIiau6Sm5rKdByXNJex2kEQKZbjuHyQGT0EZ3G1RpDUPqmIY4LUJLe1+FF+rbvdypMYmxjiIW6qj7BotnUWWmYtM/TsFRRLHvRq/rs7vqDFTvWXnaX/LQRI5W1CaTkOSMek4+weJgVNQcaP45CAJtzqll9rY+7MYg4oah0kwV8uEbhwh+ZLv4fXo5ej8jk51pK83DbRLheQQpu3Xk0KZMQJU0qgryiWtU1DyEi3Ceyi86FlOytahq8I9qjdiP/n42gycgnLpiRvLcHyBxDbBlInZpo2LaqRF6yyt7AY6izOpi08/DypKjt6woOLkEYxLXgsW5jGGFhXUU6/rLDv9b8mCyjmTRJzFNl2PJwOqw8ApqKi5GSYqSSwoQ+Pq2tOPsv5c51XZoHOtxNHrjYgovuggCYcxqAwbrTiyxyUYNJBCmTHyIMZG7NxE5I50sqCMUXyxpHQijWS7Lp2xQWcAFVTQZWVDksbfHCIrWjzR5bi4THo5ii/OxFCnTBKGetMgTwskEwtKW0/iYrXopiiIyBZU8KWQ3cKmTl6+Y1C6KlxTE7nknRx0Bk5BiT15l7GURFF8hnBb13lQ0UEScnmp5G7TNDJJk8XqctdFL1jY+e1mQdnJZ4vpOWVhhaS93IZpQD8PBat18UWMQclRfJ3fvZTqyN/oXIh7HQPKwCmoNBYsTJTNXLjjzkESEWHm6iq7abznOsWQNIpP90FnFcWXR5h5FvXoykxag0nE7Cbqdn6nGcUnz4PqHN+NKL449UYv+W5fXtGV2cApqKiBWxNJGn7zx+VWpo3sac+sz2vJ936J4jOVlkUzoYqehgWlIyuXUuSChZIFpXHxWaQ6yn25DW3nyq2MKDHDygtmuHeru98YaAXllouv89s1Qs5Up2uIrCkaUCRJxgsduoYhzhhUulF84cdmGsWXpwWV8hiUUUHlkEswKtVR1ARoc5BMdtayjjyi+MI6VVlM3u5lBk5BufTERUoJGn7T+IlrSh6bfHRpW1C6cYR4FpRQZoxIMpcgiUyzmRutkCwUVPjfrpgG87Nq5KImxYubapowc+meGp5/Rew4xhPTiTSSBbskRo6Wx6nqvmPwFJQ0jmN/XpKG3zR2JI+VuJVjykeXdlSTbpmDeGNQendNp57w8+VGIGIMKoOkue3yjOM4qVbTLDPl3rLh9KwaObcgieALYAqsMc0lzMeCstsWXkaEBRVyHWRBFZyoJStMJAuS0JfjPg+q89ske9ruLe1E3TiZJISC4kSSiXsrEW9tpqmO8oziC2SSSFZHnhGIarn63JHhE3XluXOd312N4kshSCLJgoVpJKvtJwZOQcVesDBBT81qHpRNFF8XXHzSx5RSFJ++F2rfq+xqkESOgQZpN0Z5R/GZ0hO1t0WFmYsLFvZKFF8qY1DudZjqKvqk3oFTUPGDJLofxWcTIi9mvEij0dQFZmSyYGGqUXzZRXaZXLF5zIPKqryscvFJ744um7k4fhQxtmOav5hknbY46KpwvX9JUh1RFF/BibvcRpKemsnFV3Iss+G42GIqS4Rrx6CSRfHFWfTPLYpPOC/lxjfPSLjULajcgyTCv7Wo90i2uvVeCKkzkvGKup7npZPqKHJSepgFlazufmMoz8oYY+8FcAeA5wD4Iuf8jjzrV3HLJJGWBWUq02IMymKxxbTnheij+NzLERVFnAULXVJUZblgoakjkUkUX8rZzE0iZjcPqvM7KopPf77e6paXfO8cHzUBNinm++fo4ovYH/YtBMclnaruO3JVUAAOA/gDAK8EMJZz3Th+egqT09X23y4NrfgxzdXq+LeH9gMA1q4Yx9b1S7SNpud5eGbvaew9ck6oU+/ie3j7cVy/dSVGR/SPpFZv4LEdJwR5oqP4Hnj8MF587VosWjAPR09dAN830Y6WGp8/D1dfugzzR4el8y9MV/HErpOYmqlh/aqFOHLqQqDsOBbU9n2nUS4D1VoDzx44E9ivfmgnz0zjmT2nUa3XUa974PsntNeoQ5TvsR0nsHX9EgxFRVaEIN67Y6entMf88MmjGBlu1VHC+tULcdm6xaHlTs1U8cTOkzhxZrotX63ewPxR/x04MzkbOP6R7cdx9eZlmJ6t4cndpzA7VwMALFowgtlqvf03AKxYPB9XblqKSqWMs5Oz+NFTR7RyPHtgAhvWLARbvwS1uoendp/E6XMzWHbRGK66dFnkvfM8D9v3TmCuWsdVm/3jJ6fmsOtg5zlHRfGZygWAifMz0n0vSV6Izu+dB86gVmvg6s2d9/r46Sls33caSxeN4opNy9ou8KmZKp7cdQqT03MYGR7CVZcuw+KFI1byqDz8zDGMjZqb0nnDFVy5aRmWLhqF53k4fW4mtJ7J6SrueeQgtq5fjIuXj7e31+oNVJWchQ88fhjrVy/EFRuXSvdiZq6GJ3edwrkL/jtULpWwZf0SrF3hlzdbrePJXSdxdrKz/7JLFmPdyoWhsuVNrgqKc/41AGCMPR/AujzrbjQ83P3AHmlb7Ci+hoen95wGADy95zQWLRjBmuULAufsO3oe//bQAbkcaakA+fiHtx/Hi6+9WFv/9r2npRfbJopv16GzmKs18JoXb8I379uDGaHxAoCp6Spuft4l0rb7Hz+MHU1l8PSeU9qy4wRJ+OWdNu5TXYB3P7AH5y7MaY/Vrc4q7RcEPH1uBtv3nsbVm5e7CdukVm/grvt2Y3auHnqc2BgD/r17+ysYll1k7ofdv+2wpHhteOCJw6g1Gjh4bBKHT05GHH0aw8NlXL5hKb71g704eWbaeOS/Prgfi+bPw4kz07hv2+H29qFKGVdduiy0lr1HzuG7zQ5bveHhuZevxL8/ekg6xpSaK4xWp+U7P9ovl2Xo5B2fmMLxiSlMTs/hluevR73h4Zv378HUjN8pHZlXwZZLlgAAfvDEETyzt/M+Hjl1AW++ZYuVPCp7hA6oiQPHJvH2VzDsP3o+8tiW8tixfwLvfM2VGB6qAPDbAJWdB89g58EzWDA6jA1rFrW3P8qP4+Htx6Vj+b4J/D+vuRKj84bwKD+Oh545Ju3fvm8Cd/z0lcZOcjcYmDGombmaZD0tu2gMo/Mq1ucPD5Wxcsl87T5Tj+j0WXn78FAZK5Z0Gqy1K+Teyqlz5gZErWPdinHtcetWytsnzs1garYWUE4mucN6d8suGgUAjI0MhTa8LRbOnxd5TAtRQdXqDaNyAhDZy1PvQVSPNYwL01WtcqqUS9hySbiFFFWvq3JqMXFuxvqaWu+gqpzmjw5LATUAcEpTrk099wsK7YEnDrdlFFm1NPjtLJw/jMXjZqul5eo6dbYj+7zhCpYu6pyjPmv/eL/u2blaWzn5MnUs0jjXmcRl3ir/lKGei5ePY3xM9mbMVetSmxUmo1qu2vYAQLXewORU1VhW1HfXDXpHVebMG192mXMmiTe8dDP2HjmHaq2BHfsncOiE34M1pr8RXuixkSG89eUMo/M6t3zFkjHcdO1a3LvN722GR+90fl++YQk2r7tIe9yN163F8sVj+N7DB9rnmeULbjOlcdq0ZlHb1VMqlfDGl12GvYfPAgBWLV2AIycvtC3CWt3D+Ngw5mp1fPuH+8wXZZAlzK/+nM3LsVHoKerYvPYiXL5hCbbvm4gsLwrxmYyNDOGGq9egVPKV5PjYMLauX4KpmY7yf3rPqbY7KsmQyAuuXI0FY8Mol0pYvXw+fvjEEew65N/vRiM8U/2G1Yuw76jfqzeNjywYHcKbb9mCL3x7O2pNt5Eu23ncwAyx3v9w82UBVzLgv0dvvnUL9h45154H9dTuUzg+IbtRRZHe8crLURFcjpesWoi33LYVuw+dbVsErePVSxdD2dX7YnOd4jnzhit45Q0b2g2+idZ32L63QjVXblqGKzctxfmpOWxcswj1uoe9R8/hB48fwYWmYvUM38WaZQswPFxuW2TB8HPDNTR3mL7zXhvSGhgFJb6A42PDGBm2t55azBuuYOt630Vw6ux0W0GZBuLFl+aqS5cFekgAsLRplajHh5W1etkCo3KtlEvYdPEifO9h/2+/MYsus4VpoHmBIvvIcAVsw9L23zr//Z6mArNBmsgb0lisWqa3YkVKpRJWL1vQVlBJer7iuSPzKgF316aL5Y7C4ROTbQWVJHBi87qLJCt16/olHQXlhZe9atn8toIyHVYul7BowTxcdekybHvWH9v0GvEyFeiOEMsJs6RH5w3hcuE9ujBdbSuoliIWr3WBZqyn5dnoKChPK7vsRoZxnwl16ZkNq8M7SgBwzyMHBXmCndbVyxZg9TJ/eGB4CLh8w1I8uv14W0GZJrizDUtwfqoqKCi762ltjVJgvcLAuPjiZpAwYTNxVzeHSEVOmGmuL2qpDVOZptBYtczO8foy07hnYYhKPqzxtZ2DFZWI1BbXeXNp1aveb/WZhrWnQ8LgpkkGXWb6uuellv9PngPlEo0k1i1fp7qcjIg6RgwEJ/9K1kjCaDjbz0GdnmIzXUJ6hwzTM0qlkjLn0c2CyjurSFzyDjMfatZZAVBhjI0CqHHOgwMkKRM3g4QJm6zh0stokTvP1oLSzco3y2Z2B+m2mo6NMznXpWEypbVRsW8Y0gm3d5kg7B+jP9cVtSr1mYYp8YrFVIPWIVI9XrjVYZRVs81l3posl6qIxfc+RAbxvrfPN8ukyxTveV7oMxaVmjp+Z5ar1JbI8zzJO2DKp2ma0tJQFz4Vr9nS8hWtOR29lpkibwvqAwCmAfwWgJ9t/v5AHhXHzSBhwialkE0P2nayrjSnxDEXXeDFDpHP2OOO8aa43OeoibxtOSzLlO+rtRgBZLdO9PFpJetV752q+MKKLleiO0+diExV8cnHxVXuroq9hdrxk997czk6y9XFxWfaJhLnPZKtoei5YYC5bZGUdTm8k6xbCds/zgvd32P6Kfcw8zsB3JlnnS3ifjAmbCbuipvtLChzfU4TVQNuEqHnVymhUTOb+Wm6+FxOicp23sKt5xos2xXxTDsLKp161bpUl3JY2TYWVMfF19mms7bju/jieSzUxtm2HNGr0G6EQ67FvK6TuRKXJV/acilWjk26MlMnR1VuYV4C8a9KuYRGXbacpDZB2D/oFlTXcO0JRxHm/3WpU+3BmrDpeenKVAdmK4L5pavOJEMsF5/DOVIUXwoDtWkt3Ogy9qfWmyT1jlqX+LdpXSfAf/bB8arg8a3XQHQzeQ3P2lUUReycl8r9sy1HlyszLHAgztLtca4p1DVr0SZILj5FWYd5X6T3thKuvKX9FCTRHaT3Ig0NFeL/7dQZbfXYrtTr0iNVe6ENpbekK7OzLbpMW1ysLsmVERLsGqvnmmgMSrjvNvUKzzOZi0/9W1RQZs2nDE2gYXAHtiyOMJdaa1sc4gZJqPfP1vOhW6QybEXiOC6+OF4YVdmIZVQs2oSGbPZ1jglYUKqs4Z1SSQ6XhfFypnclS5m0x6DsgiT0x4vYrtSrWzjQREl5ecW1dioRvSWjBRVDqTsFSUT0bttlxojiC1P8LnLZXE9a6xMFXXyCTJq1k1qUrS2ooILSRXzGngclBgM4nBcaJBHm4tN8R4EgiZB5UOp+HbJ7LvRQ7XGe5ymLjurPMXlVVGu+ZFJkkNuSoYp8T8X/1f1h1nk3GBgFlWUUX1TETFidtj191zB5qUEzKDd9LzI9F5/LGWK1YQ17nCi+tCbq2tz3tNYnCoviC3fxBRtr3eFtOaX3T3Pv4wZJiHXFTMqsBm2EvYO67ygYJKH/LdYXhrSaQIyxUN+ajfaqqJGV4vniuWHjnabxsk6YObT7ey3MfGAUVJZRfDZzCqzmb4QqKDcFK5ZbExJMiok/XXqRcW6ZS8PkRfRu22XaCpKSonANl7YdU4wiGMWnf56B88qy9WwKqGiVV1EVgvL842QID1o+McegPMUSCykmKlJN3K7+FusLwybAQUXtKNooXPWZ6OovlSKi+Awuvtb2KBdgrzAwCiqO/zgMm8FwU3i3iO1KvZ7Fiy2VaxhUjwruMI9BxbCgHE6pSx9iWJnug9NJPjp3C0rsysevNzhRt/M71IJCKTgGqTm+HWauuInCrA5bTCHMNoTJYxtm3h5nUeWSLCjdux9hQcXwwkjvISxdloY2QVVuYd4XeYwpeG9M49IUxdclZDdX8vJsBsPVeQs6bI0M12gyk4tP97JK9RhdfBZCBs5xOCmi8Whh3TCklNHBddzBNtoushylrqjl0cX6bSb1dibqho9BxXH5iGe49muCQRt2yk73voelbdKPv4bL5tpJBILvoVWYueEbDVhQIa45NYy8LYPGupTahIwXfXRlYBRUXJeDCZv5LjbmvG2D5jqGZhqzKIvmvENIcZwgCZf7nHaQRFpRfK4dAxHbzCA6woIk6mEuvpLdpF5dqiNdxF+cFYmT3DP1udlasLox4YCyFeTSds4iNFTUCsE6pOEkTx1H0p9jGsdULVPxdPWVkBRQRfzmg/VVItz+3WSAFFTnd9pBEjapjmzqDHs3XMfQ5B53560Mzd8V4TpyxeU+S0kxQ8t0d/GlNQZlc0FRQSg2+3RV2QdJBAfPQ+dBBRRCcgsqTkOuO16VJ0zXWUXxeeJvjYsvwicbxwuj5tWzC5LQd3495RhZ+SvfssHFF2lBkYLqDpLPNeUgCeNDdTbnw3rcYt02YyGd3zUhLFkOOVXqCCsv4zBzUZZQC8ryjU0r3NvVrWMbxeduQdlbjmoCYt3tbFtQgcTC8nFxlHscV5ju+IZnHzmnU7TBrBhee78OlzDzuPOgbFI3yaHpevnKZbkjEubOrGjCzCUFFtImdJvBUVA2trUDNorFxtVh29N3dvEZxmDCenShFlSMWxbbxRfmaowVJJGOBeUaxWc7bcAGW2WvS4GjtaBaQRKKTGkESSSZ0qGOJbnMgwrmoNRfi83qA1H7rYNJFcVpE0BiigSNH8UXVEDGMSqaB9UdxNsexxpQkV0u+mNs5i7Z9vRdUh2p5YpjFpVy8EPu1JFcMcjn2B8rDwabj8s7Wazos3eN4gudeO3Y8ltPDC2XlGvXB0m0jolamiWOBeX6roqoUW8urm11UmxwPK1pQRnOzyPVESwUrslNHD+KL7j8ijH9WY8tWTgwCirtXHzyy2XqkVm8jDFcQjbfhjSoLs0lMb/Y4Yohuk4Vt1x8+p5iXDnSmnwoZ5KIPt42OMO14XcZlFdD7HVVte5PYFKvanXE0O5JLCjVPeni2g5O8tW7veJaUA3lO7JB7QzWLZScZNU29N9F0IIyPzfJhdde+gPa/XGCYrJkYBRUllF8Nkuq25jz1kESjklLVQVl6uWHK4Y4Pj77Q21djXFy8SUJknB9b2wVo6srxUUxBxrqkHlQkcuvxLh1SaL4RIJBEhEWlGJ5mMag4i7WJ3lhYnSU1NWtrSwow0KewZRWchnGeU5NBSQpMJoH1X3kgdvk5dm4cmwmGUZFUbXrkHKbuTWUoosvGIYsfgAh5cVQULZLYwBqT9F8XKwovkTzoPRlmpCWfUjRn28fXl8KJCDWWlCl1vHCRo0yizUPKoGLT40okztX4eeq41eqNdAZf9GfH53qyF3xqh0l1yg+03ehnhvq4tOEkUsKTAxDJwXVHZKEvuqwceXYZMIOs2hMWE3UFX4HLChDjykN15p8jv1J9mNh6dcdhnOKKVE5hFnEzhaUfYMYzMVnZ0Hpl3yPMwYltqRu54YlT4567wMrSUNttD3pf5XoIAnxAPeOEjzZhWYXOKWvv1TSW0at46JWMJDHqOy8ON1gYBSUiy/bBpvoO5uQUl+ezm+riECrhlLv4vMn+Ol7aGm41kS6G8UnlJ1WJgnHbOa263vZYJ89OzjGGBrFJx2rcYslHINyndIRFvUWbUHJDW0gF187QEB/fnSYeed3xXYeVIgF5bqirjp3yRztJ5YVXJZDVWCmDmsvMDAKyjVcOAqbCZm24xc2IdE2EYEiptQ4pbK5oQ17N7OO4vPrb/buwgZqY1hQSTxtrvc9qyg+l0F5q1RHmiAJXRRfHOJk/W7LFSJ70ig+T9inwyWKz/p5KGNQNha5OcpWOigk2EmuIzi1RK4rrSkZWTAwCirtIAkRY5CEZe/PlNhVxD3VkSCHMAblv5B6GUM/0JguPicrShMCq2K/5Hvnd2qpjiyqzi6Kz+64cjk4hhHa8QiJmgPi5RNMNg9K7e3r9+mQG+LgPTbl6BPrCyOOFybsesxLvnd+m1zw5ZLZixMWnKWbW5bW0jRZMDgKysL364KN31bcbJvo0jQNIUmqo5rq4osRxRd3iZKos+SIpaAc6rPKO9WRa922CyW6iqT2dHX1tY8rBxsl03mBSb2Gya0uJFnaRg3wiOv5CM8kYT4njDiyhLn4jEEShndIva8mSys0qSyCCkweN+0tDTUwCqquPLSk2Iw1JFnLRkXqvTmOhYgr6vqD6GK5oo87RMaYSj3qXutmsZsGcP3yLBVUSgO/rktH2LoW44zt6KoP3h+5Uaw3orKZCzJpXHxJo/iS5OKrWzboLVRXlnqLW0XZfK86kqc6UjJJGFc4MHUghXLLwWjNzjlyB0/1mKgKLKvI0zQYGAXlMp/CBqvABss6bRo15yAJqZESwszLwTGKFmlH8dmgXWhOikAqK8fblVtSPsq4uEbxqQ2+iThKU/cOqfenoutZa+rSRfF5XvD9i5eLL57Vo8rjR705KCjFtWkKmY8dxRfDCyMrB7sxTeP3qbQBpnZDnZISriTVqN6IC8qZAVJQnd95RfHZ1qkO7urLcuu9mYIkwlwDaac6skEnS9ouviRuC+eJuoZ7qxKn4ddZzmLyX/8gzYKFWguq1Dxcfo/TyMWnTmtwQVXwsps86tzwe98Sy+QpiHomcbwwkkzWCxZ2fsrzFOX7atNJDnZIlXI0C1z2EgOjoNKO4rPJAGEbomyzuJ5rfjN5oq7ikza8kKExEpkpqKAs8rXGLTedXmGS+x6+QrK7UDZjUNoFC7WZJFrHd7aJ74l4viuuofki6v1zmRyrjve4W1BRJpRYl7urWY3is1ny3ZPmN8n1G4MkApaWWIYcNKNmHiEF1SVE89g19FWHyf8rYuuWi1J26oCvaxSf6OIrlUpK6Ktcj1nG6DrjoPO3x8l5phL8KON9eHJja1Gv4VyVWBaUdgwq6AINixzrlBW0oOoa08JGzGCC2XCZwwiP4nM715T41jwGFV5+rDBz4XfwevRl6FbrVi3hkurik8ag5DpUBRQaDUi5+LqDa084CqtxI0sXX5SJrb7Uri4+VVGa3F+hS4nnYEGF9fTdyw1OUIyDq7K0nfSY2hiU4uILTCOIdPEJx2ruv41yV3fLqbUcLagQ2d1y8ZndlUaPR9SChTG8MGHL25jTnwW/T10bYJ7PqHpMZAUUUGAUxdd9kszN0GHjt7WtM2o8K47sUph5XW5kbQfy5fLs6nVFl1zVNXLORBq+ddWtEl2n5RhURlF8egtKo/jLreP174kka5TnSyk/2RiUWfbIbObCb10UXydKNNrjoSOOFyY4Ebqzz1SEzqugawPUaM3OOfL3oyogmgfVg6Sdi88u1ZFdQ6tOloxbjixf57cUxWfpuw6TMU2ioviS1JvGXKgkUXxpu/h090J18ekGz3XvlC5IQufia5URhvraiGNZro8vECQhRs45WVBBuVv33DwZPly2OF6Y4ArB0e2Qflw22AaY50EJ9evGJFUFFqPDmhcDo6DknnDy8myW25DM8rBcfNI5US4+d/nUjNBxovhsMqjHQvoYW/97ut3uRacwFypJFF9obzSGPLr6dS4+1b2pUzxRvXBJ1EgXn2pByWOeLqjuZ6mDENFaBTJoGLKZm4hqnON5MuTzRTeiqQydC17XBpg6Q+r3IyugiCEDUlDdIa01anRlGH3alkESUeMWcWQXO9bBeQ/6fWHvZlYWVEXzMUo91QT1Ro2v2NBw6MGrdeYTJBF08anH6qLzWtdiE3UYle5IvZa64lJ2IXz9pAgLKmLsreXeMnco7a8zbqojuyXfO79bw3m6NsDsCYF0bEDpK+0SLfneA8i9ijRcfJ3fNi6+8CCJcGWnzluwweg+0Ezc09WjkpF+0kYUppU3MQ0XnySLxddiW2echkA/UTdoQanH6hSMk+KPHIOS/65bdsx0yN4Et8AE2RIweyNsVh/QEccLE0x11Pnb7OKT7kL73PZ+RLn45O9HTb8V5tnpMf2EoTwrY4wtBfBJAK8AcBLAb3POv5BH3Wn1ylvYzYMS6gwbg4rodas+ZRuMiSjLai8/vO4Wuc6DskwRFV12chef67iDjes3rjy6Z19Ww8ybx5TKJaDub9O7+IIWlInILN/KdaqZS1wIpiuy76zYRC+qSkLaF2UpxvBkhGUzNwZJRKT/6lhQetlCw9EDSl9dmbe3NFSuCgrAxwHMAVgF4DoA/8QY28Y5fyqrChsNDzNzNVRrol88ebnqwOLUTDVYt6XPWnyBpmdrgbKmZ2vt33FS/ah1iQ3tzFynvjufen8AAA4KSURBVNm5unN5UUS97qIs001ZxGeVJIpPvFfTs7VY11Cru703qltR914AwGzVfK+NZWu2qZkkdOMTuro6Y1DRFyW+fzpURSDW53rPVSU0J5QVJauoDGZm66jWgm/f1EzN+J5Xa3Xj8/L3C+9CjAULq9WGnQUl1llvYGqmqm0D1CwgLdln5uR7JlZTqzcwM9cpS43iq9XN76yO4aEyhocq1se7kpuCYowtAPAmAFdzzicB3McY+waAnwPwW1nUeXZyFl///i6cuzAnbU9jTo9Yxly1jk/dZdaxUXOXxH3f+sHe0HrjJEuVtiuyfOfB/XblZWZBdcq9697doftdEe/Bl77DY5fTLs9CFtHlNjldDX0vnOvXRvGpY1BBF99jO04Ey9JEgpn44r+43TuxvnjriJXaSk8qK6J3Jl7Ldx/Sv9d/d/fTxvMf3n4cD28/bidjDE/Gtp3yczAGSQhl7z96PvAOdSZZy+fp3jW1Q3roxCQOnZiU5BPLOXxy0umdrZRLuOHqNbierbQ+x4U8x6C2AqhzzncI27YBuCqrCg8cOx9QTgAwOpJcL1cqZQwP2d2+0XnhPYyxEfseyKjlsaPz9Nc4OlJxqg8AhiplDFleq8qqJfND949F3Ju1KxbEqhcw34O4jFm8N8PDlcTKfNmiUe123fWMz58nH9OUMUrWVlmVcsn6PY5DnG/NdE7kdxSxP02iZGkfZ/jWxkaGjJ2DyGfXLLNUKkUeOzZSCf0ORkeGrN5rE/WGh+17T8c+P4o8XXzjAM4q284CWJhVhZeuvQi7D5/FiYnp9rZ1KxfiklXJq6yUS3jRNRfjUX5cMv1VhofKeMFVq0PLun7rSpydnMP5qaAyFRkbGcINV62xku+KjUtx6MQkTp7pXPuG1YuwdsU4xkaGcPrcLM5OzmrPXbJwBEsvGsPew2dRKpVwPVuBIds1rhVedM3F+P6jB9HwgBuvvRh832ls3zeBoUoZr7lxE4YqZUxOVzE5HXQrXLxiHM+7YhU8APuOnMOLrrnYqe4XXrUa9z9+ONJFZcPGNYuwZnm0shwZruCG56zB48+esFrsz/P8nvC6lePwPGBqpoqXPf8S7bHPu2IlLsxUcaF5r1YsGcOVm5ZiZLiCR/hxLFk4gss3LAEA3HD1GjzwxOG2O8vz0HbtXHPZcixf7CvBcrmEG69di4e3H2u/x/NHhrBu5UIcOH7e+t61jhMbu6WLRnHlpmVW54u8+Jo1ePDpY5J7b/XS+bjsksWh512zZQVOnZ3BGeW9nq3WMT42HPhOF4+P4EXXXIyHnjmG4xNT1vKtXTGOjWsWWR172brF2HP4HI6eutDeNjxUxguuNLcJq5bOx1WXLsPuQ2pz6d/fFwptwIuvuRgPPn1U2waNzx/G9Wwlli8exTWXLcfOg2elMabxsWE87/KVWHbRGK7dsgLPHjjjPAY1MlzBT4RcS1JKeQ2KMcauB3A/53y+sO3XANzMOX+t4ZyNAPZ897vfxbp163KRkyAIgsiPgwcP4tZbbwWATZzzveK+PF18OwAMMca2CNuuBZBZgARBEATRv+SmoDjnFwB8DcAHGWMLGGMvBvB6AJ/NSwaCIAiif8h7ou57AIwBOA7giwDenWWIOUEQBNG/5DoPinN+GsAb8qyTIAiC6E8GJtURQRAE0V+QgiIIgiB6ElJQBEEQRE+Sdy4+VyoAcPTo0W7LQRAEQWSA0L4H0m70uoJaAwC33357t+UgCIIgsmUNgF3ihl5XUD8GcBOAI2gvHkAQBEEUiAp85fRjdUduqY4IgiAIwgUKkiAIgiB6ElJQBEEQRE9CCoogCILoSUhBEQRBED0JKSiCIAiiJyEFRRAEQfQkvT4PKhGMsaUAPgngFQBOAvhtzvkXuiuVG4yx9wK4A8BzAHyRc36HsO9WAB8HsB7AjwDcwTnf19w3AuAvAbwZwBSAD3POP5qr8JY0Zf0EgNsALAWwE8B/55x/q7m/KNf5OQC3AlgA4Ch8Wf+2ua8Q19iiuTDpEwC+yjn/2ea2dwD4EIDlAL4D4OebKxz03bfKGLsHwA0Aas1NhzjnrLmvSNf5NgC/B/+9PAr/vbw3r/e16BbUxwHMAVgF4HYAf8kYu6q7IjlzGMAfAPiUuJExthz+ApC/A79RfwjAl4VD7gSwBcAGAC8D8BuMsZ/KQd44DAE4AOClAC6Cf01/zxjbWLDr/BCAjZzzRQBeB+APGGPPK9g1tvg4hImXze/urwD8HPzvcQp+p0Q8vt++1fdyzseb/1rKqTDXyRh7OYA/BvBOAAsBvATA7jzf18JaUIyxBQDeBOBqzvkkgPsYY9+A/+L8VleFc4Bz/jUAYIw9H8A6YdcbATzFOf9Kc/+dAE4yxi7nnG8H8B8BvJNzPgFggjH2N/AtsX/OUXwrmqst3yls+iZjbA+A5wFYhuJcp7g4p9f8txn+dRbiGoF2r/sMgAcAXNbcfDuAuzjn328e8zsAnmGMLQTQQAG+1SZFus7/CeCDnPMfNv8+BACMsf+EnN7XIltQWwHUOec7hG3bAPRkbyUGV8G/HgDtRn4XgKsYY0sAXCzuRx9dO2NsFfzn9xQKdp2MsU8wxqYAbIefwutuFOgaGWOLAHwQwK8pu9Rr3AXfktiK/v1WP8QYO8kYu58xdnNzWyGukzFWAfB8ACsYYzsZYwcZY3/BGBtDju9rkRXUOICzyraz8E3VIhB2fePC3+q+noYxNgzg8wA+0+yNFeo6OefvgS/fTfDdJLMo1jX+PoBPcs4PKNujrrHfvtXfBHApgLUA/hrAXYyxzSjOda4CMAx/HOkmANcBuB7AB5Dj+1pYFx+ASQCLlG2LAJzvgixZEHZ9k8LfM8q+noUxVgbwWfg9zvc2NxfuOjnndfjunZ8F8G4U5BoZY9fBD3S5XrM77BobIft6Es75j4Q/P8MYezuAV6M41znd/P9jnPMjAMAY+yh8BfV95PS+FtmC2gFgqBlN1OJa+G6jIvAU/OsB0B5z2wzfNzwB3310rXB8T187Y6wEP7ppFYA3cc6rzV2Fuk6FITSvBcW4xpsBbASwnzF2FMD7AbyJMfYIgtd4KYAR+N9pEb5VD0AJBbnO5nt3EP51qeT2vhY6mzlj7Evwb/AvwjdR7wbwImWwuqdhjA3Bb8h+D36QxC/BD21dAj8c++cB/BP8Ac2Xcs5vaJ73vwD8JIA3wG/0vwd/4LJXB9b/N/xndFtzALm1fQUKcJ2MsZUAbgHwTfi909vgu/jeAT+YoAjXOB9yz/r98BXWuwGsBPADAD8N4BH4kW5DnPO3Nc/tm2+VMbYYwAsB/Dv8b/Gt8N18z4X/rRblOj8I4FXwr6UK4BsA7gHw58jpfS2yBQUA7wEwBuA4gC8CeHcvvggRfAB+g/ZbAH62+fsDnPMT8COC/hDABPwP5m3Ceb8Hf+ByH/wP6U96rUFrwRjbAOBd8D/Yo4yxyea/2wt0nR78hvog/Ov4CID3cc6/XpRr5JxPcc6Ptv7Bd/fMcM5PNL+7/wx/fPE4/DGJ9win99O3Ogx/6scJ+HOZfgXAG7hPka7z9+FPFdgB4BkAjwL4wzzf10JbUARBEET/UnQLiiAIguhTSEERBEEQPQkpKIIgCKInIQVFEARB9CSkoAiCIIiehBQUQRAE0ZOQgiKICBhjn2aMfboL9S5sJulcF320sYw7m2sX2R7/QsbYk820UwTRVYqci48gImGMRU0E3ATgv+Qhi4b/AuBfOOcHE5TxEfgz/63gnP+IMXYcwNvhTzYliK5BCooYdNYIv98P4EXw19pqcaKZ3DVXmhbMu+CvrRMbMW2UA59r1k0KiugqpKCIgaaZkgcAwBibBDAnbmtu/3Tz2Duaf+8F8DH4KwDfBj8NzFvgW1t/AWA1gE8D+K+cc695zgoAfwo/r1kN/uJtv9paClzDDfDzLX5fkOMO+As7fgDAH8FfffgjAP4E/kqtb4GfXubnOOePNs+5E8DNnPObm3/fAz9X3HL46WlOAviN1uJzTe4G8EnG2CrO+TGDfASROeRnJoh4vB++hfFc+Mt6fx7Ar8Nv9N8GPx/bq4XjvwqgDn9tnZvhK5/PhJT/IgDbNNbbSvhJOH8KwC/DXxzw6wAehL8y7zPwE5eG8W4AT8JfFuNzAD7dXMYbQFtpH27KQBBdgxQUQcTjy5zzLzcXVfwL+Akzf4Nz/lgzMeb3ALwEABhjL4G/9Pk7OedPcM6fhJ+V/jWMsdWG8tfDX7ZAZQTAuzjnT3POPwd/Zd4pzvlfNVdq/QiA5zczi5v4Puf8zzjnO+Fnoi7BXz1V5CiADZF3gSAyhFx8BBEPMQP18eb/TyvbVjR/Pwe+2+8sY0wt51L4ykBlFP5quypHOeenlHrUegHfhbffIPsTrR+c8xpj7AR8y0xkBn7WbYLoGqSgCCIeVeG3BwDCIoutbS0PxTh8S+f1mnIOGco/BX858bB6W/UEZEG4d0RXhnr8EvjjUwTRNUhBEUT2bIO/cN8Zzrlto/84/KCH3GGMDcO37LZ1o36CaEFjUASRPf8C3yX4NcbYjYyxSxljr2iuImziHgAbQsaosuR5AC7AXxGWILoGKSiCyBjOeQN+1N2zAP4RvrL6c/irkZrOOQJfsf1MHjIq/AyAL3DOa12omyDa0Iq6BNGjMMZuAvBXAK5qzafKoc5x+Ir0xZzz3XnUSRAmyIIiiB6Fc34v/Am4a6KOTZH1AN5PyonoBciCIgiCIHoSsqAIgiCInoQUFEEQBNGTkIIiCIIgehJSUARBEERPQgqKIAiC6ElIQREEQRA9yf8F8EsbQUJP434AAAAASUVORK5CYII=\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "system = make_system(lam, mu)\n", - "run_simulation(system, update_func3)\n", - "plot(results)\n", - "decorate(xlabel='Time (min)', ylabel='Customers')\n", - "compute_metrics(results, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Sweep a range of values for `lam`, plot the results, and print the average wait time across all values of `lam`.\n", - "\n", - "How do the results compare to the scenario with two servers and one queue." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Average of averages = 6.753981831564979 minutes\n" - ] - } - ], - "source": [ - "# Solution\n", - "\n", - "sweep = sweep_lam(lam_array, mu, update_func3)\n", - "W_avg = sweep.mean()\n", - "print('Average of averages = ', W_avg, 'minutes')" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot(sweep, 'bo')\n", - "decorate(xlabel='Arrival late, lambda (per min)',\n", - " ylabel='Average time in system',\n", - " title='Multiple server, multiple queue')" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "\"\"\"\n", - "With two queues, the average of averages is slightly higher, most of the time. But the difference is small.\n", - "\n", - "The two configurations are equally good as long as both servers are busy; the only time two lines is worse is if one queue is empty and the other contains more than one customer. In real life, if we allow customers to change lanes, that disadvantage can be eliminated.\n", - "\n", - "From a theoretical point of view, one line is better. From a practical point of view, the difference is small and can be mitigated. So the best choice depends on practical considerations.\n", - "\n", - "On the other hand, you can do substantially better with an express line for customers with short service times. But that's a topic for another notebook.\n", - "\"\"\";" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/rabbits2soln.ipynb b/code/soln/rabbits2soln.ipynb deleted file mode 100644 index e4457bd29..000000000 --- a/code/soln/rabbits2soln.ipynb +++ /dev/null @@ -1,528 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Rabbit example\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Rabbit Redux\n", - "\n", - "This notebook starts with a version of the rabbit population growth model and walks through some steps for extending it.\n", - "\n", - "In the original model, we treat all rabbits as adults; that is, we assume that a rabbit is able to breed in the season after it is born. In this notebook, we extend the model to include both juvenile and adult rabbits.\n", - "\n", - "As an example, let's assume that rabbits take 3 seasons to mature. We could model that process explicitly by counting the number of rabbits that are 1, 2, or 3 seasons old. As an alternative, we can model just two stages, juvenile and adult. In the simpler model, the maturation rate is 1/3 of the juveniles per season.\n", - "\n", - "To implement this model, make these changes in the System object:\n", - "\n", - "1. Replace `p0` with two initial populations: `juvenile_pop0` and `adult_pop0`, with values `0` and `10`.\n", - "\n", - "2. Add an additional variable, `mature_rate`, with the value `0.33`." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
t00.00
t_end30.00
juvenile_pop00.00
adult_pop010.00
birth_rate0.90
mature_rate0.33
death_rate0.50
\n", - "
" - ], - "text/plain": [ - "t0 0.00\n", - "t_end 30.00\n", - "juvenile_pop0 0.00\n", - "adult_pop0 10.00\n", - "birth_rate 0.90\n", - "mature_rate 0.33\n", - "death_rate 0.50\n", - "dtype: float64" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = System(t0 = 0, \n", - " t_end = 30,\n", - " juvenile_pop0 = 0,\n", - " adult_pop0 = 10,\n", - " birth_rate = 0.9,\n", - " mature_rate = 0.33,\n", - " death_rate = 0.5)\n", - "\n", - "system" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now update `run_simulation` with the following changes:\n", - "\n", - "1. Add a second TimeSeries, named `juveniles`, to keep track of the juvenile population, and initialize it with `juvenile_pop0`.\n", - "\n", - "2. Inside the for loop, compute the number of juveniles that mature during each time step.\n", - "\n", - "3. Also inside the for loop, add a line that stores the number of juveniles in the new `TimeSeries`. For simplicity, let's assume that only adult rabbits die.\n", - "\n", - "4. During each time step, subtract the number of maturations from the juvenile population and add it to the adult population.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def run_simulation(system):\n", - " \"\"\"Runs a proportional growth model.\n", - " \n", - " Adds TimeSeries to `system` as `results`.\n", - " \n", - " system: System object with t0, t_end, p0,\n", - " birth_rate and death_rate\n", - " \"\"\"\n", - " juveniles = TimeSeries()\n", - " juveniles[system.t0] = system.juvenile_pop0\n", - " \n", - " adults = TimeSeries()\n", - " adults[system.t0] = system.adult_pop0\n", - " \n", - " for t in linrange(system.t0, system.t_end):\n", - " maturations = system.mature_rate * juveniles[t]\n", - " births = system.birth_rate * adults[t]\n", - " deaths = system.death_rate * adults[t]\n", - " \n", - " if adults[t] > 30:\n", - " market = adults[t] - 30\n", - " else:\n", - " market = 0\n", - " \n", - " juveniles[t+1] = juveniles[t] + births - maturations\n", - " adults[t+1] = adults[t] + maturations - deaths - market\n", - " \n", - " system.adults = adults\n", - " system.juveniles = juveniles" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your changes in `run_simulation`:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
010.000000
15.000000
25.470000
36.209900
47.057723
58.021560
69.117031
710.362107
811.777219
913.385586
1015.213601
1117.291261
1219.652658
1322.336542
1425.386953
1528.853947
1632.794414
1734.478600
1836.487431
1937.893339
2039.401924
2140.546917
2241.694992
2342.613800
2443.495581
2544.226171
2644.907656
2745.485241
2846.014130
2946.469075
3046.880673
3147.238170
\n", - "
" - ], - "text/plain": [ - "0 10.000000\n", - "1 5.000000\n", - "2 5.470000\n", - "3 6.209900\n", - "4 7.057723\n", - "5 8.021560\n", - "6 9.117031\n", - "7 10.362107\n", - "8 11.777219\n", - "9 13.385586\n", - "10 15.213601\n", - "11 17.291261\n", - "12 19.652658\n", - "13 22.336542\n", - "14 25.386953\n", - "15 28.853947\n", - "16 32.794414\n", - "17 34.478600\n", - "18 36.487431\n", - "19 37.893339\n", - "20 39.401924\n", - "21 40.546917\n", - "22 41.694992\n", - "23 42.613800\n", - "24 43.495581\n", - "25 44.226171\n", - "26 44.907656\n", - "27 45.485241\n", - "28 46.014130\n", - "29 46.469075\n", - "30 46.880673\n", - "31 47.238170\n", - "dtype: float64" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run_simulation(system)\n", - "system.adults" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, update `plot_results` to plot both the adult and juvenile `TimeSeries`." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def plot_results(system, title=None):\n", - " \"\"\"Plot the estimates and the model.\n", - " \n", - " system: System object with `results`\n", - " \"\"\"\n", - " newfig()\n", - " plot(system.adults, 'bo-', label='adults')\n", - " plot(system.juveniles, 'gs-', label='juveniles')\n", - " decorate(xlabel='Season', \n", - " ylabel='Rabbit population',\n", - " title=title)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And test your updated version of `plot_results`." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/png": "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\nmTPp27evvYtmV4cPH2b+/PmkpKRQVlZG165dmTVrFr169bJ30ZrFbbfdxqlTp9A0jcrKSlxdXdHp\ndNxxxx28+OKLbN68mTfffJPMzEwMBgNjx45lypQpbfaPFzT+njzzzDO8/fbbrF+/nrNnz2I0Ghk/\nfjwTJkzA2dnZ3kW/anbt2sV9991nfS9qc9R75WLviaPeKxkZGbz22mskJyfj5OREdHQ0jzzyCEOG\nDAFo9vvE4cNfCCGEcDQO3ewvhBBCOCIJfyGEEMLBSPgLIYQQDkbCXwghhHAwEv5CCCGEg5HwF0II\nIRyMi70LIISwn5SUFN5//31SU1M5d+6cdTvRcePGMWLECHsXTwhxlUjNXwgHtWPHDsaNG4ezszP/\n+7//y9atW1m5ciWdO3dm+vTprF692t5FFEJcJVLzF8JBffTRR4SEhLBgwQLrKmKhoaF0796dsrIy\nfvnlFzuXUAhxtUj4C+GgysvLqa6uprKyEjc3N5tz8+fPt36vaRrvv/8+a9eu5fjx43h7e/O73/2O\nJ5980manthUrVvDJJ5+QlZWFl5cXCQkJzJgxgy5dulhfZ+nSpaxdu5bTp0+j1+vp168fs2bNIiIi\nAgCTycTf//53Nm7cyPnz5wkICGDIkCFMnz4dHx8fAO6//34CAgJITExk0aJFnDhxgoiICKZPn87g\nwYOv9tsmRJsgzf5COKhBgwZx5swZxo8fz+bNmykuLq73uiVLlvD6668zcuRIvvrqK55//nm++eYb\nZs6cab1m7dq1vPrqq0yYMIHNmzezcuVKnJycePjhhykvLwfgs88+Y+nSpcyYMYNNmzaxbNkyCgsL\nmTJlivV15s6dy2effcaTTz7Jhg0bePbZZ9m6dSt/+ctfbMp08OBB1qxZw/z58/nss8/w9fVlxowZ\nlJSUXIV3Sog2SBNCOCSz2awtWrRI69GjhxYbG6vFx8dro0aN0hYuXKhlZmZqmqZpFRUVWp8+fbRZ\ns2bZPPeLL77QYmNjtYyMDE3TNK2goEBLT0+3ueb777/XYmNjtdTUVE3TNO3ZZ5/VEhMTba7JycnR\n9u7dq1VXV2vZ2dlaXFyctnz5cptrPvroIy02NlY7evSopmmaNn78eC0hIUHLycmxXvP1119rsbGx\n2p49e377GyOEA5CavxAOSqfTMW3aNLZv384bb7zB3XffTXFxMe+88w6JiYl8+umnHD58mOLiYq67\n7jqb5w4cOBCAffv2AeDp6cn333/PXXfdxbXXXkvv3r2ZNm0aoLZwBRg8eDCZmZlMnDjR2vQfGBhI\nQkICTk5O/PLLL2iaRp8+fWx+V8+ePQHYv3+/9ViHDh0IDAy0/mz53vK7hBCNkz5/IRycj48Pt99+\nO7fffjsAe/fuZcaMGbzwwgu89957ADz99NM8++yzdZ577tw5AF577TVWrVrFtGnTGDx4MN7e3qSm\npjJjxgzrtTfddBMffPABH3zwAS+99BJFRUX07NmTWbNm0bdvX2u3g7e3t83v8PLyArBp0q891gCw\nDljUZJNSIS6JhL8QDspkMgHg7u5uc7x79+488cQTPP7445jNZgBmzJjBoEGD6ryGn58fAOvWrWP4\n8OHW2j6oDxEX6tevH/369aOqqork5GTeeustJk+ezL///W/rgL6ioiKb51h+9vX1vdJ/qhDiAtLs\nL4QDOnv2LP369WPJkiX1nj9x4gQAkZGR+Pr6curUKTp06GB9hIWFYTab8ff3B6CiooKAgACb11i7\ndi1QUxvftm0bGRkZALi4uDBgwADmzJlDSUkJR48epVu3bjg5OZGcnGzzOrt370an05GQkNB0b4AQ\nDk5q/kI4oODgYO677z6WLl2KyWTitttuw2g0UlRUxA8//MBbb73FPffcQ2hoKA899BCLFy8mIiKC\n66+/nuLiYpYtW8aOHTvYtGkT/v7+9O7dm82bNzNixAi8vLz4xz/+Qfv27QFITU2ld+/efP755+zf\nv5+//vWvREdHU1xczIoVKzAYDMTExODt7c3IkSNZunQp7dq1o3v37uzdu5dFixYxfPhwwsPD7fyu\nCdF2SPgL4aBmz55Nt27d+Oyzz1i3bh15eXl4enrSuXNnnn76ae6++24ApkyZgqenJx988AEvv/wy\n7u7uXHvttaxatcpa83/22Wd5+umnmTBhAn5+ftx7771MmTKFvLw8li1bhouLCy+88AILFizgqaee\nIicnB19fX3r27Ml7771n7ed/4YUXCAwM5NVXXyUnJweDwcCoUaPqTPUTQvw2Ok1GyAghhBAORfr8\nhRBCCAcj4S+EEEI4GAl/IYQQwsFI+AshhBAORsJfCCGEcDAS/kIIIYSDkfAXQgghHIyEvxBCCOFg\nJPyFEEIIB/P/I/Jd/ZYeqlQAAAAASUVORK5CYII=\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_results(system, title='Proportional growth model')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This notebook demonstrates the steps we recommend for starting your project:\n", - "\n", - "1. Start with one of the examples from the book, either by copying a notebook or pasting code into a new notebook. Get the code working before you make any changes.\n", - "\n", - "2. Make one small change, and run the code again.\n", - "\n", - "3. Repeat step 2 until you have a basic implementation of your model.\n", - "\n", - "If you start with working code that you understand and make small changes, you can avoid spending a lot of time debugging.\n", - "\n", - "One you have a basic model working, you can think about what metrics to measure, what parameters to sweep, and how to use the model to predict, explain, or design." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Bonus question\n", - "\n", - "Suppose you only have room for 30 adult rabbits. Whenever the adult population exceeds 30, you take any excess rabbits to market (as pets for kind children, of course). Modify `run_simulation` to model this strategy. What effect does it have on the behavior of the system?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "What happens if rabbits only take one season to mature? Change `mature_rate` to 1.0 and run the simulation again." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.1" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/code/soln/rabbits3soln.ipynb b/code/soln/rabbits3soln.ipynb deleted file mode 100644 index 431fd14fb..000000000 --- a/code/soln/rabbits3soln.ipynb +++ /dev/null @@ -1,863 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Rabbit example\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Rabbit is Rich\n", - "\n", - "This notebook starts with a version of the rabbit population growth model. You will modify it using some of the tools in Chapter 5. Before you attempt this diagnostic, you should have a good understanding of State objects, as presented in Section 5.4. And you should understand the version of `run_simulation` in Section 5.7.\n", - "\n", - "### Separating the `State` from the `System`\n", - "\n", - "Here's the `System` object from the previous diagnostic. Notice that it includes system parameters, which don't change while the simulation is running, and population variables, which do. We're going to improve that by pulling the population variables into a `State` object." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
t00.00
t_end20.00
juvenile_pop00.00
adult_pop010.00
birth_rate0.90
mature_rate0.33
death_rate0.50
\n", - "
" - ], - "text/plain": [ - "t0 0.00\n", - "t_end 20.00\n", - "juvenile_pop0 0.00\n", - "adult_pop0 10.00\n", - "birth_rate 0.90\n", - "mature_rate 0.33\n", - "death_rate 0.50\n", - "dtype: float64" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = System(t0 = 0, \n", - " t_end = 20,\n", - " juvenile_pop0 = 0,\n", - " adult_pop0 = 10,\n", - " birth_rate = 0.9,\n", - " mature_rate = 0.33,\n", - " death_rate = 0.5)\n", - "\n", - "system" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the following cells, define a `State` object named `init` that contains two state variables, `juveniles` and `adults`, with initial values `0` and `10`. Make a version of the `System` object that does NOT contain `juvenile_pop0` and `adult_pop0`, but DOES contain `init`." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
juveniles0
adults10
\n", - "
" - ], - "text/plain": [ - "juveniles 0\n", - "adults 10\n", - "dtype: int64" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "init = State(juveniles=0, adults=10)\n", - "init" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
t00
t_end20
initjuveniles 0\n", - "adults 10\n", - "dtype: int64
birth_rate0.9
mature_rate0.33
death_rate0.5
\n", - "
" - ], - "text/plain": [ - "t0 0\n", - "t_end 20\n", - "init juveniles 0\n", - "adults 10\n", - "dtype: int64\n", - "birth_rate 0.9\n", - "mature_rate 0.33\n", - "death_rate 0.5\n", - "dtype: object" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "system = System(t0 = 0, \n", - " t_end = 20,\n", - " init = init,\n", - " birth_rate = 0.9,\n", - " mature_rate = 0.33,\n", - " death_rate = 0.5)\n", - "\n", - "system" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Updating `run_simulation`\n", - "\n", - "Here's the version of `run_simulation` from last time:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def run_simulation(system):\n", - " \"\"\"Runs a proportional growth model.\n", - " \n", - " Adds TimeSeries to `system` as `results`.\n", - " \n", - " system: System object\n", - " \"\"\"\n", - " juveniles = TimeSeries()\n", - " juveniles[system.t0] = system.juvenile_pop0\n", - " \n", - " adults = TimeSeries()\n", - " adults[system.t0] = system.adult_pop0\n", - " \n", - " for t in linrange(system.t0, system.t_end):\n", - " maturations = system.mature_rate * juveniles[t]\n", - " births = system.birth_rate * adults[t]\n", - " deaths = system.death_rate * adults[t]\n", - " \n", - " if adults[t] > 30:\n", - " market = adults[t] - 30\n", - " else:\n", - " market = 0\n", - " \n", - " juveniles[t+1] = juveniles[t] + births - maturations\n", - " adults[t+1] = adults[t] + maturations - deaths - market\n", - " \n", - " system.adults = adults\n", - " system.juveniles = juveniles" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the cell below, write a version of `run_simulation` that works with the new `System` object (the one that contains a `State` object named `init`).\n", - "\n", - "Hint: you only have to change two lines." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def run_simulation(system):\n", - " \"\"\"Runs a proportional growth model.\n", - " \n", - " Adds TimeSeries to `system` as `results`.\n", - " \n", - " system: System object \n", - " \"\"\"\n", - " juveniles = TimeSeries()\n", - " juveniles[system.t0] = system.init.juveniles\n", - " \n", - " adults = TimeSeries()\n", - " adults[system.t0] = system.init.adults\n", - " \n", - " for t in linrange(system.t0, system.t_end):\n", - " maturations = system.mature_rate * juveniles[t]\n", - " births = system.birth_rate * adults[t]\n", - " deaths = system.death_rate * adults[t]\n", - " \n", - " if adults[t] > 30:\n", - " market = adults[t] - 30\n", - " else:\n", - " market = 0\n", - " \n", - " juveniles[t+1] = juveniles[t] + births - maturations\n", - " adults[t+1] = adults[t] + maturations - deaths - market\n", - " \n", - " system.adults = adults\n", - " system.juveniles = juveniles" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your changes in `run_simulation`:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
value
010.000000
15.000000
25.470000
36.209900
47.057723
58.021560
69.117031
710.362107
811.777219
913.385586
1015.213601
1117.291261
1219.652658
1322.336542
1425.386953
1528.853947
1632.794414
1734.478600
1836.487431
1937.893339
2039.401924
2140.546917
\n", - "
" - ], - "text/plain": [ - "0 10.000000\n", - "1 5.000000\n", - "2 5.470000\n", - "3 6.209900\n", - "4 7.057723\n", - "5 8.021560\n", - "6 9.117031\n", - "7 10.362107\n", - "8 11.777219\n", - "9 13.385586\n", - "10 15.213601\n", - "11 17.291261\n", - "12 19.652658\n", - "13 22.336542\n", - "14 25.386953\n", - "15 28.853947\n", - "16 32.794414\n", - "17 34.478600\n", - "18 36.487431\n", - "19 37.893339\n", - "20 39.401924\n", - "21 40.546917\n", - "dtype: float64" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run_simulation(system)\n", - "system.adults" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Plotting the results\n", - "\n", - "Here's a version of `plot_results` that plots both the adult and juvenile `TimeSeries`." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def plot_results(system, title=None):\n", - " \"\"\"Plot the estimates and the model.\n", - " \n", - " system: System object with `results`\n", - " \"\"\"\n", - " newfig()\n", - " plot(system.adults, 'bo-', label='adults')\n", - " plot(system.juveniles, 'gs-', label='juveniles')\n", - " decorate(xlabel='Season', \n", - " ylabel='Rabbit population',\n", - " title=title)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If your changes in the previous section were successful, you should be able to run this new version of `plot_results`." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/png": "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\n/vWvxMbG4u3tzZgxY4iPj+ejjz6yKf/xxx8TFxfHPffcg6+vLx06dODhhx/miy++qNlT3llKSkqI\nj49n9uzZ6PV6vL29mTRpEuXl5ezZs8epdWtqzfm62DNnzhxSUlLo37+/s6vSqMLCQlauXMmoUaNs\njn3wwQcMHjyYlJQUvLy86Nq1KxMmTOD999/HbDbblD98+DDbtm1jxowZREVF4e/vz5QpU9DpdHzx\nxReOOJ1Gz6eqqorp06fTv39/3N3d6du3LwMGDODnn392SN3OV2Pncr6cfW3O51w2bdpEamoqs2bN\navJ6na9zfQa3lPeMSwT3ffv2YTQa6dmzp9XjvXr1Ii0tzab87t276dWrl01Zk8nEvn37mrSu5xIY\nGMi8efOIj4+veSwzMxOAqKgou88pKyvjkUceYeDAgQwaNIjZs2fbbJvrTBs2bODPf/4zffv25dZb\nb2XTpk12yzXn61Lff//7X3755RemTZvWYJnmcl1Gjx5tdwgEGv6dFxYWkpGRYVM+LS0NT09PLrvs\nsprHPDw86N69u933WlNo7Hz+8pe/cOedd9b8rGka2dnZtG3bttHXfP/997nuuutITk7mrrvuYseO\nHZe0zg1p7FxA5Q+/9957ueKKKxg6dCgvvfQSBoPBbllnX5tznYuFwWDgueee44knniAoKKjRss64\nLuf6DG4p7xmXCO6WVl1ISIjV46GhoeTl5dktHxwcbFMWsFvemUpLS5k1axbXXnutzZcXUPWOj49n\n3LhxbNmyhSVLlrBr1y6mT5/uhNraSkhIIC4ujg8++IAffviB6667jilTpths6wst57qYzWYWLlzI\nAw88QEBAgN0yzf26WDT2O7fXW2IpX38cNCQkpFldI4vXX3+dkydP2p0vYdG9e3e6d+/O2rVr+eab\nb+jatSv33XcfWVlZDqyprTZt2tCxY0cef/xxtm7dyksvvcS6dets5ndYtJRrs2LFCkJCQrjxxhsb\nLddcrkv9z+CW8p5xieDemPq/0EtdvillZ2dz1113ER4ezoIFC+yWGTJkCB9++CEDBw7Ew8ODxMRE\npk2bxubNm8nJyXFwjW299dZbzJo1i7CwMAICAnjooYdITEzkk08+Oa/XaU7XZePGjZw+fbrRzYqa\n+3VpCs3pGlVXVzN37lzef/99lixZYjXhrr41a9bw0EMPERAQQGhoKE899RT+/v58/vnnDqyxrTvv\n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\nrkJggg==\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_results(system, title='Proportional growth model')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "That's the end of the diagnostic. If you were able to get it done quickly, and you would like a challenge, here are two bonus questions:\n", - "\n", - "\n", - "### Bonus question #1\n", - "\n", - "Write a version of `run_simulation` that puts the results into a single `TimeFrame` named `results`, rather than two `TimeSeries` objects.\n", - "\n", - "Write a version of `plot_results` that can plot the results in this form.\n", - "\n", - "WARNING: This question is substantially harder, and requires you to have a good understanding of everything in Chapter 5. We don't expect most people to be able to do this exercise at this point." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def run_simulation(system):\n", - " \"\"\"Runs a proportional growth model.\n", - " \n", - " Adds TimeSeries to `system` as `results`.\n", - " \n", - " system: System object \n", - " \"\"\"\n", - " results = TimeFrame(columns = system.init.index)\n", - " results.loc[system.t0] = system.init\n", - " \n", - " for t in linrange(system.t0, system.t_end):\n", - " juveniles, adults = results.loc[t]\n", - "\n", - " maturations = system.mature_rate * juveniles\n", - " births = system.birth_rate * adults\n", - " deaths = system.death_rate * adults\n", - " \n", - " if adults > 30:\n", - " market = adults - 30\n", - " else:\n", - " market = 0\n", - " \n", - " juveniles += births - maturations\n", - " adults += maturations - deaths - market\n", - " \n", - " results.loc[t+1] = juveniles, adults\n", - " \n", - " system.results = results" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "run_simulation(system)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def plot_results(system, title=None):\n", - " \"\"\"Plot the estimates and the model.\n", - " \n", - " system: System object with `results`\n", - " \"\"\"\n", - " newfig()\n", - " plot(system.results.adults, 'bo-', label='adults')\n", - " plot(system.results.juveniles, 'gs-', label='juveniles')\n", - " decorate(xlabel='Season', \n", - " ylabel='Rabbit population',\n", - " title=title)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\nSxe2bdvGb37zGz755BPCwq6+XXzmjpkAZJVkkZJXv9JLn4A+dNV3vWH5FkIIIWyFTT25FxYWsmPH\nDn7/+98TGhqKq6srkyZNIjw8nPfff/+az1tcVcyZvDOm18FewQR5Bt2ILAshhBA2x6aC+9GjR6mu\nrmbAgAFm+wcOHEhSUlITR11ean4qdagFYbycvejl1+u68imEEELYMpsK7nl5eQD4+vqa7ffz8yM3\nN/eaz1ur1QLgpHMi0hApE9UIIYSwazbX5t6U6+nRHhkYSW55LgHuAdKBTgghhN2zqUfYgIAAAAoK\nCsz25+fnExgYeM3ndXNyo6t3VwnsQgghOgSbCu5RUVG4uLhw+PBhs/0HDx4kNja2jXIlhBBCtC82\nVS3v7e3N+PHjSUxMJCIiguDgYDZt2kRmZiaTJk26pnMuH7v8BudSCCGEsG02FdwB5s+fz5IlS5g8\neTKlpaVERkaycuVKunZtekx6ba3qMHfhwoXWyqYQQgjRZozxzhj/GtNpmqa1ZoZawv79+3nkkUfa\nOhtCCCFEq9q4caPVZmu7CO4VFRX8+OOPGAwGHB0d2zo7QgghRIuqra0lJyeHqKgo3NwsO4vbRXAX\nQgghRD2b6i0vhBBCiOsnwV0IIYSwMxLchRBCCDsjwV0IIYSwMxLchRBCCDtjc5PYXKvy8nIWL17M\nnj17KCwspFevXjz11FPceuutVtN/++23JCYmcvr0aby9vbntttuYN28e7u7urZxzS7m5uSxdupSv\nv/6asrIyevXqxTPPPMPw4cMt0m7dupV58+bh4uJitj8uLo4lS5a0VpabNGrUKC5evIiDg/n3yO3b\ntxMaGmqR3lbLZd++fUyfPt1if01NDffddx9/+ctfzPbbWrmkp6czf/58fvjhB7744gu6detmeu9f\n//oXq1atIi0tDYPBQFxcHE899VSTw0rz8vJYtGgR+/bto7y8nMjISObMmUNUVFRrXU6z17Nx40Y2\nbtxIVlYWfn5+3HfffcyaNcvib9CoT58+ODs7WyxOdeDAAYvyawlNXUtiYiJ///vfcXZ2Nkv/2GOP\n8fTTT1s9V1uXTVPXcvfdd3P+/HmztJqmUV1dzcmTJ62eqy3L5XL34HbxmdHsxNy5c7V7771XO3v2\nrFZRUaFt3rxZi4qK0s6cOWORNjU1VYuKitLWr1+vlZWVaT/99JN2//33a3Pnzm2DnFuaOHGiNn36\ndC07O1urqKjQli5dqt10003ahQsXLNJ+9NFH2siRI9sgl1dm5MiR2kcffXRFaW29XBrLzs7Whg4d\nqu3du9fiPVsql127dmnDhw/X5syZo0VERGjp6emm9/bu3av1799f27lzp1ZZWamdOHFC++Uvf6kl\nJiY2eb6pU6dq06ZN07KysrSSkhLtjTfe0IYOHarl5eW1xuU0ez2bN2/WYmJitL1792o1NTXa/v37\ntejoaG3t2rVNni8iIkL7/vvvWyPrFpq7ljfffFObMmXKVZ2vLcumuWux5plnnmn2s92W5dLcPbi9\nfGbsolq+sLCQHTt28Pvf/57Q0FBcXV2ZNGkS4eHhvP/++xbpP/jgA8LCwpg6dSru7u50796d3/72\nt2zfvt20pnxbKS4uJjw8nPnz52MwGHB1deWJJ56grKyMI0eOtGneWpotl4s1CxYsIC4ujqFDh7Z1\nVppVUFDAxo0bGTdunMV7GzZsYMSIEcTFxeHi4kKfPn2YNm0a7733HnV1dRbpT506xd69e5kzZw7B\nwcF4enoya9YsdDod27dvb43LafZ6qqqqSEhIYOjQoTg6OhITE8OwYcP4/vvvWyVvV6u5a7labV02\nV3Mtu3fvZt++fcybN6/F83W1LncPbi+fGbsI7kePHqW6upoBAwaY7R84cCBJSUkW6Q8fPszAgQMt\n0tbU1HD06NEWzevleHt788orrxAeHm7al56eDkBwcLDVY0pLS/nd737H8OHDue2225g/f77Fsrlt\n6dNPP+VXv/oVMTExPPDAA+zevdtqOlsul8b++9//cvDgQWbPnt1kGlsplwkTJlhtAoGmf+cFBQWk\npaVZpE9KSsLZ2Zm+ffua9jk5OdG/f3+rn7WW0Nz1PProozz00EOm15qmkZmZSefOnZs953vvvced\nd95JbGwsDz/8MPv377+heW5Kc9cCav7wX//619x8882MGjWKxYsXU1FRYTVtW5fN5a7FqKKigpde\neonnnnsOvV7fbNq2KJfL3YPby2fGLoK78anO19fXbL+fnx+5ublW0/v4+FikBaymb0slJSXMmzeP\n22+/3eLLC6h8h4eHM2XKFL7++mtWrFjBoUOHSEhIaIPcWoqIiCAsLIwNGzbw1VdfceeddzJr1iyL\nZX2h/ZRLXV0dy5YtY8aMGXh5eVlNY+vlYtTc79xabYkxfeN2UF9fX5sqI6O///3vnD9/3mp/CaP+\n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\nTkSuQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_results(system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Bonus question #2\n", - "\n", - "Factor out the update function.\n", - "\n", - "1. Write a function called `update` that takes a `State` object and a `System` object and returns a new `State` object that represents the state of the system after one time step.\n", - "\n", - "2. Write a version of `run_simulation` that takes an update function as a parameter and uses it to compute the update.\n", - "\n", - "3. Run your new version of `run_simulation` and plot the results.\n", - "\n", - "WARNING: This question is substantially harder, and requires you to have a good understanding of everything in Chapter 5. We don't expect most people to be able to do this exercise at this point." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def update(state, system):\n", - " \"\"\"Compute the state of the system after one time step.\n", - " \n", - " state: State object with juveniles and adults\n", - " system: System object\n", - " \n", - " returns: State object\n", - " \"\"\"\n", - " juveniles, adults = state\n", - " \n", - " maturations = system.mature_rate * juveniles\n", - " births = system.birth_rate * adults\n", - " deaths = system.death_rate * adults\n", - " \n", - " if adults > 30:\n", - " market = adults - 30\n", - " else:\n", - " market = 0\n", - " \n", - " juveniles += births - maturations\n", - " adults += maturations - deaths - market\n", - " \n", - " return State(juveniles=juveniles, adults=adults)\n", - "\n", - "\n", - "def run_simulation(system, update_func):\n", - " \"\"\"Runs a proportional growth model.\n", - " \n", - " Adds TimeSeries to `system` as `results`.\n", - " \n", - " system: System object \n", - " \"\"\"\n", - " results = TimeFrame(columns = system.init.index)\n", - " results.loc[system.t0] = system.init\n", - " \n", - " for t in linrange(system.t0, system.t_end):\n", - " results.loc[t+1] = update_func(results.loc[t], system)\n", - " \n", - " system.results = results" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "run_simulation(system, update)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAfcAAAF0CAYAAAA+UXBRAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XlYlPX+//HnDDsM+yqgKJqogYqCpqmVuGSmpZmlZu5p\nbmWnX8es074eE3PLMk0zbbG0zK3vscUtUxE33FdQEUU22YdhZn5/3DE6AjYoMCzvx3VxwXzue2be\nU+qL+/5sKqPRaEQIIYQQdYba2gUIIYQQonJJuAshhBB1jIS7EEIIUcdIuAshhBB1jIS7EEIIUcdI\nuAshhBB1jIS7EPXMvHnzCAsLY9CgQZQ3E7Z79+5Mnz69misTQlQWCXch6qmEhAR++OEHa5chhKgC\nEu5C1FN9+vQhNjaWa9euWbsUIUQlk3AXop6aPn06hYWFzJkz5x/P3bt3LyNHjqRdu3ZERETw8MMP\ns2zZMrPb+tOnTycqKoqrV68ydepUOnToQMeOHZkyZQpXr141e71z584xdepUOnbsSEREBH379i31\nekKI22dr7QKEENYREBDAhAkTmDNnDoMHD6ZFixZlnlcS7JGRkXz44YdoNBq2bt3K+++/T2ZmJtOm\nTTOdq9frmTJlCj169GD48OHs37+fWbNmodPp+PTTTwFISUnhySefxNvbm9dffx0vLy+2bt3Khx9+\nSHp6Ov/617+q5fMLUZdJuAtRj40aNYrVq1fz1ltv8fXXX5d5zrx583BxceHTTz/FxcUFgE6dOpGU\nlMSyZct45plnTO35+fk8+OCDjBw5EoDo6Gh+/fVXdu3aZXq9zz77jIKCAj7//HOCgoIAuOeee8jO\nzuaLL75g1KhReHl5VeGnFqLuk9vyQtRj9vb2vPLKK8THx/PTTz+VOq7T6YiPj6dz586mAC9x//33\nU1hYyJEjR8zaY2JizB43bNiQgoICioqKANixYwdt2rQxBXuJnj17UlxczOHDhyvjowlRr8mVuxD1\n3H333ccDDzzARx99RI8ePdBoNKZjmZmZ6HQ6/P39Sz3P19cXgNTU1DLbS9jZ2QGY+tOvXLnChQsX\nCAsLK7OeK1eu3P6HEUIAEu5CCGDGjBn07duX+fPnm81vV6lU5T6nJKzVavMbgLd6Tono6GheeeWV\nMo+V9YuEEKJiJNyFEDRq1IgxY8bw+eefM2jQIFO7p6cnDg4OXL58udRzSq6w/fz8KvReDRo0ICcn\nh5YtW95Z0UKIckmfuxACgPHjx+Pn58fbb79tarO1taVDhw789ddf5OXlmZ3/22+/4ebmRkRERIXe\np3Pnzhw/fpwTJ06Ytf/xxx/MnDmTgoKC2/8QQghAwl0I8TcnJyf+/e9/s2vXLi5dumRqnzp1Knl5\neUycOJE//viDP//8kzfffJMdO3YwefJkHBwcKvQ+zzzzDO7u7owbN44NGzawd+9eli1bxr/+9S9O\nnz6Nk5NTZX80IeoduS0vhDB58MEH6dy5Mzt37jS1tW7dmuXLlzNnzhxeeOEFiouLadasGR9++CGP\nPvpohd8jMDCQb7/9lo8//pi33nqL3Nxc/P39GTlyJM8880xlfhwh6i2VUZaEEkIIIeoUuS0vhBBC\n1DES7kIIIUQdI+EuhBBC1DES7kIIIUQdUydGyxcWFnL48GF8fX2xsbGxdjlCCCFEldLr9Vy9epXw\n8HAcHR1LHa8T4X748GGGDRtm7TKEEEKIarVy5UqioqJKtdeJcC/ZqGLlypUEBARYuRohhBCial2+\nfJlhw4aV2qipRJ0I95Jb8QEBAQQHB1u5GiGEEKJ6lNcVXSfCXQghhKgLxq8bX+6xz/p9ZvHrSLgL\nIYQQNUhqfirJ2clo7DU082qGin/eRvlmEu5CCCFEDaDT6ziVcYqU3BQAcopyCHINwtnOucKvJeEu\nhBBCWFlqXiqf7f3MFOwArvauONnd3i6JEu5CCCGEFcVfimf5weUUFhea2vyc/bjL+67buiUPEu5C\nCCGEVRQbivnh6A/8ce4PU5saNU29mhKgCbjtYAcJdyGEEKLapeWnsSh+EUlZSaY2H2cf2ga0RWOv\nuePXl3AXQgghqtHBywdZdmAZ+bp8U1tkg0iebvP0bQ2eK4uEuxBCCFEN9AY9Px7/kc1nNpva1Co1\ng1oNonuT7qhUt38b/mYS7kIIIUQVyyzIZFH8Is5mnjW1eTl5Ma79OEI9Qyv9/STchRBCiCp0JPUI\nS/YvIa8oz9QW4R/BqLajcLF3qZL3lP3c67AhQ4Ywffp0i88PCwvj+++/r8KKhBCi/jAYDaw9vpa5\nu+eagl2tUjOw5UAmRU+qsmAHCXdRjry8PJYuXWrtMoQQola6VniN2X/NZuOpjaY2D0cPXuj0Ar2b\n9a7U/vWyyG35CoqLg02bICUFGjSAPn0gOtraVVW+3bt3s3TpUkaNGmXtUoQQolY5nnacJfuWkK3N\nNrW19G3JmMgxuDq4VksNcuVeAXFxsHgxJCeDwaB8X7xYaa8OZ8+e5ZlnnuGee+6hffv2DBs2jCNH\njgCQnZ3N888/T3R0NF26dGHRokVmz12zZg1hYWEUFxeb2r7//nvCwsJKvc8333zD5MmTuXLlChER\nEWzatAmtVssbb7xBly5daNOmDd27d+fTTz/FaDRW7YcWQohawmA0sP7kej7e9bEp2FUqFf3D+jO1\n49RqC3aox1fumzfDunWg1Vr+nPh4yMsr3b5vH7RrZ/nrODhAv37Qs6flzwF47rnnaNGiBVu2bAHg\n9ddfZ8qUKfz+++988MEHHDt2jDVr1uDj48P8+fM5fvw4ISEhFXsTlL76tLQ0vv/+e7Zt2wbAokWL\niI+P58cff8TX15eEhATGjx9Pq1at6NatW4XfQwgharsbt2fVGXQcTztOZmEmAN0adcPVwZWx7cbS\nwqdFtddWr8O9IsEOkJ9fdntZgX8rWq3y/hUN92+++QZbW1scHR0BeOihh/jpp5+4evUqmzZt4vnn\nn6dhw4aA8otAZQ6Oy87ORq1Wm947IiKCP//8s8r7jYQQoqa7pr3G8bTjaPXXQ6W5d3PGthuLu6O7\nVWqqt+Hes2fFr9ydncsOcpcKDnh0cKh4sAPs37+fBQsWcPr0abRaremW+JUrV8jPzyc4ONh0rr29\n/W1dtZdn2LBhbN++na5duxIdHc29995Lv3798Pb2rrT3EEKI2uZy7mVOZZzCyPUuykZujZjWaRpq\nlfV6vut1uFc0YEv63G82dmzVD6o7d+4czz77LMOHD+fTTz/Fw8OD7du3M3bsWIqKigBQq83/IBkM\nhlu+pl6vt/j9GzRowNq1azl06BA7d+5k7dq1zJs3j2XLlhEREVHxDySEELWYwWjgTOYZknOSTW12\najvCfMLwcvSyarCDDKirkOhoJciDg0GtVr5XR7ADHD16FJ1Ox/jx4/Hw8ADg4MGDAHh7e2NnZ8el\nS5dM5xcVFZGUdH1DgpLb6YWF17cUvPH4P8nPz6ewsJDWrVszYcIE1qxZQ8uWLVm7du0dfS4hhKht\nCosLWbBngVmwa+w1tGvQDi9HLytWdl29vXK/XdHR1pn6VtKXHh8fT5cuXfj999+J+3uYfmpqKvfd\ndx8rV67kgQcewN3dnXnz5plduYeGKssbrl+/nscff5yDBw/y+++/l/t+Tk5OZGdnc+XKFVxdXZk0\naRKenp688soreHt7k5SUREpKCn369KnCTy2EEDVLWn4a8/fMJyUnxdTm4+xDmHcYNiobK1ZmTq7c\na4mSK+YZM2bQpUsXtm3bxvz582nfvj3jxo1jxIgRNGnShP79+9O7d2/c3d2JiooyPb9FixZMmDCB\nOXPmEBUVxdKlS5k4cWK579erVy98fX2JiYlhzZo1fPDBBxQVFdGnTx/atGnD2LFj6d+/P0OGDKmO\njy+EEFZ3Kv0U729/3yzYG7k1oqVPyxoV7AAqYx2YqHzx4kViYmL47bffzAaVCSGEEJVh54WdrDi0\nAr1BGatkq7bl6TZP0zG4o1Xq+afck9vyQgghRDkMRgM/HvuR/535n6nN1cGVidETq2Q3t8oi4S6E\nEEKUobC4kCX7lnDoyiFTW7BbMBOjJ+LtXLOnAUu4CyGEEDdJz09nQdwCkrOvj4hvE9CGMZFjcLB1\nsGJllpFwF0IIIW5wJuMMC/cuJEebY2rr3aw3j7Z41Orz1y0l4S6EEEL8bdfFXXx18CuKDcomWzZq\nG55q/RSdG3a2cmUVI+EuhBCi3jMajfx0/Cd+Of2LqU1jr+HZ6Gdp5tXMipXdHgl3IYQQ9Zq2WMsX\n+7/gwOUDprZA10AmdZiEj7OPFSu7fRLuQggh6q2MggwW7FnAxeyLprYI/wjGthuLo62jFSu7MxLu\nQggh6qWzmWdZGLeQbG22qa1n054MbDmw1gycK4+Eey0SERHBm2++ycCBA61dilkt06dPJykpiW++\n+cbaZQkhRLnGrxtv+jk1P5WTaScxoOzBcX/I/TzV+inubXSvtcqrVBLutUhCQoK1SzCpSbUIIYSl\njBhJykrifPZ5U5ud2o5pnabR3Lu5FSurXBLuQggh6gUjRk6ln+Jy3mVTm7OdM3f73l2ngh1kV7gK\nGb9ufLlf1SEsLIzvv/+e6dOnl9qN7cUXX2T48OHk5ubSunVrfvzxR7Pja9asoU2bNuTm5qLX65k/\nfz69e/emTZs2xMTEsHjxYrNzO3fuzF9//UW/fv1o27Ytjz76KIcOXV+CsaSWshw/fpzRo0fTsWNH\nIiMjGTduHOfOnTMd37lzJ48//jjt27cnKiqKUaNGcfr06cr4TySEEGUyGA2cSDthFuxejl609W+L\nk62TFSurGhLudYxGo6F79+5s2rTJrH3Dhg306NEDjUbD/Pnz+emnn5g7dy779u3jww8/ZOHChfz0\n00+m87Ozs1m1ahXLli1j586deHp68sYbb/zj+2dkZDBixAjatm3L1q1b2bp1K97e3owfPx69Xo9O\np2PSpEk89thj7Nmzhy1bttCkSRNeffXVyv5PIYQQAOgNehbvW0xqfqqpLUATwN1+d2Orrps3sOvm\np7LA5jObWXdyHdpircXP2XZ+W7nHKnL17mDrQL/m/ejZtKfFz6mI/v37M3XqVLKzs3FzcyMjI4Nd\nu3bx2WefYTAY+Prrr3nhhRcICwsDICoqiscff5xVq1bx6KOPAphC2Ntb2RyhR48evP/++xiNRlQq\nVbnvvW7dOuzs7Jg6dSoAjo6OzJgxg44dO7Jnzx5at26NVqvFwcEBGxsbNBoN//nPf275mkIIcbuK\nDcUsil/EwcsHTW2BmkCaejVFRd39d6f+hvvZzRUK9sqkLday+ezmKgv3rl27otFo2Lx5M4899hib\nNm3C29ubzp07k5GRQVZWFm+//TbvvPOO6TlGoxFfX1+z12nUqJHpZycnJ3Q6HXq9Hlvb8v/YnD17\nlrS0NCIiIsza1Wo1Fy9epFOnTrzwwgu89tprfPbZZ3Tq1ImePXvSuXPtWtpRCFHz6fQ6Pt37KYdT\nD5vaglyDCPUMrdPBDvU43HuG9qzwlXtlcbB1oGdo5Qa7wWAw/WxnZ8dDDz3Epk2beOyxx9i4cSP9\n+/dHrVbj6KgsyjB79mx69rx1DWp1xXttHB0dad68OT///HO554wdO5ZBgwbx559/sn37diZNmkT3\n7t2ZNWtWhd9PCCHKUqQv4pO4Tzh29Zip7d3u7zKgxYB6caew/oZ7054VvnK+1a33z/p9dqclWczB\nwYHCwkKztqSkJJydnU2PH3nkEYYNG8bp06eJj4/nrbfeApQ+eR8fH44ePWoW7leuXMHT0xN7e/s7\nqq1x48Z899135ObmotFoAOWuwMWLF2nYsCGg9Mt7eXnRt29f+vbtyyOPPMLIkSP5z3/+g4eHxx29\nvxBCaIu1zN8zn5PpJ01tfZv3pV/zfvUi2EEG1NVKoaGhnDp1iuPHj6PT6Vi1ahXJyclm57Rp04ag\noCDefvttwsPDadq0qenYiBEjWLlyJX/99Rd6vZ7jx48zdOhQlixZcse19evXDycnJ95++20yMzMp\nKChgzpw5DBo0iNzcXOLj44mJiWHHjh3o9XqKioo4cOAAPj4+uLu73/H7CyHqt8LiQubsnmMW7I+0\neIT+Yf3rTbBDPb5yvx3VeXV+K4MGDSIuLo6hQ4dib2/P4MGDGTBgAIcPHzY7r1+/fsybN4/XXnvN\nrH3MmDEUFBTw8ssvk56ejp+fHwMGDGD8+Duf0qfRaFi8eDEffvghDzzwAHZ2doSHh7N06VI0Gg3t\n27dn+vTpvPvuu1y6dAlHR0datWrFp59+Wq/+4gkhKl++Lp85u+aQmJVoanus1WP0atrLekVZicpo\nNBqtXcSdunjxIjExMfz2228EBwdbu5wqExYWxjvvvMPjjz9u7VKEEKJGyS3K5eNdH3Ph2gVT2xPh\nT9C9SXcrVlV1/in35Mq9ljh79iyA9EkLIcRNcrQ5zN41m+Ts692Tw1oPo1tINytWZV3S514L/Pzz\nz/Tv358OHTpw7711Y1MDIYSoDNcKrzHrr1mmYFepVIxoO6JeBzvIlXut0L9/f/r372/tMoQQokbJ\nLMgk9q9YUvOUledUKhWj2o6iY3BHK1dmfRLuQgghap30/HRi/4olLT8NALVKzZh2Y4gKjLJyZTWD\nhLsQQoha5WreVWL/iiWjIAMAG7UNz7R/hrYBba1cWc1R7X3uZ8+e5dlnn6VTp05ERUUxePBg/vjj\nD9Px9evXM2DAACIjI+nVqxezZ89Gr9dXd5lCCCFqoCu5V/ho50emYLdV2/Js1LMS7Dep1it3g8HA\n2LFjadOmDZs2bcLZ2ZmVK1cyZcoUfv75Z9LS0pg+fTozZ84kJiaGc+fOMWHCBOzs7Jg8eXJ1liqE\nEKKGuZRzidl/zSZbmw2AnY0dE6Mn0sq3lZUrq3mq9co9IyOD5ORkHn30UTw8PLC3t2fo0KHodDqO\nHz/OihUr6NatG3369MHe3p6wsDBGjhzJV199ZbZ2uhBCiPrlYvZFZu2cZQp2B1sHpnacKsFejmq9\ncvfx8aF9+/b88MMPRERE4OrqyjfffIOnpycdO3bkgw8+YOjQoWbPad26NVlZWSQmJhIaGlqd5Qoh\nhLCSG/fyyCnKISE1gWJDMQC9QnsxteNUmno1Le/p9V61D6ibN28e48aNo1OnTqhUKjw9PZkzZw7e\n3t5kZGSUWl/c09MTUK76JdyFEKJ+yS7K5vCVwxQblWC3Vdvy/D3P08SziZUrq9mq9bZ8UVERY8eO\npUmTJuzYsYO9e/cyefJkJkyYwOnTp6uzFCGEEDVcblGuWbDbqe1o7ddagt0C1Rruu3bt4ujRo8yY\nMQNfX180Gg3Dhg0jODiY1atX4+PjQ1ZWltlzMjMzAfD19a3OUoUQQlhRvi5fuRX/d7Dbq+1p7d8a\njb3GypXVDtUa7iWD4m6e2qbX6zEajURGRnLw4EGzY/Hx8fj6+tKoUaNqq1MIIYT1pOWnkZCagM6g\nA5Rb8RH+EbjYuVi5stqjWsO9Xbt2+Pj48NFHH5GZmYlWq2XVqlWcO3eOBx98kBEjRrBjxw42btxI\nUVERCQkJLF26lFGjRsl2oEIIUQ9kFWYx+6/ZaPVaAGxUNkT4SbBXVLUOqHNzc2PJkiXExsbSt29f\ncnJyCA0NZf78+bRtqyxAEBsby9y5c3nppZfw8fFh+PDhjB49ujrLFEIIYQUl27beuKRsuF84rvau\nVq6s9qn20fItWrRg0aJF5R7v1asXvXr1qsaKhBBCWFuBroC5u+eSkpMCwP0h9zMxeiIR/hFWrqx2\nki1fhRBCWFWRvogFcQtIykoClN3dxrQbI8F+ByTchRBCWE2xoZhP937KqfRTpranWj8lu7vdIQl3\nIYQQVmEwGliybwlHUo+Y2h6/+3G6NOpixarqBgl3IYQQ1c5oNPLVwa/Yl7LP1PZw84fpEdrDilXV\nHRLuQgghqpXRaGTVkVXsvLDT1BYTGsPDzR+2YlV1i4S7EEKIarXu5Dp+P/e76fG9je7l8VaPy3om\nlUjCXQghRLX535n/seHkBtPjqMAonmr9lAR7JZNwF0IIUS22J21n9dHVpsfhfuGMihyFWiVRVNnk\nv6gQQogqF5ccx8qElabHzb2bMyFqArbqal9LrV6QcBdCCFGlDl4+yBf7v8BoNALQ2KMxkzpMws7G\nzsqV1V0S7kIIIarM8bTjLIpfhMGo7Aoa6BrI1I5TcbR1tHJldZuEuxBCiCpxNvMsn8R9QrFB2ZPd\n18WX5+95Hhd72eGtqkm4CyGEqHQXsy8yb/c8tMXK1q0ejh5Mu2ca7o7uVq6sfpBwF0IIUamu5F7h\n410fk6/LB8DVwZVpnabh7ext5crqDxmmKIQQ4o6NXzceAK1ey4HLB9DqlSt2W7UtG4duJEATYM3y\n6h25chdCCFEpdAYdh64cMgW7jcqGcN9wGro3tHJl9Y+EuxBCiDtmxMixq8coKC4AQI2aVr6tcHNw\ns3Jl9ZOEuxBCiDt2NvMsWdosAFSoaOHTAk9HTytXVX9JuAshhLgjuy7uIjkn2fS4sUdjfJx9rFiR\nkHAXQghx285fO8+KQytMj32cfQh2C7ZiRQIk3IUQQtym3KJcFsYtRKfXAeBs50yYdxgqZIc3a5Op\ncEIIISrMYDTwefznZBRkANC7aW9mdJ2Bn4uflSsTIFfuQgghbsOaY2s4nnYcAJVKxZjIMRLsNYiE\nuxBCiAqJS45j85nNpsf9mvcjwj/CihWJm1l0Wz4/P5/ly5dz4MABsrKyyjzn22+/rdTChBBC1DwX\nrl3gy4Nfmh63CWjDQ3c9ZMWKRFksCvc33niDn3/+maZNm+Ll5VXVNQkhhKiB8oryWLj3+gA6f40/\noyNHo1LJALqaxqJw37ZtGx988AGPPvpoVdcjhBCiBjIYDXy+73PS89MBcLR1ZGL0RNmXvYayqM9d\nr9cTFRVV1bUIIYSooX46/hPHrh4zPR4dOVo2g6nBLAr3bt26sXv37qquRQghRA2099Je/u/0/5ke\nP9z8YdoEtLFiReKfWHRbfsiQIbz33nucPXuWNm3a4OzsXOqcLl26VHpxQgghrCs5O5kvD1wfQNfa\nvzUPN3/YihUJS1gU7k899RQAR48eNWtXqVQYjUZUKhXHjh0r66lCCCFqqbyiPD6J+4QifREAfi5+\njIocJQPoagGLwn358uVVXYcQQogaxGA0sGT/EtLy0wBwsHVgYvREnO1K37kVNY9F4d6hQ4eqrkMI\nIUQN8vOJnzmSesT0eFTbUTRwbWDFikRFWLy2/P79+/n66685duwYeXl5uLq60rp1a0aOHEmzZs2q\nskYhhBDVaF/KPjad2mR6/NBdDxHZINKKFYmKsmi0/JYtWxg2bBh79uwhJCSE6OhogoKC2LJlC489\n9hj79++v6jqFEEJUg0s5l1h2YJnpcbhfOP3C+lmvIHFbLLpyX7hwIQMGDODtt99Grb7++4Ber+f/\n/b//x+zZs6VfXggharl8XT4L4xaiLdYC4Oviy5h2Y1CrZBuS2sai/2MnTpxg9OjRZsEOYGNjw/jx\n40lISKiS4oQQQlQPo9HIF/u/IDUvFZABdLWdReGuUqkoLi4u+wXU8hudEELUdutOriPhyvULtRFt\nRhDoGmjFisSdsCiZw8PD+eSTT0oFvE6nY8GCBYSHh1dJcUIIIaregcsH2HByg+lx72a9aR/Y3ooV\niTtlUZ/7c889x6hRo+jatSvh4eFoNBpycnI4fPgwhYWFfPHFF1VdpxBCiEo0ft14QOlnP3D5AMVG\n5eLN09GThQ8vtGZpohJYdOUeFRXF6tWr6dGjB+np6Rw5coSMjAx69erF6tWradeuXVXXKYQQopIV\nG4o5evWoKdgdbR1p4dNCBtDVARbPc2/evDlvv/12VdYihBCimhgxciL9BPnF+QDYqGy42/du7NR2\nVq5MVIZyw33Hjh3cc8892NrasmPHjn98Idk4Rgghagej0cip9FOkF6Sb2pp7N8fFzsWKVYnKVG64\njx07lj///BNvb2/Gjh1r2iSmLLJxjBBC1B5rT6zlct5l0+NGbo3wdfa1YkWispUb7suXL8fd3d30\nsxBCiNrvt7O/mS0tG6AJIMQjxIoViapQbrjfuFnMpUuXeOihh7C3ty913uXLl/nll19kcxkhhKjh\n4pLjWHVklemxt5M3d3ndhQrZwrWusWhA3csvv0y3bt3w8vIqdezq1avMnj2bkSNHVnZtQgghKsmx\nq8dYemCp6fGotqN4/p7nsbcpfdEmar9bhvvw4cNNfe2TJk3Czs58FKXRaCQxMRE3N7cqLVIIIcTt\nS8pKYuHehegNegAauDZgUvQkCfY67JaTGQcMGEBIiNIXo9frKS4uNvvS6/Xcfffd/Pe//63Qm65Z\ns4YHH3yQiIgIYmJiWLZsmenY+vXrGTBgAJGRkfTq1YvZs2ej1+sr/smEEEKQmpfKvD3zTJvBeDp5\n8lzH53Cxl5Hxddktr9wHDhzIwIEDSUxMZMGCBZVyhb5hwwY+/PBDYmNjiY6OZv/+/bzxxhtERUWR\nn5/P9OnTmTlzJjExMZw7d44JEyZgZ2fH5MmT7/i9hRCiPrlWeI05u+aQo80BwMXehec6Poenk6eV\nKxNVzaJliL766qtyg/3SpUv06dPH4jdcsGABY8eO5d5778Xe3p6OHTuyadMmwsPDWbFiBd26daNP\nnz7Y29sTFhbGyJEj+eqrrzAYDBa/hxBC1HcFugLm7p5LWn4aAHY2dkzuMJkGrg2sXJmoDhavULdl\nyxa2b99OVlaWqc1oNHL69GmuXr1q0WukpqZy5swZnJ2dGTJkCCdOnCAoKIhnnnmGfv36ceDAAYYO\nHWr2nNatW5OVlUViYiKhoaGWliuEEPWWTq/jk7hPuJh9EQC1Ss349uMJ9ZR/Q+sLi8J91apVvPba\na/j4+JCRkYGvry/Xrl2jsLCQtm3bWrws7eXLyqIJ3333HTNnzqRhw4b88MMPvPjiizRo0ICMjAzT\n3PoSnp7K7aOMjAwJdyGE+AcGo4Ev9n/ByfSTpran2zxNhH+EFasS1c2i2/LLly/nP//5Dzt27MDB\nwYEVK1aZIXdIAAAgAElEQVSwf/9+PvroI9RqNVFRURa9WckKd8OHDycsLAxnZ2eefvppwsPDWbNm\nze1/CiGEEBiNRr5J+IZ9KftMbQNbDqRTw05WrEpYg0XhfuHCBR544AFAWWpWr9ejUql4+OGHeeyx\nx3jjjTcsejM/Pz/g+tV4iUaNGnHlyhV8fHzMbvsDZGZmAuDrK0sjCiHErWw4tYFtSdtMj3uE9qBX\n015WrEhYi0XhbmtrS2FhIQDu7u6m2+sA99xzD7t377bozfz8/PDw8CAhIcGsPSkpiaCgICIjIzl4\n8KDZsfj4eHx9fWnUqJFF7yGEEPXRtqRtrDuxzvS4Q1AHBrUahEolq8/VRxaFe9u2bYmNjSUnJ4ew\nsDA+//xzU9j/+uuvODg4WPRmNjY2jBo1ihUrVrBz506KiopYuXIlx44dY8iQIYwYMYIdO3awceNG\nioqKSEhIYOnSpYwaNUr+gAohRDn2p+zn64SvTY9b+bZiRNsR8u9mPWbRgLopU6YwduxYMjIyGDly\nJGPGjKFDhw7Y29uTl5fHiBEjLH7D8ePHU1xczMsvv0x6ejpNmjTh888/p2XLlgDExsYyd+5cXnrp\nJXx8fBg+fDijR4++vU8nhBB13Mn0kyzet9g0pqmxR2MmRE3AVm3xZChRB6mM5e3jepPc3FwcHR2x\ntbUlISGBDRs2UFxcTNu2benbt69Vf0O8ePEiMTEx/PbbbwQHB1utDiGEqE4Xsy8y88+ZFBYrd1L9\nXPx46d6XcHVwtXJloqr9U+5Z/KudRqMx/RwREUFEhEyrEEIIa0nLT2POrjmmYHd3dOf5e56XYBfA\nLcI9NjbW4hdRqVRMmzatUgoSQghxaznaHObsmkO2NhsAR1tHpnacirezt5UrEzVFueG+aNEii19E\nwl0IIapHYXEh8/bMIzUvFQBbtS2TOkwi2E26JMV15Yb78ePHq7MOIYQQ5Ri/bjwABgwcST1CZqGy\n/ocKFSsHrqS5d3NrlidqIIumwgkhhLAuI0ZOpp00BTtAM69mRDaItGJVoqayaEDd008//Y/nLF++\n/I6LEUIIUZoRIyfTT5Kan2pqC3EPoYFGdngTZbMo3HU6Xampbnl5eSQmJhIQEECLFi2qpDghhKjv\n9AY9J9JOmAV7A00DGrnLqp2ifBaF+zfffFNme2ZmJv/+97/p3bt3pRYlhBACig3FLN63uFSwN/Nq\nhgpZfU6U74763D09PXn++eeZO3duZdUjhBACZU/2T/d+yv6U/aa2QE2gBLuwyB2vT2hnZ0dKSkpl\n1CKEEAIo0hexMG4hR68eNbUFuwbTxLOJBLuwiEXhvmPHjlJtRqORa9eusXLlSgIDAyu9MCGEqI+0\nxVrm75nPyfSTprb3Y97nkbBHZCMYYTGLwn3s2LGoVCrKWobezc2N//73v5VemBBC1DeFxYXM3T2X\nMxlnTG39w/rTt3lfK1YlaiOLwr2saW4qlQpXV1dCQkJwcnKq9MKEEKI+ydflM2fXHBKzEk1tA1sO\npHczGbAsKs6icO/QoUNV1yGEEPVWXlEes3fN5sK1C6a2wXcPJiY0xopVidrM4gF1mzdvZt26dVy4\ncIFr167h4eFB06ZNGThwIJ06darKGoUQos7K1mbz8a6PSc5ONrUNaz2MbiHdrFiVqO0smgq3ZMkS\npkyZwuHDhwkMDKR9+/YEBAQQFxfH6NGj+fLLL6u6TiGEqHOyCrOYtXOWKdhVKhVPt3lagl3cMYv7\n3MeNG8e//vWvUsc+/PBDvvjiC0aMGFHpxQkhRF2VWZBJ7F+xpt3dVCoVo9qOomNwRytXJuoCi67c\ns7KyGDRoUJnHBg8eTFZWVqUWJYQQdVlafhozd840BbtapWZcu3ES7KLSWBTuYWFhXL58ucxjly9f\npmXLlpValBBC1FWpeal8tPMj0vPTAbBR2zAhagLtA9tbuTJRl1h0W/6tt97i3XffJScnh7Zt2+Lq\n6kp+fj579+5l2bJlTJ8+naKiItP59vb2VVawEELUVik5KczeNZtrhdcAsFXb8mz0s4T7hVu5MlHX\nWBTuTzzxBFqtlr1795Y6ZjQaGTJkiOmxSqXi6NGjpc4TQoj6LDk7mdm7ZpOjzQHAzsaOSdGTaOkr\ndz5F5avQCnVCCCEq7vy183y862PyivIAcLB1YEqHKdzlfZeVKxN1lUXhPmXKlKquQwgh6qTErETm\n7JpDvi4fAEdbR5675zlCPUOtXJmoyyxexCY3N5dNmzZx7Ngx8vLycHV1pXXr1vTu3RsHB4eqrFEI\nIWqV8evGA8oCNYdTD1NsLAaUPvb1Q9YT4hFizfJEPWBRuJ85c4YRI0aQlpaGq6srLi4u5ObmsmLF\nChYsWMDy5cvx9/ev6lqFEKLWyCrM4sjVI+iNegDs1HZE+EVIsItqYdFUuFmzZhEUFMSmTZuIi4tj\ny5Yt7N27l59//hknJyfZFU4IIW5wJe8KCakJpmC3V9vT2r81GnuNlSsT9YVF4b53715eeeUVmjRp\nYtbevHlzXn311TL3exdCiPrGaDTy47EfOZF+AiPKFtkONg60CWiDi52LlasT9YlFt+ULCgpwc3Mr\n85ifnx/5+fmVWpQQQtQ2Or2OpQeWEn8p3tSmsdNwt9/dONjIuCRRvSy6cg8JCWHTpk1lHtuwYQMh\nIdKHJISov7K12cz6a5ZZsHs7edMmoI0Eu7AKi67cn376aV577TUSEhKIjIxEo9GQk5PDvn372Lp1\nK++8805V1ymEEDXSpZxLzN8z37ScLECQaxChnqGokPVBhHVYFO6DBw8GlK1ff//9d1N748aNeffd\ndxk4cGDVVCeEEDXY0atH+WzvZxQWFwLKCp1P3P0EDzR5wMqVifrO4nnugwcPZvDgweTm5pKXl4eL\niwsajYz8FELUT9uStvFNwjcYjAZAWXVuXLtxRPhHWLkyISoQ7gAnT57kwoULZGdn4+HhQbNmzWjY\nsGFV1SaEEDWOwWhgzbE1bD6z2dTm6eTJ5A6TCXYLtmJlQlxnUbhfuHCBKVOmcOLECYxGo6ldpVIR\nGRnJzJkzCQoKqrIihRCiJtAWa/li/xccuHzA1NbIvRGTOkzCw9HDipUJYc6icH/ttdfIzs7mnXfe\n4e6778bZ2Zm8vDwOHz7MJ598wmuvvcaSJUuqulYhhLCarMIsPon7hKSsJFNbm4A2jIkcg4OtjIgX\nNYtF4b5v3z4WL15MdHS0WXvLli1p2LAhEyZMqJLihBCiJriYfZH5e+aTWZBpauvZtCcDWw5ErbJo\nRrEQ1cqicNdoNPj6+pZ5zN/fHxcXWXlJCFE3HU49zKL4RWiLtQCoVWqGRAyhW0g3K1cmRPks+pVz\n4MCBrF69usxjP/zwA4899lilFiWEEDXBH+f+YP6e+aZgd7R1ZErHKRLsosaz6Mrd1dWVb7/9lq1b\ntxIZGYmrqysFBQXExcVx7do1+vXrR2xsLKAMsps2bVqVFi2EEFXJYDSw6sgq/jj3h6nN29mbyR0m\nE+gaaMXKhLCMReFeEtygTIe72eLFi00/S7gLIWqzwuJCFu9bTMKVBFNbY4/GTOowCTeHsvfYEKKm\nsSjcjx8/XtV1CCGEVYxfN970s1av5UjqEXJ1uQB0a9SNdg3aMSpyFPY29tYqUYgKq9AiNkIIUVfl\nFOVw5OoRivRFprYHmz3Ioy0eRaWSNeJF7SLhLoSo14wYSc1L5VTGKdNSsipU3OV1FwNaDrBydULc\nHgl3IUS9pS3WcjL9JFfyrpjabNW2tPJpJSvOiVpNwl0IUS9dyrnEovhFZsHubOdMK99WONs6W7Ey\nIe7cHYe7VqslKysLf3//yqhHCCGq3M4LO/k64Wt0ep2pzd/Fn2ZezbBR2VixMiEqh0WL2LRs2ZL0\n9PQyj507d45HHnmkUosSQoiqoC3W8uWBL/nywJemYFer1IR5hxHmHSbBLuqMW165//TTTwAYjUY2\nbdpUav92o9HInj170Gq1VVehEEJUgpScFD6L/4yUnBRTW4AmgM3DN8vCNKLOuWW4r169msOHD6NS\nqXjnnXfKPW/48OG39ebx8fE89dRTTJw4kSlTpgCwfv16lixZQmJiIr6+vvTp04epU6diYyO/UQsh\nbs+ui7tYeWil2TS3jsEdGRYxTHZ0EzVOXBxs2gQpKdCgAfTpAzft2/aPbhnuX331FcXFxYSHh/Pd\nd9/h6elZ6hw3Nzc8PCo+qrSwsJAZM2aYbTqzZ88epk+fzsyZM4mJieHcuXNMmDABOzs7Jk+eXOH3\nEELUb0X6Ir49/C1/nv/T1GZnY8eQ8CF0bthZ5q+LGsFohJwcJcx//x1++AEKC8HTEwwGKFkEtiIB\n/48D6mxtbfntt98IDAys1L8IsbGxNGnSBD8/P1PbihUr6NatG3369AEgLCyMkSNH8sknnzBx4kTU\natlaUQhhmcu5l/ls72dcyrlkavPX+DO+/XiC3IKsWJmor0pC/NIlJchv/J6Xp5wTH3/954wM8PIC\nJyf45ZdKCvfY2FieffZZnJyc+O677275IhVdT37v3r2sXbuWn3/+mRdffNHUfuDAAYYOHWp2buvW\nrcnKyiIxMZHQ0FCL30MIUX/tvriblQkrTbu5gdyGF1WjrFvoUVGlQ7zk55LgLk9+/vWfHR3B4e8/\nrpculX1+ecoN90WLFjFixAicnJxYtGjRLV+kIuFeUFDAjBkz+Pe//11q+lxGRgbu7u5mbSVdARkZ\nGRLuQohb0ul1fHv4W3ac32Fqs7Ox48nwJ7m34b1yG15Uqrg4WLRICfLcXDhxAjZuhCZNwK2Ceww5\nOCi/HFy9ClotODuDuzuU3LAOrOCYz3LD/cbNYipz45jY2FgaN27MwIEDK+01hRDicu5lFsUvIjk7\n2dTmr/HnmfbPEOwWbMXKRF1hNCrhe+4cnD0LS5YoV9RGo/l5p09Du3Zlv0ZJiAcGXv8eGKj0r6tU\nyi8MN2y0avLggxWrtcKL2KSlpVFQUICLiwteXl4Vem7J7fh169aVedzHx4esrCyztszMTAB8fX0r\nWqoQop7Yk7yHFYdWmN2Gjw6K5qnWT+Fo62jFykRtVlgIiYlKkJd83XhbvaxgB+UcR0clvG8O8pIQ\nL09Jv/ovvyivHxioBHuljpYvodVqmTlzJuvWrSM7O9vU7unpyYABA3j++eexs7P7x9dZvXo1+fn5\n9O/f39SWm5vLoUOH+P3334mMjOTgwYNmz4mPj8fX15dGjRpZ+pmEEPWETq/juyPfsT1pu6nNVm3L\nk+FP0qVRF7kNLyxmNCp94iVX5WfPKo/LCu8Szs5KkDs7g6sruLgoX82awbvv3jrEbyU6uuJhfjOL\nwv31119n48aNPPLII4SFheHk5ER+fj5Hjhxh+fLl5OTk8NZbb/3j60yfPp3nnnvOrO25556jbdu2\njB07luTkZJ566ik2btxIjx49OHHiBEuXLmX06NHyl1QIAVzff72guIBjV4+Z9l4HGNRyEOOjxstt\neFGukgFw588rt8ibNAFbWyXUCwv/+fnOzspzQkPhgQdg82bl+TcaMOD2g72yWBTuv/76K++8847Z\nFXeJ6OhoPvjgA4vC3d3dvdSAOXt7ezQaDb6+vvj6+hIbG8vcuXN56aWX8PHxYfjw4YwePdrCjyOE\nqOuMGLmSe4UzmWfQG/Wmdj9nP17p9orchhelFBbChQvwv//BqlXKALiCguvHW7SAG2Zlm6hUEBx8\nPcxDQ5Xzbgzupk3v/BZ6VbAo3A0GA23bti3zWIcOHdDr9WUes8RXX31l9rhXr1706tXrtl9PCFF3\nZRRkcDj1MJmFmaY2NWqaejUlQBMgwS4oKoKLFyEpSekvT0qCy5eV2+s3ziG/0cWLSmi7uV0P8SZN\nICTk+lS08lTGLfSqYFG433ffffz1119l9nvv2bOHLl26VHphQghRwmg0sv38dlYfXW0W7E62TrT0\naYnGXnOLZ4u6qrgYkpPNg/zSJWVVt7LcOIdcrQaNRukr9/CA995TFoyx9u30ylJuuO/YcX2eaI8e\nPZg7dy6nT58mMjISjUZDQUEBcXFxbN++nZdffrlaihVC1D/p+eksP7ic42nXp+SqUBHkGkSIR4js\n5FaH3bhATEAAtG+vjDYvCfLkZCXg/4lKpdwyb9FCOV+jUQa+lcwhDw4Gb+8q/SjVrtxwHzt2LCqV\nCqPRaPr+1VdflbqNDvDss89y7NixKi1UCFG/GI1GtiZtZc2xNWZT3JxtnWnu3Rw3hwquEiJqld27\n4eOPlSVYs7KURWK+/bb8/vESKhX4+yu31ENCoHFjaNgQ7O0rbw55bVBuuC9fvrw66xBCCJOreVdZ\nfnA5J9NPmtpUKhW9mvbCYDSgVsk+E3VRXh4cOQIJCcoCMZmZpc8p6R8v4eOjBHhJkDdqpMwxL0tl\nzSGvDcoN9w4dOlRnHUIIgdFo5Pdzv/Pj8R/R6XWm9gauDRjRZgRNPJswsKWsbllXGI1KWB8+rAT6\n2bPX55XftJ4ZoAxuc3KCRx+9HuQ3bCxqkZo6AK6yWbxC3ebNm1m7di1nzpwxrVDXrFkzBg4cyH33\n3VeVNQoh6oEruVf48uCXnMk4Y2pTq9T0btabh5s/jK26wgtqihpIq4Vjx64HelkhDsp8cp1OGeTm\n5aWMZLe3V/rH/944VNyCRX9bPv/8c2bNmkVISIjZIjaHDx/mf//7HzNmzGD48OFVXasQog4yGA38\ndvY31p5Ya3a1HugayMi2IwnxCLFidaIypKYqQZ6QAKdOlT8ITqVSpqBFRMDDD8O6daVHr9fF/vGq\nYFG4L1++nGeeeYYXXnih1LEPPviAxYsXS7gLISosJSeF5QeXczbzrKlNrVLz0F0P0eeuPnK1XsuU\njG5PTlaushs0UBaLSU0t/znOznD33Uqgt2qlTE0rERBQP/rHq4JFf3OuXbvGY489VuaxJ598km++\n+aZSixJC1G0Go4HNZzbz84mfKTZcv4xr6N6QEW1G0NC9oRWrExVlMMD69cr2p1lZylfJ2mZljW4P\nDobwcCXQQ0OvT0m7WX3pH68KFoV7q1atSEpKIiSk9O2xlJQUWrRoUemFCSHqpks5l/jywJckZiWa\n2mzUNvS9qy8PNnsQG7XMW6/p9HplnvnJk8pt9tOn4c8/y1/9LThYCfmICOXL07P6a65vyg33oqIi\n088zZszgvffeo6ioiLZt2+Lq6kp+fj579+5l2bJlvPnmm9VSrBCidinZ5AWUNeEvZF/gfNZ5DBjo\n1qgbACEeIYxoM4IgtyBrlSn+gU6nbKxy6pQS6GfPKsu83ujG1d9AmY7m7a18xcaCBRuHikpUbri3\nbt3abCc2o9HIlClTSp1nNBp5/PHHSUhIqJoKhRC1Xm5RLiczTpJbdH0HN1u1LQ83f5jezXrLvPUa\nRqtVArzkyvzcuX9eCc7LC2xswN1dWc7V0fH6xisS7NWv3HCfNGmSbLMqhLgjOoOOpKwkUnJTMHJ9\nY2xXe1de7fYqDVwbWLE6UTIA7sIFZQBcSIgyzzwxsfz12Uv4+EDz5nDXXcr3s2eVhWduJqPbraPc\ncC/rKr0shYWFHDx4sNIKEkLUfgajga2JW4m7FGc2YE6NmsYejQlyC5Jgt7Ldu5XNUlJSlKVdSxaP\nKW95V39/8zC/ud/cx0e5UpfR7TVDheeZFN3U0RIXF8fUqVPZv39/pRUlhKi9jqcd57vD33Ep55JZ\nsHs6etLMqxlOtk5WrE6Acrt9xgwlhG9WsrxrYKB5mLtZsJS/jG6vOSwK96ysLF577TV27NhBwY07\n3P+tadOmlV6YEKJ2Sc9P5/uj37M/xfwXfUdbR5p6NsXLyQsV0tVnTWlpsHo17NunXLGXUKmUZVw9\nPJSv2NiKL+sqahaLwn3mzJkcPXqUYcOGsXTpUp588kmKiorYvHkzPXv2ZNq0aVVdpxCihtIWa/nl\n9C/878z/zK7UHWwdaOLRhCDXIBkwZ2WFhUrf+q+/Xh8Y5+ysLDDTsCEEBYHt32kQHCzBXhdYFO47\nduxg1qxZREVFsWLFCkaMGEHDhg156aWXGDNmDAcPHuT++++v4lKFEDWJ0Whk76W9rD62mswC8+27\nOgZ3ZGDLgXg4elipOgHKoLi//oKffoLsbPNjffooA+kcHMzbZQBc3WBRuKenp9OwobJilK2tLVqt\nsreyRqNh+vTpvP766xLuQtQjF65d4NvD33I647RZe4hHCE+GP0moZ6iVKhMlTp2C775TAvxGTZrA\n4MHKynBxcTIArq6yKNw9PT05d+4c/v7++Pj4cOTIEZo1a2Y6dv78+SotUghRM+Roc1h7Yi07zu/A\naLxhapuDKwNaDKBzw84yhdbKbuxXv5GHBwwcCB06XN+MRQbA1V0WhXtJv/r3339P165def/999Hp\ndHh4eLBy5UqCgmRlKSHqMr1Bz5bELaw7uY4C3fVBtWqVmpjQGPre1RcnOxkFb02FhcpV+ObN5gvO\n2NlB797Qq1fpW/Ci7rIo3F988UUKCgpwdHRk/Pjx7N69m1dffRUAd3d3Zs2aVaVFCiGs5+jVo6w6\nsoqUnBSz9nC/cAbfPRh/jb+VKhOgzE/fubPsfvUOHZSrdVnLvf6xKNydnZ15//33TY/Xrl3LyZMn\n0el0hIaG4uQkv7ELURfcuBZ8QXEBZzPPkl6QDmBaC97PxY8nwp8g3C/cKjWK606dglWr4Oae0caN\n4YknlH51UT/d9mbJzZs3N/1cVFSEvb19pRQkhLCuYkMxF7IvkJydjIHra5A62jrSt3lfujfpLvus\nW0HJUrEpKcr67Wo1pKebn1NWv7qon275N/TEiROsXLmSlJQUAgMDGTJkSKntXffu3ct//vMfNm3a\nVKWFCiGqVmFxIeezz3Mx+6LZfHWAAJcA3u7+Nm4OFixTJipdXBwsXqxstXrhgrKKnMFwfalY6VcX\nNys33A8dOsTw4cOxs7OjUaNGHDx4kDVr1rBo0SI6depEbm4uM2fOZNWqVaaR80KI2kdbrGVL4hb+\n78z/me2xDuBm70ZTr6a42rtKsFvR+vWQnKwE+40rgF+8CH37KlfrXl7Wq0/UPOWG+4IFC4iKimLe\nvHk4OztTWFjIK6+8QmxsLM8++yxvvPEGOTk5TJs2jdGjR1dnzUKISqDT69iWtI1fTv9CttZ8JJaT\nrRONPRrj4+wjS8ZakU4H27bB2rXKNqw3cnVV1n0fO9Y6tYmardxw379/PwsXLsTZ2RkAR0dHpk+f\nTteuXZk0aRL3338/r776qkyDE6KWKTYU8+f5P9l4aiNZhVlmxxxtHQlxD8HPxU9C3Yp0Oti+XZna\ndu2asjRsSbg7OCgD5vz8lKVjhShLueGenZ1tWpWuhK+vL46Ojrz55ps88sgjVV6cEKLy6A16dl3c\nxYZTG0jPNx+J5enkSd+7+mLAgBpZB95adDrYsUMJ9awbfu9q2BDOnVO+BwQog+lAlooV5bvlgDob\nG5tSbSqVinbt2lVZQUKIymUwGtiTvIf1J9dzNe+q2TE3BzceuushujTqgp2NHV1DulqpyvqtuFgJ\n9U2bzEMdlJHxTz4Jjo7Kxi+yVKywhMxnEaKOMhqNxKfEs+7EOi7nXjY7prHX8GCzB7mv8X3Y28g0\nVmspLlYWoNm4ETLN997BzU0J8G7dlNHwAJ06VX+NonYqN9xVKpWsES1ELWQ0Gjlw+QDrTq4jOTvZ\n7JiznTO9mvaie5PuONjKnClrKS5WdmvbuBEyMsyPubkp09ruu+96qAtRUeWGu9FopF+/fqUCvrCw\nkCeeeAK1+nq/nEqlYvv27VVXpRCilBtXkwMwYiSzIJPEa4m0CzDvOnO0daRn057ENImRNeCtSK9X\nrtQ3bSq9AI2r6/UrdVkTTNypcsN9wIAB1VmHEOI2GTGSVZhFUlYS2UXmU9ocbB3o3qQ7PUN74mLv\nYqUKhV4Pu3bBhg1lh3qvXsqVuixAIypLueF+41ryQoiax4iR9Px0LmZfLBXqdjZ2PND4AXo17YWr\ng6uVKqzf4uKUMD94UAl0b29l+loJjUYJ9fvvl1AXlU8G1AlRy2iLtey8sJO4S3EUFheaHVOjpoFr\nA97t/i7uju5WqlDs3AkffKCsIFfw9w65V/+eqNCkidKnLqEuqpKEuxC1RFZhFn+c+4NtSdvI1+Wb\nBbsaNf4afxq5N8LBxkGC3UqysmDLFvjvf0uPfrezU67W33tPmdYmRFWScBeihkvOTmbz2c3sSd6D\n3qA3O2antqOBpgGBboHYq2UUlrVcuKDMQY+LU/rXb5yrbmsLwcEQFKQEvAS7qA4S7kLUQEajkWNp\nx9h8ZjNHrx4tddzXxZdmXs3wd/HHRlV6sSlR9YxGOHRICfWTJ82POTsrIR8UpKwoV7IeWGBg9dcp\n6icJdyFqkGJDMXHJcWw+u7nUHHWApl5N6RnakzYBbVCrZJlYa9BqlT7133+H1NTSx5s2ha5dlQ1f\nbl4qRJaLFdVFwl2IGiBfl8/WxK38kfgH1wqvmR1TqVREBkTSs2lPQj1DrVShyMyEP/5QNnTJzzc/\nplZD+/bQo4eyqQtAWJiyRrwsFyusQcJdCCtKy0/j17O/svPCTrTF5nt6Otg60LlhZ3qE9sDH2cdK\nFYrEROXWe3w8GAzmx5ydlav0Bx4AT0/zY9HREubCeiTchagmN64ol12UTXJ2Mmn5aRgx0q1RN9Mx\nd0d3Hmj8AN1CusnCM1ZiMCjz03/9FU6fLn3czw+6d4fOnWU6m6iZJNyFqCZ6o560/DRSclJKLToD\nEOgaSK+mvYgOisZWLX81q1NcnLIk7MWLUFSk9JWXtQRs8+bKrfeIiOvbrgpRE8m/IEJUsYvZF9me\ntJ3dybspNhSXOu7p6Mlz9zxHS5+WslmTFcTFwZw5cPkyXLmibOoC0KKFcoWuViu313v0gEaNrFur\nEJaScBeiCmiLtey9tJdtSdtIzEoEMAt2NWp8XXwJdgvGxc6FVr6trFRp/aXVwt698MYbyhX7za5c\ngbncCU8AACAASURBVBEjlJXkPDyquzoh7oyEuxCVKCkrie3nt7MneU+pAXIATrZOBGgC8Nf4y6Iz\nVmA0wrlzsGOHEuxaLSTfNOPQ2fn6/PRHH7VOnULcKQl3Ie5QYXEhe5L3sC1pGxeuXSh13FZtS2SD\nSLIKs3B3dEeF3Hqvbrm5sHu3EuqXLpkfc3ZW1n/39lYC3cND6XMPDrZOrUJUBgl3IW6D0WgkMSuR\n7ee3E5ccR5G+qNQ5/hp/uoV0457ge9DYa4hLjrNCpfWX0QjHjyuBfuDA9b70GzVoAKNGKdPc7OzM\nj8mCM6I2q/ZwT09P56OPPmL79u3k5+fTrFkzpk2bRqdOnQBYv349S5YsITExEV9fX/r06cPUqVOx\nsZElNoX15evy2X1xN9vPby9zBTlbtS3tA9vTtVFXmnk1Mxsg91m/z6qz1HorMxP+/FNZRe7mvdNB\nGQUfHQ1duig7tKlUyqA6WXBG1CXVHu4TJ05Eo9Hw448/4ubmxvz585k4cSK//PILSUlJTJ8+nZkz\nZxITE8O5c+eYMGECdnZ2TJ48ubpLFfVcybx0I0aytdlczr3M1fyrGIwGs3npoExj6xrSlY5BHWVu\nuhUUF0NCgnKVfuSIctV+syZNlECPiiq9eYssOCPqmmoN95ycHJo2bcqYMWPw9fUFYNy4cSxatIhD\nhw6xbt06unXrRp8+fQAICwtj5MiRfPLJJ0ycOBG1TCwV1aiwuJCr+Ve5kneFfF1+qeP2NvZEBUbR\nNaQrTTyayDS2alQyL/3MGWVQnK0tuJTxO5WLC9xzjxLqsmmLqE+qNdxdXV157733zNouXFAGIAUE\nBHDgwAGGDh1qdrx169ZkZWWRmJhIaKisqy2qVrY2m/hL8exJ3sOeS3vKPEdjr2FoxFA6BHXAyc6p\nmisUW7ZAbKwyVS37hrWASualA7RsqQR627ZK8AtR31j1j31ubi4vv/wyMTExREREkJGRgbu7u9k5\nnn8v2JyRkSHhLqpEvi6fA5cPsCd5D8fTjmMs456ujcoGPxc/AjQBaOw13Nf4PitUWn/l58P+/coV\n+8qVyuj3m6WlKYPjOncGH1mKX9RzVgv35ORkJkyYgI+PDx999JG1yhD1VJG+iENXDhGXHMfh1MNl\nrhynQoWnoye+Lr74OPvIvunVrLBQ2S89Lk7pR9frlfa8vOvnqFTXp7B5e0P//tapVYiaxirhfujQ\nISZMmECvXr145ZVXsPt7DoqPjw9ZWVlm52ZmZgKY+uiFuF16g55jacfYk7yHA5cPlLnIDMBd3nfR\nIagDOoMOO7VdmeeIqqHTKQPj4uKU7zpd6XOcnZVb7b6+ylfJFLagoOqtVYiarNrD/eTJk4wbN47/\n396dh0dV3Y8ff0/2dbIOCWtIAoFAAEMCgj7SghupICqCiGAoKtSW+lNLEKiVqmAFEVtjqyC7LGoR\nFFq0iK3i8hVZQ2QNkGASAgnZ9/X+/jjOJJOZhDXJZPJ5Pc99wtw593JuTuZ+5p71ySefZNq0aWbv\nRUdHk5SUZLbvwIEDGAwGesikzuIaaJrG6bzT/JD5AweyDlBaVWo1XQ+fHgzpOoQhXYbg566agjYe\n2diaWe2wamrg2DE1Y9zhw6qDnDU9e6oe7RMnwocfWr4v49KFqNeqwb22tpa5c+cyYcIEi8AOEB8f\nz5QpU9i5cyd33HEHJ0+eZM2aNUyfPl16IotmNVxOVUOjtKqU7LJsckpzuLnrzVaPCfIKYkiXIQzt\nOpQgryCL92Vcesupq4OTJ9UT+qFDqk3dmm7d1NC12Fj1lG6k18u4dCGa06rB/dChQxw9epRTp06x\nbt06s/fGjRvHwoULWbZsGW+++SZz5swhMDCQqVOnMn369NbMpmiHNDRKqkrIK88jpzSHshrr0cLX\nzZehXYcypOsQuuu7y5fGVmActnb+vFr73GCAggIoLraePihIBerYWDWDnDUyLl2I5rVqcI+NjeXk\nyZPNprnrrru46667WilHoj2rqavh5KWTJF1MUgu11Fqvz/V08SSmcwxDuw61mDVOtKz/+z944w3I\ny1O92Y1V7g2HrYHqDBcbqwJ2t26qo5wQ4trJCFDRrpRWlfJj9o8cvnCYozlHTZ3iGgd2R50jgR6B\nGDwNvHbnazg6SE/31lJaqjrDJSXBmjVQWGiZJiMDeveur3I3TgMrhLgxJLgLm3ep7BJJF5I4fOEw\np/NOU6fVWU3n7OCMv7s/Ae4B+Ln7mYauSWBvWZqmJpRJSlJD186cqZ/+teEkM6B6tgcGqqr3V18F\nmXRSiJYhwV3YHE3TOFd4zhTQzxefbzJtoEcgNwXfRGFlIXpXvSyn2kpqa+H0aRXMjxyB7Gzr6Tw8\nVNqAALX5+NQvpyqBXYiWI8FdtImGvdsB6rQ6CioLyC3LJbZLLIUVVupyfxbqF8qgoEEMCh5EZ6/O\n6HQ6dp/d3dJZ7vDKytRkMklJ6mdTPdx1OggLg4EDYdw4+Phjyyp3GbYmRMuS4C7aTFVtFfkV+eSW\n55Jfnk+tpqYgaxzYnRyciDREMihoEAODBuLj5mNxLhm2duMYe7dnZakhZz16qI5wKSlqCJs1rq7Q\nr58K6AMGgLd3/XsGgwxbE6K1SXAXraaqtoqU3BSO5RxTE8pUW59QBlQP94FBAxkUNIh+hn64Orm2\nYk47ru++g7/+VQ1Vy82tfzpv3LsdwM8PBg1SAT0ion6muMZk2JoQrU+Cu2gxdVod6YXpHMs5xvFL\nxzmTd8Y0h7u1wO7u5E6gRyCzb5lNuH84DjpplG1pdXXw009w/Lja3n/f+vjzjAwV3Hv2VMF84EAZ\nsiaELZPgLm6o3LJcUzA/celEk9O9AjjggN5Vj5+7HwEeAbg7uaNDR++A3q2Y445F01Tnt+PH4cQJ\nNUtcw7bzxqutOTioJ/TAQFiyRHWIE0LYPgnu4rqUV5dzMvckx3OOcyznGNmlTXSb/lkX7y5EGiLJ\nq8jDx9VHVlprBUVFKpAbn85/XovJKg8P9QXAzw98fdXm6Kie0iWwC9F+SHAXl9V43vaiyiIKKgrI\nL89ncOfBTY47B9C76ok0RNLP0I++gX3xdfMF4IuzX7R4vjuChp3fOneGuDjVoS0lpT6gZ2Y2fw5f\nX9WmHhkJDz0EH3xgmUZ6twvRvkhwF82qqauhqLKIwspCCisKKaosokarX/u8cWB3dnQmIiCCfoZ+\nRAZG0sW7i9XpXqV3+/Xbtw9WrlTt5iUlkJoKO3eqpU8DA5s+zs0N+vRRwbxvX7UWesMi8vaW3u1C\ntHcS3IWZ8upyzuafJSUvhdN5p0nNT+XwxcNNptfpdPTw6UFkYCSRhkh6+ffCyUH+rFpSWRmcPQvL\nlkFamqp2bzhErbbWPLg7Oqpx55GRauvZs/kJZKR3uxDtn9yFO7iiyiJSclNMwTyjKAPNOHdoE9yc\n3PBz88PXzZfX73odTxfPVsptx5SXp6Z0PX1abZmZql08Kal+mteGSktVG7mxqr13bzUOXQjRcUhw\n70A0TSOnLIeUXBXIU/JSyCnNuexx7k7u+Lj54OPqg95Vj5uTm2maVwnsN1ZdnaoONwby06eb7gDn\n4aECOaiqdl9f1RGuXz/4059aL89CCNsjwd0OGTvAaWiUVpVSWKnaygsrChnWbVizx+p0Orrru9PL\nvxe9A3rTy78XCbsSWiPbHULjDnB33KGq0I2B/MwZqKho/hw6HXTvDiEhsH+/mkWu4ZP5uHEtew1C\nCNsnwd1OaJpGfkU+aQVppBakUlxZTHFVsWlK16Y4OTgR6hdKb38VyMP9w3FzcmulXHcsP/wA//iH\nmiSmqAgOHlQ90yMiLGd/a8jVVS2J2quX2kJD1ZM6qC8L0vlNCNGYBPd2qrSqlHOF51Qwz08lrSCN\nokq1vmZ6UXqTx3k4exDuH24K5iG+IZftACc9269NcbHq8HbunPq5ebNqP2/MOPubkV5fH8h79VJP\n6U11gJPOb0IIayS4twPVtdWkF6WbgnhaQdplJ4sxcnV0Re+qN7WZL7t7mdWhaeL6lJWpIG4M5Glp\nlm3lTbWdaxrcdlt9MA8IkGldhRDXR4J7G2u89KmGRll1GcWVxUzsP5HUglQyizKbnSjGyM3JjRDf\nEFILUvF28cbb1RsXRxezNc4lsF8da5PEDByo5mM3BvJz55pez7whDw+1upqXlxpLrterLTQUpkxp\n8UsRQnQgHSK4W7tB20pVZlVdFcWVxRRVFlm0k+85t6fJ4xwdHOmm70aobyg9fXvS07cnwV7B6HQ6\nTl462VrZt2v79sHy5eqpvLhYzfi2fbtaAtVguPzxzs4qbUiIGlt+//2wdausbS6EaHl2H9wbzuLl\n4KDGCK9cqd5r7QBfW1dLRlEGZ/PPcib/DKn5qXyf8f0VHRvkFWQK5KF+oXT17oqzYxNrbIqrpmmq\nPTwzU7WBZ2TApk3qibzxWPL0dMvgbpx/3RjIQ0JUB7fGbeUBAdIBTgjR8uw+uH/6qao6TU9XC19E\nRKiexp991vI31aLKIs7mnzVtaQVpVNdWX/Y4F0cX9C567ut7Hz19exLiG4KHs8cV/7/SAa55lZUq\niDcM5BkZlkPQrAV2UE/yXbrUB/GePdWUr02tZ96QdIATQrQGuw/uWVmQk6Nu0gUFavhRRETz0282\np3EbuZGGxh9v+6Ppqfxs/llyy3Ivez4HnQNeLl74uPqY2sldHdWg5bjecdeWyQ6qcfPL6NFq2tWG\nATwzs/7v4XI8PFQgd3evbyf38lLzsi9Y0PLXI4QQ18rug3vnzurp6uRJdUOvqYFjx1S7Z1UVuLhc\n23mr66opqiwybcVVxbzy9SuXPS7AI4AwvzDTtujrRThwjd80BKDK9X//q28fLy1VX+I+/FBNvdrc\nGPKGPDxU1bpxu+ce1cbu2GhV2jFjbvw1CCHEjWT3wT0uTj2tubmpDlHGqtfaWnjlFXj8cXUjvxr5\nFfkcv3ScmrqaZtM5OzoT4hNiFsx93MwXxZbAfuXq6tRT94ULasvKUtuFC/Dtt/VTsTbUeAw5qFqb\noCDzQN61q5q+tXFnt86dpY1cCNH+2H1wN96IP/tMValmZ6ube6dOKjD85S/w4IPwy19e2djiytpK\nTlw6YTWwN34q76bvJhPENKOpUQyVlXDxYn0AN/7MzlZfyqwpK7O+v6pKLaDSMJAHB19Z+zhIG7kQ\non2y++AO5jdoTYPvvoP331c3/poa9e9jxyA+Xn0BaEpNXQ3Hco5RXac6xbk4uBDkFYS3izd6Vz2v\n3H75anmh/PADvP02lJerwJySAv/5z7WvYObjo57sPTzU5umpttBQeOaZG59/IYSwZR0iuDek08Gt\nt0J4uBoSl/7zTK1HjsDLL8P06arDlDVbjm2huKpYnQcd/Qz90LvqWynn7U9dnZqVLSdHbdnZ9T//\n/W81v3pjR4/C4MFNn9PXVz15d+5s/vPkSVi1yjJ9nPRJFEJ0QB0uuBsFB8PcuWpSkS++UPsKCuCN\nN1S76tix5h2p9p/fz/9S/2d6HeYX1qECe1NV6LW1kJtrHryN/750SdWMWFNcbH1/aalqNjEYrAdx\ntybWtBk6VH1xk/ZxIYTowMEdwMkJJk6EyEhYuxZKSlS1/aefqifBxx5Ty3FeKLnA+qT1AIzoMYLo\nztHMjJnZIaZy1TT45ht45x3VFl5RAadOqSDat6+qQq+7/My4FoxTsbq7q81YnR4erjo6Ol3DX6a0\njwshhNKhg7vRgAHwwguwerXqUQ9w9qyqpn9ociWfly+nsqYSAIOngfhB8XYT2DVNfanJy1NP4Na2\npnqiJyc3X4UOamx4p07qSdxgUP/u1EnNzb5xo2X6CROuLbALIYSoJ7fRn/n4wNNPw65d8PHH6mm0\nvEJj/uZN6HqcJzwc3JydmRkzE3dn97bO7mUZq9HPn1dTng4ZonqKWwveVVXNn6upnujGgO/raz2A\nGwxNV6P37Kme2KUaXQghbjwJ7g3odHD33apD3bvvQnLRN1x0+R4uQFEh/HHsw3T36d7W2TSpq1Nt\n1/n5qr+A8efBg7B7t6r2rqxU6bZuVdXoVzqhS0Pe3qrt3M1NVcMbq9JDQ2HRomufCEiq0YUQomVI\ncLeiZ0+I/38/8Zv178PPS3nqC2/hizW34lMIo0bd2PW2rXVWu+kmKCy0DNwNfxYWWm/vPnDgyid0\nARW0AwLqN39/1dfA31+9Pn7cek/0iROvPbALIYRoORLcrSirLmNd8nJ69alB7wcXTnUjvPxhalBT\nmn76qXqCzcu7siVk6+rqp0Vt/PPwYTUsrKZGbfv3w5YtqmPZtTxlg2U1upOTCuDu7nD77eYBPCBA\n7W/uy4r0RBdCiPZFgnsjmqax9vBaLpVdQgeEdHFj0e0z+WSTC+fOqSFee/aoJ9aQEDW17fffqxnu\nunSxHsAbrzbW0NU+ZTfm6Ql+fqrd2/iztlaNIXdxUUHdOKSvWzf1tH0tpApdCCHaDwnujXx+9nOS\nLiSZXsffFE//zp3oMwc++URNVwuqE1pKSv1xOTmX7zluTVOd1crKVLA2BuyGwbvhPmvTqAYF1a9Z\n39Do0VefPyGEEO2PBPcGUnJT2HZ8m+n1HWF3MLizithOTjB+vKqWP3HCsoe5tadvI51OVX17etZP\njWoc111UpDYnJxWoXVzUFhamhuddi4bz6Us1uhBCdDwS3H9WVFnEuwffpU5TPdTC/cN5IPIBi3T9\n+6ugnJmp5kU3BuWgIHjkEfPgbfzZXJt2RIT1p+zrnTZVqtGFEKLjkuAO1Gl1rDy4ksKKQgC8XLx4\nYvATODo4WqSNi1PBODTUfP/jj19bMJWnbCGEEDeaBHdgx8kdnLx0EgCdTsdjgx/Dz93PatqWCMby\nlC2EEOJG6vDBPfliMjtTdppej4kYQz9Dv2aPkWAshBDCljm0dQbaUm5ZLqsPrTa97mfox696/6oN\ncySEEEJcvw4b3GvqalhxYAVl1Wosmp+7H9Ojp+Og67C/EiGEEHaiw0ayfx79J2kFaQA46ByYETMD\nb1fvts2UEEIIcQN0yOC+L3MfX6Z9aXr9YL8HCfMLa7sMCSGEEDdQhwvuWcVZvHfkPdPrwZ0HMyp0\nVBvmSAghhLixOlRwr6ypZPmB5VTWVALQybMT8TfFo7uRS7wJIYQQbczmgnt5eTl//vOfGTVqFDEx\nMTz00EN8++23131eTdPYmLyRrOIsAJwdnflN7G9wc3K77nMLIYQQtsTmxrm/9NJLHDt2jFWrVtGl\nSxe2bdvGb37zGz755BPCwq6+XXzmjpkAZJVkkZJXv9JLn4A+dNV3vWH5FkIIIWyFTT25FxYWsmPH\nDn7/+98TGhqKq6srkyZNIjw8nPfff/+az1tcVcyZvDOm18FewQR5Bt2ILAshhBA2x6aC+9GjR6mu\nrmbAgAFm+wcOHEhSUlITR11ean4qdagFYbycvejl1+u68imEEELYMpsK7nl5eQD4+vqa7ffz8yM3\nN/eaz1ur1QLgpHMi0hApE9UIIYSwazbX5t6U6+nRHhkYSW55LgHuAdKBTgghhN2zqUfYgIAAAAoK\nCsz25+fnExgYeM3ndXNyo6t3VwnsQgghOgSbCu5RUVG4uLhw+PBhs/0HDx4kNja2jXIlhBBCtC82\nVS3v7e3N+PHjSUxMJCIiguDgYDZt2kRmZiaTJk26pnMuH7v8BudSCCGEsG02FdwB5s+fz5IlS5g8\neTKlpaVERkaycuVKunZtekx6ba3qMHfhwoXWyqYQQgjRZozxzhj/GtNpmqa1ZoZawv79+3nkkUfa\nOhtCCCFEq9q4caPVZmu7CO4VFRX8+OOPGAwGHB0d2zo7QgghRIuqra0lJyeHqKgo3NwsO4vbRXAX\nQgghRD2b6i0vhBBCiOsnwV0IIYSwMxLchRBCCDsjwV0IIYSwMxLchRBCCDtjc5PYXKvy8nIWL17M\nnj17KCwspFevXjz11FPceuutVtN/++23JCYmcvr0aby9vbntttuYN28e7u7urZxzS7m5uSxdupSv\nv/6asrIyevXqxTPPPMPw4cMt0m7dupV58+bh4uJitj8uLo4lS5a0VpabNGrUKC5evIiDg/n3yO3b\ntxMaGmqR3lbLZd++fUyfPt1if01NDffddx9/+ctfzPbbWrmkp6czf/58fvjhB7744gu6detmeu9f\n//oXq1atIi0tDYPBQFxcHE899VSTw0rz8vJYtGgR+/bto7y8nMjISObMmUNUVFRrXU6z17Nx40Y2\nbtxIVlYWfn5+3HfffcyaNcvib9CoT58+ODs7WyxOdeDAAYvyawlNXUtiYiJ///vfcXZ2Nkv/2GOP\n8fTTT1s9V1uXTVPXcvfdd3P+/HmztJqmUV1dzcmTJ62eqy3L5XL34HbxmdHsxNy5c7V7771XO3v2\nrFZRUaFt3rxZi4qK0s6cOWORNjU1VYuKitLWr1+vlZWVaT/99JN2//33a3Pnzm2DnFuaOHGiNn36\ndC07O1urqKjQli5dqt10003ahQsXLNJ+9NFH2siRI9sgl1dm5MiR2kcffXRFaW29XBrLzs7Whg4d\nqu3du9fiPVsql127dmnDhw/X5syZo0VERGjp6emm9/bu3av1799f27lzp1ZZWamdOHFC++Uvf6kl\nJiY2eb6pU6dq06ZN07KysrSSkhLtjTfe0IYOHarl5eW1xuU0ez2bN2/WYmJitL1792o1NTXa/v37\ntejoaG3t2rVNni8iIkL7/vvvWyPrFpq7ljfffFObMmXKVZ2vLcumuWux5plnnmn2s92W5dLcPbi9\nfGbsolq+sLCQHTt28Pvf/57Q0FBcXV2ZNGkS4eHhvP/++xbpP/jgA8LCwpg6dSru7u50796d3/72\nt2zfvt20pnxbKS4uJjw8nPnz52MwGHB1deWJJ56grKyMI0eOtGneWpotl4s1CxYsIC4ujqFDh7Z1\nVppVUFDAxo0bGTdunMV7GzZsYMSIEcTFxeHi4kKfPn2YNm0a7733HnV1dRbpT506xd69e5kzZw7B\nwcF4enoya9YsdDod27dvb43LafZ6qqqqSEhIYOjQoTg6OhITE8OwYcP4/vvvWyVvV6u5a7labV02\nV3Mtu3fvZt++fcybN6/F83W1LncPbi+fGbsI7kePHqW6upoBAwaY7R84cCBJSUkW6Q8fPszAgQMt\n0tbU1HD06NEWzevleHt788orrxAeHm7al56eDkBwcLDVY0pLS/nd737H8OHDue2225g/f77Fsrlt\n6dNPP+VXv/oVMTExPPDAA+zevdtqOlsul8b++9//cvDgQWbPnt1kGlsplwkTJlhtAoGmf+cFBQWk\npaVZpE9KSsLZ2Zm+ffua9jk5OdG/f3+rn7WW0Nz1PProozz00EOm15qmkZmZSefOnZs953vvvced\nd95JbGwsDz/8MPv377+heW5Kc9cCav7wX//619x8882MGjWKxYsXU1FRYTVtW5fN5a7FqKKigpde\neonnnnsOvV7fbNq2KJfL3YPby2fGLoK78anO19fXbL+fnx+5ublW0/v4+FikBaymb0slJSXMmzeP\n22+/3eLLC6h8h4eHM2XKFL7++mtWrFjBoUOHSEhIaIPcWoqIiCAsLIwNGzbw1VdfceeddzJr1iyL\nZX2h/ZRLXV0dy5YtY8aMGXh5eVlNY+vlYtTc79xabYkxfeN2UF9fX5sqI6O///3vnD9/3mp/CaP+\n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\nTkSuQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_results(system)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.1" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/code/soln/salmon_soln.ipynb b/code/soln/salmon_soln.ipynb deleted file mode 100644 index 400ed04ab..000000000 --- a/code/soln/salmon_soln.ipynb +++ /dev/null @@ -1,1933 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Case Study: Predicting salmon returns\n", - "\n", - "This case study is based on a ModSim student project by Josh Deng and Erika Lu.\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Can we predict salmon populations?\n", - "\n", - "Each year the [U.S. Atlantic Salmon Assessment Committee](https://www.nefsc.noaa.gov/USASAC/Reports/USASAC2018-Report-30-2017-Activities.pdf) reports estimates of salmon populations in oceans and rivers in the northeastern United States. The reports are useful for monitoring changes in these populations, but they generally do not include predictions.\n", - "\n", - "The goal of this case study is to model year-to-year changes in population, evaluate how predictable these changes are, and estimate the probability that a particular population will increase or decrease in the next 10 years.\n", - "\n", - "As an example, I'll use data from page 18 of the 2017 report, which provides population estimates for the Narraguagus and Sheepscot Rivers in Maine.\n", - "\n", - "![USASAC_Report_2017_Page18](data/USASAC_Report_2017_Page18.png)\n", - "\n", - "At the end of this notebook, I make some suggestions for extracting data from a PDF document automatically, but for this example I will keep it simple and type it in.\n", - "\n", - "Here are the population estimates for the Narraguagus River:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "pops = [2749, 2845, 4247, 1843, 2562, 1774, 1201, 1284, 1287, 2339, 1177, 962, 1176, 2149, 1404, 969, 1237, 1615, 1201];" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To get this data into a Pandas Series, I'll also make a range of years to use as an index." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "range(1997, 2016)" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "years = range(1997, 2016)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the series." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
19972749.0
19982845.0
19994247.0
20001843.0
20012562.0
20021774.0
20031201.0
20041284.0
20051287.0
20062339.0
20071177.0
2008962.0
20091176.0
20102149.0
20111404.0
2012969.0
20131237.0
20141615.0
20151201.0
\n", - "
" - ], - "text/plain": [ - "1997 2749.0\n", - "1998 2845.0\n", - "1999 4247.0\n", - "2000 1843.0\n", - "2001 2562.0\n", - "2002 1774.0\n", - "2003 1201.0\n", - "2004 1284.0\n", - "2005 1287.0\n", - "2006 2339.0\n", - "2007 1177.0\n", - "2008 962.0\n", - "2009 1176.0\n", - "2010 2149.0\n", - "2011 1404.0\n", - "2012 969.0\n", - "2013 1237.0\n", - "2014 1615.0\n", - "2015 1201.0\n", - "dtype: float64" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pop_series = TimeSeries(pops, index=years, dtype=float)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what it looks like:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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W97wfvKTn/i3LV26lEWLFgW1jU6uK1otWbKE5cuXc9lll/m33XPPPf3Yop6TACXEIHf11Vdz9dVXB2xLSUnp0bni4+OJj48PRrNEECUlJfV3E3pEhviEGOSGDBlCZmZmwFdsbCwAO3bs4IorruCEE05g/vz5fP3rX6eqqooVK1awcuVKVq5ciVIKpRRw/LDZnXfeyR133MEjjzzCvHnzWLRoEe+++y6HDx9m2bJlzJ07l2uvvZaKigr/a/7+979z8cUXM2fOHBYvXsyjjz7qzxDr6LoAH330EZdddhmzZ8/mvPPO4/nnnw/4OdetW8f555/P7Nmzuf766zl69CidaflZXn75Zc4880zmzp3L/fffHzD5ury8nO9+97vMnTuXBQsWcP/991NfX+/fv2zZMn72s59x2223ccIJJ7BkyRLeeuut465ht2LFCpYtW9Zhu3784x9z9tlnM2fOHC644AJef/31gOsdPnyYu+66C6WU/zxth/ictPsXv/gF9957L3PnzmXJkiW88cYbnf6+QkEClBCiQz/4wQ844YQTePXVV3n++ee56KKLANPrOv/88zn//PP54IMP+OCDjtehe+edd0hMTOSll17inHPO4c477+SHP/wh1113HS+88AKHDh3iiSee8B/v8/m48847ee211/jRj37ESy+9xIsvvtjpdXNzc7n55pu58sor+fe//82dd97JihUr/MHg2LFjLF++nFNPPZWVK1eyaNEifve733X585eVlbFy5Up+//vf8/jjj7N69WqefPJJ//7bb7+d4uJinn32WX7729+ydu1afvrTnwac4/nnn2fixIm8/PLLfOlLX+LWW2/l4MGDDv8Cx0tNTeVXv/oVr732Gt/4xje4/fbb0VoDJrhlZ2dz991388EHH3R478pJu1944QUmT57MK6+8wsUXX8xdd91FeXl5j9vdEzLEJ0SIbdLFrN9ZSFOzt0+uFxMdxYLp2cxVzmb1P/HEEzz11FMB21avXk1aWhoFBQWcffbZjBkzBoDJkyf7j2kZysvMzOz0/GPGjGH5clPT+YYbbuCZZ57hjDPO4IwzzgDgsssu4513/Itp8+Uvfzngtd/61rd46623uPLKK0lMTGz3uk8++SRXXHEFl19+uf913/zmN/nb3/7Geeedx6uvvsrQoUO5++67cbvdTJw4kQ0bNrBp06ZO215f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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_population(series):\n", - " plot(series, label='Estimated population')\n", - " decorate(xlabel='Year', \n", - " ylabel='Population estimate', \n", - " title='Narraguacus River',\n", - " ylim=[0, 5000])\n", - " \n", - "plot_population(pop_series)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Modeling changes\n", - "\n", - "To see how the population changes from year-to-year, I'll use `diff` to compute the absolute difference between each year and the next.\n", - "\n", - "`shift` adjusts the result so each change aligns with the year it happened." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 96., 1402., -2404., 719., -788., -573., 83., 3.,\n", - " 1052., -1162., -215., 214., 973., -745., -435., 268.,\n", - " 378., -414., nan])" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "abs_diffs = np.ediff1d(pop_series, np.nan)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can compute relative differences by dividing by the original series elementwise." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1997 0.034922\n", - "1998 0.492794\n", - "1999 -0.566047\n", - "2000 0.390125\n", - "2001 -0.307572\n", - "2002 -0.322999\n", - "2003 0.069109\n", - "2004 0.002336\n", - "2005 0.817405\n", - "2006 -0.496794\n", - "2007 -0.182668\n", - "2008 0.222453\n", - "2009 0.827381\n", - "2010 -0.346673\n", - "2011 -0.309829\n", - "2012 0.276574\n", - "2013 0.305578\n", - "2014 -0.256347\n", - "2015 NaN\n", - "dtype: float64" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "rel_diffs = abs_diffs / pop_series" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Or we can use the `modsim` function `compute_rel_diff`:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1997 0.034922\n", - "1998 0.492794\n", - "1999 -0.566047\n", - "2000 0.390125\n", - "2001 -0.307572\n", - "2002 -0.322999\n", - "2003 0.069109\n", - "2004 0.002336\n", - "2005 0.817405\n", - "2006 -0.496794\n", - "2007 -0.182668\n", - "2008 0.222453\n", - "2009 0.827381\n", - "2010 -0.346673\n", - "2011 -0.309829\n", - "2012 0.276574\n", - "2013 0.305578\n", - "2014 -0.256347\n", - "2015 NaN\n", - "dtype: float64" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "rel_diffs = compute_rel_diff(pop_series)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "These relative differences are observed annual net growth rates. So let's drop the `NaN` and save them." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1997 0.034922\n", - "1998 0.492794\n", - "1999 -0.566047\n", - "2000 0.390125\n", - "2001 -0.307572\n", - "2002 -0.322999\n", - "2003 0.069109\n", - "2004 0.002336\n", - "2005 0.817405\n", - "2006 -0.496794\n", - "2007 -0.182668\n", - "2008 0.222453\n", - "2009 0.827381\n", - "2010 -0.346673\n", - "2011 -0.309829\n", - "2012 0.276574\n", - "2013 0.305578\n", - "2014 -0.256347\n", - "dtype: float64" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "rates = rel_diffs.dropna()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A simple way to model this system is to draw a random value from this series of observed rates each year. We can use the NumPy function `choice` to make a random choice from a series." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.27657378740970073" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.random.choice(rates)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Simulation\n", - "\n", - "Now we can simulate the system by drawing random growth rates from the series of observed rates.\n", - "\n", - "I'll start the simulation in 2015." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1201.0" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_0 = 2015\n", - "p_0 = pop_series[t_0]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a `System` object with variables `t_0`, `p_0`, `rates`, and `duration=10` years. \n", - "\n", - "The series of observed rates is one big parameter of the model." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
t_02015
p_01201
duration10
rates1997 0.034922\n", - "1998 0.492794\n", - "1999 -0.56...
\n", - "
" - ], - "text/plain": [ - "t_0 2015\n", - "p_0 1201\n", - "duration 10\n", - "rates 1997 0.034922\n", - "1998 0.492794\n", - "1999 -0.56...\n", - "dtype: object" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = System(t_0=t_0,\n", - " p_0=p_0,\n", - " duration=10,\n", - " rates=rates)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Write an update functon that takes as parameters `pop`, `t`, and `system`.\n", - "It should choose a random growth rate, compute the change in population, and return the new population." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def update_func1(pop, t, system):\n", - " \"\"\"Simulate one time step.\n", - " \n", - " pop: population\n", - " t: time step\n", - " system: System object\n", - " \"\"\"\n", - " rate = np.random.choice(system.rates)\n", - " pop += rate * pop\n", - " return pop" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your update function and run it a few times" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "981.6159728122345" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "update_func1(p_0, t_0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a version of `run_simulation` that stores the results in a `TimeSeries` and returns it." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Simulate a queueing system.\n", - " \n", - " system: System object\n", - " update_func: function object\n", - " \"\"\"\n", - " t_0 = system.t_0\n", - " t_end = t_0 + system.duration\n", - " \n", - " results = TimeSeries()\n", - " results[t_0] = system.p_0\n", - " \n", - " for t in linrange(t_0, t_end):\n", - " results[t+1] = update_func(results[t], t, system)\n", - "\n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use `run_simulation` to run generate a prediction for the next 10 years.\n", - "\n", - "The plot your prediction along with the original data. Your prediction should pick up where the data leave off." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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VlYWHZ81axa/+c1vuPnmmxkzZgxTp07lzTffrLGHk0ceeYRZs2bVKtfdd9/NtGnTGD16NOeccw6vv/56zH65ubksWLAApVR0neouPjdy/+53v+P2229n7NixTJ06lTfecNooicWtBdUL2BHnfqABa8SglFoMfBuYorV+z743EXgMOBbYDPxUa/2G45kM4FGMwqwEngRu1VqHHHO+j7HqegGfAD/QWm9sjIytTfBwPAVV3AqSCM1ByeZVlGz4AivUMueIHl+AtGHjSRs8uslr3XLLLZx66qncf//9lJWVRVtbzJ49m02bNgH1u4/eeustZs+ezaJFi3j22WeZP38+I0eOZM6cOXTv3p2f/OQnLFy4kHnz5gGmG+78+fPJyclh8+bN3H777XTv3p3LLrus1n03b97M9ddfz2233caECRPYuHEjt912G127duXMM8+ksLCQuXPncv755/Pd736Xzz77jAceeKBey+LAgQMsXryYP//5zxw4cIB58+bRq1cvrrvuOgBuvfVWioqKePbZZyktLeW2227j/vvv54477oiu8fzzzzNnzhyuu+463nzzTX76059y9NFHk5OT08DfhqFz5848+OCDZGdn8/HHH3PrrbcyZMgQlFI88sgjXHDBBcyePZvp06fHdAx24kbuF154geuvv55XX32VxYsXs2DBAiZOnEiXLl0aJXdDcGtBrQNOinP/28CqOPfrRCk1C0ivdq8rxn34EXAc8AywWCk1zDHtMeB44HTg/4CZwM8ca0wFFmJcjscDe4H/KKXaZb5XPAsqVCoWVHuldMuqFlNOAFaoktIt7v97Lly4kLFjx8a8ItbF7t27mTZtGjk5OQwbNoxLLrmETp06kZ6eHnXnRayu2sjJyWHu3LkMHDiQa6+9lvz8fCZPnszkyZMZMWIEM2bM4IsvvojOv/jii5k0aRI5OTmceuqpXHnllVGro7Z9H3/8cWbOnMmFF15ITk4OU6ZM4YorruDvf/87AP/617/IyMjgtttuY8iQIcycObPeMzaAsrIy7rrrLkaMGMFJJ53EDTfcwHPPPQcYpbh06VLuvfdejjnmGMaPH8/tt9/O3//+d4qLq75Qjhgxgh/+8IcMHjyYH/7wh4wcOZKXXnrJ9e+nOj/60Y8YNWoUOTk5XHLJJZx00km89dZbgFFePp8v2hW5c+eaxwJu5R4/fjxXXHEFAwYMYO7cuViWxerVqxstd0Nw+8F9H/CYbcF4gNOUUtcCPwYubsiGSqm+GAUyGdjmGLoMKAB+orW2gDVKqbOBa4CblVLZ9pwztNaf2WvdDtyrlLpLax0G5gIvaK3/ao/PBvYBZwH/boicrY0VDsXNexILqv2SOmh0i1tQqYPcW0+XXnppDZdS5Azq8ssv56qrruKkk07ipJNOYvr06WRnZzdInqFDh0bfd+3aNe69gwcPRq9XrlzJI488wvr16ykqKiIYDNbbaHD9+vWsX78+qjwAgsEgffr0AWDLli0cc8wx+Hy+6Pjo0aNZsWJFnetmZWUxYMCA6PXIkSPZv38/hYWFbN68maysLAYNGhQdHzt2LJWVlezYsYMRI0YAMGrUqJg1R40axZYtW+rcty5effVVnnnmGXbu3ElFRQUVFRV1fkGojlu5ne5Tv99Ply5dYn5PicSVgtJav6iUSgF+AaQBf8G4/K7WWr/WwD3/Atyjtd7u/MGBCcC7tnKKsARjLQGMw5x/fVBtvAcwCNhkrxG1qLTWxUqpT4GJtDMFFSrKxwpHPJceIkd/cgbVfkkbPLpZ3G2JovqHsJOf/vSnnHfeebz33nv885//5OGHH+bll19ukHvK6WbyeDyA+cBz3os0UC0uLmbOnDlMnz6d66+/nqysLF5//XUWLVpU5x4lJSVcddVVfOc734m5H9nHsqzo3hHcNG2t/kxj5te1htfrrSFH5LwtHl9++SW333478+bNY9y4caSnp3PPPffU+Ywb4sno/B1F5oTD4Sbt4xbXri+t9ZPAk0qpboBXa723oZsppa4BAlrrhXGGewDVv8bss+9Hxg86z5vs8cjYJvvP6nI512g3OPOfAp17UJmfB5gwc0FoDYYPH87w4cOZM2cO55xzDm+//Tbf//738fv9lJeXN+temzdv5vDhw9xyyy2kp5vTgLy8vJg58fYdMWIEW7durVXRDho0iHfeeYdwOIzXa044vvrqq3rlyc/PZ/v27fTv3x+Ar7/+mm7dupGZmcmgQYM4fPgwW7ZsiVojy5cvJxAIxCjw6vt8/fXXHHfccQBkZ2dz6NAhQqFQ1LrTWtcqz8qVKxk+fHjU4rUsi+3bt8dYtX6/n1AoVNsSruVuTVydQSml3lFKdQbQWu+PKCelVCel1Dsu1+iPscCurmVKfV9R4o132H71zgoSge45eDzmVyXJukKiKCkpYd++fTGvsrKy6PnLF198QW5uLu+//z67du1i4MCBAPTp04d169aRm5vbbK6fPn36EAgEePbZZ9mxYwcvvfRSTNRbbfteddVVLFmyhIcffphNmzaxYcMGXn75ZV544QUAzjvvPA4fPsw999zD5s2beemll1i6dGm98qSkpPDzn/+cdevWsWzZMh555BG++93vAjBkyBBOPvlkFixYwDfffMMXX3zB3XffzUUXXRRVrmBaoS9cuJAtW7awcOFCVq1axSWXXAIYl6FlWTz22GNs27aNp59+OuY8rjr9+/dn48aNvPfee2zevJm77rqrhgLv06cPy5cvZ9++fRQWFtZYw63crYnbIInTgHjVI5IxZ0luOA4TWbdRKRVUSkVs0SVKqb8CedS0dLpTZRHlAV2UUj7HeGT+Xsefda3RbnAGSPg7dcObUvUPRkoeCYngiSee4OSTT455/etf/8Lr9XLgwAFuuukmzjzzTO68807mzp3LlClTALjooovIyspi+vTpnHjiic0iS9euXbnzzjt5/vnnOffcc1m6dCnXXnttzJx4+x577LE88cQTLFu2jO985ztcdtllvPrqq/Tt2xeATp068eijj7J06VIuuOAC3nrrLebMmeNKnnPPPZerr76a6667jlNOOYUf/OAH0fH77ruP7t27c/nll3Pttdcyfvz4aDRihJkzZ6K15tvf/jYvvvgiv/3tb6OWSpcuXbj33nt59dVX+fa3v826deuYOXNmrfKcfvrpXHzxxdxyyy3MnDmTlJQUzjjjjJg5c+fOZeXKlZx22mnRaMPquJG7NfHU5X+1rR6ArcB4wBlW5gOmA/O11vXag0qpTKC63f01MBv4H3AhcKPWepDjmfeBz7XWNyulumAUzemOsPTvA78Gemutw0qpV4ACrfWV9niaLfPFWut6z6CUUgOBLUuWLKFfv371TU8YlmVx4K0nopZSlymXU7hqSbRwbNaEc0nq1nryCe5Yu3YtRx11VGuLITSRV155hUcffZR33nHlLIrLrFmzmDBhAtdff30zStY2qe3f/c6dO5k2bRrAIK31Vjdr1XcGtRXjRrOAz+OMh4EFbjbSWhcCMbGJdpDEFq11rlLqOeCXSqmHMKHi52OCG662nz+olHoeeEQpdRUmTP0u4DE7gg9MGPobSqkPgE+Bn2OCOWJ9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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "results = run_simulation(system, update_func1)\n", - "plot(results, label='Simulation')\n", - "plot_population(pop_series)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To get a sense of how much the results vary, we can run the model several times and plot all of the results." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_many_simulations(system, update_func, iters):\n", - " \"\"\"Runs simulations and plots the results.\n", - " \n", - " system: System object\n", - " update_func: function object\n", - " iters: number of simulations to run\n", - " \"\"\"\n", - " for i in range(iters):\n", - " results = run_simulation(system, update_func)\n", - " plot(results, color='gray', linewidth=5, alpha=0.1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The plot option `alpha=0.1` makes the lines semi-transparent, so they are darker where they overlap.\n", - "\n", - "Run `plot_many_simulations` with your update function and `iters=30`. Also plot the original data." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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M4eJ6667jlWrVgWu63Of+xynnHIKhx9+OKeffjo///nPq+bbunUrl156KUopdxyviy/Mur/0pS9x2WWXceSRR3LyySfzi1/sv13om8GCKhaLoZV93jFMF189oUSrWlCWoPZTfPzjH+eII47gZz/7GbfeeitnnHEG4Fhdr371q3n1q1/NQw89xEMPBfcVu+eee+ju7uanP/0pp556Kpdccgmf+cxnOPfcc7ntttvYsmULN9xwg3t9uVzmkksu4e677+azn/0sP/3pT7n99tvrzrt+/XouuOACzj77bP73f/+XSy65hOuuu84lg4GBAVavXs1LX/pS7rzzTk488US+8Y1vDPv69+zZw5133sk3v/lNrr/+eu677z5uvPFG9/xFF13Ezp07+cEPfsDXv/51Hn74YT7/+c9XjXHrrbdyyCGHcMcdd/DmN7+ZCy+8kM2bN4f8C9Ri5syZfOUrX+Huu+/mne98JxdddBFaa8Aht/nz5/OJT3yChx56KDB2FWbdt912G8uXL+euu+7ita99LZdeeil79+4d8bpbGZNR5sgLP4KKRqO+yj4Zw68aepAVFVR/rxUIyrr4RoG/6J088vft5AvhEuVGi3isjWNfMJ8jVbj+izfccAM33XRT1bH77ruPWbNm8dxzz3HKKaewZMkSAJYvX+5eI668uXPn1h1/yZIlrF7t1Or9wAc+wM0338zxxx/P8ccfD8CZZ57JPfe4TZJ5y1veUnXvu9/9bn71q19x9tln093d7TvvjTfeyFlnncUb3/hG9753vetd/OhHP+KVr3wlP/vZz+jp6eETn/gE0WiUQw45hEcffZS//OUvddeeyWS44oorOOiggwD48Ic/zDXXXMP555/P+vXrefDBB/nlL3/JsmVO8fzLLruM888/nwsvvJDu7m4AVq5cyQc/+EEAPvjBD/K73/2O22+/nY997GN15w7Chz70IffxW9/6Vu677z7uuecelFLMnDmTaDTKtGnTAv8uYdd9zDHH8K53vQuA1atX853vfIennnpq2NjkVEQzWFB+ij2vBeVX4dxbKLZYLPqSTqtaT2AJalR4fO2uCSMngHyhxONrd4UmqLe97W01LiWJQb3jHe/gnHPO4WUvexkve9nLOO2005g1a1ZD6zn00EPdx3PmzPE9Zn4zf/zxx7nuuutYu3Ytg4ODFAoFFixYUHeOtWvX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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot_many_simulations(system, update_func1, 30)\n", - "plot_population(pop_series)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The results are highly variable: according to this model, the population might continue to decline over the next 10 years, or it might recover and grow rapidly!\n", - "\n", - "It's hard to say how seriously we should take this model. There are many factors that influence salmon populations that are not included in the model. For example, if the population starts to grow quickly, it might be limited by resource limits, predators, or fishing. If the population starts to fall, humans might restrict fishing and stock the river with farmed fish.\n", - "\n", - "So these results should probably not be considered useful predictions. However, there might be something useful we can do, which is to estimate the probability that the population will increase or decrease in the next 10 years. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Distribution of net changes\n", - "\n", - "To describe the distribution of net changes, write a function called `run_many_simulations` that runs many simulations, saves the final populations in a `ModSimSeries`, and returns the `ModSimSeries`.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "def run_many_simulations(system, update_func, iters):\n", - " \"\"\"Runs simulations and report final populations.\n", - " \n", - " system: System object\n", - " update_func: function object\n", - " iters: number of simulations to run\n", - " \n", - " returns: series of final populations\n", - " \"\"\"\n", - " # FILL THIS IN" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def run_many_simulations(system, update_func, iters):\n", - " \"\"\"Runs simulations and report final populations.\n", - " \n", - " system: System object\n", - " update_func: function object\n", - " iters: number of simulations to run\n", - " \n", - " returns: series of final populations\n", - " \"\"\"\n", - " last_pops = ModSimSeries()\n", - " \n", - " for i in range(iters):\n", - " results = run_simulation(system, update_func)\n", - " last_pops[i] = get_last_value(results)\n", - " \n", - " return last_pops" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your function by running it with `iters=5`." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
0346.726622
13959.847052
23381.191218
3573.275173
41604.959065
\n", - "
" - ], - "text/plain": [ - "0 346.726622\n", - "1 3959.847052\n", - "2 3381.191218\n", - "3 573.275173\n", - "4 1604.959065\n", - "dtype: float64" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "run_many_simulations(system, update_func1, 5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can run 1000 simulations and describe the distribution of the results." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "count 1000.000000\n", - "mean 1659.583323\n", - "std 2986.559055\n", - "min 13.013359\n", - "25% 314.330006\n", - "50% 739.523671\n", - "75% 1692.000458\n", - "max 35721.985552\n", - "dtype: float64" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "last_pops = run_many_simulations(system, update_func1, 1000)\n", - "last_pops.describe()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we substract off the initial population, we get the distribution of changes." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "count 1000.000000\n", - "mean 458.583323\n", - "std 2986.559055\n", - "min -1187.986641\n", - "25% -886.669994\n", - "50% -461.476329\n", - "75% 491.000458\n", - "max 34520.985552\n", - "dtype: float64" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "net_changes = last_pops - p_0\n", - "net_changes.describe()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The median is negative, which indicates that the population decreases more often than it increases.\n", - "\n", - "We can be more specific by counting the number of runs where `net_changes` is positive." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "355" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.sum(net_changes > 0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Or we can use `mean` to compute the fraction of runs where `net_changes` is positive." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.355" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.mean(net_changes > 0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the fraction where it's negative." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.645" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.mean(net_changes < 0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "So, based on observed past changes, this model predicts that the population is more likely to decrease than increase over the next 10 years, by about 2:1." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## A refined model\n", - "\n", - "There are a few ways we could improve the model.\n", - "\n", - "1. It looks like there might be cyclic behavior in the past data, with a period of 4-5 years. We could extend the model to include this effect.\n", - "\n", - "2. Older data might not be as relevant for prediction as newer data, so we could give more weight to newer data.\n", - "\n", - "The second option is easier to implement, so let's try it.\n", - "\n", - "I'll use `linspace` to create an array of \"weights\" for the observed rates. The probability that I choose each rate will be proportional to these weights.\n", - "\n", - "The weights have to add up to 1, so I divide through by the total." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "weights = linspace(0, 1, len(rates))\n", - "weights /= sum(weights)\n", - "plot(weights)\n", - "decorate(xlabel='Index into the rates array',\n", - " ylabel='Weight')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "I'll add the weights to the `System` object, since they are parameters of the model." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "system.weights = weights" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can pass these weights as a parameter to `np.random.choice` (see the [documentation](https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.choice.html))" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.3055780113177041" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.random.choice(system.rates, p=system.weights)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Write an update function that takes the weights into account." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def update_func2(pop, t, system):\n", - " \"\"\"Simulate one time step.\n", - " \n", - " pop: population\n", - " t: time step\n", - " system: System object\n", - " \"\"\"\n", - " rate = np.random.choice(system.rates, p=system.weights)\n", - " pop += rate * pop\n", - " return pop" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use `plot_many_simulations` to plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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dXHnnXdy6qmncumll/L5z3+ec889l9tvv53169dz4403+sdXKhUuvfRS7r33Xr7whS9w5513cscddzS87qpVq7jwwgs5++yz+elPf8qll17K9ddf74vBwMAAF1xwAa985Su5++67OfHEE/nmN7856vvfsWMHd999N9/61re44YYbeOCBB7jpppv8/RdffDFbt27lBz/4Af/2b//Go48+ype+9KXAOW677TYOOugg7rrrLt72trdx0UUXsW7duoi/gVpmzZrF17/+de69917e8573cPHFF6O1BjxxW7BgAZ/+9Kd55JFH6sauoqz79ttvZ9myZdxzzz284Q1v4LLLLmPnzp27vW5HdMLEx+7qEFWgxuLiC3P5TQeRci6+cfBHvZXH/rKZQrE5v+hUMs6xL1nAkSra/MUbb7yRm2++ObDtgQceYPbs2WzatIlTTjmF/fffH4Bly5b5x4grb968eQ3Pv//++3PBBV6v3g9/+MPccsstHH/88Rx//PEAnHnmmdx3nz8kmbe//e2B177vfe/jl7/8JWeffTZdXV2h173ppps466yzeMtb3uK/7r3vfS8/+tGPeO1rX8tPfvITuru7+fSnP00ikeCggw7i8ccf549//GPDtWezWa688koOOOAAAD72sY9x7bXXcv7557Nq1SoefvhhfvGLX7B0qdc8//LLL+f888/noosu8pt1HnrooXzkIx8B4CMf+Qi/+c1vuOOOO/jkJz/Z8Nr1+OhHP+o/fsc73sEDDzzAfffdh1KKWbNmkUgk6Onpqft7ibruY445hve+970AXHDBBXz3u9/lmWeeGTU26Rg/YenjyeTIbbie8NhxI6mTMl8blq1nx53CYlKtjBOocfDkim1NEyeAQrHMkyu2RRaod77znTUuJYlBvfvd7+acc87hVa96Fa961as47bTTmD179pjWc/DBB/uP586dG7rN/Gb+5JNPcv3117NixQoGBwcpFovst99+Da+xYsUKVqxYwa233upvKxaLLFy4EIDVq1fz0pe+NNAg8/DDDx9VoGbOnOmLE8DLX/5ytm/fzsDAAKtWrWLmzJn+TR7gyCOPpFAosG7dOg499FAADjvssMA5DzvsMFavXt3wuo245557uOWWW1i/fj35fJ58Pj/qlwSTqOs23afJZJI5c+Y4C6oJhDV/hWBz13oWViKRCLigw4SsUX89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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "plot_many_simulations(system, update_func2, 30)\n", - "plot_population(pop_series)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use `run_many_simulations` to collect the results and `describe` to summarize the distribution of net changes." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "count 1000.000000\n", - "mean 589.136611\n", - "std 3015.074310\n", - "min -1174.105185\n", - "25% -816.184417\n", - "50% -323.818859\n", - "75% 754.629154\n", - "max 33298.648399\n", - "dtype: float64" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "last_pops = run_many_simulations(system, update_func2, 1000)\n", - "net_changes = last_pops - p_0\n", - "net_changes.describe()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Does the refined model have much effect on the probability of population decline?" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.606" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "np.mean(net_changes < 0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Extracting data from a PDF document\n", - "\n", - "The following section uses PyPDF2 to get data from a PDF document. It uses features we have not seen yet, so don't worry if it doesn't all make sense.\n", - "\n", - "The PyPDF2 package provides functions to read PDF documents and get the data.\n", - "\n", - "If you don't already have it installed, and you are using Anaconda, you can install it by running the following command in a Terminal or Git Bash:\n", - "\n", - "```\n", - "conda install -c conda-forge pypdf2\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [], - "source": [ - "import PyPDF2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The 2017 report is in the data directory." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "<_io.BufferedReader name='data/USASAC2018-Report-30-2017-Activities-Page11.pdf'>" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pdfFileObj = open('data/USASAC2018-Report-30-2017-Activities-Page11.pdf', 'rb')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `PdfFileReader` object knows how to read PDF documents." - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pdfReader = PyPDF2.PdfFileReader(pdfFileObj)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This file contains only one page." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pdfReader.numPages" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`getPage` selects the only page in the document." - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'USASAC ANNUAL REPORT 2017/30\\n11 \\nTable 1.3.2 Estimated Atlantic salmon returns to the USA, 1997-2017. \"Natural\" includes fish originating \\nfrom natural spawning, hatchery fry stocking, or hatchery egg planting Starting in 2003 estimated returns \\nalso include returns estimated adult returns from redd counts. \\nYear \\nSea age \\n \\nOrigin \\n1 SW \\n2SW \\n3SW \\nRepeat \\nTotal\\n \\nHatchery \\nNatural \\n1997 \\n278 \\n1,492 \\n8 \\n36 \\n1,814 \\n \\n1,296 \\n518 \\n1998 \\n340 \\n1,477 \\n3 \\n42 \\n1,862 \\n \\n1,146 \\n716 \\n1999 \\n402 \\n1,136 \\n3 \\n26 \\n1,567 \\n \\n959\\n608 \\n2000 \\n292 \\n535 \\n0 \\n20 \\n847 \\n \\n562\\n285 \\n2001 \\n269 \\n804 \\n7 \\n4 \\n1,084 \\n \\n833\\n251 \\n2002 \\n437 \\n505 \\n2 \\n23 \\n967 \\n \\n832\\n135 \\n2003 \\n233 \\n1,185 \\n3 \\n6 \\n1,427 \\n \\n1,238 \\n189 \\n2004 \\n319 \\n1,266 \\n21 \\n24 \\n1,630 \\n \\n1,395 \\n235 \\n2005 \\n317 \\n945 \\n0 \\n10 \\n1,272 \\n \\n1,019 \\n253 \\n2006 \\n442 \\n1,007 \\n2 \\n5 \\n1,456 \\n \\n1,167 \\n289 \\n2007 \\n299 \\n958 \\n3 \\n1 \\n1,261 \\n \\n940\\n321 \\n2008 \\n812 \\n1,758 \\n12 \\n23 \\n2,605 \\n \\n2,191 \\n414 \\n2009 \\n243 \\n2,065 \\n16 \\n16 \\n2,340 \\n \\n2,017 \\n323 \\n2010 \\n552 \\n1,081 \\n2 \\n16 \\n1,651 \\n \\n1,468 \\n183 \\n2011 \\n1,084 \\n3,053 \\n26 \\n15 \\n4,178 \\n \\n3,560 \\n618 \\n2012 \\n26 \\n879 \\n31 \\n5 \\n941 \\n731\\n210 \\n2013 \\n78 \\n525 \\n3 \\n5 \\n611 \\n \\n413\\n198 \\n2014 \\n110 \\n334 \\n3 \\n3 \\n450 \\n \\n304\\n146 \\n2015 \\n150 \\n761 \\n9 \\n1 \\n921 \\n \\n739\\n182 \\n2016 \\n232 \\n389 \\n2 \\n3 \\n626 \\n \\n448\\n178 \\n2017 363 663 13 \\n2 1041 \\n \\n806 235 \\n'" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "page = pdfReader.getPage(0)\n", - "page.extractText()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function iterates through the lines on the page, removes whitespace, and ignores lines that contain only whitespace." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "def iter_page(page):\n", - " for item in page.extractText().splitlines():\n", - " item = item.strip()\n", - " if item:\n", - " yield item" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function gets the next `n` pages from the page." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [], - "source": [ - "def next_n(iterable, n):\n", - " \"\"\"Get the next n items from an iterable.\"\"\"\n", - " return [next(iterable) for i in range(n)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We skip the text at the top of the page." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['USASAC ANNUAL REPORT 2017/30',\n", - " '11',\n", - " 'Table 1.3.2 Estimated Atlantic salmon returns to the USA, 1997-2017. \"Natural\" includes fish originating',\n", - " 'from natural spawning, hatchery fry stocking, or hatchery egg planting Starting in 2003 estimated returns',\n", - " 'also include returns estimated adult returns from redd counts.',\n", - " 'Year',\n", - " 'Sea age',\n", - " 'Origin']" - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t = iter_page(page)\n", - "discard = next_n(t, 8)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The next 7 strings are the column headings of the table." - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['1 SW', '2SW', '3SW', 'Repeat', 'Total', 'Hatchery', 'Natural']" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "columns = next_n(t, 7)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create an empty `Dataframe` with the column headings." - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
1 SW2SW3SWRepeatTotalHatcheryNatural
\n", - "
" - ], - "text/plain": [ - "Empty DataFrame\n", - "Columns: [1 SW, 2SW, 3SW, Repeat, Total, Hatchery, Natural]\n", - "Index: []" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df = pd.DataFrame(columns=columns)\n", - "df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Get the next 19 lines of the table." - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [], - "source": [ - "for i in range(19):\n", - " year = int(next(t))\n", - " data = next_n(t, 7)\n", - " df.loc[year] = data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The last line in the table gets messed up, so I'll do that one by hand." - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [], - "source": [ - "df.loc[2017] = ['363', '663', '13', '2', '1041', '806', '235'] " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the result." - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
1 SW2SW3SWRepeatTotalHatcheryNatural
19972781,4928361,8141,296518
19983401,4773421,8621,146716
19994021,1363261,567959608
2000292535020847562285
2001269804741,084833251
2002437505223967832135
20032331,185361,4271,238189
20043191,26621241,6301,395235
20053179450101,2721,019253
20064421,007251,4561,167289
2007299958311,261940321
20088121,75812232,6052,191414
20092432,06516162,3402,017323
20105521,0812161,6511,468183
20111,0843,05326154,1783,560618
201226879315941731210
20137852535611413198
201411033433450304146
201515076191921739182
20173636631321041806235
\n", - "
" - ], - "text/plain": [ - " 1 SW 2SW 3SW Repeat Total Hatchery Natural\n", - "1997 278 1,492 8 36 1,814 1,296 518\n", - "1998 340 1,477 3 42 1,862 1,146 716\n", - "1999 402 1,136 3 26 1,567 959 608\n", - "2000 292 535 0 20 847 562 285\n", - "2001 269 804 7 4 1,084 833 251\n", - "2002 437 505 2 23 967 832 135\n", - "2003 233 1,185 3 6 1,427 1,238 189\n", - "2004 319 1,266 21 24 1,630 1,395 235\n", - "2005 317 945 0 10 1,272 1,019 253\n", - "2006 442 1,007 2 5 1,456 1,167 289\n", - "2007 299 958 3 1 1,261 940 321\n", - "2008 812 1,758 12 23 2,605 2,191 414\n", - "2009 243 2,065 16 16 2,340 2,017 323\n", - "2010 552 1,081 2 16 1,651 1,468 183\n", - "2011 1,084 3,053 26 15 4,178 3,560 618\n", - "2012 26 879 31 5 941 731 210\n", - "2013 78 525 3 5 611 413 198\n", - "2014 110 334 3 3 450 304 146\n", - "2015 150 761 9 1 921 739 182\n", - "2017 363 663 13 2 1041 806 235" - ] - }, - "execution_count": 46, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In general, reading tables from PDF documents is fragile and error-prone. Sometimes it is easier to just type it in." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/spiderman_soln.ipynb b/code/soln/spiderman_soln.ipynb deleted file mode 100644 index 4c3c300d6..000000000 --- a/code/soln/spiderman_soln.ipynb +++ /dev/null @@ -1,2010 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Case study: Spider-Man\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "I'll start by getting the units we'll need from Pint." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "radian" - ], - "text/latex": [ - "$radian$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram\n", - "N = UNITS.newton\n", - "degree = UNITS.degree\n", - "radian = UNITS.radian" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Spider-Man" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this case study we'll develop a model of Spider-Man swinging from a springy cable of webbing attached to the top of the Empire State Building. Initially, Spider-Man is at the top of a nearby building, as shown in this diagram.\n", - "\n", - "![](diagrams/spiderman.png)\n", - "\n", - "The origin, `O`, is at the base of the Empire State Building. The vector `H` represents the position where the webbing is attached to the building, relative to `O`. The vector `P` is the position of Spider-Man relative to `O`. And `L` is the vector from the attachment point to Spider-Man.\n", - "\n", - "By following the arrows from `O`, along `H`, and along `L`, we can see that \n", - "\n", - "`H + L = P`\n", - "\n", - "So we can compute `L` like this:\n", - "\n", - "`L = P - H`\n", - "\n", - "The goals of this case study are:\n", - "\n", - "1. Implement a model of this scenario to predict Spider-Man's trajectory.\n", - "\n", - "2. Choose the right time for Spider-Man to let go of the webbing in order to maximize the distance he travels before landing.\n", - "\n", - "3. Choose the best angle for Spider-Man to jump off the building, and let go of the webbing, to maximize range." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "I'll create a `Params` object to contain the quantities we'll need:\n", - "\n", - "1. According to [the Spider-Man Wiki](http://spiderman.wikia.com/wiki/Peter_Parker_%28Earth-616%29), Spider-Man weighs 76 kg.\n", - "\n", - "2. Let's assume his terminal velocity is 60 m/s.\n", - "\n", - "3. The length of the web is 100 m.\n", - "\n", - "4. The initial angle of the web is 45 degrees to the left of straight down.\n", - "\n", - "5. The spring constant of the web is 40 N / m when the cord is stretched, and 0 when it's compressed.\n", - "\n", - "Here's a `Params` object." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
height381 meter
g9.8 meter / second ** 2
mass75 kilogram
area1 meter ** 2
rho1.2 kilogram / meter ** 3
v_term60.0 meter / second
length100 meter
angle225 degree
k40.0 newton / meter
t_00 second
t_end30 second
\n", - "
" - ], - "text/plain": [ - "height 381 meter\n", - "g 9.8 meter / second ** 2\n", - "mass 75 kilogram\n", - "area 1 meter ** 2\n", - "rho 1.2 kilogram / meter ** 3\n", - "v_term 60.0 meter / second\n", - "length 100 meter\n", - "angle 225 degree\n", - "k 40.0 newton / meter\n", - "t_0 0 second\n", - "t_end 30 second\n", - "dtype: object" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(height = 381 * m,\n", - " g = 9.8 * m/s**2,\n", - " mass = 75 * kg,\n", - " area = 1 * m**2,\n", - " rho = 1.2 * kg/m**3,\n", - " v_term = 60 * m / s,\n", - " length = 100 * m,\n", - " angle = (270 - 45) * degree,\n", - " k = 40 * N / m,\n", - " t_0 = 0 * s,\n", - " t_end = 30 * s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now here's a version of `make_system` that takes a `Params` object as a parameter.\n", - "\n", - "`make_system` uses the given value of `v_term` to compute the drag coefficient `C_d`." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Makes a System object for the given conditions.\n", - " \n", - " params: Params object\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " init = State(x=P_0.x, y=P_0.y, vx=V_0.x, vy=V_0.y)\n", - " C_d = 2 * mass * g / (rho * area * v_term**2) \n", - " \n", - " return System(init=init, g=g, mass=mass, rho=rho,\n", - " C_d=C_d, area=area, length=length, k=k,\n", - " t_0=t_0, t_end=t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Compute the initial position" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def compute_initial_condition(params):\n", - " \"\"\"Compute the initial values of L and P.\n", - " \"\"\"\n", - " unpack(params)\n", - " H = Vector(0, height)\n", - " theta = angle.to(radian)\n", - " x, y = pol2cart(theta, length)\n", - " L_0 = Vector(x, y)\n", - " P_0 = H + L_0\n", - " V_0 = Vector(0, 0) * m/s\n", - " \n", - " params.set(P_0=P_0, V_0=V_0)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[-70.71067812 310.28932188] meter" - ], - "text/latex": [ - "$[-70.71067812 310.28932188] meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "compute_initial_condition(params)\n", - "params.P_0" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[0. 0.] meter/second" - ], - "text/latex": [ - "$[0. 0.] \\frac{meter}{second}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params.V_0" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's make a `System`" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
initx -70.71067811865477 meter\n", - "y 310.28932...
g9.8 meter / second ** 2
mass75 kilogram
rho1.2 kilogram / meter ** 3
C_d0.3402777777777778 dimensionless
area1 meter ** 2
length100 meter
k40.0 newton / meter
t_00 second
t_end30 second
\n", - "
" - ], - "text/plain": [ - "init x -70.71067811865477 meter\n", - "y 310.28932...\n", - "g 9.8 meter / second ** 2\n", - "mass 75 kilogram\n", - "rho 1.2 kilogram / meter ** 3\n", - "C_d 0.3402777777777778 dimensionless\n", - "area 1 meter ** 2\n", - "length 100 meter\n", - "k 40.0 newton / meter\n", - "t_0 0 second\n", - "t_end 30 second\n", - "dtype: object" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(params)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
x-70.71067811865477 meter
y310.28932188134524 meter
vx0.0 meter / second
vy0.0 meter / second
\n", - "
" - ], - "text/plain": [ - "x -70.71067811865477 meter\n", - "y 310.28932188134524 meter\n", - "vx 0.0 meter / second\n", - "vy 0.0 meter / second\n", - "dtype: object" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.init" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Drag and spring forces\n", - "\n", - "Here's drag force, as we saw in Chapter 22." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def drag_force(V, system):\n", - " \"\"\"Compute drag force.\n", - " \n", - " V: velocity Vector\n", - " system: `System` object\n", - " \n", - " returns: force Vector\n", - " \"\"\"\n", - " unpack(system)\n", - " mag = rho * V.mag**2 * C_d * area / 2\n", - " direction = -V.hat()\n", - " f_drag = direction * mag\n", - " return f_drag" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[10. 10.] meter/second" - ], - "text/latex": [ - "$[10. 10.] \\frac{meter}{second}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "V_test = Vector(10, 10) * m/s" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[-28.8735269 -28.8735269] kilogram meter/second2" - ], - "text/latex": [ - "$[-28.8735269 -28.8735269] \\frac{kilogram \\cdot meter}{second^{2}}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "drag_force(V_test, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the 2-D version of spring force. We saw the 1-D version in Chapter 21." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "def spring_force(L, system):\n", - " \"\"\"Compute drag force.\n", - " \n", - " L: Vector representing the webbing\n", - " system: System object\n", - " \n", - " returns: force Vector\n", - " \"\"\"\n", - " unpack(system)\n", - " \n", - " extension = L.mag - length\n", - " if magnitude(extension) < 0:\n", - " mag = 0\n", - " else:\n", - " mag = k * extension\n", - " \n", - " direction = -L.hat()\n", - " f_spring = direction * mag\n", - " return f_spring" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[ 0 -101] meter" - ], - "text/latex": [ - "$[ 0 -101] meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "L_test = Vector(0, -system.length-1*m)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[-0. 40.] newton" - ], - "text/latex": [ - "$[-0. 40.] newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "f_spring = spring_force(L_test, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the slope function, including acceleration due to gravity, drag, and the spring force of the webbing." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes derivatives of the state variables.\n", - " \n", - " state: State (x, y, x velocity, y velocity)\n", - " t: time\n", - " system: System object with g, rho, C_d, area, mass\n", - " \n", - " returns: sequence (vx, vy, ax, ay)\n", - " \"\"\"\n", - " x, y, vx, vy = state\n", - " unpack(system)\n", - " \n", - " H = Vector(0, height)\n", - " P = Vector(x, y)\n", - " V = Vector(vx, vy)\n", - " L = P - H\n", - " \n", - " a_grav = Vector(0, -g)\n", - " a_spring = spring_force(L, system) / mass\n", - " a_drag = drag_force(V, system) / mass\n", - " \n", - " a = a_grav + a_drag + a_spring\n", - " \n", - " return vx, vy, a.x, a.y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As always, let's test the slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " ,\n", - " ,\n", - " )" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And then run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 1.19 s, sys: 0 ns, total: 1.19 s\n", - "Wall time: 1.18 s\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[]
nfev626
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events []\n", - "nfev 626\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "%time results, details = run_ode_solver(system, slope_func, max_step=0.3)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Visualizing the results\n", - "\n", - "We can extract the x and y components as `Series` objects." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The simplest way to visualize the results is to plot x and y as functions of time." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_position(results):\n", - " plot(results.x, label='x')\n", - " plot(results.y, label='y')\n", - "\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Position (m)')\n", - " \n", - "plot_position(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can plot the velocities the same way." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_velocity(results):\n", - " plot(results.vx, label='vx')\n", - " plot(results.vy, label='vy')\n", - "\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Velocity (m/s)')\n", - " \n", - "plot_velocity(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another way to visualize the results is to plot y versus x. The result is the trajectory through the plane of motion." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzs3XeYm9WV+PGvpNH03rs9nhlf9woGgzHFxrQAgUBIWXaXZHeT3SRLkmVJIWwKIb8s2U1fskmeZAmE3RRCQi/GFGPAGONuj+/Y03vvVSO9vz9ejUaaqrFnNJqZ83meeSzpfSVdNx3d+557jsUwDIQQQohgY53rAQghhBDjkQAlhBAiKEmAEkIIEZQkQAkhhAhKEqCEEEIEJQlQQgghgpIEKCGEEEFJApQQQoigJAFKCCFEUJIAJYQQIiiFzPUAAkkpFQZcCNQBzjkejhBCLCY2IAN4T2s94M8TFlWAwgxOb871IIQQYhG7DNjnz4mLLUDVATz++OOkp6fP9ViEEGLRqK+v5+Mf/zi4P4f9sdgClBMgPT2d7OzsuR6LEEIsRn5fXpEkCSGEEEFJApQQQoigJAFKCCFEUJIAJYQQIigttiQJIUSQc7lcVFdX09PTM9dDEdNgt9tJTU0lNjZ2xl4zoAFKKfVN4KNADtAJvAL8i9a6Xim1Gvg2sAXIBK7UWr8+6vnpwH8Du4B24Eda638P1PgdQy6sVgs2qyVQbynEotPc3IzFYkEphdUqizzzgWEY9PX1UVNTAzBjQSrQf/ungU8BK4EbgVzgN+5jkUAx8M+TPP/3QBxwCfAZ4GtKqb+etdF6aWzt5ZHnTvLoc6fo7nME4i2FWJTa29tJS0uT4DSPWCwWIiMjycrKorGxccZeN6AzKK31/3ndLVdKPQT8n/vYe8B7AEqpMc9VSq0DtgP5WutS4IhS6geYAe3RWR46Z6rbGRh0MoCTstoO1uYnz/ZbCrEoOZ1O7Hb7XA9DnIOIiAgcjpn7Aj9nX1GUUnHAx4C3/HzKFqDcHZyG7QE2KKVCZ3p8o7mcxri3hRAzz2KRZfT5aKb/3gIeoJRSH1dKdWNeQ8rDvCblj1Rg9NyxCbMAYdLMjXACXn/uBhKghBBits3FDOppYCOwA3AAP/fzefKVSgixIF111VU8+eSTcz2MoBPwNHOtdRfQBZxRSmmgWim1Smt9aoqnNmDOorylYNZ1apn5kfrynrm6ZAIlhADuvPNOtmzZwuc+97nzep0nnniCyMjIGRnTl7/8ZQC++93vzsjrzaW5TpMZ/tj3p3jgAWCpUirP67GrgCNa68EZH9koVovPGp8QQkxpcNC/j6bExETCw8NneTT+83fcsy1gAUopZVdKPaiUulAptUQptQ14DDiEOZsKVUptUEptcD+lwH0/EUBrfQzYC/xKKbVeKXUz8AXgx4EYv/fFP5chEUqIxe7LX/4yBw4c4Kc//SlKKa666ip+8pOfcOedd/LLX/6Sbdu2ceeddwLw4IMPsmPHDtavX88NN9zA888/7/Nao5f4qqqq+PSnP83GjRvZtm0bDzzwAP39/Z7jvb29fOMb32Dr1q2sX7+e2267Da01P/nJT/jzn//Mn//8Z5RSPhnRTz/9NLt27WLNmjXcdNNNvP32255j7777Lkop9u7dy3XXXcf69ev5+c9/zu233+4zzpaWFlavXk1RUdGM/llOJJBLfAbm/qdPAIlAPfAy8G9aa5dSKhM47HX+L92/3gU84r59B+Y1q3eADuA7WutZTzGH0Ut8EqCEWOzuu+8+ysvL2bhxI5/4xCew2Ww8/vjjnDhxgtTUVB555BHPF9v4+Hh+8IMfkJCQwNtvv829995Lfn7+uFtqBgcH+eQnP8mOHTu499576e7u5oEHHuB73/se999/PwD3338/Wmu+//3vk5mZycmTJ3G5XHziE5+gpKTEM75hR44c4Stf+Qpf/epX2bp1K8888wyf/vSnefnll3164/3sZz/jO9/5DtHR0cTGxvLDH/6QsrIy8vLMhatnn32WZcuWsXLlyln7c/UWsACltR4Cbp3keDlTJEJoreuBm2d2ZP6RJT4h5sZh3ciBU/U4hlyz/l72ECtbVqWzUY2+3D1WTEwMdrudyMhIUlJSPI9brVa+/e1vExER4XnsM5/5jOf2HXfcwauvvsru3bvHDVDPP/888fHxfOlLX/I89pWvfIW77rqLr33ta1RXV/Pss8/y1FNPsWLFCgCWLFniOXd4qdB7TI8++ijXXnvtcMNA7r77bt566y3+93//ly9+8Yue8+655x42btzoub9161aefvpp7r77bsCchd10001T/tnMFKnF5yerV3kjp2RJCBEwR4qbAhKcwCxndqS4ya8ANZG8vDyf4ATwl7/8hccee4zq6moGBwcZHBz0CSDetNacPHnSJ1AYhkF/fz+NjY2cOXOGmJgYT3DyR2lpKbfe6js/2LBhA6WlpT6PrVq1yuf+Bz/4QX784x9z9913U1payqlTp3j44Yf9ft/zJQHKT1a5BiXEnNiwPCWgM6gNy8cPHP4aHZzef/99vva1r/GlL32JzZs3ExUVxXe+8x2GhobGfX5vby8XXXQRX//618ccS0pKwjCMaW+INfz8zBo99quvvppvfOMbHDp0iL1793LxxReTlpY2rfc+HxKg/ORdINbpDMy3OSEEbFSp5zWjmU0hISE4nZMnIR85coTly5d7EiYMw6CyspKEhIRxz1+xYgVvvPEGGRkZhIaOLZJTWFhIZ2cnp0+fHncWFRISwsDAgM9jy5Yt4+jRo2PGdfHFF0869oiICK699lqefvpp9u3bx2c/+9lJz59pc51mPm9YbbLEJ4TwlZmZybFjx2hoaKCjo2Pcc3Jzczl79iyvv/46paWlfPvb36ahoWHC17zxxhuxWq188Ytf5Pjx41RUVLBnzx4eeughz+tdf/313HPPPbzzzjtUVVXx4osvejLrMjMzOX36NDU1NbS2tgLmfq0XX3yRxx9/nLKyMn70ox9RVFTERz86dSGfW2+9lSeeeIKWlhauvvrq6f4RnRcJUH7ynUFJgBJCwF133UV7ezs7duzglltuGfecnTt38uEPf5h//dd/5aMf/Sjh4eGTftBHR0fz2GOPYbVaueuuu7j55pv50Y9+RGrqyCzygQceYMOGDXz+85/nAx/4AL/61a+w2WwA3HbbbcTFxXH99dezdetWADZt2sSDDz7II488wo033sgrr7zCww8/TEZGxpS/x82bN5OamsrOnTuJioqazh/PebP4uza5ECillgJle/bsITs7e1rP1RWt7D5QCUBhTgLXXLxkimcIIc5FUVFRwNKYg8W2bdu47777uO666+Z6KGMMDAxw2WWX8f3vf59t27ZNef5Ef3/V1dXs2LEDIM+dtT0luQblJ5ttZLLpcsk1KCHE+evv7+fw4cO0tLRQUFAw18MZo7m5mccee4y4uDguvfTSgL+/BCg/eS/xDckSnxBiBjz//PN897vf5e/+7u8oLCyc6+GMcemll5KamspDDz00Jy1QJED5ySb7oIQQM+zWW28dsz8pmGit5/T9JUnCTyEh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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_trajectory(results):\n", - " plot(results.x, results.y, label='trajectory')\n", - "\n", - " decorate(xlabel='x position (m)',\n", - " ylabel='y position (m)')\n", - " \n", - "plot_trajectory(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Letting go\n", - "\n", - "Now let's find the optimal time for Spider-Man to let go. We have to run the simulation in two phases because the spring force changes abruptly when Spider-Man lets go, so we can't integrate through it.\n", - "\n", - "Here are the parameters for Phase 1, running for 9 seconds." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 316 ms, sys: 4 ms, total: 320 ms\n", - "Wall time: 313 ms\n" - ] - }, - { - "data": { - "image/png": "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dHZ29ksg3owxbN++vV+i8Hg8tLe3U1VVRVFREUlJSX3JyRfFxcVcfHH/+sHixYspLi7uVzZ37tx+9z/5yU9y3333cd1111FcXMyOHTt44IEHfH7c46UJykdag1IqNBbPmhjUGtTiWYMnDl8NTE4ffvght956KzfddBPLli1jwoQJ3HXXXXR3dw96fGtrKyeddBI/+MEPjtqWkZGBx+MZ9QWxHh+/VA+M/ZxzzuGHP/whGzduZP369Zx88slkZ2eP6rGPhyYoH2mCUio0lkjWcdVoAikqKoqenuEHIW/evJlZs2b1DZjweDzs37+ftLS0QfefPXs2r7/+Orm5ucTEHD1JTmFhIY2NjezatWvQWlRUVBQdHR39yqZPn86WLVuOiuvkk08eNvb4+HjOO+88nnnmGd58802+8Y1vDLu/v4V6mPmY4T2DeY8mKKUUkJeXx9atW6msrKShoWHQfaZMmcKePXt47bXXKC4u5o477qCysnLIc3784x/H6XRy/fXX89FHH1FaWsratWu55557+s63Zs0abrjhBt555x0OHDjAv//9776RdXl5eezatYuDBw9SV1cHWNdr/fvf/+bxxx+npKSEX/7yl+zcuZPLLx95Ip+LL76YJ554gtraWs4555zR/omOiyYoH0W5jvypunuC0x6ulApvV111FfX19axevZpPfepTg+5z9tln85nPfIbvfOc7XH755cTFxQ37QZ+YmMhjjz2G0+nkqquu4hOf+AS//OUvyco6Uou8/fbbWbx4Md/61re48MIL+d3vfofL5QLgkksuISUlhTVr1rBixQoAli5dyp133skjjzzCxz/+cV555RUeeOABcnNzR3yOy5YtIysri7PPPpsJEyaM5s9z3By+tk1GAhGZBpSsXbuWyZMnj+rYkvIGnnurBICpOcl8/NTp/g9QKcXOnTuDNow5XKxatYpbbrmF888/P9ShHKWjo4NTTz2Ve++9l1WrVo24/1CvX1lZGatXrwYosEdtj0j7oHwUE+3qux2sEUVKqcjW3t7Opk2bqK2tZebMmaEO5yg1NTU89thjpKSkcMoppwT98TVB+Sjaq4mva4ROUaWU8sXzzz/P3XffzZe+9CUKCwtDHc5RTjnlFLKysrjnnntCsgSKJigfRUV5JSitQSml/ODiiy8+6vqkcGKMCenj6yAJH8V4JahuTVBKKRVw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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "params1 = Params(params, t_end=9*s)\n", - "system1 = make_system(params1)\n", - "%time results1, details1 = run_ode_solver(system1, slope_func, max_step=0.4)\n", - "plot_trajectory(results1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The final conditions from Phase 1 are the initial conditions for Phase 2." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "9.0 second" - ], - "text/latex": [ - "$9.0 second$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_final = get_last_label(results1) * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the position Vector." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[ 42.05516926 273.03727598] meter" - ], - "text/latex": [ - "$[ 42.05516926 273.03727598] meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x, y, vx, vy = get_last_value(results1)\n", - "P_0 = Vector(x, y) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And the velocity Vector." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[14.62716281 7.96404255] meter/second" - ], - "text/latex": [ - "$[14.62716281 7.96404255] \\frac{meter}{second}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "V_0 = Vector(vx, vy) * m/s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the parameters for Phase 2. We can turn off the spring force by setting `k=0`, so we don't have to write a new slope function." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
initx 42.05516926147856 meter\n", - "y ...
g9.8 meter / second ** 2
mass75 kilogram
rho1.2 kilogram / meter ** 3
C_d0.3402777777777778 dimensionless
area1 meter ** 2
length100 meter
k0
t_09.0 second
t_end19.0 second
\n", - "
" - ], - "text/plain": [ - "init x 42.05516926147856 meter\n", - "y ...\n", - "g 9.8 meter / second ** 2\n", - "mass 75 kilogram\n", - "rho 1.2 kilogram / meter ** 3\n", - "C_d 0.3402777777777778 dimensionless\n", - "area 1 meter ** 2\n", - "length 100 meter\n", - "k 0\n", - "t_0 9.0 second\n", - "t_end 19.0 second\n", - "dtype: object" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params2 = Params(params1, t_0=t_final, t_end=t_final+10*s, P_0=P_0, V_0=V_0, k=0)\n", - "system2 = make_system(params2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's an event function that stops the simulation when Spider-Man reaches the ground." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func(state, t, system):\n", - " \"\"\"Stops when y=0.\n", - " \n", - " state: State object\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: height\n", - " \"\"\"\n", - " x, y, vx, xy = state\n", - " return y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Run Phase 2." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 408 ms, sys: 0 ns, total: 408 ms\n", - "Wall time: 399 ms\n" - ] - } - ], - "source": [ - "%time results2, details2 = run_ode_solver(system2, slope_func, events=event_func, max_step=0.4)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results1.x, results1.y, label='Phase 1')\n", - "plot(results2.x, results2.y, label='Phase 2')\n", - "\n", - "decorate(xlabel='x position (m)',\n", - " ylabel='y position (m)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can gather all that into a function that takes `t_release` and `V_0`, runs both phases, and returns the results." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "def run_two_phase(t_release, V_0, params):\n", - " \"\"\"Run both phases.\n", - " \n", - " t_release: time when Spider-Man lets go of the webbing\n", - " V_0: initial velocity\n", - " \"\"\"\n", - " params1 = Params(params, t_end=t_release, V_0=V_0)\n", - " system1 = make_system(params1)\n", - " results1, details1 = run_ode_solver(system1, slope_func, max_step=0.4)\n", - "\n", - " t_final = get_last_label(results1) * s\n", - " x, y, vx, vy = get_last_value(results1)\n", - " P_0 = Vector(x, y) * m\n", - " V_0 = Vector(vx, vy) * m/s\n", - "\n", - " params2 = Params(params1, t_0=t_final, t_end=t_final+20*s,\n", - " P_0=P_0, V_0=V_0, k=0)\n", - " system2 = make_system(params2)\n", - "\n", - " results2, details2 = run_ode_solver(system2, slope_func, events=event_func, max_step=0.4)\n", - "\n", - " results = results1.combine_first(results2)\n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's a test run." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "143.43807371928114 meter" - ], - "text/latex": [ - "$143.43807371928114 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "t_release = 9 * s\n", - "V_0 = Vector(0, 0) * m/s\n", - "\n", - "results = run_two_phase(t_release, V_0, params)\n", - "plot_trajectory(results)\n", - "x_final = get_last_value(results.x) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Maximizing range\n", - "\n", - "To find the best value of `t_release`, we need a function that takes possible values, runs the simulation, and returns the range." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "def range_func(t_release, params):\n", - " V_0 = Vector(0, 0) * m/s\n", - " results = run_two_phase(t_release, V_0, params)\n", - " x_final = get_last_value(results.x) * m\n", - " return x_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can test it." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "143.43807371928114 meter" - ], - "text/latex": [ - "$143.43807371928114 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "range_func(9*s, params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And run it for a few values." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3.0 second -22.692747959203164 meter\n", - "6.0 second 116.08097143921458 meter\n", - "9.0 second 143.43807371928114 meter\n", - "12.0 second 74.59703591943536 meter\n" - ] - } - ], - "source": [ - "for t_release in linrange(3, 15, 3) * s:\n", - " print(t_release, range_func(t_release, params))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can use `max_bounded` to find the optimum." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
fun149.50678396065007 meter
status0
successTrue
messageSolution found.
x7.95963
nfev9
\n", - "
" - ], - "text/plain": [ - "fun 149.50678396065007 meter\n", - "status 0\n", - "success True\n", - "message Solution found.\n", - "x 7.95963\n", - "nfev 9\n", - "dtype: object" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "max_bounded(range_func, [6, 12], params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, we can run the simulation with the optimal value." - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "149.4960697736888 meter" - ], - "text/latex": [ - "$149.4960697736888 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "V_0 = Vector(0, 0) * m/s\n", - "results = run_two_phase(8*s, V_0, params)\n", - "plot_trajectory(results)\n", - "x_final = get_last_value(results.x) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Taking a flying leap\n", - "\n", - "Now suppose Spider-Man can jump off the wall in any direction at a maximum speed of 20 meters per second. In what direction should he jump, and what time should he let go, to maximize the distance he travels?\n", - "\n", - "Before you go on, think about it and see what you think the optimal angle is.\n", - "\n", - "Here's a new range function that takes a guess as a parameter, where `guess` is a sequence of three values: `t_release`, launch velocity, and launch angle.\n", - "\n", - "It computes `V_0`, runs the simulation, and returns the final `x` position." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [], - "source": [ - "def range_func2(guess, params):\n", - " t_release, velocity, theta = guess\n", - " print(t_release, velocity, theta)\n", - " \n", - " V_0 = Vector(pol2cart(theta, velocity)) * m/s\n", - " \n", - " results = run_two_phase(t_release, V_0, params)\n", - " x_final = get_last_value(results.x) * m\n", - " return -x_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can test it with the conditions from the previous section." - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "8 second 0.0 meter 0 radian\n" - ] - }, - { - "data": { - "text/html": [ - "-149.4960697736888 meter" - ], - "text/latex": [ - "$-149.4960697736888 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x0 = 8*s, 0*m/2, 0*radian\n", - "range_func2(x0, params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can use `minimize` to find the optimal values for `t_release`, launch velocity, and launch angle. It takes a while to run because it has to search a 3-D space." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "8.0 5.0 0.0\n", - "8.00000001 5.0 0.0\n", - "8.0 5.00000001 0.0\n", - "8.0 5.0 1e-08\n", - "2.6133669936898345 1.7737295492042802 -3.141592653589793\n", - "2.6133670036898344 1.7737295492042802 -3.141592653589793\n", - "2.6133669936898345 1.77372955920428 -3.141592653589793\n", - "2.6133669936898345 1.7737295492042802 -3.141592643589793\n", - "7.124272994529376 4.475491989633605 -0.5107415938145107\n", - "7.124273004529376 4.475491989633605 -0.5107415938145107\n", - "7.124272994529376 4.475491999633605 -0.5107415938145107\n", - "7.124272994529376 4.475491989633605 -0.5107415838145106\n", - "7.282687611997444 4.520676094003284 -0.543634440074854\n", - "7.282687621997444 4.520676094003284 -0.543634440074854\n", - "7.282687611997444 4.520676104003284 -0.543634440074854\n", - "7.282687611997444 4.520676094003284 -0.5436344300748539\n", - "7.441809931356356 4.434616384969265 -0.6677505205233312\n", - "7.441809941356356 4.434616384969265 -0.6677505205233312\n", - "7.441809931356356 4.4346163949692645 -0.6677505205233312\n", - "7.441809931356356 4.434616384969265 -0.6677505105233311\n", - "7.852623481537876 3.8864949812780853 -1.256142044744788\n", - "7.852623491537876 3.8864949812780853 -1.256142044744788\n", - "7.852623481537876 3.8864949912780853 -1.256142044744788\n", - "7.852623481537876 3.8864949812780853 -1.256142034744788\n", - "8.596020234731993 2.703405056179955 -2.759987210190962\n", - "8.596020244731994 2.703405056179955 -2.759987210190962\n", - "8.596020234731993 2.703405066179955 -2.759987210190962\n", - "8.596020234731993 2.703405056179955 -2.759987200190962\n", - "8.323576456600778 3.1323594022290857 -2.5122448703218607\n", - "8.323576466600779 3.1323594022290857 -2.5122448703218607\n", - "8.323576456600778 3.1323594122290856 -2.5122448703218607\n", - "8.323576456600778 3.1323594022290857 -2.512244860321861\n", - "7.935235774645063 3.850097108456469 -2.3931215378141273\n", - "7.935235784645063 3.850097108456469 -2.3931215378141273\n", - "7.935235774645063 3.850097118456469 -2.3931215378141273\n", - "7.935235774645063 3.850097108456469 -2.3931215278141273\n", - "7.77290810832975 4.33467252240397 -2.439306125078437\n", - "7.77290811832975 4.33467252240397 -2.439306125078437\n", - "7.77290810832975 4.33467253240397 -2.439306125078437\n", - "7.77290810832975 4.33467252240397 -2.439306115078437\n", - "7.684587384206332 4.841427745479601 -2.4282699114304784\n", - "7.684587394206332 4.841427745479601 -2.4282699114304784\n", - "7.684587384206332 4.841427755479601 -2.4282699114304784\n", - "7.684587384206332 4.841427745479601 -2.4282699014304785\n", - "7.015191590332345 9.263976801212863 -2.05407793407216\n", - "7.015191600332345 9.263976801212863 -2.05407793407216\n", - "7.015191590332345 9.263976811212864 -2.05407793407216\n", - "7.015191590332345 9.263976801212863 -2.05407792407216\n", - "5.390190107785736 20.0 -1.1457027642114694\n", - "5.390190117785736 20.0 -1.1457027642114694\n", - "5.390190107785736 20.00000001 -1.1457027642114694\n", - "5.390190107785736 20.0 -1.1457027542114695\n", - "6.436980196076063 13.084090819084507 -1.7308579991279638\n", - "6.436980206076063 13.084090819084507 -1.7308579991279638\n", - "6.436980196076063 13.084090829084507 -1.7308579991279638\n", - "6.436980196076063 13.084090819084507 -1.7308579891279638\n", - "5.697345051428316 20.0 -1.5121100469578497\n", - "5.697345061428316 20.0 -1.5121100469578497\n", - "5.697345051428316 20.00000001 -1.5121100469578497\n", - "5.697345051428316 20.0 -1.5121100369578497\n", - "6.264856275951618 20.0 -1.9370943900613915\n", - "6.264856285951618 20.0 -1.9370943900613915\n", - "6.264856275951618 20.00000001 -1.9370943900613915\n", - "6.264856275951618 20.0 -1.9370943800613916\n", - "6.53845917087742 20.0 -1.9712071131767281\n", - "6.53845918087742 20.0 -1.9712071131767281\n", - "6.53845917087742 20.00000001 -1.9712071131767281\n", - "6.53845917087742 20.0 -1.9712071031767282\n", - "6.846807424423326 20.0 -1.968638798858853\n", - "6.846807434423326 20.0 -1.968638798858853\n", - "6.846807424423326 20.00000001 -1.968638798858853\n", - "6.846807424423326 20.0 -1.968638788858853\n", - "6.94064741952817 20.0 -1.9859513925368686\n", - "6.94064742952817 20.0 -1.9859513925368686\n", - "6.94064741952817 20.00000001 -1.9859513925368686\n", - "6.94064741952817 20.0 -1.9859513825368686\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
fun-176.4897685932084 meter
jac[-0.07169091986725107, -1.7895814607982174, -0...
nfev76
nit15
status0
messageb'CONVERGENCE: REL_REDUCTION_OF_F_<=_FACTR*EPS...
x[6.94064741952817, 20.0, -1.9859513925368686]
successTrue
hess_inv<3x3 LbfgsInvHessProduct with dtype=float64>
\n", - "
" - ], - "text/plain": [ - "fun -176.4897685932084 meter\n", - "jac [-0.07169091986725107, -1.7895814607982174, -0...\n", - "nfev 76\n", - "nit 15\n", - "status 0\n", - "message b'CONVERGENCE: REL_REDUCTION_OF_F_<=_FACTR*EPS...\n", - "x [6.94064741952817, 20.0, -1.9859513925368686]\n", - "success True\n", - "hess_inv <3x3 LbfgsInvHessProduct with dtype=float64>\n", - "dtype: object" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "guess = [8, 5, 0]\n", - "bounds = [(0,20), (0,20), (-np.pi, np.pi)]\n", - "\n", - "res = minimize(range_func2, guess, params, bounds=bounds)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the optimal values." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[ -8.06663657 -18.30107577] dimensionless" - ], - "text/latex": [ - "$[ -8.06663657 -18.30107577] dimensionless$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "t_release, velocity, theta = res.x\n", - "V_0 = Vector(pol2cart(theta, velocity))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It turns out that the best angle is down and to the left. Not obvious." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "20.0 dimensionless" - ], - "text/latex": [ - "$20.0 dimensionless$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "V_0.mag" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the trajectory looks like with the optimal values." - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "176.4897685932084" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3Xl8lOW58PHfzGSy7yEhQAiE7WKrbCou4AJqXVpaqda39bWn1p62p7bH1mNdqnZz7XKsrbUe7dseq7Wr1UotWi2oqKgoq2w3SxIgQELIQhKSSSYz8/7xPJkMgSQTSGbJXN/PZz6ZZ53rySRzzX0/9+IIBAIopZRSscYZ7QCUUkqpE9EEpZRSKiZpglJKKRWTNEEppZSKSZqglFJKxSRNUEoppWKSJiillFIxSROUUkqpmKQJSimlVEzSBKWUUiomJUU7gEgSkRTgDOAg4ItyOEoplUhcwCjgfWNMezgHJFSCwkpOb0Y7CKWUSmALgbfC2THREtRBgGeeeYbi4uJox6KUUgmjurqaa6+9FuzP4XAkWoLyARQXF1NSUhLtWJRSKhGFfXtFG0kopZSKSZqglFJKxSRNUEoppWKSJiillFIxKdEaSSilYpzf76eqqoqjR49GOxQ1AG63m6KiIrKzswftnJqghkCH10ey2xXtMJSKS4cPH8bhcCAiOJ1ayRMPAoEAbW1t7N+/H2DQkpS++4Oo0+fnxbfK+dULm3l3c9hN/ZVSIRobGxk5cqQmpzjicDhIT09nzJgxHDp0aNDOq38BgyQQCLDyg31UHmwiEAhQsf9ItENSKi75fD7cbne0w1AnIS0tDa/XO2jn0wQ1SLaU17Fjb0Nw+SOTRkQxGqXim8PhiHYI6iQM9vumCWoQ1Dd5eGvjgeDyjAkFzJhQEMWIlFIq/kW0kYSIfB/4DDAWaAL+BfyXMaba3n4F8BOgDNgM/Icx5v2Q44uB/wEuARqBnxljfhjJa+jJ5/Pz6nt76PT5ASjITmXh7DH6DVApFbZFixbxta99jaVLl0Y7lJgS6RLUduDLwDTg40Ap8FsAEZkKPAc8BcwFVgMviUheyPF/AnKAc4AbgbtE5HMRi/4E3t1cTW1jGwAup4NLzhpHkksLpkolguuuu45HHnnklM/z7LPPcvnllw9CRHD77bdz++23D8q5oi2iJShjzB9CFitF5EdA17ovAe8aYx4AEJGbgE8A1wK/EJHTgPOAicaYcmCDiPwU+E+spBZx+2qaWb+ju8XKOaeNpiAnLRqhKKViUEdHB8nJyf3ul5+fH4Fowhdu3EMtal/1RSQH+Czwtr3qTGBl13ZjTMBenh+yvdJOTl1WALNFJOK/yU6fnxXv7w0ulxZncZo2jFAqYdx+++2sWbOGX/ziF4gIixYt4pFHHuG6667jV7/6FQsWLOC6664D4L777mPx4sXMmjWLK664guXLlx9zrkWLFvHcc88Fl/ft28dXvvIV5syZw4IFC7jnnnvweDzB7a2trXzve9/j7LPPZtasWVx11VUYY3jkkUd4/vnnef755xERRCR4zLJly7jkkkuYOXMmS5YsYfXq1cFt7733HiLCqlWruOyyy5g1axaPP/44V1999TFx1tXVMWPGDLZt2zaov8veRLyjrohcCzwOZADvAV3l2iKgZwP6WmBWP9tdQAEDmGNkMGzZXUdLm9WcMi0liYvOKNX7TkolkDvvvJPKykrmzJnDF77wBVwuF8888wybN2+mqKiIJ598MviZkJuby09/+lPy8vJYvXo1t956KxMnTjwmgXTp6OjghhtuYPHixdx66620tLRwzz338OMf/5i7774bgLvvvhtjDA899BCjR49my5Yt+P1+vvCFL7B79+5gfF02bNjAHXfcwbe//W3OPvts/v73v/OVr3yFV1555Zi58R577DHuv/9+MjMzyc7O5uGHH6aiooKysjIAXnzxRSZMmMC0adOG7PcaKhojSSwD1mA1lPg+VrK6Gujv0z1mPv29nT4+2F4TXD592kjSU7XfhlJDYb05xJqt1Xg7/UP+Wu4kJ2dOL2aOFPW7b1ZWFm63m/T0dAoLC4PrnU4n9957L2lp3dX9N954Y/D5Nddcw8qVK3n11VdPmKCWL19Obm4ut912W3DdHXfcwfXXX89dd91FVVUVL774Ii+88AJTp04FYNy4ccF9U1NTAY6J6amnnuLSSy/tmjCQm266ibfffpvf//733HzzzcH9brnlFubMmRNcPvvss1m2bBk33XQTYJXClixZ0u/vZrBEPEEZY5qBZmCniBigSkSmAzVYpaRQhXSXmnrb7gPqhi7i4324q4629k4AMtPc2qRcqSG0YUdtRJITgLfTz4YdtWElqN6UlZUdk5wA/va3v/H0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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "results = run_two_phase(t_release, V_0, params)\n", - "plot_trajectory(results)\n", - "x_final = get_last_value(results.x)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/test_deg2rad.ipynb b/code/soln/test_deg2rad.ipynb deleted file mode 100644 index 3a87154ff..000000000 --- a/code/soln/test_deg2rad.ipynb +++ /dev/null @@ -1,146 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "from pint import UnitRegistry\n", - "ureg = UnitRegistry()\n", - "Q_ = ureg.Quantity" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.7853981633974483" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "angle = 45\n", - "np.deg2rad(angle)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.7853981633974483 radian" - ], - "text/latex": [ - "$0.7853981633974483 radian$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "angle = 45 * ureg.degree\n", - "np.deg2rad(angle)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "0.785 radian" - ], - "text/latex": [ - "$0.785 radian$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "angle = 0.785 * ureg.radian\n", - "np.deg2rad(angle)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "45.0 radian" - ], - "text/latex": [ - "$45.0 radian$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "angle = 45 * ureg.dimensionless\n", - "np.deg2rad(angle)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/test_ediff1d.ipynb b/code/soln/test_ediff1d.ipynb deleted file mode 100644 index 5a480379f..000000000 --- a/code/soln/test_ediff1d.ipynb +++ /dev/null @@ -1,95 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 1, 1, -9223372036854775808])" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import numpy as np\n", - "\n", - "a = np.array([1, 2, 3])\n", - "np.ediff1d(a, np.nan)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 1, 1, -9223372036854775808])" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "ename": "ValueError", - "evalue": "cannot convert float NaN to integer", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnan\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0ma\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: cannot convert float NaN to integer" - ] - } - ], - "source": [ - "a[2] = np.nan\n", - "a" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/test_plot_quantity.ipynb b/code/soln/test_plot_quantity.ipynb deleted file mode 100644 index 1efbad4ef..000000000 --- a/code/soln/test_plot_quantity.ipynb +++ /dev/null @@ -1,387 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 25\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "import pint\n", - "UNITS = pint.UnitRegistry()\n", - "Quantity = UNITS.Quantity\n", - "\n", - "import matplotlib.pyplot as plt\n", - "\n", - "import numpy as np\n", - "import pandas as pd" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "ename": "ValueError", - "evalue": "x and y must not be None", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/matplotlib/pyplot.py\u001b[0m in \u001b[0;36mplot\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 3356\u001b[0m mplDeprecation)\n\u001b[1;32m 3357\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 3358\u001b[0;31m \u001b[0mret\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0max\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3359\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3360\u001b[0m \u001b[0max\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_hold\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwashold\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/matplotlib/__init__.py\u001b[0m in \u001b[0;36minner\u001b[0;34m(ax, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1853\u001b[0m \u001b[0;34m\"the Matplotlib list!)\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mlabel_namer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__name__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1854\u001b[0m RuntimeWarning, stacklevel=2)\n\u001b[0;32m-> 1855\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0max\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1856\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1857\u001b[0m inner.__doc__ = _add_data_doc(inner.__doc__,\n", - "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/matplotlib/axes/_axes.py\u001b[0m in \u001b[0;36mplot\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1525\u001b[0m \u001b[0mkwargs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcbook\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnormalize_kwargs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_alias_map\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1526\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1527\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mline\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_get_lines\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\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 1528\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_line\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mline\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1529\u001b[0m \u001b[0mlines\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mline\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36m_grab_next_args\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 404\u001b[0m \u001b[0mthis\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 405\u001b[0m \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 406\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mseg\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_plot_args\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mthis\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwargs\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 407\u001b[0m \u001b[0;32myield\u001b[0m \u001b[0mseg\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 408\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36m_plot_args\u001b[0;34m(self, tup, kwargs)\u001b[0m\n\u001b[1;32m 364\u001b[0m \u001b[0;31m# downstream.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 365\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0many\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mtup\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 366\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"x and y must not be None\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 367\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 368\u001b[0m \u001b[0mkw\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;31mValueError\u001b[0m: x and y must not be None" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.plot(1, 1, None)" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "newton" - ], - "text/latex": [ - "$newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "meter = UNITS.meter\n", - "minute = UNITS.minute\n", - "newton = UNITS.newton" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And a few more parameters in the `Params` object." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "240.0 meter newton" - ], - "text/latex": [ - "$240.0 meter \\cdot newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "max_torque = 240 * newton * meter" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "5500 1/minute" - ], - "text/latex": [ - "$5500 \\frac{1}{minute}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "max_rpm = 5500 / minute" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "def available_torque(rpm):\n", - " if rpm > max_rpm:\n", - " return 0 * newton * meter\n", - " return max_torque * (1 - rpm/max_rpm)" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "240.0 meter newton" - ], - "text/latex": [ - "$240.0 meter \\cdot newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "available_torque(0 / minute)" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[ 0. 302.5 605. 907.5 1210. 1512.5 1815. 2117.5 2420. 2722.5 3025. 3327.5 3630. 3932.5 4235. 4537.5 4840. 5142.5 5445. 5747.5 6050. ] 1/minute" - ], - "text/latex": [ - "$[ 0. 302.5 605. 907.5 1210. 1512.5 1815. 2117.5 2420. 2722.5 3025. 3327.5 3630. 3932.5 4235. 4537.5 4840. 5142.5 5445. 5747.5 6050. ] \\frac{1}{minute}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "rpms = np.linspace(0, max_rpm*1.1, 21) / minute" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ]" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "taus = [available_torque(rpm) for rpm in rpms]" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.0 240.0 meter * newton\n", - "302.5 226.79999999999998 meter * newton\n", - "605.0 213.6 meter * newton\n", - "907.5 200.39999999999998 meter * newton\n", - "1210.0 187.20000000000002 meter * newton\n", - "1512.5 174.0 meter * newton\n", - "1815.0 160.79999999999998 meter * newton\n", - "2117.5 147.6 meter * newton\n", - "2420.0 134.39999999999998 meter * newton\n", - "2722.5 121.19999999999997 meter * newton\n", - "3025.0 107.99999999999999 meter * newton\n", - "3327.5 94.79999999999998 meter * newton\n", - "3630.0 81.59999999999997 meter * newton\n", - "3932.5 68.39999999999995 meter * newton\n", - "4235.0 55.19999999999997 meter * newton\n", - "4537.5 41.99999999999996 meter * newton\n", - "4840.0 28.799999999999972 meter * newton\n", - "5142.5 15.59999999999996 meter * newton\n", - "5445.0 2.399999999999949 meter * newton\n", - "5747.5 0.0 meter * newton\n", - "6050.0 0.0 meter * newton\n", - "dtype: object" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "series = pd.Series(taus, index=rpms.magnitude)" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "ename": "ValueError", - "evalue": "setting an array element with a sequence.", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mseries\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mxlabel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Motor speed (rad/s)'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mylabel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Available torque (N m)'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/matplotlib/pyplot.py\u001b[0m in \u001b[0;36mplot\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 3356\u001b[0m mplDeprecation)\n\u001b[1;32m 3357\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 3358\u001b[0;31m \u001b[0mret\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0max\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3359\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3360\u001b[0m \u001b[0max\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_hold\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwashold\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/matplotlib/__init__.py\u001b[0m in \u001b[0;36minner\u001b[0;34m(ax, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1853\u001b[0m \u001b[0;34m\"the Matplotlib list!)\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mlabel_namer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__name__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1854\u001b[0m RuntimeWarning, stacklevel=2)\n\u001b[0;32m-> 1855\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0max\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1856\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1857\u001b[0m inner.__doc__ = _add_data_doc(inner.__doc__,\n", - "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/matplotlib/axes/_axes.py\u001b[0m in \u001b[0;36mplot\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1526\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1527\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mline\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_get_lines\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1528\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_line\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mline\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1529\u001b[0m \u001b[0mlines\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mline\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1530\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36madd_line\u001b[0;34m(self, line)\u001b[0m\n\u001b[1;32m 1930\u001b[0m \u001b[0mline\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_clip_path\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpatch\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1931\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1932\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_update_line_limits\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mline\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1933\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mline\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_label\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 1934\u001b[0m \u001b[0mline\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_label\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'_line%d'\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlines\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36m_update_line_limits\u001b[0;34m(self, line)\u001b[0m\n\u001b[1;32m 1952\u001b[0m \u001b[0mFigures\u001b[0m \u001b[0mout\u001b[0m \u001b[0mthe\u001b[0m \u001b[0mdata\u001b[0m \u001b[0mlimit\u001b[0m \u001b[0mof\u001b[0m \u001b[0mthe\u001b[0m \u001b[0mgiven\u001b[0m \u001b[0mline\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mupdating\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdataLim\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1953\u001b[0m \"\"\"\n\u001b[0;32m-> 1954\u001b[0;31m \u001b[0mpath\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mline\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_path\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 1955\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mpath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvertices\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msize\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1956\u001b[0m \u001b[0;32mreturn\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/matplotlib/lines.py\u001b[0m in \u001b[0;36mget_path\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 949\u001b[0m \"\"\"\n\u001b[1;32m 950\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_invalidy\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_invalidx\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 951\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrecache\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 952\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_path\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 953\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/matplotlib/lines.py\u001b[0m in \u001b[0;36mrecache\u001b[0;34m(self, always)\u001b[0m\n\u001b[1;32m 655\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0malways\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_invalidy\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 656\u001b[0m \u001b[0myconv\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconvert_yunits\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_yorig\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 657\u001b[0;31m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_to_unmasked_float_array\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0myconv\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mravel\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 658\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 659\u001b[0m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_y\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/matplotlib/cbook/__init__.py\u001b[0m in \u001b[0;36m_to_unmasked_float_array\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 2048\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0masarray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfloat\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfilled\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnan\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2049\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2050\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0masarray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfloat\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2051\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2052\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/numpy/core/numeric.py\u001b[0m in \u001b[0;36masarray\u001b[0;34m(a, dtype, order)\u001b[0m\n\u001b[1;32m 490\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 491\u001b[0m \"\"\"\n\u001b[0;32m--> 492\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdtype\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcopy\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0morder\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0morder\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 493\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 494\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: setting an array element with a sequence." - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXwAAAD8CAYAAAB0IB+mAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADYBJREFUeJzt3HGI33d9x/Hny8ROprWO5QRJou1YuhrKoO7oOoRZ0Y20fyT/FEmguEppwK0OZhE6HCr1rylDELJptolT0Fr9Qw+J5A9X6RAjudJZmpTALTpzROhZu/5TtGZ774/fT++4XHLf3v3uLt77+YDA7/v7fX6/e+fD3TO/fH/3+6WqkCRtf6/a6gEkSZvD4EtSEwZfkpow+JLUhMGXpCYMviQ1sWrwk3wuyXNJnrnC7Uny6SRzSZ5O8rbJjylJWq8hz/A/Dxy4yu13AfvGf44C/7T+sSRJk7Zq8KvqCeBnV1lyCPhCjZwC3pDkTZMaUJI0GTsn8Bi7gQtLjufH1/1k+cIkRxn9L4DXvva1f3TLLbdM4MtLUh9PPvnkT6tqai33nUTws8J1K35eQ1UdB44DTE9P1+zs7AS+vCT1keS/13rfSfyWzjywd8nxHuDiBB5XkjRBkwj+DPDe8W/r3AG8WFWXnc6RJG2tVU/pJPkycCewK8k88FHg1QBV9RngBHA3MAe8BLxvo4aVJK3dqsGvqiOr3F7AX01sIknShvCdtpLUhMGXpCYMviQ1YfAlqQmDL0lNGHxJasLgS1ITBl+SmjD4ktSEwZekJgy+JDVh8CWpCYMvSU0YfElqwuBLUhMGX5KaMPiS1ITBl6QmDL4kNWHwJakJgy9JTRh8SWrC4EtSEwZfkpow+JLUhMGXpCYMviQ1YfAlqQmDL0lNGHxJasLgS1ITBl+SmjD4ktSEwZekJgy+JDUxKPhJDiQ5l2QuycMr3P7mJI8neSrJ00nunvyokqT1WDX4SXYAx4C7gP3AkST7ly37O+CxqroNOAz846QHlSStz5Bn+LcDc1V1vqpeBh4FDi1bU8Drx5dvAC5ObkRJ0iQMCf5u4MKS4/nxdUt9DLg3yTxwAvjASg+U5GiS2SSzCwsLaxhXkrRWQ4KfFa6rZcdHgM9X1R7gbuCLSS577Ko6XlXTVTU9NTX1yqeVJK3ZkODPA3uXHO/h8lM29wOPAVTV94DXALsmMaAkaTKGBP80sC/JTUmuY/Si7MyyNT8G3gWQ5K2Mgu85G0m6hqwa/Kq6BDwInASeZfTbOGeSPJLk4HjZQ8ADSX4AfBm4r6qWn/aRJG2hnUMWVdUJRi/GLr3uI0sunwXePtnRJEmT5DttJakJgy9JTRh8SWrC4EtSEwZfkpow+JLUhMGXpCYMviQ1YfAlqQmDL0lNGHxJasLgS1ITBl+SmjD4ktSEwZekJgy+JDVh8CWpCYMvSU0YfElqwuBLUhMGX5KaMPiS1ITBl6QmDL4kNWHwJakJgy9JTRh8SWrC4EtSEwZfkpow+JLUhMGXpCYMviQ1YfAlqQmDL0lNDAp+kgNJziWZS/LwFda8J8nZJGeSfGmyY0qS1mvnaguS7ACOAX8GzAOnk8xU1dkla/YBfwu8vapeSPLGjRpYkrQ2Q57h3w7MVdX5qnoZeBQ4tGzNA8CxqnoBoKqem+yYkqT1GhL83cCFJcfz4+uWuhm4Ocl3k5xKcmClB0pyNMlsktmFhYW1TSxJWpMhwc8K19Wy453APuBO4AjwL0necNmdqo5X1XRVTU9NTb3SWSVJ6zAk+PPA3iXHe4CLK6z5RlX9sqp+CJxj9A+AJOkaMST4p4F9SW5Kch1wGJhZtubrwDsBkuxidIrn/CQHlSStz6rBr6pLwIPASeBZ4LGqOpPkkSQHx8tOAs8nOQs8Dnyoqp7fqKElSa9cqpafjt8c09PTNTs7uyVfW5J+UyV5sqqm13Jf32krSU0YfElqwuBLUhMGX5KaMPiS1ITBl6QmDL4kNWHwJakJgy9JTRh8SWrC4EtSEwZfkpow+JLUhMGXpCYMviQ1YfAlqQmDL0lNGHxJasLgS1ITBl+SmjD4ktSEwZekJgy+JDVh8CWpCYMvSU0YfElqwuBLUhMGX5KaMPiS1ITBl6QmDL4kNWHwJakJgy9JTRh8SWrC4EtSE4OCn+RAknNJ5pI8fJV19ySpJNOTG1GSNAmrBj/JDuAYcBewHziSZP8K664H/hr4/qSHlCSt35Bn+LcDc1V1vqpeBh4FDq2w7uPAJ4CfT3A+SdKEDAn+buDCkuP58XW/luQ2YG9VffNqD5TkaJLZJLMLCwuveFhJ0toNCX5WuK5+fWPyKuBTwEOrPVBVHa+q6aqanpqaGj6lJGndhgR/Hti75HgPcHHJ8fXArcB3kvwIuAOY8YVbSbq2DAn+aWBfkpuSXAccBmZ+dWNVvVhVu6rqxqq6ETgFHKyq2Q2ZWJK0JqsGv6ouAQ8CJ4Fngceq6kySR5Ic3OgBJUmTsXPIoqo6AZxYdt1HrrD2zvWPJUmaNN9pK0lNGHxJasLgS1ITBl+SmjD4ktSEwZekJgy+JDVh8CWpCYMvSU0YfElqwuBLUhMGX5KaMPiS1ITBl6QmDL4kNWHwJakJgy9JTRh8SWrC4EtSEwZfkpow+JLUhMGXpCYMviQ1YfAlqQmDL0lNGHxJasLgS1ITBl+SmjD4ktSEwZekJgy+JDVh8CWpCYMvSU0YfElqYlDwkxxIci7JXJKHV7j9g0nOJnk6ybeTvGXyo0qS1mPV4CfZARwD7gL2A0eS7F+27Clguqr+EPga8IlJDypJWp8hz/BvB+aq6nxVvQw8ChxauqCqHq+ql8aHp4A9kx1TkrReQ4K/G7iw5Hh+fN2V3A98a6UbkhxNMptkdmFhYfiUkqR1GxL8rHBdrbgwuReYBj650u1VdbyqpqtqempqaviUkqR12zlgzTywd8nxHuDi8kVJ3g18GHhHVf1iMuNJkiZlyDP808C+JDcluQ44DMwsXZDkNuCzwMGqem7yY0qS1mvV4FfVJeBB4CTwLPBYVZ1J8kiSg+NlnwReB3w1yX8mmbnCw0mStsiQUzpU1QngxLLrPrLk8rsnPJckacJ8p60kNWHwJakJgy9JTRh8SWrC4EtSEwZfkpow+JLUhMGXpCYMviQ1YfAlqQmDL0lNGHxJasLgS1ITBl+SmjD4ktSEwZekJgy+JDVh8CWpCYMvSU0YfElqwuBLUhMGX5KaMPiS1ITBl6QmDL4kNWHwJakJgy9JTRh8SWrC4EtSEwZfkpow+JLUhMGXpCYMviQ1YfAlqQmDL0lNDAp+kgNJziWZS/LwCrf/VpKvjG//fpIbJz2oJGl9Vg1+kh3AMeAuYD9wJMn+ZcvuB16oqt8HPgX8/aQHlSStz5Bn+LcDc1V1vqpeBh4FDi1bcwj4t/HlrwHvSpLJjSlJWq+dA9bsBi4sOZ4H/vhKa6rqUpIXgd8Ffrp0UZKjwNHx4S+SPLOWobehXSzbq8bci0XuxSL3YtEfrPWOQ4K/0jP1WsMaquo4cBwgyWxVTQ/4+tuee7HIvVjkXixyLxYlmV3rfYec0pkH9i453gNcvNKaJDuBG4CfrXUoSdLkDQn+aWBfkpuSXAccBmaWrZkB/mJ8+R7g36vqsmf4kqSts+opnfE5+QeBk8AO4HNVdSbJI8BsVc0A/wp8Mckco2f2hwd87ePrmHu7cS8WuReL3ItF7sWiNe9FfCIuST34TltJasLgS1ITGx58P5Zh0YC9+GCSs0meTvLtJG/Zijk3w2p7sWTdPUkqybb9lbwhe5HkPePvjTNJvrTZM26WAT8jb07yeJKnxj8nd2/FnBstyeeSPHel9ypl5NPjfXo6ydsGPXBVbdgfRi/y/hfwe8B1wA+A/cvW/CXwmfHlw8BXNnKmrfozcC/eCfz2+PL7O+/FeN31wBPAKWB6q+fewu+LfcBTwO+Mj9+41XNv4V4cB94/vrwf+NFWz71Be/GnwNuAZ65w+93Atxi9B+oO4PtDHnejn+H7sQyLVt2Lqnq8ql4aH55i9J6H7WjI9wXAx4FPAD/fzOE22ZC9eAA4VlUvAFTVc5s842YZshcFvH58+QYuf0/QtlBVT3D19zIdAr5QI6eANyR502qPu9HBX+ljGXZfaU1VXQJ+9bEM282QvVjqfkb/gm9Hq+5FktuAvVX1zc0cbAsM+b64Gbg5yXeTnEpyYNOm21xD9uJjwL1J5oETwAc2Z7RrzivtCTDsoxXWY2Ify7ANDP57JrkXmAbesaETbZ2r7kWSVzH61NX7NmugLTTk+2Ino9M6dzL6X99/JLm1qv5ng2fbbEP24gjw+ar6hyR/wuj9P7dW1f9t/HjXlDV1c6Of4fuxDIuG7AVJ3g18GDhYVb/YpNk222p7cT1wK/CdJD9idI5yZpu+cDv0Z+QbVfXLqvohcI7RPwDbzZC9uB94DKCqvge8htEHq3UzqCfLbXTw/ViGRavuxfg0xmcZxX67nqeFVfaiql6sql1VdWNV3cjo9YyDVbXmD426hg35Gfk6oxf0SbKL0Sme85s65eYYshc/Bt4FkOStjIK/sKlTXhtmgPeOf1vnDuDFqvrJanfa0FM6tXEfy/AbZ+BefBJ4HfDV8evWP66qg1s29AYZuBctDNyLk8CfJzkL/C/woap6fuum3hgD9+Ih4J+T/A2jUxj3bccniEm+zOgU3q7x6xUfBV4NUFWfYfT6xd3AHPAS8L5Bj7sN90qStALfaStJTRh8SWrC4EtSEwZfkpow+JLUhMGXpCYMviQ18f+GmWq6NWLIwgAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.plot(series)\n", - "plt.xlabel('Motor speed (rad/s)')\n", - "plt.ylabel('Available torque (N m)')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/test_timestamp.ipynb b/code/soln/test_timestamp.ipynb deleted file mode 100644 index 6f125adb0..000000000 --- a/code/soln/test_timestamp.ipynb +++ /dev/null @@ -1,173 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "pandas._libs.tslibs.timestamps.Timestamp" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "timestamp = pd.Timestamp(1412526600000000000)\n", - "series = pd.Series([], dtype=object)\n", - "series['timestamp'] = timestamp\n", - "type(series.timestamp)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "int" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "series = pd.Series([], dtype=object)\n", - "series['anything'] = 300.0\n", - "series['timestamp'] = timestamp\n", - "type(series.timestamp)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "dtype('O')" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "series.dtype" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "INSTALLED VERSIONS\n", - "------------------\n", - "commit: None\n", - "python: 3.6.5.final.0\n", - "python-bits: 64\n", - "OS: Linux\n", - "OS-release: 4.4.0-128-generic\n", - "machine: x86_64\n", - "processor: x86_64\n", - "byteorder: little\n", - "LC_ALL: None\n", - "LANG: en_US.utf8\n", - "LOCALE: en_US.UTF-8\n", - "\n", - "pandas: 0.23.0\n", - "pytest: 3.5.1\n", - "pip: 10.0.1\n", - "setuptools: 39.1.0\n", - "Cython: 0.28.2\n", - "numpy: 1.14.3\n", - "scipy: 1.1.0\n", - "pyarrow: None\n", - "xarray: None\n", - "IPython: 6.4.0\n", - "sphinx: 1.7.4\n", - "patsy: 0.5.0\n", - "dateutil: 2.7.3\n", - "pytz: 2018.4\n", - "blosc: None\n", - "bottleneck: 1.2.1\n", - "tables: 3.4.3\n", - "numexpr: 2.6.5\n", - "feather: None\n", - "matplotlib: 2.2.2\n", - "openpyxl: 2.5.3\n", - "xlrd: 1.1.0\n", - "xlwt: 1.3.0\n", - "xlsxwriter: 1.0.4\n", - "lxml: 4.2.1\n", - "bs4: 4.6.0\n", - "html5lib: 1.0.1\n", - "sqlalchemy: 1.2.7\n", - "pymysql: None\n", - "psycopg2: None\n", - "jinja2: 2.10\n", - "s3fs: None\n", - "fastparquet: None\n", - "pandas_gbq: None\n", - "pandas_datareader: None\n" - ] - } - ], - "source": [ - "pd.show_versions()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/test_units_off.ipynb b/code/soln/test_units_off.ipynb deleted file mode 100644 index 1829ddeaa..000000000 --- a/code/soln/test_units_off.ipynb +++ /dev/null @@ -1,731 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Low pass filter" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "A 0.0 dimensionless\n", - "Bd 0.0 dimensionless\n", - "Bq 0.0 dimensionless\n", - "C 0.0 dimensionless\n", - "Gy 0.0 dimensionless\n", - "Hz 0.0 dimensionless\n", - "K 0.0 dimensionless\n", - "Sv 0.0 dimensionless\n", - "amp 0.0 dimensionless\n", - "ampere 0.0 dimensionless\n", - "ampere_turn 0.0 dimensionless\n", - "baud 0.0 dimensionless\n", - "becquerel 0.0 dimensionless\n", - "bit 0.0 dimensionless\n", - "bps 0.0 dimensionless\n", - "candela 0.0 dimensionless\n", - "candle 0.0 dimensionless\n", - "cd 0.0 dimensionless\n", - "coulomb 0.0 dimensionless\n", - "count 0.0 dimensionless\n", - "counts_per_second 0.0 dimensionless\n", - "cps 0.0 dimensionless\n", - "degK 0.0 dimensionless\n", - "delta_degC 0.0 dimensionless\n", - "fine_structure_constant 0.0 dimensionless\n", - "g 0.0 dimensionless\n", - "gram 0.0 dimensionless\n", - "gray 0.0 dimensionless\n", - "hertz 0.0 dimensionless\n", - "kelvin 0.0 dimensionless\n", - "lm 0.0 dimensionless\n", - "lumen 0.0 dimensionless\n", - "lux 0.0 dimensionless\n", - "lx 0.0 dimensionless\n", - "m 0.0 dimensionless\n", - "meter 0.0 dimensionless\n", - "metre 0.0 dimensionless\n", - "mol 0.0 dimensionless\n", - "mole 0.0 dimensionless\n", - "rad 0.0 dimensionless\n", - "radian 0.0 dimensionless\n", - "rps 0.0 dimensionless\n", - "s 0.0 dimensionless\n", - "sec 0.0 dimensionless\n", - "second 0.0 dimensionless\n", - "sievert 0.0 dimensionless\n", - "sr 0.0 dimensionless\n", - "steradian 0.0 dimensionless\n", - "stere 0.0 dimensionless\n", - "Δcelsius 0.0 dimensionless\n" - ] - } - ], - "source": [ - "with units_off():\n", - " for i, name in enumerate(dir(UNITS)):\n", - " unit = getattr(UNITS, name)\n", - " try:\n", - " res = 1*unit - 1\n", - " if res == 0:\n", - " print(name, 1*unit - 1)\n", - " except TypeError:\n", - " pass\n", - " if i > 10000:\n", - " break" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "-0.998 dimensionless\n" - ] - } - ], - "source": [ - "with units_off():\n", - " print(2 * UNITS.farad - 1)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1999.0 dimensionless\n" - ] - } - ], - "source": [ - "with units_off():\n", - " print(2 * UNITS.volt - 1)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1999.0 dimensionless\n" - ] - } - ], - "source": [ - "with units_off():\n", - " print(2 * UNITS.newton - 1)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "gram meter/second2" - ], - "text/latex": [ - "$\\frac{gram \\cdot meter}{second^{2}}$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "mN = UNITS.gram * UNITS.meter / UNITS.second**2" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.0 dimensionless\n" - ] - } - ], - "source": [ - "with units_off():\n", - " print(2 * mN - 1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now I'll create a `Params` object to contain the quantities we need. Using a Params object is convenient for grouping the system parameters in a way that's easy to read (and double-check)." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
R11.000000e+06
C11.000000e-09
A5.000000e+00
f1.000000e+03
\n", - "
" - ], - "text/plain": [ - "R1 1.000000e+06\n", - "C1 1.000000e-09\n", - "A 5.000000e+00\n", - "f 1.000000e+03\n", - "dtype: float64" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(\n", - " R1 = 1e6, # ohm\n", - " C1 = 1e-9, # farad\n", - " A = 5, # volt\n", - " f = 1000, # Hz \n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can pass the `Params` object `make_system` which computes some additional parameters and defines `init`.\n", - "\n", - "`make_system` uses the given radius to compute `area` and the given `v_term` to compute the drag coefficient `C_d`." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Makes a System object for the given conditions.\n", - " \n", - " params: Params object\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " init = State(V_out = 0)\n", - " omega = 2 * np.pi * f\n", - " tau = R1 * C1\n", - " cutoff = 1 / R1 / C1\n", - " t_end = 3 / f\n", - " \n", - " return System(params, init=init, t_end=t_end,\n", - " omega=omega, cutoff=cutoff)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's make a `System`" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
R11e+06
C11e-09
A5
f1000
initV_out 0\n", - "dtype: int64
t_end0.003
omega6283.19
cutoff1000
\n", - "
" - ], - "text/plain": [ - "R1 1e+06\n", - "C1 1e-09\n", - "A 5\n", - "f 1000\n", - "init V_out 0\n", - "dtype: int64\n", - "t_end 0.003\n", - "omega 6283.19\n", - "cutoff 1000\n", - "dtype: object" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the slope function," - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Compute derivatives of the state.\n", - " \n", - " state: position, velocity\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: derivatives of y and v\n", - " \"\"\"\n", - " V_out, = state\n", - " unpack(system)\n", - " \n", - " V_in = A * np.cos(omega * t)\n", - " \n", - " V_R1 = V_in - V_out\n", - " \n", - " I_R1 = V_R1 / R1 \n", - " I_C1 = I_R1\n", - "\n", - " dV_out = I_C1 / C1\n", - " \n", - " return dV_out" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As always, let's test the slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "5000.0" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And then run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The solver successfully reached the end of the integration interval.\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[]
nfev86
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events []\n", - "nfev 86\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ts = linspace(0, system.t_end, 301)\n", - "results, details = run_ode_solver(system, slope_func, t_eval=ts)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the results." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "# results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the plot of position as a function of time." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_results(results):\n", - " xs = results.V_out.index\n", - " ys = results.V_out.values\n", - "\n", - " t_end = get_last_label(results)\n", - " if t_end < 10:\n", - " xs *= 1000\n", - " xlabel = 'Time (ms)'\n", - " else:\n", - " xlabel = 'Time (s)'\n", - " \n", - " plot(xs, ys)\n", - " decorate(xlabel=xlabel,\n", - " ylabel='$V_{out}$ (volt)',\n", - " legend=False)\n", - " \n", - "plot_results(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And velocity as a function of time:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The solver successfully reached the end of the integration interval.\n", - "The solver successfully reached the end of the integration interval.\n", - "The solver successfully reached the end of the integration interval.\n", - "The solver successfully reached the end of the integration interval.\n", - "The solver successfully reached the end of the integration interval.\n", - "The solver successfully reached the end of the integration interval.\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "fs = [1, 10, 100, 1000, 10000, 100000]\n", - "for i, f in enumerate(fs):\n", - " system = make_system(Params(params, f=f))\n", - " ts = linspace(0, system.t_end, 301)\n", - " results, details = run_ode_solver(system, slope_func, t_eval=ts)\n", - " subplot(3, 2, i+1)\n", - " plot_results(results)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/test_vector.ipynb b/code/soln/test_vector.ipynb deleted file mode 100644 index 611fef30d..000000000 --- a/code/soln/test_vector.ipynb +++ /dev/null @@ -1,211 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "1 meter" - ], - "text/latex": [ - "$1 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x = 1 * UNITS.meter" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[0.5547002 0.83205029] dimensionless" - ], - "text/latex": [ - "$[0.5547002 0.83205029] dimensionless$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v = Vector(2,3).hat()" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "[0.5547002 0.83205029] meter" - ], - "text/latex": [ - "$[0.5547002 0.83205029] meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "vp = x * v" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "modsim._Vector" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(v)" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "modsim._Vector" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(v * 3)" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "modsim._Vector" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(v * x)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "pint.unit.build_quantity_class..Quantity" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(x * v)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/throwingaxe_soln.ipynb b/code/soln/throwingaxe_soln.ipynb deleted file mode 100644 index 02ed72f5c..000000000 --- a/code/soln/throwingaxe_soln.ipynb +++ /dev/null @@ -1,1696 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Case study: Throwing Axe\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Throwing axe" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Our favorite event at Lumberjack Competitions is axe throwing.  The axes used for this event typically weigh 1.5 to 2 kg, with handles roughly 0.7 m long.  They are thrown overhead at a target typically 6 m away and 1.5 m off the ground.  Normally, the axe makes one full rotation in the air to hit the target blade first, with the handle close to vertical.\n", - "\n", - "![Diagram of throwing axe](diagrams/throwingaxe1.png)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a version of `make_system` that sets the initial conditions.\n", - "\n", - "The state variables are x, y, theta, vx, vy, omega, where theta is the orientation (angle) of the axe in radians and omega is the angular velocity in radians per second.\n", - "\n", - "I chose initial conditions based on videos of axe throwing." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram\n", - "radian = UNITS.radian\n", - "\n", - "def make_system():\n", - " \"\"\"Makes a System object for the given conditions.\n", - " \n", - " returns: System with init, ...\n", - " \"\"\"\n", - " P = Vector(0, 2) * m\n", - " V = Vector(8, 4) * m/s\n", - " \n", - " init = State(x=P.x, y=P.y, theta=2, \n", - " vx=V.x, vy=V.y, omega=-7)\n", - "\n", - " t_end = 1.0 * s\n", - " \n", - " return System(init=init, t_end=t_end,\n", - " g = 9.8 * m/s**2,\n", - " mass = 1.5 * kg,\n", - " length = 0.7 * m)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's make a `System`" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
initx 0.0 meter\n", - "y ...
t_end1.0 second
g9.8 meter / second ** 2
mass1.5 kilogram
length0.7 meter
\n", - "
" - ], - "text/plain": [ - "init x 0.0 meter\n", - "y ...\n", - "t_end 1.0 second\n", - "g 9.8 meter / second ** 2\n", - "mass 1.5 kilogram\n", - "length 0.7 meter\n", - "dtype: object" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system()" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
x0.0 meter
y2.0 meter
theta2
vx8.0 meter / second
vy4.0 meter / second
omega-7
\n", - "
" - ], - "text/plain": [ - "x 0.0 meter\n", - "y 2.0 meter\n", - "theta 2\n", - "vx 8.0 meter / second\n", - "vy 4.0 meter / second\n", - "omega -7\n", - "dtype: object" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.init" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As a simple starting place, I ignore drag, so `vx` and `omega` are constant, and `ay` is just `-g`." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes derivatives of the state variables.\n", - " \n", - " state: State (x, y, x velocity, y velocity)\n", - " t: time\n", - " system: System object with length0, m, k\n", - " \n", - " returns: sequence (vx, vy, ax, ay)\n", - " \"\"\"\n", - " x, y, theta, vx, vy, omega = state\n", - " unpack(system)\n", - "\n", - " ax = 0\n", - " ay = -g\n", - " alpha = 0\n", - "\n", - " return vx, vy, omega, ax, ay, alpha" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As always, let's test the slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " ,\n", - " -7,\n", - " 0,\n", - " ,\n", - " 0)" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And then run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[]
nfev140
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events []\n", - "nfev 140\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results, details = run_ode_solver(system, slope_func, max_step=0.05)\n", - "details" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
xythetavxvyomega
0.8310166.6481281.940185-3.8171128.0-4.143957-7.0
0.8810167.0481281.720737-4.1671128.0-4.633957-7.0
0.9310167.4481281.476789-4.5171128.0-5.123957-7.0
0.9810167.8481281.208341-4.8671128.0-5.613957-7.0
1.0000008.0000001.100000-5.0000008.0-5.800000-7.0
\n", - "
" - ], - "text/plain": [ - " x y theta vx vy omega\n", - "0.831016 6.648128 1.940185 -3.817112 8.0 -4.143957 -7.0\n", - "0.881016 7.048128 1.720737 -4.167112 8.0 -4.633957 -7.0\n", - "0.931016 7.448128 1.476789 -4.517112 8.0 -5.123957 -7.0\n", - "0.981016 7.848128 1.208341 -4.867112 8.0 -5.613957 -7.0\n", - "1.000000 8.000000 1.100000 -5.000000 8.0 -5.800000 -7.0" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Visualizing the results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The simplest way to visualize the results is to plot the state variables as a function of time." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3Xd8XNd54P3fnYYZ9N4G7OWwExBVSFGE7Dhy5HXT68S7SeS4JvYnG8ub4vUb23ISlziOk7W1eb2J5dix11nH3iQb+5W1jsvasSCKIkWJACkQ5GEngUHvdfrdP+6gDAgSGBJT8Xw/HwrAmbkzD64G97nn3HPPY5imiRBCCJFpbOkOQAghhFiKJCghhBAZSRKUEEKIjCQJSgghREaSBCWEECIjSYISQgiRkSRBCSGEyEiSoIQQQmQkSVBCCCEykiQoIYQQGcmR7gBWSimVB9wH9ACRNIcjhBAiMXagDjiptQ6sZIOsSVBYyen5dAchhBDirhwBjq7kidmUoHoAvvWtb1FbW5vuWIQQQiSgt7eXxx9/HGLH8pXIpgQVAaitraWhoSHdsQghhLgzK75EI5MkhBBCZCRJUEIIITKSJCghhBAZSRKUEEKIjJTSSRJKqVLgvwBvBAqBM8Afaq1b7va1o9EoXV1dTE1N3e1LZQyn00l1dTXFxcXpDkUIIVIu1bP4vgDcA7wVGASeAJ5VSq3TWo/dzQsPDg5iGAZKKWy27O8YmqbJzMwMPp8PQJKUECJtIlET0zRx2FN7bE31kfwB4Kta6xNa68vAJ4AiYNvdvvDo6Cg1NTU5kZwADMMgPz8fr9dLf39/usMRQqxB/kCYn7/SydP/cobv/Fgz7Q+l9P1T3YN6Efh/lFLfBkaB9wJdQMfdvnAkEsHpdN7ty2Qcj8dDKJTaD4UQYm2LRk06rg5xvL0XfzAMwOhkgNGJAPnu1B1nU52gngC+hTW8FwEGgF/SWk+vxosbhrEaL5NRcvF3EkJkrt6hKZ5r7WJgZCaufdu6UuoqC1IaS6oT1O8CG4FfBIaBdwLPKKWatNYjKY5FCCFEzLQ/xIuv9nDu2nBce3GBiyONXjbWFaf8hDllCUop5QE+CbxGa30s1tyqlHoj8KvA36QqFiGEEJZo1OTVS4Oc6OglGJpfhchht3HPjmruUdUpnxwxF0MK38sZ+7d4HaYocj+WEEKknG9gkpZTXQyN++Pat3hLOLzfS3GBK02RWVKWoLTW40qpF4CnlFL/CWuI771YQ34/SVUcQgix1k3OhHjhdDcXO+OvrJQW5XGk0cuG2sy4rSXVPZf/AFwDvg+0YV2LekxrfSHFcaRNX18f9913H88+++xc25e+9CVe//rXMzMzc5sthRDi7kQiUU6d7+dbPzwXl5ycDhsP7q3n1x5RGZOcIMWTJLTWPuDXUvmemaampoaPf/zjfPrTn+aBBx5gYGCAp59+mq9//et4PJ50hyeEyFE3esdpafMxOhFfzHbbujIO76+n0JN5t+lkUz2ohLTqfl7q6CUUjib9vZwOG/fvqqVJVa/o+Y899hg/+tGP+MQnPoHP5+PXf/3Xuffee5McpRBiLRqfCvLCaR+XffGL9VQUu2m+pwFvVWGaIlteziaotgsDKUlOAKFwlLYLAytOUACf+tSneP3rX09VVRW/93u/l8TohBBrUTgS5ZTu59T5fsKR+WOhy2nngV217Nlaid2W2fdZ5myCatxeldIeVOP2qoS2OX/+PMFgkNHRUcbHx3G73UmKTgixlpimybWecZ5v8zE+FYx7bOfGcg7trUvpahB3I2cTVJOqTqhHk0oTExM8+eSTfPjDH+all17ij/7oj/jyl7+c7rCEEFludCLA820+rveOx7VXlXl4uKmB2orUrgRxt3I2QWWyz372s9TV1fGud72LN73pTbzpTW/ie9/7Ho899li6QxNCZKFQOMLL5/ppu9BPJGrOtbtdDg7uqWXXpgpsGT6ctxRJUCn23HPP8YMf/IDvfe972Gw2qqqqePLJJ/nUpz7FoUOHqKmpSXeIQogsYZoml7vGOHrax+TM/KLShmGwe1M5B/fU4c7L3sN89kaepR5++GFOnz4d1/bmN7+ZN7/5zWmKSAiRjYbH/bS0+ujqn4hrr60ooLnRS3V5fpoiWz2SoIQQIosEQxFe6ujlzMVBoub8cJ4nz8GDe+vZsbEsZ6ogSIISQogsYJom+sYIx870xBUOtBkGe7dWct+uGtyu3Dqk59ZvI4QQOWhgZIaW1i56hqbi2r1VhTQ3eakoyc1VaCRBCSFEhvIHwpw420v7lSHMBcN5hR4nD+6rZ9u60pwZzluKJCghhMgwpmnScXWY4+09zATCc+02m0Hjtiru3VmDy2lPY4SpIQlKCCEySN/wNC2tXfQNT8e1r6spornRS1nx2ll1RhKUEEJkgGl/iOPtPXRcvbnk+uF99Wz2luT0cN5SJEEJIUQaRaMmZ68McfxsD4HgfMFxu83gwI4amlQ1TsfaLDouCUoIIdKke3CSllYfg6PxxUo31RXzUKOXksK8NEWWGSRBCSFEik3NhDh2pht9I77kekmhVXJ9Y13mVLVNJ0lQQgiRIpGoyZmLAzeVAnLYbdy7s4bG7VU47GtzOG8pkqCEECIFOvsmeL7Nx/C4P659a0Mph/fXU5TvSlNkmUtSdYo9/fTTvP3tb49rGxoaYvfu3Zw7dy5NUQkhkmViOsi/vniN/7/lclxyKi9289bmLTx6aKMkp1vI2R7U9JXTTF98GTMSWv7Jd8mwO8nfdi/5m/cv+9zHHnuMp556iqtXr7Jp0yYAnn32WTZv3szOnTuTHaoQIkXCkShtFwZ4+VzfTSXX799Vw96tVRlfcj3dcrYHNXP1dEqSE4AZCTFz9fTyTwRqamo4dOgQzzzzzFzbM888w1ve8pZkhSeESLFrPeN8+8ea4+09cclpx4YyHv+lHTRur5bktAI5m6A8m/Zj2J0peS/D7sSzafne06zHHnuM73//+wBcuXKFjo4OSVBC5ICxyQD/++gVnj16hbHJwFx7ZamHt712K794/wYKPKk5LuWCnB3iy9+8f0VDbunwyCOP8Cd/8iecOnWKlpYWDh48KJV0hchioXCUU+f7OKXjS67nuewc3F3H7s3ZWXI93XI2QWUyj8fDo48+yjPPPMPRo0f54Ac/mO6QhBB3wDRNrvjGOHq6m4np4Fy7YRjs3FjOwT215Lulx3SnJEGlydve9jbe/e5343Q6eeSRR9IdjhAiQSPjflrafHT2xZdcrynPp7mpgZocKLmebpKg0uTAgQNUV1dz4MABCgoK0h2OEGKFgqEIJ8/1cfrCwE0l1w/trWPnxvI1t6hrskiCSpNgMMjk5CRvfetb0x2KEGIFTNPkYucox850MzkzP0PYMAz2bK7ggd21uPPkkLqaZG+mweDgIH//939PSUkJhw8fTnc4QohlDI3N8NwpH92Dk3Ht9ZUFNDc1UFmamyXX000SVBocPnyY6upqPv/5z8tQgBAZzB8Mc/JsH69eHowbzst3Ozm8r47t68vkbziJJEGlgdY63SEIIW7DNE3OXxvh2Kvd8SXXDYP926q4b9faKLmebpKghBBigf7haZ5bouR6Q3URzU1eytdQyfV0y6kEZZpmznW3o9Ho8k8SQtw1fyDMi7GS6+aC4bxCj5OH9nvZ0rD2Sq6nW84kKLfbzdDQEBUVFTnxITJNk1AoRF9fn0xDFyKJolGTjqtDHG/vxR+cH86z2wwat1dz785qnA4ZzkuHnElQDQ0NdHV1MTAwkO5QVo3D4aCkpITKysp0hyJETuodmuK5U10MLCq5vqG2mCONXkqL1nbJ9XTLmQTldDrnylcIIcTtTPtDHDvTw/nrw3HtxQWuuZLruTASk+1yJkEJIcRyolGTVy8NcqKjl2AoMtfusNu4Z0c196hqKbmeQSRBCSHWBN/AJC2nuhhaVHJ9i7eEw/u9FBdIVdtMk/IEpZS6B/gL4BAQAH6itf73qY5DCLE2TE4HeeFMDxc7R+LaS4vyONLoZUNtcZoiE8tJaYJSSu0EfgZ8EfgQEAWkzrkQYtVFIlFOXxzk5LleQuH52zWcDhv37axl/7ZK7DKcl9FS3YP6DPDPWutPLmg7l+IYhBA57kbvOC1tPkYnAnHt29aVcXh/PYVS1TYrpCxBKaXswKPA55RSPwd2AGeA39dat6cqDiFE7hqfCvLCaR+XfWNx7RXFbprvacBbVZimyMSdSGUPqgrIBz4CfBg4CXwQ+D9KqW1a64nbbSyEELcSjkQ5pfs5db6fcGR+OM/ltPPArlr2bK3ELiXXs04qE9TsYO8/a62fBlBKfQDwAW8Cvp3CWIQQOcA0Ta71jPN8m4/xqWDcYzs3lnNob52UXM9iqUxQg0AEmFvKW2sdUkpdAdalMA4hRA4YnQjwfJuP673jce1VZR4ebmqgtkKWCMt2KUtQWuugUqoV2DrbppRyABuBG6mKQwiR3ULhCC+f66ftQj+R6Pyirm6Xg4N7atm1qQKbDOflhFTP4vsi8DWl1L9hXYP6EFav6tkUxyGEyDKmaXK5a4yjp303lVzfvamcg3vqpOR6jknp/02t9T8opaqBzwGlwEvAI1rrydtvKYRYy4bH/bS0+ujqj59LVVtRQHOjl+ry/DRFJpIp5acbWuungKdS/b5CiOwTDEV4qaOXMxfjS6578hw8uLeeHRul5Houk/6wECLjmKaJvjHCsTM9TPvnh/NshsHerZXct6sGt0sOX7lO/g8LITLKwMgMLa1d9AxNxbV7qwppbvJSUeJJU2Qi1RJKUEqpAmA94AEGtNadSYlKCLHm+INhTrT30n5l6KaS6w/uq2fbulIZzltjlk1QSqlC4J3AO4B7ATtgAKZSagD4AfBlrfVLyQxUCJGbTNOk4+owx9t7mAnMl1y32Qwat1Vx364aKbm+Rt02QSmlfgf4JNZqD98FPg10AX6gHNgLvAb4qVKqBXhCa30lmQELIXJH3/A0La1d9A1Px7WvqymiudFLWbE7TZGJTLBcD+rNwBu01idv8fgJ4KuxXtZvA28A/tsqxieEyEHT/hDH23vouHpzyfXD++rZ7C2R4Txx+wSltX50JS8Su4/pL1YlIiFEzopGTc5eGeL42R4CwfmS63abwYEdNTSpapwOqdEkLDKLTwiREt2Dk7S0+hgcnYlr31RfwkP76ykpzEtTZCJTJTqL75eA1wHVzK9ODoDW+p2rGJcQIkdMzYQ4dqYbfWNRyfXCWMn1Oim5Lpa24gSllPoM8DGsIoO9gHn7LYQQa1kkanLm4gAvdSwquW63cWBnDU3bq6TkuritRHpQ7wferbX+ZrKCEULkhs6+CZ5v8zE87o9r37aulMP76inMd6UpMpFNEklQUeBYsgIRQmS/iekgR093c7lrNK69vNjNkUYv62qK0hSZyEaJJKi/Bn4T+MMkxSKEyFKRSJTWCwO8cq6P0KKS6/fvqmHv1iopuS4SlkiC+jTwrFLqNNZ1qNDCB7XW713NwIQQ2eF6rOT66GQgrn3HhjIO7a2nwCMl18WdSSRBfQrrRtwOoA6ZJCHEmjY2GeBom4+rPfEl1ytLPTQ3eamvLExTZCJXJJKgPgi8V2v9jSTFIoTIAqFwlFPn+zil40uu57nsHNxdx+7NUnJdrI5EElQQOJqsQIQQmc00Ta74xjh6upuJ6eBcu2EY7NxYzsE9teS7ZThPrJ5EEtRXgPcBH01SLEKIDDUy7qelzUdnX3zJ9ZryfJqbGqiRkusiCRJJUHXAL8dWkzjNzZMk3r+agQkh0i8YinDyXB+nLwzcVHL90N46dm4sl0VdRdIkkqC2AG2x7zcuekwmTAiRQ0zT5GLnKC+c7mZqQcl1wzDYu6WC+3fXSsl1kXQr/oRprV+bzECEEJlhaGyG50756B6cjGuvryyguamBylIpuS5SQ06BhBCAVXL95Nk+Xr08GDecl+92cnhfHdvXl8lwnkip5Srq/hXwaa31wHIvpJR6C1Cgtf72agUnhEg+0zQ5f22EY692x5dcNwz2x0quu5xScl2k3nI9KAO4rJT6AfA94CXAp7UOKKXKgN3Aw8BvABHgXckMVgixuvqHp3luiZLrDdVFNDd5KZeS6yKNlquo+4RS6kvAE1il3EsBlFILn/YS8FngH7TW4ZteRAiRcWYC4bmS6+aC4bxCj5OHGr1skZLrIgMsew1Ka62BDyqlngD2ApsADzAAtGqth5MbohBitUSjJh1Xhzje3os/OH8+abcZNG6v5t6d1TgdMpwnMkMis/hMrEVizyQvHCFEsvQMTtHS2sXAopLrG2qLOdLopbRISq6LzCKz+ITIcdP+EMfO9HD+evxgR3GBiyONXjbWFctwnshIkqCEyFGRqEn7pUFOdPQSDEXm2h12Gwd2VNOkqnFIyXWRwSRBCZGDfAOTtJzqYmhRyfUt3hIO7/dSXCAl10XmkwQlRA6ZnA7ywpkeLnaOxLWXFuVxpNHLhtriNEUmROIkQQmRAyKRKKcvDnLyXC+h8HzJdafDxn07a9m/rRK7DOeJLJNQglJKbQF+AagB4j7tWutPrWJcQogVut4bK7k+EV9yfdu6Mg7vr6dQSq6LLLXiBKWUegfwdcAP9BG/grmJVRJeCJEiY5MBXjjTzRXfWFx7RbGb5nsa8FZJyXWR3RLpQX0S+EvgSa11ZLknCyGSIxyJckr3c+p8P+HI/HCey2nngV217NlaiV1KrosckEiCqgX+VpKTEOlhmibXeqzhvPGpYNxjOzeWc2hvnZRcFzklkQT1U6AJuJKkWIQQtzA6EaClrYsbvfEl16vKPDzc1EBtRUGaIhMieRJJUH8P/LlSqgGr5HvcKZzW+thqBiaEgFA4wsvn+mm70E8kOn/ZN89l59CeOnZtqsAmw3kiRyWSoP5n7OsXl3jMBGSFSSFWiWmaXOqySq5PzsSXXN+9qZyDe+pw58ldIiK3JfIJ37Sab6yU+i7wGPBarfXPV/O1hchmw+N+Wlp9dPXHD+fVVhTQ3Oilujw/TZEJkVqJrGZ+fbXeVCn1G4AMmguxQDAU4aWOXs5cjC+57slz8ODeenZslJLrYm1J9Ebd7cBHsCrpmsBZ4PNa64sJvIYX+AxwBFi1pCdEtjJNE31jhGNnepj2zw/n2QyDvVsruW9XDW6XDOeJtSeRG3UfAZ4F2oEWrHLwR4BXlVJv1Fr/dIUv9VXgs1rrG4sq8wqx5gyMzNDS2kXP0FRce31lIQ/f46WixJOmyIRIv0ROyz4L/I3W+ncXNiql/ivwZ8D9y72AUuoDgFNr/XRCUQqRY/yBMCfO9tJ+ZeimkusP7qtn27pSGc4Ta14iCWoP8I4l2v8aeP9yGyul1gN/DDyYwHsKkVNM06Tj6jDH23uYCcyXXLfZDBq3VXHvzhpcTpkQKwQklqAmgHWAXtS+ARhfwfb3YK1GcWnR0N5PlVLf0Fq/L4FYhMg6fcPTtLR20Tc8Hde+rqaI5kYvZcXuNEUmRGZKJEF9F/iKUuq3gedjbc1YPah/WcH2PwX2LWp7FfhN4McJxCFEVpn2hzje3kPH1ZtLrh/eV89mb4kM5wmxhEQS1B9grWb+r8SvZP7PwH9ebmOt9QTWBIs5sZ7UVa21L4E4hMgK0ajJ2StDHD/bQyA4v4Sl3WZwYEcNTaoap0NqNAlxK4ncBzUJvD1WE2pXrPms1lrW5hNike7BSVpafQyOzsS1b6ov4aH99ZQU5qUpMiGyR8I3V2itLwOXV+PNtdYyriFyytRMiGNnutE3FpVcL4yVXK+TkutCrNRtE5RS6mPAF7XWM7Hvb0lr/dlVjUyILBKJmpy5OMBLHYtKrtttHNhZQ9P2Kim5LkSClutB/RbwNDAT+/5WTKz7pIRYczr7Jni+zcfwuD+ufWtDKYf311OU70pTZEJkt9smKK31pqW+F0LAxHSQF053c6lrNK69vNjNkUYv62qK0hSZELkhkaWO3gn8T611YFG7C/hVrfU3Vzs4ITJRJBKl9cIAr5zrI7So5Pr9u2rYu7VKSq4LsQoSmSTxdeCHQP+i9qLYY5KgRM67Hiu5PjoZd57Gjg1lHNpbT4FHSq4LsVoSSVAG8fc/zarBWmVCiJw1NhngaJuPqz3xi6ZUlnpobvJSX1mYpsiEyF3LJiil1M9i35rAd5VSC0u924EdwNEkxCZE2oXCUU6d7+OUvrnk+sHddezeLCXXhUiWlfSgZu95eg1wDWtG36wg8H3ga6salRBpZpomV3xjHD3dzcT0/DmZYRjs3FjGwT115LtlOE+IZFo2QWmtfwtAKdUF/KXWemqZTYTIaiMTVsn1zr74keua8nyamxqokZLrQqREIksdfTKZgQiRbsFQhJfP9dF2cYBoNL7k+qG9dezcWC6LugqRQsutJPFj4O1a67HY97ektX79qkYmRIqYpsnFzlGOnelmcma+5LphGOzdUsH9u2ul5LoQabDcX50PiC74XoicMjQ2w3OnfHQPTsa111cW0NzUQGWplFwXIl2WW0niPUt9L0S28wfDnDzbx6uXB4kuKLme73ZyeF8d29eXyXCeEGl2x+MWSikDq+zGjVitJyEynmmanL82wrFXu+NLrhsG+7dVcd8uKbkuRKZIZKmjvwTOaa2/FktOP8Waej6hlHpUa/1ikmIUYlX0D0/z3BIl1xuqi2hu8lIuJdeFyCiJ9KDeDvyH2Pe/hFW+/RDwDqyVzF+7uqEJsTr8gTDH23s4e3UYc8FwXqHHyUONXrZIyXUhMlIiCaoG6Ip9/yjwT1rrE0qpYeClVY9MiLsUjZp0XB3ieHsv/uD8cJ7dZtCkqjmwoxqnQ4bzhMhUiSSoEaAWK0n9AvCnsXYDa8kjITJG79AUz53qYmBRyfUNtcUcafRSWiQl14XIdIkkqB8Cf6uUagU2AT+Kte/GWgJJiLSb9oc4dqaH89eH49qLC1wcafSyqb4kTZEJIRKVSIL6EFavaT3wy1rr2SptB4B/XO3AhEhEJGrSfmmQEx29BEORuXaH3caBHdU0qWocUnJdiKySyFJHE1hJanH7k6sakRAJ8g1M0nKqi6FFJde3eEt4cF89xW4bZjhAJBgBc6mKMUu0Lfk8wDDAZsew2THsDut7mWAhRFIkdB+UUsoB/CrWsJ4JtAP/qLUO33ZDIVbIjEYwg36ioQDRoB8z5I99DWCGQ5jRMGYkDJEw/pkAlzuHGByZpMSMUGZGMMwI+U6DhkoPReM2gi0RBpMcs2Gzg92BYXdYicvmALt97mdsscdm2+xODGceNpcbw5mH4XRjc+ZhuGJf7bKskhCQ2H1QW7CuQ3kBjTU54neBP1ZKvUFrfSU5IYpsZZomZihA1D9JNDBNNBiISzhzCSgUiCUlP2Y4tKLXHRrzMzAyTdSE2VKBNgOqyvKpKMnDMEzMaOS2r7NazGgEohHMUGD5J6+AYbPPJ6u55JWHzemeT2wuD3ZPIba8AgyXW3pxIiclcqr2RaATOKy17gdQStUA34k99tbVD09kKiv5+In6p4jMTBL1T1mJyD9FxD9JdMb6ebWTxOR0kJ6hKYKhaFx7SaGLmvICnI6brzPNDcfZHYsO5Akc1Ge3M81YLy6WlJKQBM1oBNM/RdS/sso2hs2OLS8fm7sg9q8Qmzsfu7twvi0v3+rNCZFFEklQrwWaZ5MTgNa6Tyn1YeDfVj0ykVamGSU6M0lkapTI9MR88lmQjJLTQzGwufJiPQXPXI9hJmLnXOcEPZN+om47UY8d07BTVJTPgd311FYWx64JzQ6nOVJyjcg0TYglLDMStpJWJLwgiVlDknMJLRLCDIesXmPIP9+rjP1sBgOYZnT5N14YQzRCZGaCyMztVhwzsOW5reSVV4DdU4Atvxh7fgn2ghLs+cWSwETGSXSwe6krx4n9NYmMYZom0cA0kamxWCIaIzJpfR+dmViVBGQ4nLGDYj42l3vRMFXs59nhK5cbw+GKSyjhSJRTup9Tl/oJR4qhwGrPc9p5YE8tezZXprXkumEYYHdi2Fenuq5pmhAJW8Odc8OgAevnYGA+sQWmic5MEQlMrXBo0SQamCEamAEGlvpNsHsKrWRVUDKXuGz5JdjziyR5ibRIJEG1AH+hlPr3WusRAKVUOfD52GMiQ0WD/lgCGicyOTr//dQYZmT5az63Yjic2N1F2DwF2PIKsHkKY8NK+dhm2x2uO3pt0zS51jPO820+xqeCcY/t3FjOob25WXLdMAxwOLE7nOApWtE2ZjhEZHaINTBNdGaSaGCKyMwU0cBsu5+lzy/nXmW+FzbYFf+QYWD3FMUlLnt+CfbCUmyeIrn+JZImkQT1e8BPgE6lVAfWp303MAg8koTYRILMSJjw5DCR8WHCE0OEx4eITAwRvcOL97a8/LmD0XzyiV3juIvks5zRiQDPt/m43jse115V5uHhpgZqKwqS8r7ZynA4cRSWQmHpLZ9jRiNW8opd24rMTMydpESnx4jMTHHLBGaa1nOnx7EuQy94b7sTR1E59qJyHMUVOIoqsBeVY3PKSh3i7iVyH9QFpZQCHscqswHwNPAPWmv/rbcUq21uaG58aC4RhSeGiEyN3vr+nVswnHk4YkM5joLSuSEeW0FJ0hLQrYTCEV4+10/bhX4iC0quu13zJdfTOZyXzQyb3eoF3aJXZkbCcwkrMj0W9/V2kzXMSIjQaB+h0b64drunyEpaRRU4iiuwF1VgLyjGMORmabFyiUwzzwPsWuuvJTEesYgZjRCZHI0losH5XlFw5ecEht0xPzRTUIJ9QSIynOmfomyaJpe6Rnnh9M0l13dvruDg7lrceXJvUDIZdgeOonIcReU3PWZGQkSmxhclrnEikyNEgzNLvBpzw4XB/uvz72Gz35S0HMUV0tsSt7TsX33sOtN/x1rB3KaUOg48rrW+luTY1hzTNIlMjRGOnZGGR/uJTAwnMKvLwJ5fPD/UEvtq8xSmPQndytDYDM+3+ejqjy+5XltRQHOTl+qy/DRFJmYZdqf1mSquuOmxaGCa8MQw4fFBIhPD1gnU5MiSn1kzGiE8NkB4LH6Shr2gBGdpDY7SautfUYVMyhDAynpQfwo8APwx4Af+I/BlrIQl7kI0FCAUazj2AAAZVklEQVQ82h9LRn2ERvtXfLPn3EGjqAJ7ceystKgCw5EdEwcCoQgnO3o5czG+5Lonz8HhffWoDVJyPRvY8vJx5eXjqmyYazOjEetEazZpTQwRnhi+5VChNYt0DHwXAKun5SipwlFag7O0GkdpDTZ3gXwe1qCVJKg3AO/TWn8fQCn1Q+CMUsqptb7zKWBrjGlGiUyOEhrpnUtKkclRbj+zymKN51fMncU6iiqw5Rdn5R+saZroGyMcO9PDtH/+42MzDPZureT+3bXkScn1rGbY7EsOF0aDM4QnhuOunS41QmBGI4RGegmN9DI7gGjLy1/Qy6rBWVKVNSdj4s6tJEF5gVdmf9BadyilgkAdcCNZgWW7aNBv9YpGYsN1Y/0rWsbH5nLHnTk6SqpyZox+YGSGltYueobiz6S9VYU0N3mpKPGkKTKRCjaXB1eFFyq8c21mJEx4fHBuSDs82r/kDcfRwDSBvqsE+q7GWgwrCZZW4yyvw1lej91TeNN2IrutJEHZgcVH1ghSpDBONBwkNNRNaLib0FA34fEhlu0dGQaOogqcZTWxpFSTtT2j2/EHwhw/28vZK0M3lVx/cF8929aV5tzvLFbGsDtwltXiLKuda4sGpgmN9i+4FjuwxP16ZmzocAh/5zkA7PnFOCvqcZbX46zwYnfL7QjZbqVTo/4p1mua5Qa+qZSam8KjtX79qkaW4cxIyOodDfoIDfsIjQ0sO8XblufBWVprnfWVWb2j1VqBIBNFoybnrg3z4qs9cSXXbTaDxm1V3LerRkqui5vY8vLJq9lIXs1GIDY8PjFCeKyf0EispzU5wuITwNl7tfyd54HY5IsKL67yepwV9djyZMJNtllJgvrvS7T9j9UOJNNZ4+J9hIZ8hIa7CY/03X52nWHgKK7CWWb1jBxlNdjcmTubbrX1Dk3R0uqjf2Q6rn1dTRHNjV7Kit1pikxkG8OwzV1/da/bCVgjFuFYLys41E14pPempblmJ1/4b3QAYC8sw1VRj7Pci7O8DlueDClnumUTlNb6PakIJNOYZtSazDA7bDfcs8zadAaO4grrjK3Ci6O8NuU3umaCaX+I4+09dFyNL7lelO/iof31bPaWrJkkLZLH5nDhqmzAVdlA/tYD1hT2WLIKDXUTHu27OWFNjjAzOcLM9bMAOArLrSHBCmtIMFeu9eYSuftxgcj0BMGBGwQHbhAa7l52UsP8Bzx2RuZau72CaNSk/cogJ872EgjOHxjsNoMDO2poUtVLlsIQYjUYNrt17am8HrZZky9Co/23HfEITw4Tnhxm5no7GAbOsjpcVetwVW/AXii3OWSClCUopdTHgbcD24AR4F+Aj2mtJ2+7YRKZ0Qih4R4rKfXfsJYKug17fgnOinpcFV4Z016ge2CSljYfg6PxqwpsqivmoUYvJYVyZipSy7A7cFXU46qoBxZcMx7yERrusZZmWnjN2DRjIyXdTOkT2NwFuKo34Kpaj6vCK1Pa0ySVPagHsVY+fwWoAb4CFAEpHUKMTI/HekmdhIZ8Vg2fW7B7Cq3eUYVXprEuYXImxItnutE3RuLaSwrzONLoZWNdcZoiEyKeYXfODQmCtQJ8aKSH0FA3wcEuwuODcc+P+qfw3+jAf6Mj1jurs5JV9QbsBSXp+BXWpJQlKK31Gxf+qJT6BNZisykR8U8x0foTQiO9t3yOYbNb15Cq1uGqWp+TU75XQyQS5fSlQU529BIKzw+bOOw27t1ZQ+P2Khx2Gc4TmctwOK2EU7WeAqyp7bMjKcHBzrjhfTMaITjYRXCwC84dw55fgqt6Pa6qdTjL663CmCIp0rlnK4Hbj6mtokDPpSWTk3zYEtPZN0FLq4+RifjFarc2lHJ4fz1F+WtvYojIfra8fNwNO3A37JifcNFvJazwZPyEn8j0GDPXXmXm2qvWfVwVXlxV68mr2YhN7r1aVWk5GiulSoAPA3+Xqvd0Va5nxtWKGQ5ZH6jq9bgq10l3fYUmpoMcPd3N5a74c4ryYjdHGr2sq1lZcT0hMt3CCRcFOw5aq7IP3CDY30loqCvusoAZCRPsv06w/zqTZ4/iLKshr3YzrtpNtyxtIlYu5QkqVrbjfwFXgM+l6n0dRWVU/OK7Mc2o1KRJQDgSpe3CAC+f6yMcmR/Ocznt3L+rhr1bq7BLjSaRw+yeIjzrd+NZv9uaHTjSG0tKN4hMjy14pjm3hiDnjuEoqSavdhN5tZvlRPgOpTRBKaUcwHewJke8Tmt96xkKSSLJaeVmS66PTcavsL5jQxmH9tZT4JGZTWJtMeyO+ckWuw4TmRoj2H+dQN81QsM9LFzdIjzWT3isnyl9AkdxJXm1m3DVbsZRWJa+XyDLpHKauQ34JrAVeDid08vF7Y1NBjja5uNqT3zJ9cpSD81NXuorZTajEGAtp+TZtA/Ppn1EAzME+q4S7L1CcMgXN43dKjY6yNSFkzgKy3HN9qyKymUi1m2ksgf1FeA1wL8DXEqp2dUhB7TWt1uiQaRIKBzl1Pk+Tun4kut5LjsHd9exe3OFlFwX4hZseR4863fhWb+LaNBPsP8agd6rhAa74la1CE8OE740zPSlV7Dnl5BXt9lKVsWVkqwWSWWCel/sa+ui9k3AtRTGIRYxTZMrvjGOnu5mYnp+TWDDMNi5sZyDe2rJd8twnhArZXO552YFRsNBgn3XrZ7VwI24ZBWZHmP6civTl1uxe4rI824nr34bjsLSNEafOVJ5H5ScGmSgkXE/LW0+Ovvia/DUlOfT3NRATbmsliHE3bA5XLi923B7t2GGQwQHOgn0XibYfyOujEhkZoLpS68wfekVHCXVuL3byKvbuqYXtZWbftaoYCjCyXN9nL4wcFPJ9UN769i5UcbGhVhthsNpDenVbbamqA92Eey9QqDvGmZ4fvQiPNbP5Fg/k+eO4apaj7t+G66ajWvuPs219dsKTNPkYucox850Mzkzf/ZmGAZ7t1Rw/+5a3C75WAiRbIbdMVf3qjAasWYD+i5YPavZhW1Nc+4+K8PhJK92C3ne7TjL69bECaQcidaQobEZnjvlo3swfgJlfWUBzU0NVJau3aEEIdLJsNnJq7UmS0SDfgK9lwn4LsatfmOGQ/i7zuPvOo/dU0he/Tby6rfjKMrdaeuSoNYAfzDMybN9vHp5MG44L9/t5PC+Oravl9ICQmQKm8s9d2NwZGoMf/dFAr6LcTcFR2Ym5yZXOIorcXu3k1e/NecqLEiCymGmaXL+2gjHXu1mJrCg5LphsD9Wct3llJLrQmQqe0EJBdvuJX/rAcKjfQS6LxLovkQ0NH/zfHh8kMnxQSbPv4irch3u9btwVa/PiUUJJEHlqP7haVrafPQOTcW1N1QX0dzkpVxKrguRNQzDwFlWi7OsloKdD1ozAX0XCPZfn5+2bppzBVdt7oK5ae72/OxdE1ASVI7xB8K8GCu5bi4Yziv0OHmo0csWKbkuRFYzbPa5yRXRUIBAz2UC3RdjSy1Zov6p2JT1U7iqsrdXJQkqR0SjJh1Xhzje3os/OD+cZ7cZNG6v5t6d1TgdMpwnRC6xOfPmVq+ITI3h77QmUUSDs9WtF/SqZkuKrNuZNb0qSVA5oHdoiudOdTGwqOT6htpijjR6KS2SkutC5Dp7QQkFOx4gf/u9BPuu4+/ssIosxkQD00xfPsX05VZcVQ241+3EVb0Bw5a5J66SoLLYtD/EsTM9nL8eX1CtuMA1V3JdhvOEWFsMm33uZuDI9Dj+znNWryqwsFfVSXCgE1ueZ0GvqjitcS9FElQWikRN2i8NcqKjl2Bofl0vh93GgR3VNKlqKbkuhMCeX0yBeoD8bfcS7L+Ov/McwYEuZsuCRAMzsenqbRnZq5IElWV8A5O0nOpiaDy+5PoWbwmH93spLpCS60KIeAtvBLZ6Vefxd51bulflLsCzbhfu9TvTfl+VJKgsMTkd5IUzPVzsHIlrLy3Ko7nRy/razOueCyEyj9Wrup/8bQcI9t+I9ao6metV+aeYuniS6Uuv4KrbgmfDbhylNWm5XCAJKsNFIlFOXxzk5LleQuH5kutOh437dtWyf2sldhnOE0IkyOpVbSKvdhOR6YnYtar5XpVpRmM3Bl/EUVJFwbZ7cVVvSGmMkqAy2PVeq+T66ER8yfXt68t4cF89hVJyXQixCuz5RXO9qkDvVfzX2+PWAQyPDTD28g8pO/J2HEXlKYtLElQGGp8KcvS0jyu+sbj2ihKr5Lq3SkquCyFWn2Gz467firt+K+GxAWautxPovmStVmFA7D8pIwkqg4QjUU7pfk6d7yccmR/OczntPLC7lr1bKqXkuhAiJRwlVRTtey0FOw4SHOjEnl+c8pXTJUFlANM0udZjDeeNTwXjHtu5sZxDe+uk5LoQIi1sLg9u7/a0vLckqDQbnQjwfJuP673jce1VZR4ebmqgtqIgTZEJIUR6SYJKk1A4wsvn+mm70E8kOr+oq9s1X3JdhvOEEGuZJKgUM02TS12jvHD65pLruzdXcHB3Le48+d8ihBByJEyhobEZnm/z0dUfX3K9tqKA5iYv1WW5VQ1TCCHuhiSoFAiEIpzs6OXMxfiS6548B4f31aM2SMl1IYRYTBJUEpmmib4xwrEzPUz754fzbIbB3q2V3L+7ljwpuS6EEEuSBJUkAyMztLR20bOo5Lq3qpDmJi8VJZ40RSaEENlBEtQq8wfCnDjbS/uVoZtKrh/eX8/WhlIZzhNCiBWQBLVKolGTc9eGefHVnriS6zabQeO2Ku7bVSMl14UQIgGSoFZB79AULa0++kem49rX1xRxpMlLWZE7TZEJIUT2kgR1F6b9IY6399Bx9eaS6w/t97KpXkquCyHEnZIEdQeiUZP2K4OcaO8lsKDkut1mcGBHDffskJLrQghxtyRBJah7YJLnWn0Mjc3EtW+qL+Gh/fWUFOalKTIhhMgtkqBWaHImxItnutE3FpVcL8zjSKOXDXVScl0IIVaTJKhlRCJRTl8a5GTHopLrdhsHdtbQtL1KSq4LIUQSSIK6jc6+CVpafYxM+OPat60r5fC+egrzXWmKTAghcp8kqCVMTAc5erqby12jce3lxW6ONHpZV1OUpsiEEGLtkAS1QDgSpe3CAC+f67up5Pr9u2rYu7UKu9RoEkKIlJAEFTNbcn1sMhDXvmNDGYf21lPgkZLrQgiRSms+QY1NBjja5uNqT3zJ9cpSD81NXuorC9MUmRBCrG1rNkGFwlFeOd9Hq44vuZ7nsnNwdx27N1dIyXUhhEijlCcopdRHgSeAUuDHwPu11v2pen/TNLnsG+OF091MTAfn2g3DYOfGcg7uqSXfLcN5QgiRbilNUEqp9wAfA94JXAWeAr4NvC4V7z8y7qelzUdn30Rce015Ps1NDdSUS8l1IYTIFKnuQT0BfEFr/V0ApdR7gctKqT1a6/ZkvvHg6Az/9NMLccN5njwHh/bWsXNjuSzqKoQQGSZlSyAopfKA/cDPZtu01leAa8ADyX5/X//kXHIyDIN9Wyt5/NEd7NpUIclJCCEyUCp7UBVYCXHx9aYBoDrZb75tfSndQ1PYDIMDO6qpLJWS60IIkclSmaDS2k3Jdzt5w6GN6QxBCCFEAlK5yukgEOXm3lIVN/eqhBBCrHEpS1Ba6wBwGnjtbJtSahOwETiRqjiEEEJkh1TP4vsS8JRSqhVrcsQXgX9L9gw+IYQQ2SelhYy01n8H/BnwZeBFYAL4tVTGIIQQIjukfCUJrfWfYSUpIYQQ4pakFKwQQoiMlE2LxdoBent70x2HEEKIBC04dttXuk02Jag6gMcffzzdcQghhLhzdcDllTwxmxLUSeAI0ANE0hyLEEKIxNixktPJlW5gmKa5/LOEEEKIFJNJEkIIITKSJCghhBAZSRKUEEKIjCQJSgghREaSBCWEECIjSYISQgiRkSRBCSGEyEiSoIQQQmQkSVBCCCEyUjYtdbQiSqmPAk8ApcCPgfdrrZcsKa+U2g58BXgA6AU+qbX+RopCTbqV7gulVCPwceAhoAhoB57UWv+fFIabVIl8LhZscwA4DrygtX5N0oNMkQT/RhzAJ4D3ADXAdeB3tNY/SVG4SZXgvngL8ElAAcPAPwMf0VoHUxRu0iil3gb8DnAvUKy1NpZ5fkqOnTnVg1JKvQf4GNaOfhDrQ/ftWzzXCfxvoA+4D/gM8BWl1MOpiTa5EtkXQBPWgedXgEbgR8D3lVI7UhBq0iW4L2a3yQO+ATyX7PhS6Q72xdPAW4H3YR2YfxNrPcysl+DxYgtWQvoWsBt4J/DLWCd2uSAf+BnwueWemMpjZ671oJ4AvqC1/i6AUuq9wGWl1J4lysq/AfACjVrrKaA9toOfIDcOSiveF1rrry/a9o+VUr8CvB44n5JokyuRz8Wsz2D9wY4Ar0lJlKmx4n2hlNqLdSDerrW+Gmu+lspgkyyRz8U9wLjW+i9jP19VSv0jcCB14SaP1vp/ACilXrOCp6fs2JkzPajYGe9+rIMKAFrrK1h/UA8sscn9wInYDp7101s8N6vcwb5YvL0BVACjSQoxZe5kXyilDgNvAT6aghBT5g72xRuBS8CvKqU6lVLnlVJPKqVWXM8nU93BvngFKFJKPaaUMpRS64BHsYYF15qUHTtzJkFhHVBtwOLx4wGgeonnVyfw3GyT6L5Y7INYS+N/f5XjSoeE9oVSKh/4Ota1iOnkh5dSiX4uNgKbsXrSv4KVsD8E/EHyQkyZhPZFLHm9BWvYNwjcAJ7TWv9VcsPMSCk7duZSgrrtRb1VeH42uePfTSn1KNY49ONa65HVCyltEt0XnwN+pLXOhWHexRLdFzbABbxba30iNhT2p1jXo7JdQvtCKVUP/DXW5+MAVrJ6VCn1+0mILdOl7NiZS9egBoEoVhY/t6C9ipuzPVgX+LYuarvVc7NNovsCAKXUEeCfgPdprXNl6CLRfdEM7FFK/XbsZxtgKKXCwBat9fVkBptkd/I3Elj0O2tgXdIiTJ1E98V/BC5rrWcnEZxRShUBXwS+kMxAM1DKjp0504PSWgeA08BrZ9uUUpuwhilOLLHJS8ADsSGdWb9wi+dmlTvYFyil7geeBf5Aa/2dFISZEnewL34Zaybj7L8vAy/Hvu9OcrhJdQf74jiQp5RqWNC2FWt4K6vdwb7I5+ZK3lFy6BiagJQdO3OpBwXwJeAppVQr1sXOLwL/prVujx2Avwm8TmvtA36IdcD5mlLqM1gX+H4NeCQtka++Fe+L2GytH2JNKX5GKVUbe40ZrfVYGmJfbSveF1rryws3VEr1A1O3me2XbRL5G/kRVu/ib5VSH8Yq1/1R4L+kJfLVl8i++AHwn5RSH4x9vxH4NLlxnRalVDmwnljPKHZvJEAH1slZWo6dOZX9tdZ/B/wZ1lnvi8AE1o4D6wxIAc7Yc4NYs5TqsGbo/BHWhfGcuPaQyL7A6jWUAf8Z6x6X2X//NYUhJ02C+yKnJfg3Esb6GzGAk8DfYl2HWXOfi9hN6+8FPoB1I/s3sWbw/W5qo06atwCtWP+PiX3fCtSTxmOnYZrmar+mEEIIcddyqgclhBAid0iCEkIIkZEkQQkhhMhIkqCEEEJkJElQQgghMpIkKCGEEBlJEpQQd0Ep9XOl1FfTHMP/p5T60gqfW6SU6lVK7U92XELcrVxbSUKIVaGUWu4Gweta643A24Bw8iNamlJKYVW7Xbw22pK01hNKqS9grQbxi8mMTYi7JT0oIZZWt+DfW2Nt9y9ouw9Aaz2stR5PS4SWDwE/0Fr3JrDNN4CHlVJ7khOSEKtDelBCLGHhAV8pNRz7dmBxIlBK/Ry4pLX+zQU/X8ZaKur9WOUq/hvwCeBJrPLiNuArWuuPL3gdR+zxd2ElwMvAX2mtn75VjEopG9bSPB9c1P4Q8OfAvljTFeAjWusfxX63fqXUMeAdwB+uaIcIkQbSgxJi9f0K1rplDwG/D3wMa6X4QuAI8GHgY0qpNyzY5qtYw4UfAHYCnwL+XCl1u9pLe7HWUHxptiFW7fYZrJWl74n9+xNgcfHFEyxYyVuITCQ9KCFW31Wt9f8b+/6CUuoPgHVa63+3oO33gdcB/xor8/BOYJfW+vzsa8SuLz0BfO0W77Mp9tW3oK0YK2k9o7W+GGu7yM26sKrlCpGxJEEJsfpOL/q5N/Zvcdtsiex7sVYMf9nKSXMc3FyDaCFP7GtgtkFrPRKbVfgjpdTPgOeA72qt9aJt/Qu2FyIjyRCfEKsvtOhn8xZts39/s18fJL5Y4h7mryMtZSD2tWxho9b6t7DKkv8EeBhoV0p9YNG25Qu2FyIjSQ9KiPR7JfZ1vdb62QS2a8VKdLuBloUPxAostgNfUEp9GWvCxsIJF3uxKgULkbEkQQmRZlrrS0qpv8OqXPsRrOJ5BVi9oCqt9Z/fYrshpdRLWL2kFgCl1Fbgt7AqvXZiFZw7Apya3U4pZQDNWDMLhchYMsQnRGZ4P1bJ8Y9jldn+KdaU8yvLbPc3wG8s+HkK2AZ8B7gA/C/gGPFT0V+DNaPwH1chbiGSRirqCpHFlFJO4AzwUa3191a4zQ+A527VMxMiU0gPSogsprUOYfW0ClbyfKVUEdYQ4lPJjEuI1SA9KCGEEBlJelBCCCEykiQoIYQQGUkSlBBCiIwkCUoIIURGkgQlhBAiI0mCEkIIkZH+L42LpQuqtzS+AAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results.x, label='x')\n", - "plot(results.y, label='y')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Position (m)')" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results.theta, label='theta', color='C2')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Angle (radian)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can plot the velocities the same way." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results.vx, label='vx')\n", - "plot(results.vy, label='vy')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Velocity (m/s)')" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3Xl8FfW5x/FPArE1AqJWARVEoT6lRVzBasWlqK3aWkVqFwXctValtRVwueKCCi1FW1u3VkUW8bbqdasLuGCXa1W8WAri08qiJApoFa0KAiH3j98EDjHnZE6YOZkk3/frdV6cmfnNzDM54Tz5zfyWstraWkRERLKmvLkDEBERaYgSlIiIZJISlIiIZJISlIiIZJISlIiIZJISlIiIZJISlIiIZJISlIiIZJISlIiIZJISlIiIZFL75g4gK8zsM0B/4C2gppnDERFpbdoB3YAX3f2TODsoQW3UH/hzcwchItLKDQT+EqegEtRGbwFMmzaNrl27NncsIiKtyrJlyzjppJMg+q6NQwlqoxqArl27svPOOzd3LCIirVXsRyhqJCEiIpmkBCUiIpmkBCUiIpmkBCUiIpmkRhIi0mqtX7+eqqoqPvroo+YOpdWrqKhghx12oFOnTokdUwlKRFqtd955h7KyMsyM8nLdMEpLbW0tq1atorq6GiCxJKVPTERarZUrV9KlSxclp5SVlZVRWVnJTjvtxIoVKxI7rj41EWm1ampqqKioaO4w2owtt9yStWvXJnY8JSgRadXKysqaO4Q2I+mftRKUiIhkkhKUiIhkkhKUiIhkkhKUiIhkkhKUiEhGrFq1issuu4z+/fuz9957M2LECN59910ARo8ezahRoxg/fjz77rsvhx56KM888wzV1dUMHTqUvffemzPOOIOVK1duOF5NTQ033HADBx98MHvvvTdDhw7F3Tdsr62tZcKECey33358+ctf5rbbbmPo0KHceOONG8pcc801DBo0iD333JNjjjmGRx99tGQ/DyUoEZGMGDduHC+++CI333wzU6dO5a233uLiiy/esH3mzJlstdVW3HfffRx++OGMHj2aMWPGcNZZZzF9+nSqqqq49dZbN5T/9a9/zZ///GcmTpzIAw88wD777MPpp5++YWSN+++/n3vuuYfrrruOqVOn8sorrzB//vxNYurcuTPX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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results.omega, label='omega', color='C2')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Angular velocity (rad/s)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another way to visualize the results is to plot y versus x. The result is the trajectory through the plane of motion." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(results.x, results.y, label='trajectory', color='C3')\n", - "\n", - "decorate(xlabel='x position (m)',\n", - " ylabel='y position (m)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercises\n", - "\n", - "**Exercise:** Find the starting conditions that make the final height of the COG as close as possible to 1.5 m. Ideally, the final angle should be a little past vertical." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Compute the total velocity of the leading edge of the axe at the point of impact, that is, the sum of velocity due to translation and rotation." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_frame(theta):\n", - " rhat = Vector(pol2cart(theta, 1))\n", - " that = rhat.perp()\n", - " return rhat, that" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [], - "source": [ - "# quick, get those state variables into Vector objects!\n", - "state = get_last_value(results)\n", - "x, y, theta, vx, vy, omega = state\n", - "P = Vector(x, y)\n", - "V = Vector(vx, vy)\n", - "rhat, that = make_frame(theta)" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "Unknown property update", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0mA\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mP\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0ml1\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mrhat\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0mB\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mP\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0ml2\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mrhat\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 16\u001b[0;31m \u001b[0mplot_segment\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[0mcolor\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'red'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mupdate\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 17\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;31m# plot the axe head\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/ModSimPy/code/soln/modsim.py\u001b[0m in \u001b[0;36mplot_segment\u001b[0;34m(A, B, **options)\u001b[0m\n\u001b[1;32m 1307\u001b[0m \u001b[0mxs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mB\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1308\u001b[0m \u001b[0mys\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mB\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1309\u001b[0;31m \u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mxs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mys\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1310\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1311\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/ModSimPy/code/soln/modsim.py\u001b[0m in \u001b[0;36mplot\u001b[0;34m(*args, **options)\u001b[0m\n\u001b[1;32m 652\u001b[0m \u001b[0mlines\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstyle\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 653\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 654\u001b[0;31m \u001b[0mlines\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 655\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mlines\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 656\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/envs/ModSimPy/lib/python3.6/site-packages/matplotlib/pyplot.py\u001b[0m in \u001b[0;36mplot\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 3356\u001b[0m mplDeprecation)\n\u001b[1;32m 3357\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 3358\u001b[0;31m \u001b[0mret\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0max\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3359\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3360\u001b[0m \u001b[0max\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_hold\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwashold\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/envs/ModSimPy/lib/python3.6/site-packages/matplotlib/__init__.py\u001b[0m in \u001b[0;36minner\u001b[0;34m(ax, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1853\u001b[0m \u001b[0;34m\"the Matplotlib list!)\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mlabel_namer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__name__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1854\u001b[0m RuntimeWarning, stacklevel=2)\n\u001b[0;32m-> 1855\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0max\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1856\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1857\u001b[0m inner.__doc__ = _add_data_doc(inner.__doc__,\n", - "\u001b[0;32m~/anaconda3/envs/ModSimPy/lib/python3.6/site-packages/matplotlib/axes/_axes.py\u001b[0m in \u001b[0;36mplot\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1525\u001b[0m \u001b[0mkwargs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcbook\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnormalize_kwargs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_alias_map\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1526\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1527\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mline\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_get_lines\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\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 1528\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_line\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mline\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1529\u001b[0m \u001b[0mlines\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mline\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/envs/ModSimPy/lib/python3.6/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36m_grab_next_args\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 404\u001b[0m \u001b[0mthis\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 405\u001b[0m \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 406\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mseg\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_plot_args\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mthis\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwargs\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 407\u001b[0m \u001b[0;32myield\u001b[0m \u001b[0mseg\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 408\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/envs/ModSimPy/lib/python3.6/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36m_plot_args\u001b[0;34m(self, tup, kwargs)\u001b[0m\n\u001b[1;32m 394\u001b[0m \"with non-matching shapes is deprecated.\")\n\u001b[1;32m 395\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mj\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mxrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmax\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mncx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mncy\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--> 396\u001b[0;31m \u001b[0mseg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mj\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mncx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mj\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mncy\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkw\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 397\u001b[0m \u001b[0mret\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mseg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 398\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mret\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/envs/ModSimPy/lib/python3.6/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36m_makeline\u001b[0;34m(self, x, y, kw, kwargs)\u001b[0m\n\u001b[1;32m 298\u001b[0m \u001b[0mdefault_dict\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_getdefaults\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 299\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_setdefaults\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdefault_dict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 300\u001b[0;31m \u001b[0mseg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmlines\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mLine2D\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 301\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mseg\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 302\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/envs/ModSimPy/lib/python3.6/site-packages/matplotlib/lines.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, xdata, ydata, linewidth, linestyle, color, marker, markersize, markeredgewidth, markeredgecolor, markerfacecolor, markerfacecoloralt, fillstyle, antialiased, dash_capstyle, solid_capstyle, dash_joinstyle, solid_joinstyle, pickradius, drawstyle, markevery, **kwargs)\u001b[0m\n\u001b[1;32m 419\u001b[0m \u001b[0;31m# update kwargs before updating data to give the caller a\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 420\u001b[0m \u001b[0;31m# chance to init axes (and hence unit support)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 421\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 422\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpickradius\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpickradius\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 423\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mind_offset\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/envs/ModSimPy/lib/python3.6/site-packages/matplotlib/artist.py\u001b[0m in \u001b[0;36mupdate\u001b[0;34m(self, props)\u001b[0m\n\u001b[1;32m 886\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 887\u001b[0m ret = [_update_property(self, k, v)\n\u001b[0;32m--> 888\u001b[0;31m for k, v in props.items()]\n\u001b[0m\u001b[1;32m 889\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 890\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0meventson\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mstore\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/envs/ModSimPy/lib/python3.6/site-packages/matplotlib/artist.py\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 886\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 887\u001b[0m ret = [_update_property(self, k, v)\n\u001b[0;32m--> 888\u001b[0;31m for k, v in props.items()]\n\u001b[0m\u001b[1;32m 889\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 890\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0meventson\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mstore\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/envs/ModSimPy/lib/python3.6/site-packages/matplotlib/artist.py\u001b[0m in \u001b[0;36m_update_property\u001b[0;34m(self, k, v)\u001b[0m\n\u001b[1;32m 879\u001b[0m \u001b[0mfunc\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'set_'\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mk\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 880\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mcallable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfunc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 881\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mAttributeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Unknown property %s'\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mk\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 882\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 883\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mAttributeError\u001b[0m: Unknown property update" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "decorate(xlabel='x position (m)',\n", - " ylabel='y position (m)',\n", - " xlim=[7, 9],\n", - " ylim=[0, 1.5],\n", - " legend=False)\n", - "\n", - "# plot the handle\n", - "# l1 and l2 are the lengths of the handle below and above\n", - "# the center of gravity (COG)\n", - "l1 = 0.6\n", - "l2 = 0.1\n", - "A = P - l1 * rhat\n", - "B = P + l2 * rhat\n", - "plot_segment(A, B, color='red', update=True)\n", - "\n", - "# plot the axe head\n", - "C = B + l2 * that\n", - "D = B - l2 * that\n", - "plot_segment(C, D, color='black', linewidth=10, update=True)\n", - "\n", - "# plot the COG\n", - "plot(x, y, 'bo', update=True)\n", - " \n", - "plot_segment(P, D, color='green', linewidth=1, update=True)\n", - "\n", - "# compute the velocity of point D due to rotation\n", - "R = D - P\n", - "VrotD = omega * R.perp()\n", - "\n", - "plot_segment(D, D+VrotD, color='orange', linewidth=1, update=True)\n", - "\n", - "# add in the velocity due to translation\n", - "Vtot = VrotD + V\n", - "plot_segment(D, D+Vtot, color='purple', linewidth=1, update=True)\n", - "\n", - "# print the magnitude of velocity at the point of contact\n", - "Vtot.mag" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Animation\n", - "\n", - "NOTE: This section needs to be updated.\n", - "\n", - "Animating this system is a little more complicated, if we want to show the shape and orientation of the axe.\n", - "\n", - "It is useful to construct a frame with $\\hat{r}$ along the handle of the axe and $\\hat{\\theta}$ perpendicular." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we're ready to animate the results. The following figure shows the frame and the labeled points A, B, C, and D.\n", - "\n", - "![Diagram of the axe with reference frame](diagrams/throwingaxe2.png)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "application/javascript": [ - "/* Put everything inside the global mpl namespace */\n", - "window.mpl = {};\n", - "\n", - "\n", - "mpl.get_websocket_type = function() {\n", - " if (typeof(WebSocket) !== 'undefined') {\n", - " return WebSocket;\n", - " } else if (typeof(MozWebSocket) !== 'undefined') {\n", - " return MozWebSocket;\n", - " } else {\n", - " alert('Your browser does not have WebSocket support.' +\n", - " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", - " 'Firefox 4 and 5 are also supported but you ' +\n", - " 'have to enable WebSockets in about:config.');\n", - " };\n", - "}\n", - "\n", - "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", - " this.id = figure_id;\n", - "\n", - " this.ws = websocket;\n", - "\n", - " this.supports_binary = (this.ws.binaryType != undefined);\n", - "\n", - " if (!this.supports_binary) {\n", - " var warnings = document.getElementById(\"mpl-warnings\");\n", - " if (warnings) {\n", - " warnings.style.display = 'block';\n", - " warnings.textContent = (\n", - " \"This browser does not support binary websocket messages. \" +\n", - " \"Performance may be slow.\");\n", - " }\n", - " }\n", - "\n", - " this.imageObj = new Image();\n", - "\n", - " this.context = undefined;\n", - " this.message = undefined;\n", - " this.canvas = undefined;\n", - " this.rubberband_canvas = undefined;\n", - " this.rubberband_context = undefined;\n", - " this.format_dropdown = undefined;\n", - "\n", - " this.image_mode = 'full';\n", - "\n", - " this.root = $('
');\n", - " this._root_extra_style(this.root)\n", - " this.root.attr('style', 'display: inline-block');\n", - "\n", - " $(parent_element).append(this.root);\n", - "\n", - " this._init_header(this);\n", - " this._init_canvas(this);\n", - " this._init_toolbar(this);\n", - "\n", - " var fig = this;\n", - "\n", - " this.waiting = false;\n", - "\n", - " this.ws.onopen = function () {\n", - " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", - " fig.send_message(\"send_image_mode\", {});\n", - " if (mpl.ratio != 1) {\n", - " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", - " }\n", - " fig.send_message(\"refresh\", {});\n", - " }\n", - "\n", - " this.imageObj.onload = function() {\n", - " if (fig.image_mode == 'full') {\n", - " // Full images could contain transparency (where diff images\n", - " // almost always do), so we need to clear the canvas so that\n", - " // there is no ghosting.\n", - " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", - " }\n", - " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " };\n", - "\n", - " this.imageObj.onunload = function() {\n", - " this.ws.close();\n", - " }\n", - "\n", - " this.ws.onmessage = this._make_on_message_function(this);\n", - "\n", - " this.ondownload = ondownload;\n", - "}\n", - "\n", - "mpl.figure.prototype._init_header = function() {\n", - " var titlebar = $(\n", - " '
');\n", - " var titletext = $(\n", - " '
');\n", - " titlebar.append(titletext)\n", - " this.root.append(titlebar);\n", - " this.header = titletext[0];\n", - "}\n", - "\n", - "\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "\n", - "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "mpl.figure.prototype._init_canvas = function() {\n", - " var fig = this;\n", - "\n", - " var canvas_div = $('
');\n", - "\n", - " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", - "\n", - " function canvas_keyboard_event(event) {\n", - " return fig.key_event(event, event['data']);\n", - " }\n", - "\n", - " canvas_div.keydown('key_press', canvas_keyboard_event);\n", - " canvas_div.keyup('key_release', canvas_keyboard_event);\n", - " this.canvas_div = canvas_div\n", - " this._canvas_extra_style(canvas_div)\n", - " this.root.append(canvas_div);\n", - "\n", - " var canvas = $('');\n", - " canvas.addClass('mpl-canvas');\n", - " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", - "\n", - " this.canvas = canvas[0];\n", - " this.context = canvas[0].getContext(\"2d\");\n", - "\n", - " var backingStore = this.context.backingStorePixelRatio ||\n", - "\tthis.context.webkitBackingStorePixelRatio ||\n", - "\tthis.context.mozBackingStorePixelRatio ||\n", - "\tthis.context.msBackingStorePixelRatio ||\n", - "\tthis.context.oBackingStorePixelRatio ||\n", - "\tthis.context.backingStorePixelRatio || 1;\n", - "\n", - " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", - "\n", - " var rubberband = $('');\n", - " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", - "\n", - " var pass_mouse_events = true;\n", - "\n", - " canvas_div.resizable({\n", - " start: function(event, ui) {\n", - " pass_mouse_events = false;\n", - " },\n", - " resize: function(event, ui) {\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " stop: function(event, ui) {\n", - " pass_mouse_events = true;\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " });\n", - "\n", - " function mouse_event_fn(event) {\n", - " if (pass_mouse_events)\n", - " return fig.mouse_event(event, event['data']);\n", - " }\n", - "\n", - " rubberband.mousedown('button_press', mouse_event_fn);\n", - " rubberband.mouseup('button_release', mouse_event_fn);\n", - " // Throttle sequential mouse events to 1 every 20ms.\n", - " rubberband.mousemove('motion_notify', mouse_event_fn);\n", - "\n", - " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", - " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", - "\n", - " canvas_div.on(\"wheel\", function (event) {\n", - " event = event.originalEvent;\n", - " event['data'] = 'scroll'\n", - " if (event.deltaY < 0) {\n", - " event.step = 1;\n", - " } else {\n", - " event.step = -1;\n", - " }\n", - " mouse_event_fn(event);\n", - " });\n", - "\n", - " canvas_div.append(canvas);\n", - " canvas_div.append(rubberband);\n", - "\n", - " this.rubberband = rubberband;\n", - " this.rubberband_canvas = rubberband[0];\n", - " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", - " this.rubberband_context.strokeStyle = \"#000000\";\n", - "\n", - " this._resize_canvas = function(width, height) {\n", - " // Keep the size of the canvas, canvas container, and rubber band\n", - " // canvas in synch.\n", - " canvas_div.css('width', width)\n", - " canvas_div.css('height', height)\n", - "\n", - " canvas.attr('width', width * mpl.ratio);\n", - " canvas.attr('height', height * mpl.ratio);\n", - " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", - "\n", - " rubberband.attr('width', width);\n", - " rubberband.attr('height', height);\n", - " }\n", - "\n", - " // Set the figure to an initial 600x600px, this will subsequently be updated\n", - " // upon first draw.\n", - " this._resize_canvas(600, 600);\n", - "\n", - " // Disable right mouse context menu.\n", - " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", - " return false;\n", - " });\n", - "\n", - " function set_focus () {\n", - " canvas.focus();\n", - " canvas_div.focus();\n", - " }\n", - "\n", - " window.setTimeout(set_focus, 100);\n", - "}\n", - "\n", - "mpl.figure.prototype._init_toolbar = function() {\n", - " var fig = this;\n", - "\n", - " var nav_element = $('
')\n", - " nav_element.attr('style', 'width: 100%');\n", - " this.root.append(nav_element);\n", - "\n", - " // Define a callback function for later on.\n", - " function toolbar_event(event) {\n", - " return fig.toolbar_button_onclick(event['data']);\n", - " }\n", - " function toolbar_mouse_event(event) {\n", - " return fig.toolbar_button_onmouseover(event['data']);\n", - " }\n", - "\n", - " for(var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " // put a spacer in here.\n", - " continue;\n", - " }\n", - " var button = $('
" - ], - "text/plain": [ - "2 1.40\n", - "3 1.49\n", - "4 1.75\n", - "5 2.18\n", - "6 2.78\n", - "8 4.45\n", - "10 6.74\n", - "15 14.86\n", - "20 25.39\n", - "25 35.60\n", - "30 45.00\n", - "35 53.65\n", - "40 61.60\n", - "45 68.92\n", - "50 75.66\n", - "55 81.85\n", - "60 87.56\n", - "65 92.80\n", - "70 97.63\n", - "dtype: float64" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "site45 = TimeSeries([1.4, 1.49, 1.75, 2.18, 2.78, 4.45, 6.74,\n", - " 14.86, 25.39, 35.60, 45.00, 53.65, 61.60,\n", - " 68.92, 75.66, 81.85, 87.56, 92.8, 97.63],\n", - " index=years)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the series for site index 65." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
21.40
31.56
42.01
52.76
63.79
86.64
1010.44
1523.26
2037.65
2551.66
3065.00
3577.50
4089.07
4599.66
50109.28
55117.96
60125.74
65132.68
70138.84
\n", - "
" - ], - "text/plain": [ - "2 1.40\n", - "3 1.56\n", - "4 2.01\n", - "5 2.76\n", - "6 3.79\n", - "8 6.64\n", - "10 10.44\n", - "15 23.26\n", - "20 37.65\n", - "25 51.66\n", - "30 65.00\n", - "35 77.50\n", - "40 89.07\n", - "45 99.66\n", - "50 109.28\n", - "55 117.96\n", - "60 125.74\n", - "65 132.68\n", - "70 138.84\n", - "dtype: float64" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "site65 = TimeSeries([1.4, 1.56, 2.01, 2.76, 3.79, 6.64, 10.44, \n", - " 23.26, 37.65, 51.66, 65.00, 77.50, 89.07, \n", - " 99.66, 109.28, 117.96, 125.74, 132.68, 138.84],\n", - " index=years)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And for site index 85." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2 1.40\n", - "3 1.80\n", - "4 2.71\n", - "5 4.09\n", - "6 5.92\n", - "8 10.73\n", - "10 16.81\n", - "15 34.03\n", - "20 51.26\n", - "25 68.54\n", - "30 85.00\n", - "35 100.34\n", - "40 114.33\n", - "45 126.91\n", - "50 138.06\n", - "55 147.86\n", - "60 156.39\n", - "65 163.76\n", - "70 170.10\n", - "dtype: float64" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "site85 = Series([1.4, 1.8, 2.71, 4.09, 5.92, 10.73, 16.81, \n", - " 34.03, 51.26, 68.54, 85, 100.34, 114.33,\n", - " 126.91, 138.06, 147.86, 156.39, 163.76, 170.10],\n", - " index=years)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the curves look like:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving figure to file figs/trees-fig01.pdf\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(site85, label='SI 85')\n", - "plot(site65, label='SI 65')\n", - "plot(site45, label='SI 45')\n", - "decorate(xlabel='Time (years)',\n", - " ylabel='Height (feet)')\n", - "\n", - "savefig('figs/trees-fig01.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For my examples I'll work with the SI 65 data; as an exercise, you can run the notebook again with either of the other curves." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "data = site65;" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Model 1\n", - "\n", - "As a starting place, let's assume that the ability of the tree to gain mass is limited by the area it exposes to sunlight, and that the growth rate (in mass) is proportional to that area. In that case we can write:\n", - "\n", - "$ m_{n+1} = m_n + \\alpha A$\n", - "\n", - "where $m_n$ is the mass of the at time step $n$, $A$ is the area exposed to sunlight, and $\\alpha$ is an unknown growth parameter.\n", - "\n", - "To get from $m$ to $A$, I'll make the additional assumption that mass is proportional to height raised to an unknown power:\n", - "\n", - "$ m = \\beta h^D $\n", - "\n", - "where $h$ is height, $\\beta$ is an unknown constant of proportionality, and $D$ is the dimension that relates height and mass. \n", - "\n", - "We'll start by assuming $D=3$, but we'll revisit that assumption.\n", - "\n", - "Finally, we'll assume that area is proportional to height squared:\n", - "\n", - "$ A = \\gamma h^2$\n", - "\n", - "I'll specify height in feet, and choose units for mass and area so that $\\beta=1$ and $\\gamma=1$.\n", - "\n", - "Putting all that together, we can write a difference equation for height:\n", - "\n", - "$ h_{n+1}^D = h_n^D + \\alpha h_n^2 $\n", - "\n", - "Now let's solve it. Here's a system object with the parameters and initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
alpha7.0
dim3.0
h_01.4
t_02.0
t_end70.0
\n", - "
" - ], - "text/plain": [ - "alpha 7.0\n", - "dim 3.0\n", - "h_0 1.4\n", - "t_0 2.0\n", - "t_end 70.0\n", - "dtype: float64" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "alpha = 7\n", - "dim = 3\n", - "\n", - "t_0 = get_first_label(data)\n", - "t_end = get_last_label(data)\n", - "\n", - "h_0 = get_first_value(data)\n", - "\n", - "system = System(alpha=alpha, dim=dim, \n", - " h_0=h_0, t_0=t_0, t_end=t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's an update function that takes the current height as a parameter and returns the height during the next time step." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "def update(height, t, system):\n", - " \"\"\"Update height based on geometric model.\n", - " \n", - " height: current height in feet\n", - " t: what year it is\n", - " system: system object with model parameters\n", - " \"\"\"\n", - " area = height**2\n", - " mass = height**system.dim\n", - " mass += system.alpha * area\n", - " return mass**(1/system.dim)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test the update function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2.5439688299649954" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "update(h_0, t_0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's our usual version of `run_simulation`." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "def run_simulation(system, update_func):\n", - " \"\"\"Simulate the system using any update function.\n", - " \n", - " system: System object\n", - " update_func: function that computes the population next year\n", - " \n", - " returns: TimeSeries\n", - " \"\"\"\n", - " results = TimeSeries()\n", - " results[system.t_0] = system.h_0\n", - " \n", - " for t in linrange(system.t_0, system.t_end):\n", - " results[t+1] = update_func(results[t], t, system)\n", - " \n", - " return results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's how we run it." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "66 140.497321\n", - "67 142.792941\n", - "68 145.089152\n", - "69 147.385935\n", - "70 149.683273\n", - "dtype: float64" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results = run_simulation(system, update)\n", - "results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot the results:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_results(results, data):\n", - " plot(results, ':', label='model', color='gray')\n", - " plot(data, label='data')\n", - " decorate(xlabel='Time (years)',\n", - " ylabel='Height (feet)')\n", - " \n", - "plot_results(results, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The result converges to a straight line.\n", - "\n", - "I chose the value of `alpha` to fit the data as well as I could, but it is clear that the data have curvature that's not captured by the model.\n", - "\n", - "Here are the errors:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2 0.000000\n", - "3 0.983969\n", - "4 1.942915\n", - "5 2.792070\n", - "6 3.496882\n", - "8 4.387116\n", - "10 4.557437\n", - "15 2.194275\n", - "20 -1.346851\n", - "25 -4.304519\n", - "30 -6.468590\n", - "35 -7.709926\n", - "40 -7.962119\n", - "45 -7.189989\n", - "50 -5.413433\n", - "55 -2.669382\n", - "60 0.997125\n", - "65 5.522310\n", - "70 10.843273\n", - "dtype: float64" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "errors = results - data\n", - "errors.dropna()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the mean absolute error." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "4.251693738559427" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "def mean_abs_error(results, data):\n", - " return np.mean(np.abs(results-data))\n", - "\n", - "mean_abs_error(results, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This model might explain why the height of a tree grows roughly linearly:\n", - "\n", - "1. If area is proportional to $h^2$ and mass is proportional to $h^3$, and\n", - "\n", - "2. Change in mass is proportional to area, and\n", - "\n", - "3. Height grows linearly, then\n", - "\n", - "4. Area grows in proportion to $h^2$, and\n", - "\n", - "5. Mass grows in proportion to $h^3$.\n", - "\n", - "If the goal is to explain (approximate) linear growth, we might stop there. But this model does not fit the data particularly well, and it implies that trees could keep growing forever.\n", - "\n", - "So we might want to do better." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Model 2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As a second attempt, let's suppose that we don't know $D$. In fact, we don't, because trees are not like simple solids; they are more like fractals, which have [fractal dimension](https://en.wikipedia.org/wiki/Fractal_dimension).\n", - "\n", - "I would expect the fractal dimension of a tree to be between 2 and 3, so I'll guess 2.5." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(7, 2.8)" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "alpha = 7\n", - "dim = 2.8\n", - "\n", - "params = alpha, dim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "I'll wrap the code from the previous section is a function that takes the parameters as inputs and makes a `System` object." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(params, data):\n", - " \"\"\"Makes a System object.\n", - " \n", - " params: sequence of alpha, dim\n", - " data: Series\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " alpha, dim = params\n", - " \n", - " t_0 = get_first_label(data)\n", - " t_end = get_last_label(data)\n", - "\n", - " h_0 = get_first_value(data)\n", - "\n", - " return System(alpha=alpha, dim=dim, \n", - " h_0=h_0, t_0=t_0, t_end=t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we use it." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
alpha7.0
dim2.8
h_01.4
t_02.0
t_end70.0
\n", - "
" - ], - "text/plain": [ - "alpha 7.0\n", - "dim 2.8\n", - "h_0 1.4\n", - "t_0 2.0\n", - "t_end 70.0\n", - "dtype: float64" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(params, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With different values for the parameters, we get curves with different behavior. Here are a few that I chose by hand." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "def run_and_plot(alpha, dim, data):\n", - " params = alpha, dim\n", - " system = make_system(params, data)\n", - " results = run_simulation(system, update)\n", - " plot(results, ':', color='gray')" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "run_and_plot(0.145, 2, data)\n", - "run_and_plot(0.58, 2.4, data)\n", - "run_and_plot(2.8, 2.8, data)\n", - "run_and_plot(6.6, 3, data)\n", - "run_and_plot(15.5, 3.2, data)\n", - "run_and_plot(38, 3.4, data)\n", - "\n", - "plot(data, label='data')\n", - "decorate(xlabel='Time (years)',\n", - " ylabel='Height (feet)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To find the parameters that best fit the data, I'll use `fit_leastsq`.\n", - "\n", - "We need an error function that takes parameters and returns errors:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "def error_func(params, data, update_func):\n", - " \"\"\"Runs the model and returns errors.\n", - " \n", - " params: sequence of alpha, dim\n", - " data: Series\n", - " update_func: function object\n", - " \n", - " returns: Series of errors\n", - " \"\"\"\n", - " print(params)\n", - " system = make_system(params, data)\n", - " results = run_simulation(system, update_func)\n", - " return (results - data).dropna()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we use it:" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(7, 2.8)\n" - ] - }, - { - "data": { - "text/plain": [ - "2 0.000000\n", - "3 1.148852\n", - "4 2.494726\n", - "5 3.987474\n", - "6 5.596116\n", - "8 9.023410\n", - "10 12.588184\n", - "15 21.692189\n", - "20 32.897280\n", - "25 47.207627\n", - "30 64.370278\n", - "35 84.203833\n", - "40 106.551950\n", - "45 131.281973\n", - "50 158.242758\n", - "55 187.291743\n", - "60 218.296906\n", - "65 251.121491\n", - "70 285.640417\n", - "dtype: float64" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "errors = error_func(params, data, update)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can pass `error_func` to `fit_leastsq`, which finds the parameters that minimize the squares of the errors." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[7. 2.8]\n", - "[7. 2.8]\n", - "[7. 2.8]\n", - "[7.0000001 2.8 ]\n", - "[7. 2.80000004]\n", - "[6.92221276 2.90250933]\n", - "[6.92221286 2.90250933]\n", - "[6.92221276 2.90250937]\n", - "[9.09216731 3.04344997]\n", - "[9.09216745 3.04344997]\n", - "[9.09216731 3.04345002]\n", - "[11.32004198 3.11949616]\n", - "[11.32004214 3.11949616]\n", - "[11.32004198 3.11949621]\n", - "[11.45136365 3.11757872]\n", - "[11.45136382 3.11757872]\n", - "[11.45136365 3.11757877]\n", - "[11.46621698 3.11792121]\n", - "[11.46621715 3.11792121]\n", - "[11.46621698 3.11792126]\n", - "[11.46393831 3.11787315]\n", - "[11.46393848 3.11787315]\n", - "[11.46393831 3.1178732 ]\n", - "[11.46425733 3.11787985]\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
fvec[0.0, 1.259430004271867, 2.5891995571210504, 3...
nfev22
fjac[[-1211.616390427339, -0.0007452893651874872, ...
ipvt[2, 1]
qtf[4.18653877720665e-05, 0.00060508849225549]
cov_x[[0.27796549579880336, 0.005806517068508905], ...
mesgBoth actual and predicted relative reductions ...
ier1
\n", - "
" - ], - "text/plain": [ - "fvec [0.0, 1.259430004271867, 2.5891995571210504, 3...\n", - "nfev 22\n", - "fjac [[-1211.616390427339, -0.0007452893651874872, ...\n", - "ipvt [2, 1]\n", - "qtf [4.18653877720665e-05, 0.00060508849225549]\n", - "cov_x [[0.27796549579880336, 0.005806517068508905], ...\n", - "mesg Both actual and predicted relative reductions ...\n", - "ier 1\n", - "dtype: object" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "best_params, details = fit_leastsq(error_func, params, data, update)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Using the best parameters we found, we can run the model and plot the results." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "system = make_system(best_params, data)\n", - "results = run_simulation(system, update)\n", - "\n", - "plot_results(results, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The mean absolute error is better than for Model 1, but that doesn't mean much. The model still doesn't fit the data well." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "3.618490489889672" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "mean_abs_error(results, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And the estimated fractal dimension is 3.11, which doesn't seem likely.\n", - "\n", - "Let's try one more thing." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Model 3" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Models 1 and 2 imply that trees can grow forever, but we know that's not true. As trees get taller, it gets harder for them to move water and nutrients against the force of gravity, and their growth slows.\n", - "\n", - "We can model this effect by adding a term to the model similar to what we saw in the logistic model of population growth. Instead of assuming:\n", - "\n", - "$ m_{n+1} = m_n + \\alpha A $ \n", - "\n", - "Let's assume\n", - "\n", - "$ m_{n+1} = m_n + \\alpha A (1 - h / K) $\n", - "\n", - "where $K$ is similar to the carrying capacity of the logistic model. As $h$ approaches $K$, the factor $(1 - h/K)$ goes to 0, causing growth to level off.\n", - "\n", - "Here's what the implementation of this model looks like:" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[2.0, 2.5, 150]" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "alpha = 2.0\n", - "dim = 2.5\n", - "K = 150\n", - "\n", - "params = [alpha, dim, K]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's an updated version of `make_system`" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(params, data):\n", - " \"\"\"Makes a System object.\n", - " \n", - " params: sequence of alpha, dim, K\n", - " data: Series\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " alpha, dim, K = params\n", - " \n", - " t_0 = get_first_label(data)\n", - " t_end = get_last_label(data)\n", - "\n", - " h_0 = get_first_value(data)\n", - "\n", - " return System(alpha=alpha, dim=dim, K=K, \n", - " h_0=h_0, t_0=t_0, t_end=t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the new `System` object." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
alpha2.0
dim2.5
K150.0
h_01.4
t_02.0
t_end70.0
\n", - "
" - ], - "text/plain": [ - "alpha 2.0\n", - "dim 2.5\n", - "K 150.0\n", - "h_0 1.4\n", - "t_0 2.0\n", - "t_end 70.0\n", - "dtype: float64" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(params, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And here's the new update function." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [], - "source": [ - "def update3(height, t, system):\n", - " \"\"\"Update height based on geometric model with growth limiting term.\n", - " \n", - " height: current height in feet\n", - " t: what year it is\n", - " system: system object with model parameters\n", - " \"\"\"\n", - " area = height**2\n", - " mass = height**system.dim\n", - " mass += system.alpha * area * (1 - height/system.K)\n", - " return mass**(1/system.dim)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As always, we'll test the update function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2.075043351397076" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "update3(h_0, t_0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can test the error function with the new update function." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[2.0, 2.5, 150]\n" - ] - }, - { - "data": { - "text/plain": [ - "2 0.000000\n", - "3 0.515043\n", - "4 0.920001\n", - "5 1.216367\n", - "6 1.431972\n", - "8 1.685640\n", - "10 1.801190\n", - "15 1.975039\n", - "20 3.929748\n", - "25 7.701256\n", - "30 11.775782\n", - "35 15.027748\n", - "40 16.865948\n", - "45 17.173291\n", - "50 16.106566\n", - "55 13.967791\n", - "60 11.095254\n", - "65 7.785693\n", - "70 4.284147\n", - "dtype: float64" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "error_func(params, data, update3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And search for the best parameters." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[ 2. 2.5 150. ]\n", - "[ 2. 2.5 150. ]\n", - "[ 2. 2.5 150. ]\n", - "[ 2.00000003 2.5 150. ]\n", - "[ 2. 2.50000004 150. ]\n", - "[ 2. 2.5 150.00000224]\n", - "[ 2.57150998 2.62640233 152.85619548]\n", - "[ 2.57151002 2.62640233 152.85619548]\n", - "[ 2.57150998 2.62640237 152.85619548]\n", - "[ 2.57150998 2.62640233 152.85619775]\n", - "[ 2.69480841 2.64180464 164.38502718]\n", - "[ 2.69480845 2.64180464 164.38502718]\n", - "[ 2.69480841 2.64180467 164.38502718]\n", - "[ 2.69480841 2.64180464 164.38502963]\n", - "[ 2.69504971 2.64141865 165.06670862]\n", - "[ 2.69504975 2.64141865 165.06670862]\n", - "[ 2.69504971 2.64141869 165.06670862]\n", - "[ 2.69504971 2.64141865 165.06671108]\n", - "[ 2.69600956 2.64152837 165.08460657]\n", - "[ 2.6960096 2.64152837 165.08460657]\n", - "[ 2.69600956 2.64152841 165.08460657]\n", - "[ 2.69600956 2.64152837 165.08460903]\n", - "[ 2.69593577 2.64152036 165.08358693]\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
fvec[0.0, 0.6026560697086136, 1.1051308784106184, ...
nfev21
fjac[[-935.0854735477023, -0.0005819515032659498, ...
ipvt[2, 1, 3]
qtf[-1.8155922127449653e-05, -0.00029942939542415...
cov_x[[0.034808802477415246, 0.003865949037799679, ...
mesgBoth actual and predicted relative reductions ...
ier1
\n", - "
" - ], - "text/plain": [ - "fvec [0.0, 0.6026560697086136, 1.1051308784106184, ...\n", - "nfev 21\n", - "fjac [[-935.0854735477023, -0.0005819515032659498, ...\n", - "ipvt [2, 1, 3]\n", - "qtf [-1.8155922127449653e-05, -0.00029942939542415...\n", - "cov_x [[0.034808802477415246, 0.003865949037799679, ...\n", - "mesg Both actual and predicted relative reductions ...\n", - "ier 1\n", - "dtype: object" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "best_params, details = fit_leastsq(error_func, params, data, update3)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With these parameters, we can fit the data much better." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzs3Xd8XNWZ+P/PzEiaUe/Vci+P3LsxHQyElkJZfrCBACFkA0k22Sy7G76EJISSZJPdhA0Q2BJCCNkQkgVCi4FQjAEDtnFDto+bLEuy1btGGk25vz/uSB4Ly5aNpJmRnvfrpZd0z70z81x7NI/Ouc89x2FZFkoppVSscUY7AKWUUupoNEEppZSKSZqglFJKxSRNUEoppWJSQrQDGCki4gaWA4eAYJTDUUopdXQuoBhYb4zxRe4YswkKOzmtjXYQSimlhuRM4O3IhrGcoA4B/O53v6OoqCjasSillDqK2tparr32Wgh/ZkcaywkqCFBUVERpaWm0Y1FKKXVsH7sUo0USSimlYpImKKWUUjFJE5RSSqmYNKrXoETk68CNwHzg98aYG49yzPeBu4ALjDF/Dbe5gYeBvwG8wE+MMT8bnaiVUkpFw2j3oA4C9wKPHm2niEzHTkIDqznuAmYCk4FzgX8RkYtGLkyllFLRNqoJyhjztDHmWaBpkEMeBL4N9A5ovx64xxjTYozZAfw3dk9MKaXUGBUz16BE5Cqg1xjz0oD2bKAE2BLRvAWYO4rhKaWUCrMsi7pmL29vqWHt5hr8gZGZrCcm7oMSkTTgh8CnjrI7Lfy9LaKtDUgf6biUUkod5u3xs/tAK9v3N9PU1o0VsnA4HSS7E1g2u3DYXy8mEhTwA+C3xpiKo+zrDH/PAHoifu4YjcCUUmo8C4Usquo62L6/mYqDbYRCFu3t7Xi9XoLBIKUTJpCb6RmR146VIb7zgG+ISK2I1AITgadE5NvGmBbsoomFEccvBMqjEGfcuv322/n5z38+pGNXrVrFu+++O8IRKaViWWuHj3XbDvH4S9t5/u197K1uJRSyV2Dv7u4m4PeRmxLkU8uLmFqSOSIxjHaZeUL4NV2AS0Q8QAA7QSVGHLoe+EfgL+Htx4E7RWQDUAh8GfjiaMWtlFLjgT8QZG9NG9v3NVFR04zX68Xj9pCcktx/TFFuKlJcQmdjBYkJTjwu/4jFM9pDfHcC34/Yvg74gTHmrsiDRCQItBhj+ob3vo99H1Ql0A38qzFm9ciHq5RSY1tfwcOO/c3srmql1x+ko72D5pZmAAIpAXKy0ymbksPsKTnkZHjo6emhqSmHgoICEhMTj/MKJ29UE1Q4Ed01hOOmDNj2ATeFv8a0VatW8fnPf54///nPVFVVcckll/CP//iP3H777WzcuJGFCxfyH//xH2RmZvLaa6/xs5/9jLq6OmbPns1dd93F9OnTAdi+fTvf+c532L9/P2effTYOh+OI13njjTe4//77qampYcaMGdx1112UlZVF45SVUlHg7fGzs7KZbbvr6eg+sgovOSUZRyvkpDoozvJxzUWCO+lwIvJ4PEyYMGHEY4yVIomoM8awa9cuAGbNmoWIHLG/vLycffv2ATBnzpz+RNBn69atVFZWArBgwQImT558xP7Ozk7S0tIYildeeYVf//rXBINBLrvsMnbs2MF9993HjBkzuPnmm/ntb3/LpZdeym233cZDDz3EihUreOyxx7jlllt48cUXAfja177GDTfcwLXXXstrr73Gbbfdxs0339x/LnfccQePPPII8+bN47nnnuOrX/0qq1evJikp6QT/5ZRS8SIUsqisbWd7RRNbdlTR2dVlFzqUlvb/EZuV7mbOlBLaS3vJzkiluLiYxARXVOLVBBWDrrvuOvLy8gBYtmwZOTk5zJkzB4ALLriAdevW4XA4OPvsszn99NMB+NKXvsTjjz/Opk2bcDqd+P1+brjhBhwOBxdddBGPPfZY//M/9dRTXH311SxcaNedXH755TzyyCNs3ryZFStWjO7JKqVGXEtHDzv3N7NzfwtdPfY1o86uLvx+++dAr4+FZSXMnpJLUW6KnazKCqIZMqAJKib1JScAt9t9xLbH48Hr9VJfX09JSUl/u9PppLi4mLq6OlwuF4WFhUcM60Uee/DgQZ599lmeeOKJ/ja/3099ff1InZJSapQFgiF2VjTywbYDtHktEpOOvFaUkpKC1dvOhBwXZ64oYOb0SVGKdHCaoMJE5GPDepHmzp3L3LmDT16xYMECFixYMOj+oQ7vDVVBQUH/kCTYFzoPHTrUn5jq6uqwLKs/SR08eJCJEycCUFxczC233MKtt946rDEppaKv09vLtr1NrNtcQV19EyErRHp6Ojk5OQCkeBIpm5zNlKLJOII9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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "system = make_system(best_params, data)\n", - "results = run_simulation(system, update3)\n", - "\n", - "plot_results(results, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And the mean absolute error is substantually smaller." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.8895329463347671" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "mean_abs_error(results, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The estimated fractal dimension is about 2.6, which is plausible for a tree.\n", - "\n", - "Basically, it suggests that if you double the height of the tree, the mass grows by a factor of $2^{2.6}$" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "6.062866266041593" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "2**2.6" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In other words, the mass of the tree scales faster than area, but not as fast as it would for a solid 3-D object.\n", - "\n", - "What is this model good for?\n", - "\n", - "1) It offers a possible explanation for the shape of tree growth curves.\n", - "\n", - "2) It provides a way to estimate the fractal dimension of a tree based on a growth curve (probably with different values for different species).\n", - "\n", - "3) It might provide a way to predict future growth of a tree, based on measurements of past growth. As with the logistic population model, this would probably only work if we have observed the part of the curve where the growth rate starts to decline." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Analysis\n", - "\n", - "With some help from my colleague, John Geddes, we can do some analysis.\n", - "\n", - "Starting with the difference equation in terms of mass:\n", - " \n", - "$m_{n+1} = m_n + \\alpha A (1 - h / K) $\n", - "\n", - "We can write the corresponding differential equation:\n", - "\n", - "(1) $ \\frac{dm}{dt} = \\alpha A (1 - h / K) $\n", - "\n", - "With\n", - "\n", - "(2) $A = h^2$\n", - "\n", - "and\n", - "\n", - "(3) $m = h^D$\n", - "\n", - "Taking the derivative of the last equation yields\n", - "\n", - "(4) $\\frac{dm}{dt} = D h^{D-1} \\frac{dh}{dt}$\n", - "\n", - "Combining (1), (2), and (4), we can write a differential equation for $h$:\n", - "\n", - "(5) $\\frac{dh}{dt} = \\frac{\\alpha}{D} h^{3-D} (1 - h/K)$\n", - "\n", - "Now let's consider two cases:\n", - "\n", - "* With infinite $K$, the factor $(1 - h/K)$ approaches 1, so we have Model 2.\n", - "* With finite $K$, we have Model 3.\n", - "\n", - "#### Model 2\n", - "\n", - "Within Model 2, we'll consider two special cases, with $D=2$ and $D=3$.\n", - "\n", - "With $D=2$, we have\n", - "\n", - "$\\frac{dh}{dt} = \\frac{\\alpha}{2} h$\n", - "\n", - "which yields exponential growth with parameter $\\alpha/2$.\n", - "\n", - "With $D=3$, we have Model 1, with this equation:\n", - "\n", - "$\\frac{dh}{dt} = \\frac{\\alpha}{3}$\n", - "\n", - "which yields linear growth with parameter $\\alpha/3$.\n", - "\n", - "This result explains why Model 1 is linear.\n", - "\n", - "\n", - "\n", - "#### Model 3\n", - "\n", - "Within Model 3, we'll consider two special cases, with $D=2$ and $D=3$.\n", - "\n", - "With $D=2$, we have\n", - "\n", - "$\\frac{dh}{dt} = \\frac{\\alpha}{2} h (1 - h/K)$\n", - "\n", - "which yields logisitic growth with parameters $r = \\alpha/2$ and $K$.\n", - "\n", - "With $D=3$, we have\n", - "\n", - "$\\frac{dh}{dt} = \\frac{\\alpha}{3} (1 - h/K)$\n", - "\n", - "which yields a first order step response; that is, it converges to $K$ like a negative exponential:\n", - "\n", - "$ h(t) = c \\exp(-\\frac{\\alpha}{3K} t) + K $\n", - "\n", - "where $c$ is a constant that depends on the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "metadata": {}, - "outputs": [], - "source": [ - "alpha = 10\n", - "D = 3\n", - "K = 200\n", - "params = alpha, D, K\n", - "system = make_system(params, data)\n", - "results = run_simulation(system, update3);" - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "t = results.index\n", - "a = alpha/3\n", - "h = (-220) * exp(-a * t / K) + K\n", - "plot(t, h)\n", - "plot(results)\n", - "decorate(xlabel='Time (years)',\n", - " ylabel='Height (feet)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Additional resources:\n", - "\n", - "Garcia, [A stochastic differential equation model for the\n", - "height growth of forest stands](http://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=664FED1E46ABCBF6E16741C294B79976?doi=10.1.1.608.81&rep=rep1&type=pdf)\n", - "\n", - "[EasySDE software and data](http://forestgrowth.unbc.ca/)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/wall_soln.ipynb b/code/soln/wall_soln.ipynb deleted file mode 100644 index 4059dc559..000000000 --- a/code/soln/wall_soln.ipynb +++ /dev/null @@ -1,1998 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "\n", - "Copyright 2018 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Inferring thermal resistance and thermal mass of a wall\n", - "\n", - "This case study is based on Gori, Marincioni, Biddulph, Elwell, \"Inferring the thermal resistance and effective thermal mass distribution of a wall from in situ measurements to characterise heat transfer at both the interior and exterior surfaces\", *Energy and Buildings*, Volume 135, 15 January 2017, Pages 398-409, [which I downloaded here](https://www.sciencedirect.com/science/article/pii/S0378778816313056).\n", - " \n", - "The authors put their paper under a Creative Commons license, and [make their data available here](http://discovery.ucl.ac.uk/1526521). I thank them for their commitment to open, reproducible science, which made this case study possible.\n", - "\n", - "The goal of their paper is to model the thermal behavior of a wall as a step toward understanding the \"performance gap between the expected energy use of buildings and their measured energy use\". The wall they study is identified as the exterior wall of an office building in central London, [not unlike this one](https://www.google.com/maps/@51.5269375,-0.1303666,3a,75y,90h,88.17t/data=!3m6!1e1!3m4!1sAoAXzN0mbGF9acaVEgUdDA!2e0!7i13312!8i6656).\n", - "\n", - "The following figure shows the scenario and their model:\n", - "\n", - "![Figure 2](https://ars.els-cdn.com/content/image/1-s2.0-S0378778816313056-gr2.jpg)\n", - "\n", - "On the interior and exterior surfaces of the wall, they measure temperature and heat flux over a period of three days. They model the wall using two thermal masses connected to the surfaces, and to each other, by thermal resistors.\n", - "\n", - "The primary methodology of the paper is a Bayesian method for inferring the parameters of the system (two thermal masses and three thermal resistances).\n", - "\n", - "The primary result is a comparison of two models: the one shown here with two thermal masses, and a simpler model with only one thermal mass. They find that the two-mass model is able to reproduce the measured fluxes substantially better.\n", - "\n", - "Tempting as it is, I will not replicate their method for estimating the parameters. Rather, I will\n", - "\n", - "1. Implement their model and run it with their estimated parameters, to replicate the results, and \n", - "\n", - "2. Use SciPy's `leastsq` to see if I can find parameters that yield lower errors (root mean square).\n", - "\n", - "`leastsq` is a wrapper for some venerable FORTRAN code that runs [\"a modification of the Levenberg-Marquardt algorithm\"](https://www.math.utah.edu/software/minpack/minpack/lmdif.html), which is one of my favorites ([really](http://allendowney.com/research/model)).\n", - "\n", - "Implementing their model in the ModSimPy framework turns out to be straightforward. The simulations run fast enough even when we carry units through the computation. And the results are visually similar to the ones in the original paper.\n", - "\n", - "I find that `leastsq` is not able to find parameters that yield substantially better results, which suggest that the estimates in the paper are at least locally optimal.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Loading the data\n", - "\n", - "First I'll load the units we need from Pint." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "degC" - ], - "text/latex": [ - "$degC$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "m = UNITS.meter\n", - "K = UNITS.kelvin\n", - "W = UNITS.watt\n", - "J = UNITS.joule\n", - "degC = UNITS.celsius" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Read the data." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Q_inQ_outT_intT_ext
2014-10-05 16:30:0010.9946.84016.9214.68
2014-10-05 16:35:0010.9526.01216.9214.69
2014-10-05 16:40:0010.8827.04016.9314.66
2014-10-05 16:45:0010.7988.88016.9314.59
2014-10-05 16:50:0010.75610.49116.9414.50
\n", - "
" - ], - "text/plain": [ - " Q_in Q_out T_int T_ext\n", - "2014-10-05 16:30:00 10.994 6.840 16.92 14.68\n", - "2014-10-05 16:35:00 10.952 6.012 16.92 14.69\n", - "2014-10-05 16:40:00 10.882 7.040 16.93 14.66\n", - "2014-10-05 16:45:00 10.798 8.880 16.93 14.59\n", - "2014-10-05 16:50:00 10.756 10.491 16.94 14.50" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "data = pd.read_csv('data/DataOWall.csv', parse_dates=[0], index_col=0, header=0, skiprows=[1,2])\n", - "data.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The index contains Pandas `Timestamp` objects, which is good for dealing with real-world dates and times, but not as good for running the simulations, so I'm going to convert to seconds." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Timestamp('2014-10-05 16:30:00')" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "timestamp_0 = get_first_label(data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Subtracting the first `Timestamp` yields `Timedelta` objects:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "TimedeltaIndex(['0 days 00:00:00', '0 days 00:05:00', '0 days 00:10:00',\n", - " '0 days 00:15:00', '0 days 00:20:00', '0 days 00:25:00',\n", - " '0 days 00:30:00', '0 days 00:35:00', '0 days 00:40:00',\n", - " '0 days 00:45:00',\n", - " ...\n", - " '2 days 23:10:00', '2 days 23:15:00', '2 days 23:20:00',\n", - " '2 days 23:25:00', '2 days 23:30:00', '2 days 23:35:00',\n", - " '2 days 23:40:00', '2 days 23:45:00', '2 days 23:50:00',\n", - " '2 days 23:55:00'],\n", - " dtype='timedelta64[ns]', length=864, freq=None)" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "time_deltas = data.index - timestamp_0" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then we can convert to seconds and replace the index." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Q_inQ_outT_intT_ext
010.9946.84016.9214.68
30010.9526.01216.9214.69
60010.8827.04016.9314.66
90010.7988.88016.9314.59
120010.75610.49116.9414.50
\n", - "
" - ], - "text/plain": [ - " Q_in Q_out T_int T_ext\n", - "0 10.994 6.840 16.92 14.68\n", - "300 10.952 6.012 16.92 14.69\n", - "600 10.882 7.040 16.93 14.66\n", - "900 10.798 8.880 16.93 14.59\n", - "1200 10.756 10.491 16.94 14.50" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "data.index = time_deltas.days * 86400 + time_deltas.seconds\n", - "data.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The timesteps are all 5 minutes:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.all(np.diff(data.index) == 300)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mark the columns of the `Dataframe` with units." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "data.Q_in.units = W / m**2\n", - "data.Q_out.units = W / m**2\n", - "data.T_int.units = degC\n", - "data.T_ext.units = degC" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot the measured fluxes." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(data.Q_in, color='C2')\n", - "plot(data.Q_out, color='C0')\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Heat flux (W/$m^2$)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot the measured temperatures." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot(data.T_int, color='C2')\n", - "plot(data.T_ext, color='C0')\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Temperature (degC)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Params and System objects\n", - "\n", - "Here's a `Params` object with the [estimated parameters from the paper](https://www.sciencedirect.com/science/article/pii/S0378778816313056#tbl0005)." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
R10.076 kelvin * meter ** 2 / watt
R20.272 kelvin * meter ** 2 / watt
R30.078 kelvin * meter ** 2 / watt
C1212900.0 joule / kelvin / meter ** 2
C2113100.0 joule / kelvin / meter ** 2
\n", - "
" - ], - "text/plain": [ - "R1 0.076 kelvin * meter ** 2 / watt\n", - "R2 0.272 kelvin * meter ** 2 / watt\n", - "R3 0.078 kelvin * meter ** 2 / watt\n", - "C1 212900.0 joule / kelvin / meter ** 2\n", - "C2 113100.0 joule / kelvin / meter ** 2\n", - "dtype: object" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(\n", - " R1 = 0.076 * m**2 * K / W,\n", - " R2 = 0.272 * m**2 * K / W,\n", - " R3 = 0.078 * m**2 * K / W,\n", - " C1 = 212900 * J / m**2 / K,\n", - " C2 = 113100 * J / m**2 / K)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Pass the `Params` object `make_system`, which computes `init`, packs the parameters into `Series` objects, and computes the interpolation functions.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "def make_system(params, data):\n", - " \"\"\"Makes a System object for the given conditions.\n", - " \n", - " params: Params object\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " R1, R2, R3, C1, C2 = params\n", - " \n", - " init = State(T_C1 = Quantity(16.11, degC),\n", - " T_C2 = Quantity(15.27, degC))\n", - " \n", - " ts = data.index\n", - " t_end = ts[-1]\n", - " \n", - " return System(init=init,\n", - " R=Series([R1, R2, R3]),\n", - " C=Series([C1, C2]),\n", - " T_int_func=interpolate(data.T_int),\n", - " T_ext_func=interpolate(data.T_ext),\n", - " t_end=t_end, ts=ts)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make a `System` object" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
initT_C1 16.11 degC\n", - "T_C2 15.27 degC\n", - "dtype: o...
R0 0.076 kelvin * meter ** 2 / watt\n", - "1 0.2...
C0 212900.0 joule / kelvin / meter ** 2\n", - "1 ...
T_int_func<function interpolate.<locals>.<lambda> at 0x7...
T_ext_func<function interpolate.<locals>.<lambda> at 0x7...
t_end258900
tsInt64Index([ 0, 300, 600, 900, ...
\n", - "
" - ], - "text/plain": [ - "init T_C1 16.11 degC\n", - "T_C2 15.27 degC\n", - "dtype: o...\n", - "R 0 0.076 kelvin * meter ** 2 / watt\n", - "1 0.2...\n", - "C 0 212900.0 joule / kelvin / meter ** 2\n", - "1 ...\n", - "T_int_func . at 0x7...\n", - "T_ext_func . at 0x7...\n", - "t_end 258900\n", - "ts Int64Index([ 0, 300, 600, 900, ...\n", - "dtype: object" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(params, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test the interpolation function:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " ,\n", - " )" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.T_ext_func(0), system.T_ext_func(150), system.T_ext_func(300)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Implementing the model\n", - "\n", - "Next we need a slope function that takes instantaneous values of the two internal temperatures and computes their time rates of change.\n", - "\n", - "The slope function gets called two ways.\n", - "\n", - "* When we call it directly, `state` is a `State` object and the values it contains have units.\n", - "\n", - "* When `run_ode_solver` calls it, `state` is an array and the values it contains don't have units.\n", - "\n", - "In the second case, we have to apply the units before attempting the computation. `require_units` applies units if necessary:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function computes the fluxes between the four zones." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "def compute_flux(state, t, system):\n", - " \"\"\"Compute the fluxes between the walls surfaces and the internal masses.\n", - " \n", - " state: State with T_C1 and T_C2\n", - " t: time in seconds\n", - " system: System with interpolated measurements and the R Series\n", - " \n", - " returns: Series of fluxes\n", - " \"\"\"\n", - " # unpack the temperatures and apply units\n", - " T_C1, T_C2 = state\n", - " T_C1 = require_units(T_C1, degC)\n", - " T_C2 = require_units(T_C2, degC)\n", - " \n", - " # compute a series of temperatures from inside out\n", - " T_int = system.T_int_func(t)\n", - " T_ext = system.T_ext_func(t)\n", - " T = Series([T_int, T_C1, T_C2, T_ext])\n", - " \n", - " # compute differences of adjacent temperatures\n", - " T_diff = np.diff(T)\n", - "\n", - " # compute fluxes between adjacent compartments\n", - " Q = T_diff / system.R\n", - " return Q" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can test it like this." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0 -10.657894736842135 delta_degC * watt / kelvin...\n", - "1 -3.0882352941176463 delta_degC * watt / kelvin...\n", - "2 -7.564102564102562 delta_degC * watt / kelvin ...\n", - "dtype: object" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "compute_flux(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a slope function that computes derivatives of `T_C1` and `T_C2`" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Compute derivatives of the state.\n", - " \n", - " state: position, velocity\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: derivatives of y and v\n", - " \"\"\"\n", - " Q = compute_flux(state, t, system)\n", - "\n", - " # compute the net flux in each node\n", - " Q_diff = np.diff(Q)\n", - " \n", - " # compute the rate of change of temperature\n", - " dQdt = Q_diff / system.C\n", - " return dQdt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test the slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0 3.555499973097458e-05 delta_degC * watt / joule\n", - "1 -3.844086774341106e-05 delta_degC * watt / joule\n", - "dtype: object" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "slope_func(system.init, system.ts[1], system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Run the simulation, generating estimates for the time steps in the data." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[]
nfev254
njev0
nlu0
status0
messageThe solver successfully reached the end of the...
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events []\n", - "nfev 254\n", - "njev 0\n", - "nlu 0\n", - "status 0\n", - "message The solver successfully reached the end of the...\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results, details = run_ode_solver(system, slope_func, t_eval=system.ts)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the results look like." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
T_C1T_C2
016.11000015.270000
30016.12051615.258636
60016.13082015.247580
90016.14099215.235209
120016.15097515.220746
\n", - "
" - ], - "text/plain": [ - " T_C1 T_C2\n", - "0 16.110000 15.270000\n", - "300 16.120516 15.258636\n", - "600 16.130820 15.247580\n", - "900 16.140992 15.235209\n", - "1200 16.150975 15.220746" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_results(results, data):\n", - " plot(data.T_int, color='C2')\n", - " plot(results.T_C1, color='orange')\n", - " plot(results.T_C2, color='C1')\n", - " plot(data.T_ext, color='C0')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Temperature (degC)')\n", - " \n", - "plot_results(results, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "These results are similar to what's in the paper:\n", - "\n", - "![Figure 5](https://ars.els-cdn.com/content/image/1-s2.0-S0378778816313056-gr5.jpg). \n", - "\n", - "To get the estimated fluxes, we have to go through the results and basically do the flux calculation again." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Qm_inQm_out
010.65797.5641
30010.51957.2902
60010.51557.53308
90010.38178.2719
120010.38199.24034
\n", - "
" - ], - "text/plain": [ - " Qm_in Qm_out\n", - "0 10.6579 7.5641\n", - "300 10.5195 7.2902\n", - "600 10.5155 7.53308\n", - "900 10.3817 8.2719\n", - "1200 10.3819 9.24034" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "def recompute_fluxes(results, system):\n", - " \"\"\"Compute fluxes between wall surfaces and internal masses.\n", - " \n", - " results: Timeframe with T_C1 and T_C2\n", - " system: System object\n", - " \n", - " returns: Timeframe with Qm_in and Qm_out\n", - " \"\"\"\n", - " Q_frame = TimeFrame(index=results.index, columns=['Qm_in', 'Qm_out'])\n", - " \n", - " for t, row in results.iterrows():\n", - " Q = compute_flux(row, t, system)\n", - " \n", - " Q_frame.row[t] = (-Q[0].magnitude, \n", - " -Q[2].magnitude)\n", - " \n", - " return Q_frame\n", - " \n", - "Q_frame = recompute_fluxes(results, system)\n", - "Q_frame.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's see how the estimates compare to the data." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_Q_in(Q_frame, data):\n", - " plot(Q_frame.Qm_in, color='gray')\n", - " plot(data.Q_in, color='C2')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Heat flux (W/$m^2$)')\n", - " \n", - "plot_Q_in(Q_frame, data)" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_Q_out(Q_frame, data):\n", - " plot(Q_frame.Qm_out, color='gray')\n", - " plot(data.Q_out, color='C0')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Heat flux (W/$m^2$)')\n", - " \n", - "plot_Q_out(Q_frame, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "These results are also similar to what's in the paper (the bottom row):\n", - "\n", - "![Figure 3](https://ars.els-cdn.com/content/image/1-s2.0-S0378778816313056-gr3.jpg)\n", - "\n", - "I'll compute the array of errors, including `Q_in` and `Q_out`:" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "def compute_error(Q_frame, data):\n", - " error_Q_in = (Q_frame.Qm_in - data.Q_in).astype(float)\n", - " error_Q_out = (Q_frame.Qm_out - data.Q_out).astype(float)\n", - " return np.hstack([error_Q_in, error_Q_out])" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-0.33610526, -0.43247718, -0.36646728, ..., 3.45293108,\n", - " 1.58928364, 3.71097458])" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "errors = compute_error(Q_frame, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the root mean squared error." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.8330081709528363\n" - ] - } - ], - "source": [ - "print(np.sqrt(np.mean(errors**2)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Estimating parameters" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's see if we can do any better than the parameters in the paper." - ] - }, - { - "cell_type": "code", - "execution_count": 125, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
R10.076 kelvin * meter ** 2 / watt
R20.272 kelvin * meter ** 2 / watt
R30.078 kelvin * meter ** 2 / watt
C1212900.0 joule / kelvin / meter ** 2
C2113100.0 joule / kelvin / meter ** 2
\n", - "
" - ], - "text/plain": [ - "R1 0.076 kelvin * meter ** 2 / watt\n", - "R2 0.272 kelvin * meter ** 2 / watt\n", - "R3 0.078 kelvin * meter ** 2 / watt\n", - "C1 212900.0 joule / kelvin / meter ** 2\n", - "C2 113100.0 joule / kelvin / meter ** 2\n", - "dtype: object" - ] - }, - "execution_count": 125, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(\n", - " R1 = 0.076 * m**2 * K / W,\n", - " R2 = 0.272 * m**2 * K / W,\n", - " R3 = 0.078 * m**2 * K / W,\n", - " C1 = 212900 * J / m**2 / K,\n", - " C2 = 113100 * J / m**2 / K)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's an error function that takes a hypothetical set of parameters, runs the simulation, and returns an array of errors." - ] - }, - { - "cell_type": "code", - "execution_count": 126, - "metadata": {}, - "outputs": [], - "source": [ - "def error_func(params, data):\n", - " \"\"\"Run a simulation and return an array of errors.\n", - " \n", - " params: Params object or array\n", - " data: DataFrame\n", - " \n", - " returns: array of float\n", - " \"\"\"\n", - " print(params)\n", - " system = make_system(params, data)\n", - " results, details = run_ode_solver(system, slope_func, t_eval=system.ts)\n", - " Q_frame = recompute_fluxes(results, system)\n", - " errors = compute_error(Q_frame, data)\n", - " print('RMSE', np.sqrt(np.mean(errors**2)))\n", - " return errors" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `error_func`." - ] - }, - { - "cell_type": "code", - "execution_count": 127, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "R1 0.076 kelvin * meter ** 2 / watt\n", - "R2 0.272 kelvin * meter ** 2 / watt\n", - "R3 0.078 kelvin * meter ** 2 / watt\n", - "C1 212900.0 joule / kelvin / meter ** 2\n", - "C2 113100.0 joule / kelvin / meter ** 2\n", - "dtype: object\n", - "RMSE 1.8330081709528363\n" - ] - }, - { - "data": { - "text/plain": [ - "array([-0.33610526, -0.43247718, -0.36646728, ..., 3.45293108,\n", - " 1.58928364, 3.71097458])" - ] - }, - "execution_count": 127, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "errors = error_func(params, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Pass `error_func` to `fit_leastsq` to see if it can do any better." - ] - }, - { - "cell_type": "code", - "execution_count": 124, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[7.600e-02 2.720e-01 7.800e-02 2.129e+05 1.131e+05]\n", - "RMSE 1.8505924115934524\n", - "[7.600e-02 2.720e-01 7.800e-02 2.129e+05 1.131e+05]\n", - "RMSE 1.8505924115934524\n", - "[7.600e-02 2.720e-01 7.800e-02 2.129e+05 1.131e+05]\n", - "RMSE 1.8505924115934524\n", - "[7.60000011e-02 2.72000000e-01 7.80000000e-02 2.12900000e+05\n", - " 1.13100000e+05]\n", - "RMSE 1.8555753453523043\n", - "[7.60000000e-02 2.72000004e-01 7.80000000e-02 2.12900000e+05\n", - " 1.13100000e+05]\n", - "RMSE 1.8432262123392518\n", - "[7.60000000e-02 2.72000000e-01 7.80000012e-02 2.12900000e+05\n", - " 1.13100000e+05]\n", - "RMSE 1.901052298632084\n", - "[7.60000000e-02 2.72000000e-01 7.80000000e-02 2.12900003e+05\n", - " 1.13100000e+05]\n", - "RMSE 1.9141948565632672\n", - "[7.60000000e-02 2.72000000e-01 7.80000000e-02 2.12900000e+05\n", - " 1.13100002e+05]\n", - "RMSE 1.9254240170566361\n", - "[7.60000006e-02 2.72000019e-01 7.79999992e-02 2.12899980e+05\n", - " 1.13100005e+05]\n", - "RMSE 1.846084441287066\n", - "[7.60000017e-02 2.72000019e-01 7.79999992e-02 2.12899980e+05\n", - " 1.13100005e+05]\n", - "RMSE 1.8221067418257664\n", - "[7.60000006e-02 2.72000023e-01 7.79999992e-02 2.12899980e+05\n", - " 1.13100005e+05]\n", - "RMSE 1.8502383692024875\n", - "[7.60000006e-02 2.72000019e-01 7.80000003e-02 2.12899980e+05\n", - " 1.13100005e+05]\n", - "RMSE 1.8005638751695812\n", - "[7.60000006e-02 2.72000019e-01 7.79999992e-02 2.12899983e+05\n", - " 1.13100005e+05]\n", - "RMSE 1.821050271762518\n", - "[7.60000006e-02 2.72000019e-01 7.79999992e-02 2.12899980e+05\n", - " 1.13100007e+05]\n", - "RMSE 1.8088000228677374\n", - "[7.59999998e-02 2.72000016e-01 7.80000007e-02 2.12899981e+05\n", - " 1.13100010e+05]\n", - "RMSE 1.8361042667244358\n" - ] - } - ], - "source": [ - "best_params, details = fit_leastsq(error_func, params, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The search ends normally." - ] - }, - { - "cell_type": "code", - "execution_count": 123, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
fvec[-0.3361052367318642, -0.6843824494040387, -0....
nfev13
fjac[[-2310206065.5804176, -1112552470.6892738, -2...
ipvt[3, 1, 2, 5, 4]
qtf[4.031779346939402, -0.13667226215662664, 1.26...
cov_x[[6.353204439137648e-17, 1.4331161896476686e-1...
mesgThe relative error between two consecutive ite...
ier2
\n", - "
" - ], - "text/plain": [ - "fvec [-0.3361052367318642, -0.6843824494040387, -0....\n", - "nfev 13\n", - "fjac [[-2310206065.5804176, -1112552470.6892738, -2...\n", - "ipvt [3, 1, 2, 5, 4]\n", - "qtf [4.031779346939402, -0.13667226215662664, 1.26...\n", - "cov_x [[6.353204439137648e-17, 1.4331161896476686e-1...\n", - "mesg The relative error between two consecutive ite...\n", - "ier 2\n", - "dtype: object" - ] - }, - "execution_count": 123, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "details" - ] - }, - { - "cell_type": "code", - "execution_count": 119, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'The relative error between two consecutive iterates is at most 0.000000'" - ] - }, - "execution_count": 119, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "details.mesg" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The best params are only slightly different from the starting place. " - ] - }, - { - "cell_type": "code", - "execution_count": 120, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
R10.076000
R20.272000
R30.078000
C1212899.990310
C2113100.012281
\n", - "
" - ], - "text/plain": [ - "R1 0.076000\n", - "R2 0.272000\n", - "R3 0.078000\n", - "C1 212899.990310\n", - "C2 113100.012281\n", - "dtype: float64" - ] - }, - "execution_count": 120, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "best_params" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the results look like with the `best_params`." - ] - }, - { - "cell_type": "code", - "execution_count": 121, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.7908027900352392\n" - ] - } - ], - "source": [ - "system = make_system(best_params, data)\n", - "results, details = run_ode_solver(system, slope_func, t_eval=system.ts)\n", - "Q_frame = recompute_fluxes(results, system)\n", - "errors = compute_error(Q_frame, data)\n", - "print(np.sqrt(np.mean(errors**2)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The RMS error is only slightly smaller.\n", - "\n", - "And the results are visually similar." - ] - }, - { - "cell_type": "code", - "execution_count": 85, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_Q_in(Q_frame, data)" - ] - }, - { - "cell_type": "code", - "execution_count": 86, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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v0x7ezstLS089dRTcefX1dWxZ8+ejLRFCJEd9g8T4SRdqDSSomeVBKgCsnTpUlasWMH999/P2WefzQUXXEBXVxdPPPEEpaWZG15ZvXo1+/btY+HChdx222189atfZcGCBQBceeWVzJw5k8WLF3PjjTdy2WWXxZ178803s27dOubOnctjjz2WsTYJ75MeVGHy8hCfkax7NxoppU4G/hCdhl3Inn32WdauXcv69etpamrKd3PEGPHQM7913R4M+Lj5qjNy3BqRDuc9O/eMJs6eaeXZe37LR/zxoPti/7NVPeeemd33lr1793LBBRcATNVaf2zfJ5MkCti1116L3+9n+/btEqBE3kn/qTB5uQclAarAXXXVVcM6b86cOa7bv/e97zF37tyRNEmMVRKhCoftXiWfJJGDtiQhAWqM2r59e76bIITIk4CtllSyAJXvZMAySUIIkRGGdKEKxsyTa2JfJxvik2nmQgghcipgq/NUURpIeFy+J2ZKgBJCZES+38xEeooC8UUIF8yeSMDvHgryPUlCApQQQoxh5SUBlvzFaa778j1JQgKUEGJI8v2pWmSes1cVle/nihKghBBDoiZX5nPHTAAAC9NJREFUu26XwDX6SKojMSS5Lvl+55138uCDD2bt9UXhWTB7Iic1VOS7GSIH8v2hQwJUAZGS78ILSor8XLloOpcvmBq3XfpPhWEoye16e0NZa0c6ZKFuEtv1IX7z+wP09mXvJgX8JvP+pJE5qj7pcemUfL/66qtdz+3p6eGBBx7gxRdfBODSSy9l5cqVBINBNm7cyDPPPMP69etjxyul2Lx5M7/+9a/ZtGkThmHw9NNPM3/+fB599NEMXbkodM41Mse75EOSV100bzI//80fY1+nq+NET7aalBYJUEn89v2WrAYngN6+EL99vyVlgEqn5HuiALVu3Tp27NjBc889h2EYLFu2jEceeYSvfe1rSX/mDTfcwPbt22loaGDFihVDuzAx6skjp8Jx6kk1GIaBzzQ4eYJ7sVI3x7v6stiq1GSIL4mzZoxPuD4gUwJ+k7NmjE95XLol391s2rSJ5cuXU1tby7hx41i+fDnPP//8iNothPPD28wp4/LUEpGKaRrMmFzD9EnVQ3quNPuUuiy2KjXpQSUxR9Wn7NnkSrol390cOnQoLtt5U1MThw4dylpbxdhwojv+0/X80xvz1BKR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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plot_Q_out(Q_frame, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Try starting the model with a different set of parameters and see if it moves toward the parameters in the paper.\n", - "\n", - "I found that no matter where I start, `fit_leastsq` doesn't move far, which suggests that it is not able to optimize the parameters effectively." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Notes\n", - "\n", - "Notes on working with degC.\n", - "\n", - "Usually I construct a `Quantity` object by multiplying a number and a unit. With degC, that doesn't work; you get `OffsetUnitCalculusError: Ambiguous operation with offset unit (degC).`" - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "metadata": {}, - "outputs": [], - "source": [ - "#16.11 * C " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The problem seems to be that it doesn't know whether you want a temperature measurement or a temperature difference.\n", - "\n", - "You can create a temperature measurement like this." - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "16.11 degC" - ], - "text/latex": [ - "$16.11 degC$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "T = Quantity(16.11, degC)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you convert to Kelvin, it does the right thing." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "289.26 kelvin" - ], - "text/latex": [ - "$289.26 kelvin$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "T.to(K)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When you subtract temperatures, the results is a temperature difference, indicated by the units." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "16.11 delta_degC" - ], - "text/latex": [ - "$16.11 delta_degC$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "diff = T - 273.15 * K" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you convert a temperature difference to Kelvin, it does the right thing." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "16.11 kelvin" - ], - "text/latex": [ - "$16.11 kelvin$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "diff.to(K)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/soln/yoyo_soln.ipynb b/code/soln/yoyo_soln.ipynb deleted file mode 100644 index e4ff3a3b6..000000000 --- a/code/soln/yoyo_soln.ipynb +++ /dev/null @@ -1,1167 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Case study\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Yo-yo\n", - "\n", - "Suppose you are holding a yo-yo with a length of string wound around its axle, and you drop it while holding the end of the string stationary. As gravity accelerates the yo-yo downward, tension in the string exerts a force upward. Since this force acts on a point offset from the center of mass, it exerts a torque that causes the yo-yo to spin.\n", - "\n", - "![](diagrams/yoyo.png)\n", - "\n", - "This figure shows the forces on the yo-yo and the resulting torque. The outer shaded area shows the body of the yo-yo. The inner shaded area shows the rolled up string, the radius of which changes as the yo-yo unrolls.\n", - "\n", - "In this model, we can't figure out the linear and angular acceleration independently; we have to solve a system of equations:\n", - "\n", - "$\\sum F = m a $\n", - "\n", - "$\\sum \\tau = I \\alpha$\n", - "\n", - "where the summations indicate that we are adding up forces and torques.\n", - "\n", - "As in the previous examples, linear and angular velocity are related because of the way the string unrolls:\n", - "\n", - "$\\frac{dy}{dt} = -r \\frac{d \\theta}{dt} $\n", - "\n", - "In this example, the linear and angular accelerations have opposite sign. As the yo-yo rotates counter-clockwise, $\\theta$ increases and $y$, which is the length of the rolled part of the string, decreases.\n", - "\n", - "Taking the derivative of both sides yields a similar relationship between linear and angular acceleration:\n", - "\n", - "$\\frac{d^2 y}{dt^2} = -r \\frac{d^2 \\theta}{dt^2} $\n", - "\n", - "Which we can write more concisely:\n", - "\n", - "$ a = -r \\alpha $\n", - "\n", - "This relationship is not a general law of nature; it is specific to scenarios like this where there is rolling without stretching or slipping.\n", - "\n", - "Because of the way we've set up the problem, $y$ actually has two meanings: it represents the length of the rolled string and the height of the yo-yo, which decreases as the yo-yo falls. Similarly, $a$ represents acceleration in the length of the rolled string and the height of the yo-yo.\n", - "\n", - "We can compute the acceleration of the yo-yo by adding up the linear forces:\n", - "\n", - "$\\sum F = T - mg = ma $\n", - "\n", - "Where $T$ is positive because the tension force points up, and $mg$ is negative because gravity points down.\n", - "\n", - "Because gravity acts on the center of mass, it creates no torque, so the only torque is due to tension:\n", - "\n", - "$\\sum \\tau = T r = I \\alpha $\n", - "\n", - "Positive (upward) tension yields positive (counter-clockwise) angular acceleration.\n", - "\n", - "Now we have three equations in three unknowns, $T$, $a$, and $\\alpha$, with $I$, $m$, $g$, and $r$ as known parameters. It is simple enough to solve these equations by hand, but we can also get SymPy to do it for us.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "from sympy import init_printing, symbols, Eq, solve\n", - "\n", - "init_printing()" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "T, a, alpha, I, m, g, r = symbols('T a alpha I m g r')" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAE4AAAAJBAMAAABu2Qf6AAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIquJdjLdEETvu2aZVM0GsGrEAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAy0lEQVQYGWNgYFR2TWfAAWx8GhgYOIwqTBwYGDoFuD4AlTG/A4MDQCYciDWwT2S8wMz1lmsCA/tsBp4FcBlUxmQGhjUsBbFsChwBDDzfGdgcUKXZn6UBQV4AUIrhHDNDQX0DUJ5zAUN9AKo6GA8oxWBpzsBwHyRQbwCkC4DuA5mRlqYAEoMCoBSD/QWgmSB+/QWGdbwCMCkUmn8DUB1QBOhMBgY+B+4kZhRpOIdHgYH7PGsB4w+QCLuKqZAHXAqVYbx7A48JA0cCUBQAnD4uhckWRCkAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$a = - \\alpha r$$" - ], - "text/plain": [ - "a = -α⋅r" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "eq1 = Eq(a, -r * alpha)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAH4AAAASBAMAAABvO3eaAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIu+7q82J3ZlmRDJUdhDTAJzgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABn0lEQVQ4EaWRPUibQRjHf28S3yRvkmsKHS1pEewkCCqUQkmk4haSQUtBSm9ocRAxgx+TIIi4CM3WqfgOYse2kwgO0QZXA4pDQTiknTUtiLXx404hkHOR1/9wPPf87n/PPffg/Okt/B14JwkoVxIqESsHtBOBzBxxP6h/HZYrpFRQvy5cUHhB7cb37LZ5++AHq/2P92qru75NH42vKe/9w4O8N/7CMOfUPoHbrZOypybO/eQTQz8MG70xofeULG74JUc/2TKJeMOsLdpRNBKVKZwT4sctBA7LzLMRLZKRLBsWubg58XvGaMRsOkjUhTgjeUGk+4Y214+KWdSRjy7yymT1+C0l/pEqkTwmViJUs2AH4j/swCJ0GabHbyleJzxHrEhU8jWtNB29ftukjsSJvliwANN4daEzevyWdOUHFUIS/coFpxWLBuHiJp3o9mLFdu3MKsuOqFNIk/HN/7x2LfqWTznpnJmPbZMSt+9yyG6Rlf1R+AJ7UB2z/L/2v+d9r0QqR3LwmwWb2+fNKECQMM3dQxMqnLuHnernpbvarwAXjHFA09aCYwAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$T - g m = a m$$" - ], - "text/plain": [ - "T - g⋅m = a⋅m" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "eq2 = Eq(T - m * g, m * a)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAE0AAAAOBAMAAACY64xBAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIu+7q82J3ZlmRDJUdhDTAJzgAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABGUlEQVQoFW2QvUsDQRDF34V47CXmrrQMNtqKGLDTQi29FAp2WQQ/Gj2LgKVaWHutlVvFwiK2dvsnpLQR7j9QDIL4Fd8cZMmKD+7N+80MyzEIXpfS4VpH4x810reLcTvUqJxA5WP2atJ2WAWa54iMa0yGvnb0ANxYNArXmAyZccSUFqg59kLHo3mh3mbvyesKDMUOH40UBO/iumUXpe5ti3YkovZNmzHxbGAZoi9a3XZxx+op/CTOAcvl71d/SHH84a2UMPUCKE4vQ0Gej5pm76+SBU757R7LhOejVFscODgVdcvcHwDPGsisIM9HVbS4r+weSHLule2VQkrTiPs6I6pVRFf1AmFrtMXnceuvkIL10QbL/nWujvALuqBADvdlz34AAAAASUVORK5CYII=\n", - "text/latex": [ - "$$T r = I \\alpha$$" - ], - "text/plain": [ - "T⋅r = I⋅α" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "eq3 = Eq(T * r, I * alpha)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/latex": [ - "$$\\left \\{ T : \\frac{I g m}{I + m r^{2}}, \\quad a : - \\frac{g m r^{2}}{I + m r^{2}}, \\quad \\alpha : \\frac{g m r}{I + m r^{2}}\\right \\}$$" - ], - "text/plain": [ - "⎧ 2 ⎫\n", - "⎪ I⋅g⋅m -g⋅m⋅r g⋅m⋅r ⎪\n", - "⎨T: ────────, a: ────────, α: ────────⎬\n", - "⎪ 2 2 2⎪\n", - "⎩ I + m⋅r I + m⋅r I + m⋅r ⎭" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "soln = solve([eq1, eq2, eq3], [T, a, alpha])" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAEoAAAAsBAMAAAAwUzMFAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAdqvNmSJEibsy3VQQ72YW//XhAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAByUlEQVQ4Ec2UvS9DURjGn6L3q98G1oqhtjapzdAmYmFpJCaDxiIWik6WViImg91iM4mPhUSkXVjLxOovUOoWIa7zHveeS3OuaxLPcM573ueXc09y7nkAoUC6NSYW3kUs4+25zmLJrb2rStHbc52cW/5QPZN3nDhRlqcSA8rShhRV7llbrQYfVW0NjT6cSSn1hbVrdbxuGxnMlHAnpbqbrH0D3aw3ikQPSalYFdDfEdhBDRgHrqTUYhyImtDyGAV2oZhhGVbZAkJNxA5wjXAbPZlpGbXPmmET6UKwjWgT3SXJVQQHrRTDZvtXoewgkEQoxfb21LqnIwydTuSrSl1L+kI4up3wh/4RYf1Gf3ve8nrd/4NqoSvjTxkF3SSq1xoueNORIvvdSC1vhpzQG43KE41M559T52jEqaM6f9Rhp/+5LvOJv0Oq5FQ0zyl6h1w2JWJCWb5YimMO8+TSO+SyKRETqjaiXeojp5vk0jvksikRE9tGUtmKWFaTXBFbNiViggohHltYyWb3stlh6oqYYIUjHlt8Ye/lxgTLC0c8tr5RIiauHQYw+OFobe8lYsK+QLImcw8lmplsSsQEKyRyziWxvrQWvtTfyw/BApwYNLJRJwAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$\\frac{I g m}{I + m r^{2}}$$" - ], - "text/plain": [ - " I⋅g⋅m \n", - "────────\n", - " 2\n", - "I + m⋅r " - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "soln[T]" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAFwAAAAxBAMAAAClq9PvAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEM3dMlSJdrsime9mq0Tz+RmlAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACC0lEQVRIDdWVT2sTURTFTzqZMckkr8GdmyYE66KbhhbciJBN15mF0oWUDkWMFIoDIi2FlNSNUlHGhYgLSYhfoKt222/Quukf6GJAmZXQVLQ1Cxvfq3MhmclMXjelPZt7ePf3HvfNMGcACc2vZSUoQjQ7kycvURU73pbACNGd5Bl5qaoeS2EEKRY5qTovRRGUbpCTqi6+S3H/ofjow9VQXK28qLkHH6uWu+TA/eouQu90WqH4U9Qb5msrduqo6zBfGm9CSdHgj3j4gTGB5AnSrbhxf8DYeg3l97EzsS1Ti3ETrfIWnkNtIVWEbgkTrW0ba0jloZg4srPcRGsW+A3dRM5BM2lwE62cnWmj7mAbuKMJEy2296SIb0AVmHkszCAlGoOIrn5JTC0t7RhvpWH+Ug+XjQvg1wq9OSl0G+jI6Mpc7XJzuNoZs8OvHszhn+Gw6PhymJ14+K3+23w5rFFKTPfHfTk8RCnRH/fn8HDNO9XD2cKnxT1WeQe2MFKx4M/hI6sX1xKvkFvBI2iJ0cR6IIeflXrxKSWPuskjZErZYaVADo97NLxhsvwjn81iH8IE9ed8aa5QuFcojAnPE+0LsHluAjg7pSXvdDSBu2DtmDABab9oifAN8MBO5T9ggzpdVaHHTrPzf3W6hSHT7PfT/jz+1/Q2e6ezIm7sQN0tcRMlGiaK6er96PK99h9r4Kmx1f+aIQAAAABJRU5ErkJggg==\n", - "text/latex": [ - "$$- \\frac{g m r^{2}}{I + m r^{2}}$$" - ], - "text/plain": [ - " 2 \n", - "-g⋅m⋅r \n", - "────────\n", - " 2\n", - "I + m⋅r " - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "soln[a]" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAEoAAAAnBAMAAABalMPGAAAAMFBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAVIl2uyKZEO8yZt2rRM0C/HbBAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABkUlEQVQ4Ec1UP0/CQBx9rVX+lAOMixuNGCaiGBMXNSExwUWTOriZwG5iWBwNRr9A+QT4DSBMjkScnIibLsLCWPAPISZivStXUpprcDK+4e7d77387q65PmACKbmRJenjzSZJHoKkL5KZieQiq8gXZGUPpV0sQ1Y6yqVLdKg0RCxeVtvIa6iirDaI4UiuOZhFRS+WWkgUcQ9GRKjcYBtIAKfAtU1ErqqOHHAG1EEGAUZEoF1egSsERlhoH1EiREmPDDA/QriPqKZRIgRprtRAaphrQLozKPGBUvARXGUDPpd3eSAPceBei7n0sBUXK/+5av0Gf3uBVK44e0NZj7Rnu1Q9NGCuHaur+7uDLf7Uvv09TKE/FwV5YyPF+XjyjmqGVWTn8S559fE6ZU/RPlfFrnDBlmPZaZcnKx7xxPQXe19KeK/prAh1FveZa91gIwV3TWdF0LLsE/XGnolLnBUftmvNNG9Ns8u4KCvIu6eXMCvkT69LlBWq87mc04uy4qT3pfFm/I4zsoK7nAP4zM8+deAHB06So4uK1AUAAAAASUVORK5CYII=\n", - "text/latex": [ - "$$\\frac{g m r}{I + m r^{2}}$$" - ], - "text/plain": [ - " g⋅m⋅r \n", - "────────\n", - " 2\n", - "I + m⋅r " - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "soln[alpha]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "The results are\n", - "\n", - "$T = m g I / I^* $\n", - "\n", - "$a = -m g r^2 / I^* $\n", - "\n", - "$\\alpha = m g r / I^* $\n", - "\n", - "where $I^*$ is the augmented moment of inertia, $I + m r^2$.\n", - "\n", - "You can also see [the derivation of these equations in this video](https://www.youtube.com/watch?v=chC7xVDKl4Q).\n", - "\n", - "To simulate the system, we don't really need $T$; we can plug $a$ and $\\alpha$ directly into the slope function." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "newton" - ], - "text/latex": [ - "$newton$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "radian = UNITS.radian\n", - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram\n", - "N = UNITS.newton" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Simulate the descent of a yo-yo. How long does it take to reach the end of the string?\n", - "\n", - "I provide a `Params` object with the system parameters:\n", - "\n", - "* `Rmin` is the radius of the axle. `Rmax` is the radius of the axle plus rolled string.\n", - "\n", - "* `Rout` is the radius of the yo-yo body. `mass` is the total mass of the yo-yo, ignoring the string. \n", - "\n", - "* `L` is the length of the string.\n", - "\n", - "* `g` is the acceleration of gravity." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
Rmin0.008 meter
Rmax0.016 meter
Rout0.035 meter
mass0.05 kilogram
L1 meter
g9.8 meter / second ** 2
t_end1 second
\n", - "
" - ], - "text/plain": [ - "Rmin 0.008 meter\n", - "Rmax 0.016 meter\n", - "Rout 0.035 meter\n", - "mass 0.05 kilogram\n", - "L 1 meter\n", - "g 9.8 meter / second ** 2\n", - "t_end 1 second\n", - "dtype: object" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "params = Params(Rmin = 8e-3 * m,\n", - " Rmax = 16e-3 * m,\n", - " Rout = 35e-3 * m,\n", - " mass = 50e-3 * kg,\n", - " L = 1 * m,\n", - " g = 9.8 * m / s**2,\n", - " t_end = 1 * s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's a `make_system` function that computes `I` and `k` based on the system parameters.\n", - "\n", - "I estimated `I` by modeling the yo-yo as a solid cylinder with uniform density ([see here](https://en.wikipedia.org/wiki/List_of_moments_of_inertia)).\n", - "\n", - "In reality, the distribution of weight in a yo-yo is often designed to achieve desired effects. But we'll keep it simple." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Make a system object.\n", - " \n", - " params: Params with Rmin, Rmax, Rout, \n", - " mass, L, g, t_end\n", - " \n", - " returns: System with init, k, Rmin, Rmax, mass,\n", - " I, g, ts\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " init = State(theta = 0 * radian,\n", - " omega = 0 * radian/s,\n", - " y = L,\n", - " v = 0 * m / s)\n", - " \n", - " I = mass * Rout**2 / 2\n", - " k = (Rmax**2 - Rmin**2) / 2 / L / radian \n", - " \n", - " return System(init=init, k=k,\n", - " Rmin=Rmin, Rmax=Rmax,\n", - " mass=mass, I=I, g=g,\n", - " t_end=t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `make_system`" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
inittheta 0 radian\n", - "omega 0.0 radi...
k9.6e-05 meter / radian
Rmin0.008 meter
Rmax0.016 meter
mass0.05 kilogram
I3.0625000000000006e-05 kilogram * meter ** 2
g9.8 meter / second ** 2
t_end1 second
\n", - "
" - ], - "text/plain": [ - "init theta 0 radian\n", - "omega 0.0 radi...\n", - "k 9.6e-05 meter / radian\n", - "Rmin 0.008 meter\n", - "Rmax 0.016 meter\n", - "mass 0.05 kilogram\n", - "I 3.0625000000000006e-05 kilogram * meter ** 2\n", - "g 9.8 meter / second ** 2\n", - "t_end 1 second\n", - "dtype: object" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system = make_system(params)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
theta0 radian
omega0.0 radian / second
y1 meter
v0.0 meter / second
\n", - "
" - ], - "text/plain": [ - "theta 0 radian\n", - "omega 0.0 radian / second\n", - "y 1 meter\n", - "v 0.0 meter / second\n", - "dtype: object" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "system.init" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Write a slope function for this system, using these results from the book:\n", - "\n", - "$ r = \\sqrt{2 k y + R_{min}^2} $ \n", - "\n", - "$ T = m g I / I^* $\n", - "\n", - "$ a = -m g r^2 / I^* $\n", - "\n", - "$ \\alpha = m g r / I^* $\n", - "\n", - "where $I^*$ is the augmented moment of inertia, $I + m r^2$.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def slope_func(state, t, system):\n", - " \"\"\"Computes the derivatives of the state variables.\n", - " \n", - " state: State object with theta, omega, y, v\n", - " t: time\n", - " system: System object with Rmin, k, I, mass\n", - " \n", - " returns: sequence of derivatives\n", - " \"\"\"\n", - " theta, omega, y, v = state\n", - " unpack(system)\n", - " \n", - " r = sqrt(2*k*y + Rmin**2)\n", - " alpha = mass * g * r / (I + mass * r**2)\n", - " a = -r * alpha\n", - " \n", - " return omega, alpha, v, a " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your slope function with the initial paramss." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(,\n", - " ,\n", - " ,\n", - " )" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "slope_func(system.init, 0*s, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Write an event function that will stop the simulation when `y` is 0." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution\n", - "\n", - "def event_func(state, t, system):\n", - " \"\"\"Stops when y is 0.\n", - " \n", - " state: State object with theta, omega, y, v\n", - " t: time\n", - " system: System object with Rmin, k, I, mass\n", - " \n", - " returns: y\n", - " \"\"\"\n", - " theta, omega, y, v = state\n", - " return y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Test your event function:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "collapsed": true, - "scrolled": false - }, - "outputs": [ - { - "data": { - "text/html": [ - "1 meter" - ], - "text/latex": [ - "$1 meter$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "event_func(system.init, 0*s, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": true, - "scrolled": false - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
values
solNone
t_events[[0.879217870162702]]
nfev134
njev0
nlu0
status1
messageA termination event occurred.
successTrue
\n", - "
" - ], - "text/plain": [ - "sol None\n", - "t_events [[0.879217870162702]]\n", - "nfev 134\n", - "njev 0\n", - "nlu 0\n", - "status 1\n", - "message A termination event occurred.\n", - "success True\n", - "dtype: object" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "results, details = run_ode_solver(system, slope_func, events=event_func, max_step=0.05*s)\n", - "details" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Check the final state. If things have gone according to plan, the final value of `y` should be close to 0." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
thetaomegayv
0.70614744.018235121.3198493.272909e-01-1.765726
0.75614750.267960128.6088402.369824e-01-1.844989
0.80614756.872431135.4958481.429620e-01-1.914052
0.85614763.809267141.8850014.576279e-02-1.971982
0.87921867.114651144.6306439.020562e-17-1.994692
\n", - "
" - ], - "text/plain": [ - " theta omega y v\n", - "0.706147 44.018235 121.319849 3.272909e-01 -1.765726\n", - "0.756147 50.267960 128.608840 2.369824e-01 -1.844989\n", - "0.806147 56.872431 135.495848 1.429620e-01 -1.914052\n", - "0.856147 63.809267 141.885001 4.576279e-02 -1.971982\n", - "0.879218 67.114651 144.630643 9.020562e-17 -1.994692" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Solution\n", - "\n", - "results.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot the results." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`theta` should increase and accelerate." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_theta(results):\n", - " plot(results.theta, color='C0', label='theta')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Angle (rad)')\n", - "plot_theta(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`y` should decrease and accelerate down." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_y(results):\n", - " plot(results.y, color='C1', label='y')\n", - "\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Length (m)')\n", - " \n", - "plot_y(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot velocity as a function of time; is the yo-yo accelerating?" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "v = results.v * m / s\n", - "plot(v)\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Velocity (m/s)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use `gradient` to estimate the derivative of `v`. How goes the acceleration of the yo-yo compare to `g`?" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Solution\n", - "\n", - "a = gradient(v)\n", - "plot(a)\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Acceleration (m/$s^2$)')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/test_install.py b/code/test_install.py deleted file mode 100644 index 64aa3040d..000000000 --- a/code/test_install.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -Code from Modeling and Simulation in Python. - -Copyright 2017 Allen Downey - -License: https://creativecommons.org/licenses/by/4.0) -""" - -import logging -logger = logging.getLogger(name='modsim.py') - -#TODO: Make this Python 3.7 when conda is ready - -# make sure we have Python 3.6 or better -import sys -if sys.version_info < (3, 6): - logger.warning('modsim.py depends on Python 3.6 features.') - -import inspect -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -import scipy -import sympy - -import seaborn as sns -sns.set(style='white', font_scale=1.2) - -import pint -UNITS = pint.UnitRegistry() -Quantity = UNITS.Quantity - -# expose some names so we can use them without dot notation -from copy import copy -from numpy import sqrt, log, exp, pi -from pandas import DataFrame, Series -from time import sleep - -from scipy.interpolate import interp1d -from scipy.interpolate import InterpolatedUnivariateSpline -from scipy.integrate import odeint -from scipy.integrate import solve_ivp -from scipy.optimize import leastsq -from scipy.optimize import minimize_scalar - -import scipy.optimize - -print("All imports were successful.") diff --git a/code/tests/modsim_test.py b/code/tests/modsim_test.py deleted file mode 100644 index fea6e1185..000000000 --- a/code/tests/modsim_test.py +++ /dev/null @@ -1,417 +0,0 @@ -import unittest -from modsim import * - -class TestModSimDataFrame(unittest.TestCase): - - def test_constructor(self): - msf = ModSimDataFrame(columns=['A', 'T', 'dt']) - msf.row[1000] = [1, 2, np.nan] - msf.row['label'] = ['4', 5, 6.0] - - col = msf.A - self.assertIsInstance(col, ModSimSeries) - self.assertEqual(col[1000], 1) - - col = msf.T - self.assertIsInstance(col, ModSimSeries) - self.assertEqual(col[1000], 2) - - col = msf.dt - self.assertIsInstance(col, ModSimSeries) - self.assertEqual(col['label'], 6.0) - - row = msf.row[1000] - self.assertIsInstance(row, ModSimSeries) - - self.assertEqual(row.A, 1) - self.assertEqual(row.T, 2) - self.assertTrue(np.isnan(row.dt)) - - self.assertEqual(row['A'], 1) - self.assertEqual(row['T'], 2) - self.assertTrue(np.isnan(row['dt'])) - -class TestTimeFrame(unittest.TestCase): - - def test_constructor(self): - msf = TimeFrame(columns=['A', 'T', 'dt']) - msf.row[1000] = [1, 2, np.nan] - msf.row['label'] = ['4', 5, 6.0] - - col = msf.A - self.assertIsInstance(col, TimeSeries) - - col = msf.T - self.assertIsInstance(col, TimeSeries) - - col = msf.dt - self.assertIsInstance(col, TimeSeries) - - row = msf.row[1000] - self.assertIsInstance(row, State) - - row = msf.row['label'] - self.assertIsInstance(row, State) - -class TestSweepFrame(unittest.TestCase): - - def test_constructor(self): - msf = SweepFrame(columns=['A', 'T', 'dt']) - msf.row[1000] = [1, 2, np.nan] - msf.row['label'] = ['4', 5, 6.0] - - col = msf.A - self.assertIsInstance(col, SweepSeries) - - col = msf.T - self.assertIsInstance(col, SweepSeries) - - col = msf.dt - self.assertIsInstance(col, SweepSeries) - - row = msf.row[1000] - self.assertIsInstance(row, SweepSeries) - - row = msf.row['label'] - self.assertIsInstance(row, SweepSeries) - -class TestCartPol(unittest.TestCase): - - def test_cart2pol(self): - theta, r = cart2pol(3, 4) - self.assertAlmostEqual(r, 5) - self.assertAlmostEqual(theta, 0.9272952180016122) - - theta, r, z = cart2pol(2, 2, 2) - self.assertAlmostEqual(r, 2 * np.sqrt(2)) - self.assertAlmostEqual(theta, np.pi/4) - self.assertAlmostEqual(z, 2) - - def test_pol2cart(self): - theta = 0.9272952180016122 - r = 5 - x, y = pol2cart(theta, r) - self.assertAlmostEqual(x, 3) - self.assertAlmostEqual(y, 4) - - angle = 45 * UNITS.degree - r = 2 * np.sqrt(2) - z = 2 - x, y, z = pol2cart(angle, r, z) - self.assertAlmostEqual(x, 2) - self.assertAlmostEqual(y, 2) - self.assertAlmostEqual(z, 2) - -class TestLinspaceLinRange(unittest.TestCase): - - def test_linspace(self): - array = linspace(0, 1, 11) - self.assertEqual(len(array), 11) - self.assertAlmostEqual(array[0], 0) - self.assertAlmostEqual(array[1], 0.1) - self.assertAlmostEqual(array[10], 1.0) - - meter = UNITS.meter - start = 11 - stop = 13 * meter - array = linspace(start, stop, 37) - self.assertEqual(len(array), 37) - self.assertAlmostEqual(array[0], 11 * meter) - self.assertAlmostEqual(array[1], 11.055555555555555 * meter) - self.assertAlmostEqual(array[36], 13 * meter) - - def test_linrange(self): - array = linrange(0, 1, 0.1) - self.assertEqual(len(array), 10) - self.assertAlmostEqual(array[0], 0) - self.assertAlmostEqual(array[1], 0.1) - self.assertAlmostEqual(array[9], 0.9) - - array = linrange(0, 1, 0.1, endpoint=True) - self.assertEqual(len(array), 11) - self.assertAlmostEqual(array[0], 0) - self.assertAlmostEqual(array[1], 0.1) - self.assertAlmostEqual(array[10], 1.0) - - meter = UNITS.meter - start = 11 * meter - stop = 13 * meter - step = 0.2 * meter - array = linrange(start, stop, step, endpoint=True) - - self.assertEqual(len(array), 11) - self.assertAlmostEqual(array[0], 11 * meter) - self.assertAlmostEqual(array[1], 11.2 * meter) - self.assertAlmostEqual(magnitude(array[10]), 13) - -class TestAbsRelDiff(unittest.TestCase): - - def test_abs_diff(self): - abs_diff = compute_abs_diff([1, 3, 7.5]) - self.assertEqual(len(abs_diff), 3) - self.assertAlmostEqual(abs_diff[1], 4.5) - - ts = linrange(1950, 1960, endpoint=True) - ps = linspace(3, 4, len(ts)) - abs_diff = compute_abs_diff(ps) - self.assertEqual(len(abs_diff), 11) - self.assertAlmostEqual(abs_diff[1], 0.1) - self.assertTrue(np.isnan(abs_diff[-1])) - - series = TimeSeries(ps, index=ts) - abs_diff = compute_abs_diff(series) - self.assertEqual(len(abs_diff), 11) - self.assertAlmostEqual(abs_diff[1950], 0.1) - self.assertTrue(np.isnan(abs_diff[1960])) - - def test_rel_diff(self): - rel_diff = compute_rel_diff([1, 3, 7.5]) - self.assertEqual(len(rel_diff), 3) - self.assertAlmostEqual(rel_diff[1], 1.5) - - ts = linrange(1950, 1960, endpoint=True) - ps = linspace(3, 4, len(ts)) - rel_diff = compute_rel_diff(ps) - self.assertEqual(len(rel_diff), 11) - self.assertAlmostEqual(rel_diff[0], 0.0333333333) - self.assertTrue(np.isnan(rel_diff[-1])) - - series = TimeSeries(ps, index=ts) - rel_diff = compute_rel_diff(series) - self.assertEqual(len(rel_diff), 11) - self.assertAlmostEqual(rel_diff[1950], 0.0333333333) - self.assertTrue(np.isnan(rel_diff[1960])) - -class TestRunOdeint(unittest.TestCase): - - def test_run_ideint(self): - pass - -class TestVector(unittest.TestCase): - def assertArrayEqual(self, res, ans): - self.assertTrue(isinstance(res, np.ndarray)) - self.assertTrue((res == ans).all()) - - def assertVectorEqual(self, res, ans): - self.assertTrue(isinstance(res, ModSimVector)) - self.assertTrue((res == ans).all()) - - def assertVectorAlmostEqual(self, res, ans): - [self.assertQuantityAlmostEqual(x, y) for x, y in zip(res, ans)] - - def assertQuantityAlmostEqual(self, x, y): - self.assertEqual(units(x), units(y)) - self.assertAlmostEqual(magnitude(x), magnitude(y)) - - def test_vector_mag(self): - m = UNITS.meter - - v = [3, 4] - self.assertEqual(vector_mag(v), 5) - v = Vector(3, 4) - self.assertEqual(vector_mag(v), 5) - v = Vector(3, 4)*m - self.assertEqual(vector_mag(v), 5*m) - - def test_vector_mag2(self): - m = UNITS.meter - - v = [3, 4] - self.assertEqual(vector_mag2(v), 25) - v = Vector(3, 4) - self.assertEqual(vector_mag2(v), 25) - v = Vector(3, 4)*m - self.assertEqual(vector_mag2(v), 25*m*m) - - def test_vector_angle(self): - m = UNITS.meter - ans = 0.927295218 - v = [3, 4] - self.assertAlmostEqual(vector_angle(v), ans) - v = Vector(3, 4) - self.assertAlmostEqual(vector_angle(v), ans) - v = Vector(3, 4)*m - self.assertAlmostEqual(vector_angle(v), ans) - - def test_vector_hat(self): - m = UNITS.meter - v = [3, 4] - ans = [0.6, 0.8] - self.assertArrayEqual(vector_hat(v), ans) - - v = Vector(3, 4) - self.assertVectorEqual(vector_hat(v), ans) - v = Vector(3, 4)*m - self.assertVectorEqual(vector_hat(v), ans) - - v = [0, 0] - ans = [0, 0] - self.assertArrayEqual(vector_hat(v), ans) - v = Vector(0, 0) - self.assertVectorEqual(vector_hat(v), ans) - v = Vector(0, 0)*m - self.assertVectorEqual(vector_hat(v), ans) - - def test_vector_perp(self): - m = UNITS.meter - v = [3, 4] - ans = [-4, 3] - self.assertTrue((vector_perp(v) == ans).all()) - v = Vector(3, 4) - self.assertTrue((vector_perp(v) == ans).all()) - v = Vector(3, 4)*m - self.assertTrue((vector_perp(v) == ans*m).all()) - - def test_vector_dot(self): - m = UNITS.meter - s = UNITS.second - v = [3, 4] - w = [5, 6] - ans = 39 - self.assertAlmostEqual(vector_dot(v, w), ans) - v = Vector(3, 4) - self.assertAlmostEqual(vector_dot(v, w), ans) - self.assertAlmostEqual(vector_dot(w, v), ans) - - v = Vector(3, 4)*m - self.assertAlmostEqual(vector_dot(v, w), ans*m) - self.assertAlmostEqual(vector_dot(w, v), ans*m) - - w = Vector(5, 6)/s - self.assertAlmostEqual(vector_dot(v, w), ans*m/s) - self.assertAlmostEqual(vector_dot(w, v), ans*m/s) - - def test_vector_cross_2D(self): - m = UNITS.meter - s = UNITS.second - ans = -2 - - v = [3, 4] - w = [5, 6] - self.assertAlmostEqual(vector_cross(v, w), ans) - self.assertAlmostEqual(vector_cross(w, v), -ans) - - v = Vector(3, 4) - self.assertAlmostEqual(vector_cross(v, w), ans) - self.assertAlmostEqual(vector_cross(w, v), -ans) - - v = Vector(3, 4)*m - self.assertAlmostEqual(vector_cross(v, w), ans*m) - self.assertAlmostEqual(vector_cross(w, v), -ans*m) - - w = Vector(5, 6)/s - self.assertAlmostEqual(vector_cross(v, w), ans*m/s) - self.assertAlmostEqual(vector_cross(w, v), -ans*m/s) - - def test_vector_cross_3D(self): - m = UNITS.meter - s = UNITS.second - ans = [-2, 4, -2] - - v = [3, 4, 5] - w = [5, 6, 7] - self.assertArrayEqual(vector_cross(v, w), ans) - self.assertArrayEqual(-vector_cross(w, v), ans) - - v = Vector(3, 4, 5) - self.assertVectorEqual(vector_cross(v, w), ans) - self.assertVectorEqual(-vector_cross(w, v), ans) - - v = Vector(3, 4, 5)*m - self.assertVectorEqual(vector_cross(v, w), ans*m) - self.assertVectorEqual(-vector_cross(w, v), ans*m) - - w = Vector(5, 6, 7)/s - self.assertVectorEqual(vector_cross(v, w), ans*m/s) - self.assertVectorEqual(-vector_cross(w, v), ans*m/s) - - def test_scalar_proj(self): - m = UNITS.meter - s = UNITS.second - ans = 4.9934383 - ans2 = 7.8 - - v = [3, 4] - w = [5, 6] - self.assertAlmostEqual(scalar_proj(v, w), ans) - self.assertAlmostEqual(scalar_proj(w, v), ans2) - - v = Vector(3, 4) - self.assertAlmostEqual(scalar_proj(v, w), ans) - self.assertAlmostEqual(scalar_proj(w, v), ans2) - - v = Vector(3, 4)*m - self.assertQuantityAlmostEqual(scalar_proj(v, w), ans*m) - self.assertAlmostEqual(scalar_proj(w, v), ans2) - - w = Vector(5, 6)/s - self.assertQuantityAlmostEqual(scalar_proj(v, w), ans*m) - self.assertQuantityAlmostEqual(scalar_proj(w, v), ans2/s) - - def test_vector_proj(self): - m = UNITS.meter - s = UNITS.second - ans = [3.19672131, 3.83606557] - ans2 = Quantity([4.68, 6.24]) - - v = [3, 4] - w = [5, 6] - self.assertVectorAlmostEqual(vector_proj(v, w), ans) - self.assertVectorAlmostEqual(vector_proj(w, v), ans2) - - v = Vector(3, 4) - self.assertVectorAlmostEqual(vector_proj(v, w), ans) - self.assertVectorAlmostEqual(vector_proj(w, v), ans2) - - v = Vector(3, 4)*m - self.assertVectorAlmostEqual(vector_proj(v, w), ans*m) - self.assertVectorAlmostEqual(vector_proj(w, v), ans2) - - w = Vector(5, 6)/s - self.assertVectorAlmostEqual(vector_proj(v, w), ans*m) - self.assertVectorAlmostEqual(vector_proj(w, v), ans2/s) - - def test_vector_dist(self): - m = UNITS.meter - v = [3, 4] - w = [6, 8] - ans = 5 - self.assertAlmostEqual(vector_dist(v, w), ans) - self.assertAlmostEqual(vector_dist(w, v), ans) - - v = Vector(3, 4) - self.assertAlmostEqual(vector_dist(v, w), ans) - self.assertAlmostEqual(vector_dist(w, v), ans) - - v = Vector(3, 4)*m - w = Vector(6, 8)*m - self.assertAlmostEqual(vector_dist(v, w), ans*m) - self.assertAlmostEqual(vector_dist(w, v), ans*m) - - def test_vector_diff_angle(self): - m = UNITS.meter - v = [3, 4] - w = [5, 6] - ans = 0.0512371674 - self.assertAlmostEqual(vector_diff_angle(v, w), ans) - self.assertAlmostEqual(vector_diff_angle(w, v), -ans) - - v = Vector(3, 4) - self.assertAlmostEqual(vector_diff_angle(v, w), ans) - self.assertAlmostEqual(vector_diff_angle(w, v), -ans) - - v = Vector(3, 4)*m - w = Vector(5, 6)*m - self.assertAlmostEqual(vector_diff_angle(v, w), ans) - self.assertAlmostEqual(vector_diff_angle(w, v), -ans) - - -class TestSeriesCopy(unittest.TestCase): - def test_series_copy(self): - series = TimeSeries() - res = series.copy() - #print(type(res)) - self.assertTrue(isinstance(res, TimeSeries)) - -if __name__ == '__main__': - unittest.main() diff --git a/code/variables_r_strange.ipynb b/code/variables_r_strange.ipynb deleted file mode 100644 index bd2531f00..000000000 --- a/code/variables_r_strange.ipynb +++ /dev/null @@ -1,490 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Hey friends. #\n", - "\n", - "Variables r weird. Shift+Enter if you agree.\n", - "\n", - "by Jane Sieving" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from modsim import System\n", - "# If this doesn't work, move this file into your /code folder.\n", - "# It needs to be in the same folder as modsim.py." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's write a function, like they do in ModSim notebooks all the time. We'll give it some parameters just to make it feel important." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def func1(input1, input2):\n", - " print(\"Input 1 = \", input1)\n", - " print(\"Input 2 = \", input2)\n", - " output = input1 + input2\n", - " print(\"Output = \", output)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "func1(1, 2.5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "All it does is add two numbers and print a lot of stuff. Printing is fun, let's do it some more." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(output)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Well, shucks. That didn't work. `output` isn't defined? But we defined it in that super technical function!\n", - "You can try printing `input1` or `input2` as well, and you'll probably see the same error." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "output = 5\n", - "print(output)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "So, output was defined in the function, but doesn't exist outside of the function executing. What happens if you run the function again and print output?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "func1(1, 2.5) # will print input1, input2, and output\n", - "print(\"Output also =\", output)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "What? There are 2 versions of `output`? Well ya see, this is where we come to **global** and **local** variables. Local variables only exist within a function or class* that is using them (they are temporary for a function/specific to a class). Global variables can be accessed by any function or class.\n", - "\n", - "When you create `output` in `func1` (and when you pass in values for `input1` and `input2`) those are local variables that only `func1` \"knows\" about. When you assign a value to output outside of `func1`, that is a global variable that you can access any time.\n", - "\n", - "\\**Classes are pre-defined or user-defined objects that have different methods and parmeters. TimeSeries, Series, State and System are all classes defined in the modsim.py library.*" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def func2(input1, input2):\n", - " print(\"Input 1 = \", input1)\n", - " print(\"Input 2 = \", input2)\n", - " print(\"Output = \", output) # this will be the global value from earlier, since it's not locally computed" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "func1(1, 2.5)\n", - "func2(1, 2.5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we're running two similar functions one after another, and they print their results one after another.\n", - "The only difference between the functions is that one computes a value for `output` locally, and the other just prints `output`.\n", - "\n", - "`func2` accesses the global version of `output` because it has no other option.* `func1` accesses the local variable `output` because local variables override global values with the same name.\n", - "\n", - "\\**This isn't universally true, in some languages/cases you have to declare a variable as global for it to be accessed anywhere.*\n", - "\n", - "Let's look at another example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def forgetful_func(input1, input2):\n", - " print(input1, input2)\n", - " \n", - "forgetful_func(1, 6)\n", - "\n", - "print(\"Now we try to print those:\")\n", - "print(input1, input2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Why isn't the second `print` command (outside of the function) working? Because `input1` and `input2` still aren't global. We getting this? Good.\n", - "\n", - "Now let's do what the book does all the time:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def useful_func(input1, input2):\n", - " print(input1 + input2)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "useful_func(2, 3) # This should make sense. You pass in these parameters, they get used, they aren't stored after printing." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "input1 = 1.5\n", - "input2 = 4\n", - "useful_func(input1, input2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, I dare you to do something w_i_l_d: change those variable names in the cell above. Change them to be all different. Change them to be all the same. How does the function care about your variable names?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "cat = 5\n", - "dog = 10\n", - "useful_func(cat, dog)\n", - "useful_func(dog, dog)\n", - "useful_func(input2, dog)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Cool, so that's inputs (mostly). What about outputs? That poor little small-town `output` variable wants to be a global star.\n", - "This, friends, is why `return` is so gosh darn important." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def will_you_remember_me(thing1, thing2):\n", - " new_thing = thing1 + thing2\n", - " return new_thing" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "will_you_remember_me(3, 8)\n", - "print(new_thing)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Foiled again! But we used `return` and everything! Well guess what, a return value that doesn't get assigned to anything is like a letter without an address (cue joke about The Twitter and snail mail implying that I'm old or something).\n", - "To store a return value, you HAVE TO assign it to a variable OUTSIDE of the function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "I_will_remember_you = will_you_remember_me(6, 7)\n", - "print(I_will_remember_you)\n", - "# Please, never name things like this. Please." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you're lazy you can do this, but your variable will not be saved so be careful.\n", - "It just feeds the output (return value) of your function to the input of print()." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(will_you_remember_me(5, 6))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can do the above as a shortcut, but remember that your return value will be LOST FOREVER after that.\n", - "\n", - "Like Jack at the end of *Titanic*.\n", - "\n", - "Think about the choices that you make." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Keyword arguments ##\n", - "Okay, I guess I should talk about keyword arguments and that whole `System(this=this, that=that)` nonsense you're seeing.\n", - "\n", - "So basically, a class (object) has a bunch of properties (attributes). Usually a given class will have specific attributes, but classes like `State` and `System` are set up to pretty much take any parameters you give them and accept them as attributes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "solar_system = System(planets=8, central_mass=\"Sun\")\n", - "\n", - "print(solar_system.planets, solar_system.central_mass)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "other_system = System(planets=5, central_mass=\"Me\")\n", - "\n", - "print(other_system.planets, other_system.central_mass)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "friends = 1000\n", - "center_of_universe = \" \" # Type your name here?\n", - "\n", - "personal_universe = System(friends=friends, \n", - " center_of_universe=center_of_universe)\n", - "\n", - "print(personal_universe.friends, \n", - " personal_universe.center_of_universe)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "So, you've created two systems by directly setting parameters, and one by passing in premade variables. In the book those variables usually have the same name as their target, which obscures what's actually happening." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "apples = 1\n", - "bananas = 1\n", - "cherries = 7\n", - "grapes = 13\n", - "\n", - "fruit_salad = System(a=apples, b=bananas, c=cherries, d=grapes)\n", - "\n", - "print(fruit_salad.a, fruit_salad.b, fruit_salad.c, fruit_salad.d)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(fruit_salad.apples)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Oh dear, did that last one fail? Of course it did, apples isn't an attribute of fruit_salad. You *can* print plain old apples, because it's global:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(apples)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You *can't* print plain old a, because that's an attribute of fruit_salad and isn't defined outside of that." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(a)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can, however, do this:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "global_a = fruit_salad.a # This is a name I chose, it doesn't 'make' the variable global.\n", - "print(global_a)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And you could just as well call `global_a` something else, like `a`, and there would be a global variable `a` as well as a property `a`, and if you changed one the other would not be affected." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "b = fruit_salad.b\n", - "b += 10\n", - "fruit_salad.b += 100\n", - "print(b, fruit_salad.b)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "So, when you type `bikeshare = System(this=this, that=that)`,\n", - "the *left* parts are specific attributes of the System object, *local* to the system.\n", - "The *right* parts are values you are *passing in*, which happen to already be defined as global variables with the same names.\n", - "\n", - "You're just telling a function or class to use the global value on the right, calling it by the name on the left." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/data/DataOWall.csv b/data/DataOWall.csv similarity index 100% rename from code/data/DataOWall.csv rename to data/DataOWall.csv diff --git a/data/World_population_estimates.csv b/data/World_population_estimates.csv new file mode 100644 index 000000000..ed7de057b --- /dev/null +++ b/data/World_population_estimates.csv @@ -0,0 +1,68 @@ +Year,census,prb,un,maddison,hyde,tanton,biraben,mj,thomlinson,durand,clark +1950,2557628654,2516000000.0,2525149000.0,2544000000.0,2527960000.0,2400000000.0,2527000000.0,2500000000.0,2400000000.0,,2486000000.0 +1951,2594939877,,2572850917.0,2571663000.0,,,,,,, +1952,2636772306,,2619292068.0,2617949000.0,,,,,,, +1953,2682053389,,2665865392.0,2665959000.0,,,,,,, +1954,2730228104,,2713172027.0,2716927000.0,,,,,,, +1955,2782098943,,2761650981.0,2769074000.0,,,,,,, +1956,2835299673,,2811572031.0,2822502000.0,,,,,,, +1957,2891349717,,2863042795.0,2879934000.0,,,,,,, +1958,2948137248,,2916030167.0,2939254000.0,,,,,,, +1959,3000716593,,2970395814.0,2995909000.0,,,,,,, +1960,3043001508,,3026002942.0,3041507000.0,3042000000.0,,,,,, +1961,3083966929,,3082830266.0,3082161000.0,,,,,,, +1962,3140093217,,3141071531.0,3135787000.0,,,,,,,3036000000.0 +1963,3209827882,,3201178277.0,3201354000.0,,,,,,, +1964,3281201306,,3263738832.0,3266477000.0,,,,,,, +1965,3350425793,,3329122479.0,3333138000.0,,,,,,, +1966,3420677923,,3397475247.0,3402224000.0,,,,,,,3288000000.0 +1967,3490333715,,3468521724.0,3471464000.0,,,,,,, +1968,3562313822,,3541674891.0,3543086000.0,,,,,,, +1969,3637159050,,3616108749.0,3615743000.0,,,,,,, +1970,3712697742,,3691172616.0,3691157000.0,3710000000.0,,3637000000.0,,3600000000.0,"3,600,000,000– 3,700,000,000",3632000000.0 +1971,3790326948,,3766754345.0,3769818000.0,,,,,,, +1972,3866568653,,3842873611.0,3846499000.0,,,,,,, +1973,3942096442,,3919182332.0,3922793000.0,3923000000.0,,,,,,3860000000.0 +1974,4016608813,,3995304922.0,3997677000.0,,,,,,, +1975,4089083233,,4071020434.0,4070671000.0,,,,3900000000.0,4000000000.0,, +1976,4160185010,,4146135850.0,4141445000.0,,,,,,, +1977,4232084578,,4220816737.0,4213539000.0,,,,,,, +1978,4304105753,,4295664825.0,4286317000.0,,,,,,, +1979,4379013942,,4371527871.0,4363144000.0,,,,,,, +1980,4451362735,,4449048798.0,4439529000.0,4461000000.0,,,,,, +1981,4534410125,,4528234634.0,4514838000.0,,,,,,, +1982,4614566561,,4608962418.0,4587307000.0,,,,,,, +1983,4695736743,,4691559840.0,4676388000.0,,,,,,, +1984,4774569391,,4776392828.0,4756521000.0,,,,,,, +1985,4856462699,,4863601517.0,4837719000.0,,5000000000.0,,,,, +1986,4940571232,,4953376710.0,4920968000.0,,,,,,, +1987,5027200492,,5045315871.0,5006672000.0,,,,,,, +1988,5114557167,,5138214688.0,5093306000.0,,,,,,, +1989,5201440110,,5230000000.0,5180540000.0,,,,,,, +1990,5288955934,,5320816667.0,5269029000.0,5308000000.0,,,,,, +1991,5371585922,,5408908724.0,5351922000.0,,,,,,, +1992,5456136278,,5494899570.0,5435722000.0,,,,,,, +1993,5538268316,,5578865109.0,5518127000.0,,,,,,, +1994,5618682132,,5661086346.0,5599396000.0,,,,,,, +1995,5699202985,5760000000.0,5741822412.0,5681575000.0,,,,,,, +1996,5779440593,,5821016750.0,5762212000.0,,,,,,, +1997,5857972543,5840000000.0,5898688337.0,5842122000.0,,,,,,, +1998,5935213248,,5975303657.0,5921366000.0,,,,,,, +1999,6012074922,,6051478010.0,5999622000.0,,,,,,, +2000,6088571383,6067000000.0,6127700428.0,6076558000.0,6145000000.0,,,5750000000.0,,, +2001,6165219247,6137000000.0,6204147026.0,6154791000.0,,,,,,, +2002,6242016348,6215000000.0,6280853817.0,6231704000.0,,,,,,, +2003,6318590956,6314000000.0,6357991749.0,6308364000.0,,,,,,, +2004,6395699509,6396000000.0,6435705595.0,6374056000.0,,,,,,, +2005,6473044732,6477000000.0,6514094605.0,6462987000.0,,,,,,, +2006,6551263534,6555000000.0,6593227977.0,6540214000.0,,,,,,, +2007,6629913759,6625000000.0,6673105937.0,6616689000.0,,,,,,, +2008,6709049780,6705000000.0,6753649228.0,6694832000.0,,,,,,, +2009,6788214394,6809972000.0,6834721933.0,6764086000.0,,,,,,, +2010,6858584755,6892319000.0,6916183482.0,,,,,,,, +2011,6935999491,6986951000.0,6997998760.0,,,,,,,, +2012,7013871313,7057075000.0,7080072417.0,,,,,,,, +2013,7092128094,7136796000.0,7162119434.0,,,,,,,, +2014,7169968185,7238184000.0,7243784000.0,,,,,,,, +2015,7247892788,7336435000.0,7349472000.0,,,,,,,, +2016,7325996709,7418151841.0,,,,,,,,, diff --git a/code/data/World_population_estimates.html b/data/World_population_estimates.html similarity index 100% rename from code/data/World_population_estimates.html rename to data/World_population_estimates.html diff --git a/data/World_population_estimates2.csv b/data/World_population_estimates2.csv new file mode 100644 index 000000000..ed7de057b --- /dev/null +++ b/data/World_population_estimates2.csv @@ -0,0 +1,68 @@ +Year,census,prb,un,maddison,hyde,tanton,biraben,mj,thomlinson,durand,clark +1950,2557628654,2516000000.0,2525149000.0,2544000000.0,2527960000.0,2400000000.0,2527000000.0,2500000000.0,2400000000.0,,2486000000.0 +1951,2594939877,,2572850917.0,2571663000.0,,,,,,, +1952,2636772306,,2619292068.0,2617949000.0,,,,,,, +1953,2682053389,,2665865392.0,2665959000.0,,,,,,, +1954,2730228104,,2713172027.0,2716927000.0,,,,,,, +1955,2782098943,,2761650981.0,2769074000.0,,,,,,, +1956,2835299673,,2811572031.0,2822502000.0,,,,,,, +1957,2891349717,,2863042795.0,2879934000.0,,,,,,, +1958,2948137248,,2916030167.0,2939254000.0,,,,,,, +1959,3000716593,,2970395814.0,2995909000.0,,,,,,, +1960,3043001508,,3026002942.0,3041507000.0,3042000000.0,,,,,, +1961,3083966929,,3082830266.0,3082161000.0,,,,,,, +1962,3140093217,,3141071531.0,3135787000.0,,,,,,,3036000000.0 +1963,3209827882,,3201178277.0,3201354000.0,,,,,,, +1964,3281201306,,3263738832.0,3266477000.0,,,,,,, +1965,3350425793,,3329122479.0,3333138000.0,,,,,,, +1966,3420677923,,3397475247.0,3402224000.0,,,,,,,3288000000.0 +1967,3490333715,,3468521724.0,3471464000.0,,,,,,, +1968,3562313822,,3541674891.0,3543086000.0,,,,,,, +1969,3637159050,,3616108749.0,3615743000.0,,,,,,, +1970,3712697742,,3691172616.0,3691157000.0,3710000000.0,,3637000000.0,,3600000000.0,"3,600,000,000– 3,700,000,000",3632000000.0 +1971,3790326948,,3766754345.0,3769818000.0,,,,,,, +1972,3866568653,,3842873611.0,3846499000.0,,,,,,, +1973,3942096442,,3919182332.0,3922793000.0,3923000000.0,,,,,,3860000000.0 +1974,4016608813,,3995304922.0,3997677000.0,,,,,,, +1975,4089083233,,4071020434.0,4070671000.0,,,,3900000000.0,4000000000.0,, +1976,4160185010,,4146135850.0,4141445000.0,,,,,,, +1977,4232084578,,4220816737.0,4213539000.0,,,,,,, +1978,4304105753,,4295664825.0,4286317000.0,,,,,,, +1979,4379013942,,4371527871.0,4363144000.0,,,,,,, +1980,4451362735,,4449048798.0,4439529000.0,4461000000.0,,,,,, +1981,4534410125,,4528234634.0,4514838000.0,,,,,,, +1982,4614566561,,4608962418.0,4587307000.0,,,,,,, +1983,4695736743,,4691559840.0,4676388000.0,,,,,,, +1984,4774569391,,4776392828.0,4756521000.0,,,,,,, +1985,4856462699,,4863601517.0,4837719000.0,,5000000000.0,,,,, +1986,4940571232,,4953376710.0,4920968000.0,,,,,,, +1987,5027200492,,5045315871.0,5006672000.0,,,,,,, +1988,5114557167,,5138214688.0,5093306000.0,,,,,,, +1989,5201440110,,5230000000.0,5180540000.0,,,,,,, +1990,5288955934,,5320816667.0,5269029000.0,5308000000.0,,,,,, +1991,5371585922,,5408908724.0,5351922000.0,,,,,,, +1992,5456136278,,5494899570.0,5435722000.0,,,,,,, +1993,5538268316,,5578865109.0,5518127000.0,,,,,,, +1994,5618682132,,5661086346.0,5599396000.0,,,,,,, +1995,5699202985,5760000000.0,5741822412.0,5681575000.0,,,,,,, +1996,5779440593,,5821016750.0,5762212000.0,,,,,,, +1997,5857972543,5840000000.0,5898688337.0,5842122000.0,,,,,,, +1998,5935213248,,5975303657.0,5921366000.0,,,,,,, +1999,6012074922,,6051478010.0,5999622000.0,,,,,,, +2000,6088571383,6067000000.0,6127700428.0,6076558000.0,6145000000.0,,,5750000000.0,,, +2001,6165219247,6137000000.0,6204147026.0,6154791000.0,,,,,,, +2002,6242016348,6215000000.0,6280853817.0,6231704000.0,,,,,,, +2003,6318590956,6314000000.0,6357991749.0,6308364000.0,,,,,,, +2004,6395699509,6396000000.0,6435705595.0,6374056000.0,,,,,,, +2005,6473044732,6477000000.0,6514094605.0,6462987000.0,,,,,,, +2006,6551263534,6555000000.0,6593227977.0,6540214000.0,,,,,,, +2007,6629913759,6625000000.0,6673105937.0,6616689000.0,,,,,,, +2008,6709049780,6705000000.0,6753649228.0,6694832000.0,,,,,,, +2009,6788214394,6809972000.0,6834721933.0,6764086000.0,,,,,,, +2010,6858584755,6892319000.0,6916183482.0,,,,,,,, +2011,6935999491,6986951000.0,6997998760.0,,,,,,,, +2012,7013871313,7057075000.0,7080072417.0,,,,,,,, +2013,7092128094,7136796000.0,7162119434.0,,,,,,,, +2014,7169968185,7238184000.0,7243784000.0,,,,,,,, +2015,7247892788,7336435000.0,7349472000.0,,,,,,,, +2016,7325996709,7418151841.0,,,,,,,,, diff --git a/data/World_population_estimates3.csv b/data/World_population_estimates3.csv new file mode 100644 index 000000000..2ab604fe1 --- /dev/null +++ b/data/World_population_estimates3.csv @@ -0,0 +1,46 @@ +Year,census,prb,un +2016,7334771614.0,,7432663280.0 +2017,7412778971.0,, +2018,7490427640.0,, +2019,7567402977.0,, +2020,7643402123.0,,7758157000.0 +2021,7718256830.0,, +2022,7792021317.0,, +2023,7864725370.0,, +2024,7936271554.0,, +2025,8006580553.0,8000000000.0,8141661000.0 +2026,8075716000.0,, +2027,8143729466.0,, +2028,8210559895.0,, +2029,8276190519.0,, +2030,8340606590.0,8505000000.0,8500766000.0 +2031,8403880343.0,, +2032,8466094022.0,, +2033,8527246205.0,, +2034,8587325154.0,, +2035,8646304704.0,,8838908000.0 +2036,8704239274.0,, +2037,8761189197.0,, +2038,8817138785.0,, +2039,8872066537.0,, +2040,8925949679.0,,9157234000.0 +2041,8978822945.0,, +2042,9030723366.0,, +2043,9081617002.0,, +2044,9131462326.0,, +2045,9180225214.0,,9453892000.0 +2046,9227935007.0,, +2047,9274616811.0,, +2048,9320232984.0,, +2049,9364750182.0,, +2050,9408141302.0,9804000000.0,9725148000.0 +2055,,,9968809000.0 +2060,,,10184290000.0 +2065,,,10375719000.0 +2070,,,10547989000.0 +2075,,,10701653000.0 +2080,,,10836635000.0 +2085,,,10953525000.0 +2090,,,11055270000.0 +2095,,,11142461000.0 +2100,,,11213317000.0 diff --git a/code/data/baseball_drag.csv b/data/baseball_drag.csv similarity index 100% rename from code/data/baseball_drag.csv rename to data/baseball_drag.csv diff --git a/code/data/glucose_insulin.csv b/data/glucose_insulin.csv similarity index 100% rename from code/data/glucose_insulin.csv rename to data/glucose_insulin.csv diff --git a/environment.yml b/environment.yml index 393de2e84..56d42fc40 100644 --- a/environment.yml +++ b/environment.yml @@ -1,143 +1,21 @@ name: ModSimPy channels: - conda-forge - - dsdale24 - - defaults dependencies: - - appdirs=1.4.3=py36h28b3542_0 - - asn1crypto=0.24.0=py36_0 - - attrs=18.2.0=py36h28b3542_0 - - automat=0.7.0=py36_0 - - backcall=0.1.0=py36_0 - - beautifulsoup4=4.7.1=py36_1 - - blas=1.0=mkl - - ca-certificates=2018.11.29=ha4d7672_0 - - certifi=2018.11.29=py36_1000 - - cffi=1.11.5=py36he75722e_1 - - constantly=15.1.0=py36h28b3542_0 - - cryptography=2.5=py36h1ba5d50_0 - - cryptography-vectors=2.5=py_0 - - cycler=0.10.0=py36_0 - - dbus=1.13.6=h746ee38_0 - - decorator=4.3.2=py36_0 - - entrypoints=0.3=py36_0 - - expat=2.2.6=he6710b0_0 - - fastcache=1.0.2=py36h14c3975_2 - - fontconfig=2.13.0=h9420a91_0 - - freetype=2.9.1=h8a8886c_1 - - glib=2.56.2=hd408876_0 - - gmp=6.1.2=h6c8ec71_1 - - gmpy2=2.0.8=py36h10f8cd9_2 - - gst-plugins-base=1.14.0=hbbd80ab_1 - - gstreamer=1.14.0=hb453b48_1 - - html5lib=1.0.1=py36_0 - - hyperlink=18.0.0=py36_0 - - icu=58.2=h9c2bf20_1 - - idna=2.8=py36_0 - - incremental=17.5.0=py36_0 - - intel-openmp=2019.1=144 - - ipykernel=5.1.0=py36h39e3cac_0 - - ipython=7.2.0=py36h39e3cac_0 - - ipython_genutils=0.2.0=py36_0 - - ipywidgets=7.4.2=py36_0 - - jedi=0.13.2=py36_0 - - jinja2=2.10=py36_0 - - jpeg=9b=h024ee3a_2 - - jsonschema=2.6.0=py36_0 - - jupyter=1.0.0=py36_7 - - jupyter_client=5.2.4=py36_0 - - jupyter_console=6.0.0=py36_0 - - jupyter_core=4.4.0=py36_0 - - jupyterlab=0.35.3=py36_0 - - jupyterlab_launcher=0.13.1=py36_0 - - jupyterlab_server=0.2.0=py36_0 - - kiwisolver=1.0.1=py36hf484d3e_0 - - libedit=3.1.20181209=hc058e9b_0 - - libffi=3.2.1=hd88cf55_4 - - libgcc-ng=8.2.0=hdf63c60_1 - - libgfortran-ng=7.3.0=hdf63c60_0 - - libpng=1.6.36=hbc83047_0 - - libsodium=1.0.16=h1bed415_0 - - libstdcxx-ng=8.2.0=hdf63c60_1 - - libuuid=1.0.3=h1bed415_2 - - libxcb=1.13=h1bed415_1 - - libxml2=2.9.9=he19cac6_0 - - libxslt=1.1.33=h7d1a2b0_0 - - lxml=4.3.0=py36hefd8a0e_0 - - markupsafe=1.1.0=py36h7b6447c_0 - - matplotlib=3.0.2=py36h5429711_0 - - mistune=0.8.4=py36h7b6447c_0 - - mkl=2019.1=144 - - mkl_fft=1.0.10=py36ha843d7b_0 - - mkl_random=1.0.2=py36hd81dba3_0 - - mpc=1.1.0=h10f8cd9_1 - - mpfr=4.0.1=hdf1c602_3 - - mpmath=1.1.0=py36_0 - - nbconvert=5.3.1=py36_0 - - nbformat=4.4.0=py36_0 - - ncurses=6.1=he6710b0_1 - - notebook=5.7.4=py36_0 - - numpy=1.15.4=py36h7e9f1db_0 - - numpy-base=1.15.4=py36hde5b4d6_0 - - openssl=1.1.1a=h14c3975_1000 - - pandas=0.24.1=py36he6710b0_0 - - pandoc=2.2.3.2=0 - - pandocfilters=1.4.2=py36_1 - - parso=0.3.2=py36_0 - - patsy=0.5.1=py36_0 - - pcre=8.42=h439df22_0 - - pexpect=4.6.0=py36_0 - - pickleshare=0.7.5=py36_0 - - pint=0.9=py36_2 - - pip=19.0.1=py36_0 - - prometheus_client=0.5.0=py36_0 - - prompt_toolkit=2.0.8=py_0 - - ptyprocess=0.6.0=py36_0 - - pyasn1=0.4.5=py_0 - - pyasn1-modules=0.2.3=py36_0 - - pycparser=2.19=py36_0 - - pygments=2.3.1=py36_0 - - pyhamcrest=1.9.0=py36_2 - - pyopenssl=19.0.0=py36_0 - - pyparsing=2.3.1=py36_0 - - pypdf2=1.26.0=py36_1 - - pyqt=5.9.2=py36h05f1152_2 - - pysolar=0.8=py_0 - - python=3.6.8=h0371630_0 - - python-dateutil=2.7.5=py36_0 - - pytz=2018.9=py36_0 - - pyzmq=17.1.2=py36he6710b0_2 - - qt=5.9.7=h5867ecd_1 - - qtconsole=4.4.3=py36_0 - - readline=7.0=h7b6447c_5 - - scipy=1.2.0=py36h7c811a0_0 - - seaborn=0.9.0=py36_0 - - send2trash=1.5.0=py36_0 - - service_identity=18.1.0=py36h28b3542_0 - - setuptools=40.7.3=py36_0 - - simplegeneric=0.8.1=py36_2 - - sip=4.19.8=py36hf484d3e_0 - - six=1.12.0=py36_0 - - soupsieve=1.7.1=py36_0 - - sqlite=3.26.0=h7b6447c_0 - - statsmodels=0.9.0=py36h035aef0_0 - - sympy=1.3=py36_0 - - terminado=0.8.1=py36_1 - - testpath=0.4.2=py36_0 - - tk=8.6.8=hbc83047_0 - - tornado=5.1.1=py36h7b6447c_0 - - traitlets=4.3.2=py36_0 - - twisted=18.9.0=py36h7b6447c_0 - - wcwidth=0.1.7=py36_0 - - webencodings=0.5.1=py36_1 - - wheel=0.32.3=py36_0 - - widgetsnbextension=3.4.2=py36_0 - - xz=5.2.4=h14c3975_4 - - zeromq=4.3.1=he6710b0_3 - - zlib=1.2.11=h7b6447c_3 - - zope=1.0=py36_1 - - zope.interface=4.6.0=py36h7b6447c_0 - - pip: - - thinkstats2==2.0.0 -prefix: /home/downey/anaconda3/envs/ModSimPy + - python + - jupyter + - numpy + - matplotlib + - pandas + - scipy + - pint + - sympy + - lxml + - html5lib + - beautifulsoup4 + - pytables + - pip +# pip install pytest nbmake +# pip install jupyter-book ghp-import +# pip install pypandoc fastdoc diff --git a/examples/build.sh b/examples/build.sh new file mode 100644 index 000000000..304386ffc --- /dev/null +++ b/examples/build.sh @@ -0,0 +1,25 @@ +# copy the notebooks and remove the solutions +cp ../ModSimPySolutions/examples/*.ipynb . +python remove_soln.py + +# run nbmake +rm modsim.py chap*.py +pytest --nbmake \ +bungee1.ipynb \ +bungee2.ipynb \ +glucose.ipynb \ +header.ipynb \ +insulin.ipynb \ +plague.ipynb \ +queue.ipynb \ +# salmon.ipynb \ does not run without solutions +spiderman.ipynb \ +throwingaxe.ipynb \ +trees.ipynb \ +wall.ipynb \ + + +# add and commit +git add *.ipynb +git commit -m "Updating examples" +git push diff --git a/code/chap21.ipynb b/examples/bungee1.ipynb similarity index 50% rename from code/chap21.ipynb rename to examples/bungee1.ipynb index 6a439e0fc..5c67e507f 100644 --- a/code/chap21.ipynb +++ b/examples/bungee1.ipynb @@ -4,466 +4,69 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Chapter 21\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", - "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### With air resistance" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next we'll add air resistance using the [drag equation](https://en.wikipedia.org/wiki/Drag_equation)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "I'll start by getting the units we'll need from Pint." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram" + "# Bungee Dunk" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now I'll create a `Params` object to contain the quantities we need. Using a Params object is convenient for grouping the system parameters in a way that's easy to read (and double-check)." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "params = Params(height = 381 * m,\n", - " v_init = 0 * m / s,\n", - " g = 9.8 * m/s**2,\n", - " mass = 2.5e-3 * kg,\n", - " diameter = 19e-3 * m,\n", - " rho = 1.2 * kg/m**3,\n", - " v_term = 18 * m / s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can pass the `Params` object `make_system` which computes some additional parameters and defines `init`.\n", + "*Modeling and Simulation in Python*\n", "\n", - "`make_system` uses the given radius to compute `area` and the given `v_term` to compute the drag coefficient `C_d`." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def make_system(params):\n", - " \"\"\"Makes a System object for the given conditions.\n", - " \n", - " params: Params object\n", - " \n", - " returns: System object\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " area = np.pi * (diameter/2)**2\n", - " C_d = 2 * mass * g / (rho * area * v_term**2)\n", - " init = State(y=height, v=v_init)\n", - " t_end = 30 * s\n", - " \n", - " return System(params, area=area, C_d=C_d, \n", - " init=init, t_end=t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's make a `System`" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "system = make_system(params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the slope function, including acceleration due to gravity and drag." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Compute derivatives of the state.\n", - " \n", - " state: position, velocity\n", - " t: time\n", - " system: System object\n", - " \n", - " returns: derivatives of y and v\n", - " \"\"\"\n", - " y, v = state\n", - " unpack(system)\n", - " \n", - " f_drag = rho * v**2 * C_d * area / 2\n", - " a_drag = f_drag / mass\n", - " \n", - " dydt = v\n", - " dvdt = -g + a_drag\n", - " \n", - " return dydt, dvdt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As always, let's test the slope function with the initial conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "slope_func(system.init, 0, system)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use the same event function as in the previous chapter." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def event_func(state, t, system):\n", - " \"\"\"Return the height of the penny above the sidewalk.\n", - " \"\"\"\n", - " y, v = state\n", - " return y" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And then run the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "results, details = run_ode_solver(system, slope_func, events=event_func, max_step=0.5*s)\n", - "details.message" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here are the results." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The final height is close to 0, as expected.\n", - "\n", - "Interestingly, the final velocity is not exactly terminal velocity, which suggests that there are some numerical errors.\n", + "Copyright 2021 Allen Downey\n", "\n", - "We can get the flight time from `results`." + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" ] }, { "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "t_sidewalk = get_last_label(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the plot of position as a function of time." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "def plot_position(results):\n", - " plot(results.y)\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Position (m)')\n", - " \n", - "plot_position(results)\n", - "savefig('figs/chap09-fig02.pdf')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And velocity as a function of time:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, + "execution_count": 1, "metadata": { - "collapsed": true + "tags": [] }, "outputs": [], "source": [ - "def plot_velocity(results):\n", - " plot(results.v, color='C1', label='v')\n", - " \n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Velocity (m/s)')\n", - " \n", - "plot_velocity(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "From an initial velocity of 0, the penny accelerates downward until it reaches terminal velocity; after that, velocity is constant." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Run the simulation with an initial velocity, downward, that exceeds the penny's terminal velocity. Hint: You can create a new `Params` object based on an existing one, like this:\n", - "\n", - "`params = Params(params, v_init = -30 * m / s)`\n", + "# install Pint if necessary\n", "\n", - "What do you expect to happen? Plot velocity and position as a function of time, and see if they are consistent with your prediction." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "plot_position(results)" + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 2, "metadata": { - "scrolled": false + "tags": [] }, "outputs": [], "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Suppose we drop a quarter from the Empire State Building and find that its flight time is 19.1 seconds. Use this measurement to estimate the terminal velocity.\n", - "\n", - "1. You can get the relevant dimensions of a quarter from https://en.wikipedia.org/wiki/Quarter_(United_States_coin).\n", - "\n", - "2. Create a `Params` object with the system parameters. We don't know `v_term`, so we'll start with the inital guess `v_term = 18 * m / s`.\n", - "\n", - "3. Use `make_system` to create a `System` object.\n", + "# download modsim.py if necessary\n", "\n", - "4. Call `run_ode_solver` to simulate the system. How does the flight time of the simulation compare to the measurement?\n", + "from os.path import basename, exists\n", "\n", - "5. Try a few different values of `t_term` and see if you can get the simulated flight time close to 19.1 seconds.\n", - "\n", - "6. Optionally, write an error function and use `fsolve` to improve your estimate.\n", - "\n", - "7. Use your best estimate of `v_term` to compute `C_d`.\n", - "\n", - "Note: I fabricated the observed flight time, so don't take the results of this exercise too seriously." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/modsim.py')" ] }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 3, "metadata": { - "collapsed": true + "tags": [] }, "outputs": [], "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Bungee jumping" + "# import functions from modsim\n", + "\n", + "from modsim import *" ] }, { @@ -497,35 +100,20 @@ }, { "cell_type": "code", - "execution_count": 25, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram\n", - "N = UNITS.newton" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "collapsed": true - }, + "execution_count": 4, + "metadata": {}, "outputs": [], "source": [ - "params = Params(y_attach = 80 * m,\n", - " v_init = 0 * m / s,\n", - " g = 9.8 * m/s**2,\n", - " mass = 75 * kg,\n", - " area = 1 * m**2,\n", - " rho = 1.2 * kg/m**3,\n", - " v_term = 60 * m / s,\n", - " L = 25 * m,\n", - " k = 40 * N / m)" + "params = Params(y_attach = 80, # m,\n", + " v_init = 0, # m / s,\n", + " g = 9.8, # m/s**2,\n", + " mass = 75, # kg,\n", + " area = 1, # m**2,\n", + " rho = 1.2, # kg/m**3,\n", + " v_term = 60, # m / s,\n", + " L = 25, # m,\n", + " k = 40, # N / m\n", + " )" ] }, { @@ -539,10 +127,8 @@ }, { "cell_type": "code", - "execution_count": 27, - "metadata": { - "collapsed": true - }, + "execution_count": 5, + "metadata": {}, "outputs": [], "source": [ "def make_system(params):\n", @@ -552,11 +138,14 @@ " \n", " returns: System object\n", " \"\"\"\n", - " unpack(params)\n", + " area, mass = params.area, params.mass\n", + " g, rho = params.g, params.rho\n", + " v_init, v_term = params.v_init, params.v_term\n", + " y_attach = params.y_attach\n", " \n", " C_d = 2 * mass * g / (rho * area * v_term**2)\n", " init = State(y=y_attach, v=v_init)\n", - " t_end = 20 * s\n", + " t_end = 20\n", "\n", " return System(params, C_d=C_d, \n", " init=init, t_end=t_end)" @@ -571,7 +160,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -582,15 +171,15 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "`spring_force` computes the force of the cord on the jumper:" + "`spring_force` computes the force of the cord on the jumper.\n", + "\n", + "If the spring is not extended, it returns `zero_force`, which is either 0 Newtons or 0, depending on whether the `System` object has units. I did that so the slope function works correctly with and without units." ] }, { "cell_type": "code", - "execution_count": 29, - "metadata": { - "collapsed": true - }, + "execution_count": 7, + "metadata": {}, "outputs": [], "source": [ "def spring_force(y, system):\n", @@ -605,10 +194,11 @@ " \n", " returns: force in N\n", " \"\"\"\n", - " unpack(system)\n", + " y_attach, L, k = system.y_attach, system.L, system.k\n", + " \n", " distance_fallen = y_attach - y\n", " if distance_fallen <= L:\n", - " return 0 * N\n", + " return 0\n", " \n", " extension = distance_fallen - L\n", " f_spring = k * extension\n", @@ -624,29 +214,20 @@ }, { "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "spring_force(80*m, system)" - ] - }, - { - "cell_type": "code", - "execution_count": 31, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ - "spring_force(55*m, system)" + "spring_force(55, system)" ] }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ - "spring_force(54*m, system)" + "spring_force(54, system)" ] }, { @@ -658,7 +239,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -670,7 +251,8 @@ "\n", " returns: drag force\n", " \"\"\"\n", - " unpack(system)\n", + " rho, C_d, area = system.rho, system.C_d, system.area\n", + " \n", " f_drag = -np.sign(v) * rho * v**2 * C_d * area / 2\n", " return f_drag" ] @@ -684,11 +266,11 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ - "v = -60 * m/s\n", + "v = -60\n", "f_drag = drag_force(v, system)" ] }, @@ -701,11 +283,12 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ - "a_drag = f_drag / system.mass" + "a_drag = f_drag / system.mass\n", + "a_drag" ] }, { @@ -717,13 +300,11 @@ }, { "cell_type": "code", - "execution_count": 36, - "metadata": { - "collapsed": true - }, + "execution_count": 13, + "metadata": {}, "outputs": [], "source": [ - "def slope_func(state, t, system):\n", + "def slope_func(t, state, system):\n", " \"\"\"Compute derivatives of the state.\n", " \n", " state: position, velocity\n", @@ -734,10 +315,11 @@ " returns: derivatives of y and v\n", " \"\"\"\n", " y, v = state\n", - " unpack(system)\n", + " mass, g = system.mass, system.g\n", " \n", " a_drag = drag_force(v, system) / mass\n", " a_spring = spring_force(y, system) / mass\n", + " \n", " dvdt = -g + a_drag + a_spring\n", " \n", " return v, dvdt" @@ -752,11 +334,11 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ - "slope_func(system.init, 0, system)" + "slope_func(0, system.init, system)" ] }, { @@ -768,14 +350,12 @@ }, { "cell_type": "code", - "execution_count": 38, - "metadata": { - "collapsed": true - }, + "execution_count": 15, + "metadata": {}, "outputs": [], "source": [ - "results, details = run_ode_solver(system, slope_func, max_step=0.3*s)\n", - "details" + "results, details = run_solve_ivp(system, slope_func)\n", + "details.message" ] }, { @@ -787,7 +367,19 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "def plot_position(results):\n", + " results.y.plot()\n", + " decorate(xlabel='Time (s)',\n", + " ylabel='Position (m)')" + ] + }, + { + "cell_type": "code", + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -798,7 +390,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "After reaching the lowest point, the jumper springs back almost to almost 70 m, and oscillates several times. That looks like more osciallation that we expect from an actual jump, which suggests that there some dissipation of energy in the real world that is not captured in our model. To improve the model, that might be a good thing to investigate.\n", + "After reaching the lowest point, the jumper springs back almost to almost 70 m and oscillates several times. That looks like more oscillation that we expect from an actual jump, which suggests that there is some dissipation of energy in the real world that is not captured in our model. To improve the model, that might be a good thing to investigate.\n", "\n", "But since we are primarily interested in the initial descent, the model might be good enough for now.\n", "\n", @@ -807,7 +399,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -830,47 +422,45 @@ }, { "cell_type": "code", - "execution_count": 41, - "metadata": { - "scrolled": false - }, + "execution_count": 19, + "metadata": {}, "outputs": [], "source": [ - "plot_velocity(results)" + "def plot_velocity(results):\n", + " results.v.plot(color='C1', label='v')\n", + " \n", + " decorate(xlabel='Time (s)',\n", + " ylabel='Velocity (m/s)')" ] }, { "cell_type": "code", - "execution_count": 42, - "metadata": {}, + "execution_count": 20, + "metadata": { + "scrolled": false + }, "outputs": [], "source": [ - "subplot(1, 2, 1)\n", - "plot_position(results)\n", - "\n", - "subplot(1, 2, 2)\n", - "plot_velocity(results)\n", - "\n", - "savefig('figs/chap09-fig03.pdf')" + "plot_velocity(results)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Although we compute acceleration inside the slope function, we don't get acceleration as a result from `run_ode_solver`.\n", + "Although we compute acceleration inside the slope function, we don't get acceleration as a result from `run_solve_ivp`.\n", "\n", "We can approximate it by computing the numerical derivative of `ys`:" ] }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "a = gradient(results.v)\n", - "plot(a)\n", + "a.plot(color='C2')\n", "decorate(xlabel='Time (s)',\n", " ylabel='Acceleration (m/$s^2$)')" ] @@ -884,11 +474,12 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ - "max_acceleration = max(a) * m/s**2" + "max_acceleration = max(a)\n", + "max_acceleration" ] }, { @@ -900,36 +491,18 @@ }, { "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [], - "source": [ - "max_acceleration / g" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Under the hood\n", - "\n", - "The gradient function in `modsim.py` adapts the NumPy function of the same name so it works with `Series` objects.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 46, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ - "%psource gradient" + "max_acceleration / system.g" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Solving for length\n", + "## Solving for length\n", "\n", "Assuming that `k` is fixed, let's find the length `L` that makes the minimum altitude of the jumper exactly 0." ] @@ -938,18 +511,18 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The metric we are interested in is the lowest point of the first oscillation. For both efficiency and accuracy, it is better to stop the simulation when we reach this point, rather than run past it and the compute the minimum.\n", + "The metric we are interested in is the lowest point of the first oscillation. For both efficiency and accuracy, it is better to stop the simulation when we reach this point, rather than run past it and then compute the minimum.\n", "\n", "Here's an event function that stops the simulation when velocity is 0." ] }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ - "def event_func(state, t, system):\n", + "def event_func(t, state, system):\n", " \"\"\"Return velocity.\n", " \"\"\"\n", " y, v = state\n", @@ -965,56 +538,79 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ - "event_func(system.init, 0, system)" + "event_func(0, system.init, system)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "And we see that we have a problem. Since the event function returns 0 under the initial conditions, the simulation would stop immediately. We can solve that problem by specifying the direction of the event function:" + "If we call `run_solve_ivp` with this event function, we'll see that the simulation stops immediately because the initial velocity is 0.\n", + "\n", + "We could work around that by starting with a very small, non-zero initial velocity.\n", + "But we can also avoid it by setting the `direction` attribute of the `event_func`:" ] }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ - "event_func.direction = +1" + "event_func.direction = 10" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "When direction is positive, it only stops the simulation if the velocity is 0 and increasing, which is what we want." + "The value 1 (or any positive value) indicates that the event should only occur if the result from `event_func` is increasing.\n", + "A negative value would indicate that the results should be decreasing.\n", + "\n", + "Now we can test it and confirm that it stops at the bottom of the jump." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "results, details = run_solve_ivp(system, slope_func, \n", + " events=event_func)\n", + "details.message" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now we can test it an confirm that it stops at the bottom of the jump." + "Here are the results." ] }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ - "results, details = run_ode_solver(system, slope_func, events=event_func, max_step=0.3*s)\n", "plot_position(results)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And here's the height of the jumper at the lowest point." + ] + }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -1029,28 +625,23 @@ "\n", "Test the error function with a guess of 25 m and confirm that the return value is about 5 meters.\n", "\n", - "Use `fsolve` with your error function to find the value of `L` that yields a perfect bungee dunk.\n", + "Use `root_scalar` with your error function to find the value of `L` that yields a perfect bungee dunk.\n", "\n", - "Run a simulation with the result from `fsolve` and confirm that it works.\n" + "Run a simulation with the result from `root_scalar` and confirm that it works." ] }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "# Solution goes here" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ @@ -1059,7 +650,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ @@ -1068,23 +659,16 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ "# Solution goes here" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Optional exercise:** Search for the combination of length and spring constant that yields minimum height 0 while minimizing peak acceleration." - ] - }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -1093,7 +677,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ @@ -1110,7 +694,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -1124,7 +708,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.6" + "version": "3.8.16" } }, "nbformat": 4, diff --git a/code/jump.ipynb b/examples/bungee2.ipynb similarity index 62% rename from code/jump.ipynb rename to examples/bungee2.ipynb index 0b672c793..d2c903736 100644 --- a/code/jump.ipynb +++ b/examples/bungee2.ipynb @@ -4,53 +4,86 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Bungee dunk example, taking into account the mass of the bungee cord\n", + "# Bungee Dunk Revisited" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Modeling and Simulation in Python*\n", "\n", - "Copyright 2018 Allen Downey\n", + "Copyright 2021 Allen Downey\n", "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" ] }, { "cell_type": "code", "execution_count": 1, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", + "# install Pint if necessary\n", "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/modsim.py')" ] }, { - "cell_type": "markdown", - "metadata": {}, + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [] + }, + "outputs": [], "source": [ - "### Bungee jumping" + "# import functions from modsim\n", + "\n", + "from modsim import *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "In Chapter 21, we simulated a bungee jump with a model that took into account gravity, air resistance, and the spring force of the bungee cord, but we ignored the weight of the cord.\n", + "In the previous case study, we simulated a bungee jump with a model that took into account gravity, air resistance, and the spring force of the bungee cord, but we ignored the weight of the cord.\n", "\n", - "It is tempting to say that the weight of the cord doesn't matter, because it falls along with the jumper. But that intuition is incorrect, as explained by [Heck, Uylings, and Kędzierska](http://iopscience.iop.org/article/10.1088/0031-9120/45/1/007). As the cord falls, it transfers energy to the jumper. They derive a differential equation that relates the acceleration of the jumper to position and velocity:\n", + "It is tempting to say that the weight of the cord doesn't matter because it falls along with the jumper. But that intuition is incorrect, as explained by [Heck, Uylings, and Kędzierska](http://iopscience.iop.org/article/10.1088/0031-9120/45/1/007). As the cord falls, it transfers energy to the jumper. They derive a differential equation that relates the acceleration of the jumper to position and velocity:\n", "\n", "$a = g + \\frac{\\mu v^2/2}{\\mu(L+y) + 2L}$ \n", "\n", - "where $a$ is the net acceleration of the number, $g$ is acceleration due to gravity, $v$ is the velocity of the jumper, $y$ is the position of the jumper relative to the starting point (usually negative), $L$ is the length of the cord, and $\\mu$ is the mass ratio of the cord and jumper.\n", + "where $a$ is the net acceleration of the jumper, $g$ is acceleration due to gravity, $v$ is the velocity of the jumper, $y$ is the position of the jumper relative to the starting point (usually negative), $L$ is the length of the cord, and $\\mu$ is the mass ratio of the cord and jumper.\n", "\n", "If you don't believe this model is correct, [this video might convince you](https://www.youtube.com/watch?v=X-QFAB0gEtE).\n", "\n", - "Following the example in Chapter 21, we'll model the jump with the following modeling assumptions:\n", + "Following the previous case study, we'll model the jump with the following assumptions:\n", "\n", "1. Initially the bungee cord hangs from a crane with the attachment point 80 m above a cup of tea.\n", "\n", @@ -75,31 +108,21 @@ }, { "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram\n", - "N = UNITS.newton" - ] - }, - { - "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ - "params = Params(v_init = 0 * m / s,\n", - " g = 9.8 * m/s**2,\n", - " M = 75 * kg, # mass of jumper\n", - " m_cord = 75 * kg, # mass of cord\n", - " area = 1 * m**2, # frontal area of jumper\n", - " rho = 1.2 * kg/m**3, # density of air\n", - " v_term = 60 * m / s, # terminal velocity of jumper\n", - " L = 25 * m, # length of cord\n", - " k = 40 * N / m) # spring constant of cord" + "params = Params(y_attach = 80, # m,\n", + " v_init = 0, # m / s,\n", + " g = 9.8, # m/s**2,\n", + " M = 75, # kg,\n", + " m_cord = 75, # kg\n", + " area = 1, # m**2,\n", + " rho = 1.2, # kg/m**3,\n", + " v_term = 60, # m / s,\n", + " L = 25, # m,\n", + " k = 40, # N / m\n", + " )" ] }, { @@ -115,7 +138,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -126,14 +149,16 @@ " \n", " returns: System object\n", " \"\"\"\n", - " unpack(params)\n", + " M, m_cord = params.M, params.m_cord\n", + " g, rho, area = params.g, params.rho, params.area\n", + " v_init, v_term = params.v_init, params.v_term\n", " \n", " # back out the coefficient of drag\n", " C_d = 2 * M * g / (rho * area * v_term**2)\n", " \n", " mu = m_cord / M\n", - " init = State(y=0*m, v=v_init)\n", - " t_end = 10 * s\n", + " init = State(y=params.y_attach, v=v_init)\n", + " t_end = 8\n", "\n", " return System(params, C_d=C_d, mu=mu,\n", " init=init, t_end=t_end)" @@ -148,11 +173,11 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ - "system = make_system(params)" + "system1 = make_system(params)" ] }, { @@ -164,7 +189,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -175,7 +200,8 @@ " \n", " returns: drag force in N\n", " \"\"\"\n", - " unpack(system)\n", + " rho, C_d, area = system.rho, system.C_d, system.area\n", + "\n", " f_drag = -np.sign(v) * rho * v**2 * C_d * area / 2\n", " return f_drag" ] @@ -189,11 +215,11 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ - "drag_force(20 * m/s, system)" + "drag_force(20, system1)" ] }, { @@ -207,30 +233,22 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "def cord_acc(y, v, system):\n", - " \"\"\"Computes acceleration due to the force of \n", - " the bungee cord on the jumper.\n", + " \"\"\"Computes the force of the bungee cord on the jumper:\n", " \n", " y: height of the jumper\n", " v: velocity of the jumpter\n", " \n", " returns: acceleration in m/s\n", " \"\"\"\n", - " # Fill this in\n", - " return 0" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" + " L, mu = system.L, system.mu\n", + " \n", + " a_cord = -v**2 / 2 / (2*L/mu + (L+y))\n", + " return a_cord" ] }, { @@ -246,9 +264,9 @@ "metadata": {}, "outputs": [], "source": [ - "y = -20 * m\n", - "v = -20 * m/s\n", - "cord_acc(y, v, system)" + "y = -20\n", + "v = -20\n", + "cord_acc(y, v, system1)" ] }, { @@ -264,7 +282,7 @@ "metadata": {}, "outputs": [], "source": [ - "def slope_func1(state, t, system):\n", + "def slope_func1(t, state, system):\n", " \"\"\"Compute derivatives of the state.\n", " \n", " state: position, velocity\n", @@ -275,23 +293,15 @@ " returns: derivatives of y and v\n", " \"\"\"\n", " y, v = state\n", - " unpack(system)\n", + " M, g = system.M, system.g\n", " \n", - " # Fill this in\n", - " dvdt = -g\n", + " a_drag = drag_force(v, system) / M\n", + " a_cord = cord_acc(y, v, system)\n", + " dvdt = -g + a_cord + a_drag\n", " \n", " return v, dvdt" ] }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -301,11 +311,11 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ - "slope_func1(system.init, 0, system)" + "slope_func1(0, system1.init, system1)" ] }, { @@ -317,11 +327,11 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ - "def event_func(state, t, system):\n", + "def event_func1(t, state, system):\n", " \"\"\"Run until y=-L.\n", " \n", " state: position, velocity\n", @@ -329,22 +339,10 @@ " system: System object containing g, rho,\n", " C_d, area, and mass\n", " \n", - " returns: difference between y and -L\n", + " returns: difference between y and y_attach-L\n", " \"\"\"\n", - " y, v = state\n", - " unpack(system)\n", - "\n", - " # Fill this in\n", - " return 1" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" + " y, v = state \n", + " return y - (system.y_attach - system.L)" ] }, { @@ -356,11 +354,11 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ - "event_func(system.init, 0, system)" + "event_func1(0, system1.init, system1)" ] }, { @@ -372,29 +370,13 @@ }, { "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "results, details = run_ode_solver(system, slope_func1, \n", - " events=event_func, max_step=0.1)\n", - "details.message" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how long it takes to drop 25 meters." - ] - }, - { - "cell_type": "code", - "execution_count": 18, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ - "t_final = get_last_label(results)" + "results1, details1 = run_solve_ivp(system1, slope_func1, \n", + " events=event_func1)\n", + "details1.message" ] }, { @@ -406,124 +388,48 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "def plot_position(results, **options):\n", - " plot(results.y, **options)\n", + " results.y.plot(**options)\n", " decorate(xlabel='Time (s)',\n", " ylabel='Position (m)')\n", " \n", - "plot_position(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's velocity as a function of time:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "scrolled": false - }, - "outputs": [], - "source": [ - "def plot_velocity(results):\n", - " plot(results.v, color='C1', label='v')\n", - " \n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Velocity (m/s)')\n", - " \n", - "plot_velocity(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Velocity when we reach the end of the cord." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "min(results.v) * m/s" + "plot_position(results1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Sweeping cord weight\n", - "\n", - "Now let's see how velocity at the crossover point depends on the weight of the cord." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "def sweep_m_cord(m_cord_array, params):\n", - " sweep = SweepSeries()\n", - "\n", - " for m_cord in m_cord_array:\n", - " system = make_system(Params(params, m_cord=m_cord))\n", - " results, details = run_ode_solver(system, slope_func1, \n", - " events=event_func)\n", - " min_velocity = min(results.v) * m/s\n", - " sweep[m_cord.magnitude] = min_velocity\n", - " \n", - " return sweep" + "We can use `min` to find the lowest point:" ] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ - "m_cord_array = linspace(1, 201, 51) * kg\n", - "sweep = sweep_m_cord(m_cord_array, params);" + "min(results1.y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Here's what it looks like. As expected, a heavier cord gets the jumper going faster.\n", - "\n", - "There's a hitch near 25 kg that seems to be due to numerical error." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "plot(sweep)\n", - "\n", - "decorate(xlabel='Mass of cord (kg)',\n", - " ylabel='Fastest downward velocity (m/s)')" + "As expected, Phase 1 ends when the jumper reaches an altitude of 55 m." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Phase 2\n", + "## Phase 2\n", "\n", - "Once the jumper falls past the length of the cord, acceleration due to energy transfer from the cord stops abruptly. As the cord stretches, it starts to exert a spring force. So let's simulate this second phase." + "Once the jumper has fallen more than the length of the cord, acceleration due to energy transfer from the cord stops abruptly. As the cord stretches, it starts to exert a spring force. So let's simulate this second phase." ] }, { @@ -535,7 +441,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -551,8 +457,9 @@ " \n", " returns: force in N\n", " \"\"\"\n", - " unpack(system)\n", - " distance_fallen = -y\n", + " L, k = system.L, system.k\n", + " \n", + " distance_fallen = system.y_attach - y\n", " extension = distance_fallen - L\n", " f_spring = k * extension\n", " return f_spring" @@ -567,20 +474,20 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ - "spring_force(-25*m, system)" + "spring_force(55, system1)" ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ - "spring_force(-26*m, system)" + "spring_force(56, system1)" ] }, { @@ -592,11 +499,11 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ - "def slope_func2(state, t, system):\n", + "def slope_func2(t, state, system):\n", " \"\"\"Compute derivatives of the state.\n", " \n", " state: position, velocity\n", @@ -607,7 +514,7 @@ " returns: derivatives of y and v\n", " \"\"\"\n", " y, v = state\n", - " unpack(system)\n", + " M, g = system.M, system.g\n", " \n", " a_drag = drag_force(v, system) / M\n", " a_spring = spring_force(y, system) / M\n", @@ -620,89 +527,70 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "I'll run Phase 1 again so we can get the final state." + "The initial state for Phase 2 is the final state from Phase 1." ] }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ - "system1 = make_system(params)" + "t_final = results1.index[-1]\n", + "t_final" ] }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ - "event_func.direction=-1\n", - "results1, details1 = run_ode_solver(system1, slope_func1, \n", - " events=event_func, max_step=0.1)\n", - "print(details1.message)" + "state_final = results1.iloc[-1]\n", + "state_final" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now I need the final time, position, and velocity from Phase 1." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "t_final = get_last_label(results1)" + "And that gives me the starting conditions for Phase 2." ] }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ - "init2 = results1.row[t_final]" + "system2 = System(system1, t_0=t_final, init=state_final)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "And that gives me the starting conditions for Phase 2." + "Here's how we run Phase 2, setting the direction of the event function so it doesn't stop the simulation immediately. " ] }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ - "system2 = System(system1, t_0=t_final, t_end=t_final+10, init=init2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's how we run Phase 2, setting the direction of the event function so it doesn't stop the simulation immediately. " + "results2, details2 = run_solve_ivp(system2, slope_func2)\n", + "details2.message" ] }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ - "event_func.direction=+1\n", - "results2, details2 = run_ode_solver(system2, slope_func2, \n", - " events=event_func, max_step=0.1)\n", - "print(details2.message)\n", - "t_final = get_last_label(results2)" + "t_final = results2.index[-1]\n", + "t_final" ] }, { @@ -714,7 +602,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -731,11 +619,11 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ - "min(results2.y) * m" + "min(results2.y)" ] }, { @@ -747,24 +635,20 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ - "def simulate_system2(params):\n", - " \n", + "def run_two_phases(params):\n", " system1 = make_system(params)\n", - " event_func.direction=-1\n", - " results1, details1 = run_ode_solver(system1, slope_func1, events=event_func, max_step=0.1)\n", - "\n", - " t_final = get_last_label(results1)\n", - " init2 = results1.row[t_final]\n", + " results1, details1 = run_solve_ivp(system1, slope_func1, \n", + " events=event_func1)\n", + " t_final = results1.index[-1]\n", + " state_final = results1.iloc[-1]\n", " \n", - " system2 = System(system1, t_0=t_final, t_end=t_final+10, init=init2)\n", - " event_func.direction=+1\n", - " results2, details2 = run_ode_solver(system2, slope_func2, events=event_func, max_step=0.1)\n", - " t_final = get_last_label(results2)\n", - " return TimeFrame(pd.concat([results1, results2]))" + " system2 = system1.set(t_0=t_final, init=state_final)\n", + " results2, details2 = run_solve_ivp(system2, slope_func2)\n", + " return pd.concat([results1, results2])" ] }, { @@ -776,16 +660,16 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ - "results = simulate_system2(params);" + "results = run_two_phases(params)" ] }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ @@ -794,18 +678,20 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 36, "metadata": {}, "outputs": [], "source": [ - "params_no_cord = Params(params, m_cord=1*kg)\n", - "results_no_cord = simulate_system2(params_no_cord);" + "params_no_cord = params.set(m_cord=1)\n", + "results_no_cord = run_two_phases(params_no_cord);" ] }, { "cell_type": "code", - "execution_count": 41, - "metadata": {}, + "execution_count": 37, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "plot_position(results, label='m_cord = 75 kg')\n", @@ -814,26 +700,29 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ - "min(results_no_cord.y) * m" + "min(results_no_cord.y)" ] }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ - "diff = min(results.y) - min(results_no_cord.y)" + "diff = min(results.y) - min(results_no_cord.y)\n", + "diff" ] }, { "cell_type": "markdown", "metadata": {}, - "source": [] + "source": [ + "The difference is about a meter, which could certainly be the difference between a successful bungee dunk and a bad day." + ] }, { "cell_type": "code", @@ -845,7 +734,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -859,7 +748,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.6" + "version": "3.10.13" } }, "nbformat": 4, diff --git a/code/filter.ipynb b/examples/filter.ipynb similarity index 79% rename from code/filter.ipynb rename to examples/filter.ipynb index 6be18e99b..dd78c2a40 100644 --- a/code/filter.ipynb +++ b/examples/filter.ipynb @@ -4,27 +4,68 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Modeling and Simulation in Python\n", - "\n", + "# Low-Pass Filter" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Modeling and Simulation in Python*\n", "\n", - "Copyright 2017 Allen Downey\n", + "Copyright 2021 Allen Downey\n", "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" ] }, { "cell_type": "code", "execution_count": 1, - "metadata": {}, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", + "# download modsim.py if necessary\n", "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", "\n", - "# import functions from the modsim.py module\n", "from modsim import *" ] }, @@ -32,11 +73,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Low pass filter\n", - "\n", - "The following circuit diagram (from [Wikipedia](https://en.wikipedia.org/wiki/File:RC_Divider.svg)) shows a low-pass filter built with one resistor and one capacitor. \n", + "The following circuit diagram (from [Wikipedia](https://commons.wikimedia.org/wiki/File:1st_Order_Lowpass_Filter_RC.svg)) shows a low-pass filter built with one resistor and one capacitor. \n", "\n", - "![Low-pass filter circuit diagram](diagrams/RC_Divider.png)\n", + "![Circuit diagram of a low-pass filter](https://github.com/AllenDowney/ModSim/raw/main/figs/Lowpass_Filter_RC.png)\n", "\n", "A \"filter\" is a circuit takes a signal, $V_{in}$, as input and produces a signal, $V_{out}$, as output. In this context, a \"signal\" is a voltage that changes over time.\n", "\n", @@ -64,7 +103,7 @@ "\n", "$ \\frac{d }{dt} V_{out} = \\frac{V_{in} - V_{out}}{R C} $\n", "\n", - "Follow the instructions blow to simulate the low-pass filter for input signals like this:\n", + "Follow the instructions below to simulate the low-pass filter for input signals like this:\n", "\n", "$ V_{in}(t) = A \\cos (2 \\pi f t) $\n", "\n", @@ -75,46 +114,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Params and System objects\n", + "## Params and System objects\n", "\n", "Here's a `Params` object to contain the quantities we need. I've chosen values for `R1` and `C1` that might be typical for a circuit that works with audio signal." ] }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "ohm = UNITS.ohm\n", - "farad = UNITS.farad\n", - "volt = UNITS.volt\n", - "Hz = UNITS.Hz\n", - "second = UNITS.second" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "params = Params(\n", - " R1 = 1e6 * ohm,\n", - " C1 = 1e-9 * farad,\n", - " A = 5 * volt,\n", - " f = 1000 * Hz)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Normally I would recommend running this computation with units, but for this example it turns out to be too slow.\n", - "\n", - "Here's a `Params` object with no units." - ] - }, { "cell_type": "code", "execution_count": 4, @@ -122,11 +126,12 @@ "outputs": [], "source": [ "params = Params(\n", - " R1 = 1e6, # ohm\n", - " C1 = 1e-9, # farad\n", - " A = 5, # volt\n", - " f = 1000, # Hz\n", - ")" + " R1 = 1e6, # * ohm\n", + " C1 = 1e-9, # * farad\n", + " A = 5, # * volt\n", + " f = 1000, # * Hz\n", + ")\n", + "params" ] }, { @@ -150,6 +155,8 @@ "metadata": {}, "outputs": [], "source": [ + "from numpy import pi\n", + "\n", "def make_system(params):\n", " \"\"\"Makes a System object for the given conditions.\n", " \n", @@ -157,18 +164,19 @@ " \n", " returns: System object\n", " \"\"\"\n", - " unpack(params)\n", + " f, R1, C1 = params.f, params.R1, params.C1\n", " \n", " init = State(V_out = 0)\n", - " omega = 2 * np.pi * f\n", + " omega = 2 * pi * f\n", " tau = R1 * C1\n", - " cutoff = 1 / R1 / C1 / 2 / np.pi\n", + " cutoff = 1 / R1 / C1 / 2 / pi\n", " t_end = 4 / f\n", - " ts = linspace(0, t_end, 401)\n", " \n", - " return System(R1=R1, C1=C1, A=A, f=f,\n", - " init=init, t_end=t_end, ts=ts,\n", - " omega=omega, tau=tau, cutoff=cutoff)" + " return System(params, \n", + " init=init, \n", + " t_end=t_end, num=401,\n", + " omega=omega, tau=tau, \n", + " cutoff=cutoff)" ] }, { @@ -184,14 +192,22 @@ "metadata": {}, "outputs": [], "source": [ - "system = make_system(params)" + "system = make_system(params)\n", + "system" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Exercise:** Write a slope function that takes as an input a `State` object that contains `V_out`, and returns the derivative of `V_out`." + "The system variable `num` controls how many time steps we get from `run_solve_ivp`. The default is 101; in this case we increase it to 401 because the methods we'll use to analyze the results require high resolution in time." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** Write a slope function that takes as an input a `State` object that contains `V_out`, and returns the derivative of `V_out`.\n" ] }, { @@ -216,7 +232,7 @@ "metadata": {}, "outputs": [], "source": [ - "slope_func(system.init, 0, system)" + "slope_func(0, system.init, system)" ] }, { @@ -232,8 +248,17 @@ "metadata": {}, "outputs": [], "source": [ - "results, details = run_ode_solver(system, slope_func, t_eval=system.ts)\n", - "details" + "results, details = run_solve_ivp(system, slope_func)\n", + "details.message" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "results.tail()" ] }, { @@ -245,25 +270,24 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "def plot_results(results):\n", - " xs = results.V_out.index\n", - " ys = results.V_out.values\n", - "\n", - " t_end = get_last_label(results)\n", - " if t_end < 10:\n", - " xs *= 1000\n", + " V_out = results.V_out.copy()\n", + " t_end = results.index[-1]\n", + " \n", + " if t_end < 0.1:\n", + " V_out.index *= 1000\n", " xlabel = 'Time (ms)'\n", " else:\n", + " V_out = results.V_out\n", " xlabel = 'Time (s)'\n", " \n", - " plot(xs, ys)\n", + " V_out.plot(label='_nolegend')\n", " decorate(xlabel=xlabel,\n", - " ylabel='$V_{out}$ (volt)',\n", - " legend=False)\n", + " ylabel='$V_{out}$ (volt)')\n", " \n", "plot_results(results)" ] @@ -281,22 +305,24 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Sweeping frequency\n", + "## Sweeping frequency\n", "\n", - "Plot `V_out` looks like for a range of frequencies:" + "Here's what `V_out` looks like for a range of frequencies:" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ + "from matplotlib.pyplot import subplot\n", + "\n", "fs = [1, 10, 100, 1000, 10000, 100000]\n", "\n", "for i, f in enumerate(fs):\n", - " system = make_system(Params(params, f=f))\n", - " results, details = run_ode_solver(system, slope_func, t_eval=system.ts)\n", + " system = make_system(params.set(f=f))\n", + " results, details = run_solve_ivp(system, slope_func)\n", " subplot(3, 2, i+1)\n", " plot_results(results)" ] @@ -312,7 +338,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Estimating the output ratio\n", + "## Estimating the output ratio\n", "\n", "Let's compare the amplitudes of the input and output signals. Below the cutoff frequency, we expect them to be about the same. Above the cutoff, we expect the amplitude of the output signal to be smaller.\n", "\n", @@ -321,12 +347,12 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ - "system = make_system(Params(params, f=1000))\n", - "results, details = run_ode_solver(system, slope_func, t_eval=system.ts)\n", + "system = make_system(params.set(f=1000))\n", + "results, details = run_solve_ivp(system, slope_func)\n", "V_out = results.V_out\n", "plot_results(results)" ] @@ -340,7 +366,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -352,8 +378,10 @@ " \n", " returns: TimeSeries\n", " \"\"\"\n", - " unpack(system)\n", - " V_in = A * np.cos(omega * results.index)\n", + " A, omega = system.A, system.omega\n", + " \n", + " ts = results.index\n", + " V_in = A * np.cos(omega * ts)\n", " return TimeSeries(V_in, results.index, name='V_in')" ] }, @@ -366,14 +394,14 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "V_in = compute_vin(results, system)\n", "\n", - "plot(V_out)\n", - "plot(V_in)\n", + "V_out.plot()\n", + "V_in.plot()\n", "\n", "decorate(xlabel='Time (s)',\n", " ylabel='V (volt)')" @@ -388,7 +416,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -411,11 +439,12 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ - "A_in = estimate_A(V_in)" + "A_in = estimate_A(V_in)\n", + "A_in" ] }, { @@ -427,11 +456,12 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ - "A_out = estimate_A(V_out)" + "A_out = estimate_A(V_out)\n", + "A_out" ] }, { @@ -443,23 +473,24 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ - "ratio = A_out / A_in" + "ratio = A_out / A_in\n", + "ratio" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Exercise:** Encapsulate the code we have so far in a function that takes two TimeSeries objects and returns the ratio between their amplitudes." + "**Exercise:** Encapsulate the code we have so far in a function that takes two `TimeSeries` objects and returns the ratio between their amplitudes." ] }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -475,7 +506,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -486,7 +517,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Estimating phase offset\n", + "## Estimating phase offset\n", "\n", "The delay between the peak of the input and the peak of the output is call a \"phase shift\" or \"phase offset\", usually measured in fractions of a cycle, degrees, or radians.\n", "\n", @@ -495,13 +526,13 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "corr = np.correlate(V_out, V_in, mode='same')\n", - "corr = TimeSeries(corr, V_in.index)\n", - "plot(corr, color='C5')\n", + "corr_series = make_series(V_in.index, corr)\n", + "corr_series.plot(color='C4')\n", "decorate(xlabel='Lag (s)',\n", " ylabel='Correlation')" ] @@ -515,11 +546,12 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ - "peak_time = corr.idxmax()" + "peak_time = corr_series.idxmax()\n", + "peak_time" ] }, { @@ -531,16 +563,17 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ - "period = 1 / system.f" + "period = 1 / system.f\n", + "period" ] }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -556,7 +589,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ @@ -573,7 +606,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -591,7 +624,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -607,7 +640,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -618,7 +651,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Sweeping frequency again\n", + "## Sweeping frequency again\n", "\n", "**Exercise:** Write a function that takes as parameters an array of input frequencies and a `Params` object.\n", "\n", @@ -629,7 +662,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ @@ -645,7 +678,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ @@ -654,7 +687,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ @@ -670,11 +703,11 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ - "plot(ratios, color='C2', label='output ratio')\n", + "ratios.plot(color='C2', label='output ratio')\n", "decorate(xlabel='Frequency (Hz)',\n", " ylabel='$V_{out} / V_{in}$')" ] @@ -688,15 +721,18 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "def plot_ratios(ratios, system):\n", " \"\"\"Plot output ratios.\n", " \"\"\"\n", - " plt.axvline(system.cutoff, color='gray', alpha=0.4)\n", - " plot(ratios, color='C2', label='output ratio')\n", + " # axvline can't handle a Quantity with units\n", + " cutoff = magnitude(system.cutoff)\n", + " plt.axvline(cutoff, color='gray', alpha=0.4)\n", + " \n", + " ratios.plot(color='C2', label='output ratio')\n", " decorate(xlabel='Frequency (Hz)',\n", " ylabel='$V_{out} / V_{in}$',\n", " xscale='log', yscale='log')" @@ -704,7 +740,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ @@ -722,15 +758,18 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "def plot_offsets(offsets, system):\n", " \"\"\"Plot phase offsets.\n", " \"\"\"\n", - " plt.axvline(system.cutoff, color='gray', alpha=0.4)\n", - " plot(offsets, color='C9')\n", + " # axvline can't handle a Quantity with units\n", + " cutoff = magnitude(system.cutoff)\n", + " plt.axvline(cutoff, color='gray', alpha=0.4)\n", + " \n", + " offsets.plot(color='C9', label='phase offset')\n", " decorate(xlabel='Frequency (Hz)',\n", " ylabel='Phase offset (degree)',\n", " xscale='log')" @@ -738,7 +777,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ @@ -771,7 +810,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ @@ -787,7 +826,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -796,7 +835,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ @@ -812,7 +851,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ @@ -828,21 +867,21 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 42, "metadata": {}, "outputs": [], "source": [ - "plot(A, ':', color='gray')\n", + "A.plot(style=':', color='gray', label='analysis')\n", "plot_ratios(ratios, system)" ] }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 43, "metadata": {}, "outputs": [], "source": [ - "plot(phi, ':', color='gray')\n", + "phi.plot(style=':', color='gray', label='analysis')\n", "plot_offsets(offsets, system)" ] }, @@ -850,7 +889,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "For the phase offsets, there are small differences between the theoretical results and our estimates, but that is probably because it is not easy to estimate phase offsets precisely from numerical results." + "For the phase offsets, there are differences between the theoretical results and our estimates, but that is probably because it is not easy to estimate phase offsets precisely from numerical results." ] }, { @@ -870,7 +909,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -884,7 +923,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.6" + "version": "3.10.13" } }, "nbformat": 4, diff --git a/examples/glucose.ipynb b/examples/glucose.ipynb new file mode 100644 index 000000000..08bae4768 --- /dev/null +++ b/examples/glucose.ipynb @@ -0,0 +1,616 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The Glucose Minimal Model" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the previous chapter we implemented the glucose minimal model using given parameters, but I didn't say where those parameters came from.\n", + "This notebook solves the mystery.\n", + "\n", + "We'll use a SciPy function called `leastsq`, which stands for \"least squares\"; that is, it finds the parameters that minimize the sum of squared differences between the results of the model and the data.\n", + "\n", + "You can think of `leastsq` as optional material. We won't use it in the book itself, but it appears in a few of the case studies.\n", + "\n", + "You can read more about `leastsq` in [the SciPy documentation](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.leastsq.html). It uses the Levenberg-Marquart algorithm, which you can read about [on Wikipedia](https://en.wikipedia.org/wiki/Levenberg%E2%80%93Marquardt_algorithm)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following cells download and read the data." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "download('https://github.com/AllenDowney/ModSim/raw/main/data/' +\n", + " 'glucose_insulin.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "data = pd.read_csv('glucose_insulin.csv', index_col='time');" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We'll use `make_system` and `slope_func` as defined in Chapter 18." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'chap18.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "from chap18 import make_system\n", + "from chap18 import slope_func" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Computing errors\n", + "\n", + "In this context, the \"errors\" are the differences between the results from the model and the data.\n", + "\n", + "To compute the errors, I'll start again with the parameters we used in Chapter 18." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "G0 = 270\n", + "k1 = 0.02\n", + "k2 = 0.02\n", + "k3 = 1.5e-05" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "params = G0, k1, k2, k3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`make_system` takes the parameters and actual data and returns a `System` object." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "system = make_system(params, data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here's how we run the ODE solver." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "results, details = run_solve_ivp(system, slope_func, \n", + " t_eval=data.index)\n", + "details.message" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Because we specify `t_eval=data.index`, the results are evaluated at the some time stamps as the data." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "results.tail()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can plot the results like this." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "data.glucose.plot(style='o', alpha=0.5, label='data')\n", + "results.G.plot(style='-', color='C0', label='model')\n", + "\n", + "decorate(xlabel='Time (min)',\n", + " ylabel='Concentration (mg/dL)')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "During the first three time steps, the model does not fit the data. That's because it takes some time for the injected glucose to disperse.\n", + "\n", + "We can compute the errors like this." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "errors = results.G - data.glucose\n", + "errors.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first few errors are substantially larger than the rest.\n", + "\n", + "In the next section, we'll use `leastsq` to search for the parameters that minimize the sum of the squared errors." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Optimization\n", + "\n", + "To use `leastsq`, we need an \"error function\" that takes a sequence of parameters and returns an array of errors.\n", + "\n", + "Here's a function that does what we need." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "def error_func(params, data):\n", + " \"\"\"Computes an array of errors to be minimized.\n", + " \n", + " params: sequence of parameters\n", + " data: DataFrame of values to be matched\n", + " \n", + " returns: array of errors\n", + " \"\"\"\n", + " print(params)\n", + " \n", + " # make a System with the given parameters\n", + " system = make_system(params, data)\n", + " \n", + " # solve the ODE\n", + " results, details = run_solve_ivp(system, slope_func, \n", + " t_eval=data.index)\n", + " \n", + " # compute the difference between the model\n", + " # results and actual data\n", + " errors = results.G - data.glucose\n", + " return errors.iloc[3:]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`error_func` uses the given parameters to make a `System` object, runs the simulation, and returns the errors.\n", + "\n", + "But notice that it does not return all of the errors; rather, it uses `iloc` to select only the elements with index 3 or more.\n", + "In other words, it omits the elements with index 0, 1, and 2.\n", + "Note: You can read more about this use of `iloc` [in the Pandas documentation](https://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-integer).\n", + "\n", + "Since we don't expect the model to fit the data in this regime, we'll leave it out.\n", + "\n", + "We can call `error_func` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "error_func(params, data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we're ready to call `leastsq`. As arguments, we pass `error_func`, the parameters where we want to start the search, and the data, which will be passed as an argument to `error_func`." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "best_params, fit_details = leastsq(error_func, params, data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each time `error_func` is called, it prints the parameters, so we can get a sense of how `leastsq` works.\n", + "\n", + "`leastsq` has two return values.\n", + "The first is an array with the best parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "best_params" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The second is an object with information about the results, including a success flag and a message." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "fit_details.success" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "fit_details.mesg" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that we have `best_params`, we can use it to make a `System` object and run it." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "system2 = make_system(best_params, data)\n", + "results2, details = run_solve_ivp(system2, slope_func, \n", + " t_eval=data.index)\n", + "details.message" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here are the results, along with the data." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "data.glucose.plot(style='o', alpha=0.5, label='data')\n", + "results.G.plot(style='-', color='C0', label='model')\n", + "\n", + "decorate(xlabel='Time (min)',\n", + " ylabel='Concentration (mg/dL)')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can compute the errors like this." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [], + "source": [ + "errors2 = results2.G - data.glucose\n", + "errors2.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And the sum of the squared errors like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import sum\n", + "\n", + "sum(errors2.iloc[3:]**2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If things have gone according to plan, the sum of squared errors should be smaller now, compared to the parameters we started with." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "sum(errors.iloc[3:]**2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interpreting parameters\n", + "\n", + "So we found the parameters that best match the data. You might wonder why this is useful.\n", + "\n", + "The parameters themselves don't mean very much, but we can use them to compute two quantities we care about:\n", + "\n", + "- \"Glucose effectiveness\", $E$, which is the tendency of elevated\n", + " glucose to cause depletion of glucose.\n", + "\n", + "- \"Insulin sensitivity\", $S$, which is the ability of elevated blood\n", + " insulin to enhance glucose effectiveness.\n", + "\n", + "Glucose effectiveness is defined as the change in $dG/dt$ as we vary\n", + "$G$: \n", + "\n", + "$$E \\equiv - \\frac{\\delta \\dot{G}}{\\delta G}$$ \n", + "\n", + "where $\\dot{G}$ is shorthand for $dG/dt$. Taking the derivative of $dG/dt$ with respect to $G$, we get \n", + "\n", + "$$E = k_1 + X$$ \n", + "\n", + "The **glucose effectiveness index**, $S_G$, is the value of $E$ when blood insulin is near its basal level, $I_b$.\n", + "In that case, $X$ approaches 0 and $E$ approaches $k_1$. So we can use\n", + "the best-fit value of $k_1$ as an estimate of $S_G$." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Insulin sensitivity is defined as the change in $E$ as we vary $I$:\n", + "\n", + "$$S \\equiv - \\frac{\\delta E}{\\delta I}$$ \n", + "\n", + "The **insulin sensitivity index**, $S_I$, is the value of $S$ when $E$ and $I$ are at steady state: \n", + "\n", + "$$S_I \\equiv \\frac{\\delta E_{SS}}{\\delta I_{SS}}$$ \n", + "\n", + "$E$ and $I$ are at steady state when $dG/dt$ and $dX/dt$ are 0, but we don't actually have to solve those equations to find $S_I$. \n", + "\n", + "If we set $dX/dt = 0$ and solve for $X$, we find the relation:\n", + "\n", + "$$X_{SS} = \\frac{k_3}{k_2} I_{SS}$$ \n", + "\n", + "And since $E = k_1 + X$, we have:\n", + "\n", + "$$S_I = \\frac{\\delta E_{SS}}{\\delta I_{SS}} = \\frac{\\delta X_{SS}}{\\delta I_{SS}}$$\n", + "\n", + "Taking the derivative of $X_{SS}$ with respect to $I_{SS}$, we have:\n", + "\n", + "$$S_I = k_3 / k_2$$ \n", + "\n", + "So if we find parameters that make the model fit the data, we can use $k_3 / k_2$ as an estimate of $S_I$." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For the parameters we found, here are the estimated values of $S_G$ and $S_I$:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "G0, k1, k2, k3 = best_params\n", + "indices = SimpleNamespace(S_G=k1, S_I=k3/k2)\n", + "show(indices)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "According to [Boston et al](https://www.researchgate.net/publication/8931437_MINMOD_Millennium_A_Computer_Program_to_Calculate_Glucose_Effectiveness_and_Insulin_Sensitivity_From_the_Frequently_Sampled_Intravenous_Glucose_Tolerance_Test), normal ranges for these values are..." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "S_G_interval = 1.2e-3, 4.5e-2\n", + "S_G_interval" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "S_I_interval = 5.0e-5, 2.2e-3\n", + "S_I_interval" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The estimated quantities are within the normal intervals." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "**Exercise:** How sensitive are the results to the starting guess for the parameters? If you try different values for the starting guess, do we get the same values for the best parameters?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/header.ipynb b/examples/header.ipynb new file mode 100644 index 000000000..be0a4822b --- /dev/null +++ b/examples/header.ipynb @@ -0,0 +1,95 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Title" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/hiv_model.ipynb b/examples/hiv_model.ipynb new file mode 100644 index 000000000..f1abf75c3 --- /dev/null +++ b/examples/hiv_model.ipynb @@ -0,0 +1,313 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Modeling HIV infection" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "During the initial phase of HIV infection, the concentration of the virus in the bloodstream typically increases quickly and then decreases.\n", + "The most obvious explanation for the decline is an immune response that destroys the virus or controls its replication.\n", + "However, at least in some patients, the decline occurs even without any detectable immune response.\n", + "\n", + "In 1996 Andrew Phillips proposed another explanation for the decline (\"Reduction of HIV Concentration During Acute Infection: Independence from a Specific Immune Response\", available from ).\n", + "\n", + "Phillips presents a system of differential equations that models the concentrations of the HIV virus and the CD4 cells it infects.\n", + "The model does not include an immune response; nevertheless, it demonstrates behavior that is qualitatively similar to what is seen in patients during the first few weeks after infection.\n", + "\n", + "His conclusion is that the observed decline in the concentration of HIV might not be caused by an immune response; it could be due to the dynamic interaction between HIV and the cells it infects.\n", + "\n", + "In this notebook, we'll implement Phillips's model and consider whether it does the work it is meant to do." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The Model\n", + "\n", + "The model has four state variables, `R`, `L`, `E`, and `V`. Read the paper to understand what they represent.\n", + "\n", + "Here are the initial conditional we can glean from the paper. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "init = State(R=200, L=0, E=0, V=4e-7)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The behavior of the system is controlled by 9 parameters.\n", + "That might seem like a lot, but they are not entirely free parameters; their values are constrained by measurements and background knowledge (although some are more constrained than others).\n", + "Here are the values from Table 1.\n", + "\n", + "Note: the parameter $\\rho$ (the Greek letter \"rho\") in the table appears as $p$ in the equations. Since it represents a proportion, we'll use $p$." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "gamma = 1.36\n", + "mu = 1.36e-3\n", + "tau = 0.2\n", + "beta = 0.00027\n", + "p = 0.1\n", + "alpha = 3.6e-2\n", + "sigma = 2\n", + "delta = 0.33\n", + "pi = 100" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here's a `System` object with the initial conditions and the duration of the simulation (120 days).\n", + "Normally we would store the parameters in the `System` object, but the code will be less cluttered if we leave them as global variables." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "system = System(init=init,\n", + " t_end=120,\n", + " num=481)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** Use the equations in the paper to write a slope function that takes a `State` object with the current values of `R`, `L`, `E`, and `V`, and returns their derivatives in the corresponding order." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Test your slope function with the initial conditions.\n", + "The results should be approximately\n", + "\n", + "```\n", + "-2.16e-08, 2.16e-09, 1.944e-08, -8e-07\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** Now use `run_solve_ivp` to simulate the system of equations." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The next few cells plot the results on the same scale as the figures in the paper.\n", + "\n", + "**Exericise:** Compare your results to the results in the paper.\n", + "Are they consistent?" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "results.V.plot(label='V')\n", + "decorate(xlabel='Time (days)',\n", + " ylabel='Free virions V',\n", + " yscale='log',\n", + " ylim=[0.1, 1e4])" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "results.R.plot(label='R', color='C1')\n", + "decorate(xlabel='Time (days)',\n", + " ylabel='Number of cells',\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "results.L.plot(color='C2', label='L')\n", + "results.E.plot(color='C4', label='E')\n", + "decorate(xlabel='Time (days)',\n", + " ylabel='Number of cells',\n", + " yscale='log',\n", + " ylim=[0.1, 100])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** What kind of work is this model doing? Do you find the results convincing? What are the strengths of the model? What are the weaknesses?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** Read [this response to Phillips's article](https://science.sciencemag.org/content/sci/272/5270/1960.full.pdf) and Phillips's response to the response. What do you think of the arguments on both sides. Do you think the model Phillips proposed is sufficient to make his argument, or do you think it leaves out essential features of the real world?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/insulin.ipynb b/examples/insulin.ipynb new file mode 100644 index 000000000..3366b38fb --- /dev/null +++ b/examples/insulin.ipynb @@ -0,0 +1,424 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The Insulin Minimal Model" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following cells download and read the data." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSim/main/data/glucose_insulin.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "data = pd.read_csv('glucose_insulin.csv', index_col='time');" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In Chapter 17 I present the glucose minimal model; in Chapter 18 we implemented it using `run_simulation` and `run_solve_ivp`.\n", + "In this case study, we'll implement the other half of the Minimal Model, which describes the concentration of insulin.\n", + "\n", + "In the insulin minimal model, the concentration of insulin, $I$, is governed by this differential equation:\n", + "\n", + "$ \\frac{dI}{dt} = -k I(t) + \\gamma (G(t) - G_T) t $\n", + "\n", + "where $G(t)$ is the concentration of glucose at time $t$, and\n", + "$k$, $\\gamma$, and $G_T$ are positive-valued parameters:\n", + "\n", + "* $k$ controls the rate of insulin disappearance.\n", + "\n", + "* $G_T$ determines the glucose threshold: when $G(t)$ exceeds this threshold, it causes insulin to appear; when $G(t)$ is below this threshold, it causes insulin to disappear.\n", + "\n", + "* $\\gamma$ controls how quickly insulin appears or disappears when the concentration of glucose is elevated or depressed.\n", + " \n", + "Notice that this equation depends on time, $t$, since the initial injection. It has the effect of increasing glucose sensitivity over time. If you are familiar with control systems, the effect of this term is similar to the integral term in a [PID controller](https://en.wikipedia.org/wiki/PID_controller).\n", + "\n", + "In addition to the three parameters in the equation, we will also consider the initial concentration of insulin, $I_0$, to be a free parameter; that is, we will choose the value of $I_0$, and the other parameters, that best fit the data." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** Write a version of `make_system` that takes the parameters of the model (`I0`, `k`, `gamma`, and `G_T`) as parameters, along with a `DataFrame` containing the measurements, and returns a `System` object suitable for use with `run_solve_ivp`.\n", + "\n", + "Use it to make a `System` object with the following parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [], + "source": [ + "I0 = 360 \n", + "k = 0.25\n", + "gamma = 0.004\n", + "G_T = 80\n", + "\n", + "params = I0, k, gamma, G_T" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** Write a slope function that takes a time stamp, a `State` object, and a `System` object, and returns the derivative of `I` with respect to time. Test your function with the initial conditions from `system`." + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** Run `run_solve_ivp` with your `System` object and slope function, and plot the results, along with the measured insulin levels. Use the keyword argument `t_eval=data.index` so the results are evaluated as the same time stamps as the data." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** Write an error function that takes a sequence of parameters as an argument, along with the `DataFrame` containing the measurements. It should make a `System` object with the given parameters, call `run_solve_ivp`, and compute the difference between the results of the simulation and the measured values. Test your error function by calling it with the parameters from the previous exercise.\n", + "\n", + "Hint: As we did with the glucose model, you might want to drop the first 2-3 elements from the sequence of errors." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** Use `leastsq` to find the parameters that best fit the data. Make a `System` object with those parameters, run it, and plot the results along with the measurements." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** Using the best parameters, estimate the sensitivity to glucose of the first and second phase pancreatic responsivity:\n", + "\n", + "$ \\phi_1 = \\frac{I_{max} - I_b}{k (G_0 - G_b)} $\n", + "\n", + "$ \\phi_2 = \\gamma \\times 10^4 $\n", + "\n", + "For $G_0$, use the best estimate from the glucose model, 272 mg/dL. For $G_b$ and $I_b$, use the initial measurements from the data.\n", + "For $I_{max}$ is the maximum measurement of insulin concentration." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "According to [Pacini and Bergman](https://www.researchgate.net/publication/13707725_Insulin_sensitivity_and_glucose_effectiveness_Minimal_model_analysis_of_regular_and_insulin-modified_FSIGT), here are the normal ranges for these quantities." + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": {}, + "outputs": [], + "source": [ + "phi_1_interval = 2, 4\n", + "phi_1_interval" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": {}, + "outputs": [], + "source": [ + "phi_2_interval = 20, 35\n", + "phi_2_interval" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Do your estimates fall in these ranges?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/kitten.ipynb b/examples/kitten.ipynb new file mode 100644 index 000000000..f9a5804c0 --- /dev/null +++ b/examples/kitten.ipynb @@ -0,0 +1,504 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Kittens" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you have used the Internet, you have probably seen videos of kittens unrolling toilet paper.\n", + "And you might have wondered how long it would take a standard kitten to unroll 47 m of paper, the length of a standard roll.\n", + "\n", + "The interactions of the kitten and the paper rolls are complex. To keep things simple, let's assume that the kitten pulls down on the free end of the roll with constant force. And let's neglect the friction between the roll and the axle.\n", + "\n", + "This diagram shows the paper roll with the force applied by the kitten, $F$, the lever arm of the force around the axis of rotation, $r$, and the resulting torque, $\\tau$.\n", + "\n", + "![Diagram of a roll of toilet paper, showing a force, lever arm, and the resulting torque.](https://github.com/AllenDowney/ModSim/raw/main/figs/kitten.png)\n", + "\n", + "Assuming that the force applied by the kitten is 0.002 N, how long would it take to unroll a standard roll of toilet paper?\n", + "\n", + "We'll use the same parameters as in Chapter 24:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "Rmin = 0.02 # m\n", + "Rmax = 0.055 # m\n", + "Mcore = 15e-3 # kg\n", + "Mroll = 215e-3 # kg\n", + "L = 47 # m\n", + "tension = 0.002 # N" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`Rmin` and `Rmax` are the minimum and maximum radius of the roll, respectively.\n", + "`Mcore` is the weight of the core (the cardboard tube at the center) and `Mroll` is the total weight of the paper.\n", + "`L` is the unrolled length of the paper.\n", + "`tension` is the force the kitten applies by pulling on the loose end of the roll (I chose this value because it yields reasonable results)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In Chapter 24 we defined $k$ to be the constant that relates a change in the radius of the roll to a change in the rotation of the roll:\n", + "\n", + "$$dr = k~d\\theta$$ \n", + "\n", + "And we derived the equation for $k$ in terms of $R_{min}$, $R_{max}$, and $L$. \n", + "\n", + "$$k = \\frac{1}{2L} (R_{max}^2 - R_{min}^2)$$\n", + "\n", + "So we can compute `k` like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "k = (Rmax**2 - Rmin**2) / 2 / L \n", + "k " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Moment of Inertia\n", + "\n", + "To compute angular acceleration, we'll need the moment of inertia for the roll.\n", + "\n", + "At you can find moments of inertia for\n", + "simple geometric shapes. I'll model the core as a \"thin cylindrical shell\", and the paper roll as a \"thick-walled cylindrical tube with open ends\".\n", + "\n", + "The moment of inertia for a thin shell is just $m r^2$, where $m$ is the mass and $r$ is the radius of the shell.\n", + "\n", + "For a thick-walled tube the moment of inertia is\n", + "\n", + "$$I = \\frac{\\pi \\rho h}{2} (r_2^4 - r_1^4)$$ \n", + "\n", + "where $\\rho$ is the density of the material, $h$ is the height of the tube (if we think of the roll oriented vertically), $r_2$ is the outer diameter, and $r_1$ is the inner diameter.\n", + "\n", + "Since the outer diameter changes as the kitten unrolls the paper, we\n", + "have to compute the moment of inertia, at each point in time, as a\n", + "function of the current radius, `r`, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "def moment_of_inertia(r):\n", + " \"\"\"Moment of inertia for a roll of toilet paper.\n", + " \n", + " r: current radius of roll in meters\n", + " \n", + " returns: moment of inertia in kg m**2\n", + " \"\"\" \n", + " Icore = Mcore * Rmin**2 \n", + " Iroll = np.pi * rho_h / 2 * (r**4 - Rmin**4)\n", + " return Icore + Iroll" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`Icore` is the moment of inertia of the core; `Iroll` is the moment of inertia of the paper.\n", + "\n", + "`rho_h` is the density of the paper in terms of mass per unit of area. \n", + "To compute `rho_h`, we compute the area of the complete roll like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "area = np.pi * (Rmax**2 - Rmin**2)\n", + "area" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And divide the mass of the roll by that area." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "rho_h = Mroll / area\n", + "rho_h" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As an example, here's the moment of inertia for the complete roll." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "moment_of_inertia(Rmax)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As `r` decreases, so does `I`. Here's the moment of inertia when the roll is empty." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "moment_of_inertia(Rmin)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The way $I$ changes over time might be more of a problem than I have made it seem. In the same way that $F = m a$ only applies when $m$ is constant, $\\tau = I \\alpha$ only applies when $I$ is constant. When $I$ varies, we usually have to use a more general version of Newton's law. However, I believe that in this example, mass and moment of inertia vary together in a way that makes the simple approach work out.\n", + "\n", + "A friend of mine who is a physicist is not convinced; nevertheless, let's proceed on the assumption that I am right." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simulation\n", + "\n", + "The state variables we'll use are\n", + "\n", + "* `theta`, the total rotation of the roll in radians, \n", + "\n", + "* `omega`, angular velocity in rad / s,\n", + "\n", + "* `r`, the radius of the roll, and\n", + "\n", + "* `y`, the length of the unrolled paper.\n", + "\n", + "Here's a `State` object with the initial conditions." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "init = State(theta=0, omega=0, y=0, r=Rmax)\n", + "init" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And here's a `System` object with the starting conditions and `t_end`." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "system = System(init=init, t_end=120)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can take it from here." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:**\n", + "\n", + "Write a slope function we can use to simulate this system. Test it with the initial conditions. The results should be approximately\n", + "\n", + "```\n", + "0.0, 0.294, 0.0, 0.0\n", + "```\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** Write an event function that stops the simulation when `y` equals `L`, that is, when the entire roll is unrolled. Test your function with the initial conditions." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now run the simulation." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And check the results." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "results.tail()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The final value of `theta` should be about 200 rotations, the same as in Chapter 24.\n", + "\n", + "The final value of `omega` should be about 63 rad/s, which is about 10 revolutions per second. That's pretty fast, but it might be plausible.\n", + "\n", + "The final value of `y` should be `L`, which is 47 m.\n", + "\n", + "The final value of `r` should be `Rmin`, which is 0.02 m.\n", + "\n", + "And the total unrolling time should be about 76 seconds, which seems plausible." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following cells plot the results.\n", + "\n", + "`theta` increases slowly at first, then accelerates." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [], + "source": [ + "results.theta.plot(color='C0', label='theta')\n", + "decorate(xlabel='Time (s)',\n", + " ylabel='Angle (rad)')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Angular velocity, `omega`, increases almost linearly at first, as constant force yields almost constant torque. Then, as the radius decreases, the lever arm decreases, yielding lower torque, but moment of inertia decreases even more, yielding higher angular acceleration." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "results.omega.plot(color='C2', label='omega')\n", + "\n", + "decorate(xlabel='Time (s)',\n", + " ylabel='Angular velocity (rad/s)')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`y` increases slowly and then accelerates." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "results.y.plot(color='C1', label='y')\n", + "\n", + "decorate(xlabel='Time (s)',\n", + " ylabel='Length (m)')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`r` decreases slowly, then accelerates." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "results.r.plot(color='C4', label='r')\n", + "\n", + "decorate(xlabel='Time (s)',\n", + " ylabel='Radius (m)')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/orbit.ipynb b/examples/orbit.ipynb new file mode 100644 index 000000000..32d03430d --- /dev/null +++ b/examples/orbit.ipynb @@ -0,0 +1,379 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Orbiting the Sun" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In a previous example, we modeled the interaction between the Earth and the Sun, simulating what would happen if the Earth stopped in its orbit and fell straight into the Sun.\n", + "\n", + "Now let's extend the model to two dimensions and simulate one revolution of the Earth around the Sun, that is, one year.\n", + "\n", + "At perihelion, the distance from the Earth to the Sun is 147.09 million km and its velocity is 30,290 m/s." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "r_0 = 147.09e9 # initial distance m\n", + "v_0 = 30.29e3 # initial velocity m/s" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here are the other constants we'll need, all with about 4 significant digits." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "G = 6.6743e-11 # gravitational constant N / kg**2 * m**2\n", + "m1 = 1.989e30 # mass of the Sun kg\n", + "m2 = 5.972e24 # mass of the Earth kg\n", + "t_end = 3.154e7 # one year in seconds" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** Put the initial conditions in a `State` object with variables `x`, `y`, `vx`, and `vy`.\n", + "Create a `System` object with variables `init` and `t_end`." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** Write a function called `universal_gravitation` that takes a `State` and a `System` and returns the gravitational force of the Sun on the Earth as a `Vector`.\n", + "\n", + "Test your function with the initial conditions; the result should be a Vector with approximate components:\n", + "\n", + "```\n", + "x -3.66e+22\n", + "y 0\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** Write a slope function that takes a timestamp, a `State`, and a `System` and computes the derivatives of the state variables.\n", + "\n", + "Test your function with the initial conditions. The result should be a sequence of four values, approximately\n", + "\n", + "```\n", + "0.0, -30290.0, -0.006, 0.0\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** Use `run_solve_ivp` to run the simulation.\n", + "Save the return values in variables called `results` and `details`." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can use the following function to plot the results." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib.pyplot import plot\n", + "\n", + "def plot_trajectory(results):\n", + " x = results.x / 1e9\n", + " y = results.y / 1e9\n", + "\n", + " make_series(x, y).plot(label='orbit')\n", + " plot(0, 0, 'yo')\n", + "\n", + " decorate(xlabel='x distance (million km)',\n", + " ylabel='y distance (million km)')" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "plot_trajectory(results)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You will probably see that the earth does not end up back where it started, as we expect it to after one year.\n", + "The following cells compute the error, which is the distance between the initial and final positions." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "error = results.iloc[-1] - system.init\n", + "error" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "offset = Vector(error.x, error.y)\n", + "vector_mag(offset) / 1e9" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The problem is that the algorithm used by `run_solve_ivp` does not work very well with systems like this.\n", + "There are two ways we can improve it.\n", + "\n", + "`run_solve_ivp` takes a keyword argument, `rtol`, that specifies the \"relative tolerance\", which determines the size of the time steps in the simulation. Lower values of `rtol` require smaller steps, which yield more accurate results.\n", + "The default value of `rtol` is `1e-3`. \n", + "\n", + "**Exercise:** Try running the simulation again with smaller values, like `1e-4` or `1e-5`, and see what effect it has on the magnitude of `offset`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The other way to improve the results is to use a different algorithm. `run_solve_ivp` takes a keyword argument, `method`, that specifies which algorithm it should use. The default is `RK45`, which is a good, general-purpose algorithm, but not particularly good for this system. One of the other options is `RK23`, which is usually less accurate than `RK45` (with the same step size), but for this system it turns out to be unreasonably good, [for reasons I don't entirely understand](https://mathoverflow.net/questions/314940/celestial-mechanics-and-runge-kutta-methods).\n", + "Yet another option is 'DOP853', which is particularly good when `rtol` is small.\n", + "\n", + "**Exercise:** Run the simulation with one of these methods and see what effect it has on the results. To get a sense of how efficient the methods are, display `details.nfev`, which is the number of times `run_solve_ivp` called the slope function." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "details.nfev" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Animation\n", + "\n", + "You can use the following draw function to animate the results, if you want to see what the orbit looks like (not in real time)." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "xlim = results.x.min(), results.x.max()\n", + "ylim = results.y.min(), results.y.max()\n", + "\n", + "def draw_func(t, state):\n", + " x, y, vx, vy = state\n", + " plot(x, y, 'b.')\n", + " plot(0, 0, 'yo')\n", + " decorate(xlabel='x distance (million km)',\n", + " ylabel='y distance (million km)',\n", + " xlim=xlim,\n", + " ylim=ylim)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "# animate(results, draw_func)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.1" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/plague.ipynb b/examples/plague.ipynb new file mode 100644 index 000000000..6c55ca6ff --- /dev/null +++ b/examples/plague.ipynb @@ -0,0 +1,623 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "combined-semiconductor", + "metadata": {}, + "source": [ + "# The Freshman Plague" + ] + }, + { + "cell_type": "markdown", + "id": "imported-table", + "metadata": { + "tags": [] + }, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "electoral-turkey", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "formal-context", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "progressive-typing", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", + "\n", + "from modsim import *" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "undefined-miller", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "download('https://github.com/AllenDowney/ModSimPy/raw/master/' +\n", + " 'chap11.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "growing-sperm", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import code from previous notebooks\n", + "\n", + "from chap11 import make_system\n", + "from chap11 import update_func\n", + "from chap11 import run_simulation" + ] + }, + { + "cell_type": "markdown", + "id": "plastic-trigger", + "metadata": {}, + "source": [ + "[Click here to run this case study on Colab](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/examples/plague.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "complex-renewal", + "metadata": {}, + "source": [ + "This case study picks up where Chapter 12 leaves off." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "recent-cooper", + "metadata": {}, + "outputs": [], + "source": [ + "def add_immunization(system, fraction):\n", + " system.init.s -= fraction\n", + " system.init.r += fraction" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "found-learning", + "metadata": {}, + "outputs": [], + "source": [ + "tc = 3 # time between contacts in days \n", + "tr = 4 # recovery time in days\n", + "\n", + "beta = 1 / tc # contact rate in per day\n", + "gamma = 1 / tr # recovery rate in per day\n", + "\n", + "system = make_system(beta, gamma)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "synthetic-element", + "metadata": {}, + "outputs": [], + "source": [ + "def calc_total_infected(results, system):\n", + " s_0 = results.s[0]\n", + " s_end = results.s[system.t_end]\n", + " return s_0 - s_end" + ] + }, + { + "cell_type": "markdown", + "id": "changed-capability", + "metadata": {}, + "source": [ + "## Hand washing\n", + "\n", + "Suppose you are the Dean of Student Life, and you have a budget of just \\$1200 to combat the Freshman Plague. You have two options for spending this money:\n", + "\n", + "1. You can pay for vaccinations, at a rate of \\$100 per dose.\n", + "\n", + "2. You can spend money on a campaign to remind students to wash hands\n", + " frequently.\n", + "\n", + "We have already seen how we can model the effect of vaccination. Now\n", + "let's think about the hand-washing campaign. We'll have to answer two\n", + "questions:\n", + "\n", + "1. How should we incorporate the effect of hand washing in the model?\n", + "\n", + "2. How should we quantify the effect of the money we spend on a\n", + " hand-washing campaign?\n", + "\n", + "For the sake of simplicity, let's assume that we have data from a\n", + "similar campaign at another school showing that a well-funded campaign\n", + "can change student behavior enough to reduce the infection rate by 20%.\n", + "\n", + "In terms of the model, hand washing has the effect of reducing `beta`.\n", + "That's not the only way we could incorporate the effect, but it seems\n", + "reasonable and it's easy to implement." + ] + }, + { + "cell_type": "markdown", + "id": "alpine-guest", + "metadata": {}, + "source": [ + "Now we have to model the relationship between the money we spend and the\n", + "effectiveness of the campaign. Again, let's suppose we have data from\n", + "another school that suggests:\n", + "\n", + "- If we spend \\$500 on posters, materials, and staff time, we can\n", + " change student behavior in a way that decreases the effective value of `beta` by 10%.\n", + "\n", + "- If we spend \\$1000, the total decrease in `beta` is almost 20%.\n", + "\n", + "- Above \\$1000, additional spending has little additional benefit." + ] + }, + { + "cell_type": "markdown", + "id": "agricultural-trinidad", + "metadata": {}, + "source": [ + "## Logistic function\n", + "\n", + "To model the effect of a hand-washing campaign, I'll use a [generalized logistic function](https://en.wikipedia.org/wiki/Generalised_logistic_function) (GLF), which is a convenient function for modeling curves that have a generally sigmoid shape. The parameters of the GLF correspond to various features of the curve in a way that makes it easy to find a function that has the shape you want, based on data or background information about the scenario." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "blond-armstrong", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import exp\n", + "\n", + "def logistic(x, A=0, B=1, C=1, M=0, K=1, Q=1, nu=1):\n", + " \"\"\"Computes the generalize logistic function.\n", + " \n", + " A: controls the lower bound\n", + " B: controls the steepness of the transition \n", + " C: not all that useful, AFAIK\n", + " M: controls the location of the transition\n", + " K: controls the upper bound\n", + " Q: shift the transition left or right\n", + " nu: affects the symmetry of the transition\n", + " \n", + " returns: float or array\n", + " \"\"\"\n", + " exponent = -B * (x - M)\n", + " denom = C + Q * exp(exponent)\n", + " return A + (K-A) / denom ** (1/nu)" + ] + }, + { + "cell_type": "markdown", + "id": "seven-budget", + "metadata": {}, + "source": [ + "The following array represents the range of possible spending." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "disturbed-amount", + "metadata": {}, + "outputs": [], + "source": [ + "spending = linspace(0, 1200, 21)" + ] + }, + { + "cell_type": "markdown", + "id": "supreme-nutrition", + "metadata": {}, + "source": [ + "`compute_factor` computes the reduction in `beta` for a given level of campaign spending.\n", + "\n", + "`M` is chosen so the transition happens around \\$500.\n", + "\n", + "`K` is the maximum reduction in `beta`, 20%.\n", + "\n", + "`B` is chosen by trial and error to yield a curve that seems feasible." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "otherwise-answer", + "metadata": {}, + "outputs": [], + "source": [ + "def compute_factor(spending):\n", + " \"\"\"Reduction factor as a function of spending.\n", + " \n", + " spending: dollars from 0 to 1200\n", + " \n", + " returns: fractional reduction in beta\n", + " \"\"\"\n", + " return logistic(spending, M=500, K=0.2, B=0.01)" + ] + }, + { + "cell_type": "markdown", + "id": "expected-venture", + "metadata": {}, + "source": [ + "Here's what it looks like." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "seventh-strike", + "metadata": {}, + "outputs": [], + "source": [ + "percent_reduction = compute_factor(spending) * 100\n", + "\n", + "make_series(spending, percent_reduction).plot()\n", + "\n", + "decorate(xlabel='Hand-washing campaign spending (USD)',\n", + " ylabel='Percent reduction in infection rate',\n", + " title='Effect of hand washing on infection rate')" + ] + }, + { + "cell_type": "markdown", + "id": "legal-michigan", + "metadata": {}, + "source": [ + "The result is the following function, which\n", + "takes spending as a parameter and returns `factor`, which is the factor\n", + "by which `beta` is reduced:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "obvious-congress", + "metadata": {}, + "outputs": [], + "source": [ + "def compute_factor(spending):\n", + " return logistic(spending, M=500, K=0.2, B=0.01)" + ] + }, + { + "cell_type": "markdown", + "id": "sunset-investing", + "metadata": {}, + "source": [ + "I use `compute_factor` to write `add_hand_washing`, which takes a\n", + "`System` object and a budget, and modifies `system.beta` to model the\n", + "effect of hand washing:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "polish-multiple", + "metadata": {}, + "outputs": [], + "source": [ + "def add_hand_washing(system, spending):\n", + " factor = compute_factor(spending)\n", + " system.beta *= (1 - factor)" + ] + }, + { + "cell_type": "markdown", + "id": "worthy-fellowship", + "metadata": {}, + "source": [ + "Now we can sweep a range of values for `spending` and use the simulation\n", + "to compute the effect:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "statistical-garden", + "metadata": {}, + "outputs": [], + "source": [ + "def sweep_hand_washing(spending_array):\n", + " sweep = SweepSeries()\n", + " \n", + " for spending in spending_array:\n", + " system = make_system(beta, gamma)\n", + " add_hand_washing(system, spending)\n", + " results = run_simulation(system, update_func)\n", + " sweep[spending] = calc_total_infected(results, system)\n", + " \n", + " return sweep" + ] + }, + { + "cell_type": "markdown", + "id": "clinical-surge", + "metadata": {}, + "source": [ + "Here's how we run it:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "joined-graduation", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy import linspace\n", + "\n", + "spending_array = linspace(0, 1200, 20)\n", + "infected_sweep2 = sweep_hand_washing(spending_array)" + ] + }, + { + "cell_type": "markdown", + "id": "designing-smile", + "metadata": {}, + "source": [ + "The following figure shows the result. " + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "cheap-decision", + "metadata": {}, + "outputs": [], + "source": [ + "infected_sweep2.plot()\n", + "\n", + "decorate(xlabel='Hand-washing campaign spending (USD)',\n", + " ylabel='Total fraction infected',\n", + " title='Effect of hand washing on total infections')" + ] + }, + { + "cell_type": "markdown", + "id": "selective-right", + "metadata": {}, + "source": [ + "Below \\$200, the campaign has little effect. \n", + "\n", + "At \\$800 it has a substantial effect, reducing total infections from more than 45% to about 20%. \n", + "\n", + "Above \\$800, the additional benefit is small." + ] + }, + { + "cell_type": "markdown", + "id": "lucky-boulder", + "metadata": {}, + "source": [ + "## Optimization\n", + "\n", + "Let's put it all together. With a fixed budget of \\$1200, we have to\n", + "decide how many doses of vaccine to buy and how much to spend on the\n", + "hand-washing campaign.\n", + "\n", + "Here are the parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "surrounded-copying", + "metadata": {}, + "outputs": [], + "source": [ + "num_students = 90\n", + "budget = 1200\n", + "price_per_dose = 100\n", + "max_doses = int(budget / price_per_dose)\n", + "max_doses" + ] + }, + { + "cell_type": "markdown", + "id": "expired-conditioning", + "metadata": {}, + "source": [ + "The fraction `budget/price_per_dose` might not be an integer. `int` is a\n", + "built-in function that converts numbers to integers, rounding down.\n", + "\n", + "We'll sweep the range of possible doses:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "shaped-utility", + "metadata": {}, + "outputs": [], + "source": [ + "dose_array = linrange(max_doses)" + ] + }, + { + "cell_type": "markdown", + "id": "occupational-reply", + "metadata": {}, + "source": [ + "In this example we call `linrange` with only one argument; it returns a NumPy array with the integers from 0 to `max_doses`, including both.\n", + "\n", + "Then we run the simulation for each element of `dose_array`:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "recognized-release", + "metadata": {}, + "outputs": [], + "source": [ + "def sweep_doses(dose_array):\n", + " sweep = SweepSeries()\n", + " \n", + " for doses in dose_array:\n", + " fraction = doses / num_students\n", + " spending = budget - doses * price_per_dose\n", + " \n", + " system = make_system(beta, gamma)\n", + " add_immunization(system, fraction)\n", + " add_hand_washing(system, spending)\n", + " \n", + " results = run_simulation(system, update_func)\n", + " sweep[doses] = calc_total_infected(results, system)\n", + "\n", + " return sweep" + ] + }, + { + "cell_type": "markdown", + "id": "cardiac-mitchell", + "metadata": {}, + "source": [ + "For each number of doses, we compute the fraction of students we can\n", + "immunize, `fraction` and the remaining budget we can spend on the\n", + "campaign, `spending`. Then we run the simulation with those quantities\n", + "and store the number of infections.\n", + "\n", + "The following figure shows the result." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "worth-mounting", + "metadata": {}, + "outputs": [], + "source": [ + "infected_sweep3 = sweep_doses(dose_array)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "adjusted-highlight", + "metadata": {}, + "outputs": [], + "source": [ + "infected_sweep3.plot()\n", + "\n", + "decorate(xlabel='Doses of vaccine',\n", + " ylabel='Total fraction infected',\n", + " title='Total infections vs. doses')" + ] + }, + { + "cell_type": "markdown", + "id": "dynamic-easter", + "metadata": {}, + "source": [ + "If we buy no doses of vaccine and spend the entire budget on the campaign, the fraction infected is around 19%. At 4 doses, we have \\$800 left for the campaign, and this is the optimal point that minimizes the number of students who get sick.\n", + "\n", + "As we increase the number of doses, we have to cut campaign spending,\n", + "which turns out to make things worse. But interestingly, when we get\n", + "above 10 doses, the effect of herd immunity starts to kick in, and the\n", + "number of sick students goes down again." + ] + }, + { + "cell_type": "markdown", + "id": "timely-bottom", + "metadata": {}, + "source": [ + "**Exercise:** Suppose the price of the vaccine drops to $50 per dose. How does that affect the optimal allocation of the spending?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "swiss-preview", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/population.ipynb b/examples/population.ipynb new file mode 100644 index 000000000..af67e35c8 --- /dev/null +++ b/examples/population.ipynb @@ -0,0 +1,108 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# One Queue or Two" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/data/World_population_estimates.html')" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext nb_black" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import requests\n", + "\n", + "# Fetch the data.\n", + "df = pd.read_csv(\"https://ourworldindata.org/grapher/population.csv?v=1&csvType=filtered&useColumnShortNames=true&time=1700..latest&country=~OWID_WRL\", storage_options = {'User-Agent': 'Our World In Data data fetch/1.0'})\n", + "\n", + "# Fetch the metadata\n", + "metadata = requests.get(\"https://ourworldindata.org/grapher/population.metadata.json?v=1&csvType=filtered&useColumnShortNames=true&time=1700..latest&country=~OWID_WRL\").json()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "table2.loc[1950:1980].plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/code/queue.ipynb b/examples/queue.ipynb similarity index 86% rename from code/queue.ipynb rename to examples/queue.ipynb index 63a3af330..3980cf92a 100644 --- a/code/queue.ipynb +++ b/examples/queue.ipynb @@ -4,50 +4,26 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Case Study: Queueing theory\n", - "\n", - "Copyright 2017 Allen Downey\n", - "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" + "# One Queue or Two" ] }, { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], + "cell_type": "markdown", + "metadata": {}, "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", + "This notebook presents a case study from *Modeling and Simulation in Python*. It explores a question related to queueing theory, which is the study of systems that involve waiting in lines, also known as \"queues\".\n", "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", + "Suppose you are designing the checkout area for a new store. There is room for two checkout counters and a waiting area for customers. You can make two lines, one for each counter, or one line that serves both counters.\n", "\n", - "# import functions from the modsim.py module\n", - "from modsim import *\n", + "In theory, you might expect a single line to be better, but it has some practical drawbacks: in order to maintain a single line, you would have to install rope barriers, and customers might be put off by what seems to be a longer line, even if it moves faster.\n", "\n", - "# set the random number generator\n", - "np.random.seed(7)" + "So you'd like to check whether the single line is really better and by how much. Simulation can help answer this question." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## One queue or two?\n", - "\n", - "This notebook presents a solution to an exercise from *Modeling and Simulation in Python*. It uses features from the first four chapters to answer a question related to queueing theory, which is the study of systems that involve waiting in lines, also known as \"queues\".\n", - "\n", - "Suppose you are designing the checkout area for a new store. There is room for two checkout counters and a waiting area for customers. You can make two lines, one for each counter, or one line that serves both counters.\n", - "\n", - "In theory, you might expect a single line to be better, but it has some practical drawbacks: in order to maintain a single line, you would have to install rope barriers, and customers might be put off by what seems to be a longer line, even if it moves faster.\n", - "\n", - "So you'd like to check whether the single line is really better and by how much. Simulation can help answer this question.\n", - "\n", "As we did in the bikeshare model, we'll assume that a customer is equally likely to arrive during any timestep. I'll denote this probability using the Greek letter lambda, $\\lambda$, or the variable name `lam`. The value of $\\lambda$ probably varies from day to day, so we'll have to consider a range of possibilities.\n", "\n", "Based on data from other stores, you know that it takes 5 minutes for a customer to check out, on average. But checkout times are highly variable: most customers take less than 5 minutes, but some take substantially more. A simple way to model this variability is to assume that when a customer is checking out, they have the same probability of finishing up during each time step. I'll denote this probability using the Greek letter mu, $\\mu$, or the variable name `mu`.\n", @@ -59,16 +35,47 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### One server, one queue\n", + "## One server, one queue\n", "\n", "Write a function called `make_system` that takes `lam` and `mu` as parameters and returns a `System` object with variables `lam`, `mu`, and `duration`. Set `duration`, which is the number of time steps to simulate, to 10 hours, expressed in minutes. " ] }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/modsim.py')" + ] + }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], + "source": [ + "from modsim import *" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], "source": [ "# Solution goes here" ] @@ -82,7 +89,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -93,19 +100,17 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Write an update function that takes as parameters `x`, which is the total number of customer in the store, including the one checking out; `t`, which is the number of minutes that have elapsed in the simulation, and `system`, which is a `System` object.\n", + "Write an update function that takes as parameters `x`, which is the total number of customers in the store, including the one checking out; `t`, which is the number of minutes that have elapsed in the simulation, and `system`, which is a `System` object.\n", "\n", "If there's a customer checking out, it should use `flip` to decide whether they are done. And it should use `flip` to decide if a new customer has arrived.\n", "\n", - "It should return the total number of customers at the end of the time step.\n" + "It should return the total number of customers at the end of the time step." ] }, { "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, + "execution_count": 5, + "metadata": {}, "outputs": [], "source": [ "# Solution goes here" @@ -120,7 +125,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -136,7 +141,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -147,7 +152,7 @@ " update_func: function object\n", " \"\"\"\n", " x = 0\n", - " results = TimeSeries()\n", + " results = TimeSeries(name='Queue length')\n", " results[0] = x\n", " \n", " for t in linrange(0, system.duration):\n", @@ -166,7 +171,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -186,10 +191,8 @@ }, { "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, + "execution_count": 9, + "metadata": {}, "outputs": [], "source": [ "def compute_metrics(results, system):\n", @@ -214,7 +217,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -234,7 +237,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -254,7 +257,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -270,7 +273,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -279,7 +282,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -295,11 +298,11 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ - "# W_avg = sweep.mean()" + "# Solution goes here" ] }, { @@ -319,7 +322,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -329,8 +332,9 @@ " lam_array: array of values for `lam`\n", " mu: probability of finishing a checkout\n", " \"\"\"\n", - " W = 1 / (mu - lam_array)\n", - " plot(lam_array, W, 'g-')" + " W_array = 1 / (mu - lam_array)\n", + " W_series = make_series(lam_array, W_array)\n", + " W_series.plot(style='-', label='analysis')" ] }, { @@ -342,7 +346,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -353,7 +357,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Multiple servers\n", + "## Multiple servers\n", "\n", "Now let's try the other two queueing strategies:\n", "\n", @@ -362,17 +366,15 @@ "\n", "The following figure shows the three scenarios:\n", "\n", - "![](diagrams/queue.png)\n", + "![](https://github.com/AllenDowney/ModSim/raw/main/figs/queue.png)\n", "\n", "Write an update function for one queue with two servers." ] }, { "cell_type": "code", - "execution_count": 17, - "metadata": { - "collapsed": true - }, + "execution_count": 18, + "metadata": {}, "outputs": [], "source": [ "# Solution goes here" @@ -387,7 +389,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -405,10 +407,8 @@ }, { "cell_type": "code", - "execution_count": 19, - "metadata": { - "collapsed": true - }, + "execution_count": 20, + "metadata": {}, "outputs": [], "source": [ "# Solution goes here" @@ -425,7 +425,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -434,7 +434,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -445,7 +445,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Multiple queues\n", + "## Multiple queues\n", "\n", "To simulate the scenario with two separate queues, we need two state variables to keep track of customers in each queue.\n", "\n", @@ -456,10 +456,8 @@ }, { "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": true - }, + "execution_count": 23, + "metadata": {}, "outputs": [], "source": [ "# Solution goes here" @@ -474,10 +472,8 @@ }, { "cell_type": "code", - "execution_count": 23, - "metadata": { - "collapsed": true - }, + "execution_count": 24, + "metadata": {}, "outputs": [], "source": [ "# Solution goes here" @@ -492,7 +488,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -510,7 +506,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ @@ -519,7 +515,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -528,13 +524,24 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "# Solution goes here" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Modeling and Simulation in Python*\n", + "\n", + "Copyright 2021 Allen Downey\n", + "\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, { "cell_type": "code", "execution_count": null, @@ -544,8 +551,9 @@ } ], "metadata": { + "celltoolbar": "Tags", "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -559,7 +567,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.5" + "version": "3.8.16" } }, "nbformat": 4, diff --git a/code/salmon.ipynb b/examples/salmon.ipynb similarity index 68% rename from code/salmon.ipynb rename to examples/salmon.ipynb index 3825ca7e3..58818287b 100644 --- a/code/salmon.ipynb +++ b/examples/salmon.ipynb @@ -4,32 +4,68 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Case Study: Predicting salmon returns\n", - "\n", - "This case study is based on a ModSim student project by Josh Deng and Erika Lu.\n", + "# Salmon" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Modeling and Simulation in Python*\n", "\n", - "Copyright 2017 Allen Downey\n", + "Copyright 2021 Allen Downey\n", "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": true + "tags": [] }, "outputs": [], "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", "\n", - "# import functions from the modsim.py module\n", "from modsim import *" ] }, @@ -37,7 +73,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Can we predict salmon populations?\n", + "## Can we predict salmon populations?\n", "\n", "Each year the [U.S. Atlantic Salmon Assessment Committee](https://www.nefsc.noaa.gov/USASAC/Reports/USASAC2018-Report-30-2017-Activities.pdf) reports estimates of salmon populations in oceans and rivers in the northeastern United States. The reports are useful for monitoring changes in these populations, but they generally do not include predictions.\n", "\n", @@ -45,20 +81,21 @@ "\n", "As an example, I'll use data from page 18 of the 2017 report, which provides population estimates for the Narraguagus and Sheepscot Rivers in Maine.\n", "\n", - "![USASAC_Report_2017_Page18](data/USASAC_Report_2017_Page18.png)\n", + "![USASAC_Report_2017_Page18](https://github.com/AllenDowney/ModSim/raw/main/data/USASAC_Report_2017_Page18.png)\n", "\n", - "At the end of this notebook, I make some suggestions for extracting data from a PDF document automatically, but for this example I will keep it simple and type it in.\n", + "There are tools for extracting data from a PDF document automatically, but for this example I will keep it simple and type it in.\n", "\n", "Here are the population estimates for the Narraguagus River:" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ - "pops = [2749, 2845, 4247, 1843, 2562, 1774, 1201, 1284, 1287, 2339, 1177, 962, 1176, 2149, 1404, 969, 1237, 1615, 1201];" + "pops = [2749, 2845, 4247, 1843, 2562, 1774, 1201, 1284, 1287, \n", + " 2339, 1177, 962, 1176, 2149, 1404, 969, 1237, 1615, 1201]" ] }, { @@ -70,11 +107,12 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ - "years = range(1997, 2016)" + "years = linrange(1997, 2015)\n", + "years" ] }, { @@ -86,11 +124,12 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ - "pop_series = TimeSeries(pops, index=years, dtype=float)" + "pop_series = TimeSeries(pops, index=years)\n", + "pop_series" ] }, { @@ -102,12 +141,12 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "def plot_population(series):\n", - " plot(series, label='Estimated population')\n", + " series.plot(label='Estimated population')\n", " decorate(xlabel='Year', \n", " ylabel='Population estimate', \n", " title='Narraguacus River',\n", @@ -122,18 +161,17 @@ "source": [ "## Modeling changes\n", "\n", - "To see how the population changes from year-to-year, I'll use `diff` to compute the absolute difference between each year and the next.\n", - "\n", - "`shift` adjusts the result so each change aligns with the year it happened." + "To see how the population changes from year-to-year, I'll use `diff` to compute the absolute difference between each year and the next and `shift` to align the changes with the year they happened." ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ - "abs_diffs = np.ediff1d(pop_series, np.nan)" + "abs_diffs = pop_series.diff().shift(-1)\n", + "abs_diffs" ] }, { @@ -145,43 +183,30 @@ }, { "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "rel_diffs = abs_diffs / pop_series" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Or we can use the `modsim` function `compute_rel_diff`:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ - "rel_diffs = compute_rel_diff(pop_series)" + "rel_diffs = abs_diffs / pop_series\n", + "rel_diffs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "These relative differences are observed annual net growth rates. So let's drop the `NaN` and save them." + "These relative differences are observed annual net growth rates.\n", + "So let's drop the `NaN` and save them." ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ - "rates = rel_diffs.dropna()" + "rates = rel_diffs.dropna()\n", + "rates" ] }, { @@ -193,7 +218,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -213,7 +238,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -225,14 +250,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Create a `System` object with variables `t_0`, `p_0`, `rates`, and `duration=10` years. \n", + "I'll create a `System` object with variables `t_0`, `p_0`, `rates`, and `duration=10` years. \n", "\n", "The series of observed rates is one big parameter of the model." ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -252,10 +277,8 @@ }, { "cell_type": "code", - "execution_count": 13, - "metadata": { - "collapsed": true - }, + "execution_count": 14, + "metadata": {}, "outputs": [], "source": [ "# Solution goes here" @@ -270,10 +293,8 @@ }, { "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": true - }, + "execution_count": 15, + "metadata": {}, "outputs": [], "source": [ "update_func1(p_0, t_0, system)" @@ -288,10 +309,8 @@ }, { "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": true - }, + "execution_count": 16, + "metadata": {}, "outputs": [], "source": [ "def run_simulation(system, update_func):\n", @@ -318,12 +337,12 @@ "source": [ "Use `run_simulation` to run generate a prediction for the next 10 years.\n", "\n", - "The plot your prediction along with the original data. Your prediction should pick up where the data leave off." + "Then plot your prediction along with the original data. Your prediction should pick up where the data leave off." ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -339,7 +358,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -352,7 +371,7 @@ " \"\"\"\n", " for i in range(iters):\n", " results = run_simulation(system, update_func)\n", - " plot(results, color='gray', linewidth=5, alpha=0.1)" + " results.plot(color='gray', label='', linewidth=1, alpha=0.3)" ] }, { @@ -366,7 +385,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -390,12 +409,12 @@ "source": [ "## Distribution of net changes\n", "\n", - "To describe the distribution of net changes, write a function called `run_many_simulations` that runs many simulations, saves the final populations in a `ModSimSeries`, and returns the `ModSimSeries`.\n" + "To describe the distribution of net changes, write a function called `run_many_simulations` that runs many simulations, saves the final populations in a `SweepSeries`, and returns the `SweepSeries`." ] }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -413,7 +432,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -429,7 +448,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -445,7 +464,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ @@ -462,7 +481,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -481,7 +500,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -497,7 +516,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ @@ -513,7 +532,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -548,15 +567,13 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "weights = linspace(0, 1, len(rates))\n", "weights /= sum(weights)\n", - "plot(weights)\n", - "decorate(xlabel='Index into the rates array',\n", - " ylabel='Weight')" + "weights" ] }, { @@ -568,7 +585,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -584,7 +601,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ @@ -598,22 +615,6 @@ "Write an update function that takes the weights into account." ] }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [], - "source": [ - "# Solution goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use `plot_many_simulations` to plot the results." - ] - }, { "cell_type": "code", "execution_count": 31, @@ -627,7 +628,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Use `run_many_simulations` to collect the results and `describe` to summarize the distribution of net changes." + "Use `plot_many_simulations` to plot the results." ] }, { @@ -643,7 +644,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Does the refined model have much effect on the probability of population decline?" + "Use `run_many_simulations` to collect the results and `describe` to summarize the distribution of net changes." ] }, { @@ -659,17 +660,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Extracting data from a PDF document\n", - "\n", - "The following section uses PyPDF2 to get data from a PDF document. It uses features we have not seen yet, so don't worry if it doesn't all make sense.\n", - "\n", - "The PyPDF2 package provides functions to read PDF documents and get the data.\n", - "\n", - "If you don't already have it installed, and you are using Anaconda, you can install it by running the following command in a Terminal or Git Bash:\n", - "\n", - "```\n", - "conda install -c conda-forge pypdf2\n", - "```" + "Does the refined model have much effect on the probability of population decline?" ] }, { @@ -678,231 +669,13 @@ "metadata": {}, "outputs": [], "source": [ - "import PyPDF2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The 2017 report is in the data directory." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "pdfFileObj = open('data/USASAC2018-Report-30-2017-Activities-Page11.pdf', 'rb')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `PdfFileReader` object knows how to read PDF documents." - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "pdfReader = PyPDF2.PdfFileReader(pdfFileObj)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This file contains only one page." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [], - "source": [ - "pdfReader.numPages" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`getPage` selects the only page in the document." - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [], - "source": [ - "page = pdfReader.getPage(0)\n", - "page.extractText()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function iterates through the lines on the page, removes whitespace, and ignores lines that contain only whitespace." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "def iter_page(page):\n", - " for item in page.extractText().splitlines():\n", - " item = item.strip()\n", - " if item:\n", - " yield item" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function gets the next `n` pages from the page." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [], - "source": [ - "def next_n(iterable, n):\n", - " \"\"\"Get the next n items from an iterable.\"\"\"\n", - " return [next(iterable) for i in range(n)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We skip the text at the top of the page." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [], - "source": [ - "t = iter_page(page)\n", - "discard = next_n(t, 8)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The next 7 strings are the column headings of the table." - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [], - "source": [ - "columns = next_n(t, 7)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create an empty `Dataframe` with the column headings." - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [], - "source": [ - "df = pd.DataFrame(columns=columns)\n", - "df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Get the next 19 lines of the table." - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [], - "source": [ - "for i in range(19):\n", - " year = int(next(t))\n", - " data = next_n(t, 7)\n", - " df.loc[year] = data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The last line in the table gets messed up, so I'll do that one by hand." - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [], - "source": [ - "df.loc[2017] = ['363', '663', '13', '2', '1041', '806', '235'] " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the result." - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [], - "source": [ - "df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In general, reading tables from PDF documents is fragile and error-prone. Sometimes it is easier to just type it in." + "# Solution goes here" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -916,7 +689,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.6" + "version": "3.10.13" } }, "nbformat": 4, diff --git a/code/spiderman.ipynb b/examples/spiderman.ipynb similarity index 56% rename from code/spiderman.ipynb rename to examples/spiderman.ipynb index 3b6c696af..35154f0b7 100644 --- a/code/spiderman.ipynb +++ b/examples/spiderman.ipynb @@ -4,62 +4,69 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Case study: Spider-Man\n", + "# Spider-Man" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Modeling and Simulation in Python*\n", "\n", - "Copyright 2017 Allen Downey\n", + "Copyright 2021 Allen Downey\n", "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 55, "metadata": { - "collapsed": true + "tags": [] }, "outputs": [], "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", + "# install Pint if necessary\n", "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", - "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" ] }, { - "cell_type": "markdown", - "metadata": {}, + "cell_type": "code", + "execution_count": 56, + "metadata": { + "tags": [] + }, + "outputs": [], "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", "\n", - "I'll start by getting the units we'll need from Pint." + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/modsim.py')" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 57, "metadata": { - "collapsed": true + "tags": [] }, "outputs": [], "source": [ - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram\n", - "N = UNITS.newton\n", - "degree = UNITS.degree\n", - "radian = UNITS.radian" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Spider-Man" + "# import functions from modsim\n", + "\n", + "from modsim import *" ] }, { @@ -68,17 +75,18 @@ "source": [ "In this case study we'll develop a model of Spider-Man swinging from a springy cable of webbing attached to the top of the Empire State Building. Initially, Spider-Man is at the top of a nearby building, as shown in this diagram.\n", "\n", - "![](diagrams/spiderman.png)\n", + "![Diagram of the initial state for the Spider-Man case\n", + "study.](https://github.com/AllenDowney/ModSim/raw/main/figs/spiderman.png)\n", "\n", - "The origin, `O`, is at the base of the Empire State Building. The vector `H` represents the position where the webbing is attached to the building, relative to `O`. The vector `P` is the position of Spider-Man relative to `O`. And `L` is the vector from the attachment point to Spider-Man.\n", + "The origin, `O⃗`, is at the base of the Empire State Building. The vector `H⃗` represents the position where the webbing is attached to the building, relative to `O⃗`. The vector `P⃗` is the position of Spider-Man relative to `O⃗`. And `L⃗` is the vector from the attachment point to Spider-Man.\n", "\n", - "By following the arrows from `O`, along `H`, and along `L`, we can see that \n", + "By following the arrows from `O⃗`, along `H⃗`, and along `L⃗`, we can see that \n", "\n", - "`H + L = P`\n", + "`H⃗ + L⃗ = P⃗`\n", "\n", - "So we can compute `L` like this:\n", + "So we can compute `L⃗` like this:\n", "\n", - "`L = P - H`\n", + "`L⃗ = P⃗ - H⃗`\n", "\n", "The goals of this case study are:\n", "\n", @@ -105,99 +113,72 @@ "\n", "5. The spring constant of the web is 40 N / m when the cord is stretched, and 0 when it's compressed.\n", "\n", - "Here's a `Params` object." + "Here's a `Params` object with the parameters of the system." ] }, { "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, + "execution_count": 4, + "metadata": {}, "outputs": [], "source": [ - "params = Params(height = 381 * m,\n", - " g = 9.8 * m/s**2,\n", - " mass = 75 * kg,\n", - " area = 1 * m**2,\n", - " rho = 1.2 * kg/m**3,\n", - " v_term = 60 * m / s,\n", - " length = 100 * m,\n", - " angle = (270 - 45) * degree,\n", - " k = 40 * N / m,\n", - " t_0 = 0 * s,\n", - " t_end = 30 * s)" + "params = Params(height = 381, # m,\n", + " g = 9.8, # m/s**2,\n", + " mass = 75, # kg,\n", + " area = 1, # m**2,\n", + " rho = 1.2, # kg/m**3,\n", + " v_term = 60, # m / s,\n", + " length = 100, # m,\n", + " angle = (270 - 45), # degree,\n", + " k = 40, # N / m,\n", + " t_0 = 0, # s,\n", + " t_end = 30, # s\n", + " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now here's a version of `make_system` that takes a `Params` object as a parameter.\n", - "\n", - "`make_system` uses the given value of `v_term` to compute the drag coefficient `C_d`." + "Compute the initial position" ] }, { "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, + "execution_count": 5, + "metadata": {}, "outputs": [], "source": [ - "def make_system(params):\n", - " \"\"\"Makes a System object for the given conditions.\n", + "def initial_condition(params):\n", + " \"\"\"Compute the initial position and velocity.\n", " \n", " params: Params object\n", - " \n", - " returns: System object\n", " \"\"\"\n", - " unpack(params)\n", - " \n", - " init = State(x=P_0.x, y=P_0.y, vx=V_0.x, vy=V_0.y)\n", - " C_d = 2 * mass * g / (rho * area * v_term**2) \n", + " H⃗ = Vector(0, params.height)\n", + " theta = np.deg2rad(params.angle)\n", + " x, y = pol2cart(theta, params.length)\n", + " L⃗ = Vector(x, y)\n", + " P⃗ = H⃗ + L⃗\n", " \n", - " return System(init=init, g=g, mass=mass, rho=rho,\n", - " C_d=C_d, area=area, length=length, k=k,\n", - " t_0=t_0, t_end=t_end)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Compute the initial position" + " return State(x=P⃗.x, y=P⃗.y, vx=0, vy=0)" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ - "def compute_initial_condition(params):\n", - " \"\"\"Compute the initial values of L and P.\n", - " \"\"\"\n", - " unpack(params)\n", - " H = Vector(0, height)\n", - " theta = angle.to(radian)\n", - " x, y = pol2cart(theta, length)\n", - " L_0 = Vector(x, y)\n", - " P_0 = H + L_0\n", - " V_0 = Vector(0, 0) * m/s\n", - " \n", - " params.set(P_0=P_0, V_0=V_0)" + "initial_condition(params)" ] }, { - "cell_type": "code", - "execution_count": 6, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "compute_initial_condition(params)\n", - "params.P_0" + "Now here's a version of `make_system` that takes a `Params` object as a parameter.\n", + "\n", + "`make_system` uses the given value of `v_term` to compute the drag coefficient `C_d`." ] }, { @@ -206,7 +187,20 @@ "metadata": {}, "outputs": [], "source": [ - "params.V_0" + "def make_system(params):\n", + " \"\"\"Makes a System object for the given conditions.\n", + " \n", + " params: Params object\n", + " \n", + " returns: System object\n", + " \"\"\"\n", + " init = initial_condition(params)\n", + " \n", + " mass, g = params.mass, params.g\n", + " rho, area, v_term = params.rho, params.area, params.v_term\n", + " C_d = 2 * mass * g / (rho * area * v_term**2)\n", + " \n", + " return System(params, init=init, C_d=C_d)" ] }, { @@ -219,9 +213,7 @@ { "cell_type": "code", "execution_count": 8, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "system = make_system(params)" @@ -251,17 +243,18 @@ "metadata": {}, "outputs": [], "source": [ - "def drag_force(V, system):\n", + "def drag_force(V⃗, system):\n", " \"\"\"Compute drag force.\n", " \n", - " V: velocity Vector\n", + " V⃗: velocity Vector\n", " system: `System` object\n", " \n", " returns: force Vector\n", " \"\"\"\n", - " unpack(system)\n", - " mag = rho * V.mag**2 * C_d * area / 2\n", - " direction = -V.hat()\n", + " rho, C_d, area = system.rho, system.C_d, system.area\n", + " \n", + " mag = rho * vector_mag(V⃗)**2 * C_d * area / 2\n", + " direction = -vector_hat(V⃗)\n", " f_drag = direction * mag\n", " return f_drag" ] @@ -272,7 +265,7 @@ "metadata": {}, "outputs": [], "source": [ - "V_test = Vector(10, 10) * m/s" + "V⃗_test = Vector(10, 10)" ] }, { @@ -281,7 +274,7 @@ "metadata": {}, "outputs": [], "source": [ - "drag_force(V_test, system)" + "drag_force(V⃗_test, system)" ] }, { @@ -297,23 +290,21 @@ "metadata": {}, "outputs": [], "source": [ - "def spring_force(L, system):\n", + "def spring_force(L⃗, system):\n", " \"\"\"Compute drag force.\n", " \n", - " L: Vector representing the webbing\n", + " L⃗: Vector representing the webbing\n", " system: System object\n", " \n", " returns: force Vector\n", " \"\"\"\n", - " unpack(system)\n", - " \n", - " extension = L.mag - length\n", - " if magnitude(extension) < 0:\n", + " extension = vector_mag(L⃗) - system.length\n", + " if extension < 0:\n", " mag = 0\n", " else:\n", - " mag = k * extension\n", + " mag = system.k * extension\n", " \n", - " direction = -L.hat()\n", + " direction = -vector_hat(L⃗)\n", " f_spring = direction * mag\n", " return f_spring" ] @@ -324,7 +315,7 @@ "metadata": {}, "outputs": [], "source": [ - "L_test = Vector(0, -system.length-1*m)" + "L⃗_test = Vector(0, -system.length-1)" ] }, { @@ -333,7 +324,8 @@ "metadata": {}, "outputs": [], "source": [ - "f_spring = spring_force(L_test, system)" + "f_spring = spring_force(L⃗_test, system)\n", + "f_spring" ] }, { @@ -346,12 +338,10 @@ { "cell_type": "code", "execution_count": 16, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ - "def slope_func(state, t, system):\n", + "def slope_func(t, state, system):\n", " \"\"\"Computes derivatives of the state variables.\n", " \n", " state: State (x, y, x velocity, y velocity)\n", @@ -361,20 +351,20 @@ " returns: sequence (vx, vy, ax, ay)\n", " \"\"\"\n", " x, y, vx, vy = state\n", - " unpack(system)\n", + " P⃗ = Vector(x, y)\n", + " V⃗ = Vector(vx, vy)\n", + " g, mass = system.g, system.mass\n", " \n", - " H = Vector(0, height)\n", - " P = Vector(x, y)\n", - " V = Vector(vx, vy)\n", - " L = P - H\n", + " H⃗ = Vector(0, system.height)\n", + " L⃗ = P⃗ - H⃗\n", " \n", " a_grav = Vector(0, -g)\n", - " a_spring = spring_force(L, system) / mass\n", - " a_drag = drag_force(V, system) / mass\n", + " a_spring = spring_force(L⃗, system) / mass\n", + " a_drag = drag_force(V⃗, system) / mass\n", " \n", - " a = a_grav + a_drag + a_spring\n", + " A⃗ = a_grav + a_drag + a_spring\n", " \n", - " return vx, vy, a.x, a.y" + " return V⃗.x, V⃗.y, A⃗.x, A⃗.y" ] }, { @@ -387,12 +377,10 @@ { "cell_type": "code", "execution_count": 17, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ - "slope_func(system.init, 0, system)" + "slope_func(0, system.init, system)" ] }, { @@ -405,13 +393,11 @@ { "cell_type": "code", "execution_count": 18, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ - "%time results, details = run_ode_solver(system, slope_func, max_step=0.3)\n", - "details" + "results, details = run_solve_ivp(system, slope_func)\n", + "details.message" ] }, { @@ -433,14 +419,12 @@ { "cell_type": "code", "execution_count": 19, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "def plot_position(results):\n", - " plot(results.x, label='x')\n", - " plot(results.y, label='y')\n", + " results.x.plot(label='x')\n", + " results.y.plot(label='y')\n", "\n", " decorate(xlabel='Time (s)',\n", " ylabel='Position (m)')\n", @@ -458,14 +442,12 @@ { "cell_type": "code", "execution_count": 20, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "def plot_velocity(results):\n", - " plot(results.vx, label='vx')\n", - " plot(results.vy, label='vy')\n", + " results.vx.plot(label='vx')\n", + " results.vy.plot(label='vy')\n", "\n", " decorate(xlabel='Time (s)',\n", " ylabel='Velocity (m/s)')\n", @@ -482,26 +464,26 @@ }, { "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": true - }, + "execution_count": 31, + "metadata": {}, "outputs": [], "source": [ - "def plot_trajectory(results):\n", - " plot(results.x, results.y, label='trajectory')\n", + "def plot_trajectory(results, label):\n", + " x = results.x\n", + " y = results.y\n", + " make_series(x, y).plot(label=label)\n", "\n", " decorate(xlabel='x position (m)',\n", " ylabel='y position (m)')\n", " \n", - "plot_trajectory(results)" + "plot_trajectory(results, label='trajectory')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Letting go\n", + "## Letting go\n", "\n", "Now let's find the optimal time for Spider-Man to let go. We have to run the simulation in two phases because the spring force changes abruptly when Spider-Man lets go, so we can't integrate through it.\n", "\n", @@ -510,16 +492,14 @@ }, { "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": true - }, + "execution_count": 33, + "metadata": {}, "outputs": [], "source": [ - "params1 = Params(params, t_end=9*s)\n", + "params1 = params.set(t_end=9)\n", "system1 = make_system(params1)\n", - "%time results1, details1 = run_ode_solver(system1, slope_func, max_step=0.4)\n", - "plot_trajectory(results1)" + "results1, details1 = run_solve_ivp(system1, slope_func)\n", + "plot_trajectory(results1, label='phase 1')" ] }, { @@ -531,61 +511,47 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ - "t_final = get_last_label(results1) * s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the position Vector." + "t_0 = results1.index[-1]\n", + "t_0" ] }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ - "x, y, vx, vy = get_last_value(results1)\n", - "P_0 = Vector(x, y) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And the velocity Vector." + "init = results1.iloc[-1]\n", + "init" ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 36, "metadata": {}, "outputs": [], "source": [ - "V_0 = Vector(vx, vy) * m/s" + "t_end = t_0 + 10" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Here are the parameters for Phase 2. We can turn off the spring force by setting `k=0`, so we don't have to write a new slope function." + "Here is the `System` for Phase 2. We can turn off the spring force by setting `k=0`, so we don't have to write a new slope function." ] }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ - "params2 = Params(params1, t_0=t_final, t_end=t_final+10*s, P_0=P_0, V_0=V_0, k=0)\n", - "system2 = make_system(params2)" + "system2 = system1.set(init=init, t_0=t_0, t_end=t_end, k=0)" ] }, { @@ -597,11 +563,11 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ - "def event_func(state, t, system):\n", + "def event_func(t, state, system):\n", " \"\"\"Stops when y=0.\n", " \n", " state: State object\n", @@ -610,7 +576,7 @@ " \n", " returns: height\n", " \"\"\"\n", - " x, y, vx, xy = state\n", + " x, y, vx, vy = state\n", " return y" ] }, @@ -623,11 +589,13 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ - "%time results2, details2 = run_ode_solver(system2, slope_func, events=event_func, max_step=0.4)" + "results2, details2 = run_solve_ivp(system2, slope_func, \n", + " events=event_func)\n", + "details2.message" ] }, { @@ -639,15 +607,12 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ - "plot(results1.x, results1.y, label='Phase 1')\n", - "plot(results2.x, results2.y, label='Phase 2')\n", - "\n", - "decorate(xlabel='x position (m)',\n", - " ylabel='y position (m)')" + "plot_trajectory(results1, label='phase 1')\n", + "plot_trajectory(results2, label='phase 2')" ] }, { @@ -659,33 +624,28 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ - "def run_two_phase(t_release, V_0, params):\n", + "def run_two_phase(t_release, params):\n", " \"\"\"Run both phases.\n", " \n", " t_release: time when Spider-Man lets go of the webbing\n", - " V_0: initial velocity\n", " \"\"\"\n", - " params1 = Params(params, t_end=t_release, V_0=V_0)\n", + " params1 = params.set(t_end=t_release)\n", " system1 = make_system(params1)\n", - " results1, details1 = run_ode_solver(system1, slope_func, max_step=0.4)\n", - "\n", - " t_final = get_last_label(results1) * s\n", - " x, y, vx, vy = get_last_value(results1)\n", - " P_0 = Vector(x, y) * m\n", - " V_0 = Vector(vx, vy) * m/s\n", + " results1, details1 = run_solve_ivp(system1, slope_func)\n", "\n", - " params2 = Params(params1, t_0=t_final, t_end=t_final+20*s,\n", - " P_0=P_0, V_0=V_0, k=0)\n", - " system2 = make_system(params2)\n", + " t_0 = results1.index[-1]\n", + " t_end = t_0 + 10\n", + " init = results1.iloc[-1]\n", "\n", - " results2, details2 = run_ode_solver(system2, slope_func, events=event_func, max_step=0.4)\n", + " system2 = system1.set(init=init, t_0=t_0, t_end=t_end, k=0)\n", + " results2, details2 = run_solve_ivp(system2, slope_func, \n", + " events=event_func)\n", "\n", - " results = results1.combine_first(results2)\n", - " return results" + " return pd.concat([results1, results2])" ] }, { @@ -697,231 +657,178 @@ }, { "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [], - "source": [ - "t_release = 9 * s\n", - "V_0 = Vector(0, 0) * m/s\n", - "\n", - "results = run_two_phase(t_release, V_0, params)\n", - "plot_trajectory(results)\n", - "x_final = get_last_value(results.x) * m" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Maximizing range\n", - "\n", - "To find the best value of `t_release`, we need a function that takes possible values, runs the simulation, and returns the range." - ] - }, - { - "cell_type": "code", - "execution_count": 32, + "execution_count": 44, "metadata": {}, "outputs": [], "source": [ - "def range_func(t_release, params):\n", - " V_0 = Vector(0, 0) * m/s\n", - " results = run_two_phase(t_release, V_0, params)\n", - " x_final = get_last_value(results.x) * m\n", - " return x_final" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can test it." + "t_release = 9 \n", + "results = run_two_phase(t_release, params)\n", + "plot_trajectory(results, 'trajectory')" ] }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 45, "metadata": {}, "outputs": [], "source": [ - "range_func(9*s, params)" + "x_final = results.iloc[-1].x\n", + "x_final" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "And run it for a few values." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [], - "source": [ - "for t_release in linrange(3, 15, 3) * s:\n", - " print(t_release, range_func(t_release, params))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can use `max_bounded` to find the optimum." + "### Animation\n", + "\n", + "Here's a draw function we can use to animate the results." ] }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 46, "metadata": {}, "outputs": [], "source": [ - "max_bounded(range_func, [6, 12], params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, we can run the simulation with the optimal value." + "from matplotlib.pyplot import plot\n", + "\n", + "xlim = results.x.min(), results.x.max()\n", + "ylim = results.y.min(), results.y.max()\n", + "\n", + "def draw_func(t, state):\n", + " plot(state.x, state.y, 'bo')\n", + " decorate(xlabel='x position (m)',\n", + " ylabel='y position (m)',\n", + " xlim=xlim,\n", + " ylim=ylim)" ] }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 47, "metadata": {}, "outputs": [], "source": [ - "V_0 = Vector(0, 0) * m/s\n", - "results = run_two_phase(8*s, V_0, params)\n", - "plot_trajectory(results)\n", - "x_final = get_last_value(results.x) * m" + "# animate(results, draw_func)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Taking a flying leap\n", - "\n", - "Now suppose Spider-Man can jump off the wall in any direction at a maximum speed of 20 meters per second. In what direction should he jump, and what time should he let go, to maximize the distance he travels?\n", - "\n", - "Before you go on, think about it and see what you think the optimal angle is.\n", + "## Maximizing range\n", "\n", - "Here's a new range function that takes a guess as a parameter, where `guess` is a sequence of three values: `t_release`, launch velocity, and launch angle.\n", - "\n", - "It computes `V_0`, runs the simulation, and returns the final `x` position." + "To find the best value of `t_release`, we need a function that takes possible values, runs the simulation, and returns the range." ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 48, "metadata": {}, "outputs": [], "source": [ - "def range_func2(guess, params):\n", - " t_release, velocity, theta = guess\n", - " print(t_release, velocity, theta)\n", - " \n", - " V_0 = Vector(pol2cart(theta, velocity)) * m/s\n", + "def range_func(t_release, params):\n", + " \"\"\"Compute the final value of x.\n", " \n", - " results = run_two_phase(t_release, V_0, params)\n", - " x_final = get_last_value(results.x) * m\n", - " return -x_final" + " t_release: time to release web\n", + " params: Params object\n", + " \"\"\"\n", + " results = run_two_phase(t_release, params)\n", + " x_final = results.iloc[-1].x\n", + " print(t_release, x_final)\n", + " return x_final" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We can test it with the conditions from the previous section." + "We can test it." ] }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 49, "metadata": {}, "outputs": [], "source": [ - "x0 = 8*s, 0*m/2, 0*radian\n", - "range_func2(x0, params)" + "range_func(9, params)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now we can use `minimize` to find the optimal values for `t_release`, launch velocity, and launch angle. It takes a while to run because it has to search a 3-D space." + "And run it for a few values." ] }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 50, "metadata": {}, "outputs": [], "source": [ - "guess = [8, 5, 0]\n", - "bounds = [(0,20), (0,20), (-np.pi, np.pi)]\n", - "\n", - "res = minimize(range_func2, guess, params, bounds=bounds)" + "for t_release in linrange(3, 15, 3):\n", + " range_func(t_release, params)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Here are the optimal values." + "Now we can use `maximize_scalar` to find the optimum." ] }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 51, "metadata": {}, "outputs": [], "source": [ - "t_release, velocity, theta = res.x\n", - "V_0 = Vector(pol2cart(theta, velocity))" + "bounds = [6, 12]\n", + "res = maximize_scalar(range_func, params, bounds=bounds)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "It turns out that the best angle is down and to the left. Not obvious." + "Finally, we can run the simulation with the optimal value." ] }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 53, "metadata": {}, "outputs": [], "source": [ - "V_0.mag" + "best_time = res.x\n", + "results = run_two_phase(best_time, params)\n", + "plot_trajectory(results, label='trajectory')" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 54, "metadata": {}, + "outputs": [], "source": [ - "Here's what the trajectory looks like with the optimal values." + "x_final = results.iloc[-1].x\n", + "x_final" ] }, { "cell_type": "code", - "execution_count": 42, + "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "results = run_two_phase(t_release, V_0, params)\n", - "plot_trajectory(results)\n", - "x_final = get_last_value(results.x)" - ] + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -935,7 +842,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.6" + "version": "3.8.16" } }, "nbformat": 4, diff --git a/code/throwingaxe.ipynb b/examples/throwingaxe.ipynb similarity index 56% rename from code/throwingaxe.ipynb rename to examples/throwingaxe.ipynb index 848e98562..6ca67e19f 100644 --- a/code/throwingaxe.ipynb +++ b/examples/throwingaxe.ipynb @@ -4,47 +4,78 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Modeling and Simulation in Python\n", + "# Throwing Axes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Modeling and Simulation in Python*\n", "\n", - "Case study: Throwing Axe\n", + "Copyright 2021 Allen Downey\n", "\n", - "Copyright 2017 Allen Downey\n", + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 2, "metadata": { - "collapsed": true + "tags": [] }, "outputs": [], "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", + "# download modsim.py if necessary\n", "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", + "from os.path import basename, exists\n", "\n", - "# import functions from the modsim.py module\n", - "from modsim import *" + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/modsim.py')" ] }, { - "cell_type": "markdown", - "metadata": {}, + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [] + }, + "outputs": [], "source": [ - "### Throwing axe" + "# import functions from modsim\n", + "\n", + "from modsim import *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Our favorite event at Lumberjack Competitions is axe throwing.  The axes used for this event typically weigh 1.5 to 2 kg, with handles roughly 0.7 m long.  They are thrown overhead at a target typically 6 m away and 1.5 m off the ground.  Normally, the axe makes one full rotation in the air to hit the target blade first, with the handle close to vertical.\n", + "My favorite event at Lumberjack Competitions is axe throwing.  The axes used for this event typically weigh 1.5 to 2 kg, with handles roughly 0.7 m long.  They are thrown overhead at a target typically 6 m away and 1.5 m off the ground.  Normally, the axe makes one full rotation in the air to hit the target blade first, with the handle close to vertical.\n", "\n", - "![Diagram of throwing axe](diagrams/throwingaxe1.png)" + "![Diagram of throwing axe](https://github.com/AllenDowney/ModSim/raw/main/figs/throwingaxe1.png)" ] }, { @@ -60,57 +91,47 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram\n", - "radian = UNITS.radian\n", - "\n", - "def make_system():\n", - " \"\"\"Makes a System object for the given conditions.\n", - " \n", - " returns: System with init, ...\n", - " \"\"\"\n", - " P = Vector(0, 2) * m\n", - " V = Vector(8, 4) * m/s\n", - " \n", - " init = State(x=P.x, y=P.y, theta=2, \n", - " vx=V.x, vy=V.y, omega=-7)\n", - "\n", - " t_end = 1.0 * s\n", - " \n", - " return System(init=init, t_end=t_end,\n", - " g = 9.8 * m/s**2,\n", - " mass = 1.5 * kg,\n", - " length = 0.7 * m)" + "g = 9.8 # m/s**2,\n", + "mass = 1.5 # kg,\n", + "length = 0.7 # m" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 5, "metadata": {}, + "outputs": [], "source": [ - "Let's make a `System`" + "x = 0 # m\n", + "y = 2 # m\n", + "vx = 8 # m / s\n", + "vy = 4 # m / s\n", + "theta = 2 # rad\n", + "omega = -7 # rad / s" ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ - "system = make_system()" + "init = State(x=x, y=y, \n", + " vx=vx, vy=vy, \n", + " theta=theta, omega=omega)" ] }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ - "system.init" + "system = System(init=init, t_end=1)" ] }, { @@ -122,29 +143,18 @@ }, { "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, + "execution_count": 8, + "metadata": {}, "outputs": [], "source": [ - "def slope_func(state, t, system):\n", - " \"\"\"Computes derivatives of the state variables.\n", - " \n", - " state: State (x, y, x velocity, y velocity)\n", - " t: time\n", - " system: System object with length0, m, k\n", - " \n", - " returns: sequence (vx, vy, ax, ay)\n", - " \"\"\"\n", - " x, y, theta, vx, vy, omega = state\n", - " unpack(system)\n", + "def slope_func(t, state, system):\n", + " x, y, vx, vy, theta, omega = state\n", "\n", " ax = 0\n", " ay = -g\n", " alpha = 0\n", "\n", - " return vx, vy, omega, ax, ay, alpha" + " return vx, vy, ax, ay, omega, alpha" ] }, { @@ -156,11 +166,11 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ - "slope_func(system.init, 0, system)" + "slope_func(0, system.init, system)" ] }, { @@ -172,17 +182,17 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ - "results, details = run_ode_solver(system, slope_func, max_step=0.05)\n", + "results, details = run_solve_ivp(system, slope_func)\n", "details" ] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -205,56 +215,62 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ - "plot(results.x, label='x')\n", - "plot(results.y, label='y')\n", + "def plot_position(results):\n", + " results.x.plot(label='x')\n", + " results.y.plot(label='y')\n", "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Position (m)')" + " decorate(xlabel='Time (s)',\n", + " ylabel='Position (m)')\n", + " \n", + "plot_position(results)" ] }, { - "cell_type": "code", - "execution_count": 27, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "plot(results.theta, label='theta', color='C2')\n", - "\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Angle (radian)')" + "We can plot the velocities the same way." ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 13, "metadata": {}, + "outputs": [], "source": [ - "We can plot the velocities the same way." + "def plot_velocity(results):\n", + " results.vx.plot(label='vx')\n", + " results.vy.plot(label='vy')\n", + "\n", + " decorate(xlabel='Time (s)',\n", + " ylabel='Velocity (m/s)')\n", + " \n", + "plot_velocity(results)" ] }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ - "plot(results.vx, label='vx')\n", - "plot(results.vy, label='vy')\n", + "results.theta.plot(label='theta', color='C2')\n", "\n", "decorate(xlabel='Time (s)',\n", - " ylabel='Velocity (m/s)')" + " ylabel='Angle (radian)')" ] }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ - "plot(results.omega, label='omega', color='C2')\n", + "results.omega.plot(label='omega', color='C2')\n", "\n", "decorate(xlabel='Time (s)',\n", " ylabel='Angular velocity (rad/s)')" @@ -269,80 +285,92 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ - "plot(results.x, results.y, label='trajectory', color='C3')\n", - "\n", - "decorate(xlabel='x position (m)',\n", - " ylabel='y position (m)')" + "def plot_trajectory(results):\n", + " x = results.x\n", + " y = results.y\n", + " make_series(x, y).plot(label='trajectory')\n", + " \n", + " decorate(xlabel='x position (m)',\n", + " ylabel='y position (m)')\n", + " \n", + "plot_trajectory(results)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Exercises\n", + "## Animation\n", "\n", - "**Exercise:** Find the starting conditions that make the final height of the COG as close as possible to 1.5 m. Ideally, the final angle should be a little past vertical." + "Animating this system is a little more complicated, if we want to show the shape and orientation of the axe.\n", + "\n", + "It is useful to construct a frame with $\\hat{r}$ along the handle of the axe and $\\hat{\\theta}$ perpendicular." ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 17, "metadata": {}, + "outputs": [], "source": [ - "**Exercise:** Compute the total velocity of the leading edge of the axe at the point of impact, that is, the sum of velocity due to translation and rotation." + "def make_frame(theta):\n", + " x, y = pol2cart(theta, 1)\n", + " rhat = Vector(x, y)\n", + " that = vector_perp(rhat)\n", + " return rhat, that" ] }, { "cell_type": "code", - "execution_count": 34, - "metadata": { - "collapsed": true - }, + "execution_count": 25, + "metadata": {}, "outputs": [], "source": [ - "def make_frame(theta):\n", - " rhat = Vector(pol2cart(theta, 1))\n", - " that = rhat.perp()\n", - " return rhat, that" + "theta = 1\n", + "rhat, that = make_frame(theta)" ] }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ - "# quick, get those state variables into Vector objects!\n", - "state = get_last_value(results)\n", - "x, y, theta, vx, vy, omega = state\n", - "P = Vector(x, y)\n", - "V = Vector(vx, vy)\n", - "rhat, that = make_frame(theta)" + "rhat" ] }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ - "# Solution goes here" + "that" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 28, "metadata": {}, + "outputs": [], "source": [ - "## Animation\n", - "\n", - "NOTE: This section needs to be updated.\n", - "\n", - "Animating this system is a little more complicated, if we want to show the shape and orientation of the axe.\n", - "\n", - "It is useful to construct a frame with $\\hat{r}$ along the handle of the axe and $\\hat{\\theta}$ perpendicular." + "np.dot(rhat, that)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "O = Vector(0, 0)\n", + "plot_segment(O, rhat)\n", + "plot_segment(O, that)\n", + "plt.axis('equal');" ] }, { @@ -351,41 +379,41 @@ "source": [ "Now we're ready to animate the results. The following figure shows the frame and the labeled points A, B, C, and D.\n", "\n", - "![Diagram of the axe with reference frame](diagrams/throwingaxe2.png)" + "![Diagram of the axe with reference frame](https://github.com/AllenDowney/ModSim/raw/main/figs//throwingaxe2.png)" ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 60, "metadata": {}, "outputs": [], "source": [ - "decorate(xlabel='x position (m)',\n", - " ylabel='y position (m)',\n", - " xlim=[0, 8.1],\n", - " ylim=[0, 5.5],\n", - " legend=False)\n", - "\n", - "\n", + "l1 = 0.6\n", + "l2 = 0.1\n", "\n", - "for t, state in system.results.iterrows():\n", - " x, y, theta, vx, vy, omega = state\n", + "def draw_func(t, state): \n", + " x, y, vx, vy, theta, omega = state\n", " P = Vector(x, y)\n", " rhat, that = make_frame(theta)\n", " \n", " # plot the handle\n", " A = P - l1 * rhat\n", " B = P + l2 * rhat\n", - " plot_segment(A, B, color='red', update=True)\n", + " plot_segment(A, B, color='red')\n", "\n", " # plot the axe head\n", " C = B + l2 * that\n", " D = B - l2 * that\n", - " plot_segment(C, D, color='black', linewidth=10, update=True)\n", + " plot_segment(C, D, color='black', linewidth=10)\n", "\n", " # plot the COG\n", - " plot(x, y, 'bo', update=True)\n", - " sleep(0.01)" + " plt.plot(x, y, 'bo')\n", + "\n", + " decorate(xlabel='x position (m)',\n", + " ylabel='y position (m)',\n", + " xlim=[-0.3, 8.2],\n", + " ylim=[0, 5]\n", + " )" ] }, { @@ -399,28 +427,42 @@ }, { "cell_type": "code", - "execution_count": 15, - "metadata": { - "scrolled": false - }, + "execution_count": 61, + "metadata": {}, "outputs": [], "source": [ - "state" + "draw_func(0, system.init)" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [], + "source": [ + "animate(results, draw_func)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercises\n", + "\n", + "**Exercise:** Find the starting conditions that make the final height of the COG as close as possible to 1.5 m. Ideally, the final angle should be a little past vertical." ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -434,7 +476,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.6" + "version": "3.8.16" } }, "nbformat": 4, diff --git a/code/trees.ipynb b/examples/trees.ipynb similarity index 81% rename from code/trees.ipynb rename to examples/trees.ipynb index 693c869a7..9faf211df 100644 --- a/code/trees.ipynb +++ b/examples/trees.ipynb @@ -4,26 +4,68 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Modeling and Simulation in Python\n", + "# Trees" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Modeling and Simulation in Python*\n", "\n", - "Copyright 2018 Allen Downey\n", + "Copyright 2021 Allen Downey\n", "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)" + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" ] }, { "cell_type": "code", "execution_count": 1, - "metadata": {}, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# install Pint if necessary\n", + "\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", "\n", - "# import functions from the modsim.py module\n", "from modsim import *" ] }, @@ -31,20 +73,20 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Modeling tree growth\n", + "## Modeling tree growth\n", "\n", "This case study is based on \"[Height-Age Curves for Planted Stands of Douglas Fir, with Adjustments for Density](http://www.cfr.washington.edu/research.smc/working_papers/smc_working_paper_1.pdf)\", a working paper by Flewelling, Collier, Gonyea, Marshall, and Turnblom.\n", "\n", "It provides \"site index curves\", which are curves that show the expected height of the tallest tree in a stand of Douglas firs as a function of age, for a stand where the trees are the same age.\n", "\n", - "Depending on the quality of the site, the trees might grow more quickly or slowing. So each curve is identified by a \"site index\" that indicates the quality of the site.\n", + "Depending on the quality of the site, the trees might grow more quickly or slowly. So each curve is identified by a \"site index\" that indicates the quality of the site.\n", "\n", "I'll start with some of the data from their Table 1. Here's the sequence of ages." ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -61,7 +103,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -80,7 +122,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -99,11 +141,11 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ - "site85 = Series([1.4, 1.8, 2.71, 4.09, 5.92, 10.73, 16.81, \n", + "site85 = TimeSeries([1.4, 1.8, 2.71, 4.09, 5.92, 10.73, 16.81, \n", " 34.03, 51.26, 68.54, 85, 100.34, 114.33,\n", " 126.91, 138.06, 147.86, 156.39, 163.76, 170.10],\n", " index=years)" @@ -118,17 +160,15 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ - "plot(site85, label='SI 85')\n", - "plot(site65, label='SI 65')\n", - "plot(site45, label='SI 45')\n", + "site85.plot(label='SI 85')\n", + "site65.plot(label='SI 65')\n", + "site45.plot(label='SI 45')\n", "decorate(xlabel='Time (years)',\n", - " ylabel='Height (feet)')\n", - "\n", - "savefig('figs/trees-fig01.pdf')" + " ylabel='Height (feet)')" ] }, { @@ -140,62 +180,62 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ - "data = site65;" + "data = site65" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Model 1\n", + "## Model 1\n", "\n", "As a starting place, let's assume that the ability of the tree to gain mass is limited by the area it exposes to sunlight, and that the growth rate (in mass) is proportional to that area. In that case we can write:\n", "\n", - "$ m_{n+1} = m_n + \\alpha A$\n", + "$$ m_{n+1} = m_n + \\alpha A$$\n", "\n", - "where $m_n$ is the mass of the at time step $n$, $A$ is the area exposed to sunlight, and $\\alpha$ is an unknown growth parameter.\n", + "where $m_n$ is the mass of the tree at time step $n$, $A$ is the area exposed to sunlight, and $\\alpha$ is an unknown growth parameter.\n", "\n", "To get from $m$ to $A$, I'll make the additional assumption that mass is proportional to height raised to an unknown power:\n", "\n", - "$ m = \\beta h^D $\n", - "\n", - "where $h$ is height, $\\beta$ is an unknown constant of proportionality, and $D$ is the dimension that relates height and mass. \n", + "$$ m = \\beta h^D $$\n", "\n", - "We'll start by assuming $D=3$, but we'll revisit that assumption.\n", + "where $h$ is height, $\\beta$ is an unknown constant of proportionality, and $D$ is the dimension that relates height and mass. We'll start by assuming $D=3$, but we'll revisit that assumption.\n", "\n", "Finally, we'll assume that area is proportional to height squared:\n", "\n", - "$ A = \\gamma h^2$\n", + "$$ A = \\gamma h^2$$\n", "\n", "I'll specify height in feet, and choose units for mass and area so that $\\beta=1$ and $\\gamma=1$.\n", "\n", "Putting all that together, we can write a difference equation for height:\n", "\n", - "$ h_{n+1}^D = h_n^D + \\alpha h_n^2 $\n", + "$$ h_{n+1}^D = h_n^D + \\alpha h_n^2 $$\n", "\n", - "Now let's solve it. Here's a system object with the parameters and initial conditions." + "Now let's simulate this system. Here's a system object with the parameters and initial conditions." ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "alpha = 7\n", "dim = 3\n", "\n", - "t_0 = get_first_label(data)\n", - "t_end = get_last_label(data)\n", - "\n", - "h_0 = get_first_value(data)\n", + "t_0 = data.index[0]\n", + "h_0 = data[t_0]\n", + "t_end = data.index[-1]\n", "\n", - "system = System(alpha=alpha, dim=dim, \n", - " h_0=h_0, t_0=t_0, t_end=t_end)" + "system = System(alpha=alpha, \n", + " dim=dim, \n", + " h_0=h_0, \n", + " t_0=t_0, \n", + " t_end=t_end)" ] }, { @@ -207,7 +247,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -228,12 +268,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Test the update function with the initial conditions." + "I'll test the update function with the initial conditions." ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -249,7 +289,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -264,7 +304,7 @@ " results = TimeSeries()\n", " results[system.t_0] = system.h_0\n", " \n", - " for t in linrange(system.t_0, system.t_end):\n", + " for t in linrange(system.t_0, system.t_end-1):\n", " results[t+1] = update_func(results[t], t, system)\n", " \n", " return results" @@ -279,7 +319,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -291,18 +331,18 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Plot the results:" + "Here's what the results look like:" ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "def plot_results(results, data):\n", - " plot(results, ':', label='model', color='gray')\n", - " plot(data, label='data')\n", + " results.plot(style=':', label='model', color='gray')\n", + " data.plot(label='data')\n", " decorate(xlabel='Time (years)',\n", " ylabel='Height (feet)')\n", " \n", @@ -313,16 +353,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The result converges to a straight line.\n", + "The model converges to a straight line.\n", "\n", "I chose the value of `alpha` to fit the data as well as I could, but it is clear that the data have curvature that's not captured by the model.\n", "\n", - "Here are the errors:" + "Here are the errors, that is, the differences between the model and the data." ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -339,12 +379,12 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "def mean_abs_error(results, data):\n", - " return np.mean(np.abs(results-data))\n", + " return (results-data).abs().mean()\n", "\n", "mean_abs_error(results, data)" ] @@ -374,7 +414,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Model 2" + "## Model 2" ] }, { @@ -388,26 +428,24 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "alpha = 7\n", - "dim = 2.8\n", - "\n", - "params = alpha, dim" + "dim = 2.5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "I'll wrap the code from the previous section is a function that takes the parameters as inputs and makes a `System` object." + "I'll wrap the code from the previous section in a function that takes the parameters as inputs and makes a `System` object." ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -421,10 +459,9 @@ " \"\"\"\n", " alpha, dim = params\n", " \n", - " t_0 = get_first_label(data)\n", - " t_end = get_last_label(data)\n", - "\n", - " h_0 = get_first_value(data)\n", + " t_0 = data.index[0]\n", + " t_end = data.index[-1]\n", + " h_0 = data[t_0]\n", "\n", " return System(alpha=alpha, dim=dim, \n", " h_0=h_0, t_0=t_0, t_end=t_end)" @@ -439,10 +476,11 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ + "params = alpha, dim\n", "system = make_system(params, data)" ] }, @@ -455,7 +493,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -463,12 +501,12 @@ " params = alpha, dim\n", " system = make_system(params, data)\n", " results = run_simulation(system, update)\n", - " plot(results, ':', color='gray')" + " results.plot(style=':', color='gray', label='_nolegend')" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -479,23 +517,23 @@ "run_and_plot(15.5, 3.2, data)\n", "run_and_plot(38, 3.4, data)\n", "\n", - "plot(data, label='data')\n", + "data.plot(label='data')\n", "decorate(xlabel='Time (years)',\n", - " ylabel='Height (feet)')" + " ylabel='Height (feet)')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "To find the parameters that best fit the data, I'll use `fit_leastsq`.\n", + "To find the parameters that best fit the data, I'll use `leastsq`.\n", "\n", "We need an error function that takes parameters and returns errors:" ] }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ @@ -523,7 +561,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -534,17 +572,25 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now we can pass `error_func` to `fit_leastsq`, which finds the parameters that minimize the squares of the errors." + "Now we can pass `error_func` to `leastsq`, which finds the parameters that minimize the squares of the errors." ] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "best_params, details = leastsq(error_func, params, data, update)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ - "best_params, details = fit_leastsq(error_func, params, data, update)\n", - "details" + "print(details.success)" ] }, { @@ -556,13 +602,12 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "system = make_system(best_params, data)\n", "results = run_simulation(system, update)\n", - "\n", "plot_results(results, data)" ] }, @@ -575,7 +620,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -595,7 +640,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Model 3" + "## Model 3" ] }, { @@ -619,7 +664,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ @@ -639,7 +684,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ @@ -653,10 +698,9 @@ " \"\"\"\n", " alpha, dim, K = params\n", " \n", - " t_0 = get_first_label(data)\n", - " t_end = get_last_label(data)\n", - "\n", - " h_0 = get_first_value(data)\n", + " t_0 = data.index[0]\n", + " t_end = data.index[-1]\n", + " h_0 = data[t_0]\n", "\n", " return System(alpha=alpha, dim=dim, K=K, \n", " h_0=h_0, t_0=t_0, t_end=t_end)" @@ -671,7 +715,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ @@ -687,7 +731,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -713,7 +757,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -724,12 +768,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now we can test the error function with the new update function." + "And we'll test the error function with the new update function." ] }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ @@ -740,17 +784,25 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "And search for the best parameters." + "Now let's search for the best parameters." ] }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 36, + "metadata": {}, + "outputs": [], + "source": [ + "best_params, details = leastsq(error_func, params, data, update3)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ - "best_params, details = fit_leastsq(error_func, params, data, update3)\n", - "details" + "details.success" ] }, { @@ -762,13 +814,12 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ "system = make_system(best_params, data)\n", "results = run_simulation(system, update3)\n", - "\n", "plot_results(results, data)" ] }, @@ -776,12 +827,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "And the mean absolute error is substantually smaller." + "And the mean absolute error is substantially smaller." ] }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -792,14 +843,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The estimated fractal dimension is about 2.6, which is plausible for a tree.\n", - "\n", - "Basically, it suggests that if you double the height of the tree, the mass grows by a factor of $2^{2.6}$" + "The estimated fractal dimension is about 2.6, which is plausible; it suggests that if you double the height of the tree, the mass grows by a factor of $2^{2.6}$" ] }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ @@ -825,7 +874,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Analysis\n", + "## Analysis\n", "\n", "With some help from my colleague, John Geddes, we can do some analysis.\n", "\n", @@ -843,22 +892,38 @@ "\n", "and\n", "\n", - "(3) $m = h^D$\n", - "\n", - "Taking the derivative of the last equation yields\n", + "(3) $m = h^D$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Taking the derivative of the previous equation (3) yields\n", "\n", "(4) $\\frac{dm}{dt} = D h^{D-1} \\frac{dh}{dt}$\n", "\n", "Combining (1), (2), and (4), we can write a differential equation for $h$:\n", "\n", - "(5) $\\frac{dh}{dt} = \\frac{\\alpha}{D} h^{3-D} (1 - h/K)$\n", - "\n", + "(5) $\\frac{dh}{dt} = \\frac{\\alpha}{D} h^{3-D} (1 - h/K)$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "Now let's consider two cases:\n", "\n", "* With infinite $K$, the factor $(1 - h/K)$ approaches 1, so we have Model 2.\n", - "* With finite $K$, we have Model 3.\n", "\n", - "#### Model 2\n", + "* With finite $K$, we have Model 3." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Model 2\n", "\n", "Within Model 2, we'll consider two special cases, with $D=2$ and $D=3$.\n", "\n", @@ -872,13 +937,14 @@ "\n", "$\\frac{dh}{dt} = \\frac{\\alpha}{3}$\n", "\n", - "which yields linear growth with parameter $\\alpha/3$.\n", - "\n", - "This result explains why Model 1 is linear.\n", - "\n", - "\n", - "\n", - "#### Model 3\n", + "which yields linear growth with parameter $\\alpha/3$. This result explains why Model 1 is linear." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Model 3\n", "\n", "Within Model 3, we'll consider two special cases, with $D=2$ and $D=3$.\n", "\n", @@ -900,32 +966,10 @@ ] }, { - "cell_type": "code", - "execution_count": 63, - "metadata": {}, - "outputs": [], - "source": [ - "alpha = 10\n", - "D = 3\n", - "K = 200\n", - "params = alpha, D, K\n", - "system = make_system(params, data)\n", - "results = run_simulation(system, update3);" - ] - }, - { - "cell_type": "code", - "execution_count": 65, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "t = results.index\n", - "a = alpha/3\n", - "h = (-220) * exp(-a * t / K) + K\n", - "plot(t, h)\n", - "plot(results)\n", - "decorate(xlabel='Time (years)',\n", - " ylabel='Height (feet)')" + "**Open Exercise** Find an analytic solution when $D$ is between 2 and 3, and compare it to the data. Note: The parameters we estimated for the difference equation might not be right for the differential equation." ] }, { @@ -950,7 +994,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -964,7 +1008,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.5" + "version": "3.8.16" } }, "nbformat": 4, diff --git a/code/wall.ipynb b/examples/wall.ipynb similarity index 51% rename from code/wall.ipynb rename to examples/wall.ipynb index 37cd7571b..d65831563 100644 --- a/code/wall.ipynb +++ b/examples/wall.ipynb @@ -4,27 +4,69 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Modeling and Simulation in Python\n", - "\n", + "# Thermal behavior of a wall" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Modeling and Simulation in Python*\n", "\n", - "Copyright 2018 Allen Downey\n", + "Copyright 2021 Allen Downey\n", "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" ] }, { "cell_type": "code", "execution_count": 1, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", + "# install Pint if necessary\n", "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSimPy/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", "\n", - "# import functions from the modsim.py module\n", "from modsim import *" ] }, @@ -32,14 +74,17 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Inferring thermal resistance and thermal mass of a wall\n", - "\n", "This case study is based on Gori, Marincioni, Biddulph, Elwell, \"Inferring the thermal resistance and effective thermal mass distribution of a wall from in situ measurements to characterise heat transfer at both the interior and exterior surfaces\", *Energy and Buildings*, Volume 135, 15 January 2017, Pages 398-409, [which I downloaded here](https://www.sciencedirect.com/science/article/pii/S0378778816313056).\n", " \n", "The authors put their paper under a Creative Commons license, and [make their data available here](http://discovery.ucl.ac.uk/1526521). I thank them for their commitment to open, reproducible science, which made this case study possible.\n", "\n", - "The goal of their paper is to model the thermal behavior of a wall as a step toward understanding the \"performance gap between the expected energy use of buildings and their measured energy use\". The wall they study is identified as the exterior wall of an office building in central London, [not unlike this one](https://www.google.com/maps/@51.5269375,-0.1303666,3a,75y,90h,88.17t/data=!3m6!1e1!3m4!1sAoAXzN0mbGF9acaVEgUdDA!2e0!7i13312!8i6656).\n", - "\n", + "The goal of their paper is to model the thermal behavior of a wall as a step toward understanding the \"performance gap between the expected energy use of buildings and their measured energy use\". The wall they study is identified as the exterior wall of an office building in central London, [not unlike this one](https://www.google.com/maps/@51.5269375,-0.1303666,3a,75y,90h,88.17t/data=!3m6!1e1!3m4!1sAoAXzN0mbGF9acaVEgUdDA!2e0!7i13312!8i6656)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "The following figure shows the scenario and their model:\n", "\n", "![Figure 2](https://ars.els-cdn.com/content/image/1-s2.0-S0378778816313056-gr2.jpg)\n", @@ -48,57 +93,42 @@ "\n", "The primary methodology of the paper is a Bayesian method for inferring the parameters of the system (two thermal masses and three thermal resistances).\n", "\n", - "The primary result is a comparison of two models: the one shown here with two thermal masses, and a simpler model with only one thermal mass. They find that the two-mass model is able to reproduce the measured fluxes substantially better.\n", - "\n", - "Tempting as it is, I will not replicate their method for estimating the parameters. Rather, I will\n", - "\n", - "1. Implement their model and run it with their estimated parameters, to replicate the results, and \n", - "\n", - "2. Use SciPy's `leastsq` to see if I can find parameters that yield lower errors (root mean square).\n", - "\n", - "`leastsq` is a wrapper for some venerable FORTRAN code that runs [\"a modification of the Levenberg-Marquardt algorithm\"](https://www.math.utah.edu/software/minpack/minpack/lmdif.html), which is one of my favorites ([really](http://allendowney.com/research/model)).\n", - "\n", - "Implementing their model in the ModSimPy framework turns out to be straightforward. The simulations run fast enough even when we carry units through the computation. And the results are visually similar to the ones in the original paper.\n", - "\n", - "I find that `leastsq` is not able to find parameters that yield substantially better results, which suggest that the estimates in the paper are at least locally optimal.\n" + "The primary result is a comparison of two models: the one shown here with two thermal masses, and a simpler model with only one thermal mass. They find that the two-mass model is able to reproduce the measured fluxes substantially better." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Loading the data\n", - "\n", - "First I'll load the units we need from Pint." + "Tempting as it is, I will not replicate their method for estimating the parameters. Rather, I will implement their model and run it with their estimated parameters." ] }, { - "cell_type": "code", - "execution_count": 2, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "m = UNITS.meter\n", - "K = UNITS.kelvin\n", - "W = UNITS.watt\n", - "J = UNITS.joule\n", - "degC = UNITS.celsius" + "The following cells download and read the data." ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 4, "metadata": {}, + "outputs": [], "source": [ - "Read the data." + "download('https://raw.githubusercontent.com/AllenDowney/' +\n", + " 'ModSim/main/data/DataOWall.csv')" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ - "data = pd.read_csv('data/DataOWall.csv', parse_dates=[0], index_col=0, header=0, skiprows=[1,2])\n", + "data = pd.read_csv('DataOWall.csv', \n", + " parse_dates=[0], index_col=0, \n", + " header=0, skiprows=[1,2])\n", "data.head()" ] }, @@ -111,11 +141,12 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ - "timestamp_0 = get_first_label(data)" + "timestamp_0 = data.index[0]\n", + "timestamp_0" ] }, { @@ -127,11 +158,12 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ - "time_deltas = data.index - timestamp_0" + "time_deltas = data.index - timestamp_0\n", + "time_deltas.dtype" ] }, { @@ -143,7 +175,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -160,7 +192,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -171,92 +203,81 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Mark the columns of the `Dataframe` with units." + "Plot the measured fluxes." ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ - "data.Q_in.units = W / m**2\n", - "data.Q_out.units = W / m**2\n", - "data.T_int.units = degC\n", - "data.T_ext.units = degC" + "data.Q_in.plot(color='C2')\n", + "data.Q_out.plot(color='C0')\n", + "decorate(xlabel='Time (s)',\n", + " ylabel='Heat flux (W/$m^2$)')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Plot the measured fluxes." + "Plot the measured temperatures." ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ - "plot(data.Q_in, color='C2')\n", - "plot(data.Q_out, color='C0')\n", + "data.T_int.plot(color='C2')\n", + "data.T_ext.plot(color='C0')\n", "decorate(xlabel='Time (s)',\n", - " ylabel='Heat flux (W/$m^2$)')" + " ylabel='Temperature (degC)')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Plot the measured temperatures." + "## Making the System object\n", + "\n", + "`params` is a sequence with the [estimated parameters from the paper](https://www.sciencedirect.com/science/article/pii/S0378778816313056#tbl0005)." ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ - "plot(data.T_int, color='C2')\n", - "plot(data.T_ext, color='C0')\n", - "decorate(xlabel='Time (s)',\n", - " ylabel='Temperature (degC)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Params and System objects\n", - "\n", - "Here's a `Params` object with the [estimated parameters from the paper](https://www.sciencedirect.com/science/article/pii/S0378778816313056#tbl0005)." + "R1 = 0.076 # m**2 * K / W,\n", + "R2 = 0.272 # m**2 * K / W,\n", + "R3 = 0.078 # m**2 * K / W,\n", + "C1 = 212900 # J / m**2 / K,\n", + "C2 = 113100 # J / m**2 / K" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ - "params = Params(\n", - " R1 = 0.076 * m**2 * K / W,\n", - " R2 = 0.272 * m**2 * K / W,\n", - " R3 = 0.078 * m**2 * K / W,\n", - " C1 = 212900 * J / m**2 / K,\n", - " C2 = 113100 * J / m**2 / K)" + "params = R1, R2, R3, C1, C2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Pass the `Params` object `make_system`, which computes `init`, packs the parameters into `Series` objects, and computes the interpolation functions.\n" + "We'll pass `params` to `make_system`, which computes `init`, packs the parameters into `Series` objects, and computes the interpolation functions." ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -269,18 +290,16 @@ " \"\"\"\n", " R1, R2, R3, C1, C2 = params\n", " \n", - " init = State(T_C1 = Quantity(16.11, degC),\n", - " T_C2 = Quantity(15.27, degC))\n", + " init = State(T_C1 = 16.11, T_C2 = 15.27)\n", " \n", - " ts = data.index\n", - " t_end = ts[-1]\n", + " t_end = data.index[-1]\n", " \n", " return System(init=init,\n", - " R=Series([R1, R2, R3]),\n", - " C=Series([C1, C2]),\n", + " R=(R1, R2, R3),\n", + " C=(C1, C2),\n", " T_int_func=interpolate(data.T_int),\n", " T_ext_func=interpolate(data.T_ext),\n", - " t_end=t_end, ts=ts)" + " t_end=t_end)" ] }, { @@ -292,7 +311,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -308,7 +327,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -319,7 +338,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Implementing the model\n", + "## Implementing the model\n", "\n", "Next we need a slope function that takes instantaneous values of the two internal temperatures and computes their time rates of change.\n", "\n", @@ -327,7 +346,7 @@ "\n", "* When we call it directly, `state` is a `State` object and the values it contains have units.\n", "\n", - "* When `run_ode_solver` calls it, `state` is an array and the values it contains don't have units.\n", + "* When `run_solve_ivp` calls it, `state` is an array and the values it contains don't have units.\n", "\n", "In the second case, we have to apply the units before attempting the computation. `require_units` applies units if necessary:" ] @@ -341,28 +360,27 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ - "def compute_flux(state, t, system):\n", - " \"\"\"Compute the fluxes between the walls surfaces and the internal masses.\n", + "def compute_flux(t, state, system):\n", + " \"\"\"Compute the fluxes between the wall's surfaces and the internal masses.\n", " \n", " state: State with T_C1 and T_C2\n", " t: time in seconds\n", " system: System with interpolated measurements and the R Series\n", " \n", " returns: Series of fluxes\n", - " \"\"\"\n", - " # unpack the temperatures and apply units\n", + " \"\"\" \n", + " # unpack the temperatures\n", " T_C1, T_C2 = state\n", - " T_C1 = require_units(T_C1, degC)\n", - " T_C2 = require_units(T_C2, degC)\n", " \n", " # compute a series of temperatures from inside out\n", " T_int = system.T_int_func(t)\n", " T_ext = system.T_ext_func(t)\n", - " T = Series([T_int, T_C1, T_C2, T_ext])\n", + " \n", + " T = [T_int, T_C1, T_C2, T_ext]\n", " \n", " # compute differences of adjacent temperatures\n", " T_diff = np.diff(T)\n", @@ -381,11 +399,11 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ - "compute_flux(system.init, 0, system)" + "compute_flux(0, system.init, system)" ] }, { @@ -397,11 +415,11 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ - "def slope_func(state, t, system):\n", + "def slope_func(t, state, system):\n", " \"\"\"Compute derivatives of the state.\n", " \n", " state: position, velocity\n", @@ -410,7 +428,7 @@ " \n", " returns: derivatives of y and v\n", " \"\"\"\n", - " Q = compute_flux(state, t, system)\n", + " Q = compute_flux(t, state, system)\n", "\n", " # compute the net flux in each node\n", " Q_diff = np.diff(Q)\n", @@ -429,28 +447,30 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ - "slope_func(system.init, system.ts[1], system)" + "slopes = slope_func(0, system.init, system)\n", + "slopes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Run the simulation, generating estimates for the time steps in the data." + "Now let's run the simulation, generating estimates for the time steps in the data." ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ - "results, details = run_ode_solver(system, slope_func, t_eval=system.ts)\n", - "details" + "results, details = run_solve_ivp(system, slope_func,\n", + " t_eval=data.index)\n", + "details.message" ] }, { @@ -462,7 +482,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -471,15 +491,15 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "def plot_results(results, data):\n", - " plot(data.T_int, color='C2')\n", - " plot(results.T_C1, color='orange')\n", - " plot(results.T_C2, color='C1')\n", - " plot(data.T_ext, color='C0')\n", + " data.T_int.plot(color='C2')\n", + " results.T_C1.plot(color='C3')\n", + " results.T_C2.plot(color='C1')\n", + " data.T_ext.plot(color='C0')\n", " decorate(xlabel='Time (s)',\n", " ylabel='Temperature (degC)')\n", " \n", @@ -499,7 +519,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -509,18 +529,24 @@ " results: Timeframe with T_C1 and T_C2\n", " system: System object\n", " \n", - " returns: Timeframe with Qm_in and Qm_out\n", + " returns: Timeframe with Q_in and Q_out\n", " \"\"\"\n", - " Q_frame = TimeFrame(index=results.index, columns=['Qm_in', 'Qm_out'])\n", + " Q_frame = TimeFrame(index=results.index, \n", + " columns=['Q_in', 'Q_out'])\n", " \n", " for t, row in results.iterrows():\n", - " Q = compute_flux(row, t, system)\n", - " \n", - " Q_frame.row[t] = (-Q[0].magnitude, \n", - " -Q[2].magnitude)\n", + " Q = compute_flux(t, row, system)\n", + " Q_frame.loc[t] = (-Q[0], -Q[2])\n", " \n", - " return Q_frame\n", - " \n", + " return Q_frame" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ "Q_frame = recompute_fluxes(results, system)\n", "Q_frame.head()" ] @@ -534,13 +560,13 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ - "def plot_Q_in(Q_frame, data):\n", - " plot(Q_frame.Qm_in, color='gray')\n", - " plot(data.Q_in, color='C2')\n", + "def plot_Q_in(frame, data):\n", + " frame.Q_in.plot(color='gray')\n", + " data.Q_in.plot(color='C2')\n", " decorate(xlabel='Time (s)',\n", " ylabel='Heat flux (W/$m^2$)')\n", " \n", @@ -549,13 +575,13 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ - "def plot_Q_out(Q_frame, data):\n", - " plot(Q_frame.Qm_out, color='gray')\n", - " plot(data.Q_out, color='C0')\n", + "def plot_Q_out(frame, data):\n", + " frame.Q_out.plot(color='gray')\n", + " data.Q_out.plot(color='C0')\n", " decorate(xlabel='Time (s)',\n", " ylabel='Heat flux (W/$m^2$)')\n", " \n", @@ -568,319 +594,7 @@ "source": [ "These results are also similar to what's in the paper (the bottom row):\n", "\n", - "![Figure 3](https://ars.els-cdn.com/content/image/1-s2.0-S0378778816313056-gr3.jpg)\n", - "\n", - "I'll compute the array of errors, including `Q_in` and `Q_out`:" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "def compute_error(Q_frame, data):\n", - " error_Q_in = (Q_frame.Qm_in - data.Q_in).astype(float)\n", - " error_Q_out = (Q_frame.Qm_out - data.Q_out).astype(float)\n", - " return np.hstack([error_Q_in, error_Q_out])" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "errors = compute_error(Q_frame, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the root mean squared error." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "print(np.sqrt(np.mean(errors**2)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Estimating parameters" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's see if we can do any better than the parameters in the paper." - ] - }, - { - "cell_type": "code", - "execution_count": 125, - "metadata": {}, - "outputs": [], - "source": [ - "params = Params(\n", - " R1 = 0.076 * m**2 * K / W,\n", - " R2 = 0.272 * m**2 * K / W,\n", - " R3 = 0.078 * m**2 * K / W,\n", - " C1 = 212900 * J / m**2 / K,\n", - " C2 = 113100 * J / m**2 / K)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's an error function that takes a hypothetical set of parameters, runs the simulation, and returns an array of errors." - ] - }, - { - "cell_type": "code", - "execution_count": 126, - "metadata": {}, - "outputs": [], - "source": [ - "def error_func(params, data):\n", - " \"\"\"Run a simulation and return an array of errors.\n", - " \n", - " params: Params object or array\n", - " data: DataFrame\n", - " \n", - " returns: array of float\n", - " \"\"\"\n", - " print(params)\n", - " system = make_system(params, data)\n", - " results, details = run_ode_solver(system, slope_func, t_eval=system.ts)\n", - " Q_frame = recompute_fluxes(results, system)\n", - " errors = compute_error(Q_frame, data)\n", - " print('RMSE', np.sqrt(np.mean(errors**2)))\n", - " return errors" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Testing `error_func`." - ] - }, - { - "cell_type": "code", - "execution_count": 127, - "metadata": {}, - "outputs": [], - "source": [ - "errors = error_func(params, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Pass `error_func` to `fit_leastsq` to see if it can do any better." - ] - }, - { - "cell_type": "code", - "execution_count": 124, - "metadata": {}, - "outputs": [], - "source": [ - "best_params, details = fit_leastsq(error_func, params, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The search ends normally." - ] - }, - { - "cell_type": "code", - "execution_count": 123, - "metadata": {}, - "outputs": [], - "source": [ - "details" - ] - }, - { - "cell_type": "code", - "execution_count": 119, - "metadata": {}, - "outputs": [], - "source": [ - "details.mesg" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The best params are only slightly different from the starting place. " - ] - }, - { - "cell_type": "code", - "execution_count": 120, - "metadata": {}, - "outputs": [], - "source": [ - "best_params" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's what the results look like with the `best_params`." - ] - }, - { - "cell_type": "code", - "execution_count": 121, - "metadata": {}, - "outputs": [], - "source": [ - "system = make_system(best_params, data)\n", - "results, details = run_ode_solver(system, slope_func, t_eval=system.ts)\n", - "Q_frame = recompute_fluxes(results, system)\n", - "errors = compute_error(Q_frame, data)\n", - "print(np.sqrt(np.mean(errors**2)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The RMS error is only slightly smaller.\n", - "\n", - "And the results are visually similar." - ] - }, - { - "cell_type": "code", - "execution_count": 85, - "metadata": {}, - "outputs": [], - "source": [ - "plot_Q_in(Q_frame, data)" - ] - }, - { - "cell_type": "code", - "execution_count": 86, - "metadata": {}, - "outputs": [], - "source": [ - "plot_Q_out(Q_frame, data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Exercise:** Try starting the model with a different set of parameters and see if it moves toward the parameters in the paper.\n", - "\n", - "I found that no matter where I start, `fit_leastsq` doesn't move far, which suggests that it is not able to optimize the parameters effectively." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Notes\n", - "\n", - "Notes on working with degC.\n", - "\n", - "Usually I construct a `Quantity` object by multiplying a number and a unit. With degC, that doesn't work; you get `OffsetUnitCalculusError: Ambiguous operation with offset unit (degC).`" - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "metadata": {}, - "outputs": [], - "source": [ - "#16.11 * C " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The problem seems to be that it doesn't know whether you want a temperature measurement or a temperature difference.\n", - "\n", - "You can create a temperature measurement like this." - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [], - "source": [ - "T = Quantity(16.11, degC)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you convert to Kelvin, it does the right thing." - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "T.to(K)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When you subtract temperatures, the results is a temperature difference, indicated by the units." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [], - "source": [ - "diff = T - 273.15 * K" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you convert a temperature difference to Kelvin, it does the right thing." - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [], - "source": [ - "diff.to(K)" + "![Figure 3](https://ars.els-cdn.com/content/image/1-s2.0-S0378778816313056-gr3.jpg)" ] }, { @@ -893,7 +607,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -907,7 +621,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.5" + "version": "3.10.13" } }, "nbformat": 4, diff --git a/code/yoyo.ipynb b/examples/yoyo.ipynb similarity index 59% rename from code/yoyo.ipynb rename to examples/yoyo.ipynb index b9650019c..e732e41a3 100644 --- a/code/yoyo.ipynb +++ b/examples/yoyo.ipynb @@ -4,30 +4,68 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Modeling and Simulation in Python\n", - "\n", - "Case study\n", + "# Simulating a Yo-Yo" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Modeling and Simulation in Python*\n", "\n", - "Copyright 2017 Allen Downey\n", + "Copyright 2021 Allen Downey\n", "\n", - "License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0)\n" + "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": true + "tags": [] }, "outputs": [], "source": [ - "# Configure Jupyter so figures appear in the notebook\n", - "%matplotlib inline\n", + "# install Pint if necessary\n", "\n", - "# Configure Jupyter to display the assigned value after an assignment\n", - "%config InteractiveShell.ast_node_interactivity='last_expr_or_assign'\n", + "try:\n", + " import pint\n", + "except ImportError:\n", + " !pip install pint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# download modsim.py if necessary\n", + "\n", + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + " local, _ = urlretrieve(url, filename)\n", + " print('Downloaded ' + local)\n", + " \n", + "download('https://github.com/AllenDowney/ModSimPy/raw/master/modsim.py')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# import functions from modsim\n", "\n", - "# import functions from the modsim.py module\n", "from modsim import *" ] }, @@ -37,13 +75,20 @@ "source": [ "## Yo-yo\n", "\n", - "Suppose you are holding a yo-yo with a length of string wound around its axle, and you drop it while holding the end of the string stationary. As gravity accelerates the yo-yo downward, tension in the string exerts a force upward. Since this force acts on a point offset from the center of mass, it exerts a torque that causes the yo-yo to spin.\n", - "\n", - "![](diagrams/yoyo.png)\n", + "Suppose you are holding a yo-yo with a length of string wound around its axle, and you drop it while holding the end of the string stationary. As gravity accelerates the yo-yo downward, tension in the string exerts a force upward. Since this force acts on a point offset from the center of mass, it exerts a torque that causes the yo-yo to spin.\n", "\n", - "This figure shows the forces on the yo-yo and the resulting torque. The outer shaded area shows the body of the yo-yo. The inner shaded area shows the rolled up string, the radius of which changes as the yo-yo unrolls.\n", + "The following diagram shows the forces on the yo-yo and the resulting torque. The outer shaded area shows the body of the yo-yo. The inner shaded area shows the rolled up string, the radius of which changes as the yo-yo unrolls.\n", "\n", - "In this model, we can't figure out the linear and angular acceleration independently; we have to solve a system of equations:\n", + "![Diagram of a yo-yo showing forces due to gravity and tension in the\n", + "string, the lever arm of tension, and the resulting\n", + "torque.](https://github.com/AllenDowney/ModSim/raw/main/figs/yoyo.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this system, we can't figure out the linear and angular acceleration independently; we have to solve a system of equations:\n", "\n", "$\\sum F = m a $\n", "\n", @@ -55,8 +100,13 @@ "\n", "$\\frac{dy}{dt} = -r \\frac{d \\theta}{dt} $\n", "\n", - "In this example, the linear and angular accelerations have opposite sign. As the yo-yo rotates counter-clockwise, $\\theta$ increases and $y$, which is the length of the rolled part of the string, decreases.\n", - "\n", + "In this example, the linear and angular accelerations have opposite sign. As the yo-yo rotates counter-clockwise, $\\theta$ increases and $y$, which is the length of the rolled part of the string, decreases." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "Taking the derivative of both sides yields a similar relationship between linear and angular acceleration:\n", "\n", "$\\frac{d^2 y}{dt^2} = -r \\frac{d^2 \\theta}{dt^2} $\n", @@ -73,73 +123,67 @@ "\n", "$\\sum F = T - mg = ma $\n", "\n", - "Where $T$ is positive because the tension force points up, and $mg$ is negative because gravity points down.\n", - "\n", + "Where $T$ is positive because the tension force points up, and $mg$ is negative because gravity points down." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "Because gravity acts on the center of mass, it creates no torque, so the only torque is due to tension:\n", "\n", "$\\sum \\tau = T r = I \\alpha $\n", "\n", "Positive (upward) tension yields positive (counter-clockwise) angular acceleration.\n", "\n", - "Now we have three equations in three unknowns, $T$, $a$, and $\\alpha$, with $I$, $m$, $g$, and $r$ as known parameters. It is simple enough to solve these equations by hand, but we can also get SymPy to do it for us.\n", - "\n" + "Now we have three equations in three unknowns, $T$, $a$, and $\\alpha$, with $I$, $m$, $g$, and $r$ as known parameters. We could solve these equations by hand, but we can also get SymPy to do it for us." ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ - "from sympy import init_printing, symbols, Eq, solve\n", + "from sympy import symbols, Eq, solve\n", "\n", - "init_printing()" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ "T, a, alpha, I, m, g, r = symbols('T a alpha I m g r')" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ - "eq1 = Eq(a, -r * alpha)" + "eq1 = Eq(a, -r * alpha)\n", + "eq1" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ - "eq2 = Eq(T - m * g, m * a)" + "eq2 = Eq(T - m * g, m * a)\n", + "eq2" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ - "eq3 = Eq(T * r, I * alpha)" + "eq3 = Eq(T * r, I * alpha)\n", + "eq3" ] }, { "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": true - }, + "execution_count": 8, + "metadata": {}, "outputs": [], "source": [ "soln = solve([eq1, eq2, eq3], [T, a, alpha])" @@ -147,7 +191,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -156,7 +200,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -165,7 +209,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -176,7 +220,6 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "\n", "The results are\n", "\n", "$T = m g I / I^* $\n", @@ -187,34 +230,43 @@ "\n", "where $I^*$ is the augmented moment of inertia, $I + m r^2$.\n", "\n", - "You can also see [the derivation of these equations in this video](https://www.youtube.com/watch?v=chC7xVDKl4Q).\n", + "You can also see [the derivation of these equations in this video](https://www.youtube.com/watch?v=chC7xVDKl4Q)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can use these equations for $a$ and $\\alpha$ to write a slope function and simulate this system." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exercise:** Simulate the descent of a yo-yo. How long does it take to reach the end of the string?\n", "\n", - "To simulate the system, we don't really need $T$; we can plug $a$ and $\\alpha$ directly into the slope function." + "Here are the system parameters:" ] }, { "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": true - }, + "execution_count": 12, + "metadata": {}, "outputs": [], "source": [ - "radian = UNITS.radian\n", - "m = UNITS.meter\n", - "s = UNITS.second\n", - "kg = UNITS.kilogram\n", - "N = UNITS.newton" + "Rmin = 8e-3 # m\n", + "Rmax = 16e-3 # m\n", + "Rout = 35e-3 # m\n", + "mass = 50e-3 # kg\n", + "L = 1 # m\n", + "g = 9.8 # m / s**2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Exercise:** Simulate the descent of a yo-yo. How long does it take to reach the end of the string?\n", - "\n", - "I provide a `Params` object with the system parameters:\n", - "\n", "* `Rmin` is the radius of the axle. `Rmax` is the radius of the axle plus rolled string.\n", "\n", "* `Rout` is the radius of the yo-yo body. `mass` is the total mass of the yo-yo, ignoring the string. \n", @@ -226,88 +278,81 @@ }, { "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, + "execution_count": 13, + "metadata": {}, "outputs": [], "source": [ - "params = Params(Rmin = 8e-3 * m,\n", - " Rmax = 16e-3 * m,\n", - " Rout = 35e-3 * m,\n", - " mass = 50e-3 * kg,\n", - " L = 1 * m,\n", - " g = 9.8 * m / s**2,\n", - " t_end = 1 * s)" + "1 / (Rmax)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Here's a `make_system` function that computes `I` and `k` based on the system parameters.\n", - "\n", - "I estimated `I` by modeling the yo-yo as a solid cylinder with uniform density ([see here](https://en.wikipedia.org/wiki/List_of_moments_of_inertia)).\n", + "Based on these parameters, we can compute the moment of inertia for the yo-yo, modeling it as a solid cylinder with uniform density ([see here](https://en.wikipedia.org/wiki/List_of_moments_of_inertia)).\n", "\n", "In reality, the distribution of weight in a yo-yo is often designed to achieve desired effects. But we'll keep it simple." ] }, { "cell_type": "code", - "execution_count": 13, - "metadata": { - "collapsed": true - }, + "execution_count": 14, + "metadata": {}, "outputs": [], "source": [ - "def make_system(params):\n", - " \"\"\"Make a system object.\n", - " \n", - " params: Params with Rmin, Rmax, Rout, \n", - " mass, L, g, t_end\n", - " \n", - " returns: System with init, k, Rmin, Rmax, mass,\n", - " I, g, ts\n", - " \"\"\"\n", - " unpack(params)\n", - " \n", - " init = State(theta = 0 * radian,\n", - " omega = 0 * radian/s,\n", - " y = L,\n", - " v = 0 * m / s)\n", - " \n", - " I = mass * Rout**2 / 2\n", - " k = (Rmax**2 - Rmin**2) / 2 / L / radian \n", - " \n", - " return System(init=init, k=k,\n", - " Rmin=Rmin, Rmax=Rmax,\n", - " mass=mass, I=I, g=g,\n", - " t_end=t_end)" + "I = mass * Rout**2 / 2\n", + "I" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Testing `make_system`" + "And we can compute `k`, which is the constant that determines how the radius of the spooled string decreases as it unwinds." ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ - "system = make_system(params)" + "k = (Rmax**2 - Rmin**2) / 2 / L \n", + "k" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The state variables we'll use are angle, `theta`, angular velocity, `omega`, the length of the spooled string, `y`, and the linear velocity of the yo-yo, `v`.\n", + "\n", + "Here is a `State` object with the initial conditions." ] }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ - "system.init" + "init = State(theta=0, omega=0, y=L, v=0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And here's a `System` object with `init` and `t_end` (chosen to be longer than I expect for the yo-yo to drop 1 m)." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "system = System(init=init, t_end=2)" ] }, { @@ -324,15 +369,13 @@ "\n", "$ \\alpha = m g r / I^* $\n", "\n", - "where $I^*$ is the augmented moment of inertia, $I + m r^2$.\n" + "where $I^*$ is the augmented moment of inertia, $I + m r^2$." ] }, { "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, + "execution_count": 18, + "metadata": {}, "outputs": [], "source": [ "# Solution goes here" @@ -342,12 +385,17 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Test your slope function with the initial paramss." + "Test your slope function with the initial conditions.\n", + "The results should be approximately\n", + "\n", + "```\n", + "0, 180.5, 0, -2.9\n", + "```" ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -358,12 +406,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "Notice that the initial acceleration is substantially smaller than `g` because the yo-yo has to start spinning before it can fall.\n", + "\n", "Write an event function that will stop the simulation when `y` is 0." ] }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -379,9 +429,8 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 21, "metadata": { - "collapsed": true, "scrolled": false }, "outputs": [], @@ -398,9 +447,8 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 22, "metadata": { - "collapsed": true, "scrolled": false }, "outputs": [], @@ -417,7 +465,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ @@ -428,27 +476,27 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Plot the results." + "How long does it take for the yo-yo to fall 1 m? Does the answer seem reasonable?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ + "The following cells plot the results.\n", + "\n", "`theta` should increase and accelerate." ] }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ - "def plot_theta(results):\n", - " plot(results.theta, color='C0', label='theta')\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Angle (rad)')\n", - "plot_theta(results)" + "results.theta.plot(color='C0', label='theta')\n", + "decorate(xlabel='Time (s)',\n", + " ylabel='Angle (rad)')" ] }, { @@ -460,49 +508,73 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ - "def plot_y(results):\n", - " plot(results.y, color='C1', label='y')\n", + "results.y.plot(color='C1', label='y')\n", "\n", - " decorate(xlabel='Time (s)',\n", - " ylabel='Length (m)')\n", - " \n", - "plot_y(results)" + "decorate(xlabel='Time (s)',\n", + " ylabel='Length (m)')\n", + " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Plot velocity as a function of time; is the yo-yo accelerating?" + "Plot velocity as a function of time; is the acceleration constant?" ] }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ - "# Solution goes here" + "results.v.plot(label='velocity', color='C3')\n", + "\n", + "decorate(xlabel='Time (s)',\n", + " ylabel='Velocity (m/s)')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Use `gradient` to estimate the derivative of `v`. How goes the acceleration of the yo-yo compare to `g`?" + "We can use `gradient` to estimate the derivative of `v`. How does the acceleration of the yo-yo compare to `g`?" ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ - "# Solution goes here" + "a = gradient(results.v)\n", + "a.plot(label='acceleration', color='C4')\n", + "decorate(xlabel='Time (s)',\n", + " ylabel='Acceleration (m/$s^2$)')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And we can use the formula for `r` to plot the radius of the spooled thread over time." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "r = np.sqrt(2*k*results.y + Rmin**2)\n", + "r.plot(label='radius')\n", + "\n", + "decorate(xlabel='Time (s)',\n", + " ylabel='Radius of spooled thread (m)')" ] }, { @@ -515,7 +587,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -529,7 +601,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.6" + "version": "3.8.16" } }, "nbformat": 4, diff --git a/code/data/baseball_drag.png b/figs/baseball_drag.png similarity index 100% rename from code/data/baseball_drag.png rename to figs/baseball_drag.png diff --git a/figs/golden1.fig b/figs/golden1.fig new file mode 100644 index 000000000..ef10ce9ff --- /dev/null +++ b/figs/golden1.fig @@ -0,0 +1,23 @@ +#FIG 3.2 Produced by xfig version 3.2.6a +Landscape +Center +Inches +Letter +100.00 +Single +-2 +1200 2 +1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 1725 3075 38 38 1687 3075 1763 3075 +1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 4350 3450 38 38 4312 3450 4388 3450 +1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 2625 4050 38 38 2587 4050 2663 4050 +2 1 2 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 1725 3150 1725 4800 +2 1 2 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 2625 4050 2625 4800 +2 1 2 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 4350 3450 4350 4800 +2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2 + 1200 4800 4800 4800 +4 0 0 50 0 16 14 0.0000 4 135 180 4275 5100 x3\001 +4 0 0 50 0 16 14 0.0000 4 135 180 2550 5100 x2\001 +4 0 0 50 0 16 14 0.0000 4 135 180 1650 5100 x1\001 diff --git a/figs/golden1.png b/figs/golden1.png new file mode 100644 index 000000000..b0d2df712 Binary files /dev/null and b/figs/golden1.png differ diff --git a/figs/golden2.fig b/figs/golden2.fig new file mode 100644 index 000000000..8eae8714b --- /dev/null +++ b/figs/golden2.fig @@ -0,0 +1,45 @@ +#FIG 3.2 Produced by xfig version 3.2.6a +Landscape +Center +Inches +Letter +100.00 +Single +-2 +1200 2 +1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 6300 3075 38 38 6262 3075 6338 3075 +1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 8925 3450 38 38 8887 3450 8963 3450 +1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 7200 4125 38 38 7162 4125 7238 4125 +1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 7800 3975 38 38 7762 3975 7838 3975 +1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 1725 3075 38 38 1687 3075 1763 3075 +1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 4350 3450 38 38 4312 3450 4388 3450 +1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 2625 4125 38 38 2587 4125 2663 4125 +1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 3225 4275 38 38 3187 4275 3263 4275 +2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2 + 1200 4800 4800 4800 +2 1 2 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 6300 3150 6300 4800 +2 1 2 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 7200 4125 7200 4800 +2 1 2 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 8925 3450 8925 4800 +2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2 + 5775 4800 9375 4800 +2 1 2 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 7800 4050 7800 4800 +2 1 2 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 1725 3150 1725 4800 +2 1 2 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 2625 4125 2625 4800 +2 1 2 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 4350 3450 4350 4800 +2 1 2 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 3225 4275 3225 4800 +4 0 0 50 0 16 14 0.0000 4 135 180 7725 5100 x4\001 +4 0 0 50 0 16 14 0.0000 4 135 180 7125 5100 x2\001 +4 0 0 50 0 16 14 0.0000 4 135 180 8850 5100 x3\001 +4 0 0 50 0 16 14 0.0000 4 135 180 6225 5100 x1\001 +4 0 0 50 0 16 14 0.0000 4 135 180 3150 5100 x4\001 +4 0 0 50 0 16 14 0.0000 4 135 180 2550 5100 x2\001 +4 0 0 50 0 16 14 0.0000 4 135 180 4275 5100 x3\001 +4 0 0 50 0 16 14 0.0000 4 135 180 1650 5100 x1\001 diff --git a/figs/golden2.png b/figs/golden2.png new file mode 100644 index 000000000..13d96a8a3 Binary files /dev/null and b/figs/golden2.png differ diff --git a/figs/secant.fig b/figs/secant.fig new file mode 100644 index 000000000..2c9f80002 --- /dev/null +++ b/figs/secant.fig @@ -0,0 +1,19 @@ +#FIG 3.2 Produced by xfig version 3.2.6a +Landscape +Center +Inches +Letter +100.00 +Single +-2 +1200 2 +1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 1725 3075 38 38 1687 3075 1763 3075 +1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 4275 5400 38 38 4237 5400 4313 5400 +2 1 2 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 1725 3150 1725 4800 +2 1 2 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 4275 4800 4275 5400 +2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2 + 1200 4800 4800 4800 +4 0 0 50 0 16 14 0.0000 4 165 450 4425 5400 f(x2)\001 +4 0 0 50 0 16 14 0.0000 4 165 450 1875 3225 f(x1)\001 diff --git a/figs/secant.png b/figs/secant.png new file mode 100644 index 000000000..01a2594ce Binary files /dev/null and b/figs/secant.png differ diff --git a/jb/_config.yml b/jb/_config.yml new file mode 100644 index 000000000..15b91be1d --- /dev/null +++ b/jb/_config.yml @@ -0,0 +1,18 @@ +# Book settings +title: Modeling and Simulation in Python +author: Allen B. Downey + +latex: + latex_documents: + targetname: book.tex + +execute: + execute_notebooks: 'off' + +repository: + url: https://github.com/AllenDowney/ModSimPy +html: + use_repository_button: true + +parse: + myst_extended_syntax: true diff --git a/jb/_toc.yml b/jb/_toc.yml new file mode 100644 index 000000000..50015db58 --- /dev/null +++ b/jb/_toc.yml @@ -0,0 +1,29 @@ +file: index +sections: +- file: preface +- file: chap01 +- file: chap02 +- file: chap03 +- file: chap04 +- file: chap05 +- file: chap06 +- file: chap07 +- file: chap08 +- file: chap09 +- file: chap10 +- file: chap11 +- file: chap12 +- file: chap13 +- file: chap14 +- file: chap15 +- file: chap16 +- file: chap17 +- file: chap18 +- file: chap19 +- file: chap20 +- file: chap21 +- file: chap22 +- file: chap23 +- file: chap24 +- file: chap25 +- file: chap26 diff --git a/jb/build.sh b/jb/build.sh new file mode 100644 index 000000000..131c2418c --- /dev/null +++ b/jb/build.sh @@ -0,0 +1,16 @@ +# pip install jupyter-book +# pip install ghp-import + +# Build the Jupyter book version + +# copy the chapter notebooks +cp ../ModSimPySolutions/chapters/chap*.ipynb . + +# add tags to hide the solutions +python prep_notebooks.py + +# build the HTML version +jb build . + +# push it to GitHub +ghp-import -n -p -f _build/html diff --git a/jb/index.md b/jb/index.md new file mode 100644 index 000000000..4804509e8 --- /dev/null +++ b/jb/index.md @@ -0,0 +1,65 @@ +# Modeling and Simulation in Python + +by Allen B. Downey + +*Modeling and Simulation in Python* is a Free Book. It is available under the [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](https://creativecommons.org/licenses/by-nc-sa/4.0/), which means that you are free to copy and modify it, as long as you attribute the work and don’t use it for commercial purposes. + +Other Free Books by Allen Downey are available from [Green Tea Press](https://greenteapress.com/wp/). + +**Run the notebooks** + +[Download the notebooks as a Zip file](https://github.com/AllenDowney/ModSimPy/raw/master/ModSimPyNotebooks.zip) + +Or use these links to run the notebooks on Colab: + +* [Chapter 1](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap01.ipynb) + +* [Chapter 2](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap02.ipynb) + +* [Chapter 3](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap03.ipynb) + +* [Chapter 4](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap04.ipynb) + +* [Chapter 5](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap05.ipynb) + +* [Chapter 6](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap06.ipynb) + +* [Chapter 7](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap07.ipynb) + +* [Chapter 8](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap08.ipynb) + +* [Chapter 9](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap09.ipynb) + +* [Chapter 10](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap10.ipynb) + +* [Chapter 11](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap11.ipynb) + +* [Chapter 12](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap12.ipynb) + +* [Chapter 13](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap13.ipynb) + +* [Chapter 14](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap14.ipynb) + +* [Chapter 15](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap15.ipynb) + +* [Chapter 16](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap16.ipynb) + +* [Chapter 17](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap17.ipynb) + +* [Chapter 18](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap18.ipynb) + +* [Chapter 19](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap19.ipynb) + +* [Chapter 20](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap20.ipynb) + +* [Chapter 21](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap21.ipynb) + +* [Chapter 22](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap22.ipynb) + +* [Chapter 23](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap23.ipynb) + +* [Chapter 24](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap24.ipynb) + +* [Chapter 25](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap25.ipynb) + +* [Chapter 26](https://colab.research.google.com/github/AllenDowney/ModSimPy/blob/master/chapters/chap26.ipynb) diff --git a/jb/preface.md b/jb/preface.md new file mode 100644 index 000000000..522750d94 --- /dev/null +++ b/jb/preface.md @@ -0,0 +1,206 @@ +# Preface + +The essential skills of modeling — abstraction, analysis, simulation, +and validation — are central in engineering, natural sciences, social +sciences, medicine, and many other fields. Some students learn these +skills implicitly, but in most schools they are not taught explicitly, +and students get little practice. That's the problem this book is meant to address. + +At Olin College, we teach these skills in a class called "Modeling and +Simulation", which all students take in their first semester. My +colleagues, John Geddes and Mark Somerville, and I developed this class and taught it for the first time in 2009. + +It is based on our belief that modeling should be taught explicitly, +early, and throughout the curriculum. It is also based on our conviction that computation is an essential part of this process. + +If students are limited to the mathematical analysis they can do by +hand, they are restricted to a small number of simple physical systems, like a projectile moving in a vacuum or a block on a frictionless plane. + +And they only see bad models; that is, models that are too +simple for their intended purpose. In nearly every mechanical system, +air resistance and friction are essential features; if we ignore them, +our predictions will be wrong and our designs won’t work. + +In most introductory physics classes, students don't make modeling +decisions; sometimes they are not even aware of the decisions that have been made for them. Our goal is to teach the entire modeling process and give students a chance to practice it. + +**How much programming do I need?** + +If you have never programmed before, you should be able to read this +book, understand it, and do the exercises. I will do my best to explain everything you need to know; in particular, I have chosen carefully the vocabulary I introduce, and I try to define each term the first time it is used. If you find that I have used a term without defining it, let me know. + +If you have programmed before, you will have an easier time getting +started, but you might be uncomfortable in some places. I take an +approach to programming you have probably not seen before. + +Most programming classes have two big problems: + +1. They go "bottom up", starting with basic language features and + gradually adding more powerful tools. As a result, it takes a long + time before students can do anything more interesting than convert + Fahrenheit to Celsius. + +2. They have no context. Students learn to program with no particular + goal in mind, so the exercises span an incoherent collection of + topics, and the exercises tend to be unmotivated. + +In this book, you learn to program with an immediate goal in mind: +writing simulations of physical systems. And we proceed "top down", by +which I mean we use professional-strength data structures and language +features right away. In particular, we use the following Python libraries: + +- NumPy for basic numerical computation (see + ). + +- SciPy for scientific computation (see ). + +- Matplotlib for visualization (see ). + +- Pandas for working with data (see ). + +- SymPy for symbolic computation, (see ). + +- Pint for units like kilograms and meters (see + ). + +- Jupyter for reading, running, and developing code (see + ). + +These tools let you work on more interesting programs sooner, but there are some drawbacks: they can be hard to use, and it can be challenging to keep track of which library does what and how they interact. + +I have tried to mitigate these problems by providing a library that makes it easier to get started with these tools, and provides some +additional capabilities. + +Some features in the ModSim library are like training wheels; at some +point you will probably stop using them and start working with the +underlying libraries directly. Other features you might find useful the whole time you are working through the book, and later. + +I encourage you to read the ModSim library code. Most of it is not +complicated, and I tried to make it readable. Particularly if you have +some programming experience, you might learn something by reverse +engineering my design decisions. + +**How much math and science do I need?** + +I assume that you know what derivatives and integrals are, but that's +about all. In particular, you don’t need to know (or remember) much +about finding derivatives or integrals of functions analytically. If you know the derivative of $x^2$ and you can integrate $2x~dx$, that will do it. More importantly, you should understand what those concepts *mean*; but if you don’t, this book might help you figure it out. + +You don't have to know anything about differential equations. + +As for science, we will cover topics from a variety of fields, including demography, epidemiology, medicine, thermodynamics, and mechanics. For the most part, I don’t assume you know anything about these topics. In fact, one of the skills you need to do modeling is the ability to learn enough about new fields to develop models and simulations. + +When we get to mechanics, I assume you understand the relationship +between position, velocity, and acceleration, and that you are familiar with Newton’s laws of motion, especially the second law, which is often expressed as $F = ma$ (force equals mass times acceleration). + +I think that's everything you need, but if you find that I left +something out, please let me know. + +**Getting started** + +To run the examples and work on the exercises in this book, you will need an environment where you can run Jupyter notebooks. + +Jupyter is a software development environment where you can run Python code, including the examples in this book, and write your own code. + +A Jupyter notebook is a document that contains text, code, and results from running the code. +Each chapter of this book is a Jupyter notebook where you can run the examples and work on exercises. + +To run the notebooks, you have two options: + +1. You can install Python and Jupyter on your computer and download my notebooks. + +2. You can run the notebooks on Colab. + +To run the notebooks on Colab, go to [the landing page for this book](https://allendowney.github.io/ModSimPy/index.html) and follow the links to the chapters. + +To run the notebooks on your computer, there are three steps: + +1. Download the notebooks and copy them to your computer. + +2. Install Python, Jupyter, and some additional libraries. + +3. Run Jupyter and open the notebooks. + +To get the notebooks, download [this Zip archive](http://modsimpy.com/zip). You will need a program like +WinZip or gzip to unpack the Zip file. Make a note of the location of +the files you download. + +The next two sections provide details for the other steps. +Installing and running software can be challenging, especially if you are not familiar with the command line. +If you run into problems, you might want to work on Colab, at least to get started. + + +**Installing Python** + +You might already have Python installed on your computer, but you might not have the latest version. To use the code in this book, you need Python 3.6 or later. Even if you have the latest version, you probably don't have all of the libraries we need. + +You could update Python and install these libraries, but I strongly +recommend that you don’t go down that road. I think you will find it +easier to use **Anaconda**, which is a free Python distribution that includes all the libraries you need for this book (and more). + +Anaconda is available for Linux, macOS, and Windows. By default, it puts all files in your home directory, so you don’t need administrator (root) permission to install it, and if you have a version of Python already, Anaconda will not remove or modify it. + +Start at . Download the installer for your system and run it. I recommend you run the installer as a normal user, not as administrator or root. + +I suggest you accept the recommended options. On Windows you have the +option to install Visual Studio Code, which is an interactive +environment for writing programs. You won't need it for this book, but +you might want it for other projects. + +By default, Anaconda installs most of the packages you need, but there +are a few more you have to add. Once the installation is complete, open a command window. On macOS or Linux, you can use Terminal. On Windows, open the Anaconda Prompt that should be in your Start menu. + +Run the following command (copy and paste it if you can, to avoid +typos): + +``` +conda install jupyter pandas sympy +conda install beautifulsoup4 lxml html5lib +conda install pint +``` + +That should be everything you need. + + +**Running Jupyter** + + If you have not used Jupyter before, you can read about it at . + +To start Jupyter on macOS or Linux, open a Terminal; on Windows, open +Git Bash. Use `cd` to “change directory" into the directory that contains the notebooks. + +``` +cd ModSimPy +``` + +Then launch the Jupyter notebook server: + +``` +jupyter notebook +``` + +Jupyter should open a window in a browser, and you should see the list +of notebooks in my repository. Click on the first notebook, and follow +the instructions to run the first few "cells". The first time you run a notebook, it might take several seconds to start, while some Python +files get initialized. After that, it should run faster. + +You can also launch Jupyter from the Start menu on Windows, the Dock on macOS, or the Anaconda Navigator on any system. If you do that, Jupyter might start in your home directory or somewhere else in your file system, so you might have to navigate to find the directory with the notebooks. + + +**Contributor List** + +If you have a suggestion or correction, send it to +. Or if you are a Git user, open an issue, or send me a pull request on [this repository](https://github.com/AllenDowney/ModSimPy). + +If I make a change based on your feedback, I will add you to the +contributor list, unless you ask to be omitted. + +If you include at least part of the sentence the error appears in, that makes it easy for me to search. Page and section numbers are fine, too, but not as easy to work with. Thanks! + +- My early work on this book benefited from conversations with my + colleagues at Olin College, including John Geddes, Mark Somerville, Alison Wood, Chris Lee, and Jason Woodard. + +- I am grateful to Lisa Downey and Jason Woodard for their thoughtful and careful copy editing. + +- Thanks to Alessandra Ferzoco, Erhardt Graeff, Emily Tow, Kelsey + Houston-Edwards, Linda Vanasupa, Matt Neal, Joanne Pratt, and Steve Matsumoto for their helpful suggestions. diff --git a/jb/prep_notebooks.py b/jb/prep_notebooks.py new file mode 100644 index 000000000..95067d965 --- /dev/null +++ b/jb/prep_notebooks.py @@ -0,0 +1,26 @@ +import nbformat as nbf +from glob import glob + +# Collect a list of all notebooks in the content folder +notebooks = glob("*.ipynb") + +# Text to look for in adding tags +text_search_dict = { + "# Solution": "hide-cell", # Hide the cell with a button to show +} + +# Search through each notebook and look for the text, add a tag if necessary +for ipath in notebooks: + ntbk = nbf.read(ipath, nbf.NO_CONVERT) + + for cell in ntbk.cells: + cell_tags = cell.get('metadata', {}).get('tags', []) + cell_tags = [] + for key, val in text_search_dict.items(): + if key in cell['source']: + if val not in cell_tags: + cell_tags.append(val) + if len(cell_tags) > 0: + cell['metadata']['tags'] = cell_tags + + nbf.write(ntbk, ipath) diff --git a/modsim.py b/modsim.py new file mode 100644 index 000000000..849527674 --- /dev/null +++ b/modsim.py @@ -0,0 +1,1239 @@ +""" +A collection of functions and classes for modeling and simulation in Python. + +Code from Modeling and Simulation in Python. +Copyright 2020-2025 Allen Downey +MIT License: https://opensource.org/licenses/MIT + +This module provides a simplified interface for modeling and simulation tasks, +built on top of NumPy, SciPy, and Pandas. It is designed to be beginner-friendly +and includes comprehensive input validation to help catch common errors. + +The module includes utilities for: + + - Mathematical operations (coordinate conversions, vector operations) + - Numerical methods (root finding, optimization, integration) + - Data manipulation and analysis + - Visualization and plotting + - System modeling and simulation + +Key features: + + - Vector operations in 2D and 3D + - Time series and sweep series data structures + - System and parameter management + - Numerical methods for solving equations and optimization + - Plotting and visualization utilities + - Unit handling and conversion + +This module is designed to be used in conjunction with the book "Modeling and +Simulation in Python" by Allen Downey. +""" + +import logging + +logger = logging.getLogger(name="modsim.py") + +# make sure we have Python 3.6 or better +import sys + +if sys.version_info < (3, 6): + logger.warning("modsim.py depends on Python 3.6 features.") + +import inspect +import numbers + +import matplotlib.pyplot as plt + +plt.rcParams["figure.dpi"] = 75 +plt.rcParams["savefig.dpi"] = 300 +plt.rcParams["figure.figsize"] = 6, 4 + +from copy import copy +from types import SimpleNamespace + +import numpy as np +import pandas as pd +import scipy +import scipy.optimize as spo +from scipy.integrate import solve_ivp +from scipy.interpolate import InterpolatedUnivariateSpline, interp1d + +# Input validation helpers + + +def validate_numeric(value, name): + """Validate that a value is numeric.""" + if not isinstance(value, numbers.Number): + raise ValueError(f"{name} must be numeric, got {type(value)}") + + +def validate_array_like(value, name): + """Validate that a value is array-like by checking for __getitem__ and __iter__.""" + if not (hasattr(value, "__getitem__") and hasattr(value, "__iter__")): + raise ValueError(f"{name} must be array-like, got {type(value)}") + + +def validate_positive(value, name): + """Validate that a value is positive.""" + if value <= 0: + raise ValueError(f"{name} must be positive, got {value}") + + +def flip(p=0.5): + """Flips a coin with the given probability. + + Args: + p (float): Probability between 0 and 1. + + Returns: + bool: True or False. + """ + return np.random.random() < p + + +def cart2pol(x, y, z=None): + """Convert Cartesian coordinates to polar. + + Args: + x (number or sequence): x coordinate. + y (number or sequence): y coordinate. + z (number or sequence, optional): z coordinate. Defaults to None. + + Returns: + tuple: (theta, rho) or (theta, rho, z). + + Raises: + ValueError: If x or y are not numeric or array-like, or if z is provided but not numeric or array-like + """ + if not isinstance(x, (int, float, list, tuple, np.ndarray, pd.Series)): + raise ValueError("x must be numeric or array-like") + if not isinstance(y, (int, float, list, tuple, np.ndarray, pd.Series)): + raise ValueError("y must be numeric or array-like") + if z is not None and not isinstance( + z, (int, float, list, tuple, np.ndarray, pd.Series) + ): + raise ValueError("z must be numeric or array-like") + x = np.asarray(x) + y = np.asarray(y) + rho = np.hypot(x, y) + theta = np.arctan2(y, x) + if z is None: + return theta, rho + else: + return theta, rho, z + + +def pol2cart(theta, rho, z=None): + """Convert polar coordinates to Cartesian. + + Args: + theta (number or sequence): Angle in radians. + rho (number or sequence): Radius. + z (number or sequence, optional): z coordinate. Defaults to None. + + Returns: + tuple: (x, y) or (x, y, z). + + Raises: + ValueError: If theta or rho are not numeric or array-like, or if z is provided but not numeric or array-like + """ + if not isinstance(theta, (int, float, list, tuple, np.ndarray, pd.Series)): + raise ValueError("theta must be numeric or array-like") + if not isinstance(rho, (int, float, list, tuple, np.ndarray, pd.Series)): + raise ValueError("rho must be numeric or array-like") + if z is not None and not isinstance( + z, (int, float, list, tuple, np.ndarray, pd.Series) + ): + raise ValueError("z must be numeric or array-like") + x = rho * np.cos(theta) + y = rho * np.sin(theta) + if z is None: + return x, y + else: + return x, y, z + + +from numpy import linspace + + +def linrange(start, stop=None, step=1): + """Make an array of equally spaced values. + + Args: + start (float): First value. + stop (float, optional): Last value (might be approximate). Defaults to None. + step (float, optional): Difference between elements. Defaults to 1. + + Returns: + np.ndarray: Array of equally spaced values. + """ + if stop is None: + stop = start + start = 0 + n = int(round((stop - start) / step)) + return linspace(start, stop, n + 1) + + +def __check_kwargs(kwargs, param_name, param_len, func, func_name): + """Check if `kwargs` has a parameter that is a sequence of a particular length. + + Args: + kwargs (dict): Dictionary of keyword arguments. + param_name (str): Name of the parameter to check. + param_len (list): List of valid lengths for the parameter. + func (callable): Function to test the parameter value. + func_name (str): Name of the function for error messages. + + Raises: + ValueError: If the parameter is missing or has an invalid length. + Exception: If the function call fails on the parameter value. + """ + param_val = kwargs.get(param_name, None) + if param_val is None or len(param_val) not in param_len: + msg = ( + "To run `{}`, you have to provide a " + "`{}` keyword argument with a sequence of length {}." + ) + raise ValueError( + msg.format(func_name, param_name, " or ".join(map(str, param_len))) + ) + + try: + func(param_val[0]) + except Exception as e: + msg = ( + "In `{}` I tried running the function you provided " + "with `{}[0]`, and I got the following error:" + ) + logger.error(msg.format(func_name, param_name)) + raise (e) + + +def root_scalar(func, *args, **kwargs): + """Find the input value that is a root of `func`. + + Wrapper for + https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.root_scalar.html + + Args: + func (callable): Function to find a root of. + *args: Additional positional arguments passed to `func`. + **kwargs: Additional keyword arguments passed to `root_scalar`. + + Returns: + RootResults: Object containing the root and convergence information. + + Raises: + ValueError: If the solver does not converge. + """ + underride(kwargs, rtol=1e-4) + + __check_kwargs(kwargs, "bracket", [2], lambda x: func(x, *args), "root_scalar") + + res = spo.root_scalar(func, *args, **kwargs) + + if not res.converged: + msg = ( + "scipy.optimize.root_scalar did not converge. " + "The message it returned is:\n" + res.flag + ) + raise ValueError(msg) + + return res + + +def minimize_scalar(func, *args, **kwargs): + """Find the input value that minimizes `func`. + + Wrapper for + https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize_scalar.html + + Args: + func (callable): Function to be minimized. + *args: Additional positional arguments passed to `func`. + **kwargs: Additional keyword arguments passed to `minimize_scalar`. + + Returns: + OptimizeResult: Object containing the minimum and optimization details. + + Raises: + Exception: If the optimization does not succeed. + """ + underride(kwargs, __func_name="minimize_scalar") + + method = kwargs.get("method", None) + if method is None: + method = "bounded" if kwargs.get("bounds", None) else "brent" + kwargs["method"] = method + + if method == "bounded": + param_name = "bounds" + param_len = [2] + else: + param_name = "bracket" + param_len = [2, 3] + + func_name = kwargs.pop("__func_name") + __check_kwargs(kwargs, param_name, param_len, lambda x: func(x, *args), func_name) + + res = spo.minimize_scalar(func, args=args, **kwargs) + + if not res.success: + msg = ( + "minimize_scalar did not succeed." + "The message it returned is: \n" + res.message + ) + raise Exception(msg) + + return res + + +def maximize_scalar(func, *args, **kwargs): + """Find the input value that maximizes `func`. + + Wrapper for https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize_scalar.html + + Args: + func (callable): Function to be maximized. + *args: Additional positional arguments passed to `func`. + **kwargs: Additional keyword arguments passed to `minimize_scalar`. + + Returns: + OptimizeResult: Object containing the maximum and optimization details. + + Raises: + Exception: If the optimization does not succeed. + """ + + def min_func(*args): + return -func(*args) + + underride(kwargs, __func_name="maximize_scalar") + + res = minimize_scalar(min_func, *args, **kwargs) + + # we have to negate the function value before returning res + res.fun = -res.fun + return res + + +def run_solve_ivp(system, slope_func, **options): + """Compute a numerical solution to a differential equation using solve_ivp. + + Args: + system (System): System object containing 'init', 't_end', and optionally 't_0'. + slope_func (callable): Function that computes slopes. + **options: Additional keyword arguments for scipy.integrate.solve_ivp. + + Returns: + tuple: (TimeFrame of results, details from solve_ivp) + + Raises: + ValueError: If required system attributes are missing or if the solver fails. + """ + system = remove_units(system) + + # make sure `system` contains `init` + if not hasattr(system, "init"): + msg = """It looks like `system` does not contain `init` + as a system variable. `init` should be a State + object that specifies the initial condition:""" + raise ValueError(msg) + + # make sure `system` contains `t_end` + if not hasattr(system, "t_end"): + msg = """It looks like `system` does not contain `t_end` + as a system variable. `t_end` should be the + final time:""" + raise ValueError(msg) + + # the default value for t_0 is 0 + t_0 = getattr(system, "t_0", 0) + + # try running the slope function with the initial conditions + try: + slope_func(t_0, system.init, system) + except Exception as e: + msg = """Before running scipy.integrate.solve_ivp, I tried + running the slope function you provided with the + initial conditions in `system` and `t=t_0` and I got + the following error:""" + logger.error(msg) + raise (e) + + # get the list of event functions + events = options.get("events", []) + + # if there's only one event function, put it in a list + try: + iter(events) + except TypeError: + events = [events] + + for event_func in events: + # make events terminal unless otherwise specified + if not hasattr(event_func, "terminal"): + event_func.terminal = True + + # test the event function with the initial conditions + try: + event_func(t_0, system.init, system) + except Exception as e: + msg = """Before running scipy.integrate.solve_ivp, I tried + running the event function you provided with the + initial conditions in `system` and `t=t_0` and I got + the following error:""" + logger.error(msg) + raise (e) + + # get dense output unless otherwise specified + if not "t_eval" in options: + underride(options, dense_output=True) + + # run the solver + bunch = solve_ivp( + slope_func, [t_0, system.t_end], system.init, args=[system], **options + ) + + # separate the results from the details + y = bunch.pop("y") + t = bunch.pop("t") + + # get the column names from `init`, if possible + if hasattr(system.init, "index"): + columns = system.init.index + else: + columns = range(len(system.init)) + + # evaluate the results at equally-spaced points + if options.get("dense_output", False): + try: + num = system.num + except AttributeError: + num = 101 + t_final = t[-1] + t_array = linspace(t_0, t_final, num) + y_array = bunch.sol(t_array) + + # pack the results into a TimeFrame + results = TimeFrame(y_array.T, index=t_array, columns=columns) + else: + results = TimeFrame(y.T, index=t, columns=columns) + + return results, bunch + + +def leastsq(error_func, x0, *args, **options): + """Find the parameters that yield the best fit for the data using least squares. + + Args: + error_func (callable): Function that computes a sequence of errors. + x0 (array-like): Initial guess for the best parameters. + *args: Additional positional arguments passed to error_func. + **options: Additional keyword arguments passed to scipy.optimize.leastsq. + + Returns: + tuple: (best_params, details) + best_params: Best-fit parameters (same type as x0 if possible). + details: SimpleNamespace with fit details and success flag. + """ + # override `full_output` so we get a message if something goes wrong + options["full_output"] = True + + # run leastsq + t = scipy.optimize.leastsq(error_func, x0=x0, args=args, **options) + best_params, cov_x, infodict, mesg, ier = t + + # pack the results into a ModSimSeries object + details = SimpleNamespace(cov_x=cov_x, mesg=mesg, ier=ier, **infodict) + details.success = details.ier in [1, 2, 3, 4] + + # if we got a Params object, we should return a Params object + if isinstance(x0, Params): + best_params = Params(pd.Series(best_params, x0.index)) + + # return the best parameters and details + return best_params, details + + +def crossings(series, value): + """Find the labels where the series passes through a given value. + + Args: + series (pd.Series): Series with increasing numerical index. + value (float): Value to find crossings for. + + Returns: + np.ndarray: Array of labels where the series crosses the value. + """ + values = series.values - value + interp = InterpolatedUnivariateSpline(series.index, values) + return interp.roots() + + +def has_nan(a): + """Check whether an array or Series contains any NaNs. + + Args: + a (array-like): NumPy array or Pandas Series. + + Returns: + bool: True if any NaNs are present, False otherwise. + """ + return np.any(np.isnan(a)) + + +def is_strictly_increasing(a): + """Check whether the elements of an array are strictly increasing. + + Args: + a (array-like): NumPy array or Pandas Series. + + Returns: + bool: True if strictly increasing, False otherwise. + """ + return np.all(np.diff(a) > 0) + + +def interpolate(series, **options): + """Create an interpolation function from a Series. + + Args: + series (pd.Series): Series object with strictly increasing index. + **options: Additional keyword arguments for scipy.interpolate.interp1d. + + Returns: + callable: Function that maps from the index to the values. + + Raises: + ValueError: If the index contains NaNs or is not strictly increasing. + """ + if has_nan(series.index): + msg = """The Series you passed to interpolate contains + NaN values in the index, which would result in + undefined behavior. So I'm putting a stop to that.""" + raise ValueError(msg) + + if not is_strictly_increasing(series.index): + msg = """The Series you passed to interpolate has an index + that is not strictly increasing, which would result in + undefined behavior. So I'm putting a stop to that.""" + raise ValueError(msg) + + # make the interpolate function extrapolate past the ends of + # the range, unless `options` already specifies a value for `fill_value` + underride(options, fill_value="extrapolate") + + # call interp1d, which returns a new function object + x = series.index + y = series.values + interp_func = interp1d(x, y, **options) + return interp_func + + +def interpolate_inverse(series, **options): + """Interpolate the inverse function of a Series. + + Args: + series (pd.Series): Series representing a mapping from a to b. + **options: Additional keyword arguments for scipy.interpolate.interp1d. + + Returns: + callable: Interpolation object, can be used as a function from b to a. + """ + inverse = pd.Series(series.index, index=series.values) + interp_func = interpolate(inverse, **options) + return interp_func + + +def gradient(series, **options): + """Computes the numerical derivative of a series. + + If the elements of series have units, they are dropped. + + Args: + series (pd.Series): Series object. + **options: Additional keyword arguments for np.gradient. + + Returns: + pd.Series: Series with the same subclass as the input. + + Raises: + ValueError: If series is not a pandas Series + """ + if not isinstance(series, pd.Series): + raise ValueError("series must be a pandas Series") + x = series.index + y = series.values + a = np.gradient(y, x, **options) + return series.__class__(a, series.index) + + +def source_code(obj): + """Print the source code for a given object. + + Args: + obj (object): Function or method object to print source for. + """ + print(inspect.getsource(obj)) + + +def underride(d, **options): + """Add key-value pairs to d only if key is not in d. + + If d is None, create a new dictionary. + + Args: + d (dict): Dictionary to update. + **options: Keyword arguments to add to d. + + Returns: + dict: Updated dictionary. + """ + if d is None: + d = {} + + for key, val in options.items(): + d.setdefault(key, val) + + return d + + +def contour(df, **options): + """Makes a contour plot from a DataFrame. + + Wrapper for plt.contour + https://matplotlib.org/3.1.0/api/_as_gen/matplotlib.pyplot.contour.html + + Note: columns and index must be numerical + + Args: + df (pd.DataFrame): DataFrame to plot. + **options: Additional keyword arguments for plt.contour. + """ + fontsize = options.pop("fontsize", 12) + underride(options, cmap="viridis") + x = df.columns + y = df.index + X, Y = np.meshgrid(x, y) + cs = plt.contour(X, Y, df, **options) + plt.clabel(cs, inline=1, fontsize=fontsize) + + +def savefig(filename, **options): + """Save the current figure. + + Keyword arguments are passed along to plt.savefig + + https://matplotlib.org/api/_as_gen/matplotlib.pyplot.savefig.html + + Args: + filename (str): Name of the file to save the figure to. + **options: Additional keyword arguments for plt.savefig. + """ + print("Saving figure to file", filename) + plt.savefig(filename, **options) + + +def decorate(**options): + """Decorate the current axes. + + Call decorate with keyword arguments like + decorate(title='Title', + xlabel='x', + ylabel='y') + + The keyword arguments can be any of the axis properties + https://matplotlib.org/api/axes_api.html + + Args: + **options: Keyword arguments for axis properties. + """ + ax = plt.gca() + ax.set(**options) + + handles, labels = ax.get_legend_handles_labels() + if handles: + ax.legend(handles, labels) + + plt.tight_layout() + + +def remove_from_legend(bad_labels): + """Remove specified labels from the current plot legend. + + Args: + bad_labels (list): Sequence of label strings to remove from the legend. + """ + ax = plt.gca() + handles, labels = ax.get_legend_handles_labels() + handle_list, label_list = [], [] + for handle, label in zip(handles, labels): + if label not in bad_labels: + handle_list.append(handle) + label_list.append(label) + ax.legend(handle_list, label_list) + + +class SettableNamespace(SimpleNamespace): + """Contains a collection of parameters. + + Used to make a System object. + + Takes keyword arguments and stores them as attributes. + """ + + def __init__(self, namespace=None, **kwargs): + """Initialize a SettableNamespace. + + Args: + namespace (SettableNamespace, optional): Namespace to copy. Defaults to None. + **kwargs: Keyword arguments to store as attributes. + """ + super().__init__() + if namespace: + self.__dict__.update(namespace.__dict__) + self.__dict__.update(kwargs) + + def get(self, name, default=None): + """Look up a variable. + + Args: + name (str): Name of the variable to look up. + default (any, optional): Value returned if `name` is not present. Defaults to None. + + Returns: + any: Value of the variable or default. + """ + try: + return self.__getattribute__(name, default) + except AttributeError: + return default + + def set(self, **variables): + """Make a copy and update the given variables. + + Args: + **variables: Keyword arguments to update. + + Returns: + Params: New Params object with updated variables. + """ + new = copy(self) + new.__dict__.update(variables) + return new + + +def magnitude(x): + """Return the magnitude of a Quantity or number. + + Args: + x (object): Quantity or number. + + Returns: + float: Magnitude as a plain number. + """ + return x.magnitude if hasattr(x, "magnitude") else x + + +def remove_units(namespace): + """Remove units from the values in a Namespace (top-level only). + + Args: + namespace (object): Namespace with attributes. + + Returns: + object: New Namespace object with units removed from values. + """ + res = copy(namespace) + for label, value in res.__dict__.items(): + if isinstance(value, pd.Series): + value = remove_units_series(value) + res.__dict__[label] = magnitude(value) + return res + + +def remove_units_series(series): + """Remove units from the values in a Series (top-level only). + + Args: + series (pd.Series): Series with possible units. + + Returns: + pd.Series: New Series object with units removed from values. + """ + res = copy(series) + for label, value in res.items(): + res[label] = magnitude(value) + return res + + +class System(SettableNamespace): + """Contains system parameters and their values. + + Takes keyword arguments and stores them as attributes. + """ + + pass + + +class Params(SettableNamespace): + """Contains system parameters and their values. + + Takes keyword arguments and stores them as attributes. + """ + + pass + + +def State(**variables): + """Contains the values of state variables. + + Args: + **variables: Keyword arguments to store as state variables. + + Returns: + pd.Series: Series with the state variables. + """ + return pd.Series(variables, name="state") + + +def make_series(x, y, **options): + """Make a Pandas Series. + + Args: + x (sequence): Sequence used as the index. + y (sequence): Sequence used as the values. + **options: Additional keyword arguments for pd.Series. + + Returns: + pd.Series: Pandas Series. + + Raises: + ValueError: If x or y are not array-like or have different lengths + """ + validate_array_like(x, "x") + validate_array_like(y, "y") + if len(x) != len(y): + raise ValueError("x and y must have the same length") + underride(options, name="values") + if isinstance(y, pd.Series): + y = y.values + series = pd.Series(y, index=x, **options) + series.index.name = "index" + return series + + +def TimeSeries(*args, **kwargs): + """Make a pd.Series object to represent a time series. + + Args: + *args: Arguments passed to pd.Series. + **kwargs: Keyword arguments passed to pd.Series. + + Returns: + pd.Series: Series with index name 'Time' and name 'Quantity'. + """ + if args or kwargs: + underride(kwargs, dtype=float) + series = pd.Series(*args, **kwargs) + else: + series = pd.Series([], dtype=float) + + series.index.name = "Time" + if "name" not in kwargs: + series.name = "Quantity" + return series + + +def SweepSeries(*args, **kwargs): + """Make a pd.Series object to store results from a parameter sweep. + + Args: + *args: Arguments passed to pd.Series. + **kwargs: Keyword arguments passed to pd.Series. + + Returns: + pd.Series: Series with index name 'Parameter' and name 'Metric'. + """ + if args or kwargs: + underride(kwargs, dtype=float) + series = pd.Series(*args, **kwargs) + else: + series = pd.Series([], dtype=np.float64) + + series.index.name = "Parameter" + if "name" not in kwargs: + series.name = "Metric" + return series + + +def show(obj): + """Display a Series or Namespace as a DataFrame. + + Args: + obj (object): Series or Namespace to display. + + Returns: + pd.DataFrame: DataFrame representation of the object. + """ + if isinstance(obj, pd.Series): + df = pd.DataFrame(obj) + return df + elif hasattr(obj, "__dict__"): + return pd.DataFrame(pd.Series(obj.__dict__), columns=["value"]) + else: + return obj + + +def TimeFrame(*args, **kwargs): + """Create a DataFrame that maps from time to State. + + Args: + *args: Arguments passed to pd.DataFrame. + **kwargs: Keyword arguments passed to pd.DataFrame. + + Returns: + pd.DataFrame: DataFrame indexed by time. + """ + underride(kwargs, dtype=float) + return pd.DataFrame(*args, **kwargs) + + +def SweepFrame(*args, **kwargs): + """Create a DataFrame that maps from parameter value to SweepSeries. + + Args: + *args: Arguments passed to pd.DataFrame. + **kwargs: Keyword arguments passed to pd.DataFrame. + + Returns: + pd.DataFrame: DataFrame indexed by parameter value. + """ + underride(kwargs, dtype=float) + return pd.DataFrame(*args, **kwargs) + + +def Vector(x, y, z=None, **options): + """Create a 2D or 3D vector as a pandas Series. + + Args: + x (float): x component. + y (float): y component. + z (float, optional): z component. Defaults to None. + **options: Additional keyword arguments for pandas.Series. + + Returns: + pd.Series: Series with keys 'x', 'y', and optionally 'z'. + """ + underride(options, name="component") + if z is None: + return pd.Series(dict(x=x, y=y), **options) + else: + return pd.Series(dict(x=x, y=y, z=z), **options) + + +## Vector functions (should work with any sequence) + + +def vector_mag(v): + """Vector magnitude. + + Args: + v (array-like): Vector. + + Returns: + float: Magnitude of the vector. + + Raises: + ValueError: If v is not array-like or is empty + """ + validate_array_like(v, "v") + if len(v) == 0: + raise ValueError("Vector cannot be empty") + return np.sqrt(np.dot(v, v)) + + +def vector_mag2(v): + """Vector magnitude squared. + + Args: + v (array-like): Vector. + + Returns: + float: Magnitude squared of the vector. + + Raises: + ValueError: If v is not array-like or is empty + """ + validate_array_like(v, "v") + if len(v) == 0: + raise ValueError("Vector cannot be empty") + return np.dot(v, v) + + +def vector_angle(v): + """Angle between v and the positive x axis. + + Only works with 2-D vectors. + + Args: + v (array-like): 2-D vector. + + Returns: + float: Angle in radians. + + Raises: + ValueError: If v is not array-like or is not 2D + """ + validate_array_like(v, "v") + if len(v) != 2: + raise ValueError("vector_angle only works with 2D vectors") + x, y = v + return np.arctan2(y, x) + + +def vector_polar(v): + """Vector magnitude and angle. + + Args: + v (array-like): Vector. + + Returns: + tuple: (magnitude, angle in radians). + + Raises: + ValueError: If v is not array-like + """ + validate_array_like(v, "v") + return vector_mag(v), vector_angle(v) + + +def vector_hat(v): + """Unit vector in the direction of v. + + Args: + v (array-like): Vector. + + Returns: + array-like: Unit vector in the direction of v. + + Raises: + ValueError: If v is not array-like + """ + validate_array_like(v, "v") + # check if the magnitude of the Quantity is 0 + mag = vector_mag(v) + if mag == 0: + return v + else: + return v / mag + + +def vector_perp(v): + """Perpendicular Vector (rotated left). + + Only works with 2-D Vectors. + + Args: + v (array-like): 2-D vector. + + Returns: + Vector: Perpendicular vector. + + Raises: + ValueError: If v is not array-like or is not 2D + """ + validate_array_like(v, "v") + if len(v) != 2: + raise ValueError("vector_perp only works with 2D vectors") + x, y = v + return Vector(-y, x) + + +def vector_dot(v, w): + """Dot product of v and w. + + Args: + v (array-like): First vector. + w (array-like): Second vector. + + Returns: + float: Dot product of v and w. + + Raises: + ValueError: If v or w are not array-like or have different lengths + """ + validate_array_like(v, "v") + validate_array_like(w, "w") + if len(v) != len(w): + raise ValueError("Vectors must have the same length") + return np.dot(v, w) + + +def vector_cross(v, w): + """Cross product of v and w. + + Args: + v (array-like): First vector. + w (array-like): Second vector. + + Returns: + array-like: Cross product of v and w. + + Raises: + ValueError: If v or w are not array-like, or not both 2D or 3D, or not same length + """ + validate_array_like(v, "v") + validate_array_like(w, "w") + if len(v) != len(w): + raise ValueError("Vectors must have the same length for cross product") + if len(v) not in (2, 3): + raise ValueError("Cross product only defined for 2D or 3D vectors") + res = np.cross(v, w) + if len(v) == 3: + return Vector(*res) + else: + return res + + +def vector_proj(v, w): + """Projection of v onto w. + + Args: + v (array-like): Vector to project. + w (array-like): Vector to project onto. + + Returns: + array-like: Projection of v onto w. + + Raises: + ValueError: If v or w are not array-like or not same length + """ + validate_array_like(v, "v") + validate_array_like(w, "w") + if len(v) != len(w): + raise ValueError("Vectors must have the same length for projection") + w_hat = vector_hat(w) + return vector_dot(v, w_hat) * w_hat + + +def scalar_proj(v, w): + """Returns the scalar projection of v onto w. + + Which is the magnitude of the projection of v onto w. + + Args: + v (array-like): Vector to project. + w (array-like): Vector to project onto. + + Returns: + float: Scalar projection of v onto w. + """ + return vector_dot(v, vector_hat(w)) + + +def vector_dist(v, w): + """Euclidean distance from v to w, with units. + + Args: + v (array-like): First vector. + w (array-like): Second vector. + + Returns: + float: Euclidean distance from v to w. + + Raises: + ValueError: If v or w are not array-like or not same length + """ + validate_array_like(v, "v") + validate_array_like(w, "w") + if len(v) != len(w): + raise ValueError("Vectors must have the same length for distance calculation") + if isinstance(v, list): + v = np.asarray(v) + return vector_mag(v - w) + + +def vector_diff_angle(v, w): + """Angular difference between two vectors, in radians. + + Args: + v (array-like): First vector. + w (array-like): Second vector. + + Returns: + float: Angular difference in radians. + + Raises: + ValueError: If v or w are not array-like or not same length + NotImplementedError: If the vectors are not 2-D. + """ + validate_array_like(v, "v") + validate_array_like(w, "w") + if len(v) != len(w): + raise ValueError("Vectors must have the same length for angle difference") + if len(v) == 2: + return vector_angle(v) - vector_angle(w) + else: + # TODO: see http://www.euclideanspace.com/maths/algebra/ + # vectors/angleBetween/ + raise NotImplementedError() + + +def plot_segment(A, B, **options): + """Plots a line segment between two Vectors. + + For 3-D vectors, the z axis is ignored. + + Additional options are passed along to plot(). + + Args: + A (Vector): First vector. + B (Vector): Second vector. + **options: Additional keyword arguments for plt.plot. + + Raises: + ValueError: If A or B are not Vector objects + """ + if not isinstance(A, pd.Series) or not isinstance(B, pd.Series): + raise ValueError("A and B must be Vector objects") + xs = A.x, B.x + ys = A.y, B.y + plt.plot(xs, ys, **options) + + +from time import sleep + +from IPython.display import clear_output + + +def animate(results, draw_func, *args, interval=None): + """Animate results from a simulation. + + Args: + results (TimeFrame): Results to animate. + draw_func (callable): Function that draws state. + *args: Additional positional arguments passed to draw_func. + interval (float, optional): Time between frames in seconds. Defaults to None. + + Raises: + ValueError: If results is not a TimeFrame or draw_func is not callable + """ + if not isinstance(results, pd.DataFrame): + raise ValueError("results must be a TimeFrame") + if not callable(draw_func): + raise ValueError("draw_func must be callable") + plt.figure() + try: + for t, state in results.iterrows(): + draw_func(t, state, *args) + plt.show() + if interval: + sleep(interval) + clear_output(wait=True) + draw_func(t, state, *args) + plt.show() + except KeyboardInterrupt: + pass diff --git a/modsim/__init__.py b/modsim/__init__.py new file mode 100644 index 000000000..004363a02 --- /dev/null +++ b/modsim/__init__.py @@ -0,0 +1 @@ +from .modsim import * diff --git a/modsim/build.sh b/modsim/build.sh new file mode 100644 index 000000000..a3878b80f --- /dev/null +++ b/modsim/build.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +# Exit on any error +set -e + +# Get the directory where this script is located +SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )" +CANONICAL_FILE="$SCRIPT_DIR/modsim.py" + +# Function to copy if destination exists +copy_if_exists() { + dest="$1" + if [ -f "$dest" ]; then + echo "Copying to $dest" + cp "$CANONICAL_FILE" "$dest" + fi +} + +# Copy to root directory +copy_if_exists "$SCRIPT_DIR/../modsim.py" + +# Copy to chapters directory +copy_if_exists "$SCRIPT_DIR/../chapters/modsim.py" + +# Copy to examples directory +copy_if_exists "$SCRIPT_DIR/../examples/modsim.py" + +# Copy to ModSimPySolutions directory and its subdirectories +copy_if_exists "$SCRIPT_DIR/../ModSimPySolutions/modsim.py" +copy_if_exists "$SCRIPT_DIR/../ModSimPySolutions/examples/modsim.py" +copy_if_exists "$SCRIPT_DIR/../ModSimPySolutions/chapters/modsim.py" + +echo "Done copying modsim.py to all locations" \ No newline at end of file diff --git a/modsim/modsim.py b/modsim/modsim.py new file mode 100644 index 000000000..cce63298c --- /dev/null +++ b/modsim/modsim.py @@ -0,0 +1,1195 @@ +""" +Code from Modeling and Simulation in Python. + +Copyright 2020 Allen Downey + +MIT License: https://opensource.org/licenses/MIT +""" + +import logging + +logger = logging.getLogger(name="modsim.py") + +# make sure we have Python 3.6 or better +import sys + +if sys.version_info < (3, 6): + logger.warning("modsim.py depends on Python 3.6 features.") + +import inspect + +import matplotlib.pyplot as plt + +plt.rcParams['figure.dpi'] = 75 +plt.rcParams['savefig.dpi'] = 300 +plt.rcParams['figure.figsize'] = 6, 4 + +import numpy as np +import pandas as pd +import scipy + +import scipy.optimize as spo + +from scipy.interpolate import interp1d +from scipy.interpolate import InterpolatedUnivariateSpline + +from scipy.integrate import solve_ivp + +from types import SimpleNamespace +from copy import copy + +# Input validation helpers +def validate_numeric(value, name): + """Validate that a value is numeric.""" + if not isinstance(value, (int, float)): + raise ValueError(f"{name} must be numeric, got {type(value)}") + +def validate_array_like(value, name): + """Validate that a value is array-like.""" + if not isinstance(value, (list, tuple, np.ndarray, pd.Series)): + raise ValueError(f"{name} must be array-like, got {type(value)}") + +def validate_positive(value, name): + """Validate that a value is positive.""" + if value <= 0: + raise ValueError(f"{name} must be positive, got {value}") + +def flip(p=0.5): + """Flips a coin with the given probability. + + Args: + p (float): Probability between 0 and 1. + + Returns: + bool: True or False. + """ + return np.random.random() < p + + +def cart2pol(x, y, z=None): + """Convert Cartesian coordinates to polar. + + Args: + x (number or sequence): x coordinate. + y (number or sequence): y coordinate. + z (number or sequence, optional): z coordinate. Defaults to None. + + Returns: + tuple: (theta, rho) or (theta, rho, z). + + Raises: + ValueError: If x or y are not numeric or array-like, or if z is provided but not numeric or array-like + """ + if not isinstance(x, (int, float, list, tuple, np.ndarray, pd.Series)): + raise ValueError("x must be numeric or array-like") + if not isinstance(y, (int, float, list, tuple, np.ndarray, pd.Series)): + raise ValueError("y must be numeric or array-like") + if z is not None and not isinstance(z, (int, float, list, tuple, np.ndarray, pd.Series)): + raise ValueError("z must be numeric or array-like") + x = np.asarray(x) + y = np.asarray(y) + rho = np.hypot(x, y) + theta = np.arctan2(y, x) + if z is None: + return theta, rho + else: + return theta, rho, z + + +def pol2cart(theta, rho, z=None): + """Convert polar coordinates to Cartesian. + + Args: + theta (number or sequence): Angle in radians. + rho (number or sequence): Radius. + z (number or sequence, optional): z coordinate. Defaults to None. + + Returns: + tuple: (x, y) or (x, y, z). + + Raises: + ValueError: If theta or rho are not numeric or array-like, or if z is provided but not numeric or array-like + """ + if not isinstance(theta, (int, float, list, tuple, np.ndarray, pd.Series)): + raise ValueError("theta must be numeric or array-like") + if not isinstance(rho, (int, float, list, tuple, np.ndarray, pd.Series)): + raise ValueError("rho must be numeric or array-like") + if z is not None and not isinstance(z, (int, float, list, tuple, np.ndarray, pd.Series)): + raise ValueError("z must be numeric or array-like") + x = rho * np.cos(theta) + y = rho * np.sin(theta) + if z is None: + return x, y + else: + return x, y, z + +from numpy import linspace + +def linrange(start, stop=None, step=1): + """Make an array of equally spaced values. + + Args: + start (float): First value. + stop (float, optional): Last value (might be approximate). Defaults to None. + step (float, optional): Difference between elements. Defaults to 1. + + Returns: + np.ndarray: Array of equally spaced values. + """ + if stop is None: + stop = start + start = 0 + n = int(round((stop-start) / step)) + return linspace(start, stop, n+1) + + +def __check_kwargs(kwargs, param_name, param_len, func, func_name): + """Check if `kwargs` has a parameter that is a sequence of a particular length. + + Args: + kwargs (dict): Dictionary of keyword arguments. + param_name (str): Name of the parameter to check. + param_len (list): List of valid lengths for the parameter. + func (callable): Function to test the parameter value. + func_name (str): Name of the function for error messages. + + Raises: + ValueError: If the parameter is missing or has an invalid length. + Exception: If the function call fails on the parameter value. + """ + param_val = kwargs.get(param_name, None) + if param_val is None or len(param_val) not in param_len: + msg = ("To run `{}`, you have to provide a " + "`{}` keyword argument with a sequence of length {}.") + raise ValueError(msg.format(func_name, param_name, ' or '.join(map(str, param_len)))) + + try: + func(param_val[0]) + except Exception as e: + msg = ("In `{}` I tried running the function you provided " + "with `{}[0]`, and I got the following error:") + logger.error(msg.format(func_name, param_name)) + raise (e) + +def root_scalar(func, *args, **kwargs): + """Find the input value that is a root of `func`. + + Wrapper for + https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.root_scalar.html + + Args: + func (callable): Function to find a root of. + *args: Additional positional arguments passed to `func`. + **kwargs: Additional keyword arguments passed to `root_scalar`. + + Returns: + RootResults: Object containing the root and convergence information. + + Raises: + ValueError: If the solver does not converge. + """ + underride(kwargs, rtol=1e-4) + + __check_kwargs(kwargs, 'bracket', [2], lambda x: func(x, *args), 'root_scalar') + + res = spo.root_scalar(func, *args, **kwargs) + + if not res.converged: + msg = ("scipy.optimize.root_scalar did not converge. " + "The message it returned is:\n" + res.flag) + raise ValueError(msg) + + return res + + +def minimize_scalar(func, *args, **kwargs): + """Find the input value that minimizes `func`. + + Wrapper for + https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize_scalar.html + + Args: + func (callable): Function to be minimized. + *args: Additional positional arguments passed to `func`. + **kwargs: Additional keyword arguments passed to `minimize_scalar`. + + Returns: + OptimizeResult: Object containing the minimum and optimization details. + + Raises: + Exception: If the optimization does not succeed. + """ + underride(kwargs, __func_name='minimize_scalar') + + method = kwargs.get('method', None) + if method is None: + method = 'bounded' if kwargs.get('bounds', None) else 'brent' + kwargs['method'] = method + + if method == 'bounded': + param_name = 'bounds' + param_len = [2] + else: + param_name = 'bracket' + param_len = [2, 3] + + func_name = kwargs.pop('__func_name') + __check_kwargs(kwargs, param_name, param_len, lambda x: func(x, *args), func_name) + + res = spo.minimize_scalar(func, args=args, **kwargs) + + if not res.success: + msg = ("minimize_scalar did not succeed." + "The message it returned is: \n" + + res.message) + raise Exception(msg) + + return res + + +def maximize_scalar(func, *args, **kwargs): + """Find the input value that maximizes `func`. + + Wrapper for https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize_scalar.html + + Args: + func (callable): Function to be maximized. + *args: Additional positional arguments passed to `func`. + **kwargs: Additional keyword arguments passed to `minimize_scalar`. + + Returns: + OptimizeResult: Object containing the maximum and optimization details. + + Raises: + Exception: If the optimization does not succeed. + """ + def min_func(*args): + return -func(*args) + + underride(kwargs, __func_name='maximize_scalar') + + res = minimize_scalar(min_func, *args, **kwargs) + + # we have to negate the function value before returning res + res.fun = -res.fun + return res + + +def run_solve_ivp(system, slope_func, **options): + """Compute a numerical solution to a differential equation using solve_ivp. + + Args: + system (System): System object containing 'init', 't_end', and optionally 't_0'. + slope_func (callable): Function that computes slopes. + **options: Additional keyword arguments for scipy.integrate.solve_ivp. + + Returns: + tuple: (TimeFrame of results, details from solve_ivp) + + Raises: + ValueError: If required system attributes are missing or if the solver fails. + """ + system = remove_units(system) + + # make sure `system` contains `init` + if not hasattr(system, "init"): + msg = """It looks like `system` does not contain `init` + as a system variable. `init` should be a State + object that specifies the initial condition:""" + raise ValueError(msg) + + # make sure `system` contains `t_end` + if not hasattr(system, "t_end"): + msg = """It looks like `system` does not contain `t_end` + as a system variable. `t_end` should be the + final time:""" + raise ValueError(msg) + + # the default value for t_0 is 0 + t_0 = getattr(system, "t_0", 0) + + # try running the slope function with the initial conditions + try: + slope_func(t_0, system.init, system) + except Exception as e: + msg = """Before running scipy.integrate.solve_ivp, I tried + running the slope function you provided with the + initial conditions in `system` and `t=t_0` and I got + the following error:""" + logger.error(msg) + raise (e) + + # get the list of event functions + events = options.get('events', []) + + # if there's only one event function, put it in a list + try: + iter(events) + except TypeError: + events = [events] + + for event_func in events: + # make events terminal unless otherwise specified + if not hasattr(event_func, 'terminal'): + event_func.terminal = True + + # test the event function with the initial conditions + try: + event_func(t_0, system.init, system) + except Exception as e: + msg = """Before running scipy.integrate.solve_ivp, I tried + running the event function you provided with the + initial conditions in `system` and `t=t_0` and I got + the following error:""" + logger.error(msg) + raise (e) + + # get dense output unless otherwise specified + if not 't_eval' in options: + underride(options, dense_output=True) + + # run the solver + bunch = solve_ivp(slope_func, [t_0, system.t_end], system.init, + args=[system], **options) + + # separate the results from the details + y = bunch.pop("y") + t = bunch.pop("t") + + # get the column names from `init`, if possible + if hasattr(system.init, 'index'): + columns = system.init.index + else: + columns = range(len(system.init)) + + # evaluate the results at equally-spaced points + if options.get('dense_output', False): + try: + num = system.num + except AttributeError: + num = 101 + t_final = t[-1] + t_array = linspace(t_0, t_final, num) + y_array = bunch.sol(t_array) + + # pack the results into a TimeFrame + results = TimeFrame(y_array.T, index=t_array, + columns=columns) + else: + results = TimeFrame(y.T, index=t, + columns=columns) + + return results, bunch + + +def leastsq(error_func, x0, *args, **options): + """Find the parameters that yield the best fit for the data using least squares. + + Args: + error_func (callable): Function that computes a sequence of errors. + x0 (array-like): Initial guess for the best parameters. + *args: Additional positional arguments passed to error_func. + **options: Additional keyword arguments passed to scipy.optimize.leastsq. + + Returns: + tuple: (best_params, details) + best_params: Best-fit parameters (same type as x0 if possible). + details: SimpleNamespace with fit details and success flag. + """ + # override `full_output` so we get a message if something goes wrong + options["full_output"] = True + + # run leastsq + t = scipy.optimize.leastsq(error_func, x0=x0, args=args, **options) + best_params, cov_x, infodict, mesg, ier = t + + # pack the results into a ModSimSeries object + details = SimpleNamespace(cov_x=cov_x, + mesg=mesg, + ier=ier, + **infodict) + details.success = details.ier in [1,2,3,4] + + # if we got a Params object, we should return a Params object + if isinstance(x0, Params): + best_params = Params(pd.Series(best_params, x0.index)) + + # return the best parameters and details + return best_params, details + + +def crossings(series, value): + """Find the labels where the series passes through a given value. + + Args: + series (pd.Series): Series with increasing numerical index. + value (float): Value to find crossings for. + + Returns: + np.ndarray: Array of labels where the series crosses the value. + """ + values = series.values - value + interp = InterpolatedUnivariateSpline(series.index, values) + return interp.roots() + + +def has_nan(a): + """Check whether an array or Series contains any NaNs. + + Args: + a (array-like): NumPy array or Pandas Series. + + Returns: + bool: True if any NaNs are present, False otherwise. + """ + return np.any(np.isnan(a)) + + +def is_strictly_increasing(a): + """Check whether the elements of an array are strictly increasing. + + Args: + a (array-like): NumPy array or Pandas Series. + + Returns: + bool: True if strictly increasing, False otherwise. + """ + return np.all(np.diff(a) > 0) + + +def interpolate(series, **options): + """Create an interpolation function from a Series. + + Args: + series (pd.Series): Series object with strictly increasing index. + **options: Additional keyword arguments for scipy.interpolate.interp1d. + + Returns: + callable: Function that maps from the index to the values. + + Raises: + ValueError: If the index contains NaNs or is not strictly increasing. + """ + if has_nan(series.index): + msg = """The Series you passed to interpolate contains + NaN values in the index, which would result in + undefined behavior. So I'm putting a stop to that.""" + raise ValueError(msg) + + if not is_strictly_increasing(series.index): + msg = """The Series you passed to interpolate has an index + that is not strictly increasing, which would result in + undefined behavior. So I'm putting a stop to that.""" + raise ValueError(msg) + + # make the interpolate function extrapolate past the ends of + # the range, unless `options` already specifies a value for `fill_value` + underride(options, fill_value="extrapolate") + + # call interp1d, which returns a new function object + x = series.index + y = series.values + interp_func = interp1d(x, y, **options) + return interp_func + + +def interpolate_inverse(series, **options): + """Interpolate the inverse function of a Series. + + Args: + series (pd.Series): Series representing a mapping from a to b. + **options: Additional keyword arguments for scipy.interpolate.interp1d. + + Returns: + callable: Interpolation object, can be used as a function from b to a. + """ + inverse = pd.Series(series.index, index=series.values) + interp_func = interpolate(inverse, **options) + return interp_func + + +def gradient(series, **options): + """Computes the numerical derivative of a series. + + If the elements of series have units, they are dropped. + + Args: + series (pd.Series): Series object. + **options: Additional keyword arguments for np.gradient. + + Returns: + pd.Series: Series with the same subclass as the input. + + Raises: + ValueError: If series is not a pandas Series + """ + if not isinstance(series, pd.Series): + raise ValueError("series must be a pandas Series") + x = series.index + y = series.values + a = np.gradient(y, x, **options) + return series.__class__(a, series.index) + + +def source_code(obj): + """Print the source code for a given object. + + Args: + obj (object): Function or method object to print source for. + """ + print(inspect.getsource(obj)) + + +def underride(d, **options): + """Add key-value pairs to d only if key is not in d. + + If d is None, create a new dictionary. + + Args: + d (dict): Dictionary to update. + **options: Keyword arguments to add to d. + + Returns: + dict: Updated dictionary. + """ + if d is None: + d = {} + + for key, val in options.items(): + d.setdefault(key, val) + + return d + + +def contour(df, **options): + """Makes a contour plot from a DataFrame. + + Wrapper for plt.contour + https://matplotlib.org/3.1.0/api/_as_gen/matplotlib.pyplot.contour.html + + Note: columns and index must be numerical + + Args: + df (pd.DataFrame): DataFrame to plot. + **options: Additional keyword arguments for plt.contour. + """ + fontsize = options.pop("fontsize", 12) + underride(options, cmap="viridis") + x = df.columns + y = df.index + X, Y = np.meshgrid(x, y) + cs = plt.contour(X, Y, df, **options) + plt.clabel(cs, inline=1, fontsize=fontsize) + + +def savefig(filename, **options): + """Save the current figure. + + Keyword arguments are passed along to plt.savefig + + https://matplotlib.org/api/_as_gen/matplotlib.pyplot.savefig.html + + Args: + filename (str): Name of the file to save the figure to. + **options: Additional keyword arguments for plt.savefig. + """ + print("Saving figure to file", filename) + plt.savefig(filename, **options) + + +def decorate(**options): + """Decorate the current axes. + + Call decorate with keyword arguments like + decorate(title='Title', + xlabel='x', + ylabel='y') + + The keyword arguments can be any of the axis properties + https://matplotlib.org/api/axes_api.html + + Args: + **options: Keyword arguments for axis properties. + """ + ax = plt.gca() + ax.set(**options) + + handles, labels = ax.get_legend_handles_labels() + if handles: + ax.legend(handles, labels) + + plt.tight_layout() + + +def remove_from_legend(bad_labels): + """Remove specified labels from the current plot legend. + + Args: + bad_labels (list): Sequence of label strings to remove from the legend. + """ + ax = plt.gca() + handles, labels = ax.get_legend_handles_labels() + handle_list, label_list = [], [] + for handle, label in zip(handles, labels): + if label not in bad_labels: + handle_list.append(handle) + label_list.append(label) + ax.legend(handle_list, label_list) + + +class SettableNamespace(SimpleNamespace): + """Contains a collection of parameters. + + Used to make a System object. + + Takes keyword arguments and stores them as attributes. + """ + def __init__(self, namespace=None, **kwargs): + """Initialize a SettableNamespace. + + Args: + namespace (SettableNamespace, optional): Namespace to copy. Defaults to None. + **kwargs: Keyword arguments to store as attributes. + """ + super().__init__() + if namespace: + self.__dict__.update(namespace.__dict__) + self.__dict__.update(kwargs) + + def get(self, name, default=None): + """Look up a variable. + + Args: + name (str): Name of the variable to look up. + default (any, optional): Value returned if `name` is not present. Defaults to None. + + Returns: + any: Value of the variable or default. + """ + try: + return self.__getattribute__(name, default) + except AttributeError: + return default + + def set(self, **variables): + """Make a copy and update the given variables. + + Args: + **variables: Keyword arguments to update. + + Returns: + Params: New Params object with updated variables. + """ + new = copy(self) + new.__dict__.update(variables) + return new + + +def magnitude(x): + """Return the magnitude of a Quantity or number. + + Args: + x (object): Quantity or number. + + Returns: + float: Magnitude as a plain number. + """ + return x.magnitude if hasattr(x, 'magnitude') else x + + +def remove_units(namespace): + """Remove units from the values in a Namespace (top-level only). + + Args: + namespace (object): Namespace with attributes. + + Returns: + object: New Namespace object with units removed from values. + """ + res = copy(namespace) + for label, value in res.__dict__.items(): + if isinstance(value, pd.Series): + value = remove_units_series(value) + res.__dict__[label] = magnitude(value) + return res + + +def remove_units_series(series): + """Remove units from the values in a Series (top-level only). + + Args: + series (pd.Series): Series with possible units. + + Returns: + pd.Series: New Series object with units removed from values. + """ + res = copy(series) + for label, value in res.items(): + res[label] = magnitude(value) + return res + + +class System(SettableNamespace): + """Contains system parameters and their values. + + Takes keyword arguments and stores them as attributes. + """ + pass + + +class Params(SettableNamespace): + """Contains system parameters and their values. + + Takes keyword arguments and stores them as attributes. + """ + pass + + +def State(**variables): + """Contains the values of state variables. + + Args: + **variables: Keyword arguments to store as state variables. + + Returns: + pd.Series: Series with the state variables. + """ + return pd.Series(variables, name='state') + + +def make_series(x, y, **options): + """Make a Pandas Series. + + Args: + x (sequence): Sequence used as the index. + y (sequence): Sequence used as the values. + **options: Additional keyword arguments for pd.Series. + + Returns: + pd.Series: Pandas Series. + + Raises: + ValueError: If x or y are not array-like or have different lengths + """ + validate_array_like(x, "x") + validate_array_like(y, "y") + if len(x) != len(y): + raise ValueError("x and y must have the same length") + underride(options, name='values') + if isinstance(y, pd.Series): + y = y.values + series = pd.Series(y, index=x, **options) + series.index.name = 'index' + return series + + +def TimeSeries(*args, **kwargs): + """Make a pd.Series object to represent a time series. + + Args: + *args: Arguments passed to pd.Series. + **kwargs: Keyword arguments passed to pd.Series. + + Returns: + pd.Series: Series with index name 'Time' and name 'Quantity'. + """ + if args or kwargs: + underride(kwargs, dtype=float) + series = pd.Series(*args, **kwargs) + else: + series = pd.Series([], dtype=float) + + series.index.name = 'Time' + if 'name' not in kwargs: + series.name = 'Quantity' + return series + + +def SweepSeries(*args, **kwargs): + """Make a pd.Series object to store results from a parameter sweep. + + Args: + *args: Arguments passed to pd.Series. + **kwargs: Keyword arguments passed to pd.Series. + + Returns: + pd.Series: Series with index name 'Parameter' and name 'Metric'. + """ + if args or kwargs: + underride(kwargs, dtype=float) + series = pd.Series(*args, **kwargs) + else: + series = pd.Series([], dtype=np.float64) + + series.index.name = 'Parameter' + if 'name' not in kwargs: + series.name = 'Metric' + return series + + +def show(obj): + """Display a Series or Namespace as a DataFrame. + + Args: + obj (object): Series or Namespace to display. + + Returns: + pd.DataFrame: DataFrame representation of the object. + """ + if isinstance(obj, pd.Series): + df = pd.DataFrame(obj) + return df + elif hasattr(obj, '__dict__'): + return pd.DataFrame(pd.Series(obj.__dict__), + columns=['value']) + else: + return obj + + +def TimeFrame(*args, **kwargs): + """Create a DataFrame that maps from time to State. + + Args: + *args: Arguments passed to pd.DataFrame. + **kwargs: Keyword arguments passed to pd.DataFrame. + + Returns: + pd.DataFrame: DataFrame indexed by time. + """ + underride(kwargs, dtype=float) + return pd.DataFrame(*args, **kwargs) + + +def SweepFrame(*args, **kwargs): + """Create a DataFrame that maps from parameter value to SweepSeries. + + Args: + *args: Arguments passed to pd.DataFrame. + **kwargs: Keyword arguments passed to pd.DataFrame. + + Returns: + pd.DataFrame: DataFrame indexed by parameter value. + """ + underride(kwargs, dtype=float) + return pd.DataFrame(*args, **kwargs) + + +def Vector(x, y, z=None, **options): + """Create a 2D or 3D vector as a pandas Series. + + Args: + x (float): x component. + y (float): y component. + z (float, optional): z component. Defaults to None. + **options: Additional keyword arguments for pandas.Series. + + Returns: + pd.Series: Series with keys 'x', 'y', and optionally 'z'. + """ + underride(options, name='component') + if z is None: + return pd.Series(dict(x=x, y=y), **options) + else: + return pd.Series(dict(x=x, y=y, z=z), **options) + + +## Vector functions (should work with any sequence) + +def vector_mag(v): + """Vector magnitude. + + Args: + v (array-like): Vector. + + Returns: + float: Magnitude of the vector. + + Raises: + ValueError: If v is not array-like or is empty + """ + validate_array_like(v, "v") + if len(v) == 0: + raise ValueError("Vector cannot be empty") + return np.sqrt(np.dot(v, v)) + + +def vector_mag2(v): + """Vector magnitude squared. + + Args: + v (array-like): Vector. + + Returns: + float: Magnitude squared of the vector. + + Raises: + ValueError: If v is not array-like or is empty + """ + validate_array_like(v, "v") + if len(v) == 0: + raise ValueError("Vector cannot be empty") + return np.dot(v, v) + + +def vector_angle(v): + """Angle between v and the positive x axis. + + Only works with 2-D vectors. + + Args: + v (array-like): 2-D vector. + + Returns: + float: Angle in radians. + + Raises: + ValueError: If v is not array-like or is not 2D + """ + validate_array_like(v, "v") + if len(v) != 2: + raise ValueError("vector_angle only works with 2D vectors") + x, y = v + return np.arctan2(y, x) + + +def vector_polar(v): + """Vector magnitude and angle. + + Args: + v (array-like): Vector. + + Returns: + tuple: (magnitude, angle in radians). + + Raises: + ValueError: If v is not array-like + """ + validate_array_like(v, "v") + return vector_mag(v), vector_angle(v) + + +def vector_hat(v): + """Unit vector in the direction of v. + + Args: + v (array-like): Vector. + + Returns: + array-like: Unit vector in the direction of v. + + Raises: + ValueError: If v is not array-like + """ + validate_array_like(v, "v") + # check if the magnitude of the Quantity is 0 + mag = vector_mag(v) + if mag == 0: + return v + else: + return v / mag + + +def vector_perp(v): + """Perpendicular Vector (rotated left). + + Only works with 2-D Vectors. + + Args: + v (array-like): 2-D vector. + + Returns: + Vector: Perpendicular vector. + + Raises: + ValueError: If v is not array-like or is not 2D + """ + validate_array_like(v, "v") + if len(v) != 2: + raise ValueError("vector_perp only works with 2D vectors") + x, y = v + return Vector(-y, x) + + +def vector_dot(v, w): + """Dot product of v and w. + + Args: + v (array-like): First vector. + w (array-like): Second vector. + + Returns: + float: Dot product of v and w. + + Raises: + ValueError: If v or w are not array-like or have different lengths + """ + validate_array_like(v, "v") + validate_array_like(w, "w") + if len(v) != len(w): + raise ValueError("Vectors must have the same length") + return np.dot(v, w) + + +def vector_cross(v, w): + """Cross product of v and w. + + Args: + v (array-like): First vector. + w (array-like): Second vector. + + Returns: + array-like: Cross product of v and w. + + Raises: + ValueError: If v or w are not array-like, or not both 2D or 3D, or not same length + """ + validate_array_like(v, "v") + validate_array_like(w, "w") + if len(v) != len(w): + raise ValueError("Vectors must have the same length for cross product") + if len(v) not in (2, 3): + raise ValueError("Cross product only defined for 2D or 3D vectors") + res = np.cross(v, w) + if len(v) == 3: + return Vector(*res) + else: + return res + + +def vector_proj(v, w): + """Projection of v onto w. + + Args: + v (array-like): Vector to project. + w (array-like): Vector to project onto. + + Returns: + array-like: Projection of v onto w. + + Raises: + ValueError: If v or w are not array-like or not same length + """ + validate_array_like(v, "v") + validate_array_like(w, "w") + if len(v) != len(w): + raise ValueError("Vectors must have the same length for projection") + w_hat = vector_hat(w) + return vector_dot(v, w_hat) * w_hat + + +def scalar_proj(v, w): + """Returns the scalar projection of v onto w. + + Which is the magnitude of the projection of v onto w. + + Args: + v (array-like): Vector to project. + w (array-like): Vector to project onto. + + Returns: + float: Scalar projection of v onto w. + """ + return vector_dot(v, vector_hat(w)) + + +def vector_dist(v, w): + """Euclidean distance from v to w, with units. + + Args: + v (array-like): First vector. + w (array-like): Second vector. + + Returns: + float: Euclidean distance from v to w. + + Raises: + ValueError: If v or w are not array-like or not same length + """ + validate_array_like(v, "v") + validate_array_like(w, "w") + if len(v) != len(w): + raise ValueError("Vectors must have the same length for distance calculation") + if isinstance(v, list): + v = np.asarray(v) + return vector_mag(v - w) + + +def vector_diff_angle(v, w): + """Angular difference between two vectors, in radians. + + Args: + v (array-like): First vector. + w (array-like): Second vector. + + Returns: + float: Angular difference in radians. + + Raises: + ValueError: If v or w are not array-like or not same length + NotImplementedError: If the vectors are not 2-D. + """ + validate_array_like(v, "v") + validate_array_like(w, "w") + if len(v) != len(w): + raise ValueError("Vectors must have the same length for angle difference") + if len(v) == 2: + return vector_angle(v) - vector_angle(w) + else: + # TODO: see http://www.euclideanspace.com/maths/algebra/ + # vectors/angleBetween/ + raise NotImplementedError() + + +def plot_segment(A, B, **options): + """Plots a line segment between two Vectors. + + For 3-D vectors, the z axis is ignored. + + Additional options are passed along to plot(). + + Args: + A (Vector): First vector. + B (Vector): Second vector. + **options: Additional keyword arguments for plt.plot. + + Raises: + ValueError: If A or B are not Vector objects + """ + if not isinstance(A, pd.Series) or not isinstance(B, pd.Series): + raise ValueError("A and B must be Vector objects") + xs = A.x, B.x + ys = A.y, B.y + plt.plot(xs, ys, **options) + + +from time import sleep +from IPython.display import clear_output + +def animate(results, draw_func, *args, interval=None): + """Animate results from a simulation. + + Args: + results (TimeFrame): Results to animate. + draw_func (callable): Function that draws state. + *args: Additional positional arguments passed to draw_func. + interval (float, optional): Time between frames in seconds. Defaults to None. + + Raises: + ValueError: If results is not a TimeFrame or draw_func is not callable + """ + if not isinstance(results, pd.DataFrame): + raise ValueError("results must be a TimeFrame") + if not callable(draw_func): + raise ValueError("draw_func must be callable") + plt.figure() + try: + for t, state in results.iterrows(): + draw_func(t, state, *args) + plt.show() + if interval: + sleep(interval) + clear_output(wait=True) + draw_func(t, state, *args) + plt.show() + except KeyboardInterrupt: + pass diff --git a/modsim/test_modsim.py b/modsim/test_modsim.py new file mode 100644 index 000000000..2b7190042 --- /dev/null +++ b/modsim/test_modsim.py @@ -0,0 +1,429 @@ +import unittest +from modsim import * +import numpy as np +import pandas as pd + +import warnings +warnings.simplefilter("error", Warning) + + +class TestCartPol(unittest.TestCase): + def test_cart2pol(self): + theta, r = cart2pol(3, 4) + self.assertAlmostEqual(r, 5) + self.assertAlmostEqual(theta, 0.9272952180016122) + + theta, r, z = cart2pol(2, 2, 2) + self.assertAlmostEqual(r, 2 * np.sqrt(2)) + self.assertAlmostEqual(theta, np.pi / 4) + self.assertAlmostEqual(z, 2) + + def test_pol2cart(self): + theta = 0.9272952180016122 + r = 5 + x, y = pol2cart(theta, r) + self.assertAlmostEqual(x, 3) + self.assertAlmostEqual(y, 4) + + angle = np.pi/4 # 45 degrees in radians + r = 2 * np.sqrt(2) + z = 2 + x, y, z = pol2cart(angle, r, z) + self.assertAlmostEqual(x, 2) + self.assertAlmostEqual(y, 2) + self.assertAlmostEqual(z, 2) + + +class TestLinspaceLinRange(unittest.TestCase): + def test_linspace(self): + warnings.simplefilter("error", Warning) + array = linspace(0, 1, 11) + self.assertEqual(len(array), 11) + self.assertAlmostEqual(array[0], 0) + self.assertAlmostEqual(array[1], 0.1) + self.assertAlmostEqual(array[10], 1.0) + + array = linspace(0, 1, 10, endpoint=False) + self.assertEqual(len(array), 10) + self.assertAlmostEqual(array[0], 0) + self.assertAlmostEqual(array[1], 0.1) + self.assertAlmostEqual(array[9], 0.9) + + def test_linrange(self): + array = linrange(0, 1, 0.1) + self.assertEqual(len(array), 11) + self.assertAlmostEqual(array[0], 0) + self.assertAlmostEqual(array[1], 0.1) + self.assertAlmostEqual(array[9], 0.9) + + +class TestOdeSolvers(unittest.TestCase): + def test_run_solve_ivp(self): + init = State(y=2) + system = System(init=init, t_0=1, t_end=3) + + def slope_func(t, state, system): + y = state[0] if isinstance(state, np.ndarray) else state.iloc[0] + dydt = y + t + return [dydt] + + results, details = run_solve_ivp(system, slope_func) + y_end = results.y.iloc[-1] + self.assertAlmostEqual(y_end, 25.5571533) + + +class TestRootFinders(unittest.TestCase): + def test_root_scalar(self): + def func(x): + return (x - 1) * (x - 2) * (x - 3) + + res = root_scalar(func, bracket=[0, 1.9]) + self.assertAlmostEqual(res.root, 1.0, places=5) + + def test_minimize_scalar(self): + def func(x): + return (x - 2)**2 + 1 + + # Test with bracket + res = minimize_scalar(func, bracket=[0, 4]) + self.assertAlmostEqual(res.x, 2.0, places=5) + self.assertAlmostEqual(res.fun, 1.0, places=5) + + # Test with bounds + res = minimize_scalar(func, bounds=[0, 4]) + self.assertAlmostEqual(res.x, 2.0, places=5) + self.assertAlmostEqual(res.fun, 1.0, places=5) + + def test_maximize_scalar(self): + def func(x): + return -(x - 2)**2 + 1 + + # Test with bracket + res = maximize_scalar(func, bracket=[0, 4]) + self.assertAlmostEqual(res.x, 2.0, places=5) + self.assertAlmostEqual(res.fun, 1.0, places=5) + + # Test with bounds + res = maximize_scalar(func, bounds=[0, 4]) + self.assertAlmostEqual(res.x, 2.0, places=5) + self.assertAlmostEqual(res.fun, 1.0, places=5) + + +class TestRunInterpolate(unittest.TestCase): + def test_has_nan(self): + a = [1, 2, 3] + self.assertFalse(has_nan(a)) + self.assertFalse(has_nan(np.array(a))) + self.assertFalse(has_nan(pd.Series(a))) + a.append(np.nan) + self.assertTrue(has_nan(a)) + self.assertTrue(has_nan(np.array(a))) + self.assertTrue(has_nan(pd.Series(a))) + + def test_is_strictly_increasing(self): + a = [1, 2, 3] + self.assertTrue(is_strictly_increasing(a)) + self.assertTrue(is_strictly_increasing(np.array(a))) + self.assertTrue(is_strictly_increasing(pd.Series(a))) + a.append(3) + self.assertFalse(is_strictly_increasing(a)) + self.assertFalse(is_strictly_increasing(np.array(a))) + self.assertFalse(is_strictly_increasing(pd.Series(a))) + + def test_interpolate(self): + index = [1, 2, 3] + values = np.array(index) * 2 - 1 + series = pd.Series(values, index=index) + i = interpolate(series) + self.assertAlmostEqual(i(1.5), 2.0) + + +class TestGradient(unittest.TestCase): + def test_gradient(self): + a = [1, 2, 4] + s = TimeSeries(a) + r = gradient(s) + self.assertTrue(isinstance(r, pd.Series)) + self.assertAlmostEqual(r[1], 1.5) + + +class TestVector(unittest.TestCase): + def assertArrayEqual(self, res, ans): + self.assertTrue(isinstance(res, (np.ndarray, pd.Series))) + self.assertTrue((res == ans).all()) + + def assertVectorEqual(self, res, ans): + self.assertTrue(isinstance(res, (pd.Series, np.ndarray))) + self.assertTrue((res == ans).all()) + + def assertVectorAlmostEqual(self, res, ans): + for x, y in zip(res, ans): + self.assertAlmostEqual(x, y) + + def test_vector_mag(self): + v = [3, 4] + self.assertEqual(vector_mag(v), 5) + v = Vector(3, 4) + self.assertEqual(vector_mag(v), 5) + + def test_vector_mag2(self): + + v = [3, 4] + self.assertEqual(vector_mag2(v), 25) + v = Vector(3, 4) + self.assertEqual(vector_mag2(v), 25) + + def test_vector_angle(self): + ans = 0.927295218 + v = [3, 4] + self.assertAlmostEqual(vector_angle(v), ans) + v = Vector(3, 4) + self.assertAlmostEqual(vector_angle(v), ans) + + def test_vector_hat(self): + v = [3, 4] + ans = [0.6, 0.8] + self.assertVectorAlmostEqual(vector_hat(v), ans) + + v = Vector(3, 4) + self.assertVectorAlmostEqual(vector_hat(v), ans) + + v = [0, 0] + ans = [0, 0] + self.assertVectorAlmostEqual(vector_hat(v), ans) + v = Vector(0, 0) + self.assertVectorAlmostEqual(vector_hat(v), ans) + + def test_vector_perp(self): + v = [3, 4] + ans = [-4, 3] + self.assertTrue((vector_perp(v) == ans).all()) + v = Vector(3, 4) + self.assertTrue((vector_perp(v) == ans).all()) + + def test_vector_dot(self): + v = [3, 4] + w = [5, 6] + ans = 39 + self.assertAlmostEqual(vector_dot(v, w), ans) + v = Vector(3, 4) + self.assertAlmostEqual(vector_dot(v, w), ans) + self.assertAlmostEqual(vector_dot(w, v), ans) + + def test_vector_cross_2D(self): + ans = -2 + + v = [3, 4] + w = [5, 6] + self.assertAlmostEqual(vector_cross(v, w), ans) + self.assertAlmostEqual(vector_cross(w, v), -ans) + + v = Vector(3, 4) + self.assertAlmostEqual(vector_cross(v, w), ans) + self.assertAlmostEqual(vector_cross(w, v), -ans) + + def test_vector_cross_3D(self): + ans = [-2, 4, -2] + + v = [3, 4, 5] + w = [5, 6, 7] + res = vector_cross(v, w) + self.assertTrue(isinstance(res, (np.ndarray, pd.Series))) + self.assertTrue((res == ans).all()) + self.assertTrue((-vector_cross(w, v) == ans).all()) + + v = Vector(3, 4, 5) + self.assertVectorEqual(vector_cross(v, w), ans) + self.assertVectorEqual(-vector_cross(w, v), ans) + + def test_scalar_proj(self): + ans = 4.9934383 + ans2 = 7.8 + + v = [3, 4] + w = [5, 6] + self.assertAlmostEqual(scalar_proj(v, w), ans) + self.assertAlmostEqual(scalar_proj(w, v), ans2) + + v = Vector(3, 4) + self.assertAlmostEqual(scalar_proj(v, w), ans) + self.assertAlmostEqual(scalar_proj(w, v), ans2) + + def test_vector_proj(self): + warnings.simplefilter("error", Warning) + ans = [3.19672131, 3.83606557] + ans2 = [4.68, 6.24] + + v = [3, 4] + w = [5, 6] + self.assertVectorAlmostEqual(vector_proj(v, w), ans) + self.assertVectorAlmostEqual(vector_proj(w, v), ans2) + + v = Vector(3, 4) + self.assertVectorAlmostEqual(vector_proj(v, w), ans) + self.assertVectorAlmostEqual(vector_proj(w, v), ans2) + + def test_vector_dist(self): + v = [3, 4] + w = [6, 8] + ans = 5 + self.assertAlmostEqual(vector_dist(v, w), ans) + self.assertAlmostEqual(vector_dist(w, v), ans) + + v = Vector(3, 4) + self.assertAlmostEqual(vector_dist(v, w), ans) + self.assertAlmostEqual(vector_dist(w, v), ans) + + def test_vector_diff_angle(self): + v = [3, 4] + w = [5, 6] + ans = 0.0512371674 + self.assertAlmostEqual(vector_diff_angle(v, w), ans) + self.assertAlmostEqual(vector_diff_angle(w, v), -ans) + + v = Vector(3, 4) + self.assertAlmostEqual(vector_diff_angle(v, w), ans) + self.assertAlmostEqual(vector_diff_angle(w, v), -ans) + + +class TestSeriesCopy(unittest.TestCase): + def test_series_copy(self): + series = TimeSeries() + res = series.copy() + self.assertTrue(isinstance(res, pd.Series)) + + +class TestLeastsq(unittest.TestCase): + def test_leastsq(self): + # Create noise-free test data: y = 2x + 1 + x = np.array([0, 1, 2, 3, 4]) + y = 2 * x + 1 + + def error_func(params, x, y): + m, b = params + return y - (m * x + b) + + # Initial guess + x0 = [1, 0] # m=1, b=0 + + # Run leastsq + best_params, details = leastsq(error_func, x0, x, y) + + # Check results + self.assertAlmostEqual(best_params[0], 2.0, places=5) # slope + self.assertAlmostEqual(best_params[1], 1.0, places=5) # intercept + self.assertTrue(details.success) + + +class TestCrossings(unittest.TestCase): + def test_crossings(self): + # Create a simple linear series from 0 to 10 + index = np.linspace(0, 10, 11) + values = index.copy() + series = pd.Series(values, index=index) + + # Find where the series crosses 5 + result = crossings(series, 5) + # Should cross exactly at 5 + self.assertEqual(len(result), 1) + self.assertAlmostEqual(result[0], 5.0, places=5) + + +class TestDataStructures(unittest.TestCase): + def test_state(self): + s = State(a=1, b=2) + self.assertIsInstance(s, pd.Series) + self.assertEqual(s['a'], 1) + self.assertEqual(s['b'], 2) + self.assertEqual(s.name, 'state') + + def test_timeseries(self): + ts = TimeSeries([1, 2, 3], index=[10, 20, 30]) + self.assertIsInstance(ts, pd.Series) + self.assertEqual(list(ts), [1, 2, 3]) + self.assertEqual(list(ts.index), [10, 20, 30]) + self.assertEqual(ts.index.name, 'Time') + self.assertEqual(ts.name, 'Quantity') + + def test_sweepseries(self): + ss = SweepSeries([4, 5, 6], index=[0.1, 0.2, 0.3]) + self.assertIsInstance(ss, pd.Series) + self.assertEqual(list(ss), [4, 5, 6]) + self.assertEqual(list(ss.index), [0.1, 0.2, 0.3]) + self.assertEqual(ss.index.name, 'Parameter') + self.assertEqual(ss.name, 'Metric') + + def test_timeframe(self): + df = TimeFrame([[1, 2], [3, 4]], columns=['a', 'b'], index=[0, 1]) + self.assertIsInstance(df, pd.DataFrame) + self.assertEqual(df.shape, (2, 2)) + self.assertEqual(list(df.columns), ['a', 'b']) + self.assertEqual(list(df.index), [0, 1]) + + def test_sweepframe(self): + df = SweepFrame([[5, 6], [7, 8]], columns=['x', 'y'], index=[0.1, 0.2]) + self.assertIsInstance(df, pd.DataFrame) + self.assertEqual(df.shape, (2, 2)) + self.assertEqual(list(df.columns), ['x', 'y']) + self.assertEqual(list(df.index), [0.1, 0.2]) + + def test_make_series(self): + s = make_series([10, 20, 30], [1, 2, 3]) + self.assertIsInstance(s, pd.Series) + self.assertEqual(list(s.index), [10, 20, 30]) + self.assertEqual(list(s.values), [1, 2, 3]) + self.assertEqual(s.name, 'values') + self.assertEqual(s.index.name, 'index') + + def test_vector_polar(self): + v = [3, 4] + mag, angle = vector_polar(v) + self.assertAlmostEqual(mag, 5.0, places=5) + self.assertAlmostEqual(angle, np.arctan2(4, 3), places=5) + + def test_interpolate_inverse(self): + # y = 2x + 1, invert to get x from y + x = np.array([0, 1, 2, 3, 4]) + y = 2 * x + 1 + series = pd.Series(y, index=x) + inv = interpolate_inverse(series) + # y=5 -> x=2 + self.assertAlmostEqual(inv(5), 2.0, places=5) + + def test_gradient(self): + s = pd.Series([1, 4, 9], index=[0, 1, 2]) + g = gradient(s) + self.assertIsInstance(g, pd.Series) + # Should be [3, 4, 5] for [1,4,9] at [0,1,2] + self.assertTrue(np.allclose(g, [3, 4, 5])) + + def test_magnitude(self): + self.assertEqual(magnitude(5), 5) + class Dummy: + magnitude = 42 + self.assertEqual(magnitude(Dummy()), 42) + + def test_remove_units(self): + class Dummy: + def __init__(self): + self.x = 5 + self.y = 10 + d = Dummy() + res = remove_units(d) + self.assertEqual(res.x, 5) + self.assertEqual(res.y, 10) + + def test_remove_units_series(self): + s = pd.Series({'a': 1, 'b': 2}) + res = remove_units_series(s) + self.assertTrue((res == s).all()) + + def test_underride(self): + d = {'a': 1} + underride(d, a=2, b=3) + self.assertEqual(d['a'], 1) + self.assertEqual(d['b'], 3) + + +if __name__ == "__main__": + unittest.main() diff --git a/modsimpy_cover.png b/modsimpy_cover.png new file mode 100644 index 000000000..483c9c978 Binary files /dev/null and b/modsimpy_cover.png differ diff --git a/notes.txt b/notes.txt new file mode 100644 index 000000000..be6a70063 --- /dev/null +++ b/notes.txt @@ -0,0 +1,19 @@ +How to do an update: + +There are three repos involved: + +* ModSim: contains shared resources between ModSimPy and ModSimMATLAB, mostly figures. Active branch is main. + +* ModSimPy: Public repo with notebooks and examples without solutions. Active branch is master. + +* ModSimPySolutions: Private repo where I do development and keep solutions. + +The active directories are chapters and examples. There are lots of leftover bits I should clean up some day! + +ModSimPySolutions is has actions on GitHub that run tests on push and once a month. + +In ModSimPy, the chapters and examples directories have build.sh files that copy the notebooks from ModSimPySolutions, removes solutions, runs tests, and pushes updates to GitHub. + +Not all notebooks can run without solutions, so those don't get tested. + +The canonical location of modsim.py is in the top directory of ModSimPy. Everything else is a copy. diff --git a/papers/Bostonetal2003MinModMillenium.pdf b/papers/Bostonetal2003MinModMillenium.pdf new file mode 100644 index 000000000..654e82db4 Binary files /dev/null and b/papers/Bostonetal2003MinModMillenium.pdf differ diff --git a/papers/dale1985.pdf b/papers/dale1985.pdf new file mode 100644 index 000000000..cacf8d8aa Binary files /dev/null and b/papers/dale1985.pdf differ diff --git a/papers/pacini1986.pdf b/papers/pacini1986.pdf new file mode 100644 index 000000000..f2bc83ad5 Binary files /dev/null and b/papers/pacini1986.pdf differ diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 000000000..38916bad6 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,5 @@ +-r requirements.txt +pandoc +pypandoc +pytest +nbmake diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..002783d6b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,11 @@ +beautifulsoup4 +html5lib +jupyter +lxml +matplotlib +numpy +pandas +pint +scipy +sympy +tables diff --git a/setup.py b/setup.py new file mode 100644 index 000000000..df21e7533 --- /dev/null +++ b/setup.py @@ -0,0 +1,21 @@ +from distutils.core import setup + +def readme(): + try: + with open('README.rst') as f: + return f.read() + except IOError: + return '' + + +setup( + name='modsimpy', + version='1.1.2', + author='Allen B. Downey', + author_email='downey@allendowney.com', + packages=['modsim'], + url='http://github.com/AllenDowney/ModSimPy', + license='LICENSE', + description='Python library for the book Modeling and Simulation in Python.', + long_description=readme(), +)