Basic programming in Python and Jupyter Notebooks#

This is a tutorial on important Python libraries such as basic data types, Numpy, function definitions and creation of loops.

In Python, the first step is always to import libraries that are will be relevant to the code that you will be writing. This usually forms the first few lines of a Python script and libraries can be imported using the import keyword. Sometimes you will also use the statement from package import method. This is a way to import a specific function from one of the packages. Packages can also be assigned aliases when importing them to make them easier to reference in the code. This can be done by using the import statement import package as alias.

# Some standard import statements
import math
import scipy

# Importing a specific function from a package
from scipy.io import loadmat

# Importing a package and assigning an alias to it
import numpy as np
import matplotlib.pyplot as plt
# Now numpy functions can be accessed through np

Basic data types in Python#

There are a few ways to store data in Python that we will be using in this jupyter-book. Python uses all of the common data types such as integers, floating point numbers and strings. Some data types that are unique in Python and which we will be using in this jupyter-book are tuples, lists, and dictionaries.

Tuples#

Tuples are data types that are defined using round brackets () and the elements of tuples can be any other data type such as integers, floating point numbers or even other tuples. A tuple is an ordered collection of elements that cannot be changed i.e., you cannot modify a tuple once it is defined.

# Defining a tuple in Python
atuple = (0.5243,"python",3)
btuple = (1,2,3)

print("First element of atuple is:", atuple[0])
First element of atuple is: 0.5243
print("Second element of atuple is:", atuple[1])
Second element of atuple is: python
# Printing number of elements in the list
elements = len(atuple)
print("Number of elements in atuple:", elements)
Number of elements in atuple: 3
# Tuples cannot be changed
atuple[0] = 0 # This will give an error
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[46], line 2
      1 # Tuples cannot be changed
----> 2 atuple[0] = 0 # This will give an error

TypeError: 'tuple' object does not support item assignment

Lists#

Lists are also collections of any other data type of Python. However, the difference between lists and tuples is that lists can be modified i.e., elements can be changed or removed and new elements can be added.

# Consider the following list and associated operations
alist = [1,"python",3]

last = alist[-1]
print("Last element of list:", last)
Last element of list: 3
alist[0] = 0 # Modifying the first element of the list
print("New list:", alist)
New list: [0, 'python', 3]
added_element = [1,2,3]
alist.append(added_element)
print("Modified list:", alist)
Modified list: [0, 'python', 3, [1, 2, 3]]
print("Number of elements in the list:", len(alist))
Number of elements in the list: 4

Dictionaries#

Dictionaries are data structures within Python that store data in key: value pairs. The values or data can be accessed using the keys and the data can be any other data type of Python. The keys must be unique and cannot be changed once defined. The associated values can be changed after the dictionary is defined.

# Consider the following dictionary and associated operations
dictionary = {1: "python", "array": np.array([1,2,3]), 3: [1,4,5]}

# Some operations on the dictionary
print("First element of dictionary:", dictionary[1])
print("Value associated with array:", dictionary["array"])
First element of dictionary: python
Value associated with array: [1 2 3]
dictionary[3] = [2,4,6] # Modifying an element of the dictionary
print("Modified dictionary:", dictionary)
Modified dictionary: {1: 'python', 'array': array([1, 2, 3]), 3: [2, 4, 6]}
# Adding a new element to a dictionary
dictionary["fruit"] = "apple"
print("Dictionary with new key:value pair:", dictionary) 
Dictionary with new key:value pair: {1: 'python', 'array': array([1, 2, 3]), 3: [2, 4, 6], 'fruit': 'apple'}

Numpy library#

A library for scientific computation in Python which is very similar to MATLAB. The basic unit for computation in Numpy is a numpy array (also called an ndarray) which is the analogue of an array used in MATLAB. We will be using this library in almost every section of the jupyter-book.

# Defining a numpy array
# import numpy as np
# library_name.method_name to access the method
a = np.array([1,2,3]) # equivalent to MATLAB a = [1,2,3];
print('1D array:', a)

b = np.array([[1,2], [3,4]]) # equivalent to MATLAB b = [1, 2 ; 3, 4]
print('2D array:', b)
1D array: [1 2 3]
2D array: [[1 2]
 [3 4]]
# Useful information about the array can also be accessed as follows
print("Dimension of array:", b.ndim)
Dimension of array: 2
print("Shape of 2D array:", b.shape, ", Shape of 1D array:", a.shape) # Think of this as matrix dimensions or vector length if 1D
Shape of 2D array: (2, 2) , Shape of 1D array: (3,)
print("Number of elements in 1D array:", a.size)
Number of elements in 1D array: 3
# Some standard arrays can also be defined using a standard Numpy statement
zero_array = np.zeros((3,2)) # The numbers in the bracket indicate the shape and size of the array needed
print("2D array of zeros:", zero_array)

ones_array = np.ones(10)
print("1D array of ones:", ones_array)
2D array of zeros: [[0. 0.]
 [0. 0.]
 [0. 0.]]
1D array of ones: [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
# Creating a evenly spaced array very similar to linspace in MATLAB
linear_array = np.linspace(0, 1, 5)
print("Linearly spaced array:", linear_array)
Linearly spaced array: [0.   0.25 0.5  0.75 1.  ]

Next, we can take a look at how to index Numpy arrays to access particular elements of the array. This indexing is the same as the indexing used for lists and tuples.

# Indexing the 1D array
print("First element of a:", a[0])
print("Last element of a:", a[-1]) # this is similar to using end in MATLAB to access the last element
First element of a: 1
Last element of a: 3
# Indexing a 2D array
print("Element of b in first row and first column:", b[0,0])
Element of b in first row and first column: 1

Another important aspect of working with Numpy arrays is reshaping the arrays in case that is needed for using the arrays with other libraries.

# Consider reshaping 1D array 'a' from a shape (3,) to shape (1,3) or (3,1)
print("Shape of a:", a.shape)

# The shape is a tuple data type with individual elements that can be accessed
print("First shape element of a:", a.shape[0])
print("Last shape element of a:", a.shape[-1])
Shape of a: (3,)
First shape element of a: 3
Last shape element of a: 3
a = a.reshape(1,-1)
print("New shape of a:", a.shape)
a = a.reshape(-1,1)
print("New shape of a:", a.shape)
New shape of a: (1, 3)
New shape of a: (3, 1)
# Reshaping 2D array 'b' from shape (2,2) to shape (1,4)
print("Shape of b:", b.shape)
b = b.reshape(1,4)
print("New shape of b:", b.shape)
Shape of b: (2, 2)
New shape of b: (1, 4)
b = b.reshape(1,5) # This will give an error because there are only 4 elements in b
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[65], line 1
----> 1 b = b.reshape(1,5) # This will give an error because there are only 4 elements in b

ValueError: cannot reshape array of size 4 into shape (1,5)

If statements#

An “if statement” in Python is written using the if keyword. The “if statement” evaluates the written Python statements only if a condition is true. A few examples of “if statements” are shown below.

a = 1
b = 2
if a < b:
    print("a is less than b")
else:
    print("a is greater than b")
a is less than b
a = 5
b = 6
if a == b:
    print("a is equal to b")
elif a != b:
    print("a is not equal to b")
a is not equal to b
# Boolean variables can also be used to write if statements
var = True
if var:
    print("The variable is true")
The variable is true

For loops#

A for loop is used to iterate over a sequence of items defined by a data type such as a list, tuple or array. The loop contains a set of statements that perform operations on each element present in the iterable data type such as lists.

One important method for defining for loops in Python, especially in the case of iterating through a list of integers, is the range function. Using range(n) defines an iterable list of integers starting from 0 and ending at n-1 which can be used within the statements of the for loop. The start, end and increment of the list of integers can also be specified in the range function. For example, to generate a list of numbers starting at 2, ending at 6 with an increment of 2 can be written as range(2,8,2). Here, the actual end mentioned in the statement will not be included in the list so the list will stop at 6 and not at 8.

# Iterating over a list using a for loop
aircraft = ["Cessna", "Boeing 787", "Airbus A380", "Boeing 777"]
for name in aircraft:
    print(name)
Cessna
Boeing 787
Airbus A380
Boeing 777
# Iterating over integers using the range function
for i in range(5):
    print(i)
0
1
2
3
4

While loops#

The while loop can be used to execute a set of statements as long as a given condition is true. This condition can be defined using standard boolean operations and statements.

# Simple while loop to print numbers
i = 0
while i < 6:
    print(i)
    i += 1
0
1
2
3
4
5
# Simple while loop to find sum of first five integers
sum = 0
i = 1
while i < 6:
    sum += i
    i += 1
print("Sum of first five integers:", sum)
Sum of first five integers: 15

Defining functions in Python#

Functions are a very important part of every programming language since we are able to define smaller and resuable blocks of code that can be incorporated in larger and more complicated programs. You can pass parameters to a function as an input and the function can return data as an output. A function is defined using the def keyword in Python.

In the example below, we will write a set of Python functions to calculate the derivate of the function

\[ f(x) = -0.1x^{4} - 0.15x^{3} - 0.5x^{2} - 0.25x + 1.25 \]

at \(x = 0.5\) using a step size \(h = 0.5\) and central differencing. We will compare the numerical approximation with the true derivative of the function.

To measure the difference between the true derivative and the approximation of the central difference, we will use the relative error which can be expressed as:

\[\text{Relative Error} = \frac{\text{true value} - \text{approximation}}{\text{true value}}\]

This will also be covered in more detail in the section on review of numerical methods of this jupyter-book.

def true_function(x):
    """
        Function which returns value of desired function at input x.
    """
    value = -0.1*x**4 - 0.15*x**3 - 0.5*x**2 - 0.25*x + 1.25

    return value

def true_derivative(x):
    """
        Returns true derivative of function at input x.
    """
    value = -0.4*x**3 - 0.45*x**2 - x - 0.25

    return value 

def central_diff(x,h,func):
    """
        Function for computing central difference.
        Input:
        x - input at which derivative is desired
        h - step size
        func - python function which should return function value based on x.
    """
    slope = (func(x+h) - func(x-h)) /2/h
    
    return slope
# Few vairables
x = 0.5
h = 0.5

# True value
true_value = true_derivative(x)
print("True value: {}\n".format(true_value))

# Central difference
approx_value = central_diff(x,h,true_function)
print("Central difference:")
print("Approx value: {}".format(approx_value))
print("Relative error: {}".format((true_value - approx_value)/true_value))
True value: -0.9125

Central difference:
Approx value: -1.0
Relative error: -0.09589041095890413