### Could not find kernel error?

If you ever see a "could not find a kernel for this notebook" error message, it will offer you a pull down menu for you to pick a fitting kernel.

Remember, kernels are the notebook's way to find the correct interpretor for the code you write into notebook cells. And these days this can be R, Julia, Python and several other things (Find the available kernels list here).

### Review on your own time: A few "last" things about types

We did some examples last class which illustrated the difference between integers and floats. Let's do one more using the conversion between Fahrenheit and Celsius as a test case. Recall the conversion formula:

$T_C = \frac{5}{9} \left(T_F - 32 \right)$



In [1]:

tempF = 212.0
tempC = (5 / 9) * (tempF - 32.0)
tempC




Out[1]:

100.0



#### Q. What will be printed?

Depending on Python version!

#### Q. What went wrong?

Nothing in Python 3, yay! ;)

### You can force variables to be certain types



In [2]:

x = 45
type(x)  #  Gives (returns) the type of variable




Out[2]:

int




In [3]:

x = float(x)
print(type(x))
x




<class 'float'>

Out[3]:

45.0



#### Q. What will this produce?



In [4]:

x = 26.9
int(x)




Out[4]:

26



### Review continued: One "last" note on modules



In [5]:

from math import *
import math
print(sqrt(2))
math.sqrt(2)




1.4142135623730951

Out[5]:

1.4142135623730951



It's using the exact same library twice, you just told Python 2 different ways to get to it. And there's even a way to prove it: With the id() function:



In [6]:

id(sqrt)




Out[6]:

4331469056




In [7]:

id(math.sqrt)




Out[7]:

4331469056



As you can see it's the same memory address (but this number is not necessarily the same on your computer), meaning the Python interpreter uses the exact same object twice, you just gave it 2 different names.

Another syntax is available to import modules:



In [8]:

#  Import math module and give it a new name
import math as m               #  Note the use of "as", a reserved word
m.sqrt(2)




Out[8]:

1.4142135623730951



or specific functions within a module:



In [9]:

#  Import sqrt from math and give it a new name
from math import sqrt as sq
from math import pi as PIE
sq(2)




Out[9]:

1.4142135623730951


Recap of importing styles: from module import function from module import * import module import module as mod from module import function as func

# Today: Loops & Lists

The point of loops is to compactly code repetitive tasks. For example, computing the gravitational force for multiple planetary masses.

Loops are an essential programming tool (this is why we program!).

Python supports two types of loops:

1. while loops
2. for loops

### While Loops (Section 2.1.2 in the book)

#### Basic While Loop

<Talk about how Python knows what's in the loop>



In [10]:

x = 0                               # Initialize the variable x to 0

while(x != 3):                      # While (as long as) x is not equal to 3
print("The value of x is", x)    # Print this to the screen
x += 1                          # Increment x by 1 (add 1 to x)
#  REPEAT!!!




The value of x is 0
The value of x is 1
The value of x is 2




In [11]:

print(x)

#  What is the value of x?




3



#### Without a while loop



In [12]:

x = 0                          # Initialize the variable x to 0

print("The value of x is", x)   # Print this to the screen
x += 1                         # Increment x by 1 (add 1 to x)

print("The value of x is", x)   # Print this to the screen
x += 1                         # Increment x by 1 (add 1 to x)

print("The value of x is", x)   # Print this to the screen
x += 1                         # Increment x by 1 (add 1 to x)




The value of x is 0
The value of x is 1
The value of x is 2



Recall the Gravitational Force Equation

$$F(r) = G \frac{m_1 m_2}{r^2}$$


In [13]:

print('# Table of Gravitational Forces for Multiple Planet Masses\n')

#  Initialize variables - use meters and kilograms for units
G           = 6.67e-11         # Gravitational constant
mass_earth   = 5.97e24          # Earth mass
mass_person  = 70               # Person mass

#  Begin calculation
mass1 = mass_earth

print('# mass1/mass_earth  Force')

#  The loop ends when conditional mass1 <= (10.0 * massEarth) is no longer true
while(mass1 <= (10.0 * mass_earth)):                   #  Note the colon!
force = G * mass1 * mass_person / radius_earth**2   #  All lines in the loop must be indented by
#  the same amount (iPython does it automatically)
#     print(str(mass1 / mass_earth) + " " + str(force))
print("{mass_ratio}\t{force:7.2f}".format(mass_ratio=mass1 / mass_earth,
force=force))
mass1 = mass1 + mass_earth                         # Increment by Earth's mass

# No indent!  This line is executed after the loop is done
print('# Done')




# Table of Gravitational Forces for Multiple Planet Masses

# mass1/mass_earth  Force
1.0	 686.94
2.0	1373.88
3.0	2060.82
4.0	2747.76
5.0	3434.70
6.0	4121.65
7.0	4808.59
8.0	5495.53
9.0	6182.47
10.0	6869.41
# Done



#### Q. What will this loop do ("trace" it)?

The increment could have been done in shorthand



In [14]:

# Note that I have to reset mass1 here!!
mass1 = mass_earth

print('# mass1/mass_earth  Force')

while(mass1 <= (10.0 * mass_earth)):
force = G * mass1 * mass_person / radius_earth**2

print("{:18.1f} {:7.2f}".format(mass1 / mass_earth, force))

#  mass1 = mass1 + mass_earth
mass1 += mass_earth      #  Shorthand version of the line above.

'# Done'




# mass1/mass_earth  Force
1.0  686.94
2.0 1373.88
3.0 2060.82
4.0 2747.76
5.0 3434.70
6.0 4121.65
7.0 4808.59
8.0 5495.53
9.0 6182.47
10.0 6869.41

Out[14]:

'# Done'



# Example 1 x = 0 while(True): x = x + 1 # Example 2 x = 0 while(x >= -1): x = x + 1
NEVER, EVER DO THIS!! (well, not EXACTLY like this...)

### Infinite loops

If you create a while loop and the conditional never becomes false, you have just made yourself an infinite loop! If you accidentally make an infinite loop in iPython notebook, go to "Kernel" then "Interrupt" in the toolbar above, then go to "Kernel" then "Restart".


In [15]:

#  How to prevent an infinite loop

maxCount = 10      #  A number that is more than your loop should ever do
count = 0          #  The current number your loop is on

#  Adding "and < maxCount" to the end of your conditional prevents infinite loops
while(True and count < maxCount):
print("Loop count: " + str(count))
count += 1     #  Increment your current loop count




Loop count: 0
Loop count: 1
Loop count: 2
Loop count: 3
Loop count: 4
Loop count: 5
Loop count: 6
Loop count: 7
Loop count: 8
Loop count: 9



#### Q. How does this work?

Remember the basic structure of a while loop: while : <...> *The must evaluate to True or False.*

### INTERLUDE: Boolean (logic) expressions (Section 2.1.3)

Boolean expressions are conditional statements. There are only two possible values: True or False

I've capitalized True and False because these are reserved words in Python.

# x == y # Is x equal to y? (remember, a single = symbol is used to assign values) # x != y # Is x not equal to y? # x >= y # Is x greater than or equal to y? # x <= y # Is x less than or equal to y? # x < y # Is x less than y? # x > y # Is x greater than y?

#### Q. What is the value of this?



In [16]:

5 <= 10




Out[16]:

True



#### Q. What is the value of this?



In [17]:

5 >= 10




Out[17]:

False


The reserved word "not" can be inserted in front of boolean expressions to change the value to its opposite


In [18]:

not 5 >= 10




Out[18]:

True



#### Q. What is the value of this?

Boolean expressions can be combined with "and", "or" and "not" to form compound conditional expressions.



In [ ]:

5 <= 10 and 5 >= 10





In [ ]:

5 <= 10 or 5 >= 10



### Back to while loops

While loops are good to use when you don't know exactly how many times you need your loop to run. They are very useful when asking the user for input.

#### Example - User Input



In [19]:

import random

minNumber = 1
maxNumber = 10

#  Get a random number between 1 and 10
randomNumber = random.randint(minNumber, maxNumber)

userGuess = -1

while(userGuess != randomNumber):
userPrompt = "Guess a number between " + str(minNumber) + " and " + str(maxNumber) + ": "

userGuess = input(userPrompt)      #  Prompt the user

userGuess = int(userGuess)

print("You have guessed the correct number! " + str(userGuess))




Guess a number between 1 and 10: 1
Guess a number between 1 and 10: 2
Guess a number between 1 and 10: 3
Guess a number between 1 and 10: 4
Guess a number between 1 and 10: 5
You have guessed the correct number! 5



### Lists (Section 2.2)

Lists are sequences of objects (which can be of different types) in a given order. To define a list of mass ratios with ten elements ** (and indices running from 0 to 9): ** Referring to our previous gravitational force example:


In [22]:

massRatio = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
massRatio




Out[22]:

[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]


We can access an element of the list by supplying its index in SQUARE BRACKETS (not parentheses or braces):


In [23]:

massRatio[3]




Out[23]:

4.0



#### Q. What will this print?



In [24]:

type(massRatio[3])




Out[24]:

float



Lesson learned: Python is zero-index based

### Modifying lists

We can append an element to the end of a list using the append "method":


In [ ]:

massRatio.append(11.0)
massRatio


Note the syntax "object.method(argument)" Append acts like a function, but it is reached through an object. That object (which we created) is a list called massRatio.
We can insert a new element at a specific location too:


In [ ]:

# This inserts 4.5 into index 4 of the list:
massRatio.insert(4, 4.5)

massRatio


We can delete an element:


In [ ]:

del massRatio[4]



#### Q. What will the next line produce?



In [ ]:

massRatio



### List operations



In [ ]:

# We can find out its length with len(object)
len(massRatio)

# Python uses [] to access elements and () to perform a function on an object.




In [25]:

massRatio = massRatio + [12.0, 13.0, 14.0]
massRatio




Out[25]:

[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 12.0, 13.0, 14.0]


which is equivalent to using the method "extend":


In [ ]:

massRatio.extend([15.0, 16.0, 17.0])
print("Extend", massRatio)

massRatio.append([18.0, 19.0, 20.0])
print("Append", massRatio)
print(massRatio[17][1])


The "index" function returns the index of the first appearance of a value

#### Q. What will this produce?



In [28]:

massRatio.index(12.0)




Out[28]:

10




In [29]:

#  And, this fails
massRatio.index(20.0)




---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-29-24206e7e4f7f> in <module>()
1 #  And, this fails
----> 2 massRatio.index(20.0)

ValueError: 20.0 is not in list


The "in" keyword:


In [ ]:

#  We can check if there is an element in a list.  The result of the check
#  is boolean:  True or False.

14.0 in massRatio




In [ ]:

99.0 in massRatio




In [ ]:

massRatio


Negative indices:


In [ ]:

#  Negative indices start counting from the right (the end) of a list:
massRatio[-4]



### Creating lists with while loops

We can create lists using a while loop. Again, this is useful when you don't know how many elements are going to be put in the list.


In [ ]:

# Initializations first
massRatio      = []       #  Creates an empty list
massRatioValue = 1.0      #  For the conditional
massRatioMax   = 5.0      #  Also for the conditional

userInput = "BIG NOPE"

# And the while loop
while(userInput != "N" and massRatioValue <= massRatioMax):   #  Remember the colon!
#  Remember to indent!
massRatio.append(massRatioValue)
massRatioValue += 1.0

userInput = input("Add another mass ratio value? ")
userInput = userInput.upper()

print("Finished creating the list massRatio!")



#### Q. What is massRatio?



In [ ]:

massRatio




In [ ]: