import numpy as np
import matplotlib.pyplot as plt

def displayPlots():
        fig = plt.figure()
        plt.plot(volt,lumi,'+')
        plt.xlabel('volt')
        plt.ylabel('lumi')
        plt.show()

        plt.figure()
        plt.plot(mA,lumi,'+')
        plt.xlabel('mA')
        plt.ylabel('lumi')
        plt.show()
        return

def regLinSimple(Y, x, n):
        # Calcul de la covariance
        covariancexY = (np.sum(x*Y)) / n-(np.mean(Y)*np.mean(x))

        # Calcul de sigma
        b = covariancexY / np.var(x)
        a = np.mean(Y) - b*np.mean(x)
        sigma = (np.sum((Y-a-b*x)**2))/n

        quadratic_error = np.sum((Y-a-b*x)**2)
        lumi_predictor = a*x+b

        print("a estimation: ",a)
        print("b estimation: ",b)
        print("sigma estimation: ",sigma)
        print("Quadratic error: ",quadratic_error)
        print("Lumi predictor: ",lumi_predictor)

        return

data = np.loadtxt('data.txt')
lumi = data[:,0]
volt = data[:,1]
mA = data[:,2]
n, = lumi.shape

print("[RLS] Luminosite ambiante en fonction de la tension à vide")
regLinSimple(lumi, volt, n)

print("[RLS] Luminosite ambiante en fonction du courant de court-circuit du capteur")
regLinSimple(lumi,mA, n)

# Parameter estimator for all the parameters
# Regression model: lum_i = Θ_0 + x_i(1) Θ_1 + x_i(2) Θ_2 + E_i
Y = lumi

X = np.matrix(np.concatenate((
        		np.ones((n,1)),
        		volt.reshape((n,1)),
        		mA.reshape((n,1))
        	), axis=1))
theta = np.multiply(np.linalg.inv(X.T*X)*X.T, Y) # (Xt*X)^(-1)*Xt*y
lumi_predictor = X*theta
quadratic_error = (Y - X*theta).T * (Y - X*theta)
print("Regression lineaire multiple de la luminosite ambiante en fonction de ces deux paramètres")
#print("Lumi predictor: ", lumi_predictor)
#print("Quadratic error: ", quadratic_error)


import numpy as np

def dX_dt(X, t=0, **params):
    animal, plant = X
    return np.array(
        [
            (params["r1"] + (params["b12"] * animal)) * plant
            - (
                (params["alpha1"] + (params["c1"] * params["b12"] * animal))
                * (plant ** 2)
            ),
            (params["r2"] + (params["b21"] * plant)) * animal
            - (
                (params["alpha2"] + (params["c2"] * params["b21"] * plant))
                * (animal ** 2)
            ),
        ]
    )

def get_fixed_points(**params):
    X_f0 = np.array([0.0, 0.0])
    K1 = params["r1"] / params["alpha1"]
    K2 = params["r2"] / params["alpha2"]
    X_f1 = np.array([K1, 0.0])
    X_f2 = np.array([0.0, K2])
    fixed_points = [X_f0]
    print(dX_dt(list(reversed(X_f1)), **params), dX_dt(list(reversed(X_f2)), **params))
    if all(dX_dt(list(reversed(X_f1)), **params) == 0):
        fixed_points.append(X_f1)
    if all(dX_dt(list(reversed(X_f2)), **params) == 0):
        fixed_points.append(X_f2)

    if params["r1"] > 0 or params["r2"] > 0:
        print(
            f"There are partial extinction points at {fixed_points[1]} and {fixed_points[2]}"
        )
    print("Trivial fixed points:", fixed_points)
    return fixed_points

def get_non_trivial_fixed_points(r1, r2, alpha1, alpha2, b12, b21, c1, c2):
    A = (c2 * b21 * alpha1) + (c1 * b12 * b21)
    B = (alpha1 * alpha2) + (c1 * b12 * r2) - (c2 * b21 * r1) - b12 * b21
    C = -r1 * alpha2 - b12 * r2

    plant_values = np.roots([A, B, C])

    animal_values = list(
        map(lambda x: (r2 + (b21 * x)) / (alpha2 + (c2 * b21 * x)), plant_values)
    )

    non_trivial_fixed_points = list(zip(plant_values, animal_values))

    print("non trivial fixed points", non_trivial_fixed_points)

    for fp in non_trivial_fixed_points:
        print(
            "d/dt close to 0",
            dX_dt(
                reversed(list(fp)),
                r1=r1,
                r2=r2,
                alpha1=alpha1,
                alpha2=alpha2,
                b12=b12,
                b21=b21,
                c1=c1,
                c2=c2,
            ),
        )

    return non_trivial_fixed_points


## Tri Diagonal Matrix Algorithm(a.k.a Thomas algorithm) solver
def TDMAsolver(a, b, c, d):
    '''
    TDMA solver, a b c d can be NumPy array type or Python list type.
    solving tridiagonal matrix
    '''
    import numpy as np
    nf = len(d) # number of equations
    ac, bc, cc, dc = map(np.array, (a, b, c, d)) # copy arrays
    for it in range(1, nf):
        mc = ac[it-1]/bc[it-1]
        bc[it] = bc[it] - mc*cc[it-1]
        dc[it] = dc[it] - mc*dc[it-1]

    xc = bc
    xc[-1] = dc[-1]/bc[-1]

    for il in range(nf-2, -1, -1):
        xc[il] = (dc[il]-cc[il]*xc[il+1])/bc[il]

    return(xc)


import numpy as np
trainDivide = 0.8
divideNum = int(trainDivide * 50)
typeDic = { #define class
    'Iris-setosa\n': 0,
    'Iris-versicolor\n': 1,
    'Iris-virginica\n': 2
}
batch_num = 10
feature_num = 4
acc0_1, acc1_2, acc0_2 = 0, 0, 0
acc0_1_feature, acc1_2_feature, acc0_2_feature = [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]

def loadData(path):
    allData = [[], [], []]  # 0: setosa, 1: versicolor, 2:virginica
    with open(path) as f:
        for line in f:
            lineContent = line.split(',')
            irisType = lineContent[4]
            index = typeDic.get(irisType)
            lineContent[4] = index
            allData[index].append(lineContent)
    f.close()
    return allData

def preprocess(class1A, class2A):
    train_x, train_y, test_x, test_y = [], [], [], []
    indices = np.arange(len(class1A))
    np.random.shuffle(indices)
    indices1 = indices[0: divideNum]
    indices2 = indices[divideNum: 50]
    for index1 in indices1:
        train_x.append(class1A[index1][0:4])
        train_y.append(class1A[index1][4])
    for index1 in indices1:
        train_x.append(class2A[index1][0:4])
        train_y.append(class2A[index1][4])
    for index2 in indices2:
        test_x.append(class1A[index2][0:4])
        test_y.append(class1A[index2][4])
    for index2 in indices2:
        test_x.append(class2A[index2][0:4])
        test_y.append(class2A[index2][4])
    return np.array(train_x), np.array(train_y), np.array(test_x), np.array(test_y)

def getMeanAndVar(arr, index):
    arr_sum = 0
    for a in arr:
        arr_sum += float(a[index])
    mean = arr_sum / len(arr)
    dsum = 0
    for a in arr:
        d = float(a[index]) - mean
        dsum += (d * d)
    var = dsum / len(arr)
    return mean, var

def gaussion(x, mean, var):
    x = float(x)
    return np.exp(-(x - mean) * (x - mean) / 2 / var / var) / var

def classify(class1, class2, feature):
    mean1, var1 = getMeanAndVar(train_x[0: divideNum], feature)
    mean2, var2 = getMeanAndVar(train_x[divideNum: 2 * divideNum], feature)
    pre_y = []
    for a in test_x:
        prob1 = gaussion(a[feature], mean1, var1)
        prob2 = gaussion(a[feature], mean2, var2)
        if prob1 > prob2:
            pre_y.append(class1)
        else:
            pre_y.append(class2)
    return pre_y

def classify_all(class1, class2):
    mean11, var11 = getMeanAndVar(train_x[0: divideNum], 0)
    mean12, var12 = getMeanAndVar(train_x[0: divideNum], 1)
    mean13, var13 = getMeanAndVar(train_x[0: divideNum], 2)
    mean14, var14 = getMeanAndVar(train_x[0: divideNum], 3)
    mean21, var21 = getMeanAndVar(train_x[divideNum: 2 * divideNum], 0)
    mean22, var22 = getMeanAndVar(train_x[divideNum: 2 * divideNum], 1)
    mean23, var23 = getMeanAndVar(train_x[divideNum: 2 * divideNum], 2)
    mean24, var24 = getMeanAndVar(train_x[divideNum: 2 * divideNum], 3)
    pre_y = []
    for a in test_x:
        prob11 = gaussion(a[0], mean11, var11)
        prob12 = gaussion(a[1], mean12, var12)
        prob13 = gaussion(a[2], mean13, var13)
        prob14 = gaussion(a[3], mean14, var14)
        prob21 = gaussion(a[0], mean21, var21)
        prob22 = gaussion(a[1], mean22, var22)
        prob23 = gaussion(a[2], mean23, var23)
        prob24 = gaussion(a[3], mean24, var24)
        prob1 = prob11 * prob12 * prob13 * prob14
        prob2 = prob21 * prob22 * prob23 * prob24
        if prob1 > prob2:
            pre_y.append(class1)
        else:
            pre_y.append(class2)
    return pre_y

def getAccuracy(test_y, pre_y):
    count = 0
    l = len(test_y)
    for i in range(l):
        if test_y[i] == pre_y[i]:
            count += 1
    return float(count) / len(test_y)

irisData = loadData("./../iris_data_set/iris.csv")

for i in range(batch_num):
    train_x, train_y, test_x, test_y = preprocess(irisData[0], irisData[1])
    for j in range(feature_num):
        acc0_1_feature[j] += getAccuracy(test_y, classify(0, 1, j))
    acc0_1 += getAccuracy(test_y, classify_all(0, 1))

    train_x, train_y, test_x, test_y = preprocess(irisData[1], irisData[2])
    for j in range(feature_num):
        acc1_2_feature[j] += getAccuracy(test_y, classify(1, 2, j))
    acc1_2 += getAccuracy(test_y, classify_all(1, 2))

    train_x, train_y, test_x, test_y = preprocess(irisData[0], irisData[2])
    for j in range(feature_num):
        acc0_2_feature[j] += getAccuracy(test_y, classify(0, 2, j))
    acc0_2 += getAccuracy(test_y, classify_all(0, 2))

print "accuracy in 4-d between class 0 and 1: ", acc0_1 / batch_num
print "accuracy in 4-d between class 1 and 2: ", acc1_2 / batch_num
print "accuracy in 4-d between class 0 and 2: ", acc0_2 / batch_num
print "accuracy in each dimension between class 0 and 1: ", acc0_1_feature[0] / batch_num, acc0_1_feature[1] / batch_num, acc0_1_feature[2] / batch_num, acc0_1_feature[3] / batch_num
print "accuracy in each dimension between class 1 and 2: ", acc1_2_feature[0] / batch_num, acc1_2_feature[1] / batch_num, acc1_2_feature[2] / batch_num, acc1_2_feature[3] / batch_num
print "accuracy in each dimension between class 0 and 2: ", acc0_2_feature[0] / batch_num, acc0_2_feature[1] / batch_num, acc0_2_feature[2] / batch_num, acc0_2_feature[3] / batch_num


import numpy as np
import matplotlib.pyplot as plt
import numpy.linalg

def cov_matrix(data):
    n_dim = np.shape(data)[1]
    n_points = np.shape(data)[0]
    cov = np.ones([n_dim, n_dim])
    for i in range(n_dim):
        for j in range(n_dim):
            mean_i = np.mean(data[:,i])
            mean_j = np.mean(data[:,j])
            cov[i,j] = np.sum((data[:,i]-mean_i) * (data[:,j]-mean_j)) / (n_points -1)
    return cov

dataA = np.loadtxt('data_A.txt')
covA = cov_matrix(dataA)
valsA, vecsA = numpy.linalg.eig(covA)

plt.figure(figsize=(6,5))
plt.scatter(dataA[:,0], dataA[:,1], alpha=0.3)

x = np.linspace(-10,10, 10)
#primer autovector
m = vecsA[1,0]/vecsA[0,0]
plt.plot(x, m*x, label='Primer Autovector')

#segundo autovector
m = vecsA[1,1]/vecsA[0,1]
plt.plot(x, m*x, label='Segundo Autovector')

plt.axis('equal')
plt.xlim([-7,7])
plt.ylim([-7,7])
plt.xlabel("$X_1$")
plt.ylabel("$X_2$")
plt.title("Data A")
plt.legend()
plt.savefig('dataA_ejes.png')

dataB = np.loadtxt('data_B.txt')
covB = cov_matrix(dataB)
valsB, vecsB = numpy.linalg.eig(covB)

plt.figure(figsize=(6,5))
plt.scatter(dataB[:,0], dataB[:,1], alpha=0.3)

x = np.linspace(-10,10, 10)
#primer autovector
m = vecsB[1,0]/vecsB[0,0]
plt.plot(x, m*x, label='Primer Autovector')

#segundo autovector
m = vecsB[1,1]/vecsB[0,1]
plt.plot(x, m*x, label='Segundo Autovector')

plt.axis('equal')
plt.xlim([-7,7])
plt.ylim([-7,7])
plt.xlabel("$X_1$")
plt.ylabel("$X_2$")
plt.title("Data B")
plt.legend()
plt.savefig('dataB_ejes.png')


from scipy.integrate import odeint
import numpy as np

def ec(y, t, m, g, r):
	theta, thetap = y
	thetapp = g*np.sin(theta)/r
	return [thetap, thetapp]

def soledo(y0, t, m, g, r):
	'''
	Resuelve la ecuacion diferencial de la pelotita y devuelve
	una matriz de len(t)x2 con theta y theta punto para cada
	instante t
	y0: vector de condiciones iniciales: [theta0, thetapunto0]
	t: vector con el tiempo (se recomienda numpy.linspace)
	m: masa de la pelotita
	g: aceleracion de gravedad
	r: radio de la semiesfera

	Ejemplo: theta = soledo([0.001,0],
							np.linspace(0,10,101),
							1,
							9.8,
							1)
	Nota: si las condiciones iniciales son [0,0], la pelotita
	no se va a mover, quedara en reposo sobre la punta de la
	semiesfera
	'''
	return odeint(ec, y0, t, args=(m,g,r))


#!/usr/bin/python3

import numpy as np
import pandas as pd
import scipy.integrate

from pandas import DataFrame

"""
    Z-table values are calculated using the cumulative distribution function of a standard normal distribution with mean of zero and standard deviation of one.
"""

def ztable() -> pd.DataFrame:
    """
        Ф(z) = (1/sqrt(2*pi))∫e^{-(t^2)/2}dt from -infinity to z
    """

    ztable = [[0 for i in range(10)] for j in range(41)]

    phi = lambda x: (np.exp(-1*(x**2)/2))/(np.sqrt(2*np.pi))
    for i in range(41):
        for j in range(10):
            z = i/10+j/100
            ztable[i][j] = scipy.integrate.quad(phi, -1*np.inf, z)[0]

    return pd.DataFrame(ztable, index=np.arange(0,4.1,0.1), columns=np.arange(0,0.1,0.01))


import numpy as np
from datetime import datetime

## Sun direction depending on time and place
# As described in Appendix D of "ME 4131 Thermal Environmental Engineering Laboratory Manual"

def sun_declination(time):
    day_of_year = time.timetuple().tm_yday
    return 23.45 * np.sin(np.radians((360/365)*(284+day_of_year)))

def equation_of_time(time):
    day_of_year = time.timetuple().tm_yday
    b = np.radians(360*(day_of_year-81)/364)
    return 0.165*np.sin(2*b) - 0.126*np.cos(b) - 0.025*np.sin(b)

def sun_direction(lat, lon, time, time_diff, summertime_shift = 0):
    lon_std = time_diff * 15
    clock_time       = time.hour + time.minute/60
    local_solar_time = clock_time + (1/15)*(lon - lon_std) + equation_of_time(time) - summertime_shift
    hour_angle = 15*(local_solar_time - 12)

    l = np.radians(lat)
    h = np.radians(hour_angle)
    d = np.radians(sun_declination(time))

    altitude = np.arcsin(np.cos(l) * np.cos(h) * np.cos(d) + np.sin(l) * np.sin(d))
    azimuth  = np.arccos((np.cos(d) * np.sin(l) * np.cos(h) - np.sin(d) * np.cos(l)) / np.cos(altitude))

    if h < 0:
        return (altitude,  azimuth)
    else:
        return (altitude, -azimuth)


import numpy as np
def sigmoid(x):
    return 1 / (1 + np.exp(-x))
# derivative of sigmoid
# sigmoid(y) * (1.0 - sigmoid(y))
# the way we use this y is already sigmoided
def dsigmoid(y):
    return y * (1.0 - y)

class NeuralNetwork(object):
    def __init__(self, input, hidden, output):
        """
        :param input: number of input neurons
        :param hidden: number of hidden neurons
        :param output: number of output neurons
        """
        self.input = input + 1 # add 1 for bias node
        self.hidden = hidden
        self.output = output
        # set up array of 1s for activations
        self.ai = [1.0] * self.input
        self.ah = [1.0] * self.hidden
        self.ao = [1.0] * self.output
        # create randomized weights
        self.wi = np.random.randn(self.input, self.hidden)
        self.wo = np.random.randn(self.hidden, self.output)
        # create arrays of 0 for changes
        self.ci = np.zeros((self.input, self.hidden))
        self.co = np.zeros((self.hidden, self.output))

    def feedForward(self, inputs):
        if len(inputs) != self.input-1:
            print(len(inputs))
            print(self.input-1)
            raise ValueError('Wrong number of inputs you silly goose!')
        # input activations
        for i in range(self.input -1): # -1 is to avoid the bias
            self.ai[i] = inputs[i]
        # hidden activations
        for j in range(self.hidden):
            sum = 0.0
            for i in range(self.input):
                sum += self.ai[i] * self.wi[i][j]
            self.ah[j] = sigmoid(sum)
        # output activations
        for k in range(self.output):
            sum = 0.0
            for j in range(self.hidden):
                sum += self.ah[j] * self.wo[j][k]
            self.ao[k] = sigmoid(sum)
        return self.ao[:]

    def backPropagate(self, targets, N):
        """
        :param targets: y values
        :param N: learning rate
        :return: updated weights and current error
        """
        if len(targets) != self.output:
            raise ValueError('Wrong number of targets you silly goose!')
        # calculate error terms for output
        # the delta tell you which direction to change the weights
        output_deltas = [0.0] * self.output
        for k in range(self.output):
            error = -(targets[k] - self.ao[k])
            output_deltas[k] = dsigmoid(self.ao[k]) * error
        # calculate error terms for hidden
        # delta tells you which direction to change the weights
        hidden_deltas = [0.0] * self.hidden
        for j in range(self.hidden):
            error = 0.0
            for k in range(self.output):
                error += output_deltas[k] * self.wo[j][k]
            hidden_deltas[j] = dsigmoid(self.ah[j]) * error
        # update the weights connecting hidden to output
        for j in range(self.hidden):
            for k in range(self.output):
                change = output_deltas[k] * self.ah[j]
                self.wo[j][k] -= N * change + self.co[j][k]
                self.co[j][k] = change
        # update the weights connecting input to hidden
        for i in range(self.input):
            for j in range(self.hidden):
                change = hidden_deltas[j] * self.ai[i]
                self.wi[i][j] -= N * change + self.ci[i][j]
                self.ci[i][j] = change
        # calculate error
        error = 0.0
        for k in range(len(targets)):
            error += 0.5 * (targets[k] - self.ao[k]) ** 2
        return error
    def train(self, patterns, iterations = 300, N = 0.0002):
        # N: learning rate
        for i in range(iterations):
            error = 0.0
            for p in patterns:
                inputs = p[0]
                targets = p[1]
                self.feedForward(inputs)
                error = self.backPropagate(targets, N)
            if i % 500 == 0:
                print('error %-.5f' % error)

    def predict(self, X):
        """
        return list of predictions after training algorithm
        """
        predictions = []
        for p in X:
            predictions.append(self.feedForward(p))
        return predictions

#use this network for a simple xor gate learning to check if its working correctly
x=NeuralNetwork(2,3,1)

y1=np.random.randint(2, size=(1000))
y2=np.random.randint(2, size=(1000))
y3=((y1*y2+(1-y1)*(1-y2))>0)*1
y=[[[y1[i],y2[i]],[y3[i]]] for i in range(0,len(y1))]

x.train(y)

#prints:
#error 0.04061
#error 0.00000
#error 0.00000
#error 0.00000
#error 0.00000
#error 0.00000

x.predict([[0,0],[1,0],[0,1],[1,1]])

#prints :  [[0.99897432819862875],[0.0011970634705067288],[0.00095282614025801119],[0.99911072794361244]]

#XOR Gate is larnt.. similarly ... on the digit dataset taken from sklearn it fits with 97% validation accuracy.. so its working great :)


"""
Collection of Jax activation functions, wrapped to fit Ivy syntax and signature.
"""

# global
import jax
import jax.numpy as jnp

# local
from ivy.functional.backends.jax import JaxArray

def relu(x: JaxArray)\
        -> JaxArray:
    return jnp.maximum(x, 0)

leaky_relu = lambda x, alpha=0.2: jnp.where(x > 0, x, x * alpha)
gelu = jax.nn.gelu
tanh = jnp.tanh
sigmoid = lambda x: 1 / (1 + jnp.exp(-x))

def softmax(x, axis=-1):
    exp_x = jnp.exp(x)
    return exp_x / jnp.sum(exp_x, axis, keepdims=True)

softplus = lambda x: jnp.log(jnp.exp(x) + 1)


#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt

x = np.arange(0, 28651, 5730)
r = np.log(0.5)
t = 5730
y = np.exp((r / t) * x)

fig, ax = plt.subplots()
plt.plot(x, y)
plt.yscale('log')
plt.xlim(0, 28500)
ax.set_title("Exponential Decay of C-14")
plt.xlabel('Time (years)')
plt.ylabel('Fraction Remaining')
plt.show()


'''

     ####  ##    ######
    ##     ##      ##
    ##     ##      ##
    ##     ##      ##
     ####  #####   ##    (Maciej Workiewicz)

This simple simulation helps to develop intuition for the Central Limit
Theorem.

If you ever wondered how does the CLT really work, but didn't find the
mathematical proof enough, then you can try this Python script and see for
yourself how does the distribution of the sample of means change in response
to the change in the size of each sample. You can also vary the number of
the samples.

It is best to set the number of samples to a large number to get a better
representation of the distribution of sample means.

ver 1.1 (2014) Maciej Workiewicz
'''
import numpy as np
import random
import matplotlib.pyplot as plt
from scipy.stats import norm

print ('''
Choose a distribution you want to sample from:
    0 - Uniform (0, 1)
    1 - Chi-square (df = 1)
    2 - Gamma (2, 2)
    3 - Laplace (1, 1)
    4 - Binomial (1, 0.1)
    5 - Poisson (5)
    ''')

a1 = int(raw_input('Distribution: '))
ns = int(raw_input('Number of samples: '))  # number of samples
ss = int(raw_input('Sample size (max = 1,000,000): '))  # sample size

'''
Population is set to 1 million

Feel free to play and change the values of the distributions or add
a new one.
'''

if a1 == 0:
    Population = np.random.rand(1000000)  # a vector
    d_name = 'Uniform U(0,1), n=1,000,000'
elif a1 == 1:
    Population = np.random.chisquare(1, 1000000)
    d_name = 'Chisquare (df=1), n=1,000,000)'
elif a1 == 2:
    Population = np.random.gamma(2, 2, 1000000)
    d_name = 'Gamma (2, 2), n=1,000,000)'
elif a1 == 3:
    Population = np.random.laplace(1, 1, 1000000)
    d_name = 'Laplace (1, 1), n=1,000,000)'
elif a1 == 4:
    Population = np.random.negative_binomial(1, 0.1, 1000000)
    d_name = 'Binomial (1, 0.1), n=1,000,000)'
elif a1 == 5:
    Population = np.random.poisson(5, 1000000)
    d_name = 'Poisson (5), n=1,000,000)'

Sample_means = np.zeros(ns)

for b1 in np.arange(ns):  # sample from population
    Sample = random.sample(Population, ss)
    s_mean = np.mean(Sample)
    Sample_means[b1] = s_mean

s_s_mean = np.mean(Sample_means)

'''
Now we will plot a histogram of the population to see how does the original
density function look like and plot the histogram of the distribution of the
sample means.

I also fitted a normal distribution to show how should the distribution
of the sample means look like if the sample size went to infinity.
'''

plt.figure(1, facecolor='white', figsize=(8.2, 11.2))
plt.suptitle('Central Limit Theorem: '+d_name, size=14)
plt.subplots_adjust(hspace=.5)
plt.subplot(211)
plt.hist(Population, bins=20, rwidth=0.8, alpha=0.6)
plt.title('Histogram of the population, size=1,000,000', size=11)

plt.subplot(212)
x = np.arange(0, 2+s_s_mean, 0.01)
plt.hist(Sample_means, bins=20, normed=1, rwidth=0.8, alpha=0.6)
plt.plot(x, norm.pdf(x, *norm.fit(Sample_means)), color='green')
plt.title('Histogram of the sample means, sample size = ' + str(ss) +
          ', number of samples = ' + str(ns), size=11)

plt.show()


from scipy import signal
import matplotlib.pyplot as plt
import numpy as np

#http://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.freqz.html#scipy.signal.freqz
#b = signal.firwin(80, 0.5, window=('kaiser', 8))

#-------------------------Low Pass filter to sensor data----------------------
#sp = 0.5  # Smoothing Parameter recommended values: 0.5>=factor>=0.02
#Low pass filer from Android Sensor programming
#b = np.array([sp, (1-sp)])
#a =1

#Hanning filter
#b = np.array([0.25, 0.5, 0.25])
#a =1

#High Pass filter Android Sensor book
#b = np.array([sp, -sp])
#a = np.array([1, -sp])

#Moving average filer
k = 25 #circular buffer length
b = np.ones(k)/k
a = 1

w, h = signal.freqz(b, a)  # compute the frequency response of a digital filter

fig_lpf = plt.figure()
plt.title('Digital filter frequency response')
ax1_lpf = fig_lpf.add_subplot(111)

plt.semilogy(w, np.abs(h), 'b')
plt.ylabel('Amplitude (dB)', color='b')
plt.xlabel('Frequency (rad/sample)')

ax2_lpf = ax1_lpf.twinx()
#angles_lpf = np.unwrap(np.angle(h)) # angle of the complex argument in radians
angles_lpf = np.rad2deg(np.unwrap(np.angle(h)))  # angle of the complex argument in degrees
plt.plot(w, angles_lpf, 'g')
plt.ylabel('Angle (Degrees)', color='g')
plt.grid()
plt.axis('tight')
plt.show()
#TODO: in the same figure, plot diferent values of factor (using legend of course)


import math

try:
    from ulab import numpy as np
except ImportError:
    import numpy as np

p = [1, 1, 1, 0]
x = [0, 1, 2, 3, 4]
result = np.polyval(p, x)
ref_result = np.array([0, 3, 14, 39, 84])
for i in range(len(x)):
    print(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9))

a = np.array(x)
result = np.polyval(p, a)
ref_result = np.array([0, 3, 14, 39, 84])
for i in range(len(x)):
    print(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9))

# linear fit
x = np.linspace(-10, 10, 20)
y = 1.5*x + 3
result = np.polyfit(x, y, 1)
ref_result = np.array([ 1.5, 3.0])
for i in range(2):
    print(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9))

# 2nd degree fit
x = np.linspace(-10, 10, 20)
y = x*x*2.5 - x*0.5 + 1.2
result = np.polyfit(x, y, 2)
ref_result = np.array([2.5,  -0.5, 1.2])
for i in range(3):
    print(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9))

# 3rd degree fit
x = np.linspace(-10, 10, 20)
y = x*x*x*1.255 + x*x*1.0 - x*0.75 + 0.0
result = np.polyfit(x, y, 3)
ref_result = np.array([1.255, 1.0, -0.75, 0.0])
for i in range(4):
    print(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9))

# 4th degree fit
x = np.linspace(-10, 10, 20)
y = x*x*x*x + x*x*x*1.255 + x*x*1.0 - x*0.75 + 0.0
result = np.polyfit(x, y, 4)
ref_result = np.array([1.0, 1.255, 1.0, -0.75, 0.0])
for i in range(5):
    print(math.isclose(result[i], ref_result[i], rel_tol=1E-9, abs_tol=1E-9))


"""  Developed by Bayram Baris Sari
*   E-mail: bayrambariss@gmail.com
*   Tel No: +90 539 593 7501
*
*   This is an implementation of Principal Component Analysis.
*
*   It decreases 64 dimensions to 2 dimensions. Then, it finds the 2 biggest eigenvector matrices.
"""

import numpy as np
import matplotlib.pyplot as plt
import os

DATA_PATH = os.path.join(os.path.dirname(__file__), "data.txt")

def read_and_split_data():
    with open(DATA_PATH, 'r') as file:
        data = [line.split(',') for line in file.read().split("\n") if line]

    number_of_rows = len(data)
    data = np.array(data)
    x, y = data[:, :-1], data[:, -1]
    x, y = x.astype(np.float32), y.astype(np.int)
    return x, y, number_of_rows

def sort_eigenvectors(eigenvalues, eigenvectors):
    # Descending sort of eigenvectors, according to their eigenvalues
    index = eigenvalues.argsort()[::-1]
    eigenvalues = eigenvalues[index]
    eigenvectors = eigenvectors[:, index]
    return eigenvalues, eigenvectors

def plot_matrix(matrix, class_matrix, number_of_rows):
    plt.figure(figsize=(8, 4))
    # Plot the data points
    plt.plot(matrix[0, :], matrix[1, :], 'o', markersize=1, color='blue', alpha=0.3)
    # Write class labels for 200 data points that are randomly selected
    for i in range(200):
        index = np.random.randint(0, number_of_rows)
        class_ = class_matrix[index]
        plt.text(matrix[0][index], matrix[1][index], f"{class_}", fontsize=8)

    plt.xlim([40, -40])
    plt.ylim([-40, 30])
    plt.xlabel('First Eigenvector')
    plt.ylabel('Second Eigenvector')
    plt.title('Data after PCA')
    plt.show()

def main():
    x, y, number_of_rows = read_and_split_data()
    # Calculate covariance matrix
    cov = np.cov(x, rowvar=False)
    # Find eigenvalues and eigenvectors of the covariance matrix
    eigenvalue, eigenvector = np.linalg.eig(cov)
    eigenvalue, eigenvector = sort_eigenvectors(eigenvalue, eigenvector)
    # Merge 2 biggest eigenvector matrices horizontally
    matrix_w = np.hstack((eigenvector[:, 0].reshape(64, 1), eigenvector[:, 1].reshape(64, 1)))
    transformed = matrix_w.T.dot(x.T)
    # Plot the transformed data
    plot_matrix(transformed, y, number_of_rows)

if __name__ == "__main__":
    main()


import pandas as pd
import numpy as np
import math as math
import warnings

warnings.filterwarnings("ignore")

#loading dataset
train = pd.read_csv(".Dataset/train.csv")
test = pd.read_csv(".Dataset/test.csv")

#dividing into classes
species_group = train.groupby(['4'])

class1 = species_group.get_group('Iris-setosa')
class2 = species_group.get_group('Iris-versicolor')
class3 = species_group.get_group('Iris-virginica')

class1.drop(['4'], inplace=True, axis=1)
class2.drop(['4'], inplace=True, axis=1)
class3.drop(['4'], inplace=True, axis=1)

def find_mean(X):

    return np.array(np.mean(X))

#classifying based on covariance matrix and multivariate normal density
classes = [class1, class2, class3]
def classifier(X):
    all_prob = []
    i=0
    for class_ in classes:
        cov = np.cov(class_.values.T)
        mu = find_mean(class_)
        size = len(X)
        det = np.linalg.det(cov)
        norm_const = 1.0/(math.pow((2*np.pi), float(size)/2) * math.pow(det, 1.0/2))
        x_mu = np.matrix(X - mu)
        inv = np.linalg.inv(cov)
        result = math.pow(math.e, -0.5 * (x_mu * inv * x_mu.T))
        all_prob.append(result*norm_const)
        i=i+1
    if (all_prob.index(max(all_prob))) == 0:
        return 'Iris-setosa'
    if (all_prob.index(max(all_prob))) == 1:
        return 'Iris-versicolor'
    if (all_prob.index(max(all_prob))) == 2:
        return 'Iris-virginica'

#predicting the test set

def predict_target(feature_vectors):
    target_class_predicted = []
    for i in range(len(feature_vectors)):
        target_class_predicted.append(classifier(feature_vectors[i]))
    return target_class_predicted

correct = 0

for flag in (predict_target(test.T[:4].T.values) == test['4'].values):
    if flag:
        correct += 1
#accuracy calculation

print("Accuracy is Bayes classifier is", (correct/test.shape[0])*100)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import numpy as np
import gym
from gym import wrappers
from time import sleep

class PolicyIteration:
    def __init__(self, env, gamma=1.0):
        self.env = env
        self.pi = np.random.choice(env.nA, env.nS)
        self.v = np.zeros(env.nS)
        self.gamma = gamma

    def policy_evaluation(self):
        eps = 1e-10
        while True:
            v_pre = np.copy(self.v)

            for s in range(self.env.nS):
                a = self.pi[s]
                self.v[s] = sum([p * (r + self.gamma * v_pre[s_]) for p, s_, r, _ in self.env.P[s][a]])
            if (np.sum(np.fabs(v_pre - self.v)) <= eps):
                break

        return self.v

    def policy_improvement(self):
        pi = np.zeros(self.env.nS)
        for s in range(self.env.nS):
            q_sa = np.zeros(self.env.nA)
            for a in range(self.env.nA):
                q_sa[a] = sum([p * (r + self.gamma * self.v[s_]) for p, s_, r, _ in self.env.P[s][a]])

            pi[s] = np.argmax(q_sa)

        return pi

    def iterate(self):
        n = 200000
        eps = 1e-20
        for i in range(n):
            self.policy_evaluation()
            pi_new = self.policy_improvement()

            if (np.all(self.pi == pi_new)):
                print('Policy-Iteration converged at step %d.' %(i+1))
                break

            self.pi = pi_new

        return self.pi

    def run_episode(self, render = False, key = False, wait = 0.0):
        obs = self.env.reset()
        total_reward = 0
        step_idx = 0
        while True:
            if render:
                self.env.render()
                if key:
                    temp_key = input('')
                elif wait > 0.0:
                    sleep(wait)

            obs, reward, done , _ = self.env.step(int(self.pi[obs]))
            total_reward += (self.gamma ** step_idx * reward)
            step_idx += 1
            if done:
                break
        return total_reward

if __name__ == '__main__':
    env = gym.make("FrozenLake8x8-v0")
    env = env.unwrapped
    gamma = 0.8
    policy_iteration = PolicyIteration(env, gamma)

    # random policy
    k = input('run 1 episode with ramdom policy?')
    score = policy_iteration.run_episode(render=True, key=False, wait=0.2)
    print('score(random policy) = ', score)

    # policy iteration
    policy_iteration.iterate()

    # 1 episode
    k = input('run 1 episode with policy iteration?')
    score = policy_iteration.run_episode(render=True, key=False, wait=0.2)
    print('score(policy iteration, 1 episode) = ', score)

    # multiple episodes
    k = input('run 100 episodes with policy iteration?')
    scores = [policy_iteration.run_episode() for _ in range(100)]
    score = np.mean(scores)
    print('Average score(policy iteration, 100 episodes) = ', score)


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import os
from scipy.stats import multivariate_normal
from polyDataMake import polyDataMake
from sklearn.linear_model import Ridge
from utils.util import save_fig

polydeg = 2 #Degree of design matrix
alph = 0.001 #Alpha of ridge regression
NFuncSamples = 10 #Number of sample coefficients to draw and use for prediction
vis = 1 #Transparency of plotted lines - in case we wish to plot a bunch.

np.random.seed(seed=1)

#Generate data
xtrain, ytrain, xtest, ytestNoisefree, ytest, sigma2 =  polyDataMake(sampling = 'sparse', deg = 2)

def polyBasis(x, deg):
    #Expands a vector to a polynomial design matrix: from a constant to the deg-power
    return(np.column_stack([x**deg for deg in range(0, deg+1)]))

def MakePlot(ypreds, SaveN, Title, lowerb = None, upperb = None):
    #Function for creating and saving plots
    fig, ax = plt.subplots()
    ax.scatter(xtrain, ytrain, s=140, facecolors='none', edgecolors='r', label='training data')
    plt.ylim([-10,80])
    plt.xlim([-8,8])
    Errlogi = lowerb is not None or upperb is not None #Determines where we will be plotting error bars as well
    if Errlogi:
        errspacing = [int(round(s)) for s in np.linspace(0,xtest.shape[0]-1,30)]
        ax.errorbar(xtest[errspacing], ypreds[errspacing,0], yerr=[lowerb[errspacing],upperb[errspacing]])
    for j in range(ypreds.shape[1]):
        ax.plot(xtest,ypreds[:,j],color='k', linewidth=2.0, label='prediction', alpha = vis)
    if Errlogi:
        plt.legend(loc=2)
    plt.title(Title)
    save_fig(SaveN +'.pdf')

xtrainp = polyBasis(xtrain,polydeg)
xtestp = polyBasis(xtest,polydeg)

#Declare and fit linear regression model
LinR = Ridge(alpha=alph,fit_intercept=False)
LinR.fit(xtrainp,ytrain)

#Determine coefficient distribution
wmle = LinR.coef_.reshape(-1,) #Mean of coefficients
wcov = sigma2 * np.linalg.inv(np.diag([alph]*(polydeg+1)) + xtrainp.T.dot(xtrainp)) #Covariance of coefficients
CoefPostDist = multivariate_normal(mean = wmle, cov = wcov)
Samples = CoefPostDist.rvs(NFuncSamples)

#Sample predictions according to samples of coefficients
SamplePreds = xtestp.dot(Samples.T)

ypredmle = LinR.predict(xtestp) #MLE prediction
noisemle = np.var(ytrain - LinR.predict(xtrainp),ddof=(polydeg + 1)) #MLE noise estimation

#plot a
noisevec = np.array([np.sqrt(noisemle)] * ypredmle.shape[0])
MakePlot(ypredmle, 'linregPostPredPlugin', 'plugin approximation (MLE)', noisevec, noisevec)

#plot b
postnoise = np.array([np.sqrt(sigma2 + xtestp[i,:].T.dot(wcov.dot(xtestp[i,:]))) for i in range(xtestp.shape[0])])
MakePlot(ypredmle, 'linregPostPredBayes', 'Posterior predictive (known variance)', postnoise, postnoise)

#plot c
MakePlot(ypredmle, 'linregPostPredSamplesPlugin', 'functions sampled from plugin approximation to posterior')

#plot d
MakePlot(SamplePreds, 'linregPostPredSamples', 'functions sampled from posterior')

plt.show()


import math
import re

import numpy as np
import pandas as pd

from lhorizon.lhorizon_utils import (
    hats,
    is_it,
    sph2cart,
    hunt_csv,
    snorm,
    listify,
)

rng = np.random.default_rng()

def test_hats_1():
    length = rng.integers(2, 1000)
    vector = np.ones(length)
    normed = hats(vector)
    assert math.isclose(normed.sum(), math.sqrt(length))

def test_hats_2():
    raining = pd.DataFrame(
        {"cats": rng.integers(1, 100, 100), "dogs": rng.integers(1, 10, 100)}
    )
    norm_raining = hats(raining)
    test_row = norm_raining.iloc[49]
    assert math.isclose(test_row["cats"] ** 2 + test_row["dogs"] ** 2, 1)

def test_is_it():
    this_range = range(100)
    is_rangelike = is_it(range, list, tuple)
    assert is_rangelike(this_range)
    assert is_rangelike(list(this_range))
    assert not is_rangelike("asdfasdfasdf")

def test_sph2cart():
    up = sph2cart(90, 90, 1)
    assert math.isclose(up[0], 0, abs_tol=1e-9)
    assert math.isclose(up[1], 0, abs_tol=1e-9)
    assert math.isclose(up[2], 1, abs_tol=1e-9)

def test_hunt_csv():
    raining = re.compile(r"(?<=cats).*(?=dogs)")
    some_csv = "catssomething,something_else,1,2dogs"
    assert hunt_csv(raining, some_csv) == [
        "something",
        "something_else",
        "1",
        "2",
    ]

def test_snorm():
    thing = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    normed = snorm(thing, 0, 1, 2, 9)
    assert math.isclose(normed[1], 0)
    assert math.isclose(normed[8], 1)

def test_listify():
    assert isinstance(listify("asdfasdf"), list)
    assert isinstance(listify([1, 2, 3, 4]), list)
    assert isinstance(listify(1), list)
    assert isinstance(listify(map(sum, [(1, 2, 3), (1, 2, 3)])), list)


# polar method for random normal distributed numbers

import numpy as np
import matplotlib.pyplot as plt

def randn(mean, std):
    while True:
        u = np.random.uniform(-1.0, 1)
        v = np.random.uniform(-1.0, 1)
        s = u**2 + v**2
        if s < 1.0 and s != 0.0:
            break
    s = np.sqrt(-2 * np.log(s) / s)
    return mean + std * u * s

def gaussian(x, mean, std):
    return 1 / np.sqrt(2*np.pi * std**2) * np.exp(- (x - mean)**2 / (2 * std**2))

if __name__ == "__main__":
    m = 0
    s = 1
    samples = [randn(m, s) for _ in range(1000)]
    x = np.linspace(np.min(samples), np.max(samples), 500)
    plt.plot(x, gaussian(x, m, s), label="gaussian")
    plt.hist(samples, histtype="step", density=True, label="samples")
    plt.xlabel("x")
    plt.ylabel("count")
    plt.title("Normal Distributed Random Numbers using the Polar Method")
    plt.legend()
    plt.show()


########## 6.3.7. Gerando recursos polinomiais ##########

    # Muitas vezes, é útil adicionar complexidade a um modelo considerando recursos não lineares dos dados de entrada. Mostramos duas possibilidades que são ambas baseadas em polinômios: a primeira usa polinômios puros, a segunda usa splines, ou seja, polinômios por partes.

##### 6.3.7.1. Recursos polinomiais

    # Um método simples e comum de usar são os recursos polinomiais, que podem obter termos de alta ordem e interação dos recursos. Ele é implementado em PolynomialFeatures:

import numpy as np
from sklearn.preprocessing import PolynomialFeatures
X = np.arange(6).reshape(3, 2)
X

poly = PolynomialFeatures(2)
poly.fit_transform(X)

    # As características de X foram transformadas de (X_1, X_2) para (1, X_1, X_2, X_1^2, X_1X_2, X_2^2).

    # Em alguns casos, apenas os termos de interação entre os recursos são necessários, e isso pode ser obtido com a configuração transaction_only=True:

X = np.arange(9).reshape(3, 3)
X

poly = PolynomialFeatures(degree=3, interaction_only=True)
poly.fit_transform(X)

    # cAs características de X foram transformadas de (X_1, X_2, X_3) para (1, X_1, X_2, X_3, X_1X_2, X_1X_3, X_2X_3, X_1X_2X_3).

    # cObserve que os recursos polinomiais são usados implicitamente em métodos de kernel (por exemplo, SVC, KernelPCA) ao usar funções de kernel polinomiais.

    # consulte Interpolação polinomial e spline para regressão Ridge usando recursos polinomiais criados.

##### 6.3.7.2. Transformador de spline

    # Outra maneira de adicionar termos não lineares em vez de polinômios puros de recursos é gerar funções de base spline para cada recurso com o SplineTransformer. Splines são polinômios por partes, parametrizados por seu grau polinomial e as posições dos nós. O SplineTransformer implementa uma base B-spline, cf. as referências abaixo.

    # Nota: O SplineTransformer trata cada recurso separadamente, ou seja, não fornece termos de interação.

    # Algumas das vantagens de splines sobre polinômios são:

        # B-splines são muito flexíveis e robustos se você mantiver um grau baixo fixo, geralmente 3, e adaptar parcimoniosamente o número de nós. Polinômios precisariam de um grau mais alto, o que leva ao próximo ponto.

        # B-splines não têm comportamento oscilatório nos limites como têm polinômios (quanto maior o grau, pior). Isso é conhecido como fenômeno de Runge.

        # B-splines fornecem boas opções para extrapolação além dos limites, ou seja, além do intervalo de valores ajustados. Dê uma olhada na extrapolação de opções.

        # B-splines geram uma matriz de recursos com uma estrutura em faixas. Para um único recurso, cada linha contém apenas grau + 1 elementos diferentes de zero, que ocorrem consecutivamente e são até positivos. Isso resulta em uma matriz com boas propriedades numéricas, e. um número de condição baixo, em nítido contraste com uma matriz de polinômios, que recebe o nome de matriz de Vandermonde. Um número de condição baixo é importante para algoritmos estáveis de modelos lineares.

    # O snippet de código a seguir mostra splines em ação:

import numpy as np
from sklearn.preprocessing import SplineTransformer
X = np.arange(5).reshape(5, 1)
X

spline = SplineTransformer(degree=2, n_knots=3)
spline.fit_transform(X)

    # À medida que o X é classificado, pode-se ver facilmente a saída da matriz em faixas. Apenas as três diagonais do meio são diferentes de zero para grau=2. Quanto maior o grau, mais sobreposição das splines.

    # Curiosamente, um SplineTransformer de grau=0 é o mesmo que KBinsDiscretizer com encode='onehot-dense' e n_bins = n_knots - 1 se knots = estratégia.

    ## Exemplos:

    ## https://scikit-learn.org/stable/auto_examples/linear_model/plot_polynomial_interpolation.html#sphx-glr-auto-examples-linear-model-plot-polynomial-interpolation-py

    ## https://scikit-learn.org/stable/auto_examples/applications/plot_cyclical_feature_engineering.html#sphx-glr-auto-examples-applications-plot-cyclical-feature-engineering-py

    ## Referências:

    ## Eilers, P., & Marx, B. (1996). Flexible Smoothing with B-splines and Penalties. Statist. Sci. 11 (1996), no. 2, 89–121. (https://doi.org/10.1214/ss/1038425655)

    ## Perperoglou, A., Sauerbrei, W., Abrahamowicz, M. et al. A review of spline function procedures in R. BMC Med Res Methodol 19, 46 (2019). (https://doi.org/10.1186/s12874-019-0666-3)


import numpy as np
from numpy.random import seed
seed(1)

import pandas as pd
from math import sqrt

from sklearn.decomposition import PCA

######################################################################
# METRICS
######################################################################

def mse(y, y_hat):
    """
    Calculates Mean Squared Error.
    MSE measures the prediction accuracy of a
    forecasting method by calculating the squared deviation
    of the prediction and the true value at a given time and
    averages these devations over the length of the series.
    y: numpy array
      actual test values
    y_hat: numpy array
      predicted values
    return: MSE
    """
    mse = np.mean(np.square(y - y_hat))
    return mse

def rmse(y, y_hat):
    """
    Calculates Root Mean Squared Error.
    RMSE measures the prediction accuracy of a
    forecasting method by calculating the squared deviation
    of the prediction and the true value at a given time and
    averages these devations over the length of the series.
    Finally the RMSE will be in the same scale
    as the original time series so its comparison with other
    series is possible only if they share a common scale.
    y: numpy array
      actual test values
    y_hat: numpy array
      predicted values
    return: RMSE
    """
    rmse = sqrt(np.mean(np.square(y - y_hat)))
    return rmse

def mape(y, y_hat):
    """
    Calculates Mean Absolute Percentage Error.
    MAPE measures the relative prediction accuracy of a
    forecasting method by calculating the percentual deviation
    of the prediction and the true value at a given time and
    averages these devations over the length of the series.
    y: numpy array
      actual test values
    y_hat: numpy array
      predicted values
    return: MAPE
    """
    mape = np.mean(np.abs(y - y_hat) / np.abs(y))
    mape = 100 * mape
    return mape

def smape(y, y_hat):
    """
    Calculates Symmetric Mean Absolute Percentage Error.
    SMAPE measures the relative prediction accuracy of a
    forecasting method by calculating the relative deviation
    of the prediction and the true value scaled by the sum of the
    absolute values for the prediction and true value at a
    given time, then averages these devations over the length
    of the series. This allows the SMAPE to have bounds between
    0% and 200% which is desireble compared to normal MAPE that
    may be undetermined.
    y: numpy array
      actual test values
    y_hat: numpy array
      predicted values
    return: SMAPE
    """
    smape = np.mean(np.abs(y - y_hat) / (np.abs(y) + np.abs(y_hat)))
    smape = 200 * smape
    return smape

def mase(y, y_hat, y_train, seasonality=1):
    """
    Calculates the M4 Mean Absolute Scaled Error.
    MASE measures the relative prediction accuracy of a
    forecasting method by comparinng the mean absolute errors
    of the prediction and the true value against the mean
    absolute errors of the seasonal naive model.
    y: numpy array
      actual test values
    y_hat: numpy array
      predicted values
    y_train: numpy array
      actual train values for Naive1 predictions
    seasonality: int
      main frequency of the time series
      Hourly 24,  Daily 7, Weekly 52,
      Monthly 12, Quarterly 4, Yearly 1
    return: MASE
    """
    scale = np.mean(abs(y_train[seasonality:] - y_train[:-seasonality]))
    mase = np.mean(abs(y - y_hat)) / scale
    mase = 100 * mase
    return mase

def rmsse(y, y_hat, y_train, seasonality=1):
    """
    Calculates the M5 Root Mean Squared Scaled Error.
    Calculates the M4 Mean Absolute Scaled Error.
    MASE measures the relative prediction accuracy of a
    forecasting method by comparinng the mean squared errors
    of the prediction and the true value against the mean
    squared errors of the seasonal naive model.
    y: numpy array
      actual test values
    y_hat: numpy array of len h (forecasting horizon)
      predicted values
    seasonality: int
      main frequency of the time series
      Hourly 24,  Daily 7, Weekly 52,
      Monthly 12, Quarterly 4, Yearly 1
    return: RMSSE
    """
    scale = np.mean(np.square(y_train[seasonality:] - y_train[:-seasonality]))
    rmsse = sqrt(mse(y, y_hat) / scale)
    rmsse = 100 * rmsse
    return rmsse

def pinball_loss(y, y_hat, tau=0.5):
    """
    Calculates the Pinball Loss.
    The Pinball loss measures the deviation of a quantile forecast.
    By weighting the absolute deviation in a non symmetric way, the
    loss pays more attention to under or over estimation.
    A common value for tau is 0.5 for the deviation from the median.
    y: numpy array
      actual test values
    y_hat: numpy array of len h (forecasting horizon)
      predicted values
    tau: float
      Fixes the quantile against which the predictions are compared.
    return: pinball_loss
    """
    delta_y = y - y_hat
    pinball = np.maximum(tau * delta_y, (tau-1) * delta_y)
    pinball = pinball.mean()
    return pinball_loss

def evaluate_panel(y_test, y_hat, y_train,
                   metric, seasonality):
    """
    Calculates a specific metric for y and y_hat
    y_test: pandas df
      df with columns unique_id, ds, y
    y_hat: pandas df
      df with columns unique_id, ds, y_hat
    y_train: pandas df
      df with columns unique_id, ds, y (train)
      this is used in the scaled metrics
    seasonality: int
      main frequency of the time series
      Hourly 24,  Daily 7, Weekly 52,
      Monthly 12, Quarterly 4, Yearly 1
    return: list of metric evaluations for each unique_id
      in the panel data
    """
    metric_name = metric.__code__.co_name
    uids = y_test.index.get_level_values('unique_id').unique()
    y_hat_uids = y_hat.index.get_level_values('unique_id').unique()
    assert len(y_test)==len(y_hat), "not same length"
    assert all(uids == y_hat_uids), "not same u_ids"

    idxs, evaluations = [], []
    for uid in uids:
        y_test_uid = y_test.loc[uid].values
        y_hat_uid = y_hat.loc[uid].values
        y_train_uid = y_train.loc[uid].y.values

        if metric_name in ['mase', 'rmsse']:
            evaluation_uid = metric(y=y_test_uid, y_hat=y_hat_uid,
                                    y_train=y_train_uid, seasonality=seasonality)
        else:
            evaluation_uid = metric(y=y_test_uid, y_hat=y_hat_uid)

        idxs.append(uid)
        evaluations.append(evaluation_uid)

    idxs = pd.Index(idxs, name='unique_id')
    evaluations = pd.Series(evaluations, index=idxs)
    return evaluations

def compute_evaluations(y_test, y_hat, y_train, metrics, seasonality): #, progress_bar
    """
    Calculates all metrics in list for y and y_hat panel data,
    and creates rank based on PCA dimensionality reduction.
    y_test: pandas df
      df with columns unique_id, ds, y
    y_hat: pandas df
      df with columns unique_id, ds, y_hat
    y_train: pandas df
      df with columns unique_id, ds, y (train)
      this is used in the scaled metrics
    metrics: list
      list of strings containing all metrics to compute
    seasonality: int
      main frequency of the time series
      Hourly 24,  Daily 7, Weekly 52,
      Monthly 12, Quarterly 4, Yearly 1
    return: list of metric evaluations
    """
    print("\n Evaluating models")
    evaluations = {}
    for metric_name, metric in metrics.items():
        print(metric_name)
        for col in y_hat.columns:
            mod_evaluation = evaluate_panel(y_test=y_test, y_hat=y_hat[col],
                                            y_train=y_train, metric=metric,
                                            seasonality=seasonality)
            mod_evaluation.name = y_hat[col].name

            if not (metric_name in evaluations.keys()):
                evaluations[metric_name] = [mod_evaluation]
            else:
                evaluations[metric_name].append(mod_evaluation)
        #progress_bar['value']+=1
        #progress_bar.update()

    # Collapse Metrics
    for metric_name, metric in metrics.items():
        evaluations[metric_name] = pd.concat(evaluations[metric_name], axis=1)
        evaluations[metric_name] = evaluations[metric_name].mean(axis=0)

    evaluations = pd.DataFrame.from_dict(evaluations)

    # PCA rank
    X = evaluations.values
    pca = PCA(n_components=1)
    pca.fit(X)
    evaluations['pca_rank'] = pca.fit_transform(X)
    evaluations['pca_rank'] = evaluations['pca_rank'].rank(ascending=False)
    evaluations['pca_rank'] = evaluations['pca_rank'].astype(int)

    evaluations.sort_values(by='pca_rank', inplace=True)
    evaluations.reset_index(inplace=True)
    evaluations.rename(columns={'index': 'model'}, inplace=True)
    return evaluations


import numpy as np

def build_haar_matrix(
        size: int) -> np.array:
    result: np.array = np.zeros((size, size))
    for w in range(0, size, 2):
        result[w, w] = 1.0 / np.sqrt(2)
        result[w, w + 1] = 1.0 / np.sqrt(2)
        result[w + 1, w] = -1.0 / np.sqrt(2)
        result[w + 1, w + 1] = 1.0 / np.sqrt(2)
    return result

def build_inverse_haar_matrix(
        size: int) -> np.array:
    return np.transpose(build_haar_matrix(size=size))

def build_daubechies_matrix(
        size: int) -> np.array:
    result: np.array = np.zeros((size, size))
    c1: float = (1.0 + np.sqrt(3.0)) / (4.0 * np.sqrt(2.0))
    c2: float = (3.0 + np.sqrt(3.0)) / (4.0 * np.sqrt(2.0))
    c3: float = (3.0 - np.sqrt(3.0)) / (4.0 * np.sqrt(2.0))
    c4: float = (1.0 - np.sqrt(3.0)) / (4.0 * np.sqrt(2.0))
    for w in range(0, size, 2):
        result[w, w] = c1
        result[w, w + 1] = c2
        result[w, ((w + 2) % size)] = c3
        result[w, ((w + 3) % size)] = c4
        result[w + 1, w] = c4
        result[w + 1, w + 1] = -c3
        result[w + 1, ((w + 2) % size)] = c2
        result[w + 1, ((w + 3) % size)] = -c1
    return result

def build_inverse_daubechies_matrix(
        size: int) -> np.array:
    return np.transpose(build_daubechies_matrix(size=size))

def h_transform(
        input_image: np.array,
        transform_matrix: np.array) -> np.array:
    height, width = input_image.shape
    matrix_row: np.array = transform_matrix
    work_image: np.array = np.zeros(input_image.shape)
    for row in range(height):
        work_image[row] = np.dot(matrix_row, input_image[row])
    approximation = range(0, width, 2)
    difference = range(0, width // 2)
    result: np.array = np.zeros(input_image.shape)
    for row in range(height):
        for i, j in zip(approximation, difference):
            result[row, j] = work_image[row, i]
            result[row, j + width // 2] = work_image[row, i + 1]
    return result

def v_transform(
        input_image: np.array,
        transform_matrix: np.array) -> np.array:
    height, width = input_image.shape
    matrix_col: np.array = transform_matrix
    work_image: np.array = np.zeros(input_image.shape)
    for col in range(width):
        work_image[:, col] = np.dot(matrix_col, input_image[:, col])
    approximation = range(0, height, 2)
    difference = range(0, height // 2)
    result: np.array = np.zeros(input_image.shape)
    for col in range(width):
        for i, j in zip(approximation, difference):
            result[j, col] = work_image[i, col]
            result[j + height // 2, col] = work_image[i + 1, col]
    return result

def inverse_h_transform(
        input_image: np.array,
        transform_matrix: np.array) -> np.array:
    height, width = input_image.shape
    matrix_row: np.array = transform_matrix
    work_image: np.array = np.zeros(input_image.shape)
    for row in range(height):
        collector = range(0, width // 2)
        spreader = range(0, width, 2)
        for i, j in zip(collector, spreader):
            work_image[row, j] = input_image[row, i]
            work_image[row, j + 1] = input_image[row, i + (width // 2)]
    result: np.array = np.zeros(input_image.shape)
    for row in range(height):
        result[row] = np.dot(matrix_row, work_image[row])
    return result

def inverse_v_transform(
        input_image: np.array,
        transform_matrix: np.array) -> np.array:
    height, width = input_image.shape
    matrix_col: np.array = transform_matrix
    work_image: np.array = np.zeros(input_image.shape)
    for col in range(width):
        collector = range(0, height // 2)
        spreader = range(0, height, 2)
        for i, j in zip(collector, spreader):
            work_image[j, col] = input_image[i, col]
            work_image[j + 1, col] = input_image[i + (height // 2), col]
    result: np.array = np.zeros(input_image.shape)
    for col in range(width):
        result[:, col] = np.dot(matrix_col, work_image[:, col])
    return result

def transform(
        input_image: np.array,
        transform_h_matrix: np.array,
        transform_v_matrix: np.array) -> np.array:
    return v_transform(
        input_image=h_transform(
            input_image=input_image,
            transform_matrix=transform_h_matrix),
        transform_matrix=transform_v_matrix)

def inverse_transform(
        input_image: np.array,
        transform_h_matrix: np.array,
        transform_v_matrix: np.array) -> np.array:
    return inverse_v_transform(
        input_image=inverse_h_transform(
            input_image=input_image,
            transform_matrix=transform_h_matrix),
        transform_matrix=transform_v_matrix)


import numpy as np
import matplotlib.pyplot as plt
import data

param_niter = 100_000
param_delta = 0.0005
param_print_frequency = 500

def sigmoid(s):
    return 1 / (1 + np.exp(-s))

def binlogreg_train(X: np.ndarray, Y_: np.ndarray):
    """Train a binary logistic regression model on the given data.

    Arguments:
        X (np.ndarray): design matrix (NxD)
        Y_ (np.ndarray): labels (Nx1)

    Returns:
        w, b: weights and biases of the model
    """

    w = np.random.randn(2)
    b = 0

    for i in range(param_niter):
        # posterior probabilities c_1 (N x 1)
        probs = sigmoid(X @ w + b)
        # print
        if i % param_print_frequency == 0:
            # loss function
            loss = np.sum(-Y_ * np.log(probs) - (1 - Y_) * np.log(1 - probs))  # scalar
            print(f"iteration {i}: loss {loss}")
        # gradients
        diff = probs - Y_
        grad_w = np.sum(diff @ X) # D x 1
        grad_b = np.sum(diff)  # 1 x 1
        # new parameters
        w += -param_delta * grad_w
        b += -param_delta * grad_b

    return w, b

def binlogreg_classify(X, w, b):
    """Classify datapoints from X with a linear regresison model with parameters w and b.

    Arguments:
        X (np.ndarray): design matrix (NxD)
        w (np.ndarray): weights of the model (Dx1)
        b (float)     : bias of the model (1x1)

    Returns:
        probs (np.ndarray): P(Y == 1 | X)
    """
    return sigmoid(X @ w + b)

def binlogreg_decfun(w, b):
    return lambda X: binlogreg_classify(X, w, b)

if __name__ == "__main__":
    np.random.seed(100)
    # get the training dataset
    X, Y_ = data.sample_gauss_2d(2, 100)
    # train the model
    w, b = binlogreg_train(X, Y_)
    # evaluate the model on the training dataset
    probs = binlogreg_classify(X, w, b)
    Y = probs >= 0.5
    # report performance
    accuracy, recall, precision = data.eval_perf_binary(Y, Y_)
    AP = data.eval_AP(Y_[probs.argsort()])
    print(accuracy, recall, precision, AP)
    # now lets plot stuff
    fun = binlogreg_decfun(w, b)
    bounding_box = (np.min(X, axis=0), np.max(X, axis=0))
    data.graph_surface(fun, bounding_box, offset=0.5)
    data.graph_data(X, Y_, Y)
    plt.show()


import numpy as np

# Numerically-stable version of softmax
def softmax(x):
    tmp_max = np.max(x, axis=-1, keepdims=True)
    tmp_out = np.exp(x - tmp_max)
    tmp_sum = np.sum(tmp_out, axis=-1, keepdims=True)
    return tmp_out / tmp_sum


from sympy import linsolve, symbols
from sympy.parsing.sympy_parser import parse_expr

def main():
    v1s, v2s, v3s, v4s = symbols("v1, v2, v3, v4")

    v1 = "-v1 + 0.4 * (0.3 * (2.0 + 0.8 * v2) + 0.7 * (3.0 + 0.8 * v3)) + 0.6 * (1 * (1.0 + 0.8 * v3))"
    v2 = "-v2 + 1.0 * (1.0 * (3.0 + 0.8 * v1))"
    v3 = "-v3 + 0.5 * (1.0 * (-3.0 + 0.8 * v1)) + 0.5 * (0.2 * (1.0 + 0.8 * v3) + 0.8 * (6.0 + 0.8 * v4))"
    v4 = "-v4 + 1.0 * (0.6 * (5.0 + 0.8 * v1) + 0.4 * (-3.0 + 0.8 * v2))"
    v1_expr = parse_expr(v1)
    v2_expr = parse_expr(v2)
    v3_expr = parse_expr(v3)
    v4_expr = parse_expr(v4)
    eqns = [v1_expr, v2_expr, v3_expr, v4_expr]
    print([round(x, 3) for x in list(linsolve(eqns, v1s, v2s, v3s, v4s))[0]])

if __name__ == "__main__":
    main()


import numpy as np

def Gaussian_Elimination(A, B):
    # initialization
    M = np.concatenate((A,B), axis=1)
    r, c = M.shape[0], (M.shape[1] - 1)

    # Forward Elimination
    for i in range(r):
        p = i + M[i:,i].argmax()
        M[p],M[i] = tuple(M[[i, p]])
        M[i] = M[i]/M[i, i]
        for j in range(i + 1, r):
            M[j] = M[j] - (M[j][i]/M[i][i])*M[i]

    # Back Substitution
    X = np.array([0.0]*c)
    Ax = M[:, :c]
    Cx= M[:, c]
    for i in range(r-1,-1,-1):
        X[i] = (Cx[i] - Ax[i].dot(X))/Ax[i,i]

    # return
    return X


import numpy as np

def load_data(file_name):
    data = np.loadtxt(file_name, delimiter=',')

    X = data[:, :-1]
    y = data[:, -1:]

    return X, y

def transform_arguments(tranformation):
    def dec(f):
        def wrapper(*args, **kwargs):
            t_args = map(tranformation, args)
            t_kwargs = {k: tranformation(v) for k, v in kwargs.iteritems()}
            return f(*t_args, **t_kwargs)
        return wrapper
    return dec

matrix_args = transform_arguments(lambda arg: np.matrix(arg, copy=False))
matrix_args_array_only = transform_arguments(lambda arg: np.matrix(arg, copy=False) if isinstance(arg, np.ndarray) else arg)

@matrix_args
def J_liner_regression(X, y, theta):
    temp = X*theta - y

    return (temp.T*temp/(2*len(y)))[0, 0]

@matrix_args_array_only
def gradient_descent(cost_function, X, y, iterations, intial_theta, alpha):
    m = len(y)
    theta = intial_theta
    J_history = []

    for _ in xrange(iterations):
        theta = theta - (alpha/m)*X.T*(X * theta - y)
        J_history.append(cost_function(X, y, theta))

    return theta, J_history

def add_zero_feature(X, axis=1):
    return np.append(np.ones((X.shape[0], 1) if axis else (1, X.shape[1])), X, axis=axis)

def sigmoid(z):
    return 1/(1+np.exp(-z))

def lr_accuracy(X, y, theta):
    theta = theta[:, np.newaxis]
    temp = sigmoid(np.dot(X, theta)).ravel()

    p = np.zeros(len(X))
    p[temp >= 0.5] = 1

    return np.mean(p == y.ravel())*100

@matrix_args
def cf_lr(theta, X, y):
    theta = theta.T
    m = len(y)
    Z = sigmoid(X*theta)
    J = (-y.T*np.log(Z) - (1-y).T*np.log(1-Z))/m

    return J[0, 0]

@matrix_args
def gf_lr(theta, X, y):
    theta = theta.T
    m = len(y)
    res = (X.T*(sigmoid(X*theta)-y))/m

    return res.A1

@matrix_args_array_only
def cf_lr_reg(theta, X, y, lambda_coef):
    theta = theta.T
    m = len(y)
    lambda_coef = float(lambda_coef)
    Z = sigmoid(X*theta)
    J = (-y.T * np.log(Z) - (1-y).T * np.log(1-Z))/m + (lambda_coef/(2 * m))*theta.T*theta

    return J[0, 0]

@matrix_args_array_only
def gf_lr_reg(theta, X, y, lambda_coef):
    theta = np.matrix(theta.T, copy=True)
    lambda_coef = float(lambda_coef)
    m = len(y)
    Z = X*theta
    theta[0, 0] = 0

    res = (X.T*(sigmoid(Z)-y))/m + (lambda_coef/m)*theta

    return res.A1

def feature_normalize(X):
    mu = np.mean(X, axis=0)[np.newaxis, :]
    sigma = np.std(X, axis=0)[np.newaxis, :]
    return mu, sigma, (X-mu)/sigma


import numpy as np

def lu_decomp(A):
    n = len(A)
    U = A.copy()
    L = np.eye(n)
    row_idx = list(range(n))

    for col in range(n):
        # find best row
        best_row, best_elem = None, None
        for row in range(col,n):
            row_i = row_idx[row]
            elem = U[row_i][col]
            if best_row is None or abs(elem) > best_elem:
                best_row = row
                best_elem = abs(elem)

        if best_elem == 0:
            #raise(Exception("Not full rank."))
            continue

        # swap rows:  best_row and col
        L[row_idx[col]][col] = 0
        L[row_idx[col]][best_row] = 1
        L[row_idx[best_row]][best_row] = 0
        L[row_idx[best_row]][col] = 1
        row_idx[col], row_idx[best_row] = row_idx[best_row] , row_idx[col]
        best_row_i = row_idx[col]

        # normalize
        for row in range(col+1,n):
            row_i = row_idx[row]
            coef = U[row_i][col] / U[best_row_i][col]
            L[row_i][col] = coef
            U[row_i] = U[row_i] - U[best_row_i] * coef

    return L, U, row_idx

def solve_lu(L_,U_,p,b_):
    L = L_[p]
    U = U_[p]
    b = b_[p]

    n = len(L)
    z = np.zeros(n)
    x = np.zeros(n)

    # Lz = b
    # L[i,:i] * sum(z[:i,0] + z[i]) = b[i]
    for i in range(n):
        z[i] = b[i] - (L[i,:i] * z[:i]).sum()

    assert np.allclose(L.dot(z),b)

    # Ux = z
    # U[i,i] * x[i] + sum(U[i,i+1:] * x[i+1:]) = z[i]
    for i in range(n-1,-1,-1):
        acc = (z[i] - (U[i,i+1:] * x[i+1:]).sum())
        if(np.isclose(U[i,i], 0)):
            if(np.isclose(acc,0)):
                # multiple solutions
                x[i] = 0.0
            else:
                # no solution
                raise(Exception('no solution'))
        else:
            x[i] = acc / U[i,i]

    assert np.allclose(U.dot(x),z)

    return x

def test_lu():
    n_teste = 500
    np.random.seed(8)

    while(n_teste > 0):
        n_teste -= 1
        n = np.random.randint(1,30)
        A = np.random.random((n,n))

        if(np.random.random() > 0.5):
            print('Not singular.')
            A[np.random.randint(0,n)] = np.random.uniform(0,100) * A[np.random.randint(0,n)]

        L, U, row_idx = lu_decomp(A)

        L = L[row_idx]
        U = U[row_idx]
        A = A[row_idx]

        assert np.allclose(L.dot(U).ravel(), A.ravel())

    print('LU decomposition Ok!')

def test_solve():
    np.random.seed(8)
    n_teste = 10
    while n_teste > 0:
        n_teste -= 1

        n = np.random.randint(1,100)
        A = np.random.random((n,n))
        b = np.random.random(n)

        #A[-1] = A[0]
        #b[-1] = b[0]

        ans_correct = np.linalg.solve(A,b)

        L,U,p = lu_decomp(A)
        ans = solve_lu(L,U,p,b)

        assert np.allclose(A.dot(ans), b)
        assert np.allclose(ans, ans_correct)

    print('Solve with LU Ok!')

if __name__ == "__main__":
    test_lu()
    test_solve()


import numpy as np
import random

def sigmoid(z):
    g = 1.0 / (1 + np.exp(-z))
    return g

def sigmoid_grad(z):
    g = sigmoid(z) * (1 - sigmoid(z))
    return g

def ravel(thetagrad):
    f = list(map(lambda x : list(x.ravel()), thetagrad))
    ravel = []
    for l in f:
        ravel += l
    return np.array(ravel).reshape(len(ravel), 1)

def unravel(theta, units, hidden_layers, n):
    theta_ = []
    theta_.append(np.array(theta[0:(n+1)*units, 0]).reshape(units, n+1))

    j = (n+1)*units
    for i in range(hidden_layers-1):
        theta_.append(np.array(theta[j:j+((units+1)*units), 0]).reshape(units, units+1))
        j += units*(units+1)

    theta_.append(np.array(theta[j:, 0]).reshape(10, units+1))

    return theta_

def mean_normalize(X):
    m, n = X.shape
    means_ = np.array([np.sum(X[:, i]) for i in range(n)]).reshape(1, n) / m
    std_ = np.std(X, axis=0)
    normalized = (X - means_) / std_
    mu = np.array([np.sum(normalized[:, i]) for i in range(n)]).reshape(1, n) / m
    return normalized, mu

def euclidean_dist(obj_1, obj_2):
    return np.sqrt(sum(np.square(obj_1 - obj_2).T))

def gaussian_model(X, mu, sigma_2):
    exp = np.exp(-np.square(X-mu) / (2*sigma_2))

    return np.array(list((1/np.sqrt(2*np.pi*sigma_2)) * exp))

def multinomial_model(X, mu, sigma_2):
    n = len(mu)
    exp = np.exp(-0.5 * (X-mu)@np.linalg.pinv(sigma_2)@(X-mu).reshape(n, 1))
    coef = 1/(((2*np.pi)**(n/2))*(np.linalg.det(sigma_2)**0.5))

    return coef*np.array(list(exp))

def test_train_split(X, y, test_size=0.4, valid_size=0):
    rand_idx = list(range(X.shape[0]))
    random.shuffle(rand_idx)
    X = X[rand_idx, :]
    y = y[rand_idx]

    train_idx = int((1-test_size)*X.shape[0])
    # valid_idx = valid_size*X.size[0]
    # test_idx = int(test_size*X.shape[0])
    return X[:train_idx, :], y[:train_idx], X[train_idx:, :], y[train_idx:]

def accuracy_score(true_y, pred_y):
    correct = 0
    for i in range(len(true_y)):
        if true_y[i] == pred_y[i]:
            correct += 1

    return correct/len(true_y)


import numpy as np

# 1. Zeros Array
arr = np.zeros(10)
print(arr)

arr = np.zeros((2, 5))
print(arr)

# 2. Ones Array
arr = np.ones(5)
print(arr)

arr = np.ones([2, 5])
print(arr)

# 3. Empty Array
arr = np.empty(10)
print(arr)

arr = np.empty((2, 5))
print(arr)

arr = np.eye(3, 2)
print(arr)

# 4. full array
arr = np.full(5, 8)
print(arr)

arr = np.full(5, arr)
print(arr)

# 5. (zeros, ones, empty, full) Like
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(a)

arr = np.zeros_like(a)
print(arr)

arr = np.ones_like(a)
print(arr)

arr = np.empty_like(a)
print(arr)

arr = np.full_like(a, 5)
print(arr)

# 1. arange
arr = np.arange(0, 20, 3)
print(arr)

# 2. linspace
arr = np.linspace(0.0, 1.0, 10)
print(arr)

arr = np.random.uniform(0, 10, 5)
print(arr)

arr = np.random.randn(5)
print(arr)

arr = np.random.normal(0, 10, 5)
print(arr)

arr = np.arange(0, 21)
print(arr)

print(arr > 10)

print(np.any(arr > 10))

print(np.all(arr > 10))

print(arr[arr == 10])

print(arr[arr % 2 == 0])

print(arr[arr % 2 != 0])

## & = and, | = or, ^ = xor, ~ = not
print(arr[(arr % 2 == 0) & (arr > 10)])

a = np.arange(5)
b = np.arange(5, 10)
print(a, b)

print(a + b)
print(a - b)
print(a * b)
print(a / b)
print(a // b)
print(a % b)
print(a ** b)

arr = np.array([1, 3, 5, 67, 89])
print(arr)

### arr.fun()
print(np.max(arr))
print(np.min(arr))
print(np.sum(arr))
print(np.var(arr))
print(np.argmax(arr))
print(np.argmin(arr))
print(np.mean(arr))
print(np.std(arr))

print(np.allclose(0.1 + 0.2, 0.3))
print(np.allclose(1e-9, 2e-9))
print(np.allclose(0.1 + 0.2 - 0.3, 0))

arr = np.array([
	[2, 4, 6],
	[7, 9, 4],
	[9, 1, 5]
	])
print(np.transpose(arr))

print(np.tile(arr, (1, 2)))
print(np.repeat(arr, 3, axis=1))
print(np.delete(arr, 1, axis=0))

print(np.eye(4))
print(np.diag([1, 2, 3]))
arr = np.array([[1, 2], [3, 4]])
print(np.diag(arr))
print(np.vander([1, 2, 3, 4], 3))

# Other than that, NumPy arrays are:
# more compact, especially when there’s more than one dimension
# faster than lists when the operation can be vectorized
# slower than lists when you append elements to the end
# usually homogeneous: can only work fast with elements of one type

rng  = np.random.default_rng()

# python list
l = [0, 1, 2]
a = l 		   # no copy
b = l[:]     # copy
c = l.copy() # copy
# numpy array
l = np.array([0, 1, 2])
a = l 		   # no copy
b = l[:]     # no copy
c = l.copy() # copy

np.var(arr, axis=None)
np.std(arr, axis=None, ddof=1.5)
np.mean(arr, axis=None)
np

np.sort(arr)
arr.sort()

Tutorial/Code/Some useful things in NumPy.py


"""
Example 3 - Logistic Regression without regularization

NOTE: The example and sample data is being taken from the "Machine Learning course by Andrew Ng" in Coursera.

Problem:
  Suppose that you are the administrator of a university department and
  you want to determine each applicant’s chance of admission based on their
  results on two exams. You have historical data from previous applicants
  that you can use as a training set for logistic regression. For each training
  example, you have the applicant’s scores on two exams and the admissions
  decision.
  Your task is to build a classification model that estimates an applicant’s
  probability of admission based the scores from those two exams.

  The file 'data/ex1data1.txt' contains the dataset for our
  Logistic regression problem.
"""

# initial imports

import numpy as np
from matplotlib import pyplot as plt

plt.ion()
from models.data_preprocessing import add_bias_unit, map_feature, feature_normalize
from models.logistic_regression import cost_function, predict, gradient_descent, gradient_function, sigmoid
from models.plotter import plot_decision_boundary

data = np.loadtxt('data/ex2data1.txt', delimiter=',')
X = data[:, :-1]
y = data[:, -1, np.newaxis]

"""
==================== Part 1: Plotting ====================
  We start the exercise by first plotting the data to understand the
  the problem we are working with.
"""
print('Plotting data with + indicating (y = 1) examples and o indicating (y = 0) examples.')
"""
Example plotting for multiple markers
x = np.array([1,2,3,4,5,6])
y = np.array([1,3,4,5,6,7])
m = np.array(['o','+','+','o','x','+'])

unique_markers = set(m)  # or yo can use: np.unique(m)

for um in unique_markers:
    mask = m == um
    # mask is now an array of booleans that van be used for indexing
    plt.scatter(x[mask], y[mask], marker=um)
"""
fig, ax = plt.subplots()
y_slim = y.ravel()
# plotting y=1 values
ax.scatter(x=X[y_slim == 1, 0], y=X[y_slim == 1, 1], marker='+', color='black', s=50)
# plotting y=0 values
# X[y_slim == 0, 0] is logical indexing with rows with y=0 only
ax.scatter(x=X[y_slim == 0, 0], y=X[y_slim == 0, 1], marker='o', color='y', s=25)

# labels
ax.set_xlabel('Exam 1 score')
ax.set_ylabel('Exam 2 score')

# Specified in plot order
ax.legend(['Admitted', 'Not admitted'])

# ============ Part 2: Compute Cost and Gradient ============
# initial sizes
m, n = X.shape

# adding bias unit
X = add_bias_unit(X)

# Initialize fitting parameters
initial_theta = np.zeros([n + 1, 1])

# Compute and display initial cost and gradient
cost = cost_function(initial_theta, X, y, regularized=False)
grad = gradient_function(initial_theta, X, y, regularized=False)
print('Cost at initial theta (zeros): {}'.format(cost))
print('Expected cost (approx): 0.693')
print('Gradient at initial theta (zeros): ')
print(grad)
print('Expected gradients (approx):\n -0.1000\n -12.0092\n -11.2628\n')

# =========== Performing gradient descent================
# from models.data_preprocessing import feature_normalize
X_norm, mu, sigma = feature_normalize(X[:, 1:])
X_norm = add_bias_unit(X_norm)

from scipy.optimize import minimize

# theta_history = np.array([]).reshape([0, n+1])
theta_history = []

def cg(abc, *args):
    theta_history.append(abc)

initial_theta = np.zeros(n + 1)
op_result = minimize(fun=cost_function, x0=initial_theta, jac=gradient_function, args=(X, y, 0.01, False), method='cg', callback=cg)

# cost = cost_function(op_result.x,X,y, regularized=False)
print('Cost at theta found by Gradient descent: {}'.format(op_result.fun))
print('Expected cost (approx): 0.203')
print('theta: {}'.format(op_result.x))
print('Expected theta (approx):\n')
print(' -25.161\n 0.206\n 0.201\n')

# converting theta_history into J_history
J_history = (np.array(theta_history[::-1]) @ op_result.x)

# plot J_history
fig1, ax1 = plt.subplots()
ax1.plot(range(J_history.size), J_history)
ax1.set_xlabel('Iterations')
ax1.set_ylabel('Cost')

theta = op_result.x[:, np.newaxis]
plot_decision_boundary(theta, X, y, sigmoid, 0.1, fig, ax)

# ============== Part 4: Predict and Accuracies ==============
#  After learning the parameters, we are going to use it to predict the outcomes
#  on unseen data. In this part, we will use the logistic regression model
#  to predict the probability that a student with score 45 on exam 1 and
#  score 85 on exam 2 will be admitted.
#
#  Furthermore, we will compute the training and test set accuracies of
#  our model.

#  Predict probability for a student with score 45 on exam 1
#  and score 85 on exam 2

prob = sigmoid(np.array([1, 45, 85]) @ theta)
print('For a student with scores 45 and 85, we predict an admission probability of {}'.format(prob))
print('Expected value: 0.775 +/- 0.002')

# Compute accuracy on our training set
p = predict(X, theta)

print('Train Accuracy: {}'.format(np.mean(p == y) * 100))
print('Expected accuracy (approx): 89.0\n')

# ===============End================
plt.ioff()
plt.show()


# coding: utf-8

# # Introdução

# A transformação para domínios alternativos ao tempo contínuo ou discreto pode provê características importantes para análise do sinal. Em particular a transformação para o dominío da frequência utilizando a transformada de Fourier visa exibir comportamentos relativos as parte que compoem o sinal.
#
# Qualquer sinal pode ser representado como uma soma infinita de senos e cossenos, tais componentes são chamadas de harmônicos. Então, uma análise em dominio da frequência pela transformada de fourier (também chamada de análise espectral) induz ao projetista identificar de forma direta as componentes e frequências presentes naquele sinal. Esta é uma forma de ver as menores partes presentes em um sinal.
#
# Em relação as imagens o processamento desta transformada é traduzida matematicamente pela aplicação da seguinte transfomada no sinal:
#
# $$F[m,n] = \frac{1}{UV} \int_o^U \int_o^V F(u,v) e^{j2\pi(um.x0 + un.y0} du dv$$

# ## Implementação e discussões

# In[1]:

import cv2
import numpy as np
import matplotlib.pyplot as plt

# - Abrir imagem:

# In[2]:

img = np.array(cv2.imread('lena.png', cv2.IMREAD_GRAYSCALE))
rows, cols = img.shape

# - Aplicar a transformada discrea de Fourier:

# In[3]:

img_dft = np.fft.fft2(img)

# - Trazer a componente DC para o centro da imagem, transladando n/2 nas duas direções:

# In[4]:

img_dft_shift = np.fft.fftshift(img_dft)

# - Calcular a magnitude a partir da componente real e imaginária:

# In[5]:

img_dft_mag = np.abs(img_dft_shift)

# In[6]:

plt.figure(2,figsize=(10,9))
plt.subplot(121)
plt.imshow(img, 'gray')
plt.title("Imagem original")
plt.axis('OFF')

plt.subplot(122)
plt.imshow(20*np.log(img_dft_mag), 'gray')
plt.title("Espectro em frequência")
plt.axis('OFF')

plt.show()

# - Cálculo da inversa:

# In[7]:

img_idft = np.fft.ifft2(img_dft)
img_inversa = np.abs(img_idft)

plt.figure(2)
plt.imshow(img_inversa, 'gray')
plt.title("Imagem após IDFT")
plt.axis('OFF')

plt.show()

# ## Filtragem na frequência

# ### Filtro passa-alta:

# - Criar máscara Laplaciana

# In[8]:

def laplacianKernel(h1, h2):

    import numpy as np
    import matplotlib.pyplot as plt
    import math as m

    sizeKernel = np.array([h1,h2])

    kernel = -1*np.ones(sizeKernel)

    center = np.uint8(kernel.shape[0]/2)

    kernel[center,center] = (-1*np.sum(kernel)) -1

    return kernel

def gaussianKernel(h1, h2):

    import numpy as np
    import matplotlib.pyplot as plt
    import math as m

    ## Returns a normalized 2D gauss kernel array for general purporses

    x, y = np.mgrid[0:h2, 0:h1]
    x = x-h2/2
    y = y-h1/2
    sigma = 2
    g = np.exp( -( x**2 + y**2 ) / (2*sigma**2) )
    return g / g.sum()

filterKernel = gaussianKernel(rows,cols)

# In[9]:

filter_dft = np.fft.fft2(filterKernel)
filter_dft_shift = np.fft.fftshift(filter_dft)
filter_dft_mag = np.abs(filter_dft_shift)

filter_dft_mag = 1-filter_dft_mag

plt.figure(3+1)
plt.imshow(filter_dft_mag, 'gray')
plt.title("Espectro em frequência do filtro Gaussiano com spread 2")
plt.show()

# - Filtragem na frequência

# In[10]:

filter_img = img_dft_shift * filter_dft_mag
filter_img_mag = np.abs(filter_img)

#img_back = np.fft.fftshift(np.fft.ifft2(filter_img))

img_back = np.fft.ifft2(filter_img)

img_back_mag = np.abs(img_back)

plt.figure(5, figsize=(12,12))
plt.subplot(221)
plt.imshow((img), 'gray')
plt.title("Imagem original")

plt.subplot(222)
plt.imshow(20*np.log(img_dft_mag), 'gray')
plt.title("Espectro da imagem original")

plt.subplot(223)
plt.imshow(img_back_mag, 'gray')
plt.title("Imagem após filtragem na frequência")

plt.subplot(224)
plt.imshow(20*np.log(filter_img_mag), 'gray')
plt.title("Especto da imagem após filtragem na frequência")

plt.show()

# O filtro escolhido foi o filtro Gaussiano inverso, que funciona de forma análoga ao traidcional: atenua baixas frequências e permite a passagem de altas frequências.
#
# O efeito na imagem é um ressalto das regiões de transições rápidas (altas frequências) promovendo um ressalto das bordas em uma imagem. Comparando os espectros é possivel notar que as regiões de constância em intensidade de cor na imagem (baixas frequências), representadas pela maior parte de informação no espectro, sofrem atenuações e o resultado é um novo espectro com um aspecto limpo, exaltando apenas componentes de alta frequência e menos presente na imagem.

# # Conclusões

# A análise de imagens no domínio da frequência é uma ferramenta que provê análises visuais perante a imagem, exibe padrões de comportamento das cores na imagem, podem resultar em identificação de componentes como ruído ou objetos ao olhar para o espectro.
#
# Há a possibilidade de realizar filtragem no domínio da frequência, assim no domínio temporal, é um processa mais rápido pois envolve uma multiplica simples ponto a ponto, talvez o grande parte do custo computacional em executar tal técnica está em realizar a transformada direta e inversa de Fourier.

PDI - Fourier e Wavelet/01 - Fourier/codes/Python/T14/T14+-+Code.py

PDI - Fourier e Wavelet/01 - Fourier/codes/Python/src/T14+-+Code.py


"""

 Created by akiselev on 2019-07-22

mean

The mean tool computes the arithmetic mean along the specified axis.

import numpy

my_array = numpy.array([ [1, 2], [3, 4] ])

print numpy.mean(my_array, axis = 0)        #Output : [ 2.  3.]
print numpy.mean(my_array, axis = 1)        #Output : [ 1.5  3.5]
print numpy.mean(my_array, axis = None)     #Output : 2.5
print numpy.mean(my_array)                  #Output : 2.5

By default, the axis is None. Therefore, it computes the mean of the flattened array.

var

The var tool computes the arithmetic variance along the specified axis.

import numpy

my_array = numpy.array([ [1, 2], [3, 4] ])

print numpy.var(my_array, axis = 0)         #Output : [ 1.  1.]
print numpy.var(my_array, axis = 1)         #Output : [ 0.25  0.25]
print numpy.var(my_array, axis = None)      #Output : 1.25
print numpy.var(my_array)                   #Output : 1.25

By default, the axis is None. Therefore, it computes the variance of the flattened array.

std

The std tool computes the arithmetic standard deviation along the specified axis.

import numpy

my_array = numpy.array([ [1, 2], [3, 4] ])

print numpy.std(my_array, axis = 0)         #Output : [ 1.  1.]
print numpy.std(my_array, axis = 1)         #Output : [ 0.5  0.5]
print numpy.std(my_array, axis = None)      #Output : 1.11803398875
print numpy.std(my_array)                   #Output : 1.11803398875

By default, the axis is None. Therefore, it computes the standard deviation of the flattened array.

Task

You are given a 2-D array of size N X M.
Your task is to find:

1.The mean along axis 1
2.The var along axis 0
3.The std along axis None
"""

import numpy
n,m = map(int, input().split())
numpy.set_printoptions(sign=' ')
arr = numpy.array([input().split() for _ in range(n)], float)
print (numpy.mean(arr, axis = 1))
print (numpy.var(arr, axis = 0))
print(numpy.around( numpy.std(arr), 12))


# -*- coding: utf-8 -*-
"""
@author: datamonday
"""
import math
import numpy as np
from sklearn.preprocessing import MinMaxScaler, StandardScaler

#################### 说明 ##########################
# 为了可读性, 所有numpy和math库的函数均使用.x()调用, #
# 这种方式在实际测试中相比直接函数x()调用耗时        #
####################################################

# =========================== Data Pre-processing =========================== #

def nomalize(X, axis=-1, order=2):
    """
    归一化 (Normalize) the dataset X. data range [0, 1].
    Notes：这种方式是参考库的作者实现方式
    ----
    param X: np.array.
    param axis: axis=0(1) means columns(rows), axis=-1 means last dim.
    param order: order=2 means l2 norm.
    """
    # 针对矩阵每个行向量求向量的l2范数
    l2 = np.atleast_1d(np.linalg.norm(X, ord=order, axis=axis))
    # 将范数为零的元素置1，防止下一步除法操作溢出（NaN）
    l2[l2 == 0] = 1

    # 返回归一化的值，各api作用详见ipynb
    return X / np.expand_dims(l2, axis=axis)

def standardize(X):
    """
    标准化 (Standardize) the dataset X. mean=0, std=1.
    ----
    param X: np.array
    """
    X_std = X
    # 按列（axis=0）计算均值和标准差
    mean = X.mean(axis=0)
    std = X.std(axis=0)
    for col in range(np.shape(X)[1]):
        if std[col]:
            X_std[:, col] = (X_std[:, col] - mean[col]) / std[col]

    return X_std

def normalize_sk(X, feature_range=(0,1)):
    """
    使用 sklearn的库函数实现归一化，方便反归一化操作.
    """
    scaler = MinMaxScaler(feature_range=feature_range)
    print("Your input data type is: ", type(X))
    minmax_X = scaler.fit_transform(X)

    return scaler, minmax_X

def standardize_sk(X):
    """
    使用 sklearn的库函数实现标准化，方便反标准化操作.
    """
    scaler = StandardScaler()
    print("Your input data type is: ", type(X))
    standard_X = scaler.fit_transform(X)

    return scaler, standard_X

# ========================== Statistical Calculate ========================== #
def mean_squared_error(y_true, y_pred):
    """
    Returns the mean squared error between y_true and y_pred
    """
    mse = np.mean(np.power(y_true - y_pred, 2))

    return mse

def calculate_variance(X):
    """
    Return the variance of the features in dataset X
    """
    mean = np.ones(np.shape(X)) * X.mean(axis=0)
    # 样本数
    n_samples = np.shape(X)[0]
    # np.diag() 函数返回方阵的第k条对角线的元素, k=0表示主对角线
    variance = (1 / n_samples) * np.diag((X - mean).T.dot(X - mean), k=0)

    return variance

def calculate_std_dev(X):
    """
    Calculate the standard deviations of the features in dataset X
    """
    std_dev = np.sqrt(calculate_variance(X))

    return std_dev

def euclidean_distance(x1, x2):
    """
    Calculates the l2 distance between two vectors
    """
    # 实现1: numpy
    # return np.sqrt(np.sum(np.square(x1 - x2) ) )

    # 实现2: scipy
    # return pdist(np.vstack([x1, x2]), metric="euclidean")

    # 实现3: Python原生模块
    distance = 0
    for i in range(len(x1)):
        distance += pow((x1[i] - x2[i]), 2)

    return math.sqrt(distance)

def calculate_softmax(X, axis=1):
    """
    Calculate the softmax of input vector X.
    softmax(y_i) = \frac{e^{y_i}}{\sum_j e^{y_i}}
    ----------
    Parameters
    ----------
    X : np.array
    axis : int, optional
        axis=1 means calculate by rows. The default is 1.

    Returns softmax(X)
    -------
    """
    # 输入矩阵每行的最大值
    row_max = X.max(axis=axis)
    # 增加维度, 否则无法运算
    row_max = row_max.reshape(-1, 1)
    # 每行元素均减去最大值, 防止溢出
    X = X - row_max
    # 分子
    X_exp = np.exp(X)
    # 分母, keep_dim保持输入的维度数量不变(尺寸可能变化)
    X_sum = np.sum(X_exp, axis=axis, keep_dim=True)

    return X_exp / X_sum

def calculate_entropy(y):
     """
     Calculate the entropy of label array y.
     ----------
     Parameters
     ----------
     y : the sample labels.
     cross entropy = -\sum_{i=1}^{k} c_i * log(p_i)
     """
     # 换底公式
     log2 = lambda x: math.log(x) / math.log(2)
     # 所有标签
     unique_labels = np.unique(y)
     # 熵
     entropy = 0
     for label in unique_labels:
         count = len(y[y == label])
         # 某一类的概率
         p = count / len(y)
         # 交叉熵计算公式
         entropy += -p * log2(p)

     return entropy

def calculate_covariance_matrix(X, Y=None):
    """
    Calculate the covariance matrix for the dataset X
    """
    if not Y:
        Y = X
    n_samples = np.shape(X)[0]
    # 方差
    covariance_matrix = (1 / n_samples) * (X - X.mean(axis=0)).T.dot(Y - Y.mean(axis=0))

    return np.array(covariance_matrix, dtype=float)

def calculate_correlation_matrix(X, Y=None):
    """
    Calculate the correlation matrix for the dataset X
    ----------

    """
    if not Y:
        Y = X
    n_samples = np.shape(X)[0]
    # 方差
    # 相关系数计算公式的分子, 协方差Cov(X, Y)
    covariance = (1 / n_samples) * (X - X.mean(axis=0)).T.dot(Y - Y.mean(axis=0))
    # 相关系数的分母 方差开根号 sqrt(D(X)), sqrt(D(Y))
    std_dev_X = np.expand_dims(calculate_std_dev(X), 1)
    std_dev_Y = np.expand_dims(calculate_std_dev(Y), 1)
    # 相关系数计算公式
    correlation_matrix = np.divide(covariance, std_dev_X.dot(std_dev_Y.T))

    return np.array(correlation_matrix, dtype=float)

# ============================ Evaluation Metrics =========================== #
def accuray_score(y_true, y_pred):
    """
    Compare y_true to y_pred and return the accuracy
    """
    try:
        accuracy = np.sum(y_true == y_pred, axis=0) / len(y_true)
    except ValueError as e:
        print("Please ensure input params must not be zero or None!", e)
    else:
        return accuracy

def precision_score():
    pass

def recall_score():
    pass

def f_score():
    pass


import numpy as np
from HW2.tools import norm_p

def steepest_descent_direction(x):
    return -1 * f_provider.grad(*x)

def newton_direction(x):
    h_i = np.linalg.inv(f_provider.hessian(*x))
    g = f_provider.grad(*x)
    return -1 * np.dot(h_i, g)

last_BFGS_H, last_step_length, last_x = [None] * 3
def BFGS_direction(x):
    global last_BFGS_H, last_step_length, last_x

    if last_BFGS_H is None:
        H = np.linalg.inv(f_provider.hessian(*x))
    else:
        last_p = -1 * np.dot(last_BFGS_H, f_provider.grad(*x))
        x_kPlus1 = last_x
        s = last_step_length * last_p
        x_k = x_kPlus1 - s
        y = f_provider.grad(*x_kPlus1) - f_provider.grad(*x_k)
        # print("-----------", s,"============", y)
        m = 1 / np.dot(s, y)
        s = s[:, None]  # shape: (size of x,) -> (size of x,1)
        y = y[:, None]
        s_yT = np.dot(s, y.T)
        y_sT = np.dot(y, s.T)
        s_sT = np.dot(s, s.T)
        mL = np.identity(np.shape(x)[0]) - m * s_yT
        mR = np.identity(np.shape(x)[0]) - m * y_sT
        rL = np.dot(np.dot(mL, last_BFGS_H), mR)
        rR = m * s_sT
        H = rL + rR

    last_BFGS_H = H
    return -1 * np.dot(H, f_provider.grad(*x))

def first_wolf_condition(x, p, alpha, c):
    """
    sufficient decrease condition
    """
    new_x = x + alpha * p
    return f_provider.f(*new_x) <= f_provider.f(*x) + c * alpha * np.dot(p, f_provider.grad(*x))

def backtracking_line_length(x, p):
    alpha, m, c = 1, .5, .5  # parameters
    while not first_wolf_condition(x, p, alpha, c):
        alpha = alpha * m
        if alpha < TOLERANCE:
            return alpha
    return alpha

TOLERANCE, MAX_STEP = .001, 10000
def line_search(x, function_provider, descent_direction_algorithm=steepest_descent_direction):
    global f_provider, last_BFGS_H, last_x, last_step_length
    f_provider = function_provider
    last_BFGS_H = None
    step = 0
    while norm_p(f_provider.grad(*x)) >= TOLERANCE and step < MAX_STEP:
        p = descent_direction_algorithm(x)
        alpha = backtracking_line_length(x, p)
        x = x + alpha * p
        last_x, last_step_length = x, alpha
        step += 1
        # print('{}. new x: {} using alpha={}'.format(step, x, alpha))
    print('number of steps: ', step)
    return x


import json
import numpy as np
import matplotlib.pyplot as plt

#--------------------------------------
#USER PARAM
#--------------------------------------
file_name='01-cluster_data.json'
n_clusters=3

#--------------------------------------
#LOAD DATA
#--------------------------------------
f = open(file_name,)
data = json.load(f)

#CONVERT TO NUMPY ARRAY WITH 2 COLUMNS
x=[]; y=[]; true_label=[]
for i in data.keys():
	x=x+data[i][0]
	y=y+data[i][1]
	for j in range(0,len(data[i][0])): true_label.append(int(i))
labels=np.array(true_label) #.reshape(len(true_label), 1)
X=np.transpose(np.array([x,y]))

#PLOTTING FUNCTION
def plot(title=''):
	#PLOT KMEANS LABELS
	fig, ax = plt.subplots()
	sp=ax.scatter(X[:,0], X[:,1],c=labels,marker=".", cmap="viridis")
	plt.colorbar(sp)
	plt.title(title)
	plt.show()

plot('CORRECT LABELS')

#--------------------------------------
#KMEANS (PARTITION BASED METHOD)
#--------------------------------------
print("---------KMEANS-----------")

from sklearn.cluster import KMeans
import numpy as np

# X = np.array([[1, 2], [1, 4], [1, 0],
#               [10, 2], [10, 4], [10, 0]])
print("X SHAPE:", X.shape)

#TEST WITH n_clusters=2
n_clusters=2
kmeans = KMeans(n_clusters=2, random_state=0).fit(X)
labels=np.array(kmeans.labels_)
#EXPLORE SELECT OBJECT ATTRIBUTIES
print("n_iter_:",kmeans.n_iter_)
print("n_features_in_:",kmeans.n_features_in_)
print("cluster_centers_:",kmeans.cluster_centers_)
print("inertia_:",kmeans.inertia_)
print("PREDICTION-1:", kmeans.predict([[0, 0]]))
print("PREDICTION-2:", kmeans.predict([[1, 1], [12, 3]]))

plot("KMEANS LABELS WITH NCLUSTER="+str(n_clusters))

#TUNING: LOOP OVER NCLUSTER PARAMETER
for n_clusters in range(3,6):
	kmeans = KMeans(n_clusters=n_clusters, random_state=0).fit(X)
	labels=np.array(kmeans.labels_)
	plot("KMEANS LABELS WITH NCLUSTER="+str(n_clusters))

#--------------------------------------
#DBSCAN (DENSITY BASED METHOD)
#--------------------------------------
print("---------DBSCAN-----------")
from sklearn.cluster import DBSCAN
import numpy as np

#TUNING: LOOP OVER EPS PARAM
for eps in [0.25,0.5,0.75,1.0,1.25,1.5,2.0]:
	clustering = DBSCAN(eps=eps).fit(X)
	labels=clustering.labels_
	plot("DBSCAN LABELS WITH eps="+str(eps))