Recurrence in ID3 algorithm - python

import numpy as np
training_set = np.array([[0, 1, 0, 1, 0, 1],[0, 0, 0, 1, 0, 0],[0, 0, 0, 0, 1, 0],[1, 0, 1, 0, 1, 0],[0, 1, 1, 1, 0, 1],[0, 1, 0, 0, 1, 1],[1, 1, 1, 0, 0, 0],[1, 1, 1, 1, 0, 1],[0, 1, 1, 0, 1, 0],[1, 1, 0, 0, 0, 1],[1, 0, 0, 0, 1, 0]])
def p(X):
Fx = X[:,X.shape[1]-1]
x0= 0
x1= 0
for i in range(len(Fx)):
if Fx[i-1] == 1:
x0 = x0+1
else:
x1 = x1+1
P0 = x0/len(Fx)
P1 = x1/len(Fx)
return(P0,P1)
def H(X):
result = -p(X)[0]*np.log(p(X)[0])-p(X)[1]*np.log(p(X)[1]) #needs to be log2
print("1 = pure, 0 = unpure 1/2 = decision can be random: Calculating Entropy: -" + str(p(X)[0]) + "*" + str(np.log(p(X)[0])) + "-" + str(p(X)[1]) + "*" + str(np.log(p(X)[1])) )
return result
def Q(X,i):
Xi = X[:,i]
result0= 0
result1= 0
for j in range(len(Xi)):
if Xi[j] == 1:
result1 = result1 + len(X[i,:])
else: result0 = result0 + len(X[i,:])
result1 = result1/len(X)
result0 = result0/len(X)
return(result0,result1)
def X_column(X,i,v):
list = X[np.where(X[:,i] == v)]
return list
def IG(X,i):
result = H(X)-Q(X,i)[0]*H(X_column(X,i,0))-Q(X,i)[1]*H(X_column(X,i,1))
return result
#To teach decision trees on learning set S, we will used following algorithm(ID3):
# 1. There is example set S
# 2. If |{f(x) : (x, f(x)) ∈ S}| = 1= 1 create leaf with label f(x)
# 3. For i = 1,2,...,n calculate value IG(S,i)
# 4. May j be an index o fthe biggest of calculated values
# 5. Set node with label Xj
# 6. For subsets:
# S0 = {(x, f(x)) ∈ S : xj = 0}
# and
# S1 = {(x, f(x)) ∈ S : xj = 1}
# run algorithm recurrent (for S ← S0 i S ← S1) and add new nodes as a childs for a node with label j
def ID3(S, recursion = 0, label = 0, tree = np.array()):
result = np.array()
recursion += 1
rows = S.shape[0]
columns = S.shape[1]
if S[:,columns-1] == True:
tree[recursion]= S[0,columns-1]
break
for i in range(rows):
result[i]= IG(S,i)
j = result.max()
tree[recursion]= 1 #czym jest etykieta xj
S0 = X_column(S,i,0)
tree[recursion+1] = ID3(S0,recursion = recursion )
S1 = X_column(S,i,1)
tree[recursion+2] = ID3(S1,recursion = recursion)
return tree
def pruning():
return tree
I have been working on implementing ID3 algorithm(decision tree), but I have no idea how to solve the recurrence. I've also translated algorithm steps from my laboratory list. Most of the necessary functions are already written, but I just can't grasp recurrency concept at that advanced level. And most of the tutorials are very trivial.

Related

dcg and ndcg implementation in python

I have implemented the Discounted Cumulative Gain (DCG) and Normalized Discounted Cumulative Gain (NDCG) in python. I am not sure if the code is correct or did I forget some important criteria for DCG and NDCG. Here is my code so far:
import numpy as np
def get_dcg_score(predictions: np.ndarray, test_interaction_matrix: np.ndarray, topK = 10) -> float:
"""
predictions - np.ndarray - predictions of the recommendation algorithm for each user.
test_interaction_matrix - np.ndarray - test interaction matrix for each user.
returns - float - mean dcg score over all user.
"""
score = None
# TODO: YOUR IMPLEMENTATION.
score = []
for idx, (pred,test) in enumerate(zip(predictions,test_interaction_matrix)):
print(idx,pred,test)
for i, (j,jj) in enumerate(zip(pred[:topK], test[:topK])):
if i == 0 and jj == 1:
sc = jj
score.append(sc)
if i != 0 and jj == 1:
sc = jj / np.log2(j+2)
score.append(sc)
if (i != 0 and jj == 0) or ( i == 0 and jj == 0):
continue
score = sum(score)/len(predictions)
return score
I evaluate this on the two arrays.
predictions = np.array([[0, 1, 2, 3], [3, 2, 1, 0]])
test_interaction_matrix = np.array([[1, 0, 0, 0], [0, 0, 0, 1]])
dcg_score = get_dcg_score(predictions, test_interaction_matrix, topK=4)
print(dcg_score)
assert np.isclose(dcg_score, 1), "1 expected"
Now for the NDCG I need to implement Ideal Discounted Cumulative Gain (IDCG) first and then divide DCG by IDCG.
Here what I have for NDCG.
def get_ndcg_score(predictions: np.ndarray, test_interaction_matrix: np.ndarray, topK = 10) -> float:
"""
predictions - np.ndarray - predictions of the recommendation algorithm for each user.
test_interaction_matrix - np.ndarray - test interaction matrix for each user.
topK - int - topK recommendations should be evaluated.
returns - average ndcg score over all users.
"""
score = None
# TODO: YOUR IMPLEMENTATION.
score_idcg = []
for i, (vp, vt) in enumerate(zip(predictions,test_interaction_matrix)):
element_sorted = sorted(vp,reverse=True)
for j, (ele_p, ele_vt) in enumerate(zip(element_sorted, vt)):
if j == 0 and ele_vt == 1:
scr = ele_vt
score_idcg.append(scr)
if j != 0 and ele_vt == 1:
scr = ele_vt / np.log2(j+2)
score_idcg.append(scr)
if (j != 0 and ele_vt == 0) or (j == 0 and ele_vt == 0):
continue
print(score_idcg)
score_idcg = sum(score_idcg)/len(predictions)
print(score_idcg)
score_dcg = get_dcg_score(predictions, test_interaction_matrix, topK = 4)
score_ndcg = score_dcg / score_idcg
return score_ndcg
Again I test it on these two arrays:
predictions = np.array([[0, 1, 2, 3], [3, 2, 1, 0], [1, 2, 3, 0], [-1, -1, -1, -1]])
test_interaction_matrix = np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0]])
ndcg_score = get_ndcg_score(predictions, test_interaction_matrix, topK=4)
assert np.isclose(ndcg_score, 1), "ndcg score is not correct."
Could somebody please look at my code and find why I don't get the right result for ndcg test? I just can't figure it out. Please also look at dcg implementation as well if it is faulty. Sorry for the horrible code. Write me if you need more info. Any suggestion is appreciated.

Edmonds–Karp time complexity

I am trying to implement a version of the Edmonds–Karp algorithm for an undirected graph. The code below works, but it is very slow when working with big matrices.
Is it possible to get the Edmonds–Karp algorithm to run faster, or should I proceed to another algorithm, like "Push Relabel"? I have though of some kind of dequeue working with the bfs, but I don't know how to do that.
The code:
def bfs(C, F, s, t):
stack = [s]
paths={s:[]}
if s == t:
return paths[s]
while(stack):
u = stack.pop()
for v in range(len(C)):
if(C[u][v]-F[u][v]>0) and v not in paths:
paths[v] = paths[u]+[(u,v)]
if v == t:
return paths[v]
stack.append(v)
return None
def maxFlow(C, s, t):
n = len(C) # C is the capacity matrix
F = [[0] * n for i in range(n)]
path = bfs(C, F, s, t)
while path != None:
flow = min(C[u][v] - F[u][v] for u,v in path)
for u,v in path:
F[u][v] += flow
F[v][u] -= flow
path = bfs(C,F,s,t)
return sum(F[s][i] for i in range(n))
C = [[ 0, 3, 3, 0, 0, 0 ], # s
[ 3, 0, 2, 3, 0, 0 ], # o
[ 0, 2, 0, 0, 2, 0 ], # p
[ 0, 0, 0, 0, 4, 2 ], # q
[ 0, 0, 0, 2, 0, 2 ], # r
[ 0, 0, 0, 0, 2, 0 ]] # t
source = 0 # A
sink = 5 # F
maxVal = maxFlow(C, source, sink)
print("max_flow_value is: ", maxVal)
I think your solution can benefit from better graph representation. In particular try to keep a list of neighbours for the BFS. I actually wrote a quite long answer on the graph representation I use for flow algorithms here https://stackoverflow.com/a/23168107/812912
If your solution is still too slow I would recommend switching to Dinic's algorithm it has served me well in many tasks.

How to find the rotation matrix of 3 orthogonal vectors in space. My current method rotates the vectors to the wrong orientation

I am looking to find the rotation matrix for getting three (almost) orthogonal vectors to be in the same orientation of the world coordinate system.
My three (almost) orthogonal vectors can be represented like this in python:
vectors = np.array([[ 0.43187079, 0.90161148, 0.02417362],
[-0.46076794, 0.19750816, 0.86526495],
[ 0.77535832, -0.38482109, 0.50073167]])
The code I currently use can get the vectors to be parallel to the world coordinates but the orientation is incorrect. Running this code,
xrotation = np.arctan2(vectors[2, 1], vectors[2, 2])
xRot = np.array([[1, 0, 0],
[0, np.cos(xrotation), -np.sin(xrotation)],
[0, np.sin(xrotation), np.cos(xrotation)]])
vectors_x = np.zeros((3, 3))
for i in range(3):
vectors_x[i, :] = np.linalg.inv(xRot.transpose()) # vectors[i, :]
yrotation = np.arctan2(vectors_x[1, 2], vectors_x[1, 0])
yRot = np.array([[np.cos(yrotation), 0, np.sin(yrotation)],
[0, 1, 0],
[-np.sin(yrotation), 0, np.cos(yrotation)]])
vectors_y = np.zeros((3, 3))
for i in range(3):
vectors_y[i, :] = np.linalg.pinv(yRot.transpose()) # vectors_x[i, :]
zrotation = np.arctan2(vectors_y[0, 0], vectors_y[0, 1])
zRot = np.array([[np.cos(zrotation), -np.sin(zrotation), 0],
[np.sin(zrotation), np.cos(zrotation), 0],
[0, 0, 1]])
vectors_z = np.zeros((3, 3))
for i in range(3):
vectors_z[i, :] = np.linalg.pinv(zRot.transpose()) # vectors_y[i, :]
Gives the three rotated orthogonal vectors:
>vectors_z
>array([[-1.11022302e-16, 1.00000000e+00, 3.19660393e-09],
[ 1.00000000e+00, -3.70417658e-09, -2.77555756e-16],
[ 2.12261116e-09, -1.98949113e-09, -1.00000000e+00]])
What do I need to change in the code to get it in the correct orientation which would look like:
array([[ 1, 0, 0],
[ 0, 1, 0],
[ 0, 0, 1]])
I know it's possible to get this by rotating the vectors 90/180 deg in the correct order but there has gotta be a more efficient way to do this by doing something else in the code above.
Thanks for your time!!!
Figured it out. Switched to a ZYZ rotation pattern and redid the euler angle calculation method. Hope this helps someone some day.
import numpy as np
def z_rotation(zrotation):
z1Rot = np.array([[np.cos(zrotation), -np.sin(zrotation), 0],
[np.sin(zrotation), np.cos(zrotation), 0],
[0, 0, 1]])
return z1Rot
def y_rotation(yrotation):
yRot = np.array([[np.cos(yrotation), 0, np.sin(yrotation)],
[0, 1, 0],
[-np.sin(yrotation), 0, np.cos(yrotation)]])
return yRot
def forward_rotation(Rot,vectors_in):
vectors = np.zeros((3, 3))
for i in range(3):
vectors[i, :] = vectors_in[i, :] # Rot
return vectors
def reverse_rotation(Rot, vectors_in):
vectors = np.zeros((3, 3))
for i in range(3):
vectors[i, :] = np.linalg.pinv(Rot.transpose()) # vectors_in[i, :]
return vectors
org_vectors = np.array([[1,0,0],[0,1,0],[0,0,1]])
z1_angle = (-.5 + np.random.random()) * 1800
y_angle = (-.5 + np.random.random()) * 1800
z2_angle = (-.5 + np.random.random()) * 1800
z1 = z1_angle*np.pi/180
y = y_angle*np.pi/180
z2 = z2_angle*np.pi/180
z1Rot = z_rotation(z1)
z1vectors = forward_rotation(z1Rot, org_vectors)
yRot = y_rotation(y)
yvectors = forward_rotation(yRot, z1vectors)
z2Rot = z_rotation(z2)
z2vectors = forward_rotation(z2Rot, yvectors)
z2angle_calc = np.arctan2(z2vectors[2,1],z2vectors[2,0])
z2rot_2 = z_rotation(z2angle_calc)
new_y = forward_rotation(z2rot_2, z2vectors)
yangle_2 = np.arctan2(new_y[2,0],new_y[2,2])
yrot_2 = y_rotation(yangle_2)
new_z1 = forward_rotation(yrot_2, new_y)
z1angle_2 = yangle_2 = np.arctan2(new_z1[0,1],new_z1[0, 0])
z1rot_2 = z_rotation(z1angle_2)
new_org_vectors = forward_rotation(z1rot_2, new_z1)
print(new_org_vectors)

Backtracking pathinding problem in Python

Recently, I've found out about backtracking and without much thinking started on the book from the guy who has shown some Sudoku backtracking tricks (https://www.youtube.com/watch?v=G_UYXzGuqvM&ab_channel=Computerphile. Unfortunately, I'm stuck with the first backtracking problem without the solution.
The problem is formulated accordingly:
Use backtracking to calculate the number of all paths from the bottom left to the top right corner in a
x * y-grid. This includes paths like https://imgur.com/3t3Np4M. Note that every point can only be visited once. Write a function np(x,y) that returns the number of paths in a x*y-grid. E.g. np(2,3) should return 38. Hint: Create a grid of booleans where you mark the positions already visited.
Whatever I change in this short block of code I'm landing nowhere near 38.
```
grid = [[0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0]]
solution = 0
def number_of_paths(x, y):
global solution
global grid
for i in range(0, x):
for j in range(0, y):
if grid[i][j] == 0:
grid[i][j] = 1
number_of_paths(x, y)
grid[i][j] = 0
solution += 1
return
if __name__ == '__main__':
number_of_paths(2, 3)
print(grid)
print(solution)```
That's a sample solution with solution with Sudoku solver.
```
grid = [[5, 3, 0, 0, 7, 0, 0, 0, 0],
[6, 0, 0, 1, 9, 5, 0, 0, 0],
[0, 9, 8, 0, 0, 0, 0, 6, 0],
[8, 0, 0, 0, 6, 0, 0, 0, 3],
[4, 0, 0, 8, 0, 3, 0, 0, 1],
[7, 0, 0, 0, 2, 0, 0, 0, 6],
[0, 6, 0, 0, 0, 0, 2, 8, 0],
[0, 0, 0, 4, 1, 9, 0, 0, 5],
[0, 0, 0, 0, 8, 0, 0, 7, 9]]
import numpy as np
def possible(y, x, n):
global grid
for i in range(0, 9):
if grid[y][i] == n:
return False
for i in range(0, 9):
if grid[i][x] == n:
return False
x0 = (x // 3) * 3
y0 = (y // 3) * 3
for i in range(0, 3):
for j in range(0, 3):
if grid[y0 + i][x0 + j] == n:
return False
return True
def solve():
global grid
for y in range(9):
for x in range(9):
if grid[y][x] == 0:
for n in range(1, 10):
if possible(y, x, n):
grid[y][x] = n
solve()
# backtracking - bad choice
grid[y][x] = 0
return
print(np,matrix(grid))
input("More?")```
A few suggestions:
You might want to use a set for a grid, adding a square as soon as it is visited, if it is not a member of the set yet.
The counter and the grid can be global but it would probably be easier for you to take them as arguments for the function at first. After the solution is clearer you can worry about those details.
You are going about the problem the wrong way. It would be good to have one function calculating the number of paths from the origin to the destination (by calling the function for the neighbors that have not been visited yet. Make sure you update the grid). On top of that you can have a function that calls the path function for every combination of origin and destination. A small tip: You do not have to calculate the same path in reverse direction! You can have a map of calculate sums of paths. If the opposite direction has been calculate, don't bother. Later, double the amount of paths by 2.
Good luck!
I will show you a solution on a coordinate system where (0,0) is the topleft and (maxY,maxX) is the bot right. Going right increases x and going down increases y.
1- If you are trying to solve the exact maze in the image, then your grid array shape is wrong. Notice that you are travelling between corners of the squares, there are 4 points you can be horizontally and 3 points you can be vertically.
2- Hint is telling you about using a boolean mask for visited state, you already have a grid array so a separate array is not necessary.
3- The main problem with your code is how you are progressing in the maze. The loop structure
for i in range(0, x):
for j in range(0, y):
does not make sense because when you are in a position (x, y), you can only move in 4 main directions (right, up, left, down). However this loops make it look like you are trying to branch into all positions behind you, which is not valid. In my code I will explicity show about this traverse stuff.
grid = [[0, 0, 0, 0],
[0, 0, 0, 0],
[1, 0, 0, 0]]
# number of solutions
solution = 0
# maximum values of x and y coordinates
maxX = len(grid[0])-1
maxY = len(grid)-1
# endpoint coordinates, top(y=0) right(x=maxX) of the maze
endX = maxX
endY = 0
# starting point coordinates, bottom(y=maxY) left(x=0) of the maze
mazeStartX = 0
mazeStartY = maxY
def number_of_paths(startX, startY):
global solution
global grid
global mask
# if we reached the goal, return at this point
if (startX == endX and startY == endY):
solution += 1
return
# possible directions are
#RIGHT (+1x, 0y)
#UP (0x, -1y)
#LEFT (-1x, 0y)
#DOWN (0x, +1y)
# I use a direction array like this to avoid nested ifs inside the for loop
dx = [1, 0, -1, 0]
dy = [0, -1, 0, 1]
for d in range(len(dx)):
newX = startX + dx[d]
newY = startY + dy[d]
# out of maze bounds
if (newX < 0 or newY < 0):
continue
# out of maze bounds
if (newX > maxX or newY > maxY):
continue
if (grid[newY][newX] == 1):
# this are is already visited
continue
else:
# branch from this point
grid[newY][newX] = 1
number_of_paths(newX, newY)
grid[newY][newX] = 0
if __name__ == '__main__':
number_of_paths(mazeStartX, mazeStartY)
print(grid)
print(solution)

Finding area of subspace of 2d array?

I am trying to make a function that given a 2d array of either 0's or 1's and a set of coordinates, returns the area of the selected region with the same value as the given coordinates.
For example given the array:
[[0, 1, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 1, 0, 0, 0]]
and the coordinates [0,0], it would return 5.
I have tried a simple DFS but am running into issues where it runs over the same spots multiple times, returning an abnormally large area like 330.
I actually figured this one out!
Basically it follows a BFS search looking only up, down, left, and right.
The part on finding the neighbours was taken from this answer to another question.
def find_area(arr, x, y):
queue = [(x,y)]
visited = []
w = len(arr[0])
h = len(arr)
area = 0
while len(queue) != 0:
curr = queue.pop(0)
print(curr)
x = curr[0]
y = curr[1]
if arr[curr[0]][curr[1]] == 0 and curr not in visited:
area += 1
visited.append(curr)
neighbors = [(x+a[0], y+a[1])
for a in [(-1,0), (1,0), (0,-1), (0,1)]
if ((0 <= x+a[0] < w) and (0 <= y+a[1] < h))]
for i in neighbors:
if i not in visited:
queue.append(i)
return area

Categories

Resources