<jupyter_start><jupyter_text>Flow Control **Materials by: <NAME>, <NAME>, <NAME>, <NAME>, <NAME> and many more** In this lesson we will cover how to write code that will execute only if specified conditions are met and also how to automate repetitive tasks using loops.  ComparisonsPython comes with literal comparison operators.  Namely, ` = == !=`.  All comparisons return the lirteral boolean values: `True` or `False`.  These can be used to test values against one another. For example,<jupyter_code>2 + 2 == 4
'big' < 'small'<jupyter_output><empty_output><jupyter_text>Comparisons can be chained together with the the **and** & **or** Python keywords.<jupyter_code>1 == 1.0 and 'hello' == 'hello'
1 > 10 or False
42 < 24 or True and 'wow' != 'mom'<jupyter_output><empty_output><jupyter_text>Comparisons may also be negated using the **not** keyword.<jupyter_code>not 2 + 2 == 5<jupyter_output><empty_output><jupyter_text>Finally, the **is** opperator says wherer two objects are the same because they occupy the same place in memory.  This is a test of *equality* (is) rather than *equivalence* (==).<jupyter_code>x = [1, 2, 3]
y = [1, 2, 3]
x is y
x = 'hello'
y = x
x is y
5 is 5.0
5 is not 5.0<jupyter_output><empty_output><jupyter_text>If statementsThat said, these comparisons can be placed inside of an **if** statement.  Such statements have the following form:    if :        The indented code will only be execute if the condition evaulates to `True`, which is a special boolean value. **ERROR: Missing round bracket for print()**<jupyter_code>x = 5
if x < 0:
    print("x is negative")
x = -5
if x < 0:
    print("x is negative")<jupyter_output>x is negative<jupyter_text>The **if** statement can be combined to great effect with a corresponding **else** clause.     if :            else:                When the condition is `True` the if-block is executed.  When the condition is `False` the else-block is executed instead.<jupyter_code>x = 5
if x < 0:
    print("x is negative")
else:
    print("x in non-negative")<jupyter_output>x in non-negative<jupyter_text>Many cases may be tested by using the **elif** statement.  These come between all the if and else statements:    if :            elif :            else:                When if-condition is true then only the if-block is executed.  When elif-condition is true then only the elif-block is executed.  When neither of these are true then the else-block is executed.<jupyter_code>x = 5
if x < 0:
    print("x is negative")
elif x == 0:
    print("x is zero")
else:
    print("x in non-negative")<jupyter_output>x in non-negative<jupyter_text>While there must be one if statetment, and there may be at most one else statement, there maybe as many elif statements as are desired.    if :            elif :            elif :            elif :            ...    else:                Only the block for top most condition that is true is executed. **ERROR: 2nd elif should say "x is one". said "x is zero"**<jupyter_code>x = 5
if x < 0:
    print("x is negative")
elif x == 0:
    print("x is zero")
elif x == 1:
    print("x is one")
elif x == 2:
    print("x is two")
else:
    print("x is positive and greater than 2")<jupyter_output>x is positive and greater than 2<jupyter_text>Be careful because the computer interprets comparisons very literally.<jupyter_code>'1' < 2
True == 'True'
False == 0
'Bears' > 'Packers'<jupyter_output><empty_output><jupyter_text>Aside About IndentationThe indentation is a feature of Python syntax. Some other programming languages use brackets to denote a command block. Python uses indentation. The amount of indentation doesn't matter, so long as everything in the same block is indented the same amount. **Exercise:** Write an if statement that prints whether x is even or odd. Hint: search up the modulus operator.<jupyter_code>x = 1

if x % 2 == 0:
    print("x is even")
else:
    print("x is odd")<jupyter_output>x is odd<jupyter_text>LoopsLoops come in two flavors: **while** and **for**.  While loops have the following structure:    while :                As long as the condition is True, the code in the block will continue to execute.  This may lead to infitinely executing loops!<jupyter_code>fruits = ['apples', 'oranges', 'pears', 'bananas']
i = 0
while i < len(fruits):
    print(fruits[i])
    i = i + 1<jupyter_output>apples
oranges
pears
bananas<jupyter_text>Meanwhile, for-loops have the following structure:    for  in :                  The loop will continue to execute as long as there are more iterations left in the iterable.  Upon each iteration, the value of that iteration is assigned to the loop variable.<jupyter_code>for fruit in fruits:
    print(fruit)
# range(n) generates numbers [0,n); n is excluded
for i in range(len(fruits)):
    print(i, fruits[i])
# Use zip to iterate over two lists at once
fruits = ['apples', 'oranges', 'pears', 'bananas']
prices = [0.49, 0.99, 1.49, 0.32]
for fruit, price in zip(fruits, prices):
    print(fruit, "cost", price, "each")
# Use "items" to iterate over a dictionary
# Note the order is non-deterministic
prices = {'apples': 0.49, 'oranges': 0.99, 'pears': 1.49, 'bananas': 0.32}
for fruit, price in prices.items():
    print(fruit, "cost", price, "each")
# Calculating a sum
values = [1254, 95818, 61813541, 1813, 4]
total = 0
for x in values:
    total = total + x
print(total)<jupyter_output>61912430<jupyter_text>Short ExerciseUsing a loop, calculate the factorial of 42 (the product of all integers up to and including 42).<jupyter_code>import math

answer = 0

# Your code here
answer = 1
for i in range(1, 43):
    answer *= i

# Answer checked here
assert(answer == math.factorial(42)) # This will give an error if your answer is incorrect
print("Your solution is correct.")<jupyter_output>Your solution is correct.<jupyter_text>break, continue, and elseA break statement exits a loop. It helpsavoid infinite loops by cutting off loops when they're clearly goingnowhere.<jupyter_code># Prints numbers while n != reasonable
reasonable = 10
for n in range(1,2000):
    if n == reasonable :
        break
    print(n)<jupyter_output>1
2
3
4
5
6
7
8
9<jupyter_text>Something you might want to do instead of breaking is to continue to thenext iteration of a loop, giving up on the current one.<jupyter_code># Prints numbers from 1 - 19, excluding 10
reasonable = 10
for n in range(1,20):
    if n == reasonable :
      continue
    print(n)<jupyter_output>1
2
3
4
5
6
7
8
9
11
12
13
14
15
16
17
18
19<jupyter_text>Importantly, Python allows you to use an else statement in a for loop.That is :<jupyter_code>knights={"<NAME>":"the Wise", 
         "Sir Lancelot":"the Brave", 
         "Sir Galahad":"the Pure", 
         "Sir Robin":"the Brave", 
         "The Black Knight":"<NAME>"} 

favorites = list(knights.keys()) # convert keys to list
favorites.remove("<NAME>") # this guy is not a facovite
for name, title in knights.items() : 
    string = name + ", "
    for fav in favorites :
        if fav == name :
            string += title
            break
    else: # this is executed if loop above does not break
        string += title + ", but not quite so brave as Sir Lancelot." 
    print(string)<jupyter_output>S<NAME>, the Wise
Sir Lancelot, the Brave
Sir Galahad, the Pure
Sir Robin, the Brave, but not quite so brave as Sir Lancelot.
The Black Knight, <NAME><jupyter_text>List comprehensionsPython has another way to perform iteration called list comprehensions. The general forms are:    [ for i in iterator]    [ for i in iterator if ]<jupyter_code># Multiply every number in a list by 2 using a for loop
nums1 = [5, 1, 3, 10]
nums2 = []
for i in range(len(nums1)):
    nums2.append(nums1[i] * 2)
    
print(nums2)
# Multiply every number in a list by 2 using a list comprehension
nums2 = [x * 2 for x in nums1]

print(nums2)
# Multiply every number in a list by 2, but only if the number is greater than 4
nums1 = [5, 1, 3, 10]
nums2 = []
for i in range(len(nums1)):
    if nums1[i] > 4:
        nums2.append(nums1[i] * 2)
    
print(nums2)
# And using a list comprehension
nums2 = [x * 2 for x in nums1 if x > 4]

print(nums2)<jupyter_output>[10, 20]<jupyter_text>Reading and Writing files with loopsLoops make processing files by each line simple.<jupyter_code>my_file = open("OtherFiles/example.txt","r")
for line in my_file:
    print(line.strip()) # .strip() removes all the whitespace
my_file.close()
new_file = open("OtherFiles/example2.txt", "w+")
dwight = ['bears', 'beets', 'Battlestar Galactica']
for i in dwight:
    new_file.write(i + '\n')
new_file.close()
#Check if write succeeded
new_file = open("OtherFiles/example2.txt", 'r')
print(new_file.readlines())<jupyter_output>['bears\n', 'beets\n', 'Battlestar Galactica\n']<jupyter_text>Using list comphersion, you can further the code to reduce all whitespace from the file:<jupyter_code>my_file = open("OtherFiles/example.txt","r")
lines = [line.strip() for line in my_file]
print(lines)

my_file.close()<jupyter_output>['There are five spaces in front of me.', 'This line is indented.', 'I have 4 trailing spaces...']<jupyter_text>Flow Control Exercise: Convert genotypesAttempt all parts. And don't forget to talk to your neighbor!  Motivation:A biologist is interested in the genetic basis of height. She measures the heights of many subjects and sends off their DNA samples to a core for genotyping arrays. These arrays determine the DNA bases at the variable sites of the genome (known as single nucleotide polymorphisms, or SNPs). Since humans are diploid, i.e. have two of each chromosome, each data point will be two DNA bases corresponding to the two chromosomes in each individual. At each SNP, there will be only three possible genotypes, e.g. AA, AG, GG for an A/G SNP. In order to test the correlation between a SNP genotype and height, she wants to perform a regression with an additive genetic model. However, she cannot do this with the data in the current form. She needs to convert the genotypes, e.g. AA, AG, and GG, to the numbers 0, 1, and 2, respectively (in the example the number corresponds the number of G bases the person has at that SNP). Since she has too much data to do this manually, e.g. in Excel, she comes to you for ideas of how to efficiently transform the data.  Intializing FileComplete the code below to generate the biologist's data. Look at the file OtherFiles/biodata.txt to understand how the data is formatted. You will have to reopen the data file in Jupyter notebook everytime you run this code to see the changes.<jupyter_code>import random

samples_length = 20 # feel free to change this variable
types = ['AA','AG','GG']

open("OtherFiles/biodata.txt", 'w+').close() # clear all previous data
data_file = open("OtherFiles/biodata.txt","a+")

open("OtherFiles/biodata-answers.txt", 'w+').close() # clear all previous data
answers_file = open("OtherFiles/biodata-answers.txt","a+")

for i in range(samples_length):
    sample_type = random.randint(0,2) # generates 0, 1, or 2 pseudorandomly
    answers_file.write(str(sample_type))
    data_file.write("Genotype #%d: %s\n" % (i, types[sample_type]))

data_file.close()
answers_file.close()<jupyter_output><empty_output><jupyter_text>Part 1:Open the input file generated above and create a new list which has the converted genotype for each subject ('AA' -> 0, 'AG' -> 1, 'GG' -> 2). Hint: you can use the types array defined in the initialization to reduce the amount of if statements.<jupyter_code>genos = []
genos_new = []

# Open the file and get the current genos. Don't forget to close it at the end!
answer_file = open("OtherFiles/biodata.txt", 'r')
lines = answer_file.readlines()
genos = [line.split(" ")[2].strip() for line in lines] #Get genotypes

# Use your knowledge of if/else statements and loop structures below to convert genos -> genos_new:
for geno in genos:
    for i in range(len(types)):
        if geno == types[i]:
            genos_new.append(i)<jupyter_output><empty_output><jupyter_text>Run the code below to check your work:<jupyter_code>answers_file = open("OtherFiles/biodata-answers.txt","r")
answers = [int(i) for i in answers_file.read()]
fail = False

if len(answers) < len(genos_new):
    print("Your have too many answers for the dataset.")
if len(answers) > len(genos_new):
    print("Your have too few answers for the dataset.")
assert(len(answers) == len(genos_new))

for case, ans, test in zip(genos, answers, genos_new):
    if ans != test:
        print("Genotype: %s Correct: %d Your Conversion: %d" % (case, ans, test))
        fail = True

if not fail:
    print("You have finished Part 1")

answers_file.close()<jupyter_output>You have finished Part 1<jupyter_text>Part 2:Count the number of each type of genome and fill out genos_counts.<jupyter_code>genos_counts = {"AA":0,"AG":0,"GG":0} # key is string type('AA','AG', or 'GG'), value is count

# iterate through genos_new and fill out genos_counts. Use the types array["AA","AG","GG"] for a more clever solution.
for geno in genos_new:
    for i in range(len(types)):
        if geno == i:
            genos_counts[types[i]] += 1

print(genos_counts)
assert(sum(genos_counts.values()) == len(genos)) # this will throw an exception if your counts are incorrect<jupyter_output>{'AA': 4, 'AG': 5, 'GG': 11}<jupyter_text>Part 3:Write your output to a new file "OtherFiles/new-biodata.txt" using the following format:    Condensed: 0100100222012    AA count: 6    AG count: 7    GG count: 17    Genotype 0:  AA 0    Genotype 1:  AG 1    ...    Genotype 11: AG 1    Genotype 12: AG 1    Notice that with two digit genotypes there is one less space between "Genotype 11:" and "AG 1". You will have to manually examine the output file to determine if your solution is correct. Specifically, pay attention to your formatting.<jupyter_code>print("Condensed: " + str(answers))
print("Counts: " + str(genos_counts))

#Create output file
open("OtherFiles/new-biodata.txt", 'w+').close() # clear all previous data
output_file = open("OtherFiles/new-biodata.txt", "w+")
#Get string of condensed genos and write to file
condensed_genos = ""
for geno in genos_new:
    condensed_genos += str(geno)
output_file.write("Condensed: %s\n" % (condensed_genos))
#Write genos count to files
for geno, count in genos_counts.items():
    output_file.write("%s count: %d\n" % (str(geno), int(count)))
#Write individual genotypes
for i in range(len(genos_new)):
    if i // 10 == 0:
        output_file.write("Genotype #%d:  %s %d\n" % (i, types[genos_new[i]], genos_new[i]))
    else:
        output_file.write("Genotype #%d: %s %d\n" % (i, types[genos_new[i]], genos_new[i]))
output_file.close()
output_file = open("OtherFiles/new-biodata.txt", "r")
print(output_file.readlines())<jupyter_output>Condensed: [0, 0, 1, 1, 1, 2, 0, 1, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 1, 2]
Counts: {'AA': 4, 'AG': 5, 'GG': 11}
['Condensed: 00111201222222202212\n', 'AA count: 4\n', 'AG count: 5\n', 'GG count: 11\n', 'Genotype #0:  AA 0\n', 'Genotype #1:  AA 0\n', 'Genotype #2:  AG 1\n', 'Genotype #3:  AG 1\n', 'Genotype #4:  AG 1\n', 'Genotype #5:  GG 2\n', 'Genotype #6:  AA 0\n', 'Genotype #7:  AG 1\n', 'Genotype #8:  GG 2\n', 'Genotype #9:  GG 2\n', 'Genotype #10: GG 2\n', 'Genotype #11: GG 2\n', 'Genotype #12: GG 2\n', 'Genotype #13: GG 2\n', 'Genotype #14: GG 2\n', 'Genotype #15: AA 0\n', 'Genotype #16: GG 2\n', 'Genotype #17: GG 2\n', 'Genotype #18: AG 1\n', 'Genotype #19: GG 2\n']<jupyter_text>Short ExercisesThese are some short exercises to get you thinking about how to apply loops and if statements. FibonacciGenerate a list of the first n terms of the Fibonacci Sequence where each element is defined as the sum of the previous two in the sequence. Written mathematically:     f(0) = 0    f(1) = 1    f(n) = f(n - 1) + f(n - 2)    Use this defintion to solve the problem. n will always be greater than or equal to 0. What should you return when n is 0, 1, or 2?<jupyter_code>n = 10
fib = [0, 1] # starts with the first two values

# Your code here
for i in range(2, n + 1):
    fib.append(fib[i - 1] + fib[i - 2])

# compare output to fibonacci sequence:
# 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
print(fib)<jupyter_output>[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]<jupyter_text>Matrix RotationGiven a two dimensional array of rows of a matrix m, rotate it 90 degrees clockwise.    m represents this matrix:        1 2 3        4 5 6        7 8 9    Expected output:        7 4 1        8 5 2        9 6 3Challenge: do this without using any lists or dictionaries (no extra memory).<jupyter_code>m = [[1,2,3], [4,5,6], [7,8,9]]
print("Input:\n" + '\n'.join([' '.join(map(str,i)) for i in m]) + '\n') # print matrix

# Your code here
# i = 2, 1, 0
# j = 0
# i = 2, 1, 0
# j = 1
# i = 2, 1, 0
# j = 2
#OR: transpose then call .reverse() on each element
n = m.copy()

for i in range(len(m)):
    for j in range(len(m)):
        m[j][i] = n[i][j]
#for i in range(len(m)):
#    m[i].reverse()
    
#for j in range(len(m)):
#    for i, x in zip(range(len(m) - 1, -1, -1), range(len(m))):
#        m[j][x] = n[i][j]


print("Result:\n" + '\n'.join([' '.join(map(str,i)) for i in m])) # print matrix<jupyter_output>Input:
1 2 3
4 5 6
7 8 9

Result:
1 2 3
2 5 6
3 6 9<jupyter_text>DiamondsGiven the height of a diamond h, print a visual representation of the diamond. h will always be even and h > 1.Examples:    h = 6      /\     /  \    /    \    \    /     \  /      \/           h = 2     /\     \/<jupyter_code># Uncomment a test case to test it
# h = 2
# h = 6
# h = 8

for i in range(0, int(h / 2)):
    top = ""
    for j in range(1, int(h / 2) - i):
        top += " "
    top += "/"
    for j in range(i):
        top += "  "
    top += "\\"
    for j in range(1, int(h / 2) - i):
        top += " "
    print(top)
for i in range(0, int(h / 2)):
    top = ""
    for j in range(0, i):
        top += " "
    top += "\\"
    for j in range(1, int(h / 2) - i):
        top += "  "
    top += "/"

    print(top)<jupyter_output>/\   
  /  \  
 /    \ 
/      \
\      /
 \    /
  \  /
   \/
{'fraction_non_alphanumeric': 0.1200044404973357, 'fraction_numerical': 0.02797513321492007, 'mean_word_length': 3.6244866529774127, 'pattern_counts': {'":': 8, '<': 109, '<?xml version=': 0, '>': 109, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '27', 'dir': 'jupyter-structured-clean-dedup', 'id': '320257', 'license': 'MIT', 'n_tokens_mistral': 6501, 'n_tokens_neox': 5986, 'n_words': 2374, 'path': 'notebooks/07-flow-control.ipynb', 'repo_name': 'johnnyhchen/EPEtutorials', 'size': '18073'}