Python has emerged as the primary programming language used in Deep Learning. The syntax is similar to MATLAB, but it has been designed with S/W developers in mind, whereas MATLAB was designed to translate mathematical formulae.
In its simplest form, a Python program is simply a text file containing Python statements After you create the file, you must tell Python to execute that file. This is done through a terminal shell, an integrated development environment (IDE), or HTML notebooks that can be run from a browser.
Python is a scripting language. This means that the command in your file have to be compiled into byte code which is then routed to a virtual machine. This functionality is hidden from the user. Byte code is a lower leel platform independent representation of the source code. This is done to speed the execution of the program. The byte code is saved as a .pyc file, whereas th source is saved as a .py file.
The simplest way to run your program is through a shell environment. So affter installing Python on your computer, you would open a shell (terminal program). Developing large progams in this manner, however, is cumbersome. A much more natural way to write your program and to test its functionality in small chunks is through an html notebook such as the Juypter notebook. These notebook files are saved as *.ipynb. Some editors such as Visual Studio Code support Juypter notebook files.
In fact this Python tutorial was written in a Jupyter notebook. The notebook has two types of blocks; markdown (for comments) and code bocks (for the python code). The nice thing about the code blocks is that you can execute them by themselves, thereby providing a convenient way to incrementally write and check the functionality of your program.
I am going to use this notebook to illustrate the basic data types, methods, and ways of writing Python programs.
Python is a dynamically typed language. This means that you do not need always need to explicitly declare the type of a variable ahead of time. Python if often intelligent enough to make a reasonably good guess about the type.
There are several basic data types. The first data type of interest are numbers and their basic binary operations.
x = 3 #declare a number variable, x
print(type(x)) #prints "<class 'int'>"
print(x) #prints "3"
print(x+1) #Addition; prints "4"
print(x-1) #Subtraction: prints "2"
print(x*2) #Multiplication: prints "6"
print(x**2) #Exponentiation; prints "9"
x += 1
print(x) #increment, prints 4
x*=2
print(x) #prints 8
y = 2.5
print(type(y)) #prints "<class 'float'>"
print(y, y+1, y*2, y**2) #prints " 2.5 3.5 5.0 6.25"
<class 'int'> 3 4 2 6 9 4 8 <class 'float'> 2.5 3.5 5.0 6.25
Another important data type are Boolean variables.
t = True
f = False
print(type(t)) #prints "<class 'bool'>"
print(t and f) #Logical AND; prints "False"
print(t or f) #Logical OR; prints "True"
print(not t) #Logical NOT; prints "False"
print(t != f) #Logical Xor; prints "True"
<class 'bool'> False True False True
Python also supports string data types
hello = 'hello' #string literals cna use single quotes
world = "world" # or double quotes
print (hello) #prints "hello"
print(len(hello)) #print string length; prints "5"
hw = hello + ' ' + world #string concatenation
print(hw) #prints "hello world"
hw12 = '%s %s %d' % (hello, world, 12) #sprintf style string formatting
print(hw12)
hello 5 hello world hello world 12
Strings are extremely useful data types for programmers. So there are a number of methods that are particularly useful in manipulating such strings.
s = "hello"
print(s.capitalize()) #Capitalize a strong; prints "Hello"
print(s.upper()) #convert a string to upperase; prints HELLO
print(s.rjust(7)) #right justify a strong, padding with spaces; prints " hello"
print(s.center(7)) #center a string, padding with spaces, prints " hello "
print(s.replace('l','(ell)')) #replade all instances of one substring by another
print(' world '.strip()) #strip leading and trailing whitespace
Hello HELLO hello hello he(ell)(ell)o world
Python has some important container types (i.e. data structures consisting of several subtypes of data).
These container types are lists, dictionaries, sets, and tuples. Let us first look at the list data type
xs = [3, 1, 2] #create a list
print(xs, xs[2]) #prints "[3, 1, 2] 2"
print(xs[-1]) #negative indices cont from end of list; prints "2"
xs[2] = 'foo' #lists can contain elements of different types
print(xs) #prints "[3, 1, 'foo']"
xs.append('bar') #add a new element to the end of the list
print(xs) #prints "[3, 1, 'foo', 'bar']"
x = xs.pop() #remove and return the last element of list
print(x,xs) #prints "bar [3, 1, 'foo']"
[3, 1, 2] 2 2 [3, 1, 'foo'] [3, 1, 'foo', 'bar'] bar [3, 1, 'foo']
There is special concise syntax used to access sublists. This is sometimes called "slicing" a list. The syntax is similar to MATLAB's slicing syntax with some notable exceptions. Another important thing to note about lists is that the indices start at 0, rather than 1.
nums = list(range(5)) #range is built-in function to create a list of integers
print(nums) #Prints "[0, 1, 2, 3, 4]"
print(nums[2:4]) #get a slice from index to 4 (exclusive); prints "[2, 3]"
print(nums[2:]) #get slice from endex 2 to end; prints "[2, 3, 4]"
print(nums[:2]) #get slice from start to index 2 (exclusive); prints "[0, 1]"
print(nums[:]) #get slice of whole list; prints "[0, 1, 2, 3, 4]"
print(nums[:-1]) #slice indices can be negative; prints "[0, 1, 2, 3]"
nums[2:4] = [8, 9] #assign a new sublist to a slice
print(nums) #prints "[0, 1, 8, 9, 4]"
[0, 1, 2, 3, 4] [2, 3] [2, 3, 4] [0, 1] [0, 1, 2, 3, 4] [0, 1, 2, 3] [0, 1, 8, 9, 4]
There is a FOR statement that can be used to loop over the elements of a list. The syntax is somewhat different from MATLAB in that it doesn't have to explicitly declare the range of indices to loop over. The statement "for animal in animals:" knows it is going to loop through all the indices and the actual variable for that index will be called "animal"
One important difference between Python and other languages is that it does not use scoping braces to identify the statements to be executed within a loop. In other words, we don't need to always match and if or while or for statement with a corresponding "end" statement. Instead Python uses the colon : to declare the start of the statements to be looped through. These statements are then typed in using a TAB indent. The end of the looped statements is the last TAB indented statement. This can be troublesome for beginners who are not used to using TABS in this way.
animals = ['cat', 'dog', 'monkey']
for animal in animals:
print(animal)
#prints "cat", "dot", "monkey", each on its own line
cat dog monkey
If you want access to index of each element within the body of aloop use the enumerate function
animals = ['cat', 'dog', 'monkey']
for idx, animal in enumerate(animals):
print('#%d: %s' % (idx + 1, animal))
# prints "#1: cat"
# "#2: dog"
# "#3: monkey"
#1: cat #2: dog #3: monkey
animals = ['cat', 'dog', 'monkey']
for indx in range(len(animals)):
print('#%d: %s' % (indx + 1, animals[indx]))
#1: cat #2: dog #3: monkey
List comprehensions: when programming, we often want to transform one type of data into another. As an example, consider the following that computes square numbers
nums = [0, 1, 2, 3, 4]
squares = []
for x in nums:
squares.append(x**2)
print(squares) #prints [0, 1, 4, 9, 16]
[0, 1, 4, 9, 16]
You can make this simpler using list comprehension
nums = [0, 1, 2, 3, 4]
squares = [x**2 for x in nums]
print(squares)
#Prints [0, 1, 4, 9, 16]
[0, 1, 4, 9, 16]
List comprehensions can also contain conditions:
nums = [0, 1, 2, 3, 4]
even_squares = [x ** 2 for x in nums if x % 2==0]
print(even_squares) #prints "[0, 4, 16]"
[0, 4, 16]
Dictionaries: a dictionary stores (key, value) pairs.
d = {'cat': 'cute', 'dog': 'furry'} #create a new dictionary with some data
print(d['cat']) #get entry from dictionary for; prints "cute"
print('cat' in d) #check if dict has a given key; prints "True"
d['fish'] = 'wet' #set an entry in the dict
print(d['fish']) #prints "wet"
#print(d[ 'monkey']) #returns "KeyError: 'monkey' since 'monkey' not in dict"
print(d.get('monkey', 'N/A')) #get an element with adefualt; prints "N/A"
print(d.get('fish', 'N/A')) # prints "wet" (using default)
del d['fish'] # removes element from dictionary
print(d.get('fish', 'N/A')) # prints "N/A" since fish no longer a key in dict
cute True wet N/A wet N/A
Loops: it is easy to iterate over the keys in a dictionary
d = {'person': 2, "cat": 4, "spider":8 }
for animal in d:
legs = d[animal]
print('A %s has %d legs' % (animal,legs))
#prints "A person has 2 legs", "A cat has 4 lengs", "A spider has 8 legs"
A person has 2 legs A cat has 4 legs A spider has 8 legs
Cute use of these ideas for a madlib story First declare the story using keys from a list to be created
storyFormat = """
Once upon a time, deep in an ancient jungle,
there lived a {animal}. This {animal}
liked to eat {food}, but the jungle had
very little {food} to offer. One day, an
explorer found the {animal} and discovered
it liked {food}. The explorer took the
{animal} back to {city}, where it could
eat as much {food} as it wanted. However,
the {animal} became homesick, so the
explorer brought it back to the jungle,
leaving a large supply of {food}.
The End
"""
print(storyFormat)
Once upon a time, deep in an ancient jungle, there lived a {animal}. This {animal} liked to eat {food}, but the jungle had very little {food} to offer. One day, an explorer found the {animal} and discovered it liked {food}. The explorer took the {animal} back to {city}, where it could eat as much {food} as it wanted. However, the {animal} became homesick, so the explorer brought it back to the jungle, leaving a large supply of {food}. The End
define functions that will allow you to pick entries for dictionary may function tellStory, tells the story after asking you to pick animal, food, city
def tellStory():
userPicks = dict()
addPick('animal', userPicks)
addPick('food', userPicks)
addPick('city', userPicks)
story = storyFormat.format(**userPicks)
print(story)
def addPick(cue, dictionary):
'''Prompt for a user response using the cue string,
and place the cue-response pair in the dictionary.
'''
prompt = 'Enter an example for ' + cue + ': '
response = input(prompt)
dictionary[cue] = response
Now run tellStory to generate a story and print it ou
tellStory()
input("Press Enter to end the program.")
Once upon a time, deep in an ancient jungle, there lived a dog. This dog liked to eat tofu, but the jungle had very little tofu to offer. One day, an explorer found the dog and discovered it liked tofu. The explorer took the dog back to detroit, where it could eat as much tofu as it wanted. However, the dog became homesick, so the explorer brought it back to the jungle, leaving a large supply of tofu. The End
''
If you want access to keys and their corresponding values use the items method
d = {'person': 2, 'cat': 4, 'spider': 8}
for animal, legs in d.items():
print('A %s has %d legs' % (animal, legs))
#prints "A person has 2 legs", "A cat has 4 lengs", "A spider has 8 legs"
A person has 2 legs A cat has 4 legs A spider has 8 legs
Dictionary comprehensions: these are similar to list comprehensions, and allow you to easily construct dictionaries
num = [0, 1, 2, 3, 4]
even_num_to_square = {x: x**2 for x in nums if x%2==0}
print(even_num_to_square) #prints "{0: 0, 2: 4, 4: 16}"
{0: 0, 2: 4, 4: 16}
Sets: a set is an unordered collection of distinct elements
animals = {'cat', 'dog'}
print('cat' in animals) #check if an element is in a set; prints "True"
print('fish' in animals) #prints "False"
animals.add('fish') #add an element to a set
print('fish' in animals) #no prints TRUE
animals.add('cat') #adding an element already in set does nothing
print(len(animals)) #prints "3"
animals.remove('cat') #remove an element from a set
print(len(animals)) #prints "2"
True False True 3 2
Loops: iterating over a set has the same syntax as iterating over a list However since sets are unordered, you cannot assumptions about the order in which you visit the set elements
animals = {'cat', 'dog', 'fish'}
for idx, animal in enumerate(animals):
print('#%d: %s' % (idx+1, animal))
#prints "?"
#1: dog #2: cat #3: fish
Set comprehensions: like lists and dictionaries, we can easily construct sets using set comprehensions
from math import sqrt #import the sqrt function from math library
nums = {int(sqrt(x)) for x in range(30)}
print(nums) #prints "{0, 1, 2, 3, 4, 5}"
{0, 1, 2, 3, 4, 5}
TUPLES: a tuple is an (immutable) ordered list of values. A tuple is imilar to a list with the difference being that tuples can be used as keys in dictionaries and as elemetns of sets, while lists cannot. Here is an example
d = {(x, x+1): x for x in range(10)} #create dict with tuple keys
t = (5, 6) #create a tuple
print(type(t)) #check type; prints "<class 'tuple'>"
print(d[t]) #fetches contents for t; prints "5"
print(d[(1,2)])
<class 'tuple'> 5 1
Functions: we saw above in matlib example python functions are dfined using the def keyword
def sign(x):
if x>0:
return "positive"
elif x< 0:
return "negative"
else:
return 'zero'
for x in [-1,0,1]:
print(sign(x))
#prints "negative", "zero", "positive"
negative zero positive
We often define functions to take optional keyword arguments
def hello(name, loud = False):
if loud:
print('HELLO, %s!' % name.upper())
else:
print('Hello, %s' %name)
hello('Bob') #prints "Hello, Bob"
hello('Fred', loud=True) #prints "HELLO, FRED!"
Hello, Bob HELLO, FRED!
CLASSES: Python syntax support the easy creation of class objects
class Greeter(object):
#Constructor
def __init__(self, name):
self.name = name #create an instance variable
#Instance method
def greet(self, loud=False):
if loud:
print('HELLO, %s!' % self.name.upper())
else:
print('Hello, %s' % self.name)
g = Greeter('Fred') #consruct an instance of the Greeter class
g.greet() #call an instance method; prints "Hello, Fred"
g.greet(loud=True) #call instance method; prints "HELLO FRED!"
Hello, Fred HELLO, FRED!
NUMPY: Numpy is the core library for scientific computing in Python. It provides a high performance multidimensional array object, and tools for working with these arrays.
Arrays: A numpy array is a grid of values, all of the same type, and is indexed by a tuple of nonnegative integers. The number of dimensions is the rank of the array, the shape of an array is a tuple of integers giving the size of the array along each dimension
We initialize numpy arrays from nested Python lists, and access elements using square brackets.
import numpy as np
a = np.array([1, 2, 3]) #create a rank 1 array
print(type(a)) #prints "<class 'numpy.ndarray'>"
print(a.shape) #prints "(3,)"
print(a[0], a[1], a[2]) #prints "1 2 3"
a[0] = 5 #change array element
print(a) #prints "[5, 2, 3]"
b = np.array([[1,2,3],[4,5,6]]) #create a rank 2 array
print(b.shape) #prints (2,3)
print(b[0, 0], b[0, 1], b[1, 0]) #prints "1 2 4"
<class 'numpy.ndarray'> (3,) 1 2 3 [5 2 3] (2, 3) 1 2 4
Numpy provides functions to create arrays
import numpy as np
a = np.zeros((2,2)) #create 2 by 2 array of all zeros
print(a) #prints "[[0. 0.]
# [0. 0.]]"
b = np.ones((1,2)) #create 1 by 2 array of all ones
print(b) #prints "[[1. 1.]]"
c = np.full((2,2), 7) #create a constsant array
print(c) #prints "[[7. 7.]
# [7. 7.]]"
d = np.eye(2) # create 2 by 2 indentiy matrix
print(d) #prints "[[ 1. 0.]
# [ 0. 1.]]"
e = np.random.random((2,2)) #create an array filled with random values
print(e) #?
[[0. 0.] [0. 0.]] [[1. 1.]] [[7 7] [7 7]] [[1. 0.] [0. 1.]] [[0.11568196 0.8248139 ] [0.38851037 0.70583698]]
Array indexing: numpy offers several ways to index into arrays
Slicing: similar to python lists, numpy arrays can be sliced. Since arrays may be multidimensional, you must specify a slice for each dimension of the array
import numpy as np
#create the following rank 2 array with shape (3,4)
#[[1 2 3 4]
# [ 5 6 7 8]
# [9 10 11 12]]
a = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
#use slicing to pull out the subarray consisting of the first 2 rows
#and columns 1 and 2; b is the following array of shape (2,2):
# [[2 3]
# [6,7]]
b = a[:2, 1:3]
print(b)
#a slice of an array is a view into the same data, so modifying it will modify the orignal array
# so b is not a copy of a, it is a pointer to elements within a
print(a[0,1]) #prints "2"
b[0,0] = 77 #b[0,0] is the same as a[0,1]
print(a[0,1]) #prints "77"
[[2 3] [6 7]] 2 77
You can also mix integer indexing with slice indexing. However doing so will yield an array of lowe rank than the original array. NOTE: this is quite different from the way MATLAB handles array slicing
import numpy as np
#create the following rank 2 array with shape (3,4)
#[[ 1 2 3 4]
# [5 6 7 8]
# [9 10 11 12]]
a = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
#two ways of accessing the data in the middle row of the array.
#mixing integer indexing with slices yields an array of lowe rrank,
#while using only slices yields an array of the same rank as the original array:
row_r1 = a[1, :] #Rank 1 view of the second row of a
row_r2 = a[1:2, :] #Rank 2 view of the second row of a
print(row_r1, row_r1.shape) #prints "[5 6 7 8] (4,)"
print(row_r2, row_r2.shape) #prints "[[5 6 7 8]] (1,4)"
#we can make the same distinction when accessing columns of an array
col_r1 = a[:,1]
col_r2 = a[:,1:2]
print(col_r1, col_r1.shape) #prints "[2 6 10] (3,)"
print(col_r2, col_r2.shape) #prints "[[ 2]
# [ 6]
# [10]] (3,1)"
[5 6 7 8] (4,) [[5 6 7 8]] (1, 4) [ 2 6 10] (3,) [[ 2] [ 6] [10]] (3, 1)
Integer array indexing: When you index into numpy arrays using slicing, the resulting array view will always be a subarray of the original matrx. In contrast, integer array indexing allows you to construct arbirary arrays using the data rrom another array
import numpy as np
a = np.array([[1,2], [3,4], [5,6]])
#an example of integer array indexing.
#the return array will hae shape (3,) and
print(a[[0,1,2],[0,1,0]])
#prints "[1 4 5]"
#the above example is equivalent to this
print(np.array([a[0,0], a[1,1], a[2,0]]))
#when using integer array indexing, you can reuse the same element from the source array
print(a[[0,0], [1,1]])
#which is equivalent to this
print(np.array([a[0,1], a[0,1]])) #both print "[2,2]"
[1 4 5] [1 4 5] [2 2] [2 2]
A useful trick with integer array indexing is selecting or mutating one element from each row of a matrix
import numpy as np
#create a new array from which we select elements
a = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])
print(a)
#create an array of indices
b = np.array([0, 2, 0, 1])
#select one elmement from each row of a using the indices in b
print(a[np.arange(4), b]) #prints "[1 6 7 11]"
#mutuate one element from each row of a rusing indices in b
a [np.arange(4), b] += 10
print(a) #prints "array([[ 11 2 3],
# [4, 5, 16],
# [17, 8, 9],
# [10, 21, 12]])"
[[ 1 2 3] [ 4 5 6] [ 7 8 9] [10 11 12]] [ 1 6 7 11] [[11 2 3] [ 4 5 16] [17 8 9] [10 21 12]]
Boolean array indexing lets you pick out aribtary elements of an array. This is used to select elements of an array that satsify some condition
import numpy as np
a = np.array([[1,2], [3,4], [5,6]])
bool_idx = (a>2) #find elements of a that are bigger than 2
#this returns a numpy array of Booleans of the same
#shape as a, where each slot of bool_idx tells
#whether that element of a is > 2
print(bool_idx) #prints "[[False False]
# [True True]
# [True True]]"
#We use boolean array indexing to construct a rank 1 array
#consisting of the elements of a corresponding to the true values of bool_idx
print(a[bool_idx]) #prints "[3 4 5 6]"
#23 can do all of the above in a single statement
print(a[a>2]) #prints "[3 4 5 6]"
[[False False] [ True True] [ True True]] [3 4 5 6] [3 4 5 6]
Data types Every numpy array is a grid of elements of the same type. Numpy provides many numeric data types that you can use to construt arrays. Numpty tries to gess a data type when you create an array, but functions that construct arrays often include an arugmeent to explicitly change the datatype
import numpy as np
x = np.array([1, 2]) #let numpy choose datatype
print(x.dtype) #prints "int64"
x = np.array([1.0, 2.0]) #let numpy choose datatype
print(x.dtype) #prints "float64"
x = np.array([1.0, 2.0], dtype = np.int64) #force a particular datatype
print(x.dtype)
int64 float64 int64
Array math Basic mathematical functions operate ELEMENTWISE an arrays, and are available both as operator overloads and functions
import numpy as np
x = np.array([[1,2],[3,4]], dtype=np.float64)
y = np.array([[5,6],[7,8]], dtype=np.float64)
#elementwise sum; both produce the array
#[[ 6.0 8.0]
# [ 10.0 12.0]]
print(x+y)
print(np.add(x,y))
#elementwise diffference, both produce array
#[[-4.0 -4.0]
# [-4.0 -4.0]]
print(x-y)
print(np.subtract(x,y))
#elementwise product; both produce the array
#[[ 5.0 12.0]
# [21.0 32.0]]
print(x*y)
print(np.multiply(x,y))
#elementwise division; both produce the array
# [[ 0.2 0.33333]
# [0.4285713 0.5]]
print(x/y)
print(np.divide(x,y))
#elementwise sequare root
#[[ 1. 1.41421356]
# [1.73205081 2]]
print(np.sqrt(x))
[[ 6. 8.] [10. 12.]] [[ 6. 8.] [10. 12.]] [[-4. -4.] [-4. -4.]] [[-4. -4.] [-4. -4.]] [[ 5. 12.] [21. 32.]] [[ 5. 12.] [21. 32.]] [[0.2 0.33333333] [0.42857143 0.5 ]] [[0.2 0.33333333] [0.42857143 0.5 ]] [[1. 1.41421356] [1.73205081 2. ]]
Note that unlike MATLAB, *, is elementwise not matrix multiplication. Instead we use dot function to compute inner products of vectors, to multiply a vector by a matrix and to multplity matrices.
import numpy as np
A = np.array([[1,2],[3,4]])
B = np.array([[5,6],[7,8]])
x = np.array([9,10])
y = np.array([11, 12])
#iner product of vectors; both produce 219
print(x.dot(y))
print(np.dot(x,y))
#matrix/vector pfoduct produces rank 1 array [29 67]
print(A.dot(x))
print(np.dot(A,x))
#matrix/matrix product produces rank 2 array [[19 22],[43 50]]
print(A.dot(B))
print(np.dot(A,B))
219 219 [29 67] [29 67] [[19 22] [43 50]] [[19 22] [43 50]]
Numpy provides functions perofrming computations on arrays. one othe most useful is sum
import numpy as np
x = np.array([[1,2],[3,4]])
print(np.sum(x)) #compute sum of all elements; prints "10"
print(np.sum(x,axis=0)) #compute sum of each columns; prints "[4 6]"
print(np.sum(x, axis=1)) #compute sum of each row; prints "[3 7]"
10 [4 6] [3 7]
We often need to reshape or manipluate rray data. Examples include transpotion of the atrix
import numpy as np
x = np.array([[1,2],[3,4]])
print(x) #prints "[[1 2]
# [3 4]]"
print(x.T) #prints "[[1 3]
# [2 4]]"
#note that taking transpose of rank 1 array does nothing:
v = np.array([1,2,3])
print(v)
print(v.T)
[[1 2] [3 4]] [[1 3] [2 4]] [1 2 3] [1 2 3]
Broadcasting is a mechanism that allows numpy to work with arrays of different shapes when performing arithmetic opeartions. We may have a smaller and larger array and we want to use the smaller array mulitple times to perform some operation on the larger array
import numpy as np
#we will add the vector v to each row of matrix A
A = np.array([[1,2,3],[4,5,6],[7,8,9,],[10,11,12]])
v = np.array([1,0,1])
Y = np.empty_like(A) #create an empty matrix with same shape as x
#add the vector to each row of the matrix A with explicit loop
for i in range(4):
Y[i,:] = A[i,:]+v
#now $Y$ is as follows
#[[ 2 2 4]
# [5 5 7]
# [8 8 10]
# [1 1 13]]
print(Y)
[[ 2 2 4] [ 5 5 7] [ 8 8 10] [11 11 13]]
This is slow when the matrix A is very large. Note that adding the vector v to each row of A is the same as forming a matrix VV by stacking multiple copies of v vertically, and then performing elementwise summation of A and VV
import numpy as np
#We wil add the vector v to each row of the matrix A
#storing the result in Y
x = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])
v = np.array([1,0,1])
VV = np.tile(v,(4,1)) #stack 4 copies of v on top of each other
print(VV) #prints [[1 0 1]
# [1 0 1]
# [1 0 1]
# [1 0 1]]
Y = A+VV #add A and VV elementwise
print(Y) #prints [[2 2 4]
# [5 5 7]
# [8 8 10]
# [11 11 13]]
[[1 0 1] [1 0 1] [1 0 1] [1 0 1]] [[ 2 2 4] [ 5 5 7] [ 8 8 10] [11 11 13]]
Numpy broadcasting allows us to perform this computation without creating multiple copies of v.
import numpy as np
#add the vector v to each row of matrix A
#storing the result in matrix Y
A = np.array([[1,2,3,],[4,5,6],[7,8,9],[11,11,12]])
v = np.array([1,0,1])
Y = A+v #add v to each row of A using broadcasting
print(Y)
[[ 2 2 4] [ 5 5 7] [ 8 8 10] [12 11 13]]
The line Y=A+v works even through A and v have different shapes. Note this works as v actually had the shape (4,3) where each row is a copy of v. Broadcasting two arrays together follows these rules
If the arrays do not have the same rank, prepend the shape of the lower rank array with 1s until both shapes have the same length.
The two arrays are said to be compatible in a dimension if they have the same size in the dimension, or if one of the arrays has size 1 in that dimension.
The arrays can be broadcast together if they are compatible in all dimensions.
After broadcasting, each array behaves as if it had shape equal to the elementwise maximum of shapes of the two input arrays.
In any dimension where one array had size 1 and the other array had size greater than 1, the first array behaves as if it were copied along that dimension
Here are some applications of broadcasting
import numpy as np
#compute outer product of vectors
v = np.array([1,2,3]) #v has shape (3,)
w = np.array([4,5]) #w has shape (2,)
#compute outer product, we reshape v to be a column vector of shape (3,1); we can then broadcast it against w
#to yield an output of shape (3,2), whih is the outer product
print(np.reshape(v, (3,1))*w)
#add a ector to each row of a matrix
A = np.array([[ 1,2,3], [4,5,6]])
print(A+v)
#add a vector to each column or a matrix
#A has shape (2,3) and w has shape (2,)
#if we transpose A then its shape is (3,2) and can be broadcast
#against w to yield a result of shape (3,2)
#transposing this results yields the final result with shape (2,3), which is the matrix A with
#vector w added to each column
print((A.T+w).T)
#another solution would reshape w to be a column ector of shape (2,1)
#and then broadcast it against A to produce same otuput
print(A + np.reshape(w, (2,1)))
#multiply a matrix by a constant:
#A has shape (2,3). nmpy treats scalars as arrays o shape ()
#these cna be broadcast together to shape (2,3),
print(x*2)
[[ 4 5] [ 8 10] [12 15]] [[2 4 6] [5 7 9]] [[ 5 6 7] [ 9 10 11]] [[ 5 6 7] [ 9 10 11]] [[ 2 4 6] [ 8 10 12] [14 16 18] [20 22 24]]
Matplotlib is a plotting library. most important function is plot
import numpy as np
import matplotlib.pyplot as plt
#compute the x and y coordinates for a sine function
x = np.arange(0, 3*np.pi, 0.1)
y = np.sin(x)
#plot points using matplotlib
plt.plot(x,y)
#plt.show() #must call plt.show() to make graphics appear
[<matplotlib.lines.Line2D at 0x7fd2e1891c50>]
plotting multiple lines, adding title legend and axis labels
import numpy as np
import matplotlib.pyplot as plt
#compute x and y coordinates of sine and cosime curves
x = np.arange(0,3*np.pi, 0.1)
y_sin = np.sin(x)
y_cos = np.cos(x)
#plot points
plt.plot(x, y_sin)
plt.plot(x, y_cos)
plt.xlabel('x axis label')
plt.ylabel('y axis label')
plt.title('Sine and Cosine')
plt.legend(['Sine', 'Cosine'])
#plt.show()
<matplotlib.legend.Legend at 0x7fd2f2c6d450>
you can plot different things in same figure using subplot function
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 3*np.pi, 0.1)
y_sin = np.sin(x)
y_cos = np.cos(x)
#set up subplot grid of height 2 and width 1, set first subplot as active
plt.subplot(2,1,1)
#make first plot
plt.plot(x, y_sin)
plt.title('Sine')
#set second subplot as active and make second plot
plt.subplot(2,1,2)
plt.plot(x, y_cos)
plt.title('Cosine')
Text(0.5, 1.0, 'Cosine')