# Run this cell to set up packages for lecture.
from lec12_imports import *
for
-loops¶for
-loops are used to repeat the execution of code for every element of a sequence.for x in ["my boyfriend", "a god", "the breeze in my hair on the weekend", "a relaxing thought"]:
print("Karma is " + x)
Karma is my boyfriend Karma is a god Karma is the breeze in my hair on the weekend Karma is a relaxing thought
# Saving the lyrics in a variable.
lyrics = ""
for x in ["my boyfriend", "a god", "the breeze in my hair on the weekend", "a relaxing thought"]:
lyrics = lyrics + "Karma is " + x +"\n"
lyrics
'Karma is my boyfriend\nKarma is a god\nKarma is the breeze in my hair on the weekend\nKarma is a relaxing thought\n'
print(lyrics)
Karma is my boyfriend Karma is a god Karma is the breeze in my hair on the weekend Karma is a relaxing thought
int
, array, or string.int
, we define an int
variable (usually set to 0
) before the loop, then use +
to add to it inside the loop.np.append
to add to it inside the loop.""
) before the loop, then use string concatenation +
to add to it inside the loop.int
, array, or string – is called the accumulator pattern.for
-loops in DSC 10¶for
-loop in DSC 10 will use the accumulator pattern. Do not use for
-loops to perform mathematical operations on every element of an array or Series.
Helpful video 🎥: For Loops (and when not to use them) in DSC 10.
String are sequences, so we can iterate over them, too!
for letter in 'uc san diego':
print(letter.upper())
U C S A N D I E G O
'california'.count('a')
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
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: for vowel in 'aeiou': # Count the number of occurrences of this vowel in s. num_vowel = s.count(vowel) # Add this count to the variable number. number = number + num_vowel # Once we've gotten through all 5 vowels, return the answer. return number
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.
vowel_count('king triton')
vowel_count('i go to uc san diego')
np.random.choice(options)
.options
, is a list or array to choose from.options
. By default, all elements are equally likely to be chosen.# Simulate a fair coin flip.
np.random.choice(['Heads', 'Tails'])
'Tails'
# Simulate a roll of a die.
np.random.choice(np.arange(1, 7))
5
np.random.choice(options, n)
will return an array of n
randomly selected elements from options
.
# Simulate 10 fair coin flips.
np.random.choice(['Heads', 'Tails'], 10)
array(['Tails', 'Heads', 'Tails', 'Tails', 'Tails', 'Tails', 'Tails', 'Heads', 'Heads', 'Tails'], dtype='<U5')
np.random.choice
selects with replacement.replace=False
.# Choose three colleges to win free HDH swag.
colleges = ['Revelle', 'John Muir', 'Thurgood Marshall',
'Earl Warren', 'Eleanor Roosevelt', 'Sixth', 'Seventh', 'Eighth']
np.random.choice(colleges, 3, replace=False)
array(['Seventh', 'Revelle', 'Sixth'], dtype='<U17')
What's the probability of getting 60 or more heads if we flip 100 coins?
Plan:
np.random.choice
to flip 100 coins.np.count_nonzero
to count the number of heads.np.count_nonzero(array)
returns the number of entries in array
that are True
.coins = np.random.choice(['Heads', 'Tails'], 100)
coins
array(['Heads', 'Tails', 'Heads', 'Tails', 'Tails', 'Heads', 'Tails', 'Tails', 'Tails', 'Tails', 'Heads', 'Heads', 'Heads', 'Heads', 'Tails', 'Tails', 'Heads', 'Tails', 'Tails', 'Heads', 'Tails', 'Heads', 'Heads', 'Heads', 'Heads', 'Heads', 'Tails', 'Tails', 'Tails', 'Tails', 'Tails', 'Tails', 'Heads', 'Heads', 'Heads', 'Tails', 'Tails', 'Heads', 'Tails', 'Heads', 'Heads', 'Heads', 'Tails', 'Tails', 'Heads', 'Heads', 'Tails', 'Heads', 'Heads', 'Heads', 'Heads', 'Heads', 'Tails', 'Heads', 'Tails', 'Heads', 'Heads', 'Heads', 'Tails', 'Heads', 'Tails', 'Heads', 'Tails', 'Tails', 'Heads', 'Tails', 'Tails', 'Heads', 'Tails', 'Heads', 'Tails', 'Tails', 'Heads', 'Tails', 'Heads', 'Heads', 'Heads', 'Heads', 'Tails', 'Heads', 'Heads', 'Heads', 'Heads', 'Tails', 'Tails', 'Heads', 'Heads', 'Heads', 'Tails', 'Heads', 'Heads', 'Tails', 'Tails', 'Tails', 'Tails', 'Tails', 'Tails', 'Heads', 'Tails', 'Tails'], dtype='<U5')
coins == 'Heads'
array([ True, False, True, False, False, True, False, False, False, False, True, True, True, True, False, False, True, False, False, True, False, True, True, True, True, True, False, False, False, False, False, False, True, True, True, False, False, True, False, True, True, True, False, False, True, True, False, True, True, True, True, True, False, True, False, True, True, True, False, True, False, True, False, False, True, False, False, True, False, True, False, False, True, False, True, True, True, True, False, True, True, True, True, False, False, True, True, True, False, True, True, False, False, False, False, False, False, True, False, False])
(coins == 'Heads').sum()
52
np.count_nonzero(coins == 'Heads') # Counts the number of Trues in the sequence.
52
np.count_nonzero([5, 6, 0, 2])
3
count_nonzero
?True == 1
and False == 0
, so counting the non-zero elements counts the number of True
s.This makes it easy to run the experiment repeatedly.
def coin_experiment():
coins = np.random.choice(['Heads', 'Tails'], 100)
return np.count_nonzero(coins == 'Heads')
coin_experiment()
46
for
-loop!np.append
!head_counts = np.array([])
head_counts
array([], dtype=float64)
head_counts = np.append(head_counts, 15)
head_counts
array([15.])
head_counts = np.append(head_counts, 25)
head_counts
array([15., 25.])
# Specify the number of repetitions.
repetitions = 10000
# Create an empty array to store the results.
head_counts = np.array([])
for i in np.arange(repetitions):
# For each repetition, run the experiment and add the result to head_counts.
head_count = coin_experiment()
head_counts = np.append(head_counts, head_count)
len(head_counts)
10000
head_counts
array([49., 56., 51., ..., 48., 48., 53.])
# In how many experiments was the number of heads >= 60?
at_least_60 = np.count_nonzero(head_counts >= 60)
at_least_60
303
# What is this as a proportion?
at_least_60 / repetitions
0.0303
# Can also use np.mean()! Why?
np.mean(head_counts >= 60)
0.0303
This is quite close to the true theoretical answer!
# The theoretical answer – don't worry about how or why this code works.
import math
sum([math.comb(100, i) * (1 / 2) ** 100 for i in np.arange(60, 101)])
0.028443966820490392
head_counts
array([49., 56., 51., ..., 48., 48., 53.])
bpd.DataFrame().assign(
Number_of_Heads=head_counts
).plot(kind='hist', bins=np.arange(30, 70), density=True, ec='w', figsize=(10, 5));
plt.axvline(60, color='C1', linewidth=4);
Suppose you’re on a game show, and you’re given the choice of three doors. A car 🚗 is behind one of the doors, and goats 🐐🐐 are behind the other two.
You pick a door, say Door #2, and the host, who knows what’s behind the doors, opens another door, say Door #3, which has a goat.
The host then says to you, “Do you want to switch to Door #1 or stay with Door #2?”
Question: Should you stay or switch?
(The question was posed in Parade magazine’s "Ask Marilyn" column in 1990. It is called the "Monty Hall problem" because Monty Hall hosted a similar game show called "Let's Make a Deal.")
from IPython.display import IFrame
IFrame('https://montyhall.io/', width=600, height=400)
Suppose you originally selected Door #2. The host reveals Door #3 to have a goat behind it. What should you do?
A. Stay with Door #2; it has just as high a chance of winning as Door #1. It doesn't matter whether you switch or not.
B. Switch to Door #1; it has a higher chance of winning than Door #2.
Plan:
When you pick a door, there are three equally-likely outcomes for what is behind the door you picked:
options = np.array(['Car', 'Goat #1', 'Goat #2'])
behind_picked_door = np.random.choice(options)
behind_picked_door
'Car'
When the host opens a different door, they always reveal a goat.
if behind_picked_door == 'Goat #1':
revealed = 'Goat #2'
elif behind_picked_door == 'Goat #2':
revealed = 'Goat #1'
else:
# This is the case in which you originally picked a car!
revealed = np.random.choice(['Goat #1', 'Goat #2'])
revealed
'Goat #2'
If you always switch, you'll end up winning the prize that is neither behind_picked_door
nor revealed
.
options
array(['Car', 'Goat #1', 'Goat #2'], dtype='<U7')
behind_picked_door
'Car'
revealed
'Goat #2'
your_prize = options[(options != behind_picked_door) & (options != revealed)][0]
your_prize
'Goat #1'
Let's put all of our work into a single function to make it easier to repeat.
def simulate_switch_strategy():
options = np.array(['Car', 'Goat #1', 'Goat #2'])
behind_picked_door = np.random.choice(options)
if behind_picked_door == 'Goat #1':
revealed = 'Goat #2'
elif behind_picked_door == 'Goat #2':
revealed = 'Goat #1'
else:
revealed = np.random.choice(['Goat #1', 'Goat #2'])
your_prize = options[(options != behind_picked_door) & (options != revealed)][0]
#print(behind_picked_door, 'was behind the door.', revealed, 'was revealed by the host. Your prize was:', your_prize)
return your_prize
Now, every time we call simulate_switch_strategy
, the result is your prize.
simulate_switch_strategy()
'Car'
We should save your prize in each game; to do so, we'll use np.append
.
repetitions = 10000
your_prizes = np.array([])
for i in np.arange(repetitions):
your_prize = simulate_switch_strategy()
your_prizes = np.append(your_prizes, your_prize)
your_prizes
array(['Car', 'Car', 'Car', ..., 'Goat #1', 'Car', 'Goat #2'], dtype='<U32')
your_prizes
array(['Car', 'Car', 'Car', ..., 'Goat #1', 'Car', 'Goat #2'], dtype='<U32')
np.count_nonzero(your_prizes == 'Car')
6736
np.count_nonzero(your_prizes == 'Car') / repetitions
0.6736
This is quite close to the true probability of winning if you switch, $\frac{2}{3}$.
car_count
to 0, and add 1 to it each time your prize is a car.car_count = 0
for i in np.arange(repetitions):
your_prize = simulate_switch_strategy()
if your_prize == 'Car':
car_count = car_count + 1
car_count / repetitions
0.6686
No arrays needed! This strategy won't always work; it depends on the goal of the simulation.
In this case, your prize is always the same as what was behind the picked door.
car_count = 0
for i in np.arange(repetitions):
options = np.array(['Car', 'Goat #1', 'Goat #2'])
behind_picked_door = np.random.choice(options)
your_prize = behind_picked_door
if your_prize == 'Car':
car_count = car_count + 1
car_count / repetitions
0.3287
To estimate the probability of an event through simulation:
for
-loop, and save the results in an array with np.append
.np.count_nonzero
.