# Basics

In this section, we will present classical computer science algorithms such as stacks, sorting etc.; constraint satisfaction algorithms such as knapsack, travelling travelling salesman etc. and market related algorithms such as next-purchase, churn predection etc. However, before all of this we want to show some basics in this story. It will be a live story, so you can always come back and check for the updates. Please not that all scripts will be in Python.

## Python Basic Types

*Some familarity with numbers*

Let’s represent some basic numerical operations in a table

You can make simple operations like below:

print(8**3)

print(2**0.5)

print(8/3)

print(8%3)

print(8//3)Output:512

1.4142135623730951

2.6666666666666665

2

2

In Python we can represent complex numbers. As you may know, complex numbers come with real and imaginary component. You can represent the imaginary component via **j **as below

`a = (3 + 2j)`

b = (5–3j)

You can handle all the numerical operations with imaginary numbers in Python.

print(a+b)

print(a*b)

print(a/b)Output:(8-1j)

(21+1j)

(0.2647058823529412+0.5588235294117647j)

*Assignments*

We can assign values to any variable easily like (a = x). But also keep in mind that we can assign them in order too. In the below example (c = 6) and

(d =7)

`c, d = 6, 7`

c += 2

print(c)

c /= -2

print(c)

d = d*3

print(d)

In general, c+= 2 would means nothing. However in Python below values are equal

c = c + 2c += 2

Specially in our algorithms section, we will use NumPy module quite a lot. Firstly let’s explain the word “module”. A file containing a set of functions you want to include in your application. Numpy is a very useful module in Python. Read the definition from official site https://numpy.org/

NumPy is the fundamental package for scientific computing with Python. It contains among other things:

a powerful N-dimensional array object

sophisticated (broadcasting) functions

tools for integrating C/C++ and Fortran code

useful linear algebra, Fourier transform, and random number capabilities

Besides its obvious scientific uses, NumPy can also be used as an efficient multi-dimensional container of generic data. Arbitrary data-types can be defined. This allows NumPy to seamlessly and speedily integrate with a wide variety of databases.

In Python, we import modules with **import **statement on the top of the code.

There are so many great operations options in NumPy here is the list of some

`sqrt(x) square root of x`

exp(x) exponential of x, i.e., ex

log(x) natural log of x, i.e., lnx

log10(x) base 10 log of x

degrees(x) converts x from radians to degrees

radians(x) converts x from degrees to radians

sin(x) sine of x (x in radians)

cos(x) cosine x (x in radians)

tan(x) tangent x (x in radians)

arcsin(x) Arc sine (in radians) of x

arccos(x) arc cosine (in radians) of x

arctan(x) arc tangent (in radians) of x

fabs(x) absolute value of x

math.factorial(n) n! of an integer

round(x) rounds a float to nearest integer

floor(x) rounds a float down to nearest integer

ceil(x) rounds a float up to nearest integer

sign(x) -1 if x < 0, +1 if x > 0, 0 if x = 0

pi PI number

Here is a quick example:

import numpy as npprint(np.sign(5))

print(np.round(9.876665))

# to nearest integer

print(np.floor(9.86868263))

# down to nearest integer

print(np.ceil(9.38792739))

# up to nearest integer

print(np.math.factorial(5))

# factorial

print(np.degrees(4))

# adians to degrees

print(np.radians(90))

#degrees to radians

print(np.sqrt(125))

#square rootOutput:1

10.0

9.0

10.0

120

229.1831180523293

1.5707963267948966

11.180339887498949

Suppose you want to calculate distance between two Cartesian coordinates.

We can easily execute it by NumPy **sqrt **function

import numpy as np x1, y1, z1 = 17.2, -6.9, 4.5x2, y2, z2 = -5.6, 12.3, 8.1r = np.sqrt((x2 — x1)**2 + (y2-y1)**2 + (z2-z1)**2)

print(r)Output:30.02399040767233

So let’s create the general form of quadratic function. ( We can do it via python function but let’s don’t do this now)

*General Form : **ax*2+*bx*+*c*,*where a*≠0

We can execute this operation in many ways thanks to NumPy. But the easiest way is using **roots **function.

import numpy as np

coeff = [1,2,1]

t = np.roots(coeff)

print(t)Output:[-1. -1.]

**Python Basic Data Structures**

*Strings*

Strings are lists of characters. Any character that you can type from

a computer keyboard, plus a variety of other characters, can be elements

in a string. Strings are created by enclosing a sequence of characters

within a pair of single or double quotes. We can make ordinary operations with strings like we did it in the numbers.

a = " stave "

b = " partners"

c = "For new generation products"

print(a + b)

print(a, end='')

print(b, end= '! ')

print(c, end= ' ')Output:stave partners

stave partners! For new generation products

You may notice that print took an end parameter. It’s important to know that, we will use this parameter quite a lot in our algorithms. GeeksForGeeks explained it beautifully:

Python’s print() function comes with a parameter called ‘end’. By default, the value of this parameter is ‘\n’, i.e. the new line character. You can end a print statement with any character/string using this parameter.

We can change types in Python. Let’s try this:

a = 97

b = “97”a = str(97)

b = int(“97”)print(a)

print(b)

print(a+b)Output:97

97

TypeError: must be str, not int

So as you can see, it return actually different results. Because numbers are also alpha numeric characters,strings can be made up of numbers. We can fix this error in this script:

c = “88”

d = 88print(c + str(d))

print(int(c) + d)Output:8888

176

*Lists*

Python has 2 simple structure. List ans Tuples. Both are accept strings and numbers.

a = [9, 1, 1, 7, 4, 5, 8, 32]

b = [90., "stabe", 1+0j, "partners", 65]i = a[1]

j = b[2]

k = b[-2]

l = a[:3]

m = a[3:]

t = a[1:3]

y = b[:]print(i)

print(j)

print(k)

print(l)

print(m)

print(t)

print(y)Output:1

(1+0j)

partners

[9, 1, 1]

[7, 4, 5, 8, 32]

[1, 1]

[90.0, 'stabe', (1+0j), 'partners', 65]

Please read the result carefully. a[x:y] actually return as **from x untill y**.

We can create list with values in some range. Simply, we can do it by **range **function. Also please not that list function returns a list and accept iterable.

**range(start,stop,step)** → Define the start point, stop point and the step size for the range function. For default step size is 1.

print( list(range(100,110,2)) )

print( list(range(10)))

print( list(range(5,10)))Output:[100, 102, 104, 106, 108]

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

[5, 6, 7, 8, 9]

*Tuples*

Basicly tuples are immutable lists. You can’t change their value. Except that you can apply all the operations like in the lists.

a = (7,8,9,12,13) #tuple

b = [7,8,9,12,13] #listprint( a[-1] )

print( a[2:])b[1] = 199

print ( b[1])

a[1] = 99

print( a[1])Output:13

(9, 12, 13)

199

TypeError: 'tuple' object does not support item assignment

We can also create multidimensional lists and tuples and like always, access them with their index number.

#2x2 List

liss = [[2,3], [3,4], [5,6], [7,8]]

print(liss)

print(liss[0])

print(liss[0][1])Output:[[2, 3], [3, 4], [5, 6], [7, 8]]

[2, 3]

3

*NumPy Arrays*

NumPy Arrays are very useful specially for scientific computation. Basicly they are simple lists. They can accept any kind of object include booleans.

There are several ways for creating NumPy array. First one is **array **function. Simply, it changes the list to a NumPy array.

import numpy as npa = [1,2,3,4,7,9,12,12,18]b = np.array(a)

print(b)Output:[ 1 2 3 4 7 9 12 12 18]

Please note that, NumPy array takes many type of objects but return the array elements as most popular object. For example, if our list include mostly strings and integer; our NumPy array will be made of strings.

Second way for creating NumPy array is **linspace **and **logspace **functions. The linspace function creates an array of N evenly spaced points between a starting point and an ending point. The form of the function is **linspace(start, stop, N)**.

import numpy as npprint(np.linspace(10,15,10))Output:[10. 10.55555556 11.11111111 11.66666667 12.22222222 12.77777778

13.33333333 13.88888889 14.44444444 15. ]

NumPy also has a closely related function **logspace **that produces evenly spaced points on a logarithmically spaced scale.The arguments are the same as those for linspace except that start and stop refer to a power of 10. That is, the array starts at 10**start and ends at 10**stop.

import numpy as np

print(np.logspace(2,3,5))Output:[ 100. 177.827941 316.22776602 562.34132519 1000. ]

NumPy also has a function is very similar to range function (remember from the lists) called **arrange**. Basicly it do the same thing.

import numpy as npprint(np.arange(10,20,2))Output:[10 12 14 16 18]

Lastly we can create arrays with zeros and ones. Also we can create identity matrix with NumPy. For those we will use:

zeros(num, dtype= ‘float’)ones(num, dtype = ‘float’)eye(num, dtype = ‘float)

Basically in default type will be float if you won’t change it. What about multidimensional arrays? Here you go:

import numpy as np

arr = np.array([[1,2,3], [1,2,3]])

print(arr)Output:[[1 2 3]

[1 2 3]]---arr = np.ones((3,4), dtype = ‘float’)print(arr)Output:[[1. 1. 1. 1.]

[1. 1. 1. 1.]

[1. 1. 1. 1.]]

So let’s make an example. Suppose you have 2 array p and t for position and time. For an object each cell will return the position for the p and the exact time for the t. So the velocity of the object is:

It’s time to slice the arrays and make the calculation

import numpy as npp = np.array([0., 1.3, 5. , 10.9, 18.9, 28.7, 40.])

t = np.array([0., 0.49, 1. , 1.5 , 2.08, 2.55, 3.2])v = (p[1:] — p[:-1])/(t[1:] — t[-1])

print(v)Output:[-1.32 -1.06818182 -0.4047619 inf]

(Divide by Zero Error)

Let’s create a 2D array where all the elements are 5

import numpy as nparr = 5* np.ones((5,5))

print(arr)Output:[[5. 5. 5. 5. 5.]

[5. 5. 5. 5. 5.]

[5. 5. 5. 5. 5.]

[5. 5. 5. 5. 5.]

[5. 5. 5. 5. 5.]]

What about multipication of two matrices? NumPy has **dot **function for that.

import numpy as nparr = 5* np.ones((5,5))

arr2 = 2* np.ones((5,3))cal = np.dot(arr, arr2)

print(cal)Output:[[50. 50. 50.]

[50. 50. 50.]

[50. 50. 50.]

[50. 50. 50.]

[50. 50. 50.]]

*Dictionaries*

Dictionaries are lists with collection of objects. Suppose we want to make a dictionary of room numbers indexed by the name of the person who occupies each room. We create our dictionary using curly brackets {…}.

` room = {“Emma”:309, “Jake”:582, “Olivia”:764}`

The dictionary above has three entries separated by commas, each entry

consisting of a key, which in this case is a string, and a value, which in this case is a room number. Each key and its value are separated by a colon. The syntax for accessing the various entries is similar to a that of a list, with the key replacing the index number. For example, to find out the room number of Olivia, we type

room[“Olivia”]Output:764

Creating dictionary is very straightforward and simple.

d= {}d[“firstName”] = “Stave”d[“lastName”] = “Partners”print(d)Output:{‘firstName’: ‘Stave’, ‘lastName’: ‘Partners’}

*Objects*

Basically, object is a collection of data with associated variables. Suppose we have a 4 kinds of objects. Apple, Orange, Barrel and Basket. In Python, we can say that we have 4 classes. But what is the difference?** Classes describe objects. **You may have 3 apples. Each one is an object but they could have different weights or colors. So, apples are associated with Apple class. This Apple Class describe the Apple object like it’s have weight and color attributes. In **Python 3 Object Oriented Book **(Dusty Phillips) there is a great example for this.

As you can see we are stroing oranges in baskets; apples in a barrel. In class level every object may have some behavior. Like we can pick some orange and put it to a basket. For this operation we should update the basket’s size or weight, pop the selected orange from the orange list. For this we can create a **pick **method. Like in split method in strings, our method should accept some parameter. In this case, our pick method can accept Basket paramet then it can update it.

Adding models and methods to individual objects allows us to create a system of interacting objects. Each object in the system is a member of a certain class. These classes specify what types of data the object can hold and what methods can be invoked on it. The data in each object can be in a different state from other objects of the same class, and each object may react to method calls differently because of the differences in state. Object-oriented analysis and design is all about figuring out what those objects are and how they should interact.

Let’s continoue with an example. Let’s crate a Apple class.

class Apple:

seasion = “winter”

vitamin = “C”new_apple = Apple()

print(new_apple.seasion)

print(new_apple.vitamin)Output:winter

C

new_apple is a a object associated with an Apple class. Notice that, there is no pharantehesis in **new_apple.seasion **because it’s looking to class and return the value. **Class variables** are defined within the class construction. Because they are owned by the class itself, class variables are shared by all instances of the class. They therefore will generally have the same value for every instance unless you are using the class variable to initialize a variable.Class variables allow us to define variables upon constructing the class. These variables and their associated values are then accessible to each instance of the class. But what is constructor ? The constructor method is used to initialize data. It is run as soon as an object of a class is instantiated. Also known as the `__init__`

method, it will be the first definition of a class and looks like this:

`class Apple:`

def __init__(self):

print(“hello constructor”)

Constructor method is automatically initialized. You should use this method to carry out any initializing you would like to do with your class objects. For more details check this out:

The key purpose of modeling an object in object-oriented design is to determine what the **public interface** of that object will be. Process of hiding the implementation, or functional details, of an object is suitably called information hiding. It is also sometimes referred to as encapsulation, but encapsulation is actually a more all-encompassing term. Encapsulated data is not necessarily hidden. Encapsulation is, literally, creating a capsule, so think of creating a time capsule. If you put a bunch of information into a time capsule, lock and bury it, it is both encapsulated and the information is hidden. On the other hand, if the time capsule has not been buried and is unlocked or made of clear plastic, the items inside it are still encapsulated, but there is no information hiding. We will continoue to object oriented design in **Basics of Object Oriented Programming** later. However this summary should be enough for the moment.

*Loops in Action*

So let’s explain the for loop with an example.

import numpy as np

import timearr = np.linspace(0,100,10000000)start_time = time.process_time()for i in range(len(arr)):

arr[i] = arr[i] * 100end_time = time.process_time()

print(“Total Process Time: {}”.format(end_time — start_time))Output:Total Process Time: 9.234375

time is an another library Python. It comes with great methods and you can check them out:

In this example, we create an array with 10 millions element. len(arr) is the length of our array which is 10 millon. For this array, we wanted to multiply every element with 100 and calculate the total processing time. i is the any element of our array. len() function measure this length and return the value. Then we loop it and change the value of every element. When we reached the 10th millon element our loop terminated then we calculate the end process time. I think it’s time to change my computer, it took 9.2 seconds.

Also we have **while **loop. It’s condition based and sometimes we wiil use it in our algorithms.

x, y = 0,1while x < 100:

x, y = y, x+y

print(x)

print(y)Output:144

233

We exacuted a basic operation which assign the value of y to x and assign the x+y to y. Please note that, we put the print function outside of the while loop. With that, we print the last value of x and y. Let’s put it to inside the loop.

x, y = 0,1while x < 100:

x, y = y, x+y

print("X is: {}".format(x))

print("Y is: {}".format(y)))Output:X is: 1

Y is: 1

X is: 1

Y is: 2

X is: 2

Y is: 3

X is: 3

Y is: 5

X is: 5

Y is: 8

X is: 8

Y is: 13

X is: 13

Y is: 21

X is: 21

Y is: 34

X is: 34

Y is: 55

X is: 55

Y is: 89

X is: 89

Y is: 144

X is: 144

Y is: 233

*List Comprehensions*

Now, we can explore the list comprehensions.

A = [[1, 2, 3],

[4, 5, 6],

[7, 8, 9]]diag = []for i in [0, 1, 2]:

diag.append(A[i][i])print(diag)Output:[1, 5, 9]

We added certain elements of A ( a[0][0], A[1][1] and A[2][2]) to our empty one dimensional diag list. We could do this by list comprehension.

A = [[1, 2, 3],

[4, 5, 6],

[7, 8, 9]]arr = [A[i][i] for i in [0,1,2]]print(arr)Output:[1, 5, 9]

Generally we can use it also for obtaining an element from an array.

A = [[1, 2, 3],

[4, 5, 6],

[7, 8, 9]]element = [a[1] for a in A]

print(element)Output:[2, 5, 8]

Less elegant way:

element =[A[i][1] for i in range(3)]Output:[2, 5, 8]---------A = [[1, 2, 3],

[4, 5, 6],

[7, 8, 9]]element = [a[i] for a in A if a[i]%3 == 0 ]

print(element)Output:[3, 6, 9]

Now, we are good to go.