<jupyter_start><jupyter_text>For Loops: Interest RatesIn this lesson we are going to learn what for loops are, how to work with them, and apply them to better understand interest rates and other programming concepts. For LoopsFor loops are of structure: > for i in range(n)>> for i in range(start, end + 1, interval)Let's create some for loops!<jupyter_code># counting with a for loop

for i in range(10, 100):
    print(i, end = ", ") # by default, end = "\n"
    
range_list = list(range(10,101))
for i in range_list:
    print(i, end = ", ")<jupyter_output>10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,<jupyter_text>Code above was made live in class, next is from the project notes<jupyter_code># create a list by entering values individually
lst1 = [0,1,2,3,4,5]
# create a list using list(range())
lst2 = list(range(6))

# Create empty list and fill using a for loop
lst3 = []
# count by ones to 100
for i in range(101):
    lst3.append(i)

lst4 = []
# count by twos
for i in range(0, 101, 2):
    lst4.append(i)

# create list counting by twos using list(range())
lst5 = list(range(0,101,2))
# counting backward
lst6 = list(range(50, -1, -2))
print("lst1:", lst1, sep = "\n")
print("lst2:", lst2, sep = "\n")
print("lst3:", lst3, sep = "\n")
print("lst4:", lst4, sep = "\n")
print("lst5:", lst5, sep = "\n")
print("lst6:", lst6, sep = "\n")<jupyter_output>lst1:
[0, 1, 2, 3, 4, 5]
lst2:
[0, 1, 2, 3, 4, 5]
lst3:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
lst4:
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100]
lst5:
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100]
lst6:
[50, 48, 46, 44, 42, 40, 38, 36, 34, 32, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0]<jupyter_text>Let's compare .append() .insert():> The equivalent of lst.append(obj) is lst.insert(-1, obj)<jupyter_code>lst7 = []
# create list of numbers from 0 to 100
for i in range(101):
    lst7.insert(i, i)
# You can call the value at some index (location) in the list by calling 
# lst[#]
print(lst7[1])
print(lst7[10])
# identify very last index with -1
print(lst7[-1], lst7[100])
print(lst7[-2], lst7[99])
print(lst7[-3], lst7[98])<jupyter_output>1
10
100 100
99 99
98 98<jupyter_text>We can create a list that counts backward without using a for loop that counts backward<jupyter_code># Create a list of numbers that counts backward from 100 to 0
lst8 = []
for i in range(101):
    lst8.insert(0, i)
print(lst8)<jupyter_output>[100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]<jupyter_text>Maybe you do not know the length of a list. You can use len(lst) to count the length of a list.<jupyter_code>len_lst1 = len(lst1)
len_lst2 = len(lst2)
len_lst3 = len(lst3)
len_lst4 = len(lst4)
len_lst5 = len(lst5)
len_lst6 = len(lst6)
len_lst7 = len(lst7)
len_lst8 = len(lst8)

print("len lst1:", len_lst1)
print("len lst2:", len_lst2)
print("len lst3:", len_lst3)
print("len lst4:", len_lst4)
print("len lst5:", len_lst5)
print("len lst6:", len_lst6)
print("len lst7:", len_lst7)
print("len lst8:", len_lst8)

for i in range(len(lst1)):
    print(lst1[i], end = ", ")<jupyter_output>len lst1: 6
len lst2: 6
len lst3: 101
len lst4: 51
len lst5: 51
len lst6: 26
len lst7: 101
len lst8: 101
0, 1, 2, 3, 4, 5,<jupyter_text>Transforming Dictionaries to Pandas DataframesDictionaries of two forms can be transformed into pandas dataframes. A dictionary can have 1 layer of keys, each of which point to a list:dct = {"key1":list1,        "key2:list2,        "key3:list3}This results in a dataframe where the keys are interepreted as column names and the list indices are interpreted as row numbers.<jupyter_code>dct = {"list1": [1,2,3,4,5],
      "list2":[1 ** 2, 2 ** 2, 3 ** 2, 4 ** 2, 5 ** 2],
      "list3":[1,4,6,3,5]}
dct
import pandas as pd
df = pd.DataFrame(dct)
df<jupyter_output><empty_output><jupyter_text>The other way to create a dataframe using a dictionary is to create dictionary of dictionaries:dct = {"key1:{ind0:elem0,              ind1:elem1,              ind2:elem2,              ind3:elem3,              ind4:elem4},       "key2 :{. . .}}<jupyter_code>#dct = {"key1:{ind0:elem0,
#              ind1:elem1,
#              ind2:elem2,
#              ind3:elem3,
#              ind4:elem4},
#       "key2 :{. . .}}
dct = {"list1":{i:i for i in range(5)},
      "list2": {0:1**2,
               1:2**2,
               2:3**2,
               3:4**2,
               4:5**2},
      "list3": {0:4,
                1:5,
                2:7,
                3:5,
                4:8}
      }
dct
df = pd.DataFrame(dct)
df<jupyter_output><empty_output><jupyter_text>Calculating Interest EarnedLet's calculate interest payments and FV with python script.$FV = PV(1+r)^t$$r = (\frac{FV}{PV})^{1/t}-1$You can python like a calculator. And conveniently, you can define variables before using them in an equation. This is the strategy that we take in solving for the above equation.Suppose that two years after you place 1000 dollars in a deposit account, the value o the deposit grows to 1200 dollars. Build python script that calculates the annual interest rate.<jupyter_code>FV = 1200
PV = 1000
t = 2
r = (FV / PV) ** (1 / t) - 1
r<jupyter_output><empty_output><jupyter_text>Let's test this result to make sure that $PV(1+r)^t$ yields the correct FV.<jupyter_code>FV_test = PV * (1 + r) ** t
FV_test<jupyter_output><empty_output><jupyter_text>Interest can be compounded annually (once per year) or it can be compounded more than once. If the interest is compounded more than once annually,  in the present equation we divide the interest by the number times compounded, $n$, and we multiply the number of years, $t$, by $n$.$FV = PV(1+\frac{r}{n})^{tn}$Next, create a dictionary that records the value of $1000 after a year of collecting interest over different rates of compounding.<jupyter_code># use dictionary to define how many times interest is compounded
compounding_def = {"annual":1,
                  "semi-annual":2,
                  "quarterly":4,
                  "monthly":12,
                  "weekly":52,
                  "daily":365}

PV = 1000
r = 0.1
t = 1
#compound interest at rate of 10%
# 10% == 0.1
# create dictionary to hold the results
compounding_interest = {}

# for var1, var2 in dict.items()
# for key1, obj1 in dict.items()
for period_name, n in compounding_def.items():
    # use round(val, #decimals) to round value to a specified # of decimals
    compounding_interest[period_name] = round(PV * (1 + r / n) ** (t * n), 2)
compounding_interest
compounding_def.items()
for key, val in compounding_def.items():
    print(key, val)
for key in compounding_def:
    print(key, compounding_def[key])
compounding_def = {"annual":1,
                   "semi-annual":2,
                   "quarterly":4,
                   "monthly":12,
                   "weekly":52,
                   "daily":365
                  }
# create variables
PV = 1000
t = 2
# use generator function to create list of interest rates
# from 1 to 20
rates = [i for i in range(1,21)]
compounding_interest = {}
for rate in rates:
    r = rate / 100
    rate = str(rate)+"%"
    compounding_interest[rate] = {}
    for period_name, n in compounding_def.items():
        compounding_interest[rate][period_name] = round(PV * (1 + r / n) ** (t * n), 2)
compounding_interest        
compounding_interest_df = pd.DataFrame(compounding_interest)
compounding_interest_df
compounding_interest_df.T<jupyter_output><empty_output><jupyter_text>Using Computational Methods to Brute Force Solve for a ValueToday we will be using computational methods, solving by brute force. To accomplish this, we need to understand if statements. If StatementsIf statements check if a statement is true. If the statement is true, then the program will execute a block of code indented after the if statements.<jupyter_code>x = True
if x:
    print(x)
else:
    print("x is not True, x is equal to",x)
x = 5
if x < 4:
    print(True)
else:
    print("x is not less than 4, x is", x)<jupyter_output>x is not less than 4, x is 5<jupyter_text>%: x % y equals remainder of x / y.<jupyter_code>for x in range(4):
    print("x ==", x)
    print("x % 2 ==", x % 2)
    if x % 2 == 0:
        print("x is an even number")
    else:
        print("x is not an even number")
for x in range(10):
    print("x is",x)
    print("x % 3 is", x % 3)
    if x % 3 == 0:
        print("x is divisible by 3, leaving no remainder")
    else:
        print("x is not divisible by 3. A remainder is left.")
# solve for 15 / 3
x = 15 /3
print(x)
# a * x = 15
a = 3
x = 1
# if a * x doesn't equal 15, keep adding one to x.
# if a * x equals 15, stop
while a * x != 15:
    print("x = ",x, "\tax=", a*x)
    x = x + 1

print("Solution for 3x = 15: x =", x)
# rewrite while loop as a for loop
a = 3
for x in range(1,1000):
    if a * x == 15:
        # break out of for loop
        break
    
print("Solution for 3x = 15: x =", x)<jupyter_output>Solution for 3x = 15: x = 5<jupyter_text>Our goal is to teach the computer to search for a solution to this algebra problem.$ax = 1000$$a = 3$Solve for x1. We will count x upward from 1 until ax > 10002. Then we will tell the computer to count in the other direction using smaller increments     count in negative direction until ax < 10003. Then count upward using a small increment . . .<jupyter_code># solve ax = 1000
a = 3
# we don't know x, so we will count x upward
x = 1
ax = a * x

# We know that we want to count x upward until ax crosses 1000,
# then we want to count downward at lower rate
# i.e., at first count upward by 1, then count downward by 0.1, 
#       then upward again by 0.001, etc...
margin = 1.0

# create variables to keep track of ax's value
# relative to the target solution of 1000
below_1000_currently = True
# we also need to know whether ax was above or below
# 1000 last period, to know whether or not the direction
# of the change in x needs to switch
below_1000_last_period = True

# if ax is approximately 1000, break out of while loop
while round(ax, 10) != 1000:
    
    # if ax < 1000, count downwrad
    # if ax > 1000, count downward
    x = x + margin if ax < 1000 else x - margin
    # after changing the value x, update the value of ax
    ax = a * x
    
    # update whether ax is above or below 1000
    below_1000_currently = True if ax < 1000 else False
    
    # if ax crossed from above 1000 to below 1000
    # or if ax crossed from below 1000 to above 1000
    # shrink the margin by which change x
    if below_1000_currently == True and below_1000_last_period == False:
        margin = margin * 0.1
        print("Start counting upward at rate of", margin)
    if below_1000_currently == False and below_1000_last_period == True:
        margin = margin * 0.1
        print("Start counting downward at rate of", margin)
    
    # once we have identified if ax crossed 1000,
    # update last periods position to match current
    below_1000_last_period = below_1000_currently
    print("x:", x,"ax:", ax)
print("You solved ax = 1000, a = 3, for x")
print("x =", round(x, 2), "ax =", round(ax, 2))
a = 3
x = 1
ax = a * x
# solve for ax = 1000
margin = 1.0


# create variable to track if ax is ablove or below 1000
below_1000 = True

# if below_1000 switches from True to False or from False to True,
# indicate that the program needs to change direct 
# (from negative to positive or from positive to negative)
decrease_margin = False

while round(ax,10) != 1000:
    if decrease_margin:
        margin = margin * .1
        decrease_margin = False
    # increase x or decrease x depending on whether
    # x is below or above 1000
    x = x + margin if ax < 1000 else x - margin
    ax = a * x
    print("x =", x, "\tax", ax)
    # if ax crosses over 1000, identify
    # 1) that ax is now greater than 1000
    # 2) x should change by a smaller margin and in a diff
    # direction
    if below_1000 == True and ax > 1000:
        #change by smaller margin
        decrease_margin = True
        # move in opposite direction
        below_1000 = False
    elif below_1000 == False and ax < 1000:
        # change by smaller margin
        decrease_margin == True
        # move in opposite direction
        below_1000 = True
    
print("Solution: (" + str(a)+")" + "("+str(x)+") =", ax)<jupyter_output><empty_output><jupyter_text>Use brute force to solve for the interest rate you would need to grow 1000 dollars to 1500 dollars after 2 years of annual compounding<jupyter_code># set up equation
PV = 1000
FV = 1000
# checking interest rates from 0.01 and above 
# 0.01 is 1%
r = 0.01
# margin should increase or decrease by no more than 1%
margin = .01
# PV = FV / (1 + r/n)**(t*n)
# FV = PV (1 + r/n) ** (t/n)

# identify whether FV above or below 1500
below_1500 = True

# set decrease_margin False
# decrease_margin will only be True if either: 
#            below_1500 == True and FV > 1500
#            or
#            below_1500 == False and FV < 1500
# Once decrease margin is true, margin shrinks
# and decrease_margin is again set to False
decrease_margin = False

while round(FV, 2) != 1500:
    if decrease_margin:
        margin = margin * 0.1
        decrease_margin = False
    # r increases by margin if below 1500
    # r decreases by margin if above 1500
    r = r + margin if FV < 1500 else r - margin
    # recalculate future value given change in r
    FV = PV * ( 1 + r) ** 2

    print("r:", r, "\tFV:", FV)
    if below_1500 == True and FV > 1500:
        decrease_margin = True
        below_1500 = False
    if below_1500 == False and FV < 1500:
        decrease_margin = True
        below_1500 = True
print("r:", r, "FV:", round(FV, 2))
# set up equation
PV = 1000
FV = 1000
# checking interest rates from 0.01 and above 
# 0.01 is 1%
r = 0.01
# margin should increase or decrease by no more than 1%
margin = .01
# PV = FV / (1 + r/n)**(t*n)
# FV = PV (1 + r/n) ** (t/n)

# identify whether FV above or below 1500
below_1500_current = True
below_1500_last_period = True

# decrease margin if either: 
#            below_1500_last_period == True and below_1500_current == False
#            or
#            below_1500_last_period == False and below_1500_current == True

# while loops and  for loops execute the indented block of code
# that follows them
while round(FV, 2) != 1500:
    # r increases by margin if below 1500
    # r decreases by margin if above 1500
    r = r + margin if FV < 1500 else r - margin
    # recalculate future value given change in r
    FV = PV * ( 1 + r) ** 2
    below_1500_current = True if FV < 1500 else False
    
    if below_1500_last_period == True and below_1500_current == False:
        margin = margin * 0.1
    if below_1500_last_period == False and below_1500_current == True:
        margin = margin * 0.1
    below_1500_last_period = below_1500_current
print("r:", round(r,2), "FV:", round(FV, 2))
print(3 < 5)
print(2 == 4)

if 3 < 5 and 2 == 4:
    print("both statements are true")
else:
    print("at least one statement is false")
PV = 1000
FV_dict = {}
for final_FV in range(1100, 2001, 100):
    r = .01
    margin = .01
    below_final_FV = True
    decrease_margin = False
    while round(FV, 2) != final_FV:
        if decrease_margin:
            margin = margin * 0.1
            decrease_margin = False
        r = r + margin if FV < final_FV else r - margin
        FV = PV * (1 + r) ** 2
        if below_final_FV == True and FV > final_FV:
            decrease_margin = True
            below_final_FV = False
        if below_final_FV == False and FV < final_FV:
            decrease_margin = True
            below_final_FV = True
    FV_dict[final_FV] = r
FV_dict<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12190948250427805, 'fraction_numerical': 0.0994276273086682, 'mean_word_length': 2.983078730904818, 'pattern_counts': {'":': 19, '<': 66, '<?xml version=': 0, '>': 60, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 118, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '446858', 'license': 'MIT', 'n_tokens_mistral': 7383, 'n_tokens_neox': 5844, 'n_words': 2393, 'path': 'In Class Demonstrations/Fall 2020/Project 2 - For Loops, Lists, and Interest Rates.ipynb', 'repo_name': 'hunterluepke/Learn-Python-for-Stats-and-Econ', 'size': '16947'}