Skip to content

Instantly share code, notes, and snippets.

@brianspiering
Last active September 6, 2017 18:27
Show Gist options
  • Save brianspiering/ae91413ab693bb066b483d330900d585 to your computer and use it in GitHub Desktop.
Save brianspiering/ae91413ab693bb066b483d330900d585 to your computer and use it in GitHub Desktop.
Closures for Data Science
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Closures for Data Science\n",
"------"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Problem\n",
"-----\n",
"\n",
"Sometimes you have data that is outside of valid bounds and has to be trimmed. If the data below a value, it should be changed to take the minimum value. If the data is above a value, it should be change to take on the maximum value.\n",
"\n",
"For example, RBG values can only be from 0 to 255:\n",
"\n",
"![](http://tutorial.techaltum.com/images/css-colors.jpg)\n",
"\n",
"Here is a bound function:"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def bound(value, min_value, max_value):\n",
" \"Limit value between the min and max\"\n",
" return min(max_value, max(value, min_value))"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [],
"source": [
"assert bound(42, min_value=0, max_value=255) == 42\n",
"assert bound(-1, min_value=0, max_value=255) == 0\n",
"assert bound(256, min_value=0, max_value=255) == 255"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Python Function Superpower: <br> A Python function can return any object, including an another function!\n",
"--------\n",
"\n",
"Let's use that feature to create a variation of our bound function"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def make_bound_function(min_value, max_value):\n",
" \"Define a bound function with a certain min and max\"\n",
" def bound(value):\n",
" \"Limit value between the min and max\"\n",
" return min(max_value, max(value, min_value))\n",
" return bound"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Take moment to wrap your head around the idea that the outer function `make_bound_func` creates and returns the inner function `bound`"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# Call the outer function to define/return the inner fuction\n",
"bound_rbg = make_bound_function(min_value=0, max_value=255)"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"function"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# The outer function returns the inner function\n",
"type(bound_rbg)"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"42\n",
"0\n",
"255\n"
]
}
],
"source": [
"# Call the inner function to bound the values\n",
"print(bound_rbg(42))\n",
"print(bound_rbg(-1))\n",
"print(bound_rbg(256))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This is a closure, a function wrapped in another function.\n",
"\n",
"The wrapping __binds__ the values/state to the inner function."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Another Problem\n",
"----\n",
"\n",
"Let's say our system can only take non-negative values.\n",
"\n",
"We can define another bound function that forces all negative numbers to be zero."
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# Let's call the outer function again to define/return a different inner fuction\n",
"from math import inf\n",
"\n",
"bound_non_negative = make_bound_function(min_value=0, max_value=inf)"
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"42\n",
"0\n",
"1000000\n"
]
}
],
"source": [
"# Call the inner function to bound the values\n",
"print(bound_non_negative(42))\n",
"print(bound_non_negative(-1))\n",
"print(bound_non_negative(1_000_000))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Summary\n",
"-----\n",
"\n",
"Closures are not required in Python. You can just pass a series of arguments to a function.\n",
"\n",
"Closures are a nice-to-have tool that can simplify complex function calls and create guard-rails for programming. Closures help build large, concurrent systems by \"freezing\" functions and limiting the dynamic nature of Python."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<br>\n",
"<br> \n",
"<br>\n",
"\n",
"----"
]
}
],
"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": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment