dcg and ndcg implementation in python - 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.

Related

Is the a workaraound for dividing two variables in a DCP optimization problem?

I am new to the world of CVXPY and have run in to an issue with an optimization problem. In it I need to derive an expression based on the optimized variable and dividing the variable with the expression - simplified but like this:
import cvxpy as cvx
x = cvx.Variable(1,nonneg=True)
y = cvx.sqrt(x)
print("y is DCP:" + str(y.is_dcp()))
z = x/y # y is x dependent so not dcp
print("z is DCP:" + str(z.is_dcp()))
objective = cvx.Maximize(cvx.sum(z))
probl = cvx.Problem(objective, [x<=10])
probl.solve(verbose=True)
Looking at the rules for DCP optimization I realize that variable/variable division is not DCP. My question if therefore if someone has a solution or workI am noew to the world ov cvxpy and have d for this issue?
Inputing a constant in place for y in z obviously fixes the issue. However, I need to optimize on an expression based on the variable. Is there a way to do this?
I added the example above for simplicity, but my problem is more in line with the following:
import numpy as np
import cvxpy as cvx
import warnings
warnings.simplefilter(action='ignore')
ratio = np.array([-1.95844359, -7.14519994, 0.08811825, 2.92089828, 2.87685278,
-3.13022284, -1.12513724, 3.72449473, -2.68733876, 2.31347068,
4.06927235, -5.38002868, 2.18026303, -2.95228569, -7.00564848,
-3.19870931, -2.1249305 ])
category = np.array([[0,0, 1, 0],
[0,0, 0, 1],
[0,1, 0, 0],
[1,0, 0, 0],
[0,1, 0, 0],
[0,0, 1, 0],
[0,0, 1, 0],
[0,1, 0, 0],
[0,0, 1, 0],
[1,0, 0, 0],
[0,1, 0, 0],
[0,0, 1, 0],
[1,0, 0, 0],
[0,0, 1, 0],
[0,0, 0, 1],
[0,0, 1, 0],
[0,0, 1, 0]])
x = cvx.Variable(17,nonneg=True)
constraints = [cvx.sum(x) == 1]
constraints += [cvx.max(x.T*category) <= 0.34]
x2 =x.copy()
category_weight = x2.T*category # Category weights
category_weight.is_dcp()
category_weight_x = category_weight*category.T
category_weight_x.is_dcp() # Category weight for each x
category_weight_x = cvx.sum(category_weight_x,axis = 1)
# sum over rows to get (len(x),)
category_weight_x_inv = cvx.inv_pos(category_weight_x)
category_weight_x_inv.is_dcp() #1/n
# PROBLEM:
x_category_weight = x2/category_weight_x # category weight_x is not constant - not allowed!
x_category_weight.is_dcp()
#
ratio_weighted_opt = ratio*x_category_weight.T #Get Ratio value for x in category
ratio_category_opt = ratio_weighted_opt.T*category #split ratio to category columns
ratio_category_opt_cap = cvx.pos(ratio_category_opt) #set negativ to 0
ratio_category_opt_cap.is_dcp()
ratio_category_opt_cap = cvx.pos(1-ratio_category_opt) #set bigger than 1 to 1
ratio_category_opt_cap +=1
ratio_category_opt_cap.is_dcp()
ratio_category_opt_cap_category = ratio_category_opt_cap*category_weight #multiply with category weight to total
objective = cvx.Maximize(cvx.sum(ratio_weighted_opt))
probl = cvx.Problem(objective, constraints)
probl.solve(verbose=True)

Google ORTools CP-SAT | Get list of index of 1's from a list of ortools-variables

I want to convert [0, 0, 1, 0, 1, 0, 1, 0] to [2, 4, 6] using ortools.
Where "2", "4", "6" in the second list are the index of "1" in the first list.
Using the below code I could get a list [0, 0, 2, 0, 4, 0, 6, 0]. How can I get [2, 4, 6]?
from ortools.sat.python import cp_model
model = cp_model.CpModel()
solver = cp_model.CpSolver()
work = {}
days = 8
horizon = 7
for i in range(days):
work[i] = model.NewBoolVar("work(%i)" % (i))
model.Add(work[0] == 0)
model.Add(work[1] == 0)
model.Add(work[2] == 1)
model.Add(work[3] == 0)
model.Add(work[4] == 1)
model.Add(work[5] == 0)
model.Add(work[6] == 1)
model.Add(work[7] == 0)
v1 = [model.NewIntVar(0, horizon, "") for _ in range(days)]
for d in range(days):
model.Add(v1[d] == d * work[d])
status = solver.Solve(model)
print("status:", status)
vec = []
for i in range(days):
vec.append(solver.Value(work[i]))
print("work",vec)
vec = []
for v in v1:
vec.append(solver.Value(v))
print("vec1",vec)
You should see this output on the console,
status: 4
work [0, 0, 1, 0, 1, 0, 1, 0]
vec1 [0, 0, 2, 0, 4, 0, 6, 0]
Thank you.
Edit:
I also wish to get a result as [4, 6, 2].
For just 3 variables, this is easy. In pseudo code:
The max index is max(work[i] * i)
The min index is min(horizon - (horizon - i) * work[i])
The medium is sum(i * work[i]) - max_index - min_index
But that is cheating.
If you want more that 3 variable, you will need parallel arrays of Boolean variables that indicate the rank of each variable.
Let me sketch the full solution.
You need to build a graph. The X axis are the variables. The why axis are the ranks. You have horizontal arcs going right, and diagonal arcs going right and up. If the variable is selected, you need to use a diagonal arc, otherwise an horizontal arc.
If using a diagonal arc, you will assign the current variable to the rank of the tail of the arc.
Then you need to add constraints to make it a contiguous path:
mass conservation at each node
variable is selected -> one of the diagonal arc must be selected
variable is not selected -> one of the horizontal arc must be selected
bottom left node has one outgoing arc
top right node has one incoming arc

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)

Recurrence in ID3 algorithm

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.

Unable to print variables in Python when using def function

I am trying to implement a simple neural net. I want to print the initial pattern, weights, activation. I then want it to print the learning process (i.e. every pattern it goes through as it learns). I am as yet unable to do this - it returns the initial and final pattern (whn I put print p in appropriate places), but nothing else. Hints and tips appreciated - I'm a complete newbie to Python!
#!/usr/bin/python
import random
p = [ [1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[0, 0, 0, 0, 0],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1] ] # pattern I want the net to learn
n = 5
alpha = 0.01
activation = [] # unit activations
weights = [] # weights
output = [] # output
def initWeights(n): # set weights to zero, n is the number of units
global weights
weights = [[[0]*n]*n] # initialised to zero
def initNetwork(p): # initialises units to activation
global activation
activation = p
def updateNetwork(k): # pick unit at random and update k times
for l in range(k):
unit = random.randint(0,n-1)
activation[unit] = 0
for i in range(n):
activation[unit] += output[i] * weights[unit][i]
output[unit] = 1 if activation[unit] > 0 else -1
def learn(p):
for i in range(n):
for j in range(n):
weights += alpha * p[i] * p[j]
You have a problem with the line:
weights = [[[0]*n]*n]
When you use*, you multiply object references. You are using the same n-len array of zeroes every time. This will cause:
>>> weights[0][1][0] = 8
>>> weights
[[[8, 0, 0], [8, 0, 0], [8, 0, 0]]]
The first item of all the sublists is 8, because they are one and the same list. You stored the same reference multiple times, and so modifying the n-th item on any of them will alter all of them.
this the line is where you get :
"IndexError: list index out of range"
output[unit] = 1 if activation[unit] > 0 else -1
because output = [] , you should do output.append() or ...

Categories

Resources