import numpy as np

# Two classes of activation functions: Saturated(tanh, sigmoid, softmax) and not saturated (RELU, ELU, Leaky-RELU)
# saturated: solve the so called “exploding/vanishing gradient”
# not saturated: accelerate the convergence speed
# More info on activation functions: http://cs231n.github.io/neural-networks-1/

def softmax(x):
    """Softmax of a vector. It is the normalized exponential function. Softmax is a generalization of the logistic
    function that squashes a vector z into another vector of real values in the range (0, 1) that add up to 1.
    Used for multiclass classification.

    Args:
        x (list or np.array): Input list or array.

    Returns:
        np.array: The softmax of the array.

    Examples:
        >>> scores = [12, 5, 1]
        >>> softmax(scores)
        array([9.99072278e-01, 9.11035992e-04, 1.66862063e-05])
        >>> sum(softmax(scores))
        1.0
    """
    return np.exp(x) / np.sum(np.exp(x), axis=0)

def tanh(x):
    """Hyperbolic tangent. Output between -1,1

    Args:
        x (list or np.array): Input list or array.

    Returns:
        np.array: The tanh of the array.

    Examples:
        >>> x = np.array([[1,1,-3],[0.5,-1,1]])
        >>> tanh(x)
        array([[ 0.76159416,  0.76159416, -0.99505475],
               [ 0.46211716, -0.76159416,  0.76159416]])
    """
    return np.tanh(x)

def sigmoid(x):
    """Sigmoid function. Output between 0,1

    Args:
        x (list or np.array): Input list or array.

    Returns:
        np.array: The sigmoid of the array.

    Examples:
        >>> x = np.array([[1,1,-3],[0.5,-1,1]])
        >>> sigmoid(x)
        array([[0.73105858, 0.73105858, 0.04742587],
               [0.62245933, 0.26894142, 0.73105858]])
    """
    return 1.0 / (1 + np.exp(-x))

def RELU(x, inplace=False):
    """Rectified Linear Unit, removing the negative parts of the input: max(x,0)
    Benchmark: https://stackoverflow.com/a/40013151/5620182

    Args:
        x (list or np.array): Input list or array.
        inplace (bool): Inplace flag.

    Returns:
        np.array: The RELU of the array (if inplace is True).

    Examples:
        >>> x = np.array([[1,1,-3],[0.5,-1,1]])
        >>> RELU(x)
        array([[ 1. ,  1. , -0. ],
               [ 0.5, -0. ,  1. ]])
        >>> RELU(x, inplace=True)
        >>> x
        array([[1. , 1. , 0. ],
               [0.5, 0. , 1. ]])
    """
    if inplace:
        np.maximum(x, 0, x)
    else:
        return x * (x > 0)

def LeakyRELU(x, alpha=0.3):
    """Leaky Rectified Linear Unit. It allows a small gradient when the unit is not active.
    `f(x) = alpha * x for x < 0, f(x) = x for x >= 0`

    Args:
        x (list or np.array): Input list or array.
        alpha (float): Scale factor.

    Returns:
        np.array: The LeakyRELU of the array .

    Examples:
        >>> x = np.array([[1,1,-3],[0.5,-1,1]])
        >>> LeakyRELU(x)
        array([[ 1. ,  1. , -0.9],
               [ 0.5, -0.3,  1. ]])

    """
    return (x >= 0) * x + (x < 0) * alpha * x

def ELU(x, alpha=1.0):
    """Exponential Linear Unit
    `f(x) =  alpha * (exp(x) - 1.) for x < 0`,
    `f(x) = x for x >= 0`.
    paper: http://arxiv.org/abs/1511.07289

    Args:
        x (list or np.array): Input list or array.
        alpha (float): Scale factor.

    Returns:
        np.array: The ELU of the array .

    Examples:
        >>> x = np.array([[1,1,-3],[0.5,-1,1]])
        >>> ELU(x)
        array([[ 1.        ,  1.        , -0.95021293],
               [ 0.5       , -0.63212056,  1.        ]])
    """
    return (x >= 0) * x + (x < 0) * (alpha * np.exp(x) - alpha)

def SELU(x):
    """Scaled Exponential Linear Unit
    paper: https://arxiv.org/abs/1706.02515

    Args:
        x (list or np.array): Input list or array.
        alpha (float): Scale factor.

    Returns:
        np.array: The SELU of the array .

    Examples:
        >>> x = np.array([[1,1,-3],[0.5,-1,1]])
        >>> SELU(x)
        array([[ 1.05070099,  1.05070099, -1.67056873],
               [ 0.52535049, -1.11133074,  1.05070099]])
    """
    alpha = 1.6732632423543772848170429916717
    scale = 1.0507009873554804934193349852946
    return scale * ((x >= 0) * x + (x < 0) * (alpha * np.exp(x) - alpha))


import numpy as np
from sklearn.utils import shuffle

def gaussian_dist(dim, corr_factor, n_samples):
    '''
    Generates samples from two gaussian random variables X_a and X_b of
    dimension dim with Corr(X_a^i, X_b^j) = delta_ij * corr_factor, returning the
    theoretical mutual information MI(X_a, X_b).
    '''
    joint_mean = np.zeros(2 * dim)
    identity = np.identity(dim)
    joint_cov = np.concatenate([np.concatenate([identity, corr_factor * identity], axis=1),
                                np.concatenate([corr_factor * identity, identity], axis=1)],
                                axis=0)
    np.fill_diagonal(joint_cov, 1)
    theoretical_mi = -(1/2) * np.log2(np.linalg.det(joint_cov))
    X_joint = np.random.multivariate_normal(joint_mean, joint_cov, size=n_samples)
    X_a = X_joint[:, :dim]
    X_b = X_joint[:, dim:]
    return X_joint, X_a, X_b, theoretical_mi

def t_dist(dim, corr_factor, n_samples, freedom_deg=2):
    '''
    Output:
    Produce M samples of d-dimensional multivariate t distribution
    Input:
    mu = mean (d dimensional numpy array or scalar)
    Sigma = scale matrix (dxd numpy array)
    N = degrees of freedom
    M = # of samples to produce
    '''
    N = freedom_deg
    mu = np.zeros(2 * dim)
    M = n_samples
    g = np.tile(np.random.gamma(N/2.,2./N,M),(2*dim,1)).T
    X_joint, _, _, _ = gaussian_dist(dim, corr_factor, n_samples)
    X_joint = mu + X_joint / np.sqrt(g)
    X_a = X_joint[:, :dim]
    X_b = X_joint[:, dim:]
    return X_joint, X_a, X_b, None

def gaussian_mixture_dist(rot, n_samples):
    '''
    Generates samples from a mixture of 4 Gaussian distributions on each quadrant of the cartesian system
    rotated by $rot$ radians around the origin.
    '''
    std = 0.05
    values = np.arange(n_samples, dtype=int)
    n_samples_bin = np.histogram(values, bins=4)[0]
    X1, Y1 = np.random.multivariate_normal([4,4], [[std,0],[0,std]], n_samples_bin[0]).T
    X2, Y2 = np.random.multivariate_normal([4,2], [[std,0],[0,std]], n_samples_bin[1]).T
    X3, Y3 = np.random.multivariate_normal([2,4], [[std,0],[0,std]], n_samples_bin[2]).T
    X4, Y4 = np.random.multivariate_normal([2,2], [[std,0],[0,std]], n_samples_bin[3]).T
    X = np.expand_dims(np.concatenate([X1,X2,X3,X4]), axis=1)
    Y = np.expand_dims(np.concatenate([Y1,Y2,Y3,Y4]), axis=1)
    Xrot = X * np.cos(rot) - Y * np.sin(rot)
    Yrot = X * np.sin(rot) + Y * np.cos(rot)
    Xrot, Yrot = shuffle(Xrot, Yrot)
    return Xrot, Yrot


from scipy.optimize import minimize
import numpy as np
import pylab as pl
from mpl_toolkits.mplot3d import Axes3D
import math

def f(x):
  """ Function that returns x_0^2 + e^{0.5*x_0} + 10*sin(x_1) + x_1^2. """
  return x[0] ** 2 + math.exp(0.5 * x[0]) + 10 * math.sin(x[1]) + x[1] ** 2

def fprime(x):
  """ The derivative of f. """
  ddx0 = 2 * x[0] + 0.5 * math.exp(0.5 * x[0])
  ddx1 = 10 * math.cos(x[1]) + 2 * x[1]
  return np.array([ddx0, ddx1])

opt_out = minimize(f, x0=np.array(
    [10, 10]), jac=fprime, tol=1e-8, method='BFGS', options={'disp': True})

# Plotting
pl.close('all')
r = 6
x_range = np.linspace(-r, r)
y_range = np.linspace(-r, r)
X, Y = np.meshgrid(x_range, y_range)
Z = np.zeros(X.shape)
for i in range(X.shape[0]):
  for j in range(X.shape[1]):
    Z[i, j] = f(np.array([X[i, j], Y[i, j]]))

fig = pl.figure('Cost function')
ax = fig.add_subplot(111, projection='3d')
surf = ax.plot_surface(X, Y, Z, cmap=pl.cm.coolwarm, alpha=0.6)
ax.scatter(opt_out.x[0], opt_out.x[1], f(opt_out.x), c='r', s=50)

pl.show(block=False)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Sep 30 10:28:57 2019

@author: amandaash
"""
import numpy as np
import numpy.linalg as lin

matrix_A = np.array([[4,-2,1],[3,6,-4],[2,1,8]])
inverse_A = lin.inv(matrix_A)
print(np.array([[52,17,2],[-32,30,19],[-9,8,30]])/263)
print('---------')
print(inverse_A)
print('---------')
identity = np.dot(matrix_A, inverse_A)
print(identity)
print('-------')

b1 = np.array([12,-25,32])
solution_1 = lin.tensorsolve(matrix_A, b1)
b2 = np.array([4,-10,22])
solution_2 = lin.tensorsolve(matrix_A, b2)
b3 = np.array([20,-30,32])
solution_3 = lin.tensorsolve(matrix_A, b3)

print(solution_1)
print('-------')
print(solution_2)
print('--------')
print(solution_3)
print('-------')

#eigen value problem
alpha = 2
beta = -1
eigen_array = [[alpha, beta],[-beta, alpha]]
solution = lin.eig(eigen_array)
print(solution)
print('--------')
eigen_array2 = np.array([[-2,2,-3], [2,1,-6], [-1,-2,0]])
solution_2 = lin.eigvals(eigen_array2)
print(solution_2)


#using sympy to do symbolic computation and to integrate an expression

from sympy import *
init_printing() #do pretty printing; turn off with (False)

x, a, L, b, v = symbols('x a L b v')

expr=expr=v*((1-b)*cos(a*x)+b)
fx=expr.subs(a,2).subs(b,0.5).subs(v,10)
plotting.plot(fx,(x,0,pi.evalf()))

intX=integrate(expr,x)
fintX=intX.subs(a,2).subs(b,0.5).subs(v,10)
plotting.plot(fintX,(x,0,pi.evalf()))

#subsitute multiple variables
res_exp = expr.subs([(a, 2), (b, 4), (v, 1)])

#solve problem of moving point (0,L) with init/final velocity v:
from sympy import *
x, L, b, v = symbols('x L b v')

expr=v*((1-b)*cos(2*pi*x/L)+b)
intX=integrate(expr,x)
#==> b=1/v
expr2=v*((1-1/v)*cos(2*pi*x/L)+1/v)
intX2=integrate(expr2,x)
#==> intX2=v*(L*sin(2*pi*x/L)/(2*pi) - L*sin(2*pi*x/L)/(2*pi*v) + x/v)
intX2.subs(x,0) #0
intX2.subs(x,L) #L
expr2.subs(x,0) #v
expr2.subs(x,L) #v


import skimage
import skimage.io
import skimage.transform
import os
import numpy as np
import utils
import matplotlib.pyplot as plt
'''
Use what you’ve learned from the lectures and the recommended resources to remove thenoise in the image seen in Figure 5a.  Note that the noise is a periodic signal.
Also, the result youshould expect can be seen in Figure 5bInclude the filtered result in your report.Implement this in the filetask4c.py/task4c.ipynb.
Hint:Try to inspect the image in the frequency domain and see if you see any abnormal spikes that might be the noise.
'''

if __name__ == "__main__":
    # DO NOT CHANGE
    impath = os.path.join("images", "noisy_moon.png")
    im = utils.read_im(impath)
    # START YOUR CODE HERE ### (You can change anything inside this block)
    '''
    plt.figure(figsize=(20, 4))
    for i in range(10):
        plt.subplot(2, 5, i)
        kernel = utils.create_low_pass_frequency_kernel(im, 9.4+(0.05*i))
        im_filtered = np.log(np.abs(np.fft.ifft2(np.multiply(np.fft.fft2(im), np.fft.fft2(kernel)))) + 1)
        plt.imshow(im_filtered, cmap="gray")
    plt.show()
    '''

    plt.figure(figsize=(20, 4))
    plt.subplot(1, 5, 1)
    plt.imshow(im, cmap="gray")

    plt.subplot(1, 5, 2)
    plt.imshow(np.log(np.abs(np.fft.fftshift(np.fft.fft2(im))) + 1), cmap="gray")

    kernel = np.ones_like(im, dtype=np.float32)
    filter_rect_height = 11
    filter_rect_width_to_subtract = 25
    # Left rectangle
    kernel[im.shape[0]//2 - filter_rect_height//2 : im.shape[0]//2 + filter_rect_height//2, 0 : im.shape[1]//2 - filter_rect_width_to_subtract] = 0
    # Right rectangle
    kernel[im.shape[0]//2 - filter_rect_height//2 : im.shape[0]//2 + filter_rect_height//2, im.shape[1]//2 + filter_rect_width_to_subtract : im.shape[1]] = 0
    plt.subplot(1, 5, 3)
    plt.imshow(kernel, cmap="gray")

    fft_img_filtered = np.log(np.abs(np.fft.fftshift(np.multiply(np.fft.fft2(im), np.fft.ifftshift(kernel)))) + 1)
    plt.subplot(1,5,4)
    plt.imshow(fft_img_filtered, cmap="gray")

    spatial_result = np.log(np.abs(np.fft.ifft2(np.multiply(np.fft.fft2(im), np.fft.ifftshift(kernel)))) + 1)
    plt.subplot(1,5,5)
    plt.imshow(spatial_result, cmap="gray")

    plt.show()

    ### END YOUR CODE HERE ###
    utils.save_im("moon_filtered.png", utils.normalize(spatial_result))


#Forming Matrices

#for a matrix thats elements depend on a function use the following code

A=zeros((n,m),float) #creates a n by n matrix of zeros as n and m

for i in range (0,n):
	for j in range (0,m):
		A[i,j]=(i+j)**2  # where n/m is the matrix lenght/width and (i+j)**2 defines each a component as the row and collumn number added and squared change depending on function

------------------------------------------------------------------------------------------------------------------------------------------

#for a matrix that is given as inital values use an array for example
A=np.array([[4.0,-1.0,-1.0,-1.0],[-1.0,3.0,0.0,-1.0],[-1.0,0.0,3.0,-1.0],[-1.0,-1.0,-1.0,4.0]])

----------------------------------------------------------------------------------------------------------------------------------------

# GaussElimination / scipy.linalg
#uses the imports
from gaussElimin import * #where gaussElimin is a file on study direct
from scipy import linalg

#when you have a matrix A and a vector b you can solve you system using gaussian elimination in the following way say you would solve Ax=b where x is your output

#from Q1.py assesment 1 part B
x=gaussElimin(A,b) #where A and b are matrices and vectors defined as arrays
print x #gives out a vector

-----------------------------------------------------------------------------------------
#scipy.linalg

x=linalg.solve(A,b)#where A and b are matrices and vectors defined as arrays solves Ax=b where x is your output given as a vector

-----------------------------------------------------------------------------------
#LU Decompositopn
from scipy import linalg

#from Q1.py assesment 1 part C
P, L, U = linalg.lu(A)
#where L is the lower triangular matrix, U is the upper triangular matrix and P is the permutaion matrix

#to solve the syatem Ax=b using the L and U matrices (from Q1.py assesment 1 part d) you have made use
from gaussElimin import *

xL=gaussElimin(L,b) #gaussElimin changes b so no need to recombine xL and x

x=gaussElimin(U,b) #where x is shown as a vector and is the final answer or 'x' from Ax=b

print dot(dot(L,P),U)#checks answer by geting back to origonal matix


import numpy as np
import matplotlib.pyplot as plt

# inputs (mean is zero)
mean = [0,0]
sig1 = 1.0
sig2 = 2.0
rho = -0.75
Npoints = 5000

#covariance matrix
cov = np.array( [[sig1*sig1,rho*sig1*sig2],[rho*sig1*sig2,sig2*sig2]] )
print("\n\n input covariance matrix: ",cov)

#
# method 1: using np routine
#
x,y = np.random.multivariate_normal(mean,cov,Npoints).T
plt.plot(x,y,'x'); plt.axis('equal'); plt.title('method 1') ; plt.show()

#
# method 2: using Chalesky decomposition
#
from numpy import linalg as LA
U = LA.cholesky(cov)
XX = np.random.normal(size=2*Npoints)
XX.shape = (2,Npoints)
x,y = np.dot(U,XX)
plt.plot(x,y,'x'); plt.axis('equal'); plt.title('method 2') ; plt.show()

#
# method 3: using eigenvvalue decomposition
#
#http://homepages.inf.ed.ac.uk/imurray2/code/matlab_octave_missing/mvnrnd.m

Lambda,E = LA.eig(cov)
LambdaM = [ [Lambda[0],0],[0,Lambda[1]]]
U = np.dot(np.sqrt(LambdaM),E.T)
U = U.T

XX = np.random.normal(size=2*Npoints)
XX.shape = (2,Npoints)
x,y = np.dot(U,XX)
plt.plot(x,y,'x'); plt.axis('equal'); plt.title('method 3') ; plt.show()

# get statistics using np
a=np.cov( np.vstack((x,y))  )
print("\n\n reasult covariance matrix: ")
print(a)

#another way
X1 = np.array(x, ndmin=2, dtype=float)
Y1 = np.array(y, copy=False, ndmin=2, dtype=float)
axis = 0
XX = np.concatenate((X1, Y1), axis)
N = X1.shape[1]
cc = (np.dot(XX, XX.T.conj()) / float(N-1) ).squeeze()
print(cc)
print("\n\n result covariance matrix (by hand): %",(cc))

# <x y>
mm = np.dot(x,y)/float(N)
print("\n\n cross term <x y> %f: "%(mm))
print("\n\n rho: %f "%(mm/np.sqrt(cc[0,0]*cc[1,1])))
print(mm)


#########################
##                     ##
## Irving Gomez Mendez ##
##  February 12, 2021  ##
##                     ##
#########################

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import t

# The general formula of a parabola is given by
#y = Bx^2 + Cx + D

points = np.array([[-1,1/2],[1,-1],[2,-1/2],[3,2]])

points_x = points[:,0]
points_y = points[:,1]
points_x2 = points_x**2

n = points.shape[0]
yy = points_y
XX = np.vstack([points_x2, points_x, np.ones(n)]).T
p = XX.shape[1]

yy

XX

# We compute the coeff.
B, C, D = np.linalg.lstsq(XX, yy, rcond=None)[0]
params  = [B, C, D]

# Calculate the SSE
SSE = np.linalg.lstsq(XX, yy, rcond=None)[1]

# We get confidence interval
alpha = 0.05
x0 = np.linspace(-1.5, 3.5, 50)
X0 = np.vstack([x0**2, x0, np.ones(len(x0))]).T

aux_t_conf = np.sqrt(SSE/(n-p)*(np.diag(X0 @ np.linalg.inv(XX.T @ XX) @ X0.T)))
yy0_hat = X0 @ np.array(params)
upp_conf = yy0_hat+t.ppf(1-alpha/2,n-p)*aux_t_conf
low_conf = yy0_hat-t.ppf(1-alpha/2,n-p)*aux_t_conf

# We get prediction interval
aux_t_pred = np.sqrt(SSE/(n-p)*(1+np.diag(X0 @ np.linalg.inv(XX.T @ XX) @ X0.T)))
yy0_hat = X0 @ np.array(params)
upp_pred = yy0_hat+t.ppf(1-alpha/2,n-p)*aux_t_pred
low_pred = yy0_hat-t.ppf(1-alpha/2,n-p)*aux_t_pred

plt.figure(figsize=(7.5,7.5))
plt.plot(points_x, points_y, 'o', label='Original data', markersize=5)
plt.plot(x0, B*x0**2+C*x0+D, 'r', label='Fitted parabola')
plt.fill_between(x0, low_pred, upp_pred, facecolor='green', alpha=0.5, label='Prediction interval')
plt.fill_between(x0, low_conf, upp_conf, facecolor='yellow', alpha=0.5, label='Confidence interval')
plt.legend(loc='upper left')


import math
import numpy as np
import copy

def coordinate_transformation_in_angle(positions, base_angle):
    '''
    Transformation the coordinate in the angle

    Parameters
    -------
    positions : numpy.ndarray
        this parameter is composed of xs, ys
        should have (2, N) shape
    base_angle : float in radians

    Returns
    -------
    traslated_positions : numpy.ndarray
        the shape is (2, N)

    '''

    positions = positions.reshape(2, -1)

    rot_matrix = [[np.cos(base_angle), np.sin(base_angle)],
                  [-1*np.sin(base_angle), np.cos(base_angle)]]

    rot_matrix = np.array(rot_matrix)

    translated_positions = np.dot(rot_matrix, positions)

    return translated_positions

def coordinate_transformation_in_position(positions, base_positions):
    '''
    Transformation the coordinate in the positions

    Parameters
    -------
    positions : numpy.ndarray
        this parameter is composed of xs, ys
        should have (2, N) shape
    base_positions : numpy.ndarray
        this parameter is composed of x, y
        shoulg have (2, 1) shape

    Returns
    -------
    traslated_positions : numpy.ndarray

    '''

    positions = positions.reshape(2, -1)
    base_positions = base_positions.reshape(2, 1)

    translated_positions = positions - base_positions

    return translated_positions

# def coordinate_inv_transformation
if __name__ == '__main__':
    positions_1 = np.array([[1.0], [2.0]])
    base_angle = 1.25

    translated_positions_1 = coordinate_transformation_in_angle(positions_1, base_angle)
    print(translated_positions_1)


# Hacktoberfest 2021
# Problem: House Prices
#
# You are given an array that represents house prices.
# Calculate and output the percentage of houses that are within one standard deviation from the mean.
# To calculate the percentage, divide the number of houses that satisfy the condition by the total number of houses, and multiply the result by 100.

import numpy as np

data = np.array([150000, 125000, 320000, 540000, 200000, 120000, 160000, 230000, 280000, 290000, 300000, 500000, 420000, 100000, 150000, 280000])

m = np.mean(data)
d = np.std(data)
y1 = m-d
y2 = m+d
s = len(data [(data > y1) & (data < y2)])
r = (s/len(data))*100
print(r)


import numpy as np

# 1. Generation of fake input data

# X1 is a "n x D" [i.e. 100 x 2] multi-dimensional array; X1 will belong to class 1
X_class1_feature1 = np.random.normal(100,30,(100))
X_class1_feature2 = np.random.normal(800,60,(100))
X1 = np.array([X_class1_feature1,X_class1_feature2])
X1 = X1.T

# Similarly, we have X2 and X3
X_class2_feature1 = np.random.normal(500,50,(100))
X_class2_feature2 = np.random.normal(300,40,(100))
X2 = np.array([X_class2_feature1,X_class2_feature2])
X2 = X2.T

X_class3_feature1 = np.random.normal(50,15,(100))
X_class3_feature2 = np.random.normal(80,25,(100))
X3 = np.array([X_class3_feature1,X_class3_feature2])
X3 = X3.T

X = np.concatenate((X1,X2,X3))  # X is 300 x 2 array
y1 = np.ones((100,1))
y2 = np.ones((100,1)) + 1
y3 = np.ones((100,1)) + 2
y = np.concatenate((y1,y2,y3))  # y is 300 x 1 vector of labels

# 2. Initialisation of variables

h = 3 # Our hidden layer will have 3 hidden units
K = 3 # K is no. of Classes

W1 = 0.01*np.random.rand(2,h)   # weights from Input Layer to Hidden Layer
b1 = np.zeros((1,h))   # bias units for the hidden layer

W2 = 0.01*np.random.randn(h,K) # weights from Hidden Layer to the Output Layer
b2 = np.zeros((1,K)) # bias units for the output layer

step_size = 0.001
reg = 0.001

# 3. Doing Gradient Descent in a loop

print("Working on Gradient Descent...")
print()
for i in range(1000):
    hidden_layer = np.maximum(0,np.dot(X,W1)+b1) # using ReLU Activation Function
    scores = np.dot(hidden_layer, W2) +b2
    exp_scores = np.exp(scores)
    probs = exp_scores/np.sum(exp_scores,axis=1,keepdims=True)
    loss_for_each_example = -np.log(probs[range(300), ((y-1).T).astype(int)])  # y-1 because our class starts from 1 instead of 0.
    data_loss = np.sum(loss_for_each_example)/300   # get the average loss across all the examples
    reg_loss = 0.5 * reg * np.sum(W1*W1) + 0.5* reg* np.sum(W2*W2)   # Note W1*W1 is element-wise multiplication
    total_loss = data_loss + reg_loss

    # Compute the gradients
    dscores = probs   # dscores refer to dL/dscores where L is the total_loss
    dscores[range(300),((y-1).T).astype(int) ] -= 1
    dscores /= 300

    dW2 = np.dot(hidden_layer.T,dscores) + reg*W2   # dW2 refers to dL/dW2
    db2 = np.sum(dscores, axis=0, keepdims=True)    # db2 refers to dL/db2; likewise for the rest below
    dhidden = np.dot(dscores,W2.T)
    dhidden[hidden_layer <= 0] = 0
    dW1 = np.dot(X.T, dhidden) + reg*W1
    db1 = np.sum(dhidden, axis=0, keepdims = True)

    W1 += -step_size * dW1
    b1 += -step_size * db1
    W2 += -step_size * dW2
    b2 += -step_size * db2

    # Progress reporting
    if i % 100 == 0:
        string = "Trained for " + str(i) + " steps"
        print(string)
        print("Loss is "+ str(total_loss))

# 4. Display of results
print("Training completed")
print()
# Generate a Test Example from Class 1
test_X_class1_feature1 = np.random.normal(100,30,(1))
test_X_class1_feature2 = np.random.normal(800,60,(1))
test_X1 = np.array([test_X_class1_feature1,test_X_class1_feature2])
test_X1 = test_X1.T

# Generate a Test Example from Class 2
test_X_class2_feature1 = np.random.normal(500,50,(1))
test_X_class2_feature2 = np.random.normal(300,40,(1))
test_X2 = np.array([test_X_class2_feature1,test_X_class2_feature2])
test_X2 = test_X2.T

# Generate a Test Example from Class 3
test_X_class3_feature1 = np.random.normal(50,15,(1))
test_X_class3_feature2 = np.random.normal(80,25,(1))
test_X3 = np.array([test_X_class3_feature1,test_X_class3_feature2])
test_X3 = test_X3.T

test_X = np.concatenate((test_X1,test_X2,test_X3))
hidden_layer = np.maximum(0, np.dot(test_X, W1) + b1)
scores = np.dot(hidden_layer, W2) + b2
predicted_class = np.argmax(scores, axis=1)

print("Testing a Random Example from Class 1...")
print("Input is ")
print(test_X1)
print()
print("Output is Class ")
print(str(predicted_class[0]+1))     # plus 1 since our Class starts from Class 1 instead of Class 0

print("Testing a Random Example from Class 2...")
print("Input is ")
print(test_X2)
print()
print("Output is Class ")
print(str(predicted_class[1]+1))

print("Testing a Random Example from Class 3...")
print("Input is ")
print(test_X3)
print()
print("Output is Class")
print(str(predicted_class[2]+1))


# finite-difference implementation of FTCS for linear advection.
#
# We are solving a_t + u a_x = 0
#
# The FTCS discretization is: anew = aold + (C/2) (aold_{i+1} - aold_{i-1})
#
# where C is the CFL number
#
# M. Zingale (2013-03-12)

import numpy
import pylab

class FDgrid:

    def __init__(self, nx, ng, xmin=0.0, xmax=1.0):

        self.xmin = xmin
        self.xmax = xmax
        self.ng = ng
        self.nx = nx

        # python is zero-based.  Make easy intergers to know where the
        # real data lives
        self.ilo = ng
        self.ihi = ng+nx-1

        # physical coords
        self.dx = (xmax - xmin)/(nx-1)
        self.x = xmin + (numpy.arange(nx+2*ng)-ng)*self.dx

        # storage for the solution
        self.a = numpy.zeros((nx+2*ng), dtype=numpy.float64)

    def scratchArray(self):
        """ return a scratch array dimensioned for our grid """
        return numpy.zeros((self.nx+2*self.ng), dtype=numpy.float64)

    def fillBCs(self):
        """ fill the a single ghostcell with periodic boundary conditions """
        self.a[self.ilo-1] = self.a[self.ihi-1]
        self.a[self.ihi+1] = self.a[self.ilo+1]

# create the grid
nx = 65
ng = 1
g = FDgrid(nx, ng)

# define the CFL and speed
C = 0.9
u = 1.0

# time info
dt = C*g.dx/u
t = 0.0
tmax = 1.0*(g.xmax - g.xmin)/u

# initialize the data -- tophat
g.a[numpy.logical_and(g.x >= 0.333, g.x <= 0.666)] = 1.0

ainit = g.a.copy()

# evolution loop
anew = g.scratchArray()

while (t < tmax):

    # fill the boundary conditions
    g.fillBCs()

    # loop over zones: note since we are periodic and both endpoints
    # are on the computational domain boundary, we don't have to
    # update both g.ilo and g.ihi -- we could set them equal instead.
    # But this is more general
    i = g.ilo
    while (i <= g.ihi):

        # FTCS
        #anew[i] = g.a[i] - 0.5*C*(g.a[i+1] - g.a[i-1])

        # upwind
        anew[i] = g.a[i] - C*(g.a[i] - g.a[i-1])

        i += 1

    # store the updated solution
    g.a[:] = anew[:]

    t += dt

pylab.plot(g.x[g.ilo:g.ihi+1], ainit[g.ilo:g.ihi+1], ls=":")
pylab.plot(g.x[g.ilo:g.ihi+1], g.a[g.ilo:g.ihi+1])
pylab.savefig("fdadvect.png")


from sympy.core.numbers import igcd
from primetest import isprime

def totient_(n):
    """returns the number of integers less than n
    and relatively prime to n"""
    if n < 1:
        raise ValueError("n must be a positive integer")
    tot=0
    for x in xrange(1,n):
        if igcd(x,n)==1:
            tot+=1
    return tot

def n_order(a,n):
    """ returns the order of a modulo n
    Order of a modulo n is the smallest integer
    k such that a^k leaves a remainder of 1 with n.
    """
    assert igcd(a,n)==1
    if a>n : a=a%n
    for x in xrange(1,totient_(n)+1):
        if (a**x)%n==1:
            return x

def is_primitive_root(a,p):
    """
    returns True if a is a primitive root of p
    """
    assert igcd(a,p) == 1,"The two numbers should be relatively prime"
    if a>p:
        a=a%p
    if n_order(a,p)==totient_(p):
        return True
    else:
        return False

def is_quad_residue(a,p):
    """
    returns True if a is a quadratic residue of p
    p should be a prime and a should be relatively
    prime to p
    """
    assert isprime(p) and p!=2,"p should be an odd prime"
    assert igcd(a,p)==1,"The two numbers should be relatively prime"
    if a>p:
        a=a%p
    rem=(a**((p-1)//2))%p    # a^(p-1 / 2) % p
    if rem==1: return True
    else : return False

def legendre_symbol(a,p):
    """
    return 1 if a is a quadratic residue of p
    else return -1
    p should be an odd prime by definition
    """
    assert isprime(p) and p!=2,"p should be an odd prime"
    assert igcd(a,p)==1,"The two numbers should be relatively prime"
    if a>p:
        a=a%p
    if is_quad_residue(a,p)==True: return 1
    else : return -1


import numpy as np

def calc_area(vertex):
    vec_a = vertex[:,1] - vertex[:,0]
    vec_b = vertex[:,2] - vertex[:,0]
    normal = np.cross(vec_a, vec_b)
    area = np.absolute(np.linalg.norm(normal, ord=2, axis=1))*0.5
    return area

def uniform_sample_on_triangle(triangle):
    while True:
        rn = np.random.rand(2)
        if np.sum(rn) <= 1.0:
            break
    return rn[0]*(triangle[1]-triangle[0]) + rn[1]*(triangle[2]-triangle[0]) + triangle[0]

# mesh
def mesh2pcl(triangle_collection, numpoints):
    area_collection = calc_area(triangle_collection)
    total_area = np.sum(area_collection)

    print("Triangle count: {}".format(triangle_collection.shape[0]))
    #print("Total surface area: {}".format(total_area))

    area_collection /= total_area

    # sample k points
    # note that this will give an error if self.area_collection.shape[0] = 0 (implies empty shape)
    sampled_triangles = np.random.choice(area_collection.shape[0], size=numpoints, p=area_collection)

    # Sample one random uvs on each triangle
    rand_uv = np.random.rand(numpoints, 2)
    oob_idx = np.sum(rand_uv, axis=-1) > 1.0
    rand_uv[oob_idx,:] = -rand_uv[oob_idx,:] + 1.0

    sampled_triangle_collection = triangle_collection[sampled_triangles,:,:]
    sampled_points =  rand_uv[:,[0]] * (sampled_triangle_collection[:,1,:] - sampled_triangle_collection[:,0,:]) \
                    + rand_uv[:,[1]] * (sampled_triangle_collection[:,2,:] - sampled_triangle_collection[:,0,:]) \
                    + sampled_triangle_collection[:,0,:]

    return sampled_points.astype(np.float32)


from sympy import Matrix, Identity, DotProduct, eye
from sympy import sin, cos, sqrt, asin, acos
from sympy.matrices.expressions.vee import vee, Vee
from sympy.matrices.expressions.hat import hat, Hat

I3 = Matrix.eye(3)
v0 = Matrix([0,0,0])

def V(theta):
    norm2 = (theta.T * theta)[0]
    if norm2 == 0:
        return eye(3)
    else:
        norm = sqrt(norm2)
        t_hat = theta.hat()
        c_ = cos(norm)
        s_ = sin(norm)
        return eye(3) + t_hat * ((1-c_)/norm2) + (t_hat**2)*((norm-s_)/norm**3)

def Vinv(theta):
    norm2 = (theta.T * theta)[0]
    if norm2 == 0:
        return eye(3)
    else:
        norm = sqrt(norm2)
        t_hat = theta.hat()
        c_ = cos(norm)
        s_ = sin(norm)
        return eye(3) - t_hat/2 +  (t_hat**2)*(2*s_ - norm*(1 - c_)) / (2 * (norm**2) * s_)

def Exp_R3(vec):
    if len(vec) != 3:
        raise NotImplementedError

    theta = sqrt((vec.T * vec)[0])
    u = vec/theta

    return I3 + sin(theta) * u.hat() + (1-sin(theta)) * u.hat() * u.hat()

def Log_R3(R):
    R_ = Matrix(R)
    cos_theta = (R_.trace()-1)/2
    theta = acos(cos_theta)
    sin_theta_2 = 2*sqrt(1 - cos_theta**2)
    return theta * (R_ - R_.T) * theta/sin_theta_2


import numpy as np

def barycentric_coordinates(point, a, b, c):
    """It calcualtes the barycentric coordinates using the least square method
    point = l1*a + l2*b + l3*c

    Args:
        point ([np array]): [point on the triangle]
        a ([np array 2D or 3D]): [triangle vertiex 1]
        b ([np array 2D or 3D]): [triangle vertiex 2]
        c ([np array 2D or 3D]): [triangle vertiex 3]

    Returns: (l1,l2,l3)
        returns the barycentric coordinate of the triangle
    """
    T = np.hstack(((a - c)[:, None], (b - c)[:, None]))
    z = np.linalg.lstsq(T, point - c, rcond=1)[0]
    l1 = z[0]
    l2 = z[1]
    l3 = 1 - (l1 + l2)
    return l1, l2, l3

#####Additioanl Barycentric Coordinate calcualtion ####

# def barycentric_coordinates2(point, a, b, c):
#     v0 = b-a
#     v1 = c-a
#     v2 = point-a

#     d00 = np.dot(v0, v0)
#     d01 = np.dot(v0, v1)
#     d11 = np.dot(v1, v1)
#     d20 = np.dot(v2, v0)
#     d21 = np.dot(v2, v1)

#     denom = d00*d11 - d01*d01
#     v = (d11 * d20 - d01 * d21) / denom
#     w = (d00 * d21 - d01 * d20) / denom
#     u = 1 - (v + w)

#     print(u, v, w)
#     print(u*(a) + v * (b) + w*(c))


import numpy as np

class simpleols:

    def estimate(self, b1 = 1, b2 = 2, N=30, error_sigma=1, maxX=5):
        """
            Creates a sample of Size N data points sampled from
            Y = b1 + b2*X + u
            where u ~ N (0, error_sigma) around the line
            and x: N equally spaced samples between zero and maxX

            From the sample, the line of best fit is identified
            that minimises that SSE by estimating b1_hat, b2_hat

            Y_hat = b1_hat + b2_hat*X

            returns:
                X: the x axis values
                Y: the true values for x offset by the error term u ~ N (0, error_sigma)
                Y_hat: the estimated line of best fit
                b1_hat: OLS estimator for b1
                b2_hat: OLS estimator for b2

        """

        # construct N samples  following the line y = b1 + b2x + u
        # where the error term u ~ N(0, error_sigma)
        u = np.random.normal(0, error_sigma, N)
        X = np.arange(0, maxX, maxX/N)

        Y = b1 + b2 * X + u

        # To calculate the line of best fit solely from the
        # observed data tha we have
        # We must Find the b1_hat, b2_hat that minimise
        # The Sum of Squared Errors
        ybar = Y.mean()
        xbar = X.mean()

        b2_hat_numerator = X.dot(Y) - N * ybar * xbar
        b2_hat_denom = X.dot(X) - N * xbar * xbar
        b2_hat = b2_hat_numerator / b2_hat_denom

        b1_hat = ybar - b2_hat * xbar

        # Line of best fit
        Y_hat = b1_hat + b2_hat * X

        return (X, Y, Y_hat, b1_hat, b2_hat)


# question 1
import math

p = (1, 1, 0)
q = (0, 0, 2)
r = (1, 1, 1)

def dot_product(v1, v2):
    return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]

def cross_product(v1, v2):
    x = v1[1] * v2[2] - v1[2] * v2[1]
    y = v1[2] * v2[0] - v1[0] * v2[2]
    z = v1[0] * v2[1] - v1[1] * v2[0]
    return (x, y, z)

def norm(v):
    val = v[0] ** 2 + v[1] ** 2 + v[2] ** 2
    return math.sqrt(val)

# question 2

def points_to_vec(p1, p2):
    v = tuple(map(lambda x, y: x - y, p1, p2))
    return v

pq = points_to_vec(p, q)
print(pq)

qr = points_to_vec(q, r)
print(qr)

def angle_dot(v1, v2):
    numerator = dot_product(v1, v2)
    denominator = norm(v1) * norm(v2)
    return math.acos(numerator / denominator)

def angle_cross(v1, v2):
    numerator = norm(cross_product(v1, v2))
    denominator = norm(v1) * norm(v2)
    return math.asin(numerator / denominator)

print(angle_dot(pq, qr))
print(angle_cross(pq, qr))

v = cross_product(pq, qr)
print(v)

from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax = plt.axes(projection="3d")

ax.scatter3D(p[0], p[1], p[2])
ax.scatter3D(q[0], q[1], q[2])
ax.scatter3D(r[0], r[1], r[2])

def get_line(t):
    return p[0] + t * v[0], p[1] + t * v[1], p[2] + t * v[2]

t = np.linspace(-10, 10, 100)
ax.plot(*get_line(t))

plt.savefig("fig1.png")

ax.view_init(0, 40)

plt.savefig("fig2.png")

d = -1 * dot_product(p, v)
print(d)


import math
import numpy as np
from matplotlib import cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.stats import multivariate_normal

def gauss_kernel_values(xs: np.ndarray, cov: np.ndarray, mean: np.ndarray = None):
    '''
    Vectorized Gaussian Kernel
    v(x) ~ exp(-x'C^{-1} x/2) for cov = C

    xs.shape = (n_obs, n_dim)
    cov is the covariance matrix n_dim x n_dim
    returns 1d array of n_dim
    '''

    if mean is None:
        mean = np.zeros(xs.shape[1])

    xs = xs - mean

    # calculating a vectorized version of x' cov^{-1} x
    Cinv = np.linalg.inv(cov)
    ds = np.sum(xs * (xs @ Cinv), axis=1)
    # alegedly this is faster: numpy.einsum("ij,ij->i", xs, xs @ Cinv)

    dim = xs.shape[1]
    detC = np.linalg.det(cov)
    nrm = (2*math.pi)**(dim/2.0)*math.sqrt(detC)
    return np.exp(-ds)/nrm

def discretized_gauss_kernel_values_3d(xs: np.ndarray, cov: np.ndarray, mean: np.ndarray = None):
    rv = multivariate_normal(mean=mean, cov=cov)
    x1, x2, x3 = np.meshgrid(xs, xs, xs)
    pos = np.stack((x1, x2, x3), axis=-1)
    vals = 1e2*rv.pdf(pos)
    return vals, pos

def generate_test_kernel_3d(
        npts: int = 10, range_std: float = 1, stds: np.ndarray = None, off_diag_correl: float = 0.0, mean: np.ndarray = None):

    if stds is None:
        stds = np.array([1.0, 1.0, 1.0])

    dim = len(stds)

    cov = np.zeros((dim, dim))
    for i in np.arange(dim):
        cov[i, i] = stds[i]**2

    for i in np.arange(dim):
        for j in np.arange(i):
            cov[i, j] = off_diag_correl * np.sqrt(cov[i, i] * cov[j, j])
            cov[j, i] = cov[i, j]

    xs = np.linspace(-range_std, range_std, npts)
    kernel, pos = discretized_gauss_kernel_values_3d(xs, cov, mean=mean)
    return kernel, pos


"""
The expected number of items to complete a collection of n
distinct elements, in a random uniform and independent
gathering process, is n * H_n, where H_n is the nth harmonic
number.
"""
import numpy as np

def harmonic_number(n):
    return np.sum([1 / (1 + i) for i in np.arange(n)])

def general_experiment(n: int, name: str):
    expected_value = n * harmonic_number(n)
    counter = 0

    bdays = set()
    while len(bdays) < n:
        bdays.update({np.random.randint(1, n + 1)})
        counter += 1

    print("Gathering all {}".format(name))
    print("Steps to complete collection :", counter)
    print("Expected number of steps     :", expected_value)
    print("Difference                   :", abs(counter - expected_value))
    print()

general_experiment(365, "birthdays")
general_experiment(6, "dice values")
general_experiment(2, "coin faces (or getting a baby girl and baby boy)")


# --------------
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# code starts here

df =pd.read_csv(path)
print(df.info())

total = len(df)

#A -  fico credit score is greater than 700
p_a = len(df[df.fico>700])/total
print(f'P(A) = P(fico>700): {p_a}')

#B -  purpose == 'debt_consolation'
df1 = df[df['purpose'] == 'debt_consolidation'] # debt_consolidation capped df
p_b = len(df1)/total
print(f'P(B) = P(purpose is debt_consolation): {p_b}')
# print(df.purpose.value_counts())

# p(B & A)
p_a_and_b = df[(df.fico>700)&(df['purpose'] == 'debt_consolidation')][['fico']].shape[0]/df.shape[0]

# p(B|A)
p_a_b = p_a_and_b/p_b
print(f'P(B|A) = {p_a_b}')

#independency check: P(B|A) == P(B)?
result = p_a_b==p_b
print(f'Independent events?: {str(result)}')

# code ends here

# --------------
# code starts here

#total rows
total = df.shape[0]

# A: paid.back.loan == Yes
prob_lp = df[df['paid.back.loan'] == 'Yes'].shape[0]/total
print(f'P(A) = P(paid.back.loan == Yes): {prob_lp}')

# B: credit.policy == Yes
new_df = df[df['credit.policy'] == 'Yes']
prob_cs = new_df.shape[0]/total
print(f'P(B) = P(credit.policy == Yes): {prob_cs}')

# P(paid.back.loan == Yes & credit.policy == Yes)
prob_cs_and_lp = new_df[new_df['paid.back.loan'] == 'Yes'].shape[0]/total
prob_pd_cs = prob_cs_and_lp/prob_lp
print(f'P(B|A): {prob_pd_cs}')

# Using Bayes theorem to calculate P(A|B)
bayes = prob_pd_cs * prob_lp / prob_cs
print(f'P(A|B): {bayes}')

# code ends here

# --------------
# code starts here

# Visualize the bar plot for the feature purpose.
df.groupby('purpose')['purpose'].count().plot(kind='bar')
plt.show()

# Visualize the bar plot for the feature purpose where paid.back.loan
# df['paid.back.loan'].value_counts()
df1 = df[df['paid.back.loan'] == 'No']
df1.groupby('purpose')['purpose'].count().plot(kind='bar')
plt.show()

# code ends here

# --------------
# code starts here

# installment median
inst_median = df.installment.median()
print(f'installment median: {inst_median}')

# installment mean
inst_mean = df.installment.mean()
print(f'installment mean: {inst_mean}')

# plot the histogram for installment
print("Histogram for 'installment':")
plt.hist(df.installment)
plt.xlabel('installment')
plt.title('Distribution for installment')
plt.show()

# plot the histogram for log.annual.inc

print("Histogram for 'log.annual.inc':")
plt.hist(df['log.annual.inc'])
plt.xlabel('log annual income')
plt.title('Distribution for log annual income')
plt.show()
df.info()
# code ends here


import numpy as np

def accuracy_score(y_true, y_predict):
    assert y_true.shape == y_predict.shape,\
        "the size of y_true must be equal to the size of y_predict"
    return (sum(y_true == y_predict) / len(y_true))

def mean_squared_error(y_true, y_predict):
    assert len(y_true) == len(y_predict),\
        "the size of y_true must be equal to the size of y_predict"
    return np.sum((y_true - y_predict) ** 2) / len(y_true)

def root_mean_squared_error(y_true, y_oredict):
    return np.sqrt(mean_squared_error(y_true, y_oredict))

def mean_absolute_error(y_true, y_predict):
    assert len(y_true) == len(y_predict),\
        "the size of y_true must be equal to the size of y_predict"
    return np.sum(np.abs(y_true - y_predict)) / len(y_true)

def r2_score(y_true, y_predict):
    return 1 - mean_squared_error(y_true, y_predict) / np.var(y_true)

def TN(y_true, y_predict):
    assert len(y_true) == len(y_predict)
    return np.sum((y_true == 0) & (y_predict == 0))

def FP(y_true, y_predict):
    assert len(y_true) == len(y_predict)
    return np.sum((y_true == 0) & (y_predict == 1))

def FN(y_true, y_predict):
    assert len(y_true) == len(y_predict)
    return np.sum((y_true == 1) & (y_predict == 0))

def TP(y_true, y_predict):
    assert len(y_true) == len(y_predict)
    return np.sum((y_true == 1) & (y_predict == 1))

def confusion_matrix(y_true, y_predict):
    return np.array([
        TN(y_true, y_predict), FP(y_true, y_predict),
        FN(y_true, y_predict), TP(y_true, y_predict)
    ])

def precision_score(y_true, y_predict):
    assert len(y_true) == len(y_predict)
    tp = TP(y_true, y_predict)
    fp = FP(y_true, y_predict)
    try:
        return tp / (tp +fp)
    except:
        return 0.0

def recall_score(y_true, y_predict):
    assert len(y_true) == len(y_predict)
    tp = TP(y_true, y_predict)
    fn = FN(y_true, y_predict)
    try:
        return tp / (tp + fn)
    except:
        return 0.0

def f1_score(y_true, y_predict):
    precision = precision_score(y_true, y_predict)
    recall = recall_score(y_true, y_predict)

    try:
        return (2 * precision * recall) / (precision +recall)
    except:
        return 0.0

def TPR(y_true, y_predict):
    return recall_score(y_true, y_predict)

def FPR(y_true, y_predict):
    assert len(y_true) == len(y_predict)
    tn = TN(y_true, y_predict)
    fp = FP(y_true, y_predict)
    try:
        return fp / (fp +tn)
    except:
        return 0.0


# https://www.lcbo.com/content/dam/lcbo/corporate-pages/about/pdf/LCBO_AR17-18-english.pdf

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.stats import norm

time_steps = 6 # 5 Years into the future
sims = 30 # 30 simulations for each time step

data = pd.DataFrame({'Fiscal Year' : [2014, 2015, 2016, 2017, 2018],
                    'Expenses to Sales Ratio' : [0.163, 0.160, 0.156, 0.160, 0.158]})
data.set_index('Fiscal Year', inplace=True)

# pct_change is the percentage change between current and prior element
log_esr = np.log(1 + data.pct_change())

# Setting up the values necessary for the yearly_esr expression
mu = log_esr.mean() # average/mean
var = log_esr.var() # variance
drift = mu - (0.5 * var) # drift
sigma = log_esr.std() # standard deviation

# esr is expenses to sales ratio
yearly_esr = np.exp(drift.values + sigma.values * norm.ppf(np.random.rand(time_steps, sims)))

# Takes last data point as as the starting point for the simulations
initial = data.iloc[-1]
monte_list = np.zeros_like(yearly_esr)
monte_list[0] = initial

# For the amount of steps taken into the future, this loop calculated the simulation data to be plotted
# Takes input price, multiplies it by the exponential value calculated in yearly_esr, sets simulated price
for t in range(1, time_steps):
    monte_list[t] = monte_list[t - 1] * yearly_esr[t]

# Histogram for the price frequencies, number of bins can be adjusted
plt.figure(figsize=(10, 6))
plt.hist(monte_list[1], bins=5, density=True)

# Fit a normal distribution to the first simulation point in monte_list data
# Could implement for each successive iteration in the monte_list index (monte_list[2], monte_list[3], ..., monte_list[time_steps - 1])
sim_mu, sim_sig = norm.fit(monte_list[1])

# Probability Density Function
xmin, xmax = plt.xlim() # set the xmin and xmax along the x-axis for the pdf
x = np.linspace(xmin, xmax)
p = norm.pdf(x, sim_mu, sim_sig)

# Plot Histogram with probability density function
plt.plot(x, p, 'k') # normal distribution fit
plt.xlabel('Expenses to Sales Ratio')
plt.ylabel('Probability Density')
title = "Histogram for 30 Simulations of Expenses to Sales Ratio 1 Year into the Future\nPDF fit results: mu = %.4f,  sigma = %.4f" % (sim_mu, sim_sig)
plt.title(title)

# Plot of 30 Monte Carlo Simulations for each year into the future
plt.figure(figsize=(10, 6))
plt.plot(monte_list) # monte carlo
plt.xlabel('Years into the Future')
plt.ylabel('Expenses to Sales Ratio')
title = "Expenses to Sales Ratio Monte Carlo Simulations (n = 30)"
plt.title(title)
plt.show()


import numpy as np
import pandas as pd
import time
from datetime import datetime
from scipy import stats

a1 = np.array([0,1,2,3,4])
a2 = np.arange(5)
a3 = np.arange(1,16,3) # start <end step
a4 = np.linspace(0,1,11) # linear scale

# np.random.seed(12345) # seed should change everytime program runs
timestamp = int(time.mktime(datetime.now().timetuple()))
np.random.seed(timestamp)
a5 = np.random.rand(5) # normal distributed random numbers
a6 = np.random.randn(5) # gaussian distributed random numbers
a7 = np.array(['MALE', 'FEMALE', 'MALE', 'FEMALE', 'MALE'])

print(a1)
print(a2)
print(a3)
print(a4)
print(a5)
print(a6)
print(a7)

t = a1.copy()
x1 = a5.copy()
x2 = a6.copy()
genders = a7.copy()

# plt.plot(t,x1)
# plt.show()

df = pd.DataFrame({'TIME':t, 'X1':x1, 'X2':x2, 'GENDER':genders})
print(df.shape) # columns, rows
print(df.columns) # column names
print(df)
# print(df.TIME)

somedata = df[ df['X1'] < 0.5 ] # filter
print("Some data: \n", somedata)
someotherdata = df[ df['X1'] < 0.5 ][ df['GENDER'] == 'MALE' ] # filter
print("Some other data:\n", someotherdata)

print("Values by gender:\n")
groupby_gender = df.groupby('GENDER')
for gender, values in groupby_gender:
    print(gender, values)

# 1-sample t-test: testing the value of a population mean
# tests if the population mean of data is likely to be equal to a given value
print("1-sample t-test")
value = 0.5
result = stats.ttest_1samp(df['X1'],value) # returns statistic and p-value
print(result) # can use result.statistic and result.pvalue for individual values

# 2-sample t-test: testing for difference across populations
print("2-sample t-test")
female_x1 = df[ df['GENDER'] == 'FEMALE' ]['X1']
male_x1 = df[ df['GENDER'] == 'MALE' ]['X1']
result = stats.ttest_ind(female_x1,male_x1) # returns statistic and p-value
            # if assuming equal variance add parameter equal_var = True
            # if data set includes NaN values then you might like to add parameter nan_policy = 'omit'
print (result) # can use result.statistic and result.pvalue for individual values

Numpy and Pandas Samples/Numpy Arrays Pandas DataFrame and Stats TTest.py


import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt

def SIRDModel(beta, gamma, δ1, δ2,tend,vaccinateAfter,minI,maxI,distanceModel):
    # Total population, N.
    N = 1.0
    # Initial number of infected and recovered individuals, I0 and R0.
    S0 = 0.99999
    I0 = 0.00001
    R0 = 0.0
    D0 = N - S0 - I0 - R0
    t = np.linspace(0.0, tend, int(tend))
    # The SIRD model differential equations.
    def rhs(SIRD, t, N, beta, gamma, δ1, δ2, vaccinateAfter, minI,maxI):
        S, I, R, D = SIRD
        if (distanceModel == 'Reactive'):
            δf2 = 0. #δ2
            δf1 = 0. #δ1

            if (I>=maxI):
                δf1 = 0.0
                δf2 = δ2 #* np.sin(2.0*np.pi*t/200)**2

            if (I <= minI):
                δf1 = δ1 #- beta/N * I #* np.sin(2.0*np.pi*t/200)**2
                δf2 = 0.0
        else:
            δf2 = δ2
            δf1 = δ1

        vacc = 0.0
        if (t>= vaccinateAfter):
            vacc = 1.0
            δf2 = 0.0 #* np.sin(2.0*np.pi*t/20)
            δf1 = 1.0 #* np.sin(2.0*np.pi*t/200)**2
            beta = 0.0

        dSdt = - beta/N * S * I + δf1*D - δf2*S - vacc*S
        dIdt =   beta/N * S * I - gamma * I
        dRdt =   gamma * I +  vacc*S
        dDdt = - δf1*D + δf2*S
        return dSdt, dIdt, dRdt, dDdt

    # Initial conditions
    y0 = S0, I0, R0, D0
    # Integrate the SIR equations over the time grid, t.
    sol = odeint(rhs, y0, t, args=(N, beta, gamma, δ1, δ2,vaccinateAfter, minI, maxI))
    return t, sol.T

def plot_sird_model(infection_rate, incubation_period, D_to_S, S_to_D, tend_months, vaccinateAfter, minIpercent=1,maxIpercent=10,distanceModel='Constant',semilogy=False):
    tend = 30.0*tend_months
    gamma = 1.0/incubation_period
    vaccinateAfter = 30*vaccinateAfter
    δ1 = D_to_S
    δ2 = S_to_D

    t,sol = SIRDModel(infection_rate, gamma, δ1, δ2, tend, vaccinateAfter,minIpercent/100.,maxIpercent/100., distanceModel)
    S, I, R, D = sol
    plt.figure(figsize=[7,4])
#     plt.clf()
    f = plt.plot
    if semilogy:
        f = plt.semilogy
    f(t/30, S*100, 'b', alpha=0.65, lw=2, label='Susceptible')
    f(t/30, R*100, 'g', alpha=0.65, lw=2, label='Recovered')
    f(t/30, I*100, 'r', alpha=0.65, lw=2, label='Infected')
    f(t/30, D*100, 'k', alpha=0.65, lw=2, label='Distanced')

    # plot maximum infected line
    maxInfected = np.max(I)*100
    plt.axhline(y=maxInfected,xmin=0,xmax=tend,linestyle='-.',color='r')

    # plot minimum infected line
    maxQuarantined = np.max(D)*100
    plt.axhline(y=maxQuarantined,xmin=0,xmax=tend,linestyle='-.',color='k')

    # fix the yticks
    if δ2 > 0.0 and semilogy != True:
        ticks = [0.0,50,100,maxInfected, maxQuarantined]
        plt.yticks(ticks)

    plt.xlabel('# Months since outbreak')
    plt.ylabel('% Population')
    plt.xlim(0,tend/30)
    plt.yticks()
    plt.minorticks_on()
#     plt.ylim(top=100)
    plt.legend()
    title = 'SIRD with $\delta_1$ =' + str(δ1) + ' $\delta_2$ = ' + str(δ2)
    plt.title(title)
    plt.grid()
#     plt.savefig(title + '.pdf')


## 1. Introduction ##

mean_new = houses_per_year['Mean Price'].mean()
mean_original = houses['SalePrice'].mean()
difference = mean_original - mean_new

## 2. Different Weights ##

houses_per_year['sum_per_year'] = houses_per_year['Mean Price'] * houses_per_year['Houses Sold']
all_sums_together = houses_per_year['sum_per_year'].sum()
total_n_houses = houses_per_year['Houses Sold'].sum()
weighted_mean = all_sums_together / total_n_houses

mean_original = houses['SalePrice'].mean()

difference = round(mean_original, 10) - round(weighted_mean, 10)

## 3. The Weighted Mean ##

def weighted_mean(distribution, weights):
    weighted_sum = []
    for mean, weight in zip(distribution, weights):
        weighted_sum.append(mean * weight)

    return sum(weighted_sum) / sum(weights)

weighted_mean_function = weighted_mean(houses_per_year['Mean Price'],
                                       houses_per_year['Houses Sold'])

from numpy import average
weighted_mean_numpy = average(houses_per_year['Mean Price'],
        weights = houses_per_year['Houses Sold'])

equal = round(weighted_mean_function, 10) == round(weighted_mean_numpy, 10)

## 4. The Median for Open-ended Distributions ##

distribution1 = [23, 24, 22, '20 years or lower,', 23, 42, 35]
distribution2 = [55, 38, 123, 40, 71]
distribution3 = [45, 22, 7, '5 books or lower', 32, 65, '100 books or more']
median1 = 23
median2 = 55
median3 = 32

## 5. Distributions with Even Number of Values ##

# Sort the values
rooms = houses['TotRms AbvGrd'].copy()
rooms = rooms.replace({'10 or more': 10})
rooms = rooms.astype(int)
rooms_sorted = rooms.sort_values()

# Find the median
middle_indices = [int((len(rooms_sorted) / 2) - 1),
                  int((len(rooms_sorted) / 2))
                 ] # len - 1 and len because Series use 0-indexing
middle_values = rooms_sorted.iloc[middle_indices] # make sure you don't use loc[]
median = middle_values.mean()

## 6. The Median as a Resistant Statistic ##

import matplotlib.pyplot as plt

houses['Lot Area'].plot.box()
plt.show()
houses['SalePrice'].plot.box()
plt.show()

lotarea_difference = houses['Lot Area'].mean() - houses['Lot Area'].median()
saleprice_difference = houses['SalePrice'].mean() - houses['SalePrice'].median()

## 7. The Median for Ordinal Scales ##

mean = houses['Overall Cond'].mean()
median = houses['Overall Cond'].median()

houses['Overall Cond'].plot.hist()
more_representative = 'mean'

'''
The mean seems more representative and more informative because it captures the
fact that there are more houses rated above 5 than rated under 5. Because of this,
the mean is slightly shifted above 5.
'''

Data Analyst in Python/Step 5 - Probability and Statistics/2. Statistics Intermediate Averages and Variability/2. The Weighted Mean and the Median.py

Data Scientist In Python/Step 5 - Probability and Statistics/2. Statistics Intermediate Averages and Variability/2. The Weighted Mean and the Median.py


from numpy import *

# y = mx + b
# m is slope, b is y-intercept
def compute_error_for_line_given_points(b, m, points):
	# initialize it at 0
	total_error = 0
	# for every point
	for i in range(1, len(points)):
		# get the x value
		x = points[i, 0]
		# get the y value
		y = points[i, 1]
		# get the difference, square it, add it to the total
		total_error += (y - (m * x + b)) ** 2

	# get the average
	average_value = total_error / float(len(points))

	return average_value

def gradient_descent_runner(points, starting_b, starting_m, learning_rate, num_iterations):
	# starting b and m
	b = starting_b
	m = starting_m

	# gradient descent
	for i in range(num_iterations):
		# update b and m with the new more accurate b and m by performing
		# this gradient step
		b, m = step_gradient(b, m, array(points), learning_rate)

	return [b, m]

def step_gradient(b_current, m_current, points, learning_rate):
	# starting points for our gradients
	b_gradient = 0
	m_gradient = 0

	N = float(len(points))

	for i in range(0, len(points)):
		x = points[i, 0]
		y = points[i, 1]
		# direction with respect to b and m
		# computing partial derivatieves of our error function
		b_gradient += -(2/N) * (y - (m_current * x + b_current))
		m_gradient += -(2/N) * x * (y - (m_current * x + b_current))

	# update our b and m values using partial derivatives
	new_b = b_current - (learning_rate * b_gradient)
	new_m = m_current - (learning_rate * m_gradient)

	return [new_b, new_m]

def run():
    # Step 1 - collect our data
    # X is the amount of hours studied
    # Y is the test score
    points = genfromtxt('data.csv', delimiter=',')

    # Step 2 - define our hyperparameters
    # how fast should our model converge?
    learning_rate = 0.0001

    # y = mx + b (slope formula)
    initial_b = 0
    initial_m = 0
    num_iterations = 1000

    # Step 3 - train our model
    print 'starting gradient descent at b = {0}, m = {1}, error = {2}'.format(
    	initial_b,
    	initial_m,
    	compute_error_for_line_given_points(initial_b, initial_m, points)
    )

    [b, m] = gradient_descent_runner(points, initial_b, initial_m, learning_rate, num_iterations)

    print 'ending point descent at b = {1}, m = {2}, error = {3}'.format(num_iterations, b, m,
    	compute_error_for_line_given_points(b, m, points)
    )

if __name__ == '__main__':
    run()


# -*- encoding: utf-8 -*-
# Date: 27/Jan/2022
# Author: Steven Huang, Auckland, NZ
# License: MIT License
"""
Description: math functions
"""
import numpy as np

def get_distance(pt1, pt2):
    """get distance of two points"""
    return np.sqrt(np.power(pt1[0] - pt2[0], 2) + np.power(pt1[1] - pt2[1], 2))

def get_line(pt1, pt2):
    """get line slope and bias from two points
    y = slope * x + bias
    """
    slope, bias = None, None
    if pt1[0] != pt2[0]:
        slope = (pt1[1] - pt2[1]) / (pt1[0] - pt2[0])
        bias = pt1[1] - slope * pt1[0]
    return slope, bias

def get_5star(R=10, r=3):
    """get five points star coordinates method, center(0,0)
    Outer points equation:{(R*cos(2πk/5+π/2),R*sin(2πk/5+π/2)) ∣ k=0,...,4}
    Inner points equation:{(r*cos(2πk/5+π/2+π/5),r*sin(2πk/5+π/2+π/5)) ∣ k=0,...,4}
    """
    # points = [[R*np.cos(i * 2 * np.pi / 5 + np.pi/2), R*np.sin(i*2*np.pi/5+np.pi/2)] for i in range(5)]

    points = []
    for i in range(5):
        """ add or sub π/2 to adjust start point """
        points.append([R*np.cos(i * 2 * np.pi / 5 - np.pi/2), R*np.sin(i*2*np.pi/5-np.pi/2)])
        points.append([r*np.cos(i*2*np.pi/5-np.pi/2+np.pi/5), r*np.sin(i*2*np.pi/5-np.pi/2+np.pi/5)])

    return np.asarray(points)

def get_regular_ngons(R=10, N=3, offset_angle=0):
    points = []
    for i in range(N):
        """ add or sub π/2 to adjust start point """
        theta = i * 2 * np.pi / N + offset_angle  # - np.pi/2
        points.append([R * np.cos(theta), R * np.sin(theta)])
    return np.asarray(points)

def points_on_triangle(v, n):
    """
    Give n random points uniformly on a triangle.

    The vertices of the triangle are given by the shape
    (2, 3) array *v*: one vertex per row.
    """
    x = np.sort(np.random.rand(2, n), axis=0)
    x = np.column_stack([x[0], x[1]-x[0], 1.0-x[1]])
    return np.dot(x, v)

def get_velocity_line(pt, vx, vy):
    """get line slope and bias from a start point and x, y change velocity,
    y = slope*x + bias
    """
    slope, bias = None, None
    if vx != 0:
        slope = vy / vx
        bias = pt[1] - slope * pt[0]
    return slope, bias

def get_velocity_line_abc(pt, vx, vy):
    """get line slope and bias from a start point and x, y change velocity,
    A*x + B*y + C = 0
    """
    return get_line_ABC(pt, (pt[0]+vx, pt[1]+vy))

def get_line_ABC(pt1, pt2):
    """get line parameters from two points
    A*x + B*y + C = 0, A=y1-y2, B=x2-x1, C=x1*y2-x2*y1
    """
    A = pt1[1] - pt2[1]
    B = pt2[0] - pt1[0]
    C = pt1[0] * pt2[1] - pt2[0] * pt1[1]
    return [A, B, C]

def get_line_ABC_inter(line1, line2):
    """get line intersection point,
    line1: A1*x + B1*y + C1 = 0, [A1, B1, C1]
    line2: A2*x + B2*y + C2 = 0, [A2, B2, C2]
    inter = B1*A2 - A1*B2
    x = (C1*B2-B1*C2)/inter, y=(A1*C2-A2*C1)/inter
    """
    inter = line1[1]*line2[0] - line1[0]*line2[1]
    if inter != 0:
        x = (line1[2]*line2[1]-line1[1]*line2[2])/inter
        y = (line1[0]*line2[2] - line2[0]*line1[2])/inter
        return (x, y)
    return None

def get_line_ABC_y(line, x):
    """line: a*x + b*y + c = 0, line=[a, b, c]"""
    a, b, c = line[0], line[1], line[2]
    if b == 0:
        return None
    return (-c - a * x) / b

def get_line_ABC_x(line, y):
    """line: a*x + b*y + c = 0, line=[a, b, c]"""
    a, b, c = line[0], line[1], line[2]
    if a == 0:
        return None
    return (-c - b * y) / a

def get_perpendicular_point_line_ABC(line, pt, reflect=False):
    """get foot point of the perpendicular from a point (x1,y1) to the line,
    line: A*x + B*y + C = 0, [A, B, C]
    tmp = -(A*x1+B*y1+c)/(A^2 + B^2)
    x = A*tmp + x1, y= B*tmp + y1
    """
    deno = line[0]**2 + line[1]**2  # denominator
    if deno != 0:
        tmp = -1*(line[0]*pt[0] + line[1]*pt[1] + line[2])/deno
        if reflect:
            tmp = tmp*2
        x = line[0]*tmp + pt[0]
        y = line[1]*tmp + pt[1]
        return (x, y)
    return None

def get_inter_pt(line):
    pass

def main():
    x = [1, 2]
    y = [3, 4]
    # print(get_line(x, y))
    print(get_distance(x, y))

if __name__ == '__main__':
    main()


import numpy as np

# Given the first Probability of our coin, calculate the entropy
def calc_entropy(P):
    return -(P * np.log2(P) + (1 - P) * np.log2(1 - P))

# Calculate the entropy of our 3 coins
print(calc_entropy(0.5))
print(calc_entropy(0.9))
print(calc_entropy(0.1))

# Calculate the entropy of someone playing tennis from our tennis dataset
# (9 out of 14 people said no)
print(calc_entropy(9 / 14))


"""Using monte carlo method find the area of a sine curve from 0
to 3pi, what is the area?

Details and Assumptions
Consider the area below the x-axis as negative area.
"""
import numpy as np

def point():
	(x, y) =  np.random.rand(2)
	x = 3*np.pi*x
	y = 2*y - 1
	return (x, y)

def simulation(runs=1):
	total   = 0
	positive = 0
	negative = 0

	while runs:
		(x, y) = point()
		if y < 0:
			if y >= np.sin(x):
				negative += 1
		else:
			if y <= np.sin(x):
				positive += 1
		total += 1
		runs  -= 1

	positive_area = 6*np.pi*positive/total
	negative_area = 6*np.pi*negative/total

	return positive_area-negative_area

area = simulation(runs=100000)
print(area)


import pandas as pd
import numpy as np
from sklearn import linear_model
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score

############# Preprocess #############
# Data load
data = np.loadtxt('data/RegressionData.txt', delimiter=',')
# 2) Permutation & Seed 0
np.random.seed(0)
n_sample = len(data)
order = np.random.permutation(n_sample)
data = data[order]

# 1) Column split
x_data = data[:,:-2]
y_data = data[:,-2:]

# 3) Data split
x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size = 0.2, random_state = 42)
x_train, x_val, y_train, y_val = train_test_split(x_train, y_train, test_size = 0.25, random_state = 42)

# 4) Scaling
mms = MinMaxScaler()
X_train = mms.fit_transform(x_train)
X_val = mms.transform(x_val)
X_test = mms.transform(x_test)

################ Validation #################
# 2) alpha list
alpha_list = [0.0001, 0.001, 0.01, 0.1, 1]
label = ['latitute', 'longitude']

# 1) Except for LS, validation
for i in range(2): # 0: latitute, 1: longitude
    for model in ['LeastSquare', 'Ridge', 'Lasso']:
        mse_list = []
        for alpha in alpha_list:
            if model == 'LeastSquare':
                continue
            if model == 'Ridge':
                rm = linear_model.Ridge(alpha=alpha)
            if model == 'Lasso':
                rm = linear_model.Lasso(alpha=alpha)
            rm.fit(X_train, y_train[:,i])
            y_hat = rm.predict(X_val)
            mse_list.append(mean_squared_error(y_val[:,i], y_hat))

################## Test ######################
        if model == 'LeastSquare':
            rm = linear_model.LinearRegression()
        elif model == 'Ridge':
            rm = linear_model.Ridge(alpha=alpha_list[np.argmin(mse_list)])
        else:
            rm = linear_model.Lasso(alpha=alpha_list[np.argmin(mse_list)])

        rm.fit(X_train, y_train[:,i])
        y_hat = rm.predict(X_test)
        rmse = np.sqrt(mean_squared_error(y_test[:,i], y_hat))
        var = r2_score(y_test[:,i], y_hat)

        # 2) Print
        if model == 'Ridge' or model == 'Lasso':
            print("%s best alpha of %s: %f" %(model, label[i], alpha_list[np.argmin(mse_list)]))
        print("%s RMSE of %s: %f" %(model, label[i], rmse))
        print("%s Variance of %s: %f" %(model, label[i], var))
        print("="*40)


from bisect import bisect_left
from typing import List, Tuple

import numpy as np
import scipy.stats as ss

# function to calculate Cohen's d for independent samples
def cohend(a: List[float], b: List[float]) -> Tuple[float, str]:
    a = np.asarray(a)
    b = np.asarray(b)
    # calculate the size of samples
    m, n = len(a), len(b)
    assert m == n, "The two list must be of the same length: {}, {}".format(m, n)
    # calculate the variance of the samples
    s1, s2 = np.var(a, ddof=1), np.var(b, ddof=1)
    # calculate the pooled standard deviation
    s = np.sqrt(((m - 1) * s1 + (n - 1) * s2) / (m + n - 2))
    # calculate the means of the samples
    u1, u2 = np.mean(a), np.mean(b)
    # calculate the effect size
    effect_size = (u1 - u2) / s
    magnitude = "large"
    if abs(effect_size) < 0.50:
        magnitude = "small"
    elif 0.50 <= abs(effect_size) < 0.80:
        magnitude = "medium"

    return effect_size, magnitude

def vargha_delaney(a: List[float], b: List[float]) -> Tuple[float, str]:
    """
    Computes Vargha and Delaney A index
    A. Vargha and H. D. Delaney.
    A critique and improvement of the CL common language
    effect size statistics of McGraw and Wong.
    Journal of Educational and Behavioral Statistics, 25(2):101-132, 2000
    The formula to compute A has been transformed to minimize accuracy errors
    See: http://mtorchiano.wordpress.com/2014/05/19/effect-size-of-r-precision/
    :param a: a numeric list
    :param b: another numeric list
    :returns the value estimate and the magnitude
    """
    m = len(a)
    n = len(b)

    assert m == n, "The two list must be of the same length: {}, {}".format(m, n)

    r = ss.rankdata(a + b)
    r1 = sum(r[0:m])

    # Compute the measure
    # A = (r1/m - (m+1)/2)/n # formula (14) in Vargha and Delaney, 2000
    A = (2 * r1 - m * (m + 1)) / (2 * n * m)  # equivalent formula to avoid accuracy errors

    levels = [0.147, 0.33, 0.474]  # effect sizes from Hess and Kromrey, 2004
    magnitude = ["negligible", "small", "medium", "large"]
    scaled_A = (A - 0.5) * 2

    magnitude = magnitude[bisect_left(levels, abs(scaled_A))]
    estimate = A

    return estimate, magnitude


import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import skewnorm
from scipy.stats import norm
from numpy.random import seed
from numpy.random import randint
from scipy.stats.kde import gaussian_kde
import random
seed(1)

## Left skewed data With n = 15
## Example 1

fig, ax = plt.subplots(1, 1)

a = 15
mean, var, skew, kurt = skewnorm.stats(a, moments='mvsk')
x = np.linspace(skewnorm.ppf(0.01, a),
                skewnorm.ppf(0.99, a), 100)

rv = skewnorm(a)
ax.plot(x, rv.pdf(x), 'k-', lw=2, label='Probability Density Function')

r = skewnorm.rvs(a, size=1000)
ax.hist(r, density=True, histtype='stepfilled', alpha=0.2)
ax.legend(loc='best', frameon=False)
plt.show()

print(r)

means = [np.mean(random.sample(list(r), 15)) for _i in range(1000)]

print(type(means))

print(means)
means = sorted(means)
fig, ax = plt.subplots(1, 1)
kde = gaussian_kde(means)
dist_space = np.linspace( min(means), max(means), 100 )
ax.plot(dist_space, kde(dist_space), 'k-', lw=2, label='Probability Density Function')
ax.hist(means, density=True, histtype='stepfilled', alpha=0.2)
ax.legend(loc='best', frameon=False)
plt.show()

##### To check if sample is normal
from scipy.stats import shapiro
stat, p = shapiro(means)
print('Statistics={}, p={}'.format(stat, p))
alpha = 0.05
if p > alpha:
    print('Sample looks Normal (do not reject H0)')
else:
    print('Sample does not look Normal (reject H0)')

## Left skewed data With n = 15
## Example 2


from sympy import Symbol
x = Symbol('x')

f = 0.2 + 25*x - 200 * x**2 + 675 * x**3 - 900 * x**4 + 400 * x**5

a = 0.0
b = 0.8
n = 3

def simp38(f, a, b, n = 3):
    h = (b-a)/n
    print('h = %.4f'% h)
    x0 = a
    print('x0 = %.4f'% x0)
    x1 = a + h
    print('x1 = %.4f'% x1)
    x2 = a + 2*h
    print('x2 = %.4f'% x2)
    x3 = a + 3*h
    print('x3 = %.4f'% x3)
    fx0 = f.subs(x, x0)
    print('f(x0) = %.4f'% fx0)
    fx1 = f.subs(x, x1)
    print('f(x1) = %.4f'% fx1)
    fx2 = f.subs(x, x2)
    print('f(x2) = %.4f'% fx2)
    fx3 = f.subs(x, x3)
    print('f(x3) = %.4f'% fx3)
    I = (b-a)/8*(fx0+ 3*fx1 + 3*fx2 + fx3)
    print("Integral using Simpson's 3/8 rule from %.1f to %.1f = %.4f" % (a, b, I))
    return I

def simp13(f, a, b, n = 2):
    h = (b-a)/n
    print('h = %.4f'% h)
    x0 = a
    print('x0 = %.4f'% x0)
    x1 = a + h
    print('x1 = %.4f'% x1)
    x2 = a + 2*h
    print('x2 = %.4f'% x2)
    fx0 = f.subs(x, x0)
    print('f(x0) = %.4f'% fx0)
    fx1 = f.subs(x, x1)
    print('f(x1) = %.4f'% fx1)
    fx2 = f.subs(x, x2)
    print('f(x2) = %.4f'% fx2)
    I = (b-a)/6*(fx0+ 4*fx1 + fx2)
    print("Integral using Simpson's 1/3 rule from %.1f to %.1f = %.4f" % (a, b, I))
    return I


from src.error import Error
import numpy as np

class MeanSquaredError(Error):

    def __init__(self):
        self.out: np.ndarray = None
        self.y: np.ndarray = None

    def get_error(self,
                  out: np.ndarray,
                  y: np.ndarray):
        self.out = out
        self.y = y
        return np.average((out - y) * (out - y))

    def get_derivative(self):
        error_derivative = 2 * (self.out - self.y)
        return error_derivative

    def __dir__(self):
        return ['out', 'y']

    def __repr__(self):
        return '<MeanSquaredError>'


# -*- coding: utf-8 -*-
import numpy as np

import scipy
import scipy.linalg
import scipy.optimize
import scipy.spatial

from ..util import constants as c

def vector(x, y, z):
    """ A shortcut for creating 3D-space vectors;
    in case you need a lot of manual np.array([...]) """
    return np.array([x, y, z])

def deg2rad(deg):
    """ Convert degrees (input) to radians """
    return deg*np.pi/180.

def rad2deg(rad):
    """ convert radians (input) to degrees """
    return rad*180./np.pi

def norm(vector):
    """ a shortcut to scipy.linalg.norm() """
    return scipy.linalg.norm(vector)

def unit_vector(vector):
    """ Returns a vector of magnitude 1 with the same direction"""
    return vector / norm(vector)

def angle_between(v1, v2):
    """ Returns the angle between vectors 'v1' and 'v2', in radians:

    >>> angle_between((1, 0, 0), (0, 1, 0))
    1.5707963267948966
    >>> angle_between((1, 0, 0), (1, 0, 0))
    0.0
    >>> angle_between((1, 0, 0), (-1, 0, 0))
    3.141592653589793

    Kudos: https://stackoverflow.com/questions/2827393/
    """
    v1_u = unit_vector(v1)
    v2_u = unit_vector(v2)

    return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))

def arbitrary_rotation_matrix(axis, theta):
    """
    Return the rotation matrix associated with counterclockwise rotation about
    the given axis by theta radians.
    # Kudos to
    # https://stackoverflow.com/questions/6802577/rotation-of-3d-vector
    #import math
    #
    # axis = np.asarray(axis)
    # axis = axis / math.sqrt(np.dot(axis, axis))
    # a = math.cos(theta / 2.0)
    # b, c, d = -axis * math.sin(theta / 2.0)
    # aa, bb, cc, dd = a * a, b * b, c * c, d * d
    # bc, ad, ac, ab, bd, cd = b * c, a * d, a * c, a * b, b * d, c * d
    # return np.array([[aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac)],
    #                  [2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab)],
    #                  [2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc]])

    # Also Kudos to the guy with another answer for the same question (used here): """
    return scipy.linalg.expm(np.cross(np.eye(3), axis/norm(axis)*theta))

def arbitrary_rotation(point, axis, theta, origin):
    """ Rotate a point around any axis given by axis by angle theta [radians] """
    point = np.asarray(point)
    axis = np.asarray(axis)
    origin = np.asarray(origin)

    rotated_point = np.dot(arbitrary_rotation_matrix(axis, theta), point - origin)
    return rotated_point + origin

def rotate(point, angle, axis='x'):
    """ Rotate a point around a given axis by specified angle """
    if axis == 'y':
        axis = vector(0, 1, 0)
    elif axis == 'z':
        axis = vector(0, 0, 1)
    elif axis == 'x':
        axis = vector(1, 0, 0)
    else:
        raise ValueError("Rotation axis should be either 'x', 'y', or 'z' ")

    return arbitrary_rotation(point, axis, angle, vector(0, 0, 0))

def to_polar(point, axis='z'):
    """ Convert (x, y, z) point to (radius, angle, height);
    the axis of the new polar coordinate system can be chosen ('x' or 'z') """

    assert axis in ['x', 'z']

    if axis == 'z':
        radius = (point[0]**2 + point[1]**2)**0.5
        angle = np.arctan2(point[1], point[0])
        height = point[2]
    else: # axis == 'x'
        radius = (point[1]**2 + point[2]**2)**0.5
        angle = np.arctan2(point[2], point[1])
        height = point[0]

    return vector(radius, angle, height)

def to_cartesian(p, direction=1, axis='z'):
    """ Converts a point given in (r, theta, z) coordinates to
    cartesian coordinate system.

    optionally, axis can be aligned with either cartesian axis x* or z and
    rotation sense can be inverted with direction=-1

    *when axis is 'x': theta goes from 0 at y-axis toward z-axis

    """
    assert direction in [-1, 1]
    assert axis in ['x', 'z']

    radius = p[0]
    angle = direction*p[1]
    height = p[2]

    if axis == 'z':
        return vector(radius*np.cos(angle), radius*np.sin(angle), height)

    # axis == 'x'
    return vector( height, radius*np.cos(angle), radius*np.sin(angle) )

def lin_map(x, x_min, x_max, out_min, out_max, limit=False):
    """ map x that should take values from x_min to x_max
        to values out_min to out_max"""
    r = float(x - x_min) * float(out_max - out_min) / \
        float(x_max - x_min) + float(out_min)

    if limit:
        return sorted([out_min, r, out_max])[1]
    else:
        return r

def arc_length_3point(A, B, C):
    """ Returns length of arc defined by 3 points, A, B and C; B is the point in between """
    ### Meticulously transcribed from
    # https://develop.openfoam.com/Development/openfoam/-/blob/master/src/mesh/blockMesh/blockEdges/arcEdge/arcEdge.C
    p1 = np.asarray(A)
    p2 = np.asarray(B)
    p3 = np.asarray(C)

    a = p2 - p1
    b = p3 - p1

    # Find centre of arcEdge
    asqr = a.dot(a)
    bsqr = b.dot(b)
    adotb = a.dot(b)

    denom = asqr*bsqr - adotb*adotb
    # if norm(denom) < 1e-5:
    #     raise ValueError("Invalid arc points!")

    fact = 0.5*(bsqr - adotb)/denom

    centre = p1 + 0.5*a + fact*(np.cross(np.cross(a, b), a))

    # Position vectors from centre
    r1 = p1 - centre
    r2 = p2 - centre
    r3 = p3 - centre

    mag1 = norm(r1)
    mag3 = norm(r3)

    # The radius from r1 and from r3 will be identical
    radius = r3

    # Determine the angle
    angle = np.arccos((r1.dot(r3))/(mag1*mag3))

    # Check if the vectors define an exterior or an interior arcEdge
    if np.dot(np.cross(r1, r2), np.cross(r1, r3)) < 0:
        angle = 2*np.pi - angle

    return angle*norm(radius)

def distance_from_line(line_point_1, line_point_2, p):
    """ Returns distance from point p from line, defined by two points """
    # TODO: TEST
    axis = line_point_2 - line_point_1
    vector = p - line_point_1

    return norm(np.cross(axis, vector))/norm(axis)


import numpy as np

def l2_regularization(W, reg_strength):
    '''
    Computes L2 regularization loss on weights and its gradient

    Arguments:
      W, np array - weights
      reg_strength - float value

    Returns:
      loss, single value - l2 regularization loss
      gradient, np.array same shape as W - gradient of weight by l2 loss
    '''
    # TODO: Copy from previous assignment
    loss = reg_strength * np.sum(W**2)
    grad = 2 * reg_strength * W

    return loss, grad

def softmax(predictions):
    '''
    Computes probabilities from scores

    Arguments:
      predictions, np array, shape is either (N) or (batch_size, N) -
        classifier output

    Returns:
      probs, np array of the same shape as predictions -
        probability for every class, 0..1
    '''
    # TODO implement softmax
    # Your final implementation shouldn't have any loops
    preds = predictions.copy()

    if preds.ndim > 1:
        maxs = np.max(preds, axis=1)[:, np.newaxis]
        preds -= maxs
        exps = np.exp(preds)
        probs = exps/np.sum(exps, axis=1)[:, np.newaxis]

    else:
        preds -= np.max(preds)
        exps = np.exp(preds)
        probs = exps/np.sum(exps)

    return probs

def cross_entropy_loss(probs, target_index):
    '''
    Computes cross-entropy loss

    Arguments:
      probs, np array, shape is either (N) or (batch_size, N) -
        probabilities for every class
      target_index: np array of int, shape is (1) or (batch_size) -
        index of the true class for given sample(s)

    Returns:
      loss: single value
    '''
    # TODO implement cross-entropy
    # Your final implementation shouldn't have any loops
    if probs.ndim > 1:
        loss = -1 * np.sum(np.log(probs[np.arange(probs.shape[0]), target_index.flatten()]))
    else:
        loss = -1 * np.log(probs[target_index])

    return loss

def softmax_with_cross_entropy(predictions, target_index):
    '''
    Computes softmax and cross-entropy loss for model predictions,
    including the gradient

    Arguments:
      predictions, np array, shape is either (N) or (batch_size, N) -
        classifier output
      target_index: np array of int, shape is (1) or (batch_size) -
        index of the true class for given sample(s)

    Returns:
      loss, single value - cross-entropy loss
      dprediction, np array same shape as predictions - gradient of predictions by loss value
    '''
    # TODO copy from the previous assignment
    probs = softmax(predictions)
    loss = cross_entropy_loss(probs, target_index)
    batch_size = probs.shape[0]

    if predictions.ndim > 1:
        dprediction = probs.copy()
        dprediction[np.arange(batch_size), target_index.flatten()] -= 1

        return loss/batch_size, dprediction/batch_size
    else:
        dprediction = probs.copy()
        dprediction[target_index] -= 1

    return loss, dprediction

class Param:
    '''
    Trainable parameter of the model
    Captures both parameter value and the gradient
    '''
    def __init__(self, value):
        self.value = value
        self.clear_grad()

    def clear_grad(self):
        self.grad = np.zeros_like(self.value)

class ReLULayer:
    def __init__(self):
        pass

    def forward(self, X):
        # TODO copy from the previous assignment
        self.grad = (X > 0).astype(float)

        return np.maximum(0, X)

    def backward(self, d_out):
        # TODO copy from the previous assignment
        d_result = d_out * self.grad

        return d_result

    def params(self):
        return {}

class FullyConnectedLayer:
    def __init__(self, n_input, n_output):
        self.W = Param(0.001 * np.random.randn(n_input, n_output))
        self.B = Param(0.001 * np.random.randn(1, n_output))
        self.X = None

    def forward(self, X):
        # TODO copy from the previous assignment
        self.X = X

        return X.dot(self.W.value) + self.B.value

    def backward(self, d_out):
        # TODO copy from the previous assignment

        d_input = d_out.dot(self.W.value.T)

        self.W.grad += self.X.T.dot(d_out)
        self.B.grad += d_out.sum(axis=0)

        return d_input

    def params(self):
        return { 'W': self.W, 'B': self.B }

class ConvolutionalLayer:
    def __init__(self, in_channels, out_channels,
                 filter_size, padding):
        '''
        Initializes the layer

        Arguments:
        in_channels, int - number of input channels
        out_channels, int - number of output channels
        filter_size, int - size of the conv filter
        padding, int - number of 'pixels' to pad on each side
        '''

        self.filter_size = filter_size
        self.in_channels = in_channels
        self.out_channels = out_channels
        self.W = Param(
            np.random.randn(filter_size, filter_size,
                            in_channels, out_channels)
        )

        self.B = Param(np.zeros(out_channels))
        self.padding = padding
        self.X = None

    def forward(self, X):
        batch_size, height, width, channels = X.shape

        out_height = height - self.filter_size + 2*self.padding + 1
        out_width = width - self.filter_size + 2*self.padding + 1

        # add padding
        if self.padding > 0:
            X_padded = np.zeros((batch_size, height + 2*self.padding,
                                 width + 2*self.padding, channels))
            X_padded[:, self.padding : height + self.padding,
                        self.padding :  width + self.padding, :] = X.copy()
            X = X_padded.copy()

        self.X = X.copy()
        # TODO: Implement forward pass
        # Hint: setup variables that hold the result
        # and one x/y location at a time in the loop below

        # It's ok to use loops for going over width and height
        # but try to avoid having any other loops
        result = np.zeros((batch_size, out_height, out_width, self.out_channels))

        for y in range(out_height):
            for x in range(out_width):
                # TODO: Implement forward pass for specific location
                X_temp = X[:, x : x+self.filter_size, y: y+self.filter_size, :]\
                            .reshape(batch_size, self.filter_size * self.filter_size * channels)
                W_temp = self.W.value.reshape(self.filter_size * self.filter_size * channels, self.out_channels)
                result[:, x, y, :] = np.dot(X_temp, W_temp) + self.B.value

        return result

    def backward(self, d_out):
        # Hint: Forward pass was reduced to matrix multiply
        # You already know how to backprop through that
        # when you implemented FullyConnectedLayer
        # Just do it the same number of times and accumulate gradients

        batch_size, height, width, channels = self.X.shape
        _, out_height, out_width, out_channels = d_out.shape

        # TODO: Implement backward pass
        # Same as forward, setup variables of the right shape that
        # aggregate input gradient and fill them for every location
        # of the output

        # Try to avoid having any other loops here too
        d_input = np.zeros(self.X.shape)

        for y in range(out_height):
            for x in range(out_width):
                # TODO: Implement backward pass for specific location
                # Aggregate gradients for both the input and
                # the parameters (W and B)

                # gradients over input
                d_out_temp = d_out[:, x, y, :].reshape(batch_size, out_channels)
                W_temp = self.W.value.reshape(self.filter_size * self.filter_size * channels, self.out_channels)
                #print(d_out_temp.shape, W_temp.T.shape)
                res_temp = np.dot(d_out_temp, W_temp.T)\
                                .reshape(batch_size, self.filter_size, self.filter_size, channels)
                d_input[:, x : x+self.filter_size, y : y+self.filter_size, :] += res_temp

                # gradient over weights and biases
                X_temp = self.X[:, x : x+self.filter_size, y: y+self.filter_size, :]\
                                .reshape(batch_size, self.filter_size * self.filter_size * channels)
                self.W.grad += np.dot(X_temp.T, d_out_temp)\
                                .reshape(self.filter_size, self.filter_size, channels, self.out_channels)
                self.B.grad += d_out_temp.sum(axis=0)

        if self.padding > 0:
            d_input = d_input[:, self.padding : height - self.padding,
                                 self.padding :  width - self.padding, :]
        return d_input

    def params(self):
        return { 'W': self.W, 'B': self.B }

class MaxPoolingLayer:
    def __init__(self, pool_size, stride):
        '''
        Initializes the max pool

        Arguments:
        pool_size, int - area to pool
        stride, int - step size between pooling windows
        '''
        self.pool_size = pool_size
        self.stride = stride
        self.X = None
        self.max_inds = None

    def forward(self, X):
        batch_size, height, width, channels = X.shape
        # TODO: Implement maxpool forward pass
        # Hint: Similarly to Conv layer, loop on
        # output x/y dimension
        self.X = X.copy()

        out_height = int((height - self.pool_size)/self.stride + 1)
        out_width = int((width - self.pool_size)/self.stride + 1)

        result = np.zeros((batch_size, out_height, out_width, channels))

        self.max_inds = np.zeros((batch_size, out_height, out_width, channels))

        for y in range(out_height):
            for x in range(out_width):
                X_temp = X[:, x : x+self.pool_size, y: y+self.pool_size, :]\
                            .reshape(batch_size, self.pool_size * self.pool_size, channels)
                cur_max = np.max(X_temp, axis=1) # max along heght or width not channel, not batch
                # save indexies and results
                self.max_inds[:, x, y, :] = np.argmax(X_temp, axis=1)
                result[:, x, y, :] = cur_max

        return result

    def backward(self, d_out):
        # TODO: Implement maxpool backward pass
        batch_size, height, width, channels = self.X.shape
        _, out_height, out_width, out_channels = d_out.shape

        d_input = np.zeros(self.X.shape)#.reshape(batch_size, height * width, channels)

        for y in range(out_height):
            for x in range(out_width):
                # flattern output and indices
                d_out_temp = d_out[:, x, y, :].reshape(-1)
                max_inds_temp = self.max_inds[:, x, y, :].reshape(-1).astype(int)

                # prepare zeros input
                d_input_temp = np.zeros((batch_size, self.pool_size * self.pool_size, channels))

                # prepare indices for multi-indexing
                batch_inds = np.repeat(np.arange(batch_size), channels)
                channel_inds = np.array([np.arange(channels) for x in range(batch_size)]).reshape(-1)
                #print(len(batch_inds), len(max_inds_temp), len(channel_inds))
                assert batch_inds.shape == channel_inds.shape, 'b={}, ch={}'.format(batch_inds.shape, channel_inds.shape)
                assert batch_inds.shape == max_inds_temp.shape, 'b={}, inds={}'.format(batch_inds.shape, max_inds_temp.shape)

                # fill 3d tensor with values
                d_input_temp[batch_inds, max_inds_temp, channel_inds] = d_out_temp
                # reshape 3d tensor to 4d
                d_input[:, x : x+self.pool_size,
                           y : y+self.pool_size, :] += \
                    d_input_temp.reshape(batch_size, self.pool_size, self.pool_size, channels)

        return d_input

    def params(self):
        return {}

class Flattener:
    def __init__(self):
        self.X_shape = None

    def forward(self, X):
        batch_size, height, width, channels = X.shape
        self.X_shape = X.shape
        # TODO: Implement forward pass
        # Layer should return array with dimensions
        # [batch_size, hight*width*channels]
        return X.reshape(batch_size, height * width * channels)

    def backward(self, d_out):
        # TODO: Implement backward pass
        batch_size, height, width, channels = self.X_shape

        return d_out.reshape(batch_size, height, width, channels)

    def params(self):
        # No params!
        return {}


from scipy.spatial import distance
import math

def euclidean_distance(x, y):
    """
    :return: Euclidean Distance and it represents the shortest distance between two points
    """
    ed = distance.euclidean(x, y)

    formula = math.sqrt(sum((a - b) ** 2 for a, b in zip(x, y)))
    return ed

def manhattan_distance(x, y):
    """
    :return: Manhattan Distance and it represents the sum of absolute differences between points
    across all the dimensions
    """
    md = distance.cityblock(x, y)

    formula = sum(abs(a - b) for a, b in zip(x, y))
    return md

def minkowski_distance(x, y):
    """
    :return: Minkowski Distance and that is generalized form of Euclidean and Manhattan distance
    p represents the order of the norm
    q = 1 is Manhattan distance and q = 2 is Euclidean distance
    """
    p = 3
    md = distance.minkowski(x, y, p)

    formula = pow(sum(abs(a - b) ** p for a, b in zip(x, y)), (1 / p))
    return md

def cosine_similarity(x, y):
    """
    The difference between Cosine Similarity and Cosine Distance is if 2 vectors are
    perfectly the same, the similarity will be 1 (angle=0) and the distance will be 0 (1-1=0)
    """
    cs = 1 - distance.cosine(x, y)

    formula = (sum(a * b for a, b in zip(x, y))
               / ((math.sqrt(sum(a * a for a in x))) *
                  (math.sqrt(sum(b * b for b in y)))))
    return cs

def cosine_distance(x, y):
    cd = distance.cosine(x, y)

    formula = 1 - cosine_similarity(x, y)
    return cd