def cosine_dist(u, v, axis):
    """Returns cosine of angle betwwen two vectors."""
    return 1 - (u*v).sum(axis)/(np.sqrt((u**2).sum(axis))*np.sqrt((v**2).sum(axis)))

u = np.array([1,2,3])
v = np.array([4,5,6])

"""
Explanation: Exercise 1
End of explanation
"""


u @ v

(u * v).sum()

"""
Explanation: Note 1: We write the dot product as the sum of element-wise products. This allows us to generalize when u, v are matrices rather than vectors. The norms in the denominator are calculated in the same way.
End of explanation
"""


M = np.array([[1.,2,3],[4,5,6]])
M.shape

"""
Explanation: Note 2: Broadcasting
End of explanation
"""


M[None,:,:].shape, M[:,None,:].shape

(M[None,:,:] + M[:,None,:]).shape

cosine_dist(M[None,:,:], M[:,None,:], 2)

"""
Explanation: Note 2A: Broadcasting for M as collection of row vectors. How we broadcast and which axis to broadcast over are determined by the need to end up with a 2x2 matrix.
End of explanation
"""


M[:,None,:].shape, M[:,:,None].shape

(M[:,None,:] + M[:,:,None]).shape

cosine_dist(M[:,None,:], M[:,:,None], 0)

"""
Explanation: Note 2B: Broadcasting for M as a collection of column vectors. How we broadcast and which axis to broadcast over are determined by the need to end up with a 3x3 matrix.
End of explanation
"""


M = np.array([[1, 0, 0, 1, 0, 0, 0, 0, 0],
    [1, 0, 1, 0, 0, 0, 0, 0, 0],
    [1, 1, 0, 0, 0, 0, 0, 0, 0],
    [0, 1, 1, 0, 1, 0, 0, 0, 0],
    [0, 1, 1, 2, 0, 0, 0, 0, 0],
    [0, 1, 0, 0, 1, 0, 0, 0, 0],
    [0, 1, 0, 0, 1, 0, 0, 0, 0],
    [0, 0, 1, 1, 0, 0, 0, 0, 0],
    [0, 1, 0, 0, 0, 0, 0, 0, 1],
    [0, 0, 0, 0, 0, 1, 1, 1, 0],
    [0, 0, 0, 0, 0, 0, 1, 1, 1],
    [0, 0, 0, 0, 0, 0, 0, 1, 1]])

M.shape

U, s, V = np.linalg.svd(M, full_matrices=False)

U.shape, s.shape, V.shape

s[2:] = 0
M2 = U @ np.diag(s) @ V

from scipy.stats import spearmanr

r2 = spearmanr(M2)[0]

r2

r2[np.tril_indices_from(r2[:5, :5], -1)]

r2[np.tril_indices_from(r2[5:, 5:], -1)]

"""
Explanation: Exeercise 2
Note 1: Using collections.Counter and pandas.DataFrame reduces the amount of code to write.
Exercise 3
End of explanation
"""


