The purpose of this Jupyter Notebook is to get you started using Python and Jupyter Notebooks for routine engineering calculations. This introduction assumes this is your first exposure to Python or Jupyter notebooks.
The easiest way to use Jupyter notebooks is to use a cloud-based service such as Google Colaboratory. You will need continuous internet connectivity to access your work, but the advantages are there is no software to install or maintain.
Installing Jupyter/Python on your Laptop
For regular off-line use you should consider installing a Jupyter Notebook/Python environment directly on your laptop. This will provide you with reliable off-line access to a computational environment. This will also allow you to install additional code libraries to meet particular needs.
Choosing this option will require an initial software installation and routine updates. For this course the recommended package is Anaconda available from Continuum Analytics. Downloading and installing the software is well documented and easy to follow. Allow about 10-30 minutes for the installation depending on your connection speed.
After installing be sure to check for updates before proceeding further. With the Anaconda package this is done by executing the following two commands in a terminal window:
> conda update conda
> conda update anaconda
Anaconda includes an 'Anaconda Navigator' application that simplifies startup of the notebook environment and manage the update process.
1. Start a Jupyter Notebook Session
If you are using a cloud-based service a Jupyter session will be started when you log on.
If you have installed a Jupyter/Python distribution on your laptop then you can open a Jupyter session in one of two different ways:
- Use the Anaconda Navigator App, or
Open a terminal window on your laptop and execute the following statement at the command line:
> jupyter notebook
Either way, once you have opened a session you should see a browser window.
At this point the browser displays a list of directories and files. You can navigate amoung the directories in the usual way by clicking on directory names or on the 'breadcrumbs' located just about the listing.
Jupyter notebooks are simply files in a directory with a .ipynb suffix.
a = 12
b = 2
print(a + b)
print(a**b)
print(a/b)
import numpy as np
# mathematical constants
print(np.pi)
print(np.e)
# trignometric functions
angle = np.pi/4
print(np.sin(angle))
print(np.cos(angle))
print(np.tan(angle))
xList = [1, 2, 3, 4]
xList
You can join one list to another or concatentate them
x = [1, 2, 3, 4];
y = [5, 6, 7, 8];
x + y
np.sum(x)
Element by element operation
print(np.add(x,y))
print(np.multiply(x,y))
print(np.dot(x,y))
A for loop is a means for iterating over the elements of a list. The colon marks the start of code that will be executed for each element of a list. Indenting has meaning in Python. In this case, everything in the indented block will be executed on each iteration of the for loop. This example also demonstrates string formatting.
for x in xList:
print("sin({0}) = {1:8.5f}".format(x,np.sin(x)))
Note that while you can do calculations on the lists, NumPy has a special object to represent math vectors or matrices called array.
This is NumPy’s main object and it is a homogeneous multidimensional array. It is a table of elements (usually numbers), all of the same type, indexed by a tuple of non-negative integers. In NumPy dimensions are called axes.
NumPy arrays are much more powerful.
Creating an array:
a = np.array([2, 3, 4])
array transforms sequences of sequences into two-dimensional arrays, sequences of sequences of sequences into three-dimensional arrays, and so on.
b = np.array([(1.5, 2, 3), (4, 5, 6)])
print(b)
The type of the array can also be explicitly specified at creation time:
c = np.array([[1, 2], [3, 4]], dtype=complex)
print(c)
Often, the elements of an array are originally unknown, but its size is known. Hence, NumPy offers several functions to create arrays with initial placeholder content. These minimize the necessity of growing arrays, an expensive operation.
print(np.zeros((3, 4)))
np.ones((2, 3, 4), dtype=np.int16)
Arithmetic operators on arrays apply elementwise. A new array is created and filled with the result.
a = np.array([20, 30, 40, 50])
b = np.arange(4)
print(a)
print(b)
c = a - b
print(c)
b**2
10 * np.sin(a)
a < 35
Important
Unlike in many matrix languages, the product operator *
operates elementwise in NumPy arrays.
The matrix product can be performed using the @
operator (in python >=3.5) or the dot
function or method:
A = np.array([[1, 1],
[0, 1]])
B = np.array([[2, 0],
[3, 4]])
A * B # elementwise product
A @ B # matrix product
A.dot(B) # another matrix product
Dictionaries are useful for storing and retrieving data as key-value pairs.
mw = {'CH4': 16.04, 'H2O': 18.02, 'O2':32.00, 'CO2': 44.01}
mw
We can retrieve a value from a dictionary:
mw['CH4']
A for loop is a useful means of interating over all key-value pairs of a dictionary.
for values in mw.keys():
print("Value {:<s} is {}".format(values, mw[values]))
Dictionaries can be sorted by key or by value
for values in sorted(mw):
print(" {:<8s} {}".format(values, mw[values]))
for values in sorted(mw, key = mw.get):
print(" {:<8s} {}".format(values, mw[values]))
Importing the matplotlib.pyplot library gives IPython notebooks plotting functionality very similar to Matlab's. Here are some examples using functions from the
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0,10)
y = np.sin(x)
z = np.cos(x)
plt.plot(x,y,'b',x,z,'r')
plt.xlabel('Radians');
plt.ylabel('Value');
plt.title('Plotting Demonstration')
plt.legend(['Sin','Cos'])
plt.grid()
plt.plot(y,z)
plt.axis('equal')
plt.subplot(2,1,1)
plt.plot(x,y)
plt.title('Sin(x)')
plt.subplot(2,1,2)
plt.plot(x,z)
plt.title('Cos(x)')
Tutorial Introduction to Python for Science and Engineering
The following text is available on Amazon. Resources for this book are available on github.
- A Primer on Scientific Programming with Python (Fourth Edition) by Hans Petter Langtangen. Resources for this book are available on github.
pycse is a package of python functions, examples, and document prepared by John Kitchin at Carnegie Mellon University.
And there is plenty more! Google it!
This second part of the notebook is to describe some more Python concepts that will be used during the class.
answer = 42
#answer contained an integer because we gave it an integer!
is_it_thursday = True
is_it_wednesday = False
#these both are 'booleans' or true/false values
pi_approx = 3.1415
#This will be a floating point number, or a number containing digits after the decimal point
my_name = "Andrea"
#This is a string datatype, the name coming from a string of characters
#Data doesn't have to be a singular unit
#p.s., we can print all of these with a print command. For Example:
print(answer)
print(pi_approx)
prices = [10, 20, 30, 40, 50]
#This is a way to define a list in place. We can also make an empty list and add to it.
colors = []
colors.append("Green")
colors.append("Blue")
colors.append("Red")
print(colors)
#We can also add unlike data to a list
prices.append("Sixty")
#As an exercise, look up lists in python and find out how to add in the middle of a list!
print(prices)
#We can access a specific element of a list too:
print(colors[0])
print(colors[2])
#Notice here how the first element of the list is index 0, not 1!
#Languages like MATLAB are 1 indexed, be careful!
#In addition to lists, there are tuples
#Tuples behave very similarly to lists except that you can't change them
# after you make them
#An empty Tuple isn't very useful:
empty_tuple = ()
#Nor is a tuple with just one value:
one_tuple = ("first",)
#But tuples with many values are useful:
rosa_parks_info = ("Rosa", "Parks", 1913, "February", 4)
#You can access tuples just like lists
print(rosa_parks_info[0] + " " + rosa_parks_info[1])
# You cannot modify existing tuples, but you can make new tuples that extend
# the information.
# I expect Tuples to come up less than lists. So we'll just leave it at that.
float1 = 5.75
float2 = 2.25
#Addition, subtraction, multiplication, division are as you expect
print(float1 + float2)
print(float1 - float2)
print(float1 * float2)
print(float1 / float2)
#Here's an interesting one that showed up in the first homework in 2017. Modulus:
print(5 % 2)
#however, they are from the 'math' package in python. Let's add that package!
import math
print(math.log(float1))
print(math.exp(float2))
print(math.pow(2,5))
# There is a quicker way to write exponents if you want:
print(2.0**5.0)
#Like in MATLAB, you can expand the math to entire lists
list3 = [1, 2, 3, 4, 5]
print(2 * list3)
%matplotlib inline
import matplotlib.pyplot as plt
x_vals = [-2, -1, 0, 1, 2]
y_vals = [-4, -2, 0, 2, 4]
plt.plot(x_vals, y_vals)
#Let's try printing a list
fib = [1, 1, 2, 3, 5, 8]
#While loops are the basic type
i = 0
while(i < len(fib)):
print(fib[i])
i = i + 1
#In matlab, to do the same thing you would have the conditional as: counter < (length(fib) + 1)
#This is because matlab starts indexing at 1, and python starts at 0.
#The above type of loop is so common that the 'for' loop is the way to write it faster.
print("Let's try that again")
#This is most similar to for loops in matlab
for i in range(0, len(fib)) :
print(fib[i])
print("One more time:")
#Or you can do so even neater
for e in fib:
print(e)
A function is a block of code which only runs when it is called.
You can pass data, known as parameters, into a function.
A function can return data as a result.
def my_function():
print("Hello from a function")
To call a function, use the function name followed by parenthesis:
my_function()
Information can be passed into functions as arguments.
Arguments are specified after the function name, inside the parentheses. You can add as many arguments as you want, just separate them with a comma.
The following example has a function with one argument (fname). When the function is called, we pass along a first name, which is used inside the function to print the full name:
def my_function(fname):
print(fname + " Refsnes")
my_function("Emil")
my_function("Tobias")
my_function("Linus")
You can send any data types of argument to a function (string, number, list, dictionary etc.), and it will be treated as the same data type inside the function.
E.g. if you send a List as an argument, it will still be a List when it reaches the function:
def my_function(food):
for x in food:
print(x)
fruits = ["apple", "banana", "cherry"]
my_function(fruits)
To let a function return a value, use the return statement:
def my_function(x):
return 5 * x
print(my_function(3))
print(my_function(5))
print(my_function(9))
A class is a user-defined blueprint or prototype from which objects are created. Classes provide a means of bundling data and functionality together. Creating a new class creates a new type of object, allowing new instances of that type to be made. Each class instance can have attributes attached to it for maintaining its state. Class instances can also have methods (defined by their class) for modifying their state.
To understand the need for creating a class let’s consider an example, let’s say you wanted to track the number of dogs that may have different attributes like breed, age. If a list is used, the first element could be the dog’s breed while the second element could represent its age. Let’s suppose there are 100 different dogs, then how would you know which element is supposed to be which? What if you wanted to add other properties to these dogs? This lacks organization and it’s why we need classes.
Class creates a user-defined data structure, which holds its own data members and member functions, which can be accessed and used by creating an instance of that class. A class is like a blueprint for an object.
It’s not hard to define Python class. To do so, you’ll need the class
keyword:
`class ClassName:
# Statement-1
.
.
.
# Statement-N`
For example
class Example:
variable = 123
If you run the above code in a Python environment, you’ll find you can call Example.variable
to return an integer value.
Example.variable
This is an example of a class for data-only objects, but it’s equally easy to define a class that returns a function object by adding the def
keyword to your code:
class Example:
def b(self):
return "this is an example class"
Example.b # we are accessing the function...this is probably not what we want to do..
We need a few more concepts:
An Object is an instance of a Class. A class is like a blueprint while an instance is a copy of the class with actual values. It’s not an idea anymore, it’s an actual dog, like a dog of breed pug who’s seven years old. You can have many dogs to create many different instances, but without the class as a guide, you would be lost, not knowing what information is required. An object consists of :
- State: It is represented by the attributes of an object. It also reflects the properties of an object.
- Behavior: It is represented by the methods of an object. It also reflects the response of an object to other objects.
- Identity: It gives a unique name to an object and enables one object to interact with other objects.
Declaring Objects (Also called instantiating a class)
When an object of a class is created, the class is said to be instantiated. All the instances share the attributes and the behavior of the class. But the values of those attributes, i.e. the state are unique for each object. A single class may have any number of instances.
Example:
class Dog:
# A simple class
# attribute
attr1 = "mammal"
attr2 = "dog"
# A sample method
def fun(self):
print("I'm a", self.attr1)
print("I'm a", self.attr2)
# Object instantiation
Rodger = Dog()
# Accessing class attributes
# and method through objects
print(Rodger.attr1)
Rodger.fun()
Class methods must have an extra first parameter in the method definition. We do not give a value for this parameter when we call the method, Python provides it.
If we have a method that takes no arguments, then we still have to have one argument.
When we call a method of this object as myobject.method(arg1, arg2)
, this is automatically converted by Python into MyClass.method(myobject, arg1, arg2)
.
Note that this means that inside the function method
(in our example) we now have access to the instance of the class! so we can access its variables, etc.
The init method is similar to constructors in C++, it constructs the object and can be used to initialise the object’s state.
Like methods, a constructor also contains a collection of statements (i.e. instructions) that are executed when the object is created.
The __init__
method runs as soon as an object of a class is instantiated. The method is useful to do any initialization you want to do with your object.
class Person:
# init method or constructor
def __init__(self, name):
self.name = name
# Sample Method
def say_hi(self):
print('Hello, my name is', self.name)
p = Person('Nikhil') # as soon as we do this, the __init__ method is called.
p.say_hi()
Instance variables are used to store data that is unique to each instance of the class. Instance variables are variables whose value is assigned inside the
__init__
method or inside a class method (a method with the argumentself
)Class variables are for attributes and methods shared by all instances of the class. Class variables are variables whose value is assigned directly in the class.
class Dog:
# Class Variable
animal = 'dog'
# The init method or constructor
def __init__(self, breed, color):
# Instance Variable
self.breed = breed
self.color = color
# Objects of Dog class
Rodger = Dog("Pug", "brown")
Buzo = Dog("Bulldog", "black")
print('Rodger details:')
print('Rodger is a', Rodger.animal)
print('Breed: ', Rodger.breed)
print('Color: ', Rodger.color)
print('\nBuzo details:')
print('Buzo is a', Buzo.animal)
print('Breed: ', Buzo.breed)
print('Color: ', Buzo.color)
# Class variables can be accessed using class
# name also
print("\nAccessing class variable using class name")
print(Dog.animal)