{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Why automate your work flow, and how to approach the process" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Questions for students to consider:**\n", "\n", "1. In the data exploration section you made some plots from your data. What if you want to look at other relationships? \n", "2. Are there computational processes you do often? How do you implement these? \n", "3. Do you have a clear workflow you could replicate from data to conclusions?\n", "4. Could you plug this new data set into your old workflow? " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Level of Python / Jupyter Automation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. __Good__ - Documenting all analysis steps in enough details that will enable them to be reproduced successfully.\n", "2. __Better__ - Script your analysis\n", "3. __Best__ - Script your analysis and write tests to validate each step." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Key takehomes\n", "- Code is read much more often that it is written\n", "- You are NEVER finshed with an analysis (drafts, reviewer comments, new data etc.). Make your own future life easy!\n", "- Repeating yourself creates opportunity for mistakes/divergence" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Learning Objectives of Automation Module: (total time, 3 hrs including 15 min break)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [Lesson 1](#lesson-1) \n", "- Employ best practices of naming a variable including: don’t use existing function names, avoid periods in names, don’t use numbers at the beginning of a variable name.\n", "- Defensive programming: catch errors instead of just fixing them.\n", "\n", "### [Lesson 2](#lesson-2) \n", "- Define \"Don't Repeat Yourself\" (DRY) and provide examples of how you would implement DRY in your code\n", "- Identify code that can be modularized following DRY and implement a modular workflow using functions.\n", "\n", "### [Lesson 3](#lesson-3) \n", "- Know how to construct a function: variables, function name, syntax, documentation, return values\n", "- Demonstrate use of function within the notebook / code. \n", "- Construct and compose function documentation that clearly defines inputs, output variables and behaviour.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Basic Overview of the suggested workflow using Socrative (Optional)__" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "- Use Socrative quiz to collect answers from student activities (students can run their code in their notebooks, and post to socrative). This will allow the instructor to see what solutions students came up with, and identify any places where misconceptions and confusion are coming up. Using Socrative quizes also allows for a record of the student work to be analyzed after class to see how students are learning and where they are having troubles.\n", "- sharing of prepared Socrative Quizes designed to be used with the automation module can be shared by URL links to each teacher so they do not have to be remade." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Please download the cleaned data file:__\n", "\n", "https://raw.githubusercontent.com/Reproducible-Science-Curriculum/automation-RR-Jupyter/gh-pages/notebooks/gapminder_cleaned.csv\n", "\n", "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Lesson 1 \n", "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lets begin by creating a new Jupyter notebook.\n", "\n", "__Question:__ \n", "\n", "- According to the organization we setup where should we put this notebook?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Review of good variable practices\n", "**Learning Objective: ** Employ best practices of naming a variable including: don’t use existing function names, avoid periods in names, don’t use numbers at the beginning of a variable name\n", "\n", "### Types of variables:\n", "- strings, integers, etc..\n", "\n", "References:\n", "- PEP8 - Style Guide for Python Code - https://www.python.org/dev/peps/pep-0008/ \n", "- https://www.tutorialspoint.com/python3/python_variable_types.htm\n", "\n", "### Keep in mind that code is read many more times then it is written!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Other useful conventions with variables to follow\n", "1. Set-up variables at the begining of your page, after importing libraries\n", "2. use variables instead of file names, or exact values or strings so that if you need to change the value of something you don't have to search through all your code to make sure you made the change everywhere, simply change the value of the variable at the top. -- This will also make your code more reproducible in the end. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Let's Get Started" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To get started we will import the python modules that we will use in the session. These modules are developed by programmers and made available as open source packages for python. We would normally have to install each of these ourself but they are included as part of the [Anaconda Python Distribution](https://www.continuum.io/downloads).\n", "\n", "The _%matplotlib inline_ statement is part of the Jupyter and IPython magic that enables plaots generated by the matplotlib package to be discplayed as output in the Jupyter Notebook instead of open in a separate window." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "import pylab as plt\n", "import matplotlib\n", "\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will continue where the _data exploration_ module left off but importing the cleaned gapminder dataset and setting it equal to a new varaible named __df__ to denote that we have imported a _pandas_ dataframe.\n", "\n", "As validation that we have imported the data we will also look at the top five rows of data using the _head_ method of pandas." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Defensive programming" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "ename": "FileNotFoundError", "evalue": "[Errno 2] No such file or directory: 'data/gapminder_cleaned.csv'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mFileNotFoundError\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 1\u001b[0m \u001b[0mcleaned_data_location\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'data/gapminder_cleaned.csv'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mdf\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_csv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcleaned_data_location\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 3\u001b[0m \u001b[0mdf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhead\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/opt/anaconda3/lib/python3.8/site-packages/pandas/io/parsers.py\u001b[0m in \u001b[0;36mread_csv\u001b[0;34m(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, squeeze, prefix, mangle_dupe_cols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, dialect, error_bad_lines, warn_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options)\u001b[0m\n\u001b[1;32m 608\u001b[0m \u001b[0mkwds\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkwds_defaults\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 609\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 610\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m_read\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilepath_or_buffer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwds\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 611\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 612\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/opt/anaconda3/lib/python3.8/site-packages/pandas/io/parsers.py\u001b[0m in \u001b[0;36m_read\u001b[0;34m(filepath_or_buffer, kwds)\u001b[0m\n\u001b[1;32m 460\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 461\u001b[0m \u001b[0;31m# Create the parser.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 462\u001b[0;31m \u001b[0mparser\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mTextFileReader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilepath_or_buffer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwds\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 463\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 464\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mchunksize\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0miterator\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/opt/anaconda3/lib/python3.8/site-packages/pandas/io/parsers.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, f, engine, **kwds)\u001b[0m\n\u001b[1;32m 817\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"has_index_names\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mkwds\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"has_index_names\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 818\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 819\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_engine\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_make_engine\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mengine\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 820\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 821\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mclose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/opt/anaconda3/lib/python3.8/site-packages/pandas/io/parsers.py\u001b[0m in \u001b[0;36m_make_engine\u001b[0;34m(self, engine)\u001b[0m\n\u001b[1;32m 1048\u001b[0m )\n\u001b[1;32m 1049\u001b[0m \u001b[0;31m# error: Too many arguments for \"ParserBase\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1050\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mmapping\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mengine\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# type: ignore[call-arg]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1051\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1052\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_failover_to_python\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/opt/anaconda3/lib/python3.8/site-packages/pandas/io/parsers.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, src, **kwds)\u001b[0m\n\u001b[1;32m 1865\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1866\u001b[0m \u001b[0;31m# open handles\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1867\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_open_handles\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msrc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwds\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 1868\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhandles\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1869\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mkey\u001b[0m \u001b[0;32min\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m\"storage_options\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"encoding\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"memory_map\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"compression\"\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/opt/anaconda3/lib/python3.8/site-packages/pandas/io/parsers.py\u001b[0m in \u001b[0;36m_open_handles\u001b[0;34m(self, src, kwds)\u001b[0m\n\u001b[1;32m 1360\u001b[0m \u001b[0mLet\u001b[0m \u001b[0mthe\u001b[0m \u001b[0mreaders\u001b[0m \u001b[0mopen\u001b[0m \u001b[0mIOHanldes\u001b[0m \u001b[0mafter\u001b[0m \u001b[0mthey\u001b[0m \u001b[0mare\u001b[0m \u001b[0mdone\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mtheir\u001b[0m \u001b[0mpotential\u001b[0m \u001b[0mraises\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1361\u001b[0m \"\"\"\n\u001b[0;32m-> 1362\u001b[0;31m self.handles = get_handle(\n\u001b[0m\u001b[1;32m 1363\u001b[0m \u001b[0msrc\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1364\u001b[0m \u001b[0;34m\"r\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/opt/anaconda3/lib/python3.8/site-packages/pandas/io/common.py\u001b[0m in \u001b[0;36mget_handle\u001b[0;34m(path_or_buf, mode, encoding, compression, memory_map, is_text, errors, storage_options)\u001b[0m\n\u001b[1;32m 640\u001b[0m \u001b[0merrors\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"replace\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 641\u001b[0m \u001b[0;31m# Encoding\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 642\u001b[0;31m handle = open(\n\u001b[0m\u001b[1;32m 643\u001b[0m \u001b[0mhandle\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 644\u001b[0m \u001b[0mioargs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmode\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'data/gapminder_cleaned.csv'" ] } ], "source": [ "cleaned_data_location = 'data/gapminder_cleaned.csv'\n", "df = pd.read_csv(cleaned_data_location)\n", "df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Whooops! That doesn't look great. and we know that the file *does* exist. We just downloaded it!\n", "Lets do some defensive programming to prevent things from breaking" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Your most common collaborator is YOU, in the future. Including error handling and messages help your colleagues and students, but most importantly, YOU.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Catching errors using try-except\n", "try and except statements are a good way to catch and deal with errors in a convenient way" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Couldn't find data file, check path? You tried data/gapminder_cleaned.csv\n" ] } ], "source": [ "cleaned_data_location = 'data/gapminder_cleaned.csv'\n", "\n", "try:\n", " df = pd.read_csv(cleaned_data_location)\n", "\n", "except FileNotFoundError:\n", " print(\"Couldn't find data file, check path? You tried\", cleaned_data_location)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercise: what do you need to fix to actually open that data file?" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "cleaned_data_location = '../data/gapminder_cleaned.csv'\n", "\n", "try:\n", " df = pd.read_csv(cleaned_data_location)\n", "\n", "except FileNotFoundError:\n", " print(\"Couldn't find data file, check path? You tried\", cleaned_data_location)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can set a flag at the top of our script to dynamically set how much infomation we want to see.\n", "e.g. VERBOSE.\n", "\n", "When the variable is set to `True` we print out extra information." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " year pop lifeexp gdppercap country continent\n", "0 1952 8425333 28.801 779.445314 afghanistan asia\n", "1 1957 9240934 30.332 820.853030 afghanistan asia\n", "2 1962 10267083 31.997 853.100710 afghanistan asia\n", "3 1967 11537966 34.020 836.197138 afghanistan asia\n", "4 1972 13079460 36.088 739.981106 afghanistan asia\n" ] } ], "source": [ "VERBOSE = True\n", "\n", "try:\n", " df = pd.read_csv(cleaned_data_location)\n", " if VERBOSE:\n", " print(df.head())\n", "\n", "except FileNotFoundError:\n", " print(\"Couldn't find data file, check path? You tried\", cleaned_data_location)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Using assert statements to be explicit about assumptions\n", "assert will fail if statement isn't true. Nothing will happen if it is true" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "years = df['year'].unique()\n", "years.sort()\n", "assert years[-1]==2007 #Check that the most recent year is as expected" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "# Lesson 2 \n", "---" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "__Learning Objectives__\n", "- Define \"Don't Repeat Yourself\" (DRY) and provide examples of how you would implement DRY in your code\n", "- Identify code that can be modularized following DRY and implement a modular workflow using functions." ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "As you write software there comes a time when you are going to encounter a situation where you want to do the same analysis step as you have already done in your analysis. Our natural tendancy is the copy the code that we wrote and paste it into teh new location for reuse. Sounds easy, right. Copy, paste, move on...not so fast.\n", "\n", "What happens if there is a problem with the code or you decide to tweak it, just a little, to change a format or enahce it?\n", "\n", "You wil have to change the code in every place you ahve copied it. How do you know if you got _all_ of the copies? What happens if one of the copies is _not_ changed?\n", "\n", "These examples illustrate the principle of \"Don't Repeat Yourself\". We are going to look at how to __refactor__ our code and pull pieces out by making them functions. They we will __call__ the function everytime we want to use that code." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# What if we want to ask questions about variables over time?\n", "## Lets start with mean life expectancy in Asia" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "#decide on a year, and calculate the statistic of interest\n", "## do it point by point!! DON'T RUSH\n", "\n", "mask_asia = df['continent'] == 'asia'\n", "df_asia = df[mask_asia]\n", "\n", "mask_1952 = df_asia['year'] == 1952\n", "df_1952 = df_asia[mask_1952]\n", "\n", "value = np.mean(df_1952['lifeexp'])\n", "\n", "# create an empty list\n", "result = []\n", "\n", "# append a row to list with a tuple containing your result\n", "result.append(('asia', '1952', value))\n", " \n", "# Turn the summary into a dataframe so that we can visualize easily\n", "result = pd.DataFrame(result, columns=['continent', 'year', 'lifeexp'])\n" ] }, { "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", "
continentyearlifeexp
0asia195246.314394
\n", "
" ], "text/plain": [ " continent year lifeexp\n", "0 asia 1952 46.314394" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "result" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "# Use a for loop to Loop through years and calculate the statistic of interest\n", "\n", "mask_asia = df['continent'] == 'asia'\n", "df_asia = df[mask_asia]\n", "\n", "years = df_asia['year'].unique()\n", "summary = []\n", "\n", "for year in years:\n", " mask_year = df_asia['year'] == year\n", " df_year = df_asia[mask_year]\n", " value = np.mean(df_year['lifeexp'])\n", " summary.append(('asia', year, value))\n", " \n", "# Turn the summary into a dataframe so that we can visualize easily\n", "summary = pd.DataFrame(summary, columns=['continent', 'year', 'lifeexp'])" ] }, { "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", "
continentyearlifeexp
0asia195246.314394
1asia195749.318544
2asia196251.563223
3asia196754.663640
4asia197257.319269
5asia197759.610556
6asia198262.617939
7asia198764.851182
8asia199266.537212
9asia199768.020515
10asia200269.233879
11asia200770.728485
\n", "
" ], "text/plain": [ " continent year lifeexp\n", "0 asia 1952 46.314394\n", "1 asia 1957 49.318544\n", "2 asia 1962 51.563223\n", "3 asia 1967 54.663640\n", "4 asia 1972 57.319269\n", "5 asia 1977 59.610556\n", "6 asia 1982 62.617939\n", "7 asia 1987 64.851182\n", "8 asia 1992 66.537212\n", "9 asia 1997 68.020515\n", "10 asia 2002 69.233879\n", "11 asia 2007 70.728485" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "summary" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# But now your PI wants that information all the years for a different continent!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Activity: How could we use variables to make it easier to re-run this for differnet continents?" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "# Define which continent / category we will use\n", "category = 'lifeexp'\n", "continent = 'asia'\n", "\n", "# Create a mask that selects the continent of choice\n", "mask_continent = df['continent'] == 'asia'\n", "df_continent = df[mask_continent]\n", "\n", "# Loop through years and calculate the statistic of interest\n", "years = df_continent['year'].unique()\n", "summary = []\n", "\n", "for year in years:\n", " mask_year = df_continent['year'] == year\n", " df_year = df_continent[mask_year]\n", " value = np.mean(df_year[category])\n", " summary.append((continent, year, value))\n", " \n", "# Turn the summary into a dataframe so that we can visualize easily\n", "summary = pd.DataFrame(summary, columns=['continent', 'year', category])" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "summary.plot.line('year', category, label = \"life expectancy\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Lesson 3 \n", "## Building functions\n", "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Learning Objectives__\n", "- Know how to construct a function: variables, function name, syntax, documentation, return values\n", "- Demonstrate use of function within the notebook / code.\n", "- Construct and compose function documentation that clearly defines inputs, output " ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "def calculate_mean_over_time(data, category, continent, verbose=False):\n", " \n", " # Create a mask that selects the continent of choice\n", " mask_continent = data['continent'] == continent\n", " data_continent = data[mask_continent]\n", "\n", " # Loop through years and calculate the statistic of interest\n", " years = data_continent['year'].unique()\n", " summary = []\n", " for year in years:\n", " if verbose:\n", " print(year)\n", " mask_year = data_continent['year'] == year\n", " data_year = data_continent[mask_year]\n", " value = np.mean(data_year[category])\n", " summary.append((continent, year, value))\n", "\n", " # Turn the summary into a dataframe so that we can visualize easily\n", " summary = pd.DataFrame(summary, columns=['continent', 'year', category])\n", " return summary" ] }, { "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
continentyearlifeexp
0asia195246.314394
1asia195749.318544
2asia196251.563223
3asia196754.663640
4asia197257.319269
5asia197759.610556
6asia198262.617939
7asia198764.851182
8asia199266.537212
9asia199768.020515
10asia200269.233879
11asia200770.728485
\n", "
" ], "text/plain": [ " continent year lifeexp\n", "0 asia 1952 46.314394\n", "1 asia 1957 49.318544\n", "2 asia 1962 51.563223\n", "3 asia 1967 54.663640\n", "4 asia 1972 57.319269\n", "5 asia 1977 59.610556\n", "6 asia 1982 62.617939\n", "7 asia 1987 64.851182\n", "8 asia 1992 66.537212\n", "9 asia 1997 68.020515\n", "10 asia 2002 69.233879\n", "11 asia 2007 70.728485" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "VERBOSE = False\n", "calculate_mean_over_time(df, \"lifeexp\", \"asia\", VERBOSE)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Activity:\n", " How would you make a function to calculate the median through time?\n", " (Hint: focus on DRY.)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "def calculate_statistic_over_time(data, category, continent, func):\n", " \n", " # Create a mask that selects the continent of choice\n", " mask_continent = data['continent'] == continent\n", " data_continent = data[mask_continent]\n", "\n", " # Loop through years and calculate the statistic of interest\n", " years = data_continent['year'].unique()\n", " summary = []\n", " for year in years:\n", " mask_year = data_continent['year'] == year\n", " data_year = data_continent[mask_year]\n", " value = func(data_year[category])\n", " summary.append((continent, year, value))\n", "\n", " # Turn the summary into a dataframe so that we can visualize easily\n", " summary = pd.DataFrame(summary, columns=['continent', 'year', category])\n", " return summary" ] }, { "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", "
continentyearlifeexp
0asia195244.869
1asia195748.284
2asia196249.325
3asia196753.655
4asia197256.950
5asia197760.765
6asia198263.739
7asia198766.295
8asia199268.690
9asia199770.265
10asia200271.028
11asia200772.396
\n", "
" ], "text/plain": [ " continent year lifeexp\n", "0 asia 1952 44.869\n", "1 asia 1957 48.284\n", "2 asia 1962 49.325\n", "3 asia 1967 53.655\n", "4 asia 1972 56.950\n", "5 asia 1977 60.765\n", "6 asia 1982 63.739\n", "7 asia 1987 66.295\n", "8 asia 1992 68.690\n", "9 asia 1997 70.265\n", "10 asia 2002 71.028\n", "11 asia 2007 72.396" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "calculate_statistic_over_time(df, \"lifeexp\", \"asia\", np.median)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Including docstrings with any functions is good programming practice, and helps out your collaborators (i.e., you!)\n", "\n", "More on docstrings: https://www.python.org/dev/peps/pep-0257/#what-is-a-docstring" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "def calculate_statistic_over_time(data, category, continent, func):\n", " \"\"\"calculate values of a statistic through time\n", "\n", " Args:\n", " data: a data frame\n", " category: one of the column headers of the data frame (e.g. 'lifeexp')\n", " continent: possible value of the continent column of that data frame (e.g. 'asia')\n", " func: the funtion to apply to data values (e.g. np.mean)\n", " \n", " Returns:\n", " a summary table of value per year.\n", "\n", " \"\"\"\n", " \n", " # Create a mask that selects the continent of choice\n", " mask_continent = data['continent'] == continent\n", " data_continent = data[mask_continent]\n", "\n", " # Loop through years and calculate the statistic of interest\n", " years = data_continent['year'].unique()\n", " summary = []\n", " for year in years:\n", " mask_year = data_continent['year'] == year\n", " data_year = data_continent[mask_year]\n", " value = func(data_year[category])\n", " summary.append((continent, year, value))\n", "\n", " # Turn the summary into a dataframe so that we can visualize easily\n", " summary = pd.DataFrame(summary, columns=['continent', 'year', category])\n", " return summary" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Defensive programming activity\n", "How would you check to make sure input values are reasonable?\n", "Use assertions or try-except statements, and add options with the VERBOSE flag" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "def calculate_statistic_over_time(data, category, continent, func, verbose=False):\n", " \"\"\"calculate values of a statistic through time\n", "\n", " Args:\n", " data: a pandas data frame\n", " category: one of the column headers of the data frame (e.g. 'lifeexp')\n", " continent: possible value of the continent column of that data frame (e.g. 'asia')\n", " func: the funtion to apply to data values (e.g. np.mean)\n", " \n", " Returns:\n", " a summary table of value per year.\n", "\n", " \"\"\"\n", " \n", " # Check values\n", " assert category in data.columns.values\n", " assert 'continent' in data.columns.values\n", " assert continent in data['continent'].unique()\n", " \n", " # Create a mask that selects the continent of choice\n", " mask_continent = data['continent'] == continent\n", " data_continent = data[mask_continent]\n", " \n", " \n", " # Loop through years and calculate the statistic of interest\n", " years = data_continent['year'].unique()\n", " if verbose:\n", " print(\"years include\", years)\n", " summary = []\n", " for year in years:\n", " mask_year = data_continent['year'] == year\n", " data_year = data_continent[mask_year]\n", " value = func(data_year[category])\n", " summary.append((continent, year, value))\n", "\n", " # Turn the summary into a dataframe so that we can visualize easily\n", " summary = pd.DataFrame(summary, columns=['continent', 'year', category])\n", " return summary" ] }, { "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", "
continentyearlifeexp
0asia195244.869
1asia195748.284
2asia196249.325
3asia196753.655
4asia197256.950
5asia197760.765
6asia198263.739
7asia198766.295
8asia199268.690
9asia199770.265
10asia200271.028
11asia200772.396
\n", "
" ], "text/plain": [ " continent year lifeexp\n", "0 asia 1952 44.869\n", "1 asia 1957 48.284\n", "2 asia 1962 49.325\n", "3 asia 1967 53.655\n", "4 asia 1972 56.950\n", "5 asia 1977 60.765\n", "6 asia 1982 63.739\n", "7 asia 1987 66.295\n", "8 asia 1992 68.690\n", "9 asia 1997 70.265\n", "10 asia 2002 71.028\n", "11 asia 2007 72.396" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "calculate_statistic_over_time(df, \"lifeexp\", \"asia\", np.median)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on function calculate_statistic_over_time in module __main__:\n", "\n", "calculate_statistic_over_time(data, category, continent, func, verbose=False)\n", " calculate values of a statistic through time\n", " \n", " Args:\n", " data: a pandas data frame\n", " category: one of the column headers of the data frame (e.g. 'lifeexp')\n", " continent: possible value of the continent column of that data frame (e.g. 'asia')\n", " func: the funtion to apply to data values (e.g. np.mean)\n", " \n", " Returns:\n", " a summary table of value per year.\n", "\n" ] } ], "source": [ "help(calculate_statistic_over_time)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "#use this function to plot life expectancy through time\n", "continents = df['continent'].unique()\n", "VERBOSE = False\n", "fig, ax = plt.subplots()\n", "\n", "for continent in continents:\n", " output = calculate_statistic_over_time(df,\"lifeexp\", continent, np.median)\n", " output.plot.line('year', \"lifeexp\", ax=ax, label=continent)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Demo: Importing your own functions as module, (main.ipynb)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.8" } }, "nbformat": 4, "nbformat_minor": 1 }