Python Fundamentals#

This notebook contains material from the Pyomo Summer Workshop 2018 by the Pyomo Developement Team and the notebook is developed by David Bernal (dbernaln@purdue.edu), Zedong Peng (peng372@purdue.edu), and Albert Lee (lee4382@purdue.edu); the content is available on Github. The original sources of this material are available on Pyomo.

1. Lists#

Lists are mutable sequences, typically used to store collections of homogeneous items (where the precise degree of similarity will vary by application). Lists are enclosed by brackets ([ ]) and their elements and size can be changed. In this section, we will learn how to replace the elements of a list, and index a number specifying the position of an item in a sequence s[i] return the item at index i. The list is given as follows:

a = [1, 2, 3, 4, 5]
Inline Exercise 1.1: Replace the '1' in the list with the string 'foo'.
# TODO: INSERT CODE HERE TO REPLACE THE '1' IN a WITH THE STRING "foo"
# TODO: INSERT CODE HERE TO REPLACE THE '1' IN a WITH THE STRING "foo"
a = [1, 2, 3, 4, 5]
a[0] = "foo"
print("CHECK a is:", a) # check your work
CHECK a is: ['foo', 2, 3, 4, 5]
Inline Exercise 1.2: Replace the `4` in the list `a` with the string `bar`.
# TODO: INSERT CODE HERE TO REPLACE THE '4' IN a WITH THE STRING "bar"
# TODO: INSERT CODE HERE TO REPLACE THE '4' IN a WITH THE STRING "bar"
a = [1, 2, 3, 4, 5]
a[3] = "bar"
print("CHECK a is:", a) # check your work
CHECK a is: [1, 2, 3, 'bar', 5]

In Python, all sequences, be they lists, strings, or tuples, support indexing, which allows for the direct access of individual items within the sequence. The numbering of these indices starts from zero (0); hence, the initial item in any sequence is found at the zeroth index. This implies that for a sequence of n elements, the last item would be positioned at an index of n-1.

Inline Exercise 1.3: Given the list `a`, guess what the output of `print(a[-1])` will print.
# TODO: INSERT CODE HERE TO PRINT WHAT YOU THINK 'print(a[-1])' WILL PRINT
# TODO: INSERT CODE HERE TO PRINT WHAT YOU THINK 'print(a[-1])' WILL PRINT
print(5)
print("CHECK a[-1] is:", a[-1]) # check your work (don't just write this for your answer!)
5
CHECK a[-1] is: 5

Another unique feature of sequence indexing in Python is the support for negative indices, which provide a means to count from the end of the sequence towards the beginning. Utilizing this approach, the last item of any given sequence can be accessed using an index of -1. Progressively, moving further towards the start of the sequence, the indices decrease, meaning the penultimate item would be at index -2, and so forth. Remarkably, the first item in a sequence can also be accessed using negative indexing by specifying the negative of the sequence’s length, denoted as -length.

Inline Exercise 1.4: Given the list `a`, modify `a` so that the output is `[1, 2, "hello", "world", 4, 5]`.
# TODO: INSERT CODE HERE TO MODIFY a SO IT IS [1, 2, "hello", "world", 4, 5]
# TODO: INSERT CODE HERE TO MODIFY a SO IT IS [1, 2, "hello", "world", 4, 5]
a = [1, 2, 3, 4, 5]
a[2:3] = ["hello", "world"]
print("CHECK a is:", a) # check your work
CHECK a is: [1, 2, 'hello', 'world', 4, 5]

In Python, slicing allows you to access a subset or a range of elements from a sequence-like object such as a list, string, or tuple. This can be accomplished by specifying two indices in the format s[i:j], where s is the sequence. This expression denotes a slice starting at index i and ending just before index j.

Inline Exercise 1.5: After setting list `b`, what do you expect `print(a[2])` to output? 
b = a
b[2] = "small"
# TODO: INSERT CODE HERE TO PRINT WHAT YOU THINK 'print(a[2])' WILL PRINT
b = a
b[2] = "small"
# TODO: INSERT CODE HERE TO PRINT WHAT YOU THINK 'print(a[2])' WILL PRINT
print("small")

print("CHECK a[2] is:", a[2]) # check your work (don't just write this for your answer!)
small
CHECK a[2] is: small

In Python, using the assignment operator as in b = a for lists doesn’t create a new copy. Instead, a and b point to the same list in memory. Therefore, changes made using one variable (like b[2] = "small") also appear when accessed through the other (a), demonstrating Python’s handling of object references.

List comprehensions provide a concise way to create lists. Common applications are to make new lists where each element is the result of some operations applied to each member of another sequence or iterable, or to create a subsequence of those elements that satisfy a certain condition.

Inline Exercise 1.6: `c` is given as a list comprehension. Find what you think `print(c[3])` will output.
c = [x for x in range(2, 10)] # this is a list comprehension!
# TODO: INSERT CODE HERE TO PRINT WHAT YOU THINK 'print(c[3])' WILL PRINT
c = [x for x in range(2, 10)] # this is a list comprehension!
# TODO: INSERT CODE HERE TO PRINT WHAT YOU THINK 'print(c[3])' WILL PRINT
print(5)

print("CHECK c[3] is:", c[3]) # check your work (don't just write this for your answer!)
5
CHECK c[3] is: 5

A list comprehension consists of brackets containing an expression followed by a for clause, then zero or more for or if clauses. The result will be a new list resulting from evaluating the expression in the context of the for and if clauses which follow it.

Inline Exercise 1.7: Using the list comprehensions in Python, write code that transforms the given list `d = [6, 5, 4, 3, 2]` into `[7, 6, 5, 4, 3]`` by adding 1 to each element. 
# TODO: INSERT CODE HERE TO USE LIST COMPREHENSION TO CREATE THE FOLLOWING LIST:
# [7, 6, 5, 4, 3]
# USING THE LIST d GIVEN ABOVE. THEN PRINT IT.
# TODO: INSERT CODE HERE TO USE LIST COMPREHENSION TO CREATE THE FOLLOWING LIST:
# [7, 6, 5, 4, 3]
# USING THE LIST d GIVEN ABOVE. THEN PRINT IT.
d = [6, 5, 4, 3, 2]
d = [x+1 for x in d]
print(d)
[7, 6, 5, 4, 3]

2. Dictionaries#

A dictionary in Python is a mutable and dynamic data structure that maps unique keys to corresponding values, akin to a table. Keys, which are immutable, can be integers, strings, floats, tuples, booleans, or the NoneType, but not mutable types like lists or dictionaries. Values, however, can be any Python object, mutable or immutable. This structure makes dictionaries a versatile tool in Python for storing and manipulating diverse data types in an intuitive key-value format.

We will conduct some exercise problems with the following dictionary

d = {"tree": "wood", "flower": "petal", "earth": "water", 101: [4, 6, 8]}
d = {"tree": "wood", "flower": "petal", "earth": "water", 101: [4, 6, 8]}

In Python, dictionaries are mutable, which means we can change their content without changing their identity.

Inline Exercise 2.1: Given the dictionary `d`, change the value of the key `tree` into `leaves`.
# TODO: INSERT CODE HERE TO REPLACE "wood" WITH "leaves"
# TODO: INSERT CODE HERE TO REPLACE "wood" WITH "leaves"
d["tree"] = "leaves"

In Python, dictionaries are not fixed in size. This means that new key-value pairs can be added to the dictionary at any time.

Inline Exercies 2.2: Given the dictionary `d`, add a new key-value pair `monty: python` to the dictionary.
# TODO: INSERT CODE HERE TO ADD THE PAIR "monty": "python"
# TODO: INSERT CODE HERE TO ADD THE PAIR "monty": "python"
d["monty"] = "python"

d[key] returns the value for key in the dictionary. If key is not available, then a KeyError will be raised.

Inline Exercise 2.3: Given the dictionary `d`, what do you think `print(d["earth"])` will output?
# TODO: INSERT CODE HERE TO FIRST CHECK IF "earth" IS A KEY,
# AND IF IT IS THEN PRINT THE VALUE IT MAPS TO
# TODO: INSERT CODE HERE TO FIRST CHECK IF "earth" IS A KEY,
# AND IF IT IS THEN PRINT THE VALUE IT MAPS TO
if "earth" in d:
    print("The value of 'earth' is:", d["earth"])
else:
    print("'earth' is not found in the dictionary.")
The value of 'earth' is: water
Inline Exercise 2.4: Print all the pairs in the dictionary `d`.
# TODO: INSERT CODE HERE TO PRINT ALL THE PAIRS IN d
# TODO: INSERT CODE HERE TO PRINT ALL THE PAIRS IN d
print("All key-value pairs:")
print(list(d.items()))
All key-value pairs:
[('tree', 'leaves'), ('flower', 'petal'), ('earth', 'water'), (101, [4, 6, 8]), ('monty', 'python')]

3. Functions#

A function in Python is a reusable block of code designed to perform a specific task. They play a pivotal role in structuring programs by enabling a divide-and-conquer approach, where a large task is broken down into several smaller, more manageable tasks. When a function is defined, its code isn’t executed immediately. Instead, the function’s code is run whenever the function is called. This call can come from various control structures within the program, including sequential code flow and within conditional (like if statements) or repetition structures (like for and while loops). By encapsulating specific tasks in functions, we can create more organized, readable, and efficient programs. There are several Exercise Problems for you to practice.

Exercise 3.1#

Write a function that takes in a list of numbers and prints the value of the largest number. Be sure to test your function. Start with the following code:

def print_max_value(nums):

	##TODO
def print_max_value(nums):
    print("The max value is: ")
    print(max(nums))

test = [1, 2, 3, 4, 5]
print_max_value(test)
The max value is: 
5

Exercise 3.2#

Write a function that takes a list of numbers and returns the largest number. Start with the following code:

def max_value(nums):

	##TODO
def max_value(nums):

	return max(nums)

test = [1, 2, 3, 4, 5]
print(max_value(test))
5

Exercise 3.3#

Do so without using any built-in functions. Start with the following code:

def my_max_value(nums):
	
	##TODO
def my_max_value(nums):
	
	tmp = nums[0]
	for i in range(1, len(nums)):
		if nums[i] > tmp:
			tmp = nums[i]

	return tmp

test = [1, 2, 3, 4, 5]
print(my_max_value(test))
5

Exercise 3.4#

Call both functions on a couple different lists of numbers to verify they return the same value.

l1 = [1, 3, 0, 5, -2]
ans1 = max_value(l1)
print(ans1)
ans2 = my_max_value(l1)
print(ans2)

l2 = [12, 0, 11.9]
print(max_value(l2))
print(my_max_value(l2))

The max value of l1 is 5, and the max value of l2 is 12. max_value() and my_max_value() returneed the same values for both lists.

l1 = [1, 3, 0, 5, -2]
ans1 = max_value(l1)
print('Max value using the built-in functions :', ans1)
ans2 = my_max_value(l1)
print('Max value without using the built-in functions :', ans2)

l2 = [12, 0, 11.9]
print('Max value using the built-in functions :', max_value(l2))
print('Max value without using the built-in functions :', my_max_value(l2))
Max value using the built-in functions : 5
Max value without using the built-in functions : 5
Max value using the built-in functions : 12
Max value without using the built-in functions : 12

Exercise 3.5#

Write a function that takes a list of numbers and returns a dict consisting of the smallest and largest number (use keys ‘smallest’ and ‘largest’). Be sure to test your function. Start with the following code:

def max_and_min(nums):
	
	##TODO
def max_and_min(nums):
	
	return {'smallest': min(nums), 'largest': max(nums)}

test = [1, 2, 3, 4, 5]
print(max_and_min(test))
{'smallest': 1, 'largest': 5}

Exercise 3.6#

Write a function that takes in two lists and prints any common elements between them hint: check if an item is in a list using: if item in list

def get_common(l1, l2):

	##TODO
def get_common(l1, l2):

	for i in l1:
		if i in l2:
			print(i)
			
get_common(l1, l2)
0

4. For Loops#

A for loop in Python is a control flow structure that allows for a fixed number of iterations, determined by the size of the iterable it traverses. An iterable, in this context, is any object capable of returning its elements one at a time, examples of which include strings, lists, and ranges, among others. With each iteration of the for loop, the variable (often referred to as the loop variable or item) is set to a different value from the iterable. This process continues until all values in the iterable have been assigned to the variable, marking the completion of the loop’s iterations. There are several Exercise Problems for you to practice.

Exercise 4.1#

Inline Exercise 4.1: Write a for loop that prints the entries of the following list, one at a time:
words = ["Wow,", "python", "is", "really", "cool."]

##TODO
words = ["Wow,", "python", "is", "really", "cool."]

##TODO
for i in words:
        print(i)
Wow,
python
is
really
cool.

Exercise 4.2#

Inline Exercise 4.2: Write a for loop that computes 2^n only using multiplication by 2.
n = 5

##TODO
n = 5

##TODO
val = 2
for i in range(0,n-1):
	val = val*2
	
print(val)
32

Exercise 4.3#

Inline Exercise 4.3: Write a for loop that adds 1 to elements in a list and prints their squared value.
nums = [0, 3, -2, -1]

##TODO
nums = [0, 3, -2, -1]

##TODO
for i in nums:
	print((i+1)**2)
1
16
1
0

5. Pandas#

Exercise 5.1#

Your colleague wants to build a classifier that will identify types of bears, but is having trouble importing data for her project. Lucikly, you know about pandas. No, not the fluffy black and white things, the python data manipulation package.

The file bears-are-bears.csv contains a human-generated categorization of several bear specimens obtained by underpaying undergraduate students to collect data. Please import the file and display it to the console to verify that it matches your expectations.

import pandas as pd

# df = pandas.read_csv(--CODE TO IMPORT HERE--)
# print(df)
df = pd.read_csv('bears-are-bears.csv', index_col=0)
print(df)
         Bear Type  Weight (kg)            Color  Endangered Bear Necessities  \
Tag #                                                                           
1       Black bear          280            Black         You    Charmin Ultra   
2       Brown bear          700            Brown          No              NaN   
3       Polar bear           80            White         Yes              NaN   
4      Andean bear          200            Black  Vulnerable              NaN   
5       Panda bear          113  Black and white         NaN           Bamboo   
6       Sloth bear          100            Black         Yes              NaN   
7         Sun bear           50            Black         Yes              NaN   
8       Panda bear          123  Black and white         Yes           Bamboo   
9       Black bear          287            Brown          No              NaN   
10      Black bear          276            Black          No              NaN   

                 Speed        Eats  
Tag #                               
1                 Fast  Everything  
2                  NaN      Salmon  
3      Faster in water  Not enough  
4                  NaN  bromeliads  
5                  NaN      Bamboo  
6                 Slow     Insects  
7                  NaN     Insects  
8                  NaN      Bamboo  
9                 Fast  Everything  
10                Fast  Everything  

Exercise 5.2#

Your colleague wants to have an alphabetically sorted unique list of all the bear types. Generate this from the pandas DataFrame.

# sorted_bears = --CODE HERE TO CREATE UNIQUE SORTED LIST--
# print("Sorted bears list:")
# print(sorted_bears)
sorted_bears = sorted(df['Bear Type'].unique().tolist())
print("Sorted bears list:")
print(sorted_bears)
Sorted bears list:
['Andean bear', 'Black bear', 'Brown bear', 'Panda bear', 'Polar bear', 'Sloth bear', 'Sun bear']