In [1]:

```
# Set up packages for lecture. Don't worry about understanding this code, but
# make sure to run it if you're following along.
import numpy as np
import babypandas as bpd
import pandas as pd
from matplotlib_inline.backend_inline import set_matplotlib_formats
import matplotlib.pyplot as plt
set_matplotlib_formats("svg")
plt.style.use('ggplot')
np.set_printoptions(threshold=20, precision=2, suppress=True)
pd.set_option("display.max_rows", 7)
pd.set_option("display.max_columns", 8)
pd.set_option("display.precision", 2)
```

- Lab 3 is due
**Saturday 2/4 at 11:59PM**. - Homework 3 is due
**Tuesday 2/7 at 11:59PM**. - The Midterm Project (Restaurants 🍔🍟) is released and due
**Tuesday 2/14 at 11:59PM**.- Working with a partner is recommended but not required.
- Partners must follow these partner guidelines. In particular, you must both contribute to all parts of the project and not split up the problems.
- Your partner can be from any lecture section.
- Course tutors will host a mixer
**today from 1:30 to 2:15PM**in the outdoor courtyard behind Center Hall. Come before or after discussion section to connect with other students looking for a project partner. Here's a menu of questions to start the conversation.

- Booleans.
- Conditional statements (i.e.
`if`

-statements). - Iteration (i.e.
`for`

-loops).

**Note:**

- We've finished introducing new DataFrame manipulation techniques.
- Today we'll cover some foundational programming tools, which will be very relevant as we start to cover more ideas in statistics (next week).

`bool`

is a data type in Python, just like`int`

,`float`

, and`str`

.- It stands for "Boolean", named after George Boole, an early mathematician.

- There are only two possible Boolean values:
`True`

or`False`

.- Yes or no.
- On or off.
- 1 or 0.

- Comparisons result in Boolean values.

In [2]:

```
capstone = 'finished'
units = 123
```

In [3]:

```
units >= 180
```

Out[3]:

False

In [4]:

```
type(units >= 180)
```

Out[4]:

bool

`not`

¶There are three operators that allow us to perform arithmetic with Booleans – `not`

, `and`

, and `or`

.

`not`

flips `True`

↔️ `False`

.

In [5]:

```
capstone
```

Out[5]:

'finished'

In [6]:

```
capstone == 'finished'
```

Out[6]:

True

In [7]:

```
not capstone == 'finished'
```

Out[7]:

False

`and`

operator¶The `and`

operator is placed between two `bool`

s. It is `True`

if **both** are `True`

; otherwise, it's `False`

.

In [8]:

```
capstone
```

Out[8]:

'finished'

In [9]:

```
units
```

Out[9]:

123

In [10]:

```
capstone == 'finished' and units >= 180
```

Out[10]:

False

In [11]:

```
capstone == 'finished' and units >= 120
```

Out[11]:

True

`or`

operator¶The `or`

operator is placed between two `bool`

s. It is `True`

if **at least one** is `True`

; otherwise, it's `False`

.

In [12]:

```
capstone
```

Out[12]:

'finished'

In [13]:

```
units
```

Out[13]:

123

In [14]:

```
capstone == 'finished' or units >= 180
```

Out[14]:

True

In [15]:

```
# Both are True!
capstone == 'finished' or units >= 0
```

Out[15]:

True

In [16]:

```
# Both are False!
capstone == 'not started' or units >= 180
```

Out[16]:

False

In [17]:

```
capstone
```

Out[17]:

'finished'

In [18]:

```
units
```

Out[18]:

123

In [19]:

```
capstone == 'finished' or (capstone == 'in progress' and units >= 180)
```

Out[19]:

True

In [20]:

```
# Different meaning!
(capstone == 'finished' or capstone == 'in progress') and units >= 180
```

Out[20]:

False

In [21]:

```
# "and" has precedence.
capstone == 'finished' or capstone == 'in progress' and units >= 180
```

Out[21]:

True

For instance, `not (a and b)`

is **different** than `not a and not b`

! If you're curious, read more about De Morgan's Laws.

In [22]:

```
capstone
```

Out[22]:

'finished'

In [23]:

```
units
```

Out[23]:

123

In [24]:

```
not (capstone == 'finished' and units >= 180)
```

Out[24]:

True

In [25]:

```
(not capstone == 'finished') and (not units >= 180)
```

Out[25]:

False

`&`

and `|`

vs. `and`

and `or`

¶- Use the
`&`

and`|`

operators between two**Series**. Arithmetic will be done element-wise (separately for each row).- This is relevant when writing DataFrame queries, e.g.
`df[(df.get('capstone') == 'finished') & (df.get('units') >= 180)]`

.

- This is relevant when writing DataFrame queries, e.g.

- Use the
`and`

and`or`

operators between two**individual**Booleans.- e.g.
`capstone == 'finished' and units >= 180`

.

- e.g.

Suppose we define `a = True`

and `b = True`

. What does the following expression evaluate to?

```
not (((not a) and b) or ((not b) or a))
```

A. `True`

B. `False`

C. Could be either one

`in`

operator¶Sometimes, we'll want to check if a particular element is in a list/array, or a particular **substring** is in a string. The `in`

operator can do this for us:

In [26]:

```
3 in [1, 2, 3]
```

Out[26]:

True

In [27]:

```
'hey' in 'hey my name is'
```

Out[27]:

True

In [28]:

```
'dog' in 'hey my name is'
```

Out[28]:

False

`if`

-statements¶- Often, we'll want to run a block of code only if a particular conditional expression is
`True`

. - The syntax for this is as follows (don't forget the colon!):

```
if <condition>:
<body>
```

- Indentation matters!

In [29]:

```
capstone = 'finished'
capstone
```

Out[29]:

'finished'

In [30]:

```
if capstone == 'finished':
print('Looks like you are ready to graduate!')
```

Looks like you are ready to graduate!

`else`

¶`else`

: Do something else if the specified condition is `False`

.

In [31]:

```
capstone = 'finished'
capstone
```

Out[31]:

'finished'

In [32]:

```
if capstone == 'finished':
print('Looks like you are ready to graduate!')
else:
print('Before you graduate, you need to finish your capstone project.')
```

Looks like you are ready to graduate!

`elif`

¶- What if we want to check more than one condition? Use
`elif`

. `elif`

: if the specified condition is`False`

, check the next condition.- If that condition is
`False`

, check the next condition, and so on, until we see a`True`

condition.- After seeing a
`True`

condition, it evaluates the indented code and stops.

- After seeing a
- If none of the conditions are
`True`

, the`else`

body is run.

In [33]:

```
capstone = 'in progress'
units = 123
```

In [34]:

```
if capstone == 'finished' and units >= 180:
print('Looks like you are ready to graduate!')
elif capstone != 'finished' and units < 180:
print('Before you graduate, you need to finish your capstone project and take', 180 - units, 'more units.')
elif units >= 180:
print('Before you graduate, you need to finish your capstone project.')
else:
print('Before you graduate, you need to take', 180 - units, 'more units.')
```

Before you graduate, you need to finish your capstone project and take 57 more units.

What if we use `if`

instead of `elif`

?

In [35]:

```
if capstone == 'finished' and units >= 180:
print('Looks like you are ready to graduate!')
if capstone != 'finished' and units < 180:
print('Before you graduate, you need to finish your capstone project and take', 180 - units, 'more units.')
if units >= 180:
print('Before you graduate, you need to finish your capstone project.')
else:
print('Before you graduate, you need to take', 180 - units, 'more units.')
```

Below, complete the implementation of the function, `grade_converter`

, which takes in a percentage grade (`grade`

) and returns the corresponding letter grade, according to this table:

Letter | Range |
---|---|

A | [90, 100] |

B | [80, 90) |

C | [70, 80) |

D | [60, 70) |

F | [0, 60) |

Your function should work on these examples:

```
>>> grade_converter(84)
'B'
>>> grade_converter(60)
'D'
```

In [36]:

```
def grade_converter(grade):
...
```

In [37]:

```
grade_converter(84)
```

In [38]:

```
grade_converter(60)
```

```
def mystery(a, b):
if (a + b > 4) and (b > 0):
return 'bear'
elif (a * b >= 4) or (b < 0):
return 'triton'
else:
return 'bruin'
```

Without running code:

- What does
`mystery(2, 2)`

return? - Find inputs so that calling
`mystery`

will produce`'bruin'`

.

In [39]:

```
def mystery(a, b):
if (a + b > 4) and (b > 0):
return 'bear'
elif (a * b >= 4) or (b < 0):
return 'triton'
else:
return 'bruin'
```

`for`

-loops¶In [40]:

```
import time
print('Launching in...')
for x in [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]:
print('t-minus', x)
time.sleep(0.5) # Pauses for half a second
print('Blast off! 🚀')
```

`for`

-loops¶- Loops allow us to repeat the execution of code. There are two types of loops in Python; the
`for`

-loop is one of them. - The syntax of a
`for`

-loop is as follows:for <element> in <sequence>: <for body>

- Read this as: "for each element of this sequence, repeat this code."
- Note: lists, arrays, and strings are all examples of sequences.

- Like with
`if`

-statements, indentation matters!

In [41]:

```
num = 4
print(num, 'squared is', num ** 2)
num = 2
print(num, 'squared is', num ** 2)
num = 1
print(num, 'squared is', num ** 2)
num = 3
print(num, 'squared is', num ** 2)
```

4 squared is 16 2 squared is 4 1 squared is 1 3 squared is 9

In [42]:

```
# The loop variable can be anything!
list_of_numbers = [4, 2, 1, 3]
for num in list_of_numbers:
print(num, 'squared is', num ** 2)
```

4 squared is 16 2 squared is 4 1 squared is 1 3 squared is 9

The line `print(num, 'squared is', num ** 2)`

is run four times:

- On the first iteration,
`num`

is 4. - On the second iteration,
`num`

is 2. - On the third iteration,
`num`

is 1. - On the fourth iteration,
`num`

is 3.

This happens, even though there is no `num =`

anywhere.

Using the array `colleges`

, write a `for`

-loop that prints:

```
Revelle College
John Muir College
Thurgood Marshall College
Earl Warren College
Eleanor Roosevelt College
Sixth College
Seventh College
```

for college in colleges: print(college + ' College')

In [43]:

```
colleges = np.array(['Revelle', 'John Muir', 'Thurgood Marshall',
'Earl Warren', 'Eleanor Roosevelt', 'Sixth', 'Seventh'])
```

In [44]:

```
...
```

Out[44]:

Ellipsis

- Recall, each element of a list/array has a numerical position.
- The position of the first element is 0, the position of the second element is 1, etc.

- We can write a
`for`

-loop that accesses each element in an array by using its position. `np.arange`

will come in handy.

In [45]:

```
actions = np.array(['ate', 'slept', 'exercised'])
feelings = np.array(['content 🙂', 'energized 😃', 'exhausted 😩'])
```

In [46]:

```
len(actions)
```

Out[46]:

3

In [47]:

```
for i in np.arange(len(actions)):
print(i)
```

0 1 2

In [48]:

```
for i in np.arange(len(actions)):
print('I', actions[i], 'and I felt', feelings[i])
```

I ate and I felt content 🙂 I slept and I felt energized 😃 I exercised and I felt exhausted 😩

We don't have to use the loop variable!

In [49]:

```
for i in np.arange(3):
print('🐻')
print('👧🏼')
```

🐻 🐻 🐻 👧🏼

- In the next few lectures, we'll learn how to
**simulate**random events, like flipping a coin.

- Often, we will:
- Run an experiment, e.g. "flip 10 coins."
- Keep track of some result, e.g. "number of heads."
- Repeat steps 1 and 2 many, many times using a
`for`

-loop.

- To store our results, we'll typically use an
`int`

or an array. - If using an
`int`

, we define an`int`

variable (usually to`0`

) before the loop, then use`+`

to add to it inside the loop. - If using an array, we create an array (usually empty) before the loop, then use
`np.append`

to add to it inside the loop.

`np.append`

¶- This function takes two inputs:
- an array
- an element to add on to the end of the array

- It returns a new array. It does not modify the input array.
- We typically use it like this to extend an array by one element:
`name_of_array = np.append(name_of_array, element_to_add)`

- ⚠️
**Remember to store the result!**

In [50]:

```
some_array = np.array([])
```

In [51]:

```
np.append(some_array, 'hello')
```

Out[51]:

array(['hello'], dtype='<U32')

In [52]:

```
some_array
```

Out[52]:

array([], dtype=float64)

In [53]:

```
# Need to save the new array!
some_array = np.append(some_array, 'hello')
some_array
```

Out[53]:

array(['hello'], dtype='<U32')

In [54]:

```
some_array = np.append(some_array, 'there')
some_array
```

Out[54]:

array(['hello', 'there'], dtype='<U32')

The function `flip(n)`

flips `n`

fair coins and returns the number of heads it saw. (Don't worry about how it works for now.)

In [55]:

```
def flip(n):
'''Returns the number of heads in n simulated coin flips, using randomness.'''
return np.random.multinomial(n, [0.5, 0.5])[0]
```

In [56]:

```
# Run this cell a few times – you'll see different results!
flip(10)
```

Out[56]:

3

Let's repeat the act of flipping 10 coins, 10000 times.

- Each time, we'll use the
`flip`

function to flip 10 coins and compute the number of heads we saw. - We'll store these numbers in an array,
`heads_array`

. - Every time we use our
`flip`

function to flip 10 coins, we'll add an element to the end of`heads_array`

.

In [57]:

```
# heads_array starts empty – before the simulation, we haven't flipped any coins!
heads_array = np.array([])
for i in np.arange(10000):
# Flip 10 coins and count the number of heads.
num_heads = flip(10)
# Add the number of heads seen to heads_array.
heads_array = np.append(heads_array, num_heads)
```

`heads_array`

contains 10000 numbers, each corresponding to the number of heads in 10 simulated coin flips.

In [58]:

```
heads_array
```

Out[58]:

array([3., 4., 3., ..., 7., 5., 4.])

In [59]:

```
len(heads_array)
```

Out[59]:

10000

In [60]:

```
(bpd.DataFrame().assign(num_heads=heads_array)
.plot(kind='hist', density=True, bins=np.arange(0, 12), ec='w', legend=False,
title = 'Distribution of the number of heads in 10 coin flips')
);
```

`for`

-loops in DSC 10¶Almost every

`for`

-loop in DSC 10 will use the**accumulator pattern**.- This means we initialize a variable, and repeatedly add on to it within a loop.

Do

**not**use`for`

-loops to perform mathematical operations on every element of an array or Series.- Instead use DataFrame manipulations and built-in array or Series methods.

Helpful video 🎥: For Loops (and when not to use them) in DSC 10.

String are sequences, so we can iterate over them, too!

In [61]:

```
for letter in 'uc san diego':
print(letter.upper())
```

U C S A N D I E G O

In [62]:

```
'california'.count('a')
```

Out[62]:

2

Below, complete the implementation of the function `vowel_count`

, which returns the number of vowels in the input string `s`

(including repeats). Example behavior is shown below.

```
>>> vowel_count('king triton')
3
>>> vowel_count('i go to uc san diego')
8
```

In [63]:

```
def vowel_count(s):
# We need to keep track of the number of vowels seen so far. Before we start, we've seen zero vowels.
number = 0
# For each of the 5 vowels:
# Count the number of occurrences of this vowel in s.
# Add this count to the variable number.
# Once we've gotten through all 5 vowels, return the answer.
```

In [64]:

```
vowel_count('king triton')
```

In [65]:

```
vowel_count('i go to uc san diego')
```

`if`

-statements allow us to run pieces of code depending on whether certain conditions are`True`

.`for`

-loops are used to repeat the execution of code for every element of a sequence.- Lists, arrays, and strings are examples of sequences.

- Probability.
- A math lesson – no code!