{
 "metadata": {
  "name": "",
  "signature": "sha256:68e419b336c43b3a5f99d948a5148ad6a7da83f9796fdc45c9132c236a5a43bc"
 },
 "nbformat": 3,
 "nbformat_minor": 0,
 "worksheets": [
  {
   "cells": [
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "[Sebastian Raschka](http://sebastianraschka.com)  \n",
      "\n",
      "- [Open in IPython nbviewer](http://nbviewer.ipython.org/github/rasbt/python_reference/blob/master/tutorials/awesome_things_ipynb?create=1) \n",
      "\n",
      "- [Link to this IPython notebook on Github](https://github.com/rasbt/python_reference/blob/master/tutorials/awesome_things_ipynb.ipynb)  \n",
      "\n",
      "- [Link to the GitHub Repository python_reference](https://github.com/rasbt/python_reference/)"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "import time\n",
      "import platform\n",
      "print('Last updated: %s' %time.strftime('%d/%m/%Y'))\n",
      "print('Created using Python', platform.python_version())"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Last updated: 27/06/2014\n",
        "Created using Python 3.4.1\n"
       ]
      }
     ],
     "prompt_number": 37
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<hr>\n",
      "I would be happy to hear your comments and suggestions.  \n",
      "Please feel free to drop me a note via\n",
      "[twitter](https://twitter.com/rasbt), [email](mailto:bluewoodtree@gmail.com), or [google+](https://plus.google.com/+SebastianRaschka).\n",
      "<hr>"
     ]
    },
    {
     "cell_type": "heading",
     "level": 1,
     "metadata": {},
     "source": [
      "Awesome things that you can do in IPython Notebooks (in progress)"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<br>\n",
      "<br>"
     ]
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Writing local files"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%%file hello.py\n",
      "def func_inside_script(x, y):\n",
      "    return x + y\n",
      "print('Hello World')"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Writing hello.py\n"
       ]
      }
     ],
     "prompt_number": 13
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<br>\n",
      "<br>"
     ]
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Running Python scripts"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "We can run Python scripts in IPython via the %run magic command. For example, the Python script that we created in the [Writing local files](#Writing-local-files) section."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%run hello.py"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Hello World\n"
       ]
      }
     ],
     "prompt_number": 14
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "func_inside_script(1, 2)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "pyout",
       "prompt_number": 16,
       "text": [
        "3"
       ]
      }
     ],
     "prompt_number": 16
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<br>\n",
      "<br>"
     ]
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Benchmarking"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%timeit [x**2 for x in range(100)] "
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "10000 loops, best of 3: 38.8 \u00b5s per loop\n"
       ]
      }
     ],
     "prompt_number": 4
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%timeit -r 5 -n 100 [x**2 for x in range(100)] "
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "100 loops, best of 5: 39 \u00b5s per loop\n"
       ]
      }
     ],
     "prompt_number": 3
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<br>\n",
      "<br>"
     ]
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Using system shell commands"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "By prepending a \"`!`\" we can conveniently execute most of the system shell commands, below are just a few examples."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "my_dir = 'new_dir'\n",
      "!mkdir $my_dir\n",
      "!pwd\n",
      "!touch $my_dir'/some.txt'\n",
      "!ls -l './new_dir'\n",
      "!ls -l $my_dir | wc -l"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "/Users/sebastian/Desktop\r\n"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "total 0\r\n",
        "-rw-r--r--  1 sebastian  staff  0 Jun 27 10:11 some.txt\r\n"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "       2\r\n"
       ]
      }
     ],
     "prompt_number": 12
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<br>\n",
      "<br>"
     ]
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Debugging"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%debug \n",
      "def some_func():\n",
      "    var = 'hello world'\n",
      "    for i in range(5):\n",
      "        print(i)\n",
      "    i / 0\n",
      "    return 'finished'"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "> \u001b[0;32m<ipython-input-1-3d5f00f75cf4>\u001b[0m(5)\u001b[0;36msome_func\u001b[0;34m()\u001b[0m\n",
        "\u001b[0;32m      4 \u001b[0;31m        \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
        "\u001b[0m\u001b[0;32m----> 5 \u001b[0;31m    \u001b[0mi\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
        "\u001b[0m\u001b[0;32m      6 \u001b[0;31m    \u001b[0;32mreturn\u001b[0m \u001b[0;34m'finished'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
        "\u001b[0m\n"
       ]
      },
      {
       "name": "stdout",
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "ipdb> var\n"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "'hello world'\n"
       ]
      }
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<br>\n",
      "<br>"
     ]
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Inline Plotting with matplotlib"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%matplotlib inline"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 33
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "import numpy as np\n",
      "from matplotlib import pyplot as plt\n",
      "import math\n",
      "\n",
      "def pdf(x, mu=0, sigma=1):\n",
      "    \"\"\"Calculates the normal distribution's probability density \n",
      "        function (PDF).  \n",
      "        \n",
      "    \"\"\"\n",
      "    term1 = 1.0 / ( math.sqrt(2*np.pi) * sigma )\n",
      "    term2 = np.exp( -0.5 * ( (x-mu)/sigma )**2 )\n",
      "    return term1 * term2\n",
      "\n",
      "\n",
      "x = np.arange(0, 100, 0.05)\n",
      "\n",
      "pdf1 = pdf(x, mu=5, sigma=2.5**0.5)\n",
      "pdf2 = pdf(x, mu=10, sigma=6**0.5)\n",
      "\n",
      "plt.plot(x, pdf1)\n",
      "plt.plot(x, pdf2)\n",
      "plt.title('Probability Density Functions')\n",
      "plt.ylabel('p(x)')\n",
      "plt.xlabel('random variable x')\n",
      "plt.legend(['pdf1 ~ N($\\mu=5$, $\\sigma=2.5$)', 'pdf2 ~ N($\\mu=10$, $\\sigma=6$)'], loc='upper right')\n",
      "plt.ylim([0,0.5])\n",
      "plt.xlim([0,20])\n",
      "\n",
      "plt.show()"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "display_data",
       "png": "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\nLCAYY4wB4C+6RXPdrA827gAAAABJRU5ErkJggg==\n",
       "text": [
        "<matplotlib.figure.Figure at 0x107e67080>"
       ]
      }
     ],
     "prompt_number": 36
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<br>\n",
      "<br>"
     ]
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "C-extensions via the Cython magic"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Cython (see [Cython's C-extensions for Python](http://cython.org)) is basically a hybrid between C and Python and can be pictured as compiled Python code with type declarations.\n",
      "Since we are working in an IPython notebook here, we can make use of the very convenient IPython magic: It will take care of the conversion to C code, the compilation, and eventually the loading of the function.\n",
      "Also, we are adding C type declarations; those type declarations are not necessary for using Cython, however, it will improve the performance of our code significantly."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%load_ext cythonmagic"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 29
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%%cython\n",
      "import numpy as np\n",
      "cimport numpy as np\n",
      "cimport cython\n",
      "@cython.boundscheck(False) \n",
      "@cython.wraparound(False)\n",
      "@cython.cdivision(True)\n",
      "cpdef cython_lstsqr(x_ary, y_ary):\n",
      "    \"\"\" Computes the least-squares solution to a linear matrix equation. \"\"\"\n",
      "    cdef double x_avg, y_avg, var_x, cov_xy,\\\n",
      "         slope, y_interc, temp\n",
      "    cdef double[:] x = x_ary # memoryview\n",
      "    cdef double[:] y = y_ary\n",
      "    cdef unsigned long N, i\n",
      "    \n",
      "    N = x.shape[0]\n",
      "    x_avg = 0\n",
      "    y_avg = 0\n",
      "    for i in range(N):\n",
      "        x_avg += x[i]\n",
      "        y_avg += y[i]\n",
      "    x_avg = x_avg/N\n",
      "    y_avg = y_avg/N\n",
      "    var_x = 0\n",
      "    cov_xy = 0\n",
      "    for i in range(N):\n",
      "        temp = (x[i] - x_avg)\n",
      "        var_x += temp**2\n",
      "        cov_xy += temp*(y[i] - y_avg)\n",
      "    slope = cov_xy / var_x\n",
      "    y_interc = y_avg - slope*x_avg\n",
      "    return (slope, y_interc)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "building '_cython_magic_cf6c91cb1e11de8d2dbe7d9178e469df' extension\n"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "C compiler: /usr/bin/clang -fno-strict-aliasing -Werror=declaration-after-statement -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I/Users/sebastian/miniconda3/envs/py34/include -arch x86_64\n",
        "\n"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "compile options: '-I/Users/sebastian/miniconda3/envs/py34/lib/python3.4/site-packages/numpy/core/include -I/Users/sebastian/miniconda3/envs/py34/include/python3.4m -c'\n"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "clang: /Users/sebastian/.ipython/cython/_cython_magic_cf6c91cb1e11de8d2dbe7d9178e469df.c\n"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "/usr/bin/clang -bundle -undefined dynamic_lookup -L/Users/sebastian/miniconda3/envs/py34/lib -arch x86_64 /Users/sebastian/.ipython/cython/Users/sebastian/.ipython/cython/_cython_magic_cf6c91cb1e11de8d2dbe7d9178e469df.o -L/Users/sebastian/miniconda3/envs/py34/lib -o /Users/sebastian/.ipython/cython/_cython_magic_cf6c91cb1e11de8d2dbe7d9178e469df.so\n"
       ]
      }
     ],
     "prompt_number": 30
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "import numpy as np\n",
      "\n",
      "x_ary = np.array([x_i*np.random.randint(8,12)/10 for x_i in range(100)])\n",
      "y_ary = np.array([y_i*np.random.randint(10,14)/10 for y_i in range(100)])"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 31
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "cython_lstsqr(x_ary, y_ary)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "pyout",
       "prompt_number": 32,
       "text": [
        "(1.1399825800539194, 2.0824398156005444)"
       ]
      }
     ],
     "prompt_number": 32
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<br>\n",
      "<br>"
     ]
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Running Fortran Code"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "There is also a convenient IPython magic command for compiling Fortran code. The Fortran magic uses NumPy's [F2PY](http://www.f2py.com) module for compiling and running the Fortran code. For more information, please see the ['Fortran magic's documentation'](http://nbviewer.ipython.org/github/mgaitan/fortran_magic/blob/master/documentation.ipynb)."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%install_ext https://raw.github.com/mgaitan/fortran_magic/master/fortranmagic.py"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Installed fortranmagic.py. To use it, type:\n",
        "  %load_ext fortranmagic\n"
       ]
      }
     ],
     "prompt_number": 17
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%load_ext fortranmagic"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "javascript": [
        "$.getScript(\"https://raw.github.com/marijnh/CodeMirror/master/mode/fortran/fortran.js\", function () {\n",
        "IPython.config.cell_magic_highlight['magic_fortran'] = {'reg':[/^%%fortran/]};});\n"
       ],
       "metadata": {},
       "output_type": "display_data"
      }
     ],
     "prompt_number": 18
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%%fortran\n",
      "SUBROUTINE fortran_lstsqr(ary_x, ary_y, slope, y_interc)\n",
      "    ! Computes the least-squares solution to a linear matrix equation. \"\"\"\n",
      "    IMPLICIT NONE\n",
      "    REAL(8), INTENT(in), DIMENSION(:) :: ary_x, ary_y\n",
      "    REAL(8), INTENT(out) :: slope, y_interc\n",
      "    REAL(8) :: x_avg, y_avg, var_x, cov_xy, temp\n",
      "    INTEGER(8) :: N, i\n",
      "    \n",
      "    N = SIZE(ary_x)\n",
      "\n",
      "    x_avg = SUM(ary_x) / N\n",
      "    y_avg = SUM(ary_y) / N\n",
      "    var_x = 0\n",
      "    cov_xy = 0\n",
      "    \n",
      "    DO i = 1, N\n",
      "        temp = ary_x(i) - x_avg\n",
      "        var_x = var_x + temp**2\n",
      "        cov_xy = cov_xy + (temp*(ary_y(i) - y_avg))\n",
      "    END DO\n",
      "    \n",
      "    slope = cov_xy / var_x\n",
      "    y_interc = y_avg - slope*x_avg\n",
      "\n",
      "END SUBROUTINE fortran_lstsqr"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "\tBuilding module \"_fortran_magic_a044885f2b0c0feac78a230b6b714e2b\"...\n",
        "\t\tConstructing wrapper function \"fortran_lstsqr\"...\n",
        "\t\t  slope,y_interc = fortran_lstsqr(ary_x,ary_y)\n",
        "\tWrote C/API module \"_fortran_magic_a044885f2b0c0feac78a230b6b714e2b\" to file \"/var/folders/bq/_946cdn92t7bqzz5frpfpw7r0000gp/T/tmp3y_jxtl_/src.macosx-10.5-x86_64-3.4/_fortran_magic_a044885f2b0c0feac78a230b6b714e2bmodule.c\"\n",
        "\tFortran 77 wrappers are saved to \"/var/folders/bq/_946cdn92t7bqzz5frpfpw7r0000gp/T/tmp3y_jxtl_/src.macosx-10.5-x86_64-3.4/_fortran_magic_a044885f2b0c0feac78a230b6b714e2b-f2pywrappers.f\"\n"
       ]
      }
     ],
     "prompt_number": 22
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "import numpy as np\n",
      "\n",
      "x_ary = np.array([x_i*np.random.randint(8,12)/10 for x_i in range(100)])\n",
      "y_ary = np.array([y_i*np.random.randint(10,14)/10 for y_i in range(100)])"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 23
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "fortran_lstsqr(x_ary, y_ary)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "pyout",
       "prompt_number": 25,
       "text": [
        "(1.1313508052697814, 3.681685640167956)"
       ]
      }
     ],
     "prompt_number": 25
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "<br>\n",
      "<br>"
     ]
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Running code from other interpreters: Ruby, Perl, and Bash"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "To use any interpreter that is installed on your system:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%%script perl\n",
      "print 'Hello, World!';"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Hello, World!"
       ]
      }
     ],
     "prompt_number": 44
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Or use the magic command for the respective interpreter directly:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%%perl\n",
      "print 'Hello, World!';"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Hello, World!"
       ]
      }
     ],
     "prompt_number": 45
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%%ruby\n",
      "puts \"Hello, World!\""
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Hello, World!\n"
       ]
      }
     ],
     "prompt_number": 46
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%%bash\n",
      "echo \"Hello World!\""
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Hello World!\n"
       ]
      }
     ],
     "prompt_number": 47
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%%script R --no-save\n",
      "cat(\"Goodbye, World!\\n\")"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "\n",
        "R version 3.0.2 (2013-09-25) -- \"Frisbee Sailing\"\n",
        "Copyright (C) 2013 The R Foundation for Statistical Computing\n",
        "Platform: x86_64-apple-darwin10.8.0 (64-bit)\n",
        "\n",
        "R is free software and comes with ABSOLUTELY NO WARRANTY.\n",
        "You are welcome to redistribute it under certain conditions.\n",
        "Type 'license()' or 'licence()' for distribution details.\n",
        "\n",
        "  Natural language support but running in an English locale\n",
        "\n",
        "R is a collaborative project with many contributors.\n",
        "Type 'contributors()' for more information and\n",
        "'citation()' on how to cite R or R packages in publications.\n",
        "\n",
        "Type 'demo()' for some demos, 'help()' for on-line help, or\n",
        "'help.start()' for an HTML browser interface to help.\n",
        "Type 'q()' to quit R.\n",
        "\n",
        "> cat(\"Goodbye, World!\\n\")\n",
        "Goodbye, World!\n",
        "> \n"
       ]
      }
     ],
     "prompt_number": 55
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [],
     "language": "python",
     "metadata": {},
     "outputs": []
    }
   ],
   "metadata": {}
  }
 ]
}