Tutorials > Python and Statistics for Artificial Intelligence

Python and Statistics for Artificial Intelligence

Published on: 30 March 2021

AI Artificial Intelligence Python Statistics

Object Oriented Programming in Python

Object Oriented Programming, often termed OOPS, is a paradigm of creating modular code that revolves around concepts “classes” and “objects”. A few important terminologies associated with OOPS are below:
  • 1. Classes: These often show a collection of functions and attributes that are fastened to a precise name and represent an abstract container;
  • 2. Attributes: Generally, the data that is associated with each class. Examples are variables declared during the creation of the class;
  • 3. Objects: An instance generated from the class. There can be multiple objects of a class and every individual object takes on the properties of the class.

Representation of OOPS

Graphical Representation of Inheritance

Creating Classes and Objects in Python

Classes in Python are created using the keyword ‘class’. The definition of the class and all corresponding contents follow through an indented block of code. The definition of a class can contains multiple functions and variables.

# Creating a Class Person with 2 functions and an attribute
class Person:
      name = “Kevin”
      def __init__ (self):
              self.age = 34

# Creating an object of the class and printing its attributes
p1 = Person()
print (p1.name)
>>> Kevin
print (p1.age)
>>> 34    

Constructors and Inheritance

The constructor is an initialization function that is always called when a class’s instance is created. The constructor is named __init__() in Python and defines the specifics of instantiating a class and its attributes.

Class inheritance is a concept of taking values of a class from its origin and giving the same properties to a child class. It creates relationship models like “Class A is a Class B”, like a triangle (child class) is a shape (parent class). All the functions and attributes of a superclass are inherited by the subclass.
 
  • 1. Overriding: During the inheritance, the behavior of the child class or the subclass can be modified. Doing this modification on functions is class “overriding” and is achieved by declaring functions in the subclass with the same name. Functions created in the subclass will take precedence over those in the parent class;
  • 2. Composition: Classes can also be built from other smaller classes that support relationship models like “Class A has a Class B”, like a Department has Students;
  • 3. Polymorphism: The functionality of similar looking functions can be changed in run-time, during their implementation. This is achieved using Polymorphism, that includes two objects of different parent class but having the same set of functions. The outward look of these functions is the same, but implementations differ.

# Creating a class and instantiating variables


 Creating a class and instantiating variables
class Animal_Dog:
    species = "Canis"
    def __init__(self, name, age):
        self.name = name
        self.age = age
    # Instance method
    def description(self):
        return f"{self.name} is {self.age} years old"
    # Another instance method
    def animal_sound(self, sound):
        return f"{self.name} says {sound}"

# Check the object’s type
Animal_Dog()
>>> <__main__.Animal_Dog object at 0x106702d30>

# Even though a and b are both instances of the Dog class, they represent two distinct objects in memory.
 a = Animal_Dog()
 b = Animal_Dog()
 a == b
>>> False

# Instantiating objects with the class’s constructor arguments
fog = Animal_Dog("Fog", 6)
bunny = Animal_Dog("Bunny", 7)
print (bunny.name)
>>> 'Bunny'
Print (bunny.age)
>>> 7
# Accessing attributes directly
Print (bunny.species)
>>> 'Canis'

# Creating a new Object to access through instance functions
fog = Dog("Fog", 6)
fog.description()
>>> 'Fog is 6 years old'
fog.speak("Whoof Whoof")
>>> 'Fog says Whoof Whoof'
fog.speak("Bhoof Whoof")
>>> 'Fog says Bhoof Whoof '

# Inheriting the Class
class GoldRet(Dog):
    def speak(self, sound="Warf"):
        return f"{self.name} says {sound}"

bunny = GoldRet("Bunny", 5)
bunny.speak()
>>> 'Bunny says Warf'
bunny.speak("Grrr Grrr")
>>> 'Bunny says Grrr Grrr'

Variables and Data Types in Python

Variables are reserved locations in the computer’s memory that store values defined within them. Whenever a variable is created, a piece of the computer’s memory is allocated to it. Based on the data type of this declared variable, the interpreter allocates varied chunks of memory. Therefore, on the basis of the assignment of variables as integer, float, strings, etc. different sizes of memory allocations are invoked.

Declaration: Variables in Python do not need explicit declaration to reserve memory space. This happens automatically when a value is assigned. The (=) sign is used to assign values to variables.
Multiple Assignment: Python allows for multiple variables to hold a single value and this declaration can be done together for all variables.
Deleting References: Memory reference once created can also be deleted. The ’del’ statement is used to delete the reference to a number object. Multiple object deletion is also supported by the ’del’ statement.
Strings: Strings are a set of characters, of which Python allows representation through single or double quotes. String subsets can be formed using the slice operator ([ ] and [:] ) where indexing starts from 0 on the left and -1 on the right. The (+) sign is the string concatenation operator and the (*) sign is the repetition operator.
 

Datatype Conversion

Function/th> Description
int(x [,base])
Converts given input to integer. Base is used for string conversions;
long(x [,base] )
Converts given input to a long integer;
float(x)
Follows conversion to floating-point number;
complex(real [,imag])
Used for creating a complex number;
str(x)
Converts any given object to a string;
eval(str)
Evaluates given string and returns an object;
tuple(s)
Conversion to tuple;
list(s)
List conversion of given input;
set(s)
Converts the given value to s;
unichr(x)
Conversion from an integer to Unicode character.

Looking at Variables and Datatypes

Data stored as Python’s variables is abstracted as objects. Data is represented by objects or through relations between individual objects. Therefore, every variable and its corresponding values are an object of a class, depending on the stored data.

Int_var = 100                  # Integer variable
Float_var  = 1000.0        # Float value
String_var  = "John"       # String variable

print (int_var)
>>> 100
print (float_var)
>>> 1000.0
Print (string_var)
>>> John

# Multiple Assignment: All are assigned to the same memory location
a = b = c = 1
# Assigning multiple variables with multiple values
a,b,c = 1,2,"jacob"

# Assigning and deleting variable references
var1 = 1
var2 = 10
>>> del var1                # Removes the reference of var1
>>> del var1, var2
# General Deleting Syntax for more than one variable together
del var1[,var2[,var3[....,varN]]]]

# Basic String Operations in Python
str = 'Hello World!'
print (str) 
>>> Hello World!
# Print the first character of string variable
print (str[0])
>>>  H
# Prints characters from 3rd to 5th positions
print (str[2:5])     
>>> llo
# Print the string twice
print (str * 2)
>>> Hello World!Hello World!
# Concatenate the string and print
print (str + "TEST")
>>> Hello World!TEST

Python Lists and Dictionary Operations

Lists: List is arguably the most versatile and highly used data type in Python, especially when programming for large data. It contains items parted by commas and confined inside square brackets. An interesting factor about lists is that the datatype of elements within a list can be different. The slice operations used to part strings are also applicable to lists. Indexing and concatenation also work like strings.
Tuple: A tuple is like a list and consists of several values separated by commas. The representation of tuples is in parenthesis and not square brackets. Tuples are read-only lists that cannot be modified or changed, unlike lists.
Dictionary: A Python dictionary is a key-value pair that works like a hash table. A key can be any Python type. Values, on the other hand, can be any arbitrary Python object. Representation of dictionaries is done by curly braces and value assignment and access works through square brackets.

# Creating a Python List
list = [ 'abcde', 1234 , 1.032, 'jack', 16.75 ]
tinylist = [12, 'jo']

# Printing the complete list
print (list)
>>> [ 'abcde', 1234 , 1.032, 'jack', 16.75 ]
# Print the first element of the list
print (list[0])
>>> abcde
# Print all elements starting from position 3
print (list[2:])
>>> 1.032, 'jack', 16.75       
# Prints list two times
print (tinylist * 2)
>>> [12, 'jo', 12, 'jo']
# Concatenate the lists
print (list + tinylist) 
>>> ['abcde', 1234, 1.023, 'jack', 16.75, 12, 'jo']

# Creating a Tuple in Python
tuple = ('abcde', 1234 , 1.032, 'jack', 16.75)
tinytuple = (12, 'jo')
# Print the Tuple
print (tuple)
>>> ('abcde', 1234 , 1.032, 'jack', 16.75)
# Print parts of the tuple
print (tuple[1:3])       
>>> (1234 , 1.032)   
# Multiplication of tuples
print (tinytuple * 2)       
>>> (12, ‘jo’, 12, ‘jo’)

# Dictionary Implementation in Python
dict = {}
dict['one'] = "This is dictionary one sample"                   
dict[2]     = "This is sample output two"                     
tinydict = {'name': 'jack','code':1234, 'department': 'finance'}

print (dict['one'])       # Prints value for the key 'one' 
>>> This is dictionary one sample
print (dict[2])             # Prints the value stored in key ‘2’
>>> This is sample output two
print (tinydict.keys())        # Prints all keys from a dictionary
>>> ['department', 'code', 'name']
print (tinydict.values())       # Prints all values from a dictionary
['finance', 1234, 'jack']

Functions and Looping Constructs in Python

Python includes several built-in functions and packages that help in performing tasks. Yet, if as per requirement, a function is not present in any of the existing libraries, user-defined functions can be created. A function is any re-usable block of code and is recognized by the ‘def’ keyword. Parameterized Functions are those where functions also take variables as parameters, these can be used in the function’s definition. User-defined functions allow for single, multiple or an unknown number of parameters to exist with the function.
Argument **kwarg: Functions can be defined with a single parameter prefixed with **. This parameter initializes to a new value during function calls. Default Parameter: During function declaration, it can be provided with a default value for any variable used within the function. This value is substituted when an appropriate argument is passed during calling the function.
Lambda Functions: The ‘lambda’ keyword is used to create anonymous functions in Python. It can have multiple arguments in the definition and during execution, the expression after ‘:’ is implemented.

Looping Constructs in Python

Both For and While loops are designed to repeat execution of a code block multiple times. The primary difference between the two is that, in a For loop, the number of executions (n) is known. The While loop on the other hand, continues until a certain condition is not met. We will look at both in detail, in the implementation below.

# Creating a parameterized function
def greetings(name):  
    print ('Howdy ', name)
# calling function with an  argument value
>>> greetings('Smith')
>>> Howdy Smith 

# Same function created with an unknown number of arguments
def greetings (*names):  
    print ('Howdy ', names[0], ', ', names[1], ', ', names[3])
# calling function with 3  argument values
>>> greet('Emily', 'Smith', 'Rowe')
>>> Howdy Emily, Smith and Row

# Implementation of the **kwargs argument
def greetings(**personDet):
	print('Hello ', personDet['firstname'],  personDet['lastname'])

>>> greet(firstname='Smith', lastname='Emily')
Hello Smith Emily
>>> greet(firstname='Bill', lastname='Keeper', age=75) 
Hello Bill Keeper

# Implementation of a lambda function
(lambda x: x*x)(5)
>>> 25

# Implementation of a While loop program
number=0
counter=0
sum=0
while number >= 0:
    number = int(input('enter a digit '))
    if number >= 0:
        counter = counter + 1 
        sum = sum + number
avg = sum/counter
print('Total numbers are: ', counter, ', Average is: ', avg)
>>> enter a digit: 10
>>> enter a digit: 20
>>> enter a digit: 30
>>> enter a digit: –1
>>> Total numbers are: 3, Average is: 20.0

# Implementation of a For loop
for i in range(1, 5):
    if i > 3
        # the continue keyword is used to skip the current iteration and move to the next
        continue
    print(i)
>>> 1
>>> 2
>>> 3

# While loop with Else condition
number = 0
while number < 3:
	number += 1   
	print('number = ', number)
else:
    print('else block is getting executed')
>>> number = 1
>>> number = 2
>>> number = 3
>>> else block is getting executed

File Processing and Exception Handling

File processing has been one of the most important aspects of a programming language in the recent years. With data becoming accessible through multiple hardware devices and being captured in Terabytes, it is not possible to perform any analysis on data that is not stored externally. File Handling comes handy in these situations where portions of a file can be accessed programmatically and made available to users for further analysis and exploration. Python enables physical files that are mapped to a built-in file object with the help of function open().
file object = open(file name[, access mode][, buffersize])
In this method, the first parameter specifies the name of the file including its local or universal path. The access mode parameter is optional and is meant for defining purpose of opening a file, for instance, read, write, append, etc. ’w’ is used to write data in a file and ’r’ to read data from the file. There is also an optional buffersize parameter that specifies values like 0 for unbuffered, 1 for line buffered and other positive entries give the buffer size.

Some important operations:
 
  • 1. File.close() – The method is used to save the file in its current state and close it;
  • 2. File.flush() – Flush the file’s internal buffer;
  • 3. Next(file) – This method iterates through the file and sequentially gives the next line;
  • 4. File.readlines() – Reads the entire file. (readline reads only until a new-line character);
  • 5. File.read([size]) – Reads only the specified byte size from the open file;
  • 6. File.tell() – Returns the current line position in the file;
  • 7. File.seek() – To read or write to the file at a specific location in the file;

# Opening a file from the local directory and writing to it
f=open("C:\mytestfile.txt","w")
f.write("Hello! This is a sample text.")
f.close()

# Opening a file and writing a defined string to it. New line character needs to be written as well.
lines=["Hello world.\n", "This is a string sample.\n"]
f=open("D:\mytestfile.txt","w")
f.writelines(lines)
f.close()

# Reading from a file upto the next line character
f=open("mytestfile.txt","r")
line=f.readline()
print(line)
f.close()

# Reading the complete file using a While loop
f=open("C:\mytestfile.txt","r")
line=f.readline()
while line!='':
    print(line)
    line=f.readline()

# Using in–built iterator to read the complete file (reads until StopIteration is found)
f=open("C:\mytestfile.txt","r")
while True:
        try:
            line=next(f)
            print (line)
        except StopIteration:
            break
f.close()

# Using Seek function and r+ to read at the given position
f=open("D:\mytestfile.txt","r+")
f.seek(6,0)
lines=f.readlines()
for line in lines:
    print(line)
f.close()

Exception Handling

Exceptions can sometimes occur outside the scope of the individual program alone, for example, the user giving an incorrect input, a malfunctioning Input device, etc. can usually cause programs to not work at their expected behavior. Exception handling is a mechanism that prevents programs to abruptly stop running and show useful messages to users when an error is encountered. Python uses keywords try, except, else and finally to implement error handling.
  • 1. Try: This block is meant for execution of the blocks of code that are expected to throw errors during execution;
  • 2. Except: If all statements of the try block are correctly executed, the except block does not run. If an error occurs in the try block, then the code moves to statements of the except block. This also catches several system-defined errors and shows them to the user;
  • 3. Else: If an else block is specified and no errors occur in the statements of the try block, the code flow comes to the else block;
  • 4. Finally: The code in the finally block is always executed, irrespective of whether or not an exception is caught.

# Writing code for the try block
try:
    print("this is the try block")
    x=int(input('Enter a numeric value: '))
    y=int(input('Enter another integer: '))
    z=x/y
except ZeroDivisionError:
    print("this is the ZeroDivisionError exception block")
    print("Division by 0 is not recognized")
else:
    print("inside the else block")
    print("Division result = ", z)
finally:
    print("inside the finally block")
    x=0
    y=0
print ("printing outside of the try, except, else and finally blocks." )

# Output Sequences
>>> this is the try block
>>> Enter a numeric value: 20
>>> Enter another integer: 5
>>> inside the else block
>>> Division result =  4.0
>>> inside the finally block
>>> printing outside of the try, except, else and finally blocks.

# Output sequence when error is caught
>>> this is the try block
>>> Enter a numeric value: 30
>>> Enter another integer: 0
>>> this is the ZeroDivisionError exception block
>>> Division by 0 is not recognized
>>> inside the finally block
>>> printing outside of the try, except, else and finally blocks.

Analysis of The Python Statistics Module

The statistics in-built python module provides functions for mathematical operations. We will be discussing the following functions for calculating central tendency and spread measure, as part of this section:
 
Function Description
mean()
Calculates the mean of the given set of data;
fmean()
Faster execution, floating point arithmetic mean;
geometric_mean())
Calculate the geometric mean of given data;
harmonic_mean()
Calculate the harmonic mean of given data;
median()
Calculate the median of given data points;
mode()/code>
Calculating the mode;
multimode()
List of the most common values of discrete/nominal data;
quantiles()
Divide the given data, based on probability, into equal intervals;
stdev(), pstdev()
Standard deviation of sample or population data;
pvariance(), variance()
Variance of sample or population data.

# Importing the statistics module
import statistics
numbers_list = [1,4,5,6,7,32,22,15,4,66]
print(statistics.mean(numbers_list))
>>> 16.2
print(statistics.median(numbers_list))
>>> 6.5
print(statistics.mode(numbers_list))
>>> 4
print(statistics.stdev(numbers_list))
>>> 20.021099980881278
print(statistics.variance(numbers_list))
>>> 400.8444444444444
print(statistics.fmean(numbers_list))
>>> 16.2
# Executing remainder of the functions on a mixed list
numbers_list = [6.75, 33, 2.22, 54, 12.23, 33, 45]
print(statistics.geometric_mean(numbers_list))
>>> 17.41072136913146
print(statistics.harmonic_mean(numbers_list))
>>> 8.954709714991052
print(statistics.multimode(numbers_list))
>>> [33]
print(statistics.quantiles(numbers_list))
>>> [6.75, 33.0, 45.0]

Connecting the Knowledge to Artificial Intelligence

Artificial Intelligence and Machine Learning are a combination of Mathematics and Programming. Throughout the last two chapters we have concentrated on the basics of both these concepts. All artificial intelligence problems can be broadly divided into two categories, Search and Representation problems. Following these, come models like Rules, frames, logics, and Nets. Largely, these algorithms revolve around calculating or minimizing distances, computing similarities, finding solutions to equations, all of which are very mathematical concepts. To implement these concepts in terms that the computer processor can understand, we use code. Python, in this case, is the bridge connecting mathematics to intelligent applications.

Connecting Mathematics and Programming to form Intelligent Solutions

Unit Testing Your Python Code

The mechanism of creating Unit Tests for programs is created to help a developer encounter issues that potential users might face in the future. Testing singular features at a time is known as Unit Testing and is generally run on every functionality of the program. Tests can be run using modules like unittest, pytest or nose2 that are part of Python libraries. Without using these libraries, the ‘assert’ statement can be used to see the outcome of a unit test. We will go through all the examples below:

# This is a test for the in–built Sum() function of Python
assert sum([2, 4, 5]) == 11, "Should be 11"
>>> This will not return any output since the assertion is correct.

# When assertion is not true, the error is printed
assert sum([2, 2, 1]) == 6, "Should be 6"
>>> Traceback (most recent call last): AssertionError: Should be 6

# To run simultaneous test cases with varied inputs, we can create a .py file out of the test case and run it 
# through the command prompt
def testing_sum():
    assert sum([2, 2, 3]) == 7, "Sum should be 7"
if __name__ == "__main__":
    testing_sum()
    print("All tests passed")

# Run on Shell Window
$ python test_sum.py
>>> All tests passed

# Using the Python unittest module to run test cases
import unittest
class Testing_Sum(unittest.TestCase):
    def testing_sum(self):
        self.assertEqual(sum([2, 2, 3]), 7, "Sum should be 7")

    def testing_sum_two(self):
        self.assertEqual(sum((2, 2, 2)), 5, "Sum should be 5")
if __name__ == '__main__':
    unittest.main()

# Running on the Shell Window
$ python test_sum_unittest.py
.F
======================================================================
FAIL: test_sum_tuple (__main__.TestingSum)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
Traceback (most recent call last):
  File "test_sum_unittest.py", line 9, in test_sum_tuple
    self.assertEqual(sum((2, 2, 2)), 5, "Sum Should be 5")
AssertionError: Sum should be 5
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
Ran 2 tests in 0.002s
FAILED (failures=1)

To run any test case on Python using the method above, we can change the name of the function from ‘sum()’ to the user-defined function that needs testing. The arguments of the function need to be provided and an expected result also needs to be there. The tests will pass when the expected results are met and fail otherwise.

Coming Up Next

  • 1. The theory of Machine Learning and Artificial Intelligence
  • 2. Data Classification in Python
  • 3. Algorithms to perform Machine Learning