{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Lecture 11:\n", "\n", "- Learn about **lambda** functions\n", "\n", "- How to use **map( )**, **filter( )**, and **reduce( )** \n", "\n", "- Explore the joys of \"List comprehension\"\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Lambda functions\n", "\n", "You can spell any Greek letter and use it as a variable name EXCEPT for **lambda**. As we learned in Lecture 2, **lambda** is a _reserved word_. Why? Because **lambda** has a special meaning in Python; it is reserved for _anonymous functions_.\n", "\n", "The syntax of a **lambda** function consists of a **name =**, followed by the word **lambda** followed by an _argument list_, a colon (:), and ending with an _expression_. Here is a simple example of an anonymous function that returns the product of the argument list: " ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "f=lambda x,y : x*y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's dissect the statement. \n", "\n", "- **f** is a new kind of object that represents the function,\n", "\n", "- $x$ and $y$ are the arguments of the anonymous function, \n", "\n", "- and the expression $x*y$ is what is returned when the function is called. \n", "\n", "We're familiar with the following syntax for a \"normal\" function:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "def g(x, y):\n", " return x*y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Both $f$ and $g$ take the same arguments and return the same value. They are essentially the same function. \n", "\n", "Let us verify this, by calling both functions with the arguments $x=2$ and $y=10$:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "20\n", "20\n" ] } ], "source": [ "print (f(2,10))\n", "print (g(2,10))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yup. Both the **lambda** function $f$ and the 'regular' function $g$ defined with the keyword **def** are of the type: _function_" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n" ] } ], "source": [ "print (type(f))\n", "print (type(g))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**lambda** functions should seem familiar. They follow the same syntax you use in math to define functions:\n", "\n", "f(x) = x2 +5x + 9 \n", " \n", "So we could easily write this as a **lambda** function like this: \n", "\n", "\n", " \n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "h = lambda x: x**2+5.*x+9" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "For a multivariate function (one with more than one argument), you need to list all the arguments after the reserved word **lambda**. For example, \n", "In math, you’d write the equation for the hypotenus of two sides, $a $ and $b$, as: \n", " \n", "hypotenuse($a$, $b$) = $\\sqrt{a^2+ b^2}$.\n", "\n", "In Python it would be:\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5.0\n" ] } ], "source": [ "hypotenus = lambda a, b: np.sqrt(a**2+b**2)\n", "print (hypotenus(3,4))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Uses of lamda functions\n", "\n", "You may be wondering why **lambda** functions are useful. The answer is that **lambda** functions are anonymous- you don't have to give them a name (although we did when we assigned the function to $f$ in the above examples). This comes in handy if you 1) write or use functions that take in other functions as arguments or 2) you just want a quickie one-off calculation. \n", "\n", "For the first reason, examples of such functions that take **lambda** functions are **map( )**, **reduce( )**, and **filter( )**.\n", "\n", "Anticipating your further questions, you can look at this useful blog post on the subject: https://stackoverflow.com/questions/890128/why-are-python-lambdas-useful" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### map( )\n", "\n", "**lambda** is often used in conjunction with the function **map( )**. \n", "\n", "**map(func, seq)** is a function that takes two arguments: a function, **func**, and a sequence, **seq**. \n", "**func** may be an ordinary function or an anonymous function defined in the first argument of **map( )**. \n", "**seq** is one or more lists on which the function is performed. So **map( )** returns a list generator with the results of whatever **func** did to the elements in **seq**. \n", "\n", "Here is an example which converts kilometers to miles (1 km = (5/8) miles). " ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "[5.0, 6.25, 15.0]\n" ] } ], "source": [ "km_to_mi=map(lambda x:(5./8.)*x,[8,10,24])\n", "print (km_to_mi) # see the list generator \n", "print (list(km_to_mi)) # see the list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The anonymous function was defined as the first argument of **map( )**. This **lambda** function takes a single variable _x_ (in km), converts it to miles by multiplying by 5/8, and returns the value. The **map( )** function then takes a sequence as the second argument, in this case, the sequence is a list with 8,10, and 24 as elements. **map( )** converts each of the values in the list to miles by applying the anonymous function.\n", "\n", "If our **lambda** function has TWO variables, e.g., $x,y$, we must pass **map( )** TWO lists of the same length for **seq**:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "map(lambda x,y : x*y,[2,3,4],[5,6,7]);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The values for $x$ get taken from the first list of numbers, while $y$ gets taken from the second list. **map( )** returns a list with the product of the two input lists:\n", "\n" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[10, 18, 28]" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(map(lambda x,y : x*y,[2,3,4],[5,6,7]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another way to use **map( )** is to define the lists and functions ahead of time, then apply the **map( )** to them as follows:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[10, 18, 28]\n" ] } ], "source": [ "a=[2,3,4]\n", "b=[5,6,7]\n", "f=lambda x,y : x*y\n", "map(f,a,b) \n", "# but let's see what it does with a print statement: \n", "print (list(map(f,a,b) ))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Well that was cool.... \n", "\n", "You can see that $x$ snags values from the first list, $a$ and $y$ uses values from the second list, $b$. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### filter( )\n", "\n", "**filter(func, seq)** is another example of a _function_ that takes a _function_, **func**, and a sequence, **seq** as arguments. The function supplied to filter must return a boolean- either **True** or **False**. **filter()** then applies that function to all the values in the sequence and returns the values that are **True**. Let's walk through this step by step beginning with a function that returns **True** or **False**. \n", "\n", " Remember that _modulo_ is the remainder and in Python we can find the modulo of a given variable $x$ with, for example 2 by this syntax: $x\\%2$ (spoken as 'x mod 2'). As an example of a boolean function, we can apply _modulo_ 2 to test whether a number is even or odd. When you divide $x$ by 2, even numbered values of $x$ will return 0 (and odd numbers return 1). And, remember that 0 is **False** and 1 is **True**. " ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "modulo of 2 divided by 2: 0\n", "modulo of 400 divided by 360: 40\n" ] } ], "source": [ "print ('modulo of 2 divided by 2: ',2%2)\n", "# and you can see that modulo is handy for keeping values between 0 and 360\n", "print ('modulo of 400 divided by 360: ',400%360) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's create an anonymous function that tests whether numbers are even or odd by the value they return. As you just learned, if modulo returns 0 then the remainder is 0 and the original value was even, whereas, if it returns 1, then the original value was odd: " ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "0\n", "1\n" ] } ], "source": [ "f= lambda x: x % 2\n", "\n", "print (f(2))\n", "print (f(3))\n", "print (f(4))\n", "print (f(5))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can add the relational operator **==** and return **True** or **False** instead of 0 or 1: " ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "False\n", "True\n", "False\n" ] } ], "source": [ "f= lambda x: x % 2 == 0\n", "\n", "\n", "print (f(2))\n", "print (f(3))\n", "print (f(4))\n", "print (f(5))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we can use **filter( )** and the function we defined to find the even values in a sequence. Similar to **map( )**, **filter ( )** applies the function to every value in the **list**, but **filter ( )** will only return the values that evaluate to **True**. The output of **filter( )** is a **list generator**, not itself a **list**, but we can turn it into a **list**. For example:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f= lambda x: x % 2 == 0 # tests if a number is even or odd\n", "mylist = list(range(20))\n", "list(filter(f, mylist)) # returns only the even ones\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### reduce( )\n", "\n", "**reduce( )** is another function that regularly uses a **lambda** function. Like **map( )** and **filter( )**, **reduce(func, seq)** takes two arguments: a function and a sequence. With **reduce( )**, the function is applied to sequential pairs of elements in the list until the list is reduced to a single value, which is then returned. In detail, the function is first performed on the first two elements. The result then replaces the first element and the second is removed. The same function is again applied to the first two elements of the new list, replacing them with the single value from the function, and so on until only a single value remains. \n", "\n", "**reduce( )** is no longer standard in Python, so it must be imported with the command:\n", "\n", "import **reduce( )** from **functools**. \n", "\n", "So let's do that. \n", "\n" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "from functools import reduce " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's try an example. We could use **reduce( )** to return the factorial of a number $n$. \n", "Remember that the factorial is \"the product of an integer and all the positive integers below it\". \n", "So, we can use our **lambda** function defined above, which returns the product of two numbers. If we use the **reduce** function and make the **lambda** operate on a list of numbers from 1 to $n$, we will get the desired product at the end. " ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "720" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "n=6\n", "reduce(lambda x,y:x*y,range(1,n+1)) # performs the lambda function sequentially on the list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can compare our function with the **Numpy** version, **np.math.factorial)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "720" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.math.factorial(n)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Whew!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### List comprehensions\n", "\n", "Another succinct way to iterate over sequences and apply different operations, is through List, Dictionary, and Set comprehensions.\n", "\n", "A List comprehension is a convenient way of applying an operation to a collection of objects. It takes this basic form:\n", "\n", "\\[**expression for** element **in** collection **if** condition\\]\n", "\n", "Here is an example that takes a list of strings, looks for those with lengths greater than 5 and returns the upper case version using the **string.upper( )** method for strings: " ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['MT. EVEREST', 'MAUNA LOA', 'SP MOUNTAIN']" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mtList=['Andes','Mt. Everest','Mauna Loa','SP Mountain']\n", "[s.upper() for s in mtList if len(s)>5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Fun fact: you can get the lower case equivalents with the method **string.lower( )**.]\n", "\n", "\n", "Note that you could achieve the same result (the upper case list of all volcanoes with names having more than 5 characters) using our old friend the **for** loop:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['MT. EVEREST', 'MAUNA LOA', 'SP MOUNTAIN']" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "another_list = []\n", "for s in mtList:\n", " if(len(s)>5):\n", " another_list.append(s.upper())\n", "another_list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or (challenge!) by using **filter( )** and **map( )** and an anonymous function:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Each of these three approaches performs similarly, but the list comprehension is the most succinct. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dictionary Comprehension\n", "Dictionary comprehensions are similar to list comprehensions, but they generate key-value pairs instead of lists. Dictionary comprehensions follow the format:\n", " \n", "{**key:value for** variable **in** collection **if** condition}\n", "\n", "\n", "The following Dictionary comprehension generates a dictionary with a word from **mtList** as the key and the length of the word as the value" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'Andes': 5, 'Mt. Everest': 11, 'Mauna Loa': 9, 'SP Mountain': 11}" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mtList=['Andes','Mt. Everest','Mauna Loa','SP Mountain'] # to remind you what mylist was\n", "{s:len(s) for s in mtList} # dictionary comprehension with mylist" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice the {key:value, key:value} structure of the output is a dictionary. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Set comprehension\n", "\n", "A Set comprehension, returns a set and follows this format:\n", "\n", " {**expression for** value **in** collection **if** condition}\n", "\n", "\n", "\n", "The following Set comprehension creates a set composed of the lengths of the words in mylist" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{5, 9, 11}" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "{len(s) for s in mtList}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can tell that a set was returned because it is in curly braces with no keys. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Complicated comprehensions\n", "List, Dictionary, and Set comprehensions can also replace complicated, nested loops. Here's an example that generates a list of x,y,z triplets if the values obey Pythagorus' rules for right triangles. Chew on it, until you get it: " ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(3, 4, 5),\n", " (5, 12, 13),\n", " (6, 8, 10),\n", " (7, 24, 25),\n", " (8, 15, 17),\n", " (9, 12, 15),\n", " (10, 24, 26),\n", " (12, 16, 20),\n", " (15, 20, 25),\n", " (20, 21, 29)]" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[(x,y,z) for x in range(1,30) \\\n", " for y in range(x,30) for z in range(y,30) \\\n", " if x**2 + y**2 == z**2]" ] } ], "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.7" } }, "nbformat": 4, "nbformat_minor": 1 }