2 Advanced Python Ai Full
2 Advanced Python Ai Full
import sys
print(sys.version)
List comprehension offers a shorter syntax when you want to create a new list based on
the values of an existing list.
Syntax
<new-list-name> = [ <expression> for <element> in <list-name> if <condition> ]
List comprehensions start and end with opening and closing square brackets.
Parameters:
expression: Operation we perform on each value inside the original list
element: A temporary variable we use to store for each item in the original list
list-name: The name of the list that we want to go through
condition: If condition evaluates to True, add the processed element to the new list
new-list-name: New values created which are saved
A tuple is in many ways similar to a list; one of the most important differences is that
tuples can be used as keys in dictionaries and as elements of sets, while lists cannot.
Here is a trivial example:
d = {(x, x + 1): x for x in range(10)} # Create a dictionary with tuple keys
t = (5, 6) # Create a tuple
print(type(t)) # Print "<class 'tuple'>"
print(d[t]) # Print "5"
print(d[(1, 2)]) # Print "1"
The zip() method takes iterable or containers and returns a single iterator object, having
mapped values from all the containers.
If the passed iterable or containers have different lengths, the one with the least items
decides the length of the new iterator.
Syntax
zip(<iterator1>, <iterator2>, <iterator3>...)
Parameters:
iterator1, iterator2, iterator3, · · · : Iterator objects that will be joined together
# It prints
# apple is red
# peach is pink
# banana is yellow
# guava is green
# papaya is orange
A class is a user-defined data type from which objects are created. It is created by
keyword class.
Class provides a means of bundling data (i.e., instance variables) and functionality (i.e.,
methods) together.
Instance variables:
They are the variables that belong to an object.
They are always public by default and can be accessed using the dot (.) operator.
Methods:
They have an extra first parameter, self, in the method definition.
We do not give a value for this parameter when we call the method, Python provides it.
The instance variables and methods are accessed by using the object.
__init__ method is similar to constructors in Java/C++. Constructors are used to
initializing the instance variables of objects.
Syntax
Parameters:
class <class-name>:
# __init__ is a constructor class-name: The name of the
def __init__(self, <arguments0>): class.
self.<instance-variable1> = <value1> arguments0, arguments1,
self.<instance-variable2> = <value2> arguments2, ...: Method
... parameters (i.e., variables)
def <method1-name>(self, <arguments1>): instance-variable1,
<statement1> instance-variable2, ...:
<statement2> Instance variables
... value1, value2, ...: Value
def <method2-name>(self, <arguments2>): assigned to the instance
<statement1> variables
<statement2> statement1, statement2, ...:
... Python statements
Parameters:
object-name: The name of an object
class-name: The name of a class
arguments: The values that we pass to the constructor/method
method-name: The name of the method
instance-variable-name: The name of an instance variable
value: The value assigned to the instance variable
As mentioned, all members in a Python class are public by default, i.e., any member can
be accessed from outside the class. To restrict the access to the members, we can make
them protected/private.
Protected members of a class are accessible from within the class and also available to its
sub-classes (Will not be covered in this course). By convention, Python makes an/a
instance variable/method protected by adding a prefix (single underscore) to it.
Private members of a class are accessible from with the class only. By convention, Python
makes an instance variable/method private by adding a prefix (double underscore) to it.
Note
“By convention” means the responsible programmer would refrain from accessing and modifying
instance variables prefixed with or from outside its class. But if they do so, still works. :(
How to retrieve and modify instance variable values? We need to define accessors and
mutators!
[email protected], [email protected] COMP 2211 (Fall 2024) 21 / 99
Accessors and Mutators
Accessors and mutator methods are used to access the protected/private instance
variables which cannot be accessed from outside the class.
Accessor methods (or getters) are used to retrieve the values of instance variables of an
object. When the accessor method is called, it returns the private instance variable value
of the object.
Mutator methods (or setters) are used to modify the values of instance variables of an
object. When the mutator method is called, it modifies the private instance variable value
of the object.
class Person:
def __init__(self, name='Tom', def set_name(self, name): # Mutator
age=18, self.__name = name
gender='M'):
self.__name = name def set_age(self, age): # Mutator
self.__age = age self.__age = age
self.__gender = gender
def set_gender(self, gender): # Mutator
def get_name(self): # Accessor self.__gender = gender
return self.__name
def print(self):
def get_age(self): # Accessor print('--- Print Person ---')
return self.__age print('Name: ' + self.__name)
print('Age: ' + str(self.__age))
def get_gender(self): # Accessor print('Gender: ' + self.__gender)
return self.__gender
tony = Student('Lala', 18, 'M', "23456789") # Define an object of Student named tony
tony.print() # Print tony
--- Print Person ---
Name: Lala
Age: 18
Gender: M
ID: 23456789
[email protected], [email protected] COMP 2211 (Fall 2024) 29 / 99
Part II
Modules
A modules in Python is a bunch of related code (functions,
classes, variables) saved in a file with the extension .py.
Packages
A package is basically a directory of a collection of modules.
Libraries
A library is a collection of packages.
For instance, if we only want to import the sqrt function from the math module, we just
need to do:
from math import sqrt # Import only sqrt function from math module
x = 100
# Now, it's imported, we can directly use sqrt to call the math.sqrt()
print(sqrt(x)) # It prints 10
There is also the “from [module] import *” syntax which imports all functions. So
the following code works as well.
from math import * # Import ALL functions from math module
x = 100
print(sqrt(x)) # It print 10
x = 100
# The following will use cmath.sqrt(), which is a different implementation to
# math.sqrt()
print(sqrt(x)) # It print 10
Score Remark
Name
Desmond 100 Hardworking
Tom 99 Clever
Peter 30 Lazy
Haha 0 >.<
If index col = 0 (i.e., the column “Name”) will be used as the column index.
Score Remark
Name
Desmond 100 Hardworking
Tom 99 Clever
Peter 30 Lazy
Haha 0 >.<
If index col = "Name", the column “Name” will be used as the column index.
Conclusion
We can assign False, integer (refer to which column), or string (column name) to index col to indicate
which column would be used as the column index.
[email protected], [email protected] COMP 2211 (Fall 2024) 42 / 99
Exporting Data
NumPy is a short form for Numerical Python and is the core library for numeric and
scientific computing in Python.
It provides high-performance multidimensional array object, and tools for working with
these arrays.
Expression Description
a[i] Select element at index i, where i is an integer (start counting from 0).
Select the i-th element from the end of the list, where n is an integer.
a[-i] The last element in the list is addressed as -1, the second to last
element as -2, and so on.
a[i:j] Select elements with indexing starting at i and ending at j-1.
a[:] or a[0:] Select all elements in the given axis.
a[:i] Select elements starting with index 0 and going up to index i - 1.
Select elements starting with index i and going up to the last element
a[i:]
in the array.
a[i:j:n] Select elements with index i through j (exclusive), with increment n.
a[::-1] Select all the elements, in reverse order.
arr[2] (3,)
arr[2, :] (3,)
arr[2:, :] (1, 3)
# When using integer array indexing, you can reuse the same
# element from the source array:
print(a[[0, 0], [1, 1]]) # Prints "[2 2]"
print(a) # prints [[ 1 2 3]
# [ 4 5 6]
# [ 7 8 9]
# [10 11 12]]"
bool_idx = (a > 2) # Find the 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.
# 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]"
Basic mathematical functions operate element-wise on arrays, and are available both as
operator overloads and as functions in the NumPy module.
import numpy as np # Elementwise product; both produce the array
# [[ 5.0 12.0]
x = np.array([[1,2],[3,4]], dtype=np.float64) # [21.0 32.0]]
y = np.array([[5,6],[7,8]], dtype=np.float64) print(x * y)
print(np.multiply(x, y))
# Elementwise sum; both produce the array
# [[ 6.0 8.0] # Elementwise division; both produce the array
# [10.0 12.0]] # [[ 0.2 0.33333333]
print(x + y) # [ 0.42857143 0.5 ]]
print(np.add(x, y)) print(x / y)
print(np.divide(x, y))
# Elementwise difference; both produce the array
# [[-4.0 -4.0] # Elementwise square root; produces the array
# [-4.0 -4.0]] # [[ 1. 1.41421356]
print(x - y) # [ 1.73205081 2. ]]
print(np.subtract(x, y)) print(np.sqrt(x))
Dot product is the sum of products of values in two same-sized vectors (arrays).
The output of the dot product is a scalar.
Let’s see how to perform dot product.
x = np.array([[1,2],[3,4]])
y = np.array([[5,6],[7,8]])
v = np.array([9,10])
w = np.array([11, 12])
# Matrix / vector product; both produce the rank 1 array [29 67]
print(x.dot(v))
print(np.dot(x, v))
x = np.array([[1,2],[3,4]])
y = np.array([[5,6],[7,8]])
v = np.array([9,10])
w = np.array([11, 12])
# Matrix / vector product; both produce the rank 1 array [29 67]
print(np.matmul(x, v))
print(x@v)
x = np.array([[1,2],[3,4]])
x = np.array([[1,2], [3,4]])
print(x) # Print "[[1 2]
# [3 4]]"
print(x.T) # Print "[[1 3]
# [2 4]]"
print(np.transpose(x)) # Print "[[1 3]
# [2 4]]"
print(x.transpose()) # Print "[[1 3]
# [2 4]]"
[email protected], [email protected] COMP 2211 (Fall 2024) 63 / 99
Transpose
The transpose function comes with axes parameter which, according to the values
specified to the axes parameter, permutes the array.
Syntax
np.transpose(<arr>,<axis>)
Parameters:
arr: the array you want to transpose
axis: By default, the value is None. When None
or no value is passed it will reverse the dimensions
of array arr. If specified, it must be the tuple or
list, which contains the permutation of [0,1,..,
N-1] where N is the number of axes of arr.
Note
import numpy as np
v = np.array([1,2,3])
print(v) # Print "[1 2 3]"
print(v.T) # Print "[1 2 3]"
print(np.transpose(v)) # Print "[1 2 3]"
Syntax
numpy.reshape(<arr>, <newshape>)
Parameters:
arr: Array to be reshaped.
newshape: int or tuple of ints
The new shape should be compatible with the original shape. If an integer, then the result
will be a 1D array of that length. One shape dimension can be -1. In this case, the value is
inferred from the length of the array and the remaining dimensions.
Question
How to create the following array in one line of code?
arr = np.array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
# Answer: arr = np.arange(24).reshape(2,3,4)
[email protected], [email protected] COMP 2211 (Fall 2024) 68 / 99
Increasing the Dimensions of an Array
np.newaxis and np.expand_dims() allow us to increase the dimensions of an array by
adding new axes.
Note: numpy.newaxis is an alias for None.
Syntax
numpy.newaxis
Usage:
Placing numpy.newaxis inside [] adds a new dimension of size 1 at that position.
numpy.expand_dims(arr, axis)
Parameters:
arr: Input array.
axis: int or tuple of ints, which refers to the position in the expanded axes where the new
axis (or axes) is placed.
arr5 = arr4[np.newaxis]
print(arr5) # It prints [[[1 2 3]
# [4 5 6]]]
[email protected], [email protected] COMP 2211 (Fall 2024) 70 / 99
Increasing the Dimensions of an Array using np.newaxis
import numpy as np
import numpy as np
import numpy as np
import numpy as np
import numpy as np
# We will add the vector v to each row of the matrix x, 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])
y = np.empty_like(x) # Create an empty matrix with the same shape as x
# Add the vector v to each row of the matrix x with an explicit loop
for i in range(4):
y[i, :] = x[i, :] + v
The above works, however when the matrix x is very large, computing an explicit loop could be slow.
The line y = x + v works even though x has shape (4, 3) and v has shape (3,) due to broadcasting;
this line works as if v actually had shape (4, 3), where each row was a copy of v, and the sum was
performed element-wise.
[email protected], [email protected] COMP 2211 (Fall 2024) 78 / 99
Broadcasting Rules
import numpy as np
import numpy as np
# a shape-(2, 3) array
y = np.array([[ 8.65, 0.27, 4.67],
[ 7.73, 7.26, 1.95]])
Questions
How many distance values do we need to compute?
Answer: 6 distances, one for each pair of rows from x and y.
So, if x has a shape of (M, 3) and y has a shape of (N, 3), how many distance values do we need to
compute?
Answer: M × N
def actualsize(input_object):
memory_size = 0 # memory_size: the actual memory size of "input_object"
# Initialize it to 0
ids = set() # ids: An empty set to store all the ids of objects in "input_object"
objects = [input_object] # objects: A list with "input_object" (traverse from "input_object")
while objects: # While "objects" is non-empty
new = [] # new: An empty list to keep the items linked by "objects"
for obj in objects: # obj: Each object in "objects"
if id(obj) not in ids: # If the id of "obj" is not in "ids"
ids.add(id(obj)) # Add the id of the "obj" to "ids"
memory_size += sys.getsizeof(obj) # Use getsizeof to determine the size of "obj"
# and add it to "memory_size"
new.append(obj) # Add "obj" to "new"
objects = gc.get_referents(*new) # Update "objects" with the list of objects directly
# referred to by *new
return memory_size # Return "memory_size"
try:
ls = ls + 4 # Add 4 to each element of list
except(TypeError):
print("Lists don't support list + int")
# Now on array
try:
arr = arr + 4 # Add 4 to each element of NumPy array
print("Modified NumPy array: ",arr) # Print the NumPy array
except(TypeError):
print("NumPy arrays don't support list + int")
Output:
Lists don't support list + int
Modified NumPy array: [5 6 7]