In [10]:

```
import numpy as np
```

Learn about

**lambda**functionsHow to use

**map( )**,**filter( )**, and**reduce( )**Explore the joys of "List comprehension"

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*.

The syntax of a **lambda** function consists of a **name =**, followed by the word **lambda** followed by an *argument list*, a colon (:), and an *expression*. Here is a simple example of an anonymous function that returns the product of the argument list:

In [11]:

```
f=lambda x,y : x*y
```

Let's dissect the statement.

**f**is a new kind of object that represents the function,$x$ and $y$ are the arguments of the anonymous function,

and the expression $x*y$ is what is returned when the function is called.

We're familiar with the following syntax for a "normal" function:

In [12]:

```
def g(x, y):
return x*y
```

Both $f$ and $g$ take the same arguments and return the same value. They are essentially the same function.

Let us verify this, by calling both functions with the parameters $x=2$ and $y=10$:

In [13]:

```
print (f(2,10))
print (g(2,10))
```

Yup. Both the **lambda** function f and the 'regular' function $g$ defined with the keyword **def** are of the type: *function*

In [14]:

```
print (type(f))
print (type(g))
```

**lambda** functions should seem familiar. They follow the same syntax you use in math to define functions:

f(x) = x^{2} +5x + 9
So we could easily write this as a **lambda** function like this:

In [14]:

```
h = lambda x: x**2+5.*x+9
```

For a multivariate function, you need to list all the arguments after the reserved word **lambda**. For example,
In math, you’d write the equation for the hypotenus of two sides, a and b, as:
hypotenuse(a, b) = $\sqrt{a^2+ b^2}$.

In Python it would be:

In [7]:

```
hypotenus = lambda a, b: np.sqrt(a**2+b**2)
print (hypotenus(3,4))
```

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.

For the first reason, examples of such functions that take lambda functions are **map( )**, **reduce( )**, and **filter( )**.

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

**lambda** is often used in conjunction with the function **map( )**.

**map(func, seq)** is a function that takes two arguments: a function, **func**, and a sequence, **seq**.
**func** may be an ordinary function or an anonymous function defined in the first argument of **map( )**.

**seq** is one or more lists on which the function is performed. So **map( )** returns a list generator (in Python 3) with the results of whatever **func** did to the elements in **seq**.

Here is an example which converts kilometers to miles (1 mile = (5/8) kilometers).

In [9]:

```
km_from_mi=map(lambda x:(5./8.)*x,[8,10,24])
# python 2.7 versus python 3. python 3 returns the list generator, not the list
# if you are running python 2.7, both of these will look the same
print (km_from_mi) # see the list generator
print (list(km_from_mi)) # see the list
```

The anonymous function was defined as the first argument of **map( )**. This **lambda** function takes a single variable *x* (in miles), converts it to km by multiplying by 5/8, and then 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 kilometers by applying the anonymous function.

If our **lambda** function has TWO variables, e.g., $x,y$, we must pass **map( )** TWO lists of the same length for **seq**:

In [4]:

```
map(lambda x,y : x*y,[2,3,4],[5,6,7]);
```

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:

In [15]:

```
print (list(map(lambda x,y : x*y,[2,3,4],[5,6,7])))
```

Another way to use **map( )** is to define the lists and functions ahead of time, then apply the **map( )** to them as follows:

In [16]:

```
a=[2,3,4]
b=[5,6,7]
f=lambda x,y : x*y
map(f,a,b)
# but let's see what it does with a print statement:
print (list(map(f,a,b) ))
```

Well that was dope....

You can see that $x$ snags values from the first list, $a$ and $y$ from the second list, $b$.

**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**.

As an example of a boolean function, we can apply *modulo* 2 ($x%2$) to test whether a number is even or odd. [Remember that *modulo* is the remainder, so when you divide $x$ by 2, even numbered values of $x$ will return 0 (and odd numbers return 1. Also, remember that 0 is **False** and 1 is **True**]

In [11]:

```
print ('modulo of 2 divided by 2: ',2%2)
print ('modulo of 400 divided by 360: ',400%360) # handy for keeping values between 0 and 360
```

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 is even, whereas, if it returns 1, then the original value is odd:

In [12]:

```
f= lambda x: x % 2
print (f(2))
print (f(3))
print (f(4))
print (f(5))
```

We can add the relational operator **==** and return **True** or **False** instead of 0 or 1:

In [13]:

```
f= lambda x: x % 2 == 0
print (f(2))
print (f(3))
print (f(4))
print (f(5))
```

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**. Note that in Python 3, you must turn the output of **filter( )** into a list. For example:

In [17]:

```
f= lambda x: x % 2 == 0 # and again, for clarity
mylist = list(range(50))
list(filter(f, mylist))
```

Out[17]:

**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.

New in Python 3, we must first import **reduce( )** from **functools**. Perhaps **reduce( )** is going the way of the passenger pidgeon?

In [4]:

```
from functools import reduce # have to do this in Python 3 - not in Python 2
```

Let's try an example. We could use **reduce( )** to return the factorial of a number $n$.
Remember that the factorial is "the product of an integer and all the integers below it".

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.

In [20]:

```
n=6
reduce(lambda x,y:x*y,range(1,n+1)) # performs the lambda function sequentially on the list
```

Out[20]:

We can compare our function with the **Numpy** version, **np.math.factorial)

In [21]:

```
np.math.factorial(n)
```

Out[21]:

Whew!

Another succinct way to iterate over sequences and apply different operations, is through List, Dictionary, and Set comprehensions.

A List comprehension is a convenient way of applying an operation to a collection of objects. It takes this basic form:

[**expression for** element **in** collection **if** condition]

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:

In [25]:

```
mtList=['Andes','Mt. Everest','Mauna Loa','SP Mountain']
[s.upper() for s in mtList if len(s)>5]
```

Out[25]:

[Fun fact: you can get the lower case equivalents with the method **string.lower( )**.]

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:

In [26]:

```
another_list = []
for s in mtList:
if(len(s)>5):
another_list.append(s.upper())
another_list
```

Out[26]:

Or (challenge!) by using **filter( )** and **map( )** and an anonymous function:

Each of these three approaches performs similarly, but the list comprehension is the most succinct.

Dictionary comprehensions are similar to list comprehensions, but they generate key-value pairs instead of lists. Dictionary comprehensions follow the format:

{**key:value for** variable **in** collection **if** condition}

The following Dictionary comprehension generates a dictionary with a word from **mtList** as the key and the length of the word as the value

In [27]:

```
mtList=['Andes','Mt. Everest','Mauna Loa','SP Mountain'] # to remind you what mylist was
{s:len(s) for s in mtList} # dictionary comprehension with mylist
```

Out[27]:

Notice the {key:value, key:value} structure of the output is a dictionary.

A Set comprehension, returns a set and follows this format:

{**expression for** value **in** collection **if** condition}

The following Set comprehension creates a set composed of the lengths of the words in mylist

In [28]:

```
{len(s) for s in mtList}
```

Out[28]:

You can tell that a set was returned because it is in curly braces with no keys.

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:

In [29]:

```
[(x,y,z) for x in range(1,30) \
for y in range(x,30) for z in range(y,30) \
if x**2 + y**2 == z**2]
```

Out[29]:

Create a new notebook with the name format: Lastname_HomeworkNumber. For example, **CychB_HW_4**

Create a markdown block in which you will describe what the notebook does.

The equations for an ellipse are:

x =a cos( $\theta$ ) , y=b sin( $\theta$ )

$a$ is the major radius

$b$ is the minor radius

$\theta$ is the anglea. Write a

**lambda function**that returns $x$ and $y$ given $a$, $b$, and $\theta$. Assume that theta goes from 0 to 360. Remember that trig functions assume the arguments are in radians

b. Plot your ellipse using**matplotlib.pyplot**(imported as**plt**). Use the**%matplotlib inline**magic command

c. To make the axes of equal length, use the**plt.axis('equal')**command. Otherwise, your axes will be squished.

- Write a
**lambda function**that returns the square of an input parameter $x$

a. Use**map( )**to generate a list of squares for a sequence with 10 values

b. Use**filter( )**and the lambda function to generate a list of squares that are between 5 and 50.

c. Use a**list comprehension**to generate the same list with only one line of code. - Create a
**Class**, it should include at least 3**attributes**and 3**methods**. Be creative! Here are a few possibilities- Card, Deck, Planet, Phone Contact, Ocean, Student, Cellphone, Dog, Car

a. Save your class in a**module**, import the module into your notebook

b. Create 3**instances**of your class, change the value of at least one attribute for one instance of your class

c. Call all three of your methods- you can use any of the instances of your class

In [ ]:

```
```