from __future__ import division
import numpy as np
import math as mt
import decimal as dm
import time as tm
'''-----------------------------------------------------------------------------
                        Auxiliar Functions:
-----------------------------------------------------------------------------'''
error = lambda y,x: y*(10**(-x))

F = lambda x: (10*mt.exp(-0.5*x)*mt.cos(2*x) -4) #F(x)

F_deriv = lambda x: mt.exp(-0.5*x)*(-20*mt.sin(2*x)-5*mt.cos(2*x)) #F'(x)

F_dderiv = lambda x: mt.exp(-0.5*x)*(20*mt.sin(2*x)-37.5*mt.cos(2*x)) #F''(x)

f = lambda x: 0.5*(mt.acos((2*mt.exp(0.5*x))/5))#f(x)=1/2*arcos(2e^0.5x/5)

'''-----------------------------------------------------------------------------
                        Iterative Method:
-----------------------------------------------------------------------------'''

def itMethod(x0,eps):
    x1=f(x0)
    err=abs(x1-x0)
    i=1
    while err > eps:
        x0=x1
        x1=f(x0)
        err=abs(x1-x0)
        i+=1
    print("Iterações: "+str(i)+"\n")
    print("Raíz: "+str(dm.Decimal(x1))+"\n")
    print("Erro: "+str(dm.Decimal(err))+"\n")
    return

'''-----------------------------------------------------------------------------
                        Newton Method:
-----------------------------------------------------------------------------'''
def newtonMethod(x0,a,b,x_der_min,x_dder_max,eps):
    min_fd=F_deriv(x_der_min) #F' min value
    max_fdd=F_dderiv(x_dder_max) #F'' max value
    m=(1/2)*(min_fd/max_fdd) #multiplier
    err=abs(b-a) #x0 error
    x1=0
    i=0; #iterations
    while(err>eps):
        x1=x0-(F(x0)/F_deriv(x0))
        err=m*(err**2)
        x0=x1
        i+=1
    print("Iterações: "+str(i)+"\n")
    print("Raíz: "+str(dm.Decimal(x0))+"\n")
    print("Erro: "+str(dm.Decimal(err))+"\n")
    return

#Tests-------------------------------------------
print("Newton Method:\n")
newtonMethod(0.54,0.5,0.54,0.5,0.5,error(5,12))

print("Itera Method:\n")
itMethod(0.54,error(5,12))


import numpy as np
from numpy.linalg import norm
from functools import wraps
from time import time

def power_vec(vector):
    return np.square(np.abs(vector))

def power_tot(vector):
    powers = power_vec(vector)
    return np.sum(powers)

def normalize_vec(vector):
    P = power_tot(vector)
    return vector / np.sqrt(P)

def normalize_pow(vector):
    return vector / np.sum(vector)

def MSE(vector1, vector2):
    vector1 = np.reshape(vector1, (-1,))
    vector2 = np.reshape(vector2, (-1,))
    return np.sum(np.square(vector1 - vector2))/vector1.size


import numpy as np
import matplotlib.pyplot as plt
from PIL import Image

max_iter = 80
divergence_radius = 4
canvas_shape = (600, 600)

def mandel_iter(canvas):

    real_c, cmplx_c = canvas
    real_z, cmplx_z = np.zeros(canvas_shape, dtype="float64"), np.zeros(
        canvas_shape, dtype="float64"
    )

    iter_array = np.zeros(canvas_shape, dtype="uint8")
    done_mask = np.zeros(canvas_shape, dtype="uint8")

    for idx in range(max_iter):

        # (a + bi) ** 2

        # a**2 - b**2
        # 2 * a * b

        real_z, cmplx_z = (
            real_z ** 2 - cmplx_z ** 2 + real_c,
            2 * real_z * cmplx_z + cmplx_c,
        )

        dists = real_z ** 2 + cmplx_z ** 2

        done_mask[dists >= divergence_radius] = 1
        # done_mask = done_mask | (dists >= divergence_radius)

        real_z[done_mask == 1] = 0
        cmplx_z[done_mask == 1] = 0

        iter_array[done_mask != 1] = idx

        if np.sum(done_mask == 0) == 0:
            break

    return iter_array

def mandelbrot(zoom=0.5, center=(0, 0), filename="deneme.png"):
    _extents = 2 / (2 ** zoom)

    x_lim = [center[0] - _extents, center[0] + _extents]
    y_lim = [center[1] - _extents, center[1] + _extents]

    x_range = np.linspace(x_lim[0], x_lim[1], canvas_shape[1], dtype="float64").reshape(
        1, canvas_shape[1]
    )
    canvas_real = np.repeat(x_range, canvas_shape[0], axis=0)
    y_range = np.linspace(y_lim[0], y_lim[1], canvas_shape[0], dtype="float64").reshape(
        canvas_shape[0], 1
    )
    canvas_cmplx = np.repeat(y_range[::-1, :], canvas_shape[1], axis=1)

    res = mandel_iter((canvas_real, canvas_cmplx))

    plt.imshow(res)
    plt.set_cmap("hot")
    plt.axis("off")
    plt.savefig(filename, bbox_inches="tight")

"""
BYTIE REPO : https://github.com/jbytecode/bytie

1 - mandelbrot çizdir : mrgranddy

2 - parametre alıp zoomdur/scale
    yaparak çizdir

2.5 - opsiyonel:
      eğer ilginç bölgeleri bulmanın
      bir yolu varsa onu bul

3 - bunun bi apisini yap

4 - bytie'ye bağla

"""


import p2_data as data
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import OneHotEncoder
import time

# *****************************************************************
# ====================helper functions=========================

def NLL(X, y, W, reg=0.0):
	'''
		Calculate negative log likelihood for softmax regression.
	'''
	# YOUR CODE GOES BELOW
	mu = X @ W # m x k
	exp_mu = np.exp(mu)
	prob = exp_mu / exp_mu.sum(axis=1).reshape(-1, 1)
	groundTruth = y * np.log(prob)
	NLL = -groundTruth.sum(axis=1).sum() + reg * np.diag(W.T @ W).sum()

	return NLL

def grad_softmax(X, y, W, reg=0.0):
	'''
		Return the gradient of W for softmax regression.
	'''
	# YOUR CODE BELOW
	mu = X @ W
	exp_mu = np.exp(mu)
	prob = exp_mu / exp_mu.sum(axis=1).reshape(-1, 1)
	grad = X.T @ (prob - y) + reg * W

	return grad

def predict(X, W):
	'''
		Return y_pred with dimension m x 1.
	'''
	# YOUR CODE BELOW
	mu = X @ W
	exp_mu = np.exp(mu)
	prob = exp_mu / exp_mu.sum(axis=1).reshape(-1, 1)
	y_pred = np.argmax(prob, axis=1).reshape(-1, 1)
	return y_pred

def get_accuracy(y_pred, y):
	'''
		Return the percentage of same bits between y_pred and y.
	'''
	diff = (y_pred == y).astype(int)
	accu = 1. * diff.sum() / len(y)
	return accu

def grad_descent(X, y, reg=0.0, lr=1e-5, eps=1e-6, \
	max_iter=500, print_freq=20):
	'''
		X is matrix with dimension m x (n + 1).
		y is label with dimension m x 1.
		reg is the parameter for regularization.
		lr is the learning rate.
		eps is the threshold of the norm for the gradients.
		max_iter is the maximum number of iterations.
		print_freq is the frequency of printing the report.

		Return the optimal weight by gradient descent and
		the corresponding learning objectives.
	'''
	m, n = X.shape
	k = y.shape[1]
	nll_list = []
	# initialize the weight and its gradient
	W = np.zeros((n, k))
	W_grad = np.ones((n, k))
	print('==> Running gradient descent...')
	iter_num = 0
	t_start = time.time()
	# Running the gradient descent algorithm
	# Update W
	# Calculate learning objectives
	# YOUR CODE GOES BELOW
	while iter_num < max_iter and np.linalg.norm(W_grad) > eps:
		# calculate NLL
		nll = NLL(X, y, W, reg=reg)
		if np.isnan(nll):
			break
		nll_list.append(nll)
		# calculate gradients and update W
		W_grad = grad_softmax(X, y, W, reg=reg)
		W -= lr * W_grad

		if (iter_num + 1) % print_freq == 0:
			print('-- Iteration {} - \
				negative log likelihood {: 4.4f}'.format(iter_num + 1, nll))
		iter_num += 1
	# benchmark
	t_end = time.time()
	print('-- Time elapsed for running gradient descent: {t:2.2f} \
		seconds'.format(t=t_end - t_start))

	return W, nll_list

def accuracy_vs_lambda(X_train, y_train_OH, X_test, y_test, lambda_list):
	'''
		Generate accuracy for all given regularization parameters.
		Generate a plot of accuracy vs lambda.
		Return the lambda with optimal accuracy.
	'''
	# Find corresponding accuracy values for each parameter
	accu_list = []
	# YOUR CODE BELOW
	for reg in lambda_list:
		W, nll_list = grad_descent(X_train, y_train_OH, reg=reg, lr=2e-5, \
		print_freq=50)
		y_pred = predict(X_test, W)
		accuracy = get_accuracy(y_pred, y_test)
		accu_list.append(accuracy)
		print('-- Accuracy is {:2.4f} for lambda = {:2.2f}'.format(accuracy, reg))
	# Plot accuracy vs lambda
	print('==> Printing accuracy vs lambda...')
	plt.style.use('ggplot')
	plt.plot(lambda_list, accu_list)
	plt.title('Accuracy versus Lambda in Softmax Regression')
	plt.xlabel('Lambda')
	plt.ylabel('Accuracy')
	plt.savefig('ACCURACY_vs_LAMBDA_2b.png', format = 'png')
	plt.close()
	print('==> Plotting completed.')
	# Find optimal lambda
	opt_lambda_index = np.argmax(accu_list)
	reg_opt = lambda_list[opt_lambda_index]
	return reg_opt

# *****************************************************************
# ====================main driver function: Softmax=========================
if __name__ == '__main__':
	# STEP 0: LOADING DATA
	df_train = data.df_train
	df_test = data.df_test
	'''
		X is a matrix with dimension m x n
		y is a vector with dimension m x 1
	'''
	X_train = data.X_train
	y_train = data.y_train
	X_test = data.X_test
	y_test = data.y_test
	# stacking an array of ones
	X_train = np.hstack((np.ones_like(y_train), X_train))
	X_test = np.hstack((np.ones_like(y_test), X_test))
	# one hot encoder
	enc = OneHotEncoder()
	y_train_OH = enc.fit_transform(y_train.copy()).astype(int).toarray()
	y_test_OH = enc.fit_transform(y_test.copy()).astype(int).toarray()

	# STEP 1: Accuracy versus lambda
	print('==> Step 1: Finding optimal regularization parameter...')
	# Fill in the code in NLL, grad_softmax, and grad_descent
	# Then, fill in predict and accuracy_vs_lambda
	lambda_list = [0.01, 0.1, 0.5, 1.0, 10.0, 50.0, 100.0, 200.0, 500.0, 1000.0]
	reg_opt = accuracy_vs_lambda(X_train, y_train_OH, X_test, y_test, lambda_list)
	print('-- Optimal regularization parameter is {:2.2f}'.format(reg_opt))
	# STEP 2: Convergence plot
	W_gd, nll_list_gd = grad_descent(X_train, y_train_OH, reg=reg_opt, max_iter=1500, lr=2e-5, \
		print_freq=100)
	print('==> Step 2: Plotting convergence plot...')
	plt.style.use('ggplot')
	# Plot the learning curve of NLL vs Iteration
	# YOUR CODE BELOW
	nll_gd_plot, = plt.plot(range(len(nll_list_gd)), nll_list_gd)
	plt.setp(nll_gd_plot, color = 'red')
	plt.title('Convergence Plot on Softmax Regression with $\lambda = {:2.2f}$'.format(reg_opt))
	plt.xlabel('Iteration')
	plt.ylabel('NLL')
	plt.savefig('CONVERGENCE_2b.png', format = 'png')
	plt.close()
	print('==> Plotting completed.')


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

'''
In a submodule analysis implement a function that given the water level time history (dates, levels)
for a station computes a least-squares fit of a polynomial of degree p to water level data. The function
should return a tuple of (i) the polynomial object and (ii) any shift of the time (date) axis (see below).
The function should have the signature:

def polyfit(dates, levels, p):

Typical usage for a polynomial of degree 3 would be:
poly, d0 = polyfit(dates, levels, 3)
where poly is a numpy.poly1d object an d0 is any shift of the date (time) axis.
'''

# Task 2F
def polyfit(dates, levels, p):
    dates_coord = []
    for each_date in dates:
        dates_float = matplotlib.dates.date2num(each_date)
        dates_coord.append(dates_float)

    x = dates_coord
    y = levels
    d0 = x[-1]
    p_coeff = np.polyfit(x - d0, y, p)
    poly = np.poly1d(p_coeff)
    return poly, d0

    '''

    # Create set of 10 data points on interval (1000, 1002)
x = np.linspace(10000, 10002, 10)
y = [0.1, 0.09, 0.23, 0.34, 0.78, 0.74, 0.43, 0.31, 0.01, -0.05]

# Using shifted x values, find coefficient of best-fit
# polynomial f(x) of degree 4
p_coeff = np.polyfit(x - x[0], y, 4)

# Convert coefficient into a polynomial that can be evaluated
# e.g. poly(0.3)
poly = np.poly1d(p_coeff)

# Plot original data points
plt.plot(x, y, '.')

# Plot polynomial fit at 30 points along interval (note that polynomial
# is evaluated using the shift x)
x1 = np.linspace(x[0], x[-1], 30)
plt.plot(x1, poly(x1 - x[0]))

# Display plot
plt.show()
    '''


from Sequences.Simple import naturals, arithmetic
from Sequences.Manipulations import partial_prods, prepend, hypersequence, differences, offset
from Sequences.NiceErrorChecking import require_integers, require_geq, require_iterable

from collections import defaultdict
from sympy import sieve, isprime
from math import prod

##############################
## CLASSES OF PRIME NUMBERS ##
##############################

def primes():
    """
    Prime Numbers: Positive integers with exactly two factors\n
    OEIS A000040
    """

    upper = 2
    while True:
        upper *= 2
        yield from sieve.primerange(upper//2, upper)

def odd_primes():
    """
    Odd Primes: Primes that are odd positive integer\n
    OEIS A065091
    """

    yield from offset(primes(),1)

def twin_primes():
    """
    Twin Primes: Primes that are two more or less than another prime\n
    OEIS A001097
    """

    P = primes()

    a,b,c = next(P),next(P),next(P)

    while True:
        if b-2 == a or b+2 == c:
            yield b
        a,b,c = b,c,next(P)

def twin_prime_pairs():
    """
    Twin Prime Pairs: Primes that are two more or less than another prime\n
    OEIS A077800
    """

    P = primes()

    a,b = next(P),next(P)

    while True:
        if a+2 == b:
            yield (a,b)
        a,b = b,next(P)

def n_gap_prime_pairs(n):
    """
    Pairs of primes (p,q) such that p+n = q\n
    OEIS A077800, A156274, A156320, A140445, A156323, A140446, A272815,
         A156328, A272816, A140447
    """

    require_integers(["n"],[n])
    require_geq(["n"],[n],1)

    if n % 2 == 1:
        raise ValueError("n must be even, for odd n either there is no pair or the only pair is (2,2+n)")

    else:
        P = primes()
        lo = next(P)
        hi = [next(P)]

        while True:
            while hi[-1] < lo+n:
                hi.append(next(P))

            if lo+n in hi:
                yield (lo,lo+n)

            lo = hi.pop(0)

def prime_tuples(K):
    """
    Tuples of primes (p,q,r...) with gaps specified by K\n
    OEIS
    """

    for gap in K:
        if gap % 2 != 0:
            raise ValueError("gaps must be even, the only odd gaps are between 2 and another prime")

    P = primes()
    L = []

    def all_gaps(K,L):
        T = tuple([L[0]+k for k in K])
        for t in T:
            if t not in L:
                return ()
        return T

    while True:
        while len(L) <= len(K):
            L.append(next(P))

        while L[-1] <= L[0]+(max(K)):
            L.append(next(P))

        T = all_gaps(K,L)
        if T:
            yield T

        L = L[1:]

def superprimes():
    """
    Prime Indexed Primes: The pth primes for p in primes\n
    OEIS A006450
    """

    yield from hypersequence(primes())

def sophie_germain_primes():
    """
    Sophie Germain Primes: Primes such that 2p+1 is prime\n
    OEIS A005384
    """

    S = set([2])

    for p in odd_primes():
        S.add(p)
        if (p-1)//2 in S:
            yield (p-1)//2

        S = {s for s in S if s > (p-1)//2}

def safe_primes():
    """
    Safe Primes: Primes such that (p-1)/2 is prime\n
    OEIS A005385
    """

    S = set([2])

    for p in odd_primes():
        S.add(p)
        if (p-1)//2 in S:
            yield p

        S = {s for s in S if s > (p-1)//2}

def pythagorean_primes():
    """
    Pythagorean Primes: Primes that can be the hypotenuse of an integer right triangle\n
    OEIS A002144
    """

    for p in primes():
        if (p-1)%4 == 0:
            yield p

def linear_primes(a,b):
    """
    Primes of the form a*n+b for naturals n

    Args:
        a -- multiplicative constant
        b -- additive constant

    OEIS A002145
    """

    for p in primes():
        if (p-a)%b == 0:
            yield p

def congruent_primes(n,k):
    """
    Primes that are congruent to n modulo k

    Args:
        n -- remainder
        k -- modulus

    OEIS A002145
    """

    for p in primes():
        if p%k == n:
            yield p

def blum_primes():
    """
    Primes such that P = 2p+1 and p = 2q+1 where p and q are odd primes\n
    OEIS
    """

    for p in congruent_primes(3,4):
        a = (p-1)//2
        b = (a-1)//2

        if a%2 == 1 and b % 2 == 1:
            if isprime(a) and isprime(b):
                yield p

#####################
## CLOSELY RELATED ##
#####################

def prime_gaps():
    """
    Prime Gaps: Gaps between successive primes\n
    OEIS A001223
    """

    yield from differences(primes())

def primorial():
    """
    Primoral Numbers: Cumulative product of primes\n
    OEIS A002110
    """

    yield from  prepend(1,partial_prods(primes()))

def prime_powers():
    """
    Prime Powers: Powers of prime numbers\n
    OEIS A000961
    """

    yield 1

    D = defaultdict(list)

    for q in naturals(2):
        if q not in D:
            yield q
            D[q + q] = [q]

        else:
            if len(D[q]) == 1:
                yield q

            for p in D[q]:
                D[p+q].append(p)

            del D[q]

def prime_characteristic():
    """
    Characteristic Function of the Primes: For each positive integer 1 if the number is prime otherwise 0\n
    OEIS A010051
    """

    cur = 1

    for p in primes():
        for i in range(p-cur):
            yield 0

        yield 1

        cur = p+1

def prime_counting():
    """
    Prime Counting Function: Count of primes less than each non-negative integer\n
    OEIS A000720
    """

    ctr = 0
    cur = 0

    for p in primes():
        for i in range(p-cur):
            yield ctr

        ctr += 1
        cur = p

def prime_product(P,inclusive=False):
    """
    Naturals such that all prime factors are in the set P

    Args:
        P -- an interable consisting of primes
        inclusive -- bool, if True only numbers divisible by all elements of P are included

    OEIS
    """

    require_iterable(["P"],[P])

    P = set(P)
    I = prod(P)

    for p in P:
        if not isprime(p):
            raise Exception(f"All elements of P must be prime, {p} is not prime")

    if inclusive:
        for n in arithmetic(I,I):
            m = n

            for p in P:
                while m % p == 0:
                    m //= p

            if m == 1:
                yield n

    else:
        for n in naturals(2):
            m = n

            for p in P:
                while m % p == 0:
                    m //= p

            if m == 1:
                yield n

def prime_signatures():
    """
    Prime Signatures: The prime signature of each positive integer, returns tuples\n
    OEIS A124010
    """

    D = defaultdict(list)

    yield ()

    for q in naturals(2):
        if q not in D:
            yield (1,)
            D[q + q] = [q]

        else:
            T = []
            r = q

            for p in D[q]:
                T.append(0)
                D[p+q].append(p)

                while r % p == 0:
                    r //= p
                    T[-1] += 1

            yield tuple(T)

            del D[q]

def odius_signature():
    """
    Positive integers which have an odd number of 1s in the binary expansions of their prime signatures
    OEIS A000028
    """

    for n,f in enumerate(prime_signatures(),1):
        total = 0
        for m in f:
            while m != 0:
                m,r = divmod(m,2)
                total ^= r
        if total == 1:
            yield n

def evil_signature():
    """
    Positive integers which have an odd number of 1s in the binary expansions of their prime signatures
    OEIS A000379
    """

    for n,f in enumerate(prime_signatures(),1):
        total = 0
        for m in f:
            while m != 0:
                m,r = divmod(m,2)
                total ^= r
        if total == 0:
            yield n

if __name__ == '__main__':
    from Sequences.Manipulations import simple_test

    print("Primes")
    simple_test(primes(),15,
                "2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47")

    print("\nPrimorials")
    simple_test(primorial(),9,
                "1, 2, 6, 30, 210, 2310, 30030, 510510, 9699690")

    print("\nPrime Powers")
    simple_test(prime_powers(),16,
                "1, 2, 3, 4, 5, 7, 8, 9, 11, 13, 16, 17, 19, 23, 25, 27")

    print("\nPythagorean Primes")
    simple_test(pythagorean_primes(),13,
                "5, 13, 17, 29, 37, 41, 53, 61, 73, 89, 97, 101, 109")

    print("\nPrime Counting Function")
    simple_test(prime_counting(),18,
                "0, 0, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7")

    print("\nCharacteristic Function of the Primes")
    simple_test(prime_characteristic(),18,
                "0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0")

    print("\nSuperprimes")
    simple_test(superprimes(),13,
                "3, 5, 11, 17, 31, 41, 59, 67, 83, 109, 127, 157, 179")

    print("\nSophie Germain Primes")
    simple_test(sophie_germain_primes(),13,
                "2, 3, 5, 11, 23, 29, 41, 53, 83, 89, 113, 131, 173")

    print("\nSafe Primes")
    simple_test(safe_primes(),13,
                "5, 7, 11, 23, 47, 59, 83, 107, 167, 179, 227, 263, 347")

    print("\nTwin Primes")
    simple_test(twin_primes(),14,
                "3, 5, 7, 11, 13, 17, 19, 29, 31, 41, 43, 59, 61, 71")

    print("\nTwin Prime Pairs")
    simple_test(twin_prime_pairs(),6,
                "(3, 5), (5, 7), (11, 13), (17, 19), (29, 31), (41, 43)")

    print("\nSexy Prime Pairs")
    simple_test(n_gap_prime_pairs(6),5,
                "(5, 11), (7, 13), (11, 17), (13, 19), (17, 23)")

    print("\nPrime Tuples of form (0, 4, 6, 10, 12)")
    simple_test(prime_tuples((0, 4, 6, 10, 12)),2,
                "(7, 11, 13, 17, 19), (97, 101, 103, 107, 109)")

    print("\nPrime Gaps")
    simple_test(prime_gaps(),18,
                "1, 2, 2, 4, 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6")

    print("\nPrimes of form 3k+4")
    simple_test(linear_primes(3,4),14,
                "3, 7, 11, 19, 23, 31, 43, 47, 59, 67, 71, 79, 83, 103")

    print("\nBlum's Special Primes")
    simple_test(blum_primes(),10,
                "23, 47, 167, 359, 719, 1439, 2039, 2879, 4079, 4127")

    print("\n(3,5)-Prime Product Numbers")
    simple_test(prime_product([3,5]),13,
                "3, 5, 9, 15, 25, 27, 45, 75, 81, 125, 135, 225, 243")

    print("\n(3,5)-Prime Product Numbers (requiring both)")
    simple_test(prime_product([3,5],inclusive=True),11,
                "15, 45, 75, 135, 225, 375, 405, 675, 1125, 1215, 1875")

    print("\nPrime Signatures")
    simple_test(prime_signatures(),9,
                "(), (1,), (1,), (2,), (1,), (1, 1), (1,), (3,), (2,)")

    print("\nOdius Signature Numbers")
    simple_test(odius_signature(),16,
                "2, 3, 4, 5, 7, 9, 11, 13, 16, 17, 19, 23, 24, 25, 29, 30")

    print("\nEvil Signature Numbers")
    simple_test(evil_signature(),15,
                "1, 6, 8, 10, 12, 14, 15, 18, 20, 21, 22, 26, 27, 28, 32")


# %%
import numpy as np
import matplotlib.pyplot as plt

def newton_descent(f, gf, hf, x0, step_size, tolerance):
    """
    Args:
        f: function to minimize
        gf: gradient of f
        hf: hessian of f
        x0: initial x value
        step_size: step size
        tolerance: tolerance for stopping condition

    Returns:
        x_hist: history of x values as gradient descent updates x
        f_hist: history of f(x) values as gradient descent updates x
        iterations: number of iterations to reach tolerance
    """

    x_hist = []
    x_hist.append(x0)

    f_hist = []
    f_hist.append(f(x0))

    iterations = 0

    x = x0

    while True:
        inv_hf = np.linalg.inv(hf(x))
        x += step_size * -inv_hf @ gf(x)

        x_hist.append(x)
        f_hist.append(f(x))
        iterations += 1

        if abs(f_hist[-1] - f_hist[-2]) < tolerance:
            break

    return x_hist, f_hist, iterations

if __name__ == '__main__':

    def f(x: np.ndarray) -> float:
        """
        f(x) = x_1**2 + x_2**2 + x_3**2 ... + x_n**2
        """
        return np.linalg.norm(x)**2

    def gf(x: np.ndarray) -> np.ndarray:
        """
        gf(x) = 2 * x
        """
        return 2 * x

    def hf(x: np.ndarray) -> np.ndarray:
        """
        hf(x) = 2 * np.eye(n)
        """
        return 2 * np.eye(len(x))

    x0 = np.array([2., 4., -1., 1.])

    # def f(x: np.ndarray) -> float:
    #     Q = np.array([[2, 0, 0],
    #                   [0, 4, 0],
    #                   [0, 0, 6]])
    #     b = np.array([2, 2, 2])
    #     return 0.5 * (x.T @ Q @ x) - (b.T @ x)

    x0 = np.array([2., -2., 2])

    step_size = 1
    tolerance = 1e-5

    x_hist, f_hist, iterations = newton_descent(
        f, gf, hf, x0, step_size, tolerance)
    print(f'min x value: {x_hist[-1]}')
    print(f'min f value: {f_hist[-1]}')
    print(f'iterations to find minimum (within tolerance): {iterations-1}')

    # plot f_hist
    plt.plot(f_hist)
    plt.xlabel('iteration')
    plt.ylabel('$f(x)$')
    plt.title('Newton descent')
    plt.show()


import numpy as np

### Chú thích ###

# z là đầu vào hàm kích hoạt

### Các hàm kích hoạt ###

def linear(z, m):
	return m*z

def elu(z, alpha):
	return z if z >= 0 else alpha*(np.exp(z) - 1)

def leakyrelu(z, alpha):
	return max(alpha * z, z)

def relu(z):
  	return max(0, z)

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

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

### Các đạo hàm ###

def linear_prime(z, m):
	return m

def elu_prime(z, alpha):
	return 1 if z > 0 else alpha*np.exp(z)

def leakyrelu_prime(z, alpha):
	return 1 if z > 0 else alpha

def sigmoid_prime(z):
  	return sigmoid(z) * (1-sigmoid(z))

def relu_prime(z):
  	return 1 if z > 0 else 0

def tanh_prime(z):
	return 1 - np.power(tanh(z), 2)


from collections import OrderedDict
import numpy as np
import pandas as pd
from statsmodels.tsa.arima_process import ArmaProcess
from statsmodels.tsa.arima_model import ARIMA
from tests import project_test, generate_random_tickers, generate_random_dates, assert_output

@project_test
def test_fit_arima(fn):
    np.random.seed(200)

    ar_params = np.array([1, -0.5])
    ma_params = np.array([1, -0.3])
    ret = ArmaProcess(ar_params, ma_params).generate_sample(nsample=5*252)

    ret = pd.Series(ret)
    drift = 100
    price = pd.Series(np.cumsum(ret)) + drift

    lret = np.log(price) - np.log(price.shift(1))
    lret = lret[1:]

    #TODO: choose autoregression lag of 1
    AR_lag_p = 1

    #TODO: choose moving average lag of 1
    MA_lag_q = 1

    #TODO: choose order of integration 1
    order_of_integration_d = 1

    #TODO: Create a tuple of p,d,q
    order = (AR_lag_p, order_of_integration_d, MA_lag_q)

    arima_model = ARIMA(lret.values, order=order)
    arima_result = arima_model.fit()

    fittedvalues = arima_result.fittedvalues
    arparams = arima_result.arparams
    maparams = arima_result.maparams

    fn_inputs = {
        'lret': lret
        }

    fn_correct_outputs = OrderedDict([
        ('fittedvalues',fittedvalues),
        ('arparams',arparams),
        ('maparams',maparams),
        ])

    assert_output(fn, fn_inputs, fn_correct_outputs)


import streamlit as st
from datetime import datetime
import matplotlib.pyplot as plt
from tree_generator import BinomialTree
import numpy as np
st.set_option('deprecation.showPyplotGlobalUse', False)
# headings
month = datetime.now().month
title = "Binomial Tree Option Pricing"
st.title(title + "🌲🎄")
st.sidebar.title("Parameters")
# user inputs on sidebar
S = st.sidebar.number_input('Stock Price (S)', value=100.,)
K = st.sidebar.number_input('Exercise Price (K)', value=100.,)
T = st.sidebar.number_input('Time Periods (T)', value=2., max_value=15.)
dt =  st.sidebar.number_input('Time step (dt)', value=1., max_value=15.,step=0.01)
r = st.sidebar.number_input('Inter-period Interest Rate (r)', value=0.05,)
q = st.sidebar.number_input('Dividend Yield (q)', value=0.0,)
sigma = st.sidebar.number_input('stock annualized volatility (sigma)', value=0.1,min_value=0.)

tree = BinomialTree()
tree.fit(r,q,sigma)
price_tree = tree.create_price_tree(S,dt,T)
st.sidebar.write("Stock Upper Factor (u) ", round(tree.u, 3))
st.sidebar.write("Stock Down Factor (d) ", round(tree.d, 3))
st.sidebar.write("Risk Neutral Probability (p) ", round(tree.p, 3))
# back to main body
st.header("*Cox-Ross-Rubinstein (CRR) binomial tree*")
st.markdown("This visualisation aims to explore the dynamics of CRR binomial tree in option pricing. "
            "https://en.wikipedia.org/wiki/Binomial_options_pricing_model"
            )
st.subheader('Key:')
c1,c2,c3,c4,c5 = st.columns(5)
with c1:
    price = st.checkbox('price tree')
with c2:
    European_call = st.checkbox('European Call tree')
with c3:
    European_put = st.checkbox('European Put tree')
with c4:
    American_call = st.checkbox('American Call tree')
with c5:
    American_put = st.checkbox('American Put tree')
# plot stock tree
if price:
    st.pyplot(tree.plot_tree(price_tree))
if European_call:
    payoff = tree.create_payoff_tree(price_tree,lambda x:np.fmax(x-K,0))
    st.pyplot(tree.plot_tree(payoff))
    st.write("European Call price ", round(tree.compute_payoff(payoff), 3))
if European_put:
    payoff = tree.create_payoff_tree(price_tree,lambda x:np.fmax(K-x,0))
    st.pyplot(tree.plot_tree(payoff))
    st.write("European put price ", round(tree.compute_payoff(payoff), 3))
if American_call:
    payoff = tree.create_payoff_tree(price_tree,lambda x:np.fmax(x-K,0),style='American')
    st.pyplot(tree.plot_tree(payoff))
    st.write("American Call price ", round(tree.compute_payoff(payoff,style='American'), 3))
if American_put:
    payoff = tree.create_payoff_tree(price_tree,lambda x:np.fmax(K-x,0),style='American')
    st.pyplot(tree.plot_tree(payoff))
    st.write("American Put price ", round(tree.compute_payoff(payoff,style='American'), 3))

st.subheader("Disclaimer")
st.write("All information aims to provide for educational purposes only and does not constitute financial advice")


# -*- coding: utf-8 -*-
import numpy as np
import numpy.random as npr
import matplotlib.pylab as plt

RANDOM_SEED = 1  # random seed
SIZE = 10000  # 試行回数
EXPECTED_V = 5  # 期待値

def x_uniform():
    # 一様分布
    # aからbまで一様の確率のときその間のxを返す。
    a = 0
    b = EXPECTED_V * 2 - a
    return npr.uniform(a, b, SIZE)

def x_poisson():
    # ポアソン分布
    # 単位時間あたりλ回起こる事象がちょうどk回発生するときのkを返す
    lambd = EXPECTED_V  # 期待値=λとなる
    return npr.poisson(lambd, size=SIZE)

def x_exponential():
    # 指数分布
    # 単位時間当たり平均λ回起こる事象＝平均1/λ時間で1回発生する事象の発生間隔がt時間であるときのtを返す
    # 平均余命など。
    lambd = 1.0 / EXPECTED_V  # 1分あたりlambd回発生する。= 平均1/lambd時間の間隔で1回発生
    return npr.exponential(1. / lambd, size=SIZE)  # 発生間隔をランダムに返す

def x_erlang():
    # アーラン分布
    # アーラン分布はガンマ分布の形状母数kを正整数に限定したもの
    k = 3
    lambd = k / EXPECTED_V  # 期待値 = k/λとなる。
    return npr.gamma(k, 1. / lambd, size=SIZE)

def histogram(x_distribution, ax, title="histogram"):
    mean = np.mean(x_distribution)
    print("%s: mean:%f" % (title, mean))
    bins = np.arange(np.floor(np.amin(x_distribution)),
                     np.ceil(np.amax(x_distribution)) + 1.0, 1.)
    n, bins, patches = ax.hist(
        x_distribution, bins=bins, align='left', normed=1, color="gray", edgecolor="k")

    ax.set_title(title, fontsize=14)
    ax.set_xlabel('value')
    ax.set_ylabel('frequency / N')

if __name__ == '__main__':
    npr.seed(RANDOM_SEED)
    fig = plt.figure()
    fig.suptitle('Histogram: N=%d, E=%-4.2f' % (SIZE, EXPECTED_V), fontsize=16)
    fig.subplots_adjust(left=None, bottom=None, right=None,
                        top=None, wspace=0.3, hspace=0.4)

    ax1 = fig.add_subplot(221)
    histogram(x_uniform(), ax1, title="uniform")

    ax2 = fig.add_subplot(222)
    histogram(x_poisson(), ax2, title="poisson")

    ax3 = fig.add_subplot(223)
    histogram(x_exponential(), ax3, title="exponential")

    ax4 = fig.add_subplot(224)
    histogram(x_erlang(), ax4, title="erlang(k=3)")

    plt.show()


"""Wilson score interval.

Gives a confidence interval for the mean of data sampled from i.i.d
Bernoulli distributions B(p).

In practice, this is extremely useful since it can be used to estimate
confidence intervals for machine learning models accuracy (and other
evaluation metrics as well).

source:
https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Wilson_score_interval
"""
import scipy.stats
import numpy as np

def wilson_score_continuity_correction(
    accuracy: float, num_inst: int, confidence: float = 0.95
):
    crit_point = scipy.stats.norm.ppf(1 - 0.5 * (1.0 - confidence))
    crit_point_sqr = crit_point ** 2

    denom = 2.0 * (num_inst + crit_point_sqr)
    mean_value = 2.0 * num_inst * accuracy

    dist = 1.0 + crit_point * np.sqrt(
        crit_point_sqr
        - 1 / num_inst
        + 4 * num_inst * accuracy * (1.0 - accuracy)
        + (4.0 * accuracy - 2.0)
    )

    lower_bound = (mean_value - dist) / denom
    upper_bound = (mean_value + dist) / denom

    lower_bound = max(0.0, lower_bound)
    upper_bound = min(1.0, upper_bound)

    return (lower_bound, upper_bound)

def wilson_score_interval(
    accuracy: float, num_inst: int, confidence: float = 0.95, conservative: bool = False
):
    crit_point = scipy.stats.norm.ppf(1 - 0.5 * (1.0 - confidence))
    crit_point_sqr_over_n = crit_point ** 2 / num_inst

    coeff_a = 1.0 / (1 + crit_point_sqr_over_n)
    coeff_b = accuracy * (1.0 - accuracy) if not conservative else 0.5

    mean_value = coeff_a * (accuracy + 0.5 * crit_point_sqr_over_n)
    dist = (
        crit_point
        * coeff_a
        * np.sqrt((coeff_b + 0.25 * crit_point_sqr_over_n) / num_inst)
    )

    lower_bound = mean_value - dist
    upper_bound = mean_value + dist

    if conservative:
        lower_bound = max(lower_bound, 0.0)
        upper_bound = min(upper_bound, 1.0)

    return (lower_bound, upper_bound)

def _test():
    accuracy = 0.9
    confidence = 0.975
    num_inst = 150

    print(wilson_score_interval(accuracy, num_inst, confidence, True))
    print(wilson_score_interval(accuracy, num_inst, confidence, False))
    print(wilson_score_continuity_correction(accuracy, num_inst, confidence))

if __name__ == "__main__":
    _test()


import numpy
import scipy.stats

def gen_paths(S0, r, sigma, T, M, I):
    """ Generates Monte Carlo paths for geometric Brownian motion.

    Parameters
    ==========
    S0: float
        initial stock/index value. Irrelevant if you only care about relative returns
    r: float
        constant short rate ("mean return")
    sigma: float
        constant volatility ("standard deviation")
    T: float
        final time horizon (always specify 1.0?)
    M: int
        number of time steps/intervals
    I: int
        number of paths to be simulated

    Returns
    =======
    paths : ndarray, shape (M+1, I)
    """

    dt = float(T) / M
    paths = numpy.zeros((M+1, I), numpy.float64)
    paths[0] = S0
    for t in range(1, M+1):
        rand = numpy.random.standard_normal(I)
        rand = (rand - rand.mean()) / rand.std()
        paths[t] = paths[t - 1] * numpy.exp(( r - 0.5 * sigma ** 2) * dt + sigma * numpy.sqrt(dt) * rand)
    return paths

if __name__ == '__main__':
    S0 = 100.
    r = 0.05
    sigma = 0.2
    T = 1.0
    M = 50
    I = 250000

    paths = gen_paths(S0, r, sigma, T, M, I)

    log_returns = numpy.log(paths[1:] / paths[0:-1])

    # How to apply metrics.ssr or metrics.pwa to this vectorised?
    print(len(log_returns[0]))


from sympy import Symbol, Derivative, sympify, pprint

def derivative_calc(expr, var):
    var = Symbol(var)
    derivative = Derivative(expr, var).doit()
    pprint(derivative)

if __name__ == "__main__":
    try:
        expr = sympify(input("Enter an expression to calculate: "))
        var = input("Enter the variable to differentiate with respect to: ")
    except Exception as err:
        print("Invalid input", err)
    else:
        derivative_calc(expr, var)

# todo : command line version


import math
import numpy as np
from matplotlib import pyplot as plt
from .Generaldistribution import Distribution

class Weibull(Distribution):
  """ Weibull distribution class for calculating and
  visualizing a Weibull distribution.

  Attributes:

      mean (float): the mean value of the distribution
      stdev (float): the standard deviation of the distribution

      data (list of floats): extracted from the data file

      lmbda (float): scale parameter of the distribution
      (missing an 'a' to prevent name clash with Python keyword)
      k (float): shape parameter of the distribution

  """
  def __init__(self, lmbda=1, k=1.5):
    self.lmbda = lmbda
    self.k = k
    self.calculate_mean()
    self.calculate_stdev()

  def calculate_mean(self, round_to=2):
    """ Method to calculate the mean

    Args:
        round_to (int): Round the mean value. [Default value: 2 floating point]

    Returns:
        float: mean of the distribution
    """
    gamma_factor = math.gamma(1 + (1 / self.k))
    self.mean = self.lmbda * gamma_factor
    return round(self.mean, round_to)

  def calculate_stdev(self, round_to=2):
    """ Method to calculate the standard deviation

    Args:
        round_to (int): Round the mean value. [Default value: 2 floating point]

    Returns:
        float: standard deviation of the distribution
    """
    first = math.gamma(1 + (2 / self.k))
    second = math.gamma(1 + (1 / self.k)) ** 2
    self.stdev = math.sqrt(self.lmbda ** 2 * (first - second))
    return round(self.stdev, round_to)

  def calculate_pdf(self, x, round_to=2):
    """ Probability density function calculator for the Weibull distribution.

    Args:
        x (float): point for caluclating the probability density function
        round_to (int): Round the mean value. [Default value: 2 floating point]

    Returns:
        float: probability density function
    """
    if x < 0:
      out = 0
    else:
      exp_factor = math.exp(-(x / self.lmbda) ** self.k)
      out = (self.k / self.lmbda) * exp_factor * \
            (x / self.lmbda) ** (self.k - 1)
    return round(out, round_to)

  def calculate_cdf(self, x, round_to=2):
    """
    Cumulative density function calculator for the Weibull distribution.
    Args:
        x (float): point for calculating the cumulative density function
        round_to (int): Round the mean value. [Default value: 2 floating point]

    Returns:
        float: cumulative density function output
    """
    if x < 0:
      out = 0
    else:
      out = 1 - math.exp(-(x / self.lmbda) ** self.k)
    return round(out, round_to)

  def plot_pdf(self, samples=250):
    """ Method to plot the pdf of the Weibull distribution.

    Args:
        samples (int): number of discrete data points

    Returns:
        list: y values for the pdf plot

    """
    x = np.linspace(0, 2.5, num=samples)
    y = np.zeros_like(x)

    for index, value in enumerate(x):
      y[index] = self.calculate_pdf(value)

    plt.plot(x, y, label=f"lambda={self.lmbda}; k={self.k}")
    plt.legend()
    plt.title(f"Probability Distribution Function for Weibull \
              Distribution lambda={self.lmbda} k={self.k}")
    plt.show()
    return y

  def __repr__(self):
    """ Method to output the characteristics of the Weibull instace.
    Args:
        None
    Returns:
        string: characteristics of the Weibull
    """
    return f"lambda: {self.lmbda}, k: {self.k}, mean: {self.mean}, \
            standard deviation: {self.stdev}"


import LinearAlgebraPurePython as la
import MachineLearningPurePy as ml

from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
import numpy as np

import matplotlib.pyplot as plt

import random
import sys

# Section 1: Fake data preparation
#   and visualization
X = [1.5,2.0,2.5,3.0,3.5,4.0]
def y_of_x(x):
    return 0.2*x + 0.7*x**2 + random.uniform(-1,1)
Y = []
for x in X:
    Y.append(y_of_x(x))

plt.scatter(X, Y)

# Section 2: Get fake data in correct format
X = la.transpose([X])
Y = la.transpose([Y])

# Section 3: Pure Python Tools Fit
poly_pp = ml.Poly_Features_Pure_Py(order = 2)
Xp = poly_pp.fit_transform(X)
print('PP Feature Names:', poly_pp.get_feature_names())
ls_pp = ml.Least_Squares(tol=2, add_ones_column=False)
ls_pp.fit(Xp, Y)
print()

# Section 4: SciKit Learn Fit
poly_sk = PolynomialFeatures(degree = 2)
X_poly = poly_sk.fit_transform(X)
print('SK Feature Names:', poly_sk.get_feature_names())
ls_sk = LinearRegression()
ls_sk.fit(X_poly, Y)
print()

# Section 5: Coefficients Comparison
tmp_ls_pp_coefs = sorted(ls_pp.coefs)
rounded_ls_pp_coefs = [round(x,8)+0 for x in la.transpose(tmp_ls_pp_coefs)[0]]
print('PurePy  LS coefficients:', rounded_ls_pp_coefs)

tmp_ls_sk_coefs = ls_sk.intercept_.tolist() + ls_sk.coef_[0][1:].tolist()
tmp_ls_sk_coefs = sorted(tmp_ls_sk_coefs)
rounded_ls_sk_coefs = [round(x,8)+0 for x in tmp_ls_sk_coefs]
print('SKLearn LS coefficients:', rounded_ls_sk_coefs, '\n')

print('Coef Deltas:', [rounded_ls_pp_coefs[i] - rounded_ls_sk_coefs[i]
        for i in range(len(rounded_ls_pp_coefs))])

# Section 6: Create Fake Test Data
XLS = [0,1,2,3,4,5]

# Section 6.1: Predict with Fake Test Data
#   Using Pure Python Tools
XLSpp = poly_pp.transform(la.transpose([XLS]))
YLSpp = ls_pp.predict(XLSpp)
YLSpp = la.transpose(YLSpp)[0]

# Section 6.2: Predict with Fake Test Data
#   Using SciKit Learn Tools
XLSsk = poly_sk.transform(la.transpose([XLS]))
YLSsk = ls_sk.predict(XLSsk)
YLSsk = YLSsk.tolist()
YLSsk = la.transpose(YLSsk)[0]

# Section 7: Calculate Prediction Differences
YLSpp.sort()
YLSsk.sort()
deltas = [ YLSpp[i] - YLSsk[i] for i in range(len(YLSpp)) ]
print( '\nPrediction Deltas:', deltas , '\n')

# Section 8: Plot Both Methods
plt.plot(XLS, YLSpp, XLS, YLSsk)
plt.xlabel('X Values')
plt.ylabel('Y Values')
plt.title('Pure Python Least Squares Line Fit')
plt.show()


'''
SymPy demonstration
http://docs.sympy.org/latest/tutorial/intro.html
'''
import sympy as sp
sp.init_printing(use_unicode=True)

print(sp.sqrt(3))
print(sp.sqrt(8))

x, y = sp.symbols('x y')
expr = x + 2*y + 1
print(expr)

print(sp.expand(x * expr))
print(sp.factor(sp.expand(x * expr)))

x, t, z, nu = sp.symbols('x t z nu')
print(sp.diff(sp.sin(x)*sp.exp(x), x))
print(sp.integrate(sp.exp(x)*sp.sin(x) + sp.exp(x)*sp.cos(x), x))

print(sp.integrate(sp.sin(x**2), (x, -sp.oo, sp.oo)))

print(sp.limit(sp.sin(x)/x, x, 0))

print(sp.solve(x**2 - 2, x))
y = sp.Function('y')
print(sp.solve(sp.Eq(y(t).diff(t, t) - y(t), sp.exp(t)), y(t)))

A = sp.Matrix([[1,2],[2,2]])
print(A.eigenvals())
print(sp.latex(A.eigenvals()))


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

from io_utilities import load_data
from visualizations import show_clusters_centroids

def distance(a,b):
    """
    Compute Euclidean Distance Between Two Points
    Input:
        a (list): an n-dimensional list or array
        b (list): an n-dimensional list or array
    Output:
        The Euclidean Distance between vectors a and b
    """
    return np.sqrt(np.sum((np.array(b)-np.array(a))**2))

def get_clusters(points,centroids):
    """
    Returns a list of clusters given all the points in the dataset and
    the current centroids.
    Input:
        points (list of lists): A list with every point in the dataset
        centroids (list of lists): A list with the current centroids
    Output:
        clusters (list of lists of lists): A List of Clusters. Each cluster
        is also a list of points in the cluster.
    """
    clusters = [[] for f in centroids]

    for i, point in enumerate(points):
        point_to_centroids = []
        for j, centroid in enumerate(centroids):
            point_to_centroids.append(distance(point,centroid))
        closest_idx = np.argmin(point_to_centroids)
        clusters[closest_idx].append(point)

    return clusters

def update_centroids(clusters):
    """
    Given a list of clusters (as prepared by get_clusters) get the new centroids
    Input:
        clusters (list of lists of lists): A List of Clusters. Each cluster
        is also a list of points in the cluster.
    Output:
        A (list of lists): The new centroids.
    """
    new_centroids = []

    for cluster in clusters:
        new_centroids.append(np.mean(cluster,axis = 0))
    return new_centroids

def k_means(points, k, iterations=10):
    """
    K Means Unsupervised ML Algorithm Implementation with Forgy Initialization
    Input:
        points (numpy array): a 2D Array with the data to cluster.
        k (int): The number of clusters to find
    """
    idx = np.random.randint(len(points),size=k)

    centroids = points[idx,:]
    clusters = get_clusters(points,centroids)

    for i in range(iterations):

        if i % 1 == 0:
            if i == 0:
                title = "Initialization"
            else:
                title = "Iteration {}".format(i+1)

            show_clusters_centroids(
                clusters,
                centroids,
                title,
            )

        clusters = get_clusters(points,centroids)
        centroids = update_centroids(clusters)

    return clusters,centroids

if __name__ == "__main__":
    data = load_data('./data/iris.data')
    k = 3

    X = np.array([f[:-1] for f in data])
    y = np.array([f[-1] for f in data])

    clusters,centroids = k_means(X,3)

    show_clusters_centroids(clusters,centroids,"Result", keep=True)
    plt.show()


#!/usr/bin/env python

import numpy as np
import iterate

class Grid:
    """
        Simple class to generate a computational grid and apply boundary conditions.
    """

    def __init__(self, nx=10, ny=10, xmin=0.0, xmax=1.0, ymin=0.0, ymax=1.0):

        self.xmin, self.xmax, self.ymin, self.ymax = xmin, xmax, ymin, ymax
        self.nx, self.ny = nx, ny

        self.dx = (xmax - xmin) / (nx - 1)

        self.dy = (ymax - ymin) / (ny - 1)

        self.u = np.zeros((ny, nx), dtype=np.double)

    def set_boundary_condtion(self, side = 'top', boundary_condition_function = lambda x,y: 0.0):

        xmin, ymin = self.xmin, self.ymin

        xmax, ymax = self.xmax, self.ymax

        x = np.linspace(xmin, xmax, self.nx)

        y = np.linspace(ymin, ymax, self.ny)

        if side == 'bottom':
            self.u[0 ,:] = boundary_condition_function(ymax,x)
        elif side == 'top':
            self.u[-1 ,:] = boundary_condition_function(ymin,x)
        elif side == 'left':
            self.u[:, 0] = boundary_condition_function(xmin,y)
        elif side == 'right':
            self.u[:, -1] = boundary_condition_function(xmax,y)

        self.initial_u = self.u.copy()

class LaplaceSolver(Grid):
    """
        Class that solves the Laplace equation in 2D
    """

    def iterate(self):
        """
            A Python (slow) implementation of a finite difference iteration
        """

        u = self.u

        ny, nx = u.shape

        dx2, dy2 = self.dx ** 2, self.dy ** 2

        err = 0.0

        for i in range(1, nx - 1):

            for j in range(1, ny - 1):

                tmp = u[i,j]

                u[i,j] = ((u[i-1, j] + u[i+1, j]) * dy2 +
                          (u[i, j-1] + u[i, j+1]) * dx2) / (dx2 + dy2) / 2

                diff = u[i,j] - tmp

                err += diff * diff

        return np.sqrt(err)

    def solve(self, max_iterations=10000, tolerance=1.0e-16, quiet=False):
        """
            Calls iterate() sequentially until the error is reduced below a tolerance.
        """

        for i in range(max_iterations):

            error = self.iterate()

            if error < tolerance:
                if not quiet:
                    print("Solution converged in " + str(i) + " iterations.")
                break

    def swig_solve(self, max_iterations=10000, tolerance=1.0e-16, quiet=False):
        """
            Calls iterate.iterate() sequentially until the error is reduced below a tolerance.
        """

        for i in range(max_iterations):

            error = iterate.iterate(self.u, self.dx, self.dy)

            if error < tolerance:
                if not quiet:
                    print("Solution converged in " + str(i) + " iterations.")
                return

        print("Solution FAILED to converged in " + str(max_iterations) + " iterations.")

        return

    def swig_solve_red_black(self, max_iterations=10000, tolerance=1.0e-16, quiet=False):
        """
            Calls iterate.iterate() sequentially until the error is reduced below a tolerance.
        """

        for i in range(max_iterations):

            error = iterate.iterate_red_black(self.u, self.dx, self.dy)

            if error < tolerance:
                if not quiet:
                    print("Solution converged in " + str(i) + " iterations.")
                return

        print("Solution FAILED to converged in " + str(max_iterations) + " iterations.")

        return

    def get_solution(self):
        return self.u

    def reset(self):
        self.u = self.initial_u.copy()
        return


from pandas import DataFrame, Series
import numpy

#################
# Syntax Reminder:
#
# The following code would create a two-column pandas DataFrame
# named df with columns labeled 'name' and 'age':
#
# people = ['Sarah', 'Mike', 'Chrisna']
# ages  =  [28, 32, 25]
# df = DataFrame({'name' : Series(people),
#                 'age'  : Series(ages)})

def create_dataframe():
    '''
    Create a pandas dataframe called 'olympic_medal_counts_df' containing
    the data from the table of 2014 Sochi winter olympics medal counts.

    The columns for this dataframe should be called
    'country_name', 'gold', 'silver', and 'bronze'.

    There is no need to  specify row indexes for this dataframe
    (in this case, the rows will automatically be assigned numbered indexes).

    You do not need to call the function in your code when running it in the
    browser - the grader will do that automatically when you submit or test it.
    '''

    countries = ['Russian Fed.', 'Norway', 'Canada', 'United States',
                 'Netherlands', 'Germany', 'Switzerland', 'Belarus',
                 'Austria', 'France', 'Poland', 'China', 'Korea',
                 'Sweden', 'Czech Republic', 'Slovenia', 'Japan',
                 'Finland', 'Great Britain', 'Ukraine', 'Slovakia',
                 'Italy', 'Latvia', 'Australia', 'Croatia', 'Kazakhstan']

    gold = [13, 11, 10, 9, 8, 8, 6, 5, 4, 4, 4, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
    silver = [11, 5, 10, 7, 7, 6, 3, 0, 8, 4, 1, 4, 3, 7, 4, 2, 4, 3, 1, 0, 0, 2, 2, 2, 1, 0]
    bronze = [9, 10, 5, 12, 9, 5, 2, 1, 5, 7, 1, 2, 2, 6, 2, 4, 3, 1, 2, 1, 0, 6, 2, 1, 0, 1]

    olympic_medal_counts_df = DataFrame({'country_name': Series(countries),
                                         'gold': Series(gold),
                                         'silver': Series(silver),
                                         'bronze': Series(bronze)})

    return olympic_medal_counts_df

def avg_medal_count():
    '''
    Compute the average number of bronze medals earned by countries who
    earned at least one gold medal.

    Save this to a variable named avg_bronze_at_least_one_gold. You do not
    need to call the function in your code when running it in the browser -
    the grader will do that automatically when you submit or test it.

    HINT-1:
    You can retrieve all of the values of a Pandas column from a
    data frame, "df", as follows:
    df['column_name']

    HINT-2:
    The numpy.mean function can accept as an argument a single
    Pandas column.

    For example, numpy.mean(df["col_name"]) would return the
    mean of the values located in "col_name" of a dataframe df.
    '''

    countries = ['Russian Fed.', 'Norway', 'Canada', 'United States',
                 'Netherlands', 'Germany', 'Switzerland', 'Belarus',
                 'Austria', 'France', 'Poland', 'China', 'Korea',
                 'Sweden', 'Czech Republic', 'Slovenia', 'Japan',
                 'Finland', 'Great Britain', 'Ukraine', 'Slovakia',
                 'Italy', 'Latvia', 'Australia', 'Croatia', 'Kazakhstan']

    gold = [13, 11, 10, 9, 8, 8, 6, 5, 4, 4, 4, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
    silver = [11, 5, 10, 7, 7, 6, 3, 0, 8, 4, 1, 4, 3, 7, 4, 2, 4, 3, 1, 0, 0, 2, 2, 2, 1, 0]
    bronze = [9, 10, 5, 12, 9, 5, 2, 1, 5, 7, 1, 2, 2, 6, 2, 4, 3, 1, 2, 1, 0, 6, 2, 1, 0, 1]

    olympic_medal_counts = {'country_name': Series(countries),
                            'gold': Series(gold),
                            'silver': Series(silver),
                            'bronze': Series(bronze)}
    df = DataFrame(olympic_medal_counts)

    avg_bronze_at_least_one_gold = numpy.mean(df[df['gold'] >= 1]['bronze'])

    return avg_bronze_at_least_one_gold

def avg_medal():
    '''
    Using the dataframe's apply method, create a new Series called
    avg_medal_count that indicates the average number of gold, silver,
    and bronze medals earned amongst countries who earned at
    least one medal of any kind at the 2014 Sochi olympics.  Note that
    the countries list already only includes countries that have earned
    at least one medal. No additional filtering is necessary.

    You do not need to call the function in your code when running it in the
    browser - the grader will do that automatically when you submit or test it.
    '''

    countries = ['Russian Fed.', 'Norway', 'Canada', 'United States',
                 'Netherlands', 'Germany', 'Switzerland', 'Belarus',
                 'Austria', 'France', 'Poland', 'China', 'Korea',
                 'Sweden', 'Czech Republic', 'Slovenia', 'Japan',
                 'Finland', 'Great Britain', 'Ukraine', 'Slovakia',
                 'Italy', 'Latvia', 'Australia', 'Croatia', 'Kazakhstan']

    gold = [13, 11, 10, 9, 8, 8, 6, 5, 4, 4, 4, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
    silver = [11, 5, 10, 7, 7, 6, 3, 0, 8, 4, 1, 4, 3, 7, 4, 2, 4, 3, 1, 0, 0, 2, 2, 2, 1, 0]
    bronze = [9, 10, 5, 12, 9, 5, 2, 1, 5, 7, 1, 2, 2, 6, 2, 4, 3, 1, 2, 1, 0, 6, 2, 1, 0, 1]

    olympic_medal_counts = {'country_name': countries,
                            'gold': Series(gold),
                            'silver': Series(silver),
                            'bronze': Series(bronze)}
    df = DataFrame(olympic_medal_counts)

    avgs = df[['gold', 'silver', 'bronze']].apply(numpy.mean)

    return avgs

print avg_medal()

def numpy_dot():
    '''
    Imagine a point system in which each country is awarded 4 points for each
    gold medal,  2 points for each silver medal, and one point for each
    bronze medal.

    Using the numpy.dot function, create a new dataframe called
    'olympic_points_df' that includes:
        a) a column called 'country_name' with the country name
        b) a column called 'points' with the total number of points the country
           earned at the Sochi olympics.

    You do not need to call the function in your code when running it in the
    browser - the grader will do that automatically when you submit or test it.
    '''

    countries = ['Russian Fed.', 'Norway', 'Canada', 'United States',
                 'Netherlands', 'Germany', 'Switzerland', 'Belarus',
                 'Austria', 'France', 'Poland', 'China', 'Korea',
                 'Sweden', 'Czech Republic', 'Slovenia', 'Japan',
                 'Finland', 'Great Britain', 'Ukraine', 'Slovakia',
                 'Italy', 'Latvia', 'Australia', 'Croatia', 'Kazakhstan']

    gold = [13, 11, 10, 9, 8, 8, 6, 5, 4, 4, 4, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
    silver = [11, 5, 10, 7, 7, 6, 3, 0, 8, 4, 1, 4, 3, 7, 4, 2, 4, 3, 1, 0, 0, 2, 2, 2, 1, 0]
    bronze = [9, 10, 5, 12, 9, 5, 2, 1, 5, 7, 1, 2, 2, 6, 2, 4, 3, 1, 2, 1, 0, 6, 2, 1, 0, 1]

    points = numpy.dot(gold, 4) + numpy.dot(silver, 2) + numpy.dot(bronze, 1)

    counts = {'country_name' : countries,
              'points' : Series(points)}

    df = DataFrame(counts)

    # another alternative
    # olympic_medal_counts = {'country_name': countries,
    #                         'gold': Series(gold),
    #                         'silver': Series(silver),
    #                         'bronze': Series(bronze)}
    # df = DataFrame(olympic_medal_counts)
    # medal_counts = olympic_medal_counts[['gold', 'silver', 'bronze']]
    # points = numpy.dot(medal_counts, [4, 2, 1])

    # alternative way with pandas only
    # df['points'] = df[['gold','silver','bronze']].dot([4, 2, 1])
    # olympic_points_df = df[['country_name','points']]

    return df

print numpy_dot()


##this will demonstrate boolean masks to access ada in numpy arrays

#counting rainydays exp.
import numpy as np
import pandas as pd

#pandas will read in our data
rainfall = pd.read_csv('data/Seattle2014.csv')['PRCP'].values
inches = rainfall / 254.0 #1/10mm -> inches
print(inches.shape) #(365,)

import matplotlib
matplotlib.use('TKagg')

import matplotlib.pyplot as plt
import seaborn; seaborn.set() #set plot style

plt.hist(inches, 40)
plt.show()

##digging into the data
#Comparison Operators as ufuncs

x = np.array([1,2,3,4,5])
print(x < 3) #less than, also have <, <=, !=, ==, ect

print((2*x) == (x ** 2))

rng = np.random.RandomState(0)
x = rng.randint(10, size=(3,4))
print(x)
print(x<6)

#working with boolean arrays
print("x:\n", x)
#count # of true entries
print(np.count_nonzero(x < 6)) # how many values less that 6
print(np.sum(x < 6)) # same value another way but you can specify axis
print(np.sum(x < 6, axis=1))
print(np.any(x > 8)) # any values greater than 8?
print(np.all(x >2)) # are all values greater than 2?
#also works along axis
print(np.any(x<4, axis = 1))

##Bolean operators
# &, | , ^, ~
print(np.sum((inches > 0.5) & (inches < 1)))
# & equive to np.bitwise_and, ect

print("Number of days without rain:     ", np.sum(inches == 0))
print("number of days with rain:        ", np.sum(inches != 0))
print("Days with more than 0.5 inches:  ", np.sum(inches > 0.5))
print("Rainy days with < 0.2 inches:    ", np.sum((inches > 0) & (inches < 0.2)))

#Boolean arrays as masks
print("x:\n", x)

print(x[x<5]) #masking operation

#construct a mask of rainy days
rainy = (inches > 0)

#construct a mask of all summer days ( 6/21 is the 172nd day)
days = np.arange(365)
summer = (days > 172) & (days < 262)

print("The median precip on rainy days in 2014 (inches):  ",
      np.median(inches[rainy]))
print("Median precip on summer days in 2014 (inches):     ",
      np.median(inches[summer]))
print("Maximum precip on summer days in 2014 (inches):    ",
      np.max(inches[summer]))
print("Median precip on non-summer rainy days(inches):    ",
      np.median(inches[rainy & ~summer]))


import numpy as np

def lin_reg_vec(theta,x,y):
    """
    Function to calculate cost and gradient for given arguments for linear regression model
    """
    val = np.dot(theta,x) - y
    grad = np.transpose(np.dot(x,np.transpose(val)))
    cost = np.sum(val**2)/2
    return cost,grad

def grad_check(theta,x,y):
    eps = pow(10,-4)
    grad = np.zeros(theta.shape)
    numf = theta.shape[0] # number of features
    for i in range(numf):
        evec = np.zeros(theta.shape)
        evec[i] = 1
        cplus,_ = lin_reg_vec(theta + eps*evec,x,y)
        csub,_ = lin_reg_vec(theta - eps*evec,x,y)
        grad[i] = (cplus - csub)/(2*eps)
    return grad

data = np.loadtxt("housing.data")  #specify path to data file
data = np.insert(data,0,1,axis=1)
np.random.shuffle(data)

train_data = data[:400,:-1]
train_labels = data[:400,-1]

test_data = data[400:,:-1]
test_labels = data[400:,-1]

train_data = np.transpose(train_data)
test_data = np.transpose(test_data)

theta = np.random.rand(train_data.shape[0])

_,grad_lin_reg = lin_reg_vec(theta,train_data,train_labels)
grad_ch = grad_check(theta,train_data,train_labels)

diff = np.abs(grad_lin_reg - grad_ch)
print (diff)

assert all(np.less(diff,1e-4)) # the difference between the two gradients computed is very less


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

# The SIR model differential equations.
def SIR(y, t, N, beta, gamma):

    S, I, R = y
    dSdt = -beta * S * I / N
    dIdt = beta * S * I / N - gamma * I
    dRdt = gamma * I

    return dSdt, dIdt, dRdt

def entrypoint(current_state, model_parameters):

    args = {
        'y0': (current_state['population'] - current_state['current_infected'] - 0, # S
               current_state['current_infected'],                                   # I
               current_state['recovered']                                                                   # R
              ),
        't': np.linspace(0, model_parameters['days_from_t0'], model_parameters['days_from_t0']+1),
        'args': (current_state['population'],                            # N
                 model_parameters['R0'] / model_parameters['sick_days'], # beta
                 1. / model_parameters['sick_days'],                     # gamma
                )
    }

    result = odeint(SIR, **args)

    result = pd.DataFrame(result, columns=['S', 'I', 'R'])

    result['days'] = args['t']
    result['I2'] = result['I'] * model_parameters['i2_percentage'] / 100 # severe cases
    result['I3'] = result['I'] * model_parameters['i3_percentage'] / 100 # critical cases
    result['I1'] = result['I'] - result['I2'] - result['I3']             # mild cases

    return result


import tclab  # pip install tclab
import numpy as np
import time
import matplotlib.pyplot as plt
from scipy.integrate import odeint

# define energy balance model
def heat(x,t,Q1,Q2):
    # Parameters
    Ta = 23 + 273.15   # K
    U = 10.0           # W/m^2-K
    m = 4.0/1000.0     # kg
    Cp = 0.5 * 1000.0  # J/kg-K
    A = 10.0 / 100.0**2 # Area in m^2
    As = 2.0 / 100.0**2 # Area in m^2
    alpha1 = 0.0100     # W / % heater 1
    alpha2 = 0.0075     # W / % heater 2
    eps = 0.9          # Emissivity
    sigma = 5.67e-8    # Stefan-Boltzman

    # Temperature States
    T1 = x[0]
    T2 = x[1]

    # Heat Transfer Exchange Between 1 and 2
    conv12 = U*As*(T2-T1)
    rad12  = eps*sigma*As * (T2**4 - T1**4)

    # Nonlinear Energy Balances
    dT1dt = (1.0/(m*Cp))*(U*A*(Ta-T1) \
            + eps * sigma * A * (Ta**4 - T1**4) \
            + conv12 + rad12 \
            + alpha1*Q1)
    dT2dt = (1.0/(m*Cp))*(U*A*(Ta-T2) \
            + eps * sigma * A * (Ta**4 - T2**4) \
            - conv12 - rad12 \
            + alpha2*Q2)

    return [dT1dt,dT2dt]

# save txt file
def save_txt(t,u1,u2,y1,y2,sp1,sp2):
    data = np.vstack((t,u1,u2,y1,y2,sp1,sp2))  # vertical stack
    data = data.T                 # transpose data
    top = 'Time (sec), Heater 1 (%), Heater 2 (%), ' \
        + 'Temperature 1 (degC), Temperature 2 (degC), ' \
        + 'Set Point 1 (degC), Set Point 2 (degC)'
    np.savetxt('data.txt',data,delimiter=',',header=top,comments='')

# Connect to Arduino
a = tclab.TCLab()

# Turn LED on
print('LED On')
a.LED(100)

# Run time in minutes
run_time = 10.0

# Number of cycles
loops = int(60.0*run_time)
tm = np.zeros(loops)

# Temperature (K)
Tsp1 = np.ones(loops) * 23.0 # set point (degC)
T1 = np.ones(loops) * a.T1 # measured T (degC)

Tsp2 = np.ones(loops) * 23.0 # set point (degC)
T2 = np.ones(loops) * a.T2 # measured T (degC)

# Predictions
Tp1 = np.ones(loops) * a.T1
Tp2 = np.ones(loops) * a.T2
error_eb = np.zeros(loops)

# impulse tests (0 - 100%)
Q1 = np.ones(loops) * 0.0
Q1[10:] = 100.0 # step up after 10 sec
Q2 = np.ones(loops) * 0.0
Q2[300:] = 100.0 # step up after 5 min (300 sec)

print('Running Main Loop. Ctrl-C to end.')
print('  Time   Q1     Q2    T1     T2')
print('{:6.1f} {:6.2f} {:6.2f} {:6.2f} {:6.2f}'.format(tm[0], \
                                                       Q1[0], \
                                                       Q2[0], \
                                                       T1[0], \
                                                       T2[0]))

# Create plot
plt.figure(figsize=(10,7))
plt.ion()
plt.show()

# Main Loop
start_time = time.time()
prev_time = start_time
try:
    for i in range(1,loops):
        # Sleep time
        sleep_max = 1.0
        sleep = sleep_max - (time.time() - prev_time)
        if sleep>=0.01:
            time.sleep(sleep-0.01)
        else:
            time.sleep(0.01)

        # Record time and change in time
        t = time.time()
        dt = t - prev_time
        prev_time = t
        tm[i] = t - start_time

        # Read temperatures in Kelvin
        T1[i] = a.T1
        T2[i] = a.T2

        # Simulate one time step with Energy Balance
        Tinit = [Tp1[i-1]+273.15,Tp2[i-1]+273.15]
        Tnext = odeint(heat,Tinit, \
                       [0,dt],args=(Q1[i-1],Q2[i-1]))
        Tp1[i] = Tnext[1,0]-273.15
        Tp2[i] = Tnext[1,1]-273.15
        error_eb[i] = error_eb[i-1] \
                      + (abs(Tp1[i]-T1[i]) \
                      +  abs(Tp2[i]-T2[i]))*dt

        # Write output (0-100)
        a.Q1(Q1[i])
        a.Q2(Q2[i])

        # Print line of data
        print('{:6.1f} {:6.2f} {:6.2f} {:6.2f} {:6.2f}'.format(tm[i], \
                                                               Q1[i], \
                                                               Q2[i], \
                                                               T1[i], \
                                                               T2[i]))

        # Plot
        plt.clf()
        ax=plt.subplot(3,1,1)
        ax.grid()
        plt.plot(tm[0:i],T1[0:i],'ro',label=r'$T_1$ measured')
        plt.plot(tm[0:i],Tp1[0:i],'k-',label=r'$T_1$ energy balance')
        plt.plot(tm[0:i],T2[0:i],'bx',label=r'$T_2$ measured')
        plt.plot(tm[0:i],Tp2[0:i],'k--',label=r'$T_2$ energy balance')
        plt.ylabel('Temperature (degC)')
        plt.legend(loc=2)
        ax=plt.subplot(3,1,2)
        ax.grid()
        plt.plot(tm[0:i],error_eb[0:i],'k-',label='Energy Balance Error')
        plt.ylabel('Cumulative Error')
        plt.legend(loc='best')
        ax=plt.subplot(3,1,3)
        ax.grid()
        plt.plot(tm[0:i],Q1[0:i],'r-',label=r'$Q_1$')
        plt.plot(tm[0:i],Q2[0:i],'b:',label=r'$Q_2$')
        plt.ylabel('Heaters')
        plt.xlabel('Time (sec)')
        plt.legend(loc='best')
        plt.draw()
        plt.pause(0.05)

    # Turn off heaters
    a.Q1(0)
    a.Q2(0)
    # Save text file and plot at end
    save_txt(tm[0:i],Q1[0:i],Q2[0:i],T1[0:i],T2[0:i],Tsp1[0:i],Tsp2[0:i])
    # Save figure
    plt.savefig('test_Models.png')

# Allow user to end loop with Ctrl-C
except KeyboardInterrupt:
    # Disconnect from Arduino
    a.Q1(0)
    a.Q2(0)
    print('Shutting down')
    a.close()
    save_txt(tm[0:i],Q1[0:i],Q2[0:i],T1[0:i],T2[0:i],Tsp1[0:i],Tsp2[0:i])
    plt.savefig('test_Models.png')

# Make sure serial connection still closes when there's an error
except:
    # Disconnect from Arduino
    a.Q1(0)
    a.Q2(0)
    print('Error: Shutting down')
    a.close()
    save_txt(tm[0:i],Q1[0:i],Q2[0:i],T1[0:i],T2[0:i],Tsp1[0:i],Tsp2[0:i])
    plt.savefig('test_Models.png')
    raise


import numpy as np

def reg_window(N):
    return np.ones(N)

def hanning_window(N):
    nn = [i for i in range(N)]
    return 0.5 * (1 - np.cos(np.multiply(nn, 2 * np.pi) / (N - 1)))

def hamming_window(N):
    nn = [i for i in range(N)]
    return 0.54 - 0.46 * np.cos(np.multiply(nn, 2 * np.pi) / (N - 1))


# coding:utf-8
import numpy as np
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

'''
author: heucoder
email: 812860165@qq.com
date: 2019.6.13
'''

def pca(data, n_dim):
    '''

    pca is O(D^3)
    :param data: (n_samples, n_features(D))
    :param n_dim: target dimensions
    :return: (n_samples, n_dim)
    '''
    data = data - np.mean(data, axis = 0, keepdims = True)

    cov = np.dot(data.T, data)

    eig_values, eig_vector = np.linalg.eig(cov)
    # print(eig_values)
    indexs_ = np.argsort(-eig_values)[:n_dim]
    picked_eig_values = eig_values[indexs_]
    picked_eig_vector = eig_vector[:, indexs_]
    data_ndim = np.dot(data, picked_eig_vector)
    return data_ndim

# data 降维的矩阵(n_samples, n_features)
# n_dim 目标维度
# fit n_features >> n_samples, reduce cal
def highdim_pca(data, n_dim):
    '''

    when n_features(D) >> n_samples(N), highdim_pca is O(N^3)

    :param data: (n_samples, n_features)
    :param n_dim: target dimensions
    :return: (n_samples, n_dim)
    '''
    N = data.shape[0]
    data = data - np.mean(data, axis = 0, keepdims = True)

    Ncov = np.dot(data, data.T)

    Neig_values, Neig_vector = np.linalg.eig(Ncov)
    indexs_ = np.argsort(-Neig_values)[:n_dim]
    Npicked_eig_values = Neig_values[indexs_]
    # print(Npicked_eig_values)
    Npicked_eig_vector = Neig_vector[:, indexs_]
    # print(Npicked_eig_vector.shape)

    picked_eig_vector = np.dot(data.T, Npicked_eig_vector)
    picked_eig_vector = picked_eig_vector/(N*Npicked_eig_values.reshape(-1, n_dim))**0.5
    # print(picked_eig_vector.shape)

    data_ndim = np.dot(data, picked_eig_vector)
    return data_ndim

if __name__ == "__main__":
    data = load_iris()
    X = data.data
    Y = data.target
    data_2d1 = pca(X, 2)
    plt.figure(figsize=(8,4))
    plt.subplot(121)
    plt.title("my_PCA")
    plt.scatter(data_2d1[:, 0], data_2d1[:, 1], c = Y)

    sklearn_pca = PCA(n_components=2)
    data_2d2 = sklearn_pca.fit_transform(X)
    plt.subplot(122)
    plt.title("sklearn_PCA")
    plt.scatter(data_2d2[:, 0], data_2d2[:, 1], c = Y)
    plt.show()


from typing import Tuple

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
from scipy.special import factorial

def likelihood(runs: int, hits: int, p: float) -> float:
    """Calculate the likelihood.

    Args:
      runs: Number of sample runs.
      hits: Number of positive sample runs.
      p: Model parameter.

    Returns:
      The likelihood.
    """
    return (
        factorial(runs)
        / (factorial(hits) * factorial(runs - hits))
        * p ** hits
        * (1 - p) ** (runs - hits)
    )

def calcaulte_marginal_likelihood(
    runs: int, hits: int, prior: np.ndarray, p: np.ndarray
) -> np.ndarray:
    """Calculate the marginal likelihood.

    Args:
      runs: Number of sample runs.
      hits: Number of positive sample runs.
      prior: Probability density function of prior.
      p: Model parameter.

    Returns:
      The marginal likelihood.
    """
    integration_size = 1 / len(prior)
    integral = 0
    for (p_value, prior_value) in zip(p, prior):
        integral += likelihood(runs, hits, p_value) * prior_value
    return integral * integration_size

def calculate_posterior(
    runs: int, hits: int, prior: np.ndarray, p: np.ndarray
) -> np.ndarray:
    """Calculate the posterior.

    Args:
      runs: Number of sample runs.
      hits: Number of positive sample runs.
      prior: Probability density function of prior.
      p: Model parameter.

    Returns:
      The posterior probability density function.
    """
    posterior = np.zeros(len(prior))
    marginal_likelihood = calcaulte_marginal_likelihood(runs, hits, prior, p)
    for i, (p_value, prior_value) in enumerate(zip(p, prior)):
        posterior[i] = (
            likelihood(runs, hits, p_value) * prior_value / marginal_likelihood
        )
    return posterior

def calculate_confidence_interval(
    posterior: np.ndarray, confidence_level: float
) -> Tuple[float, float, int, int]:
    """Calculate confidence interval.

    Args:
      confidence_level: Confidence level as a decimal number.
      posterior: Probability density function of posterior.

    Returns:
      Confidence interval.
    """
    low = 1
    idx_low = 0
    high = 0
    idx_high = 0
    integral = 0
    integration_size = 1 / len(posterior)
    for idx in np.argsort(posterior)[::-1]:
        integral += posterior[idx] * integration_size
        if low > idx * integration_size:
            low = idx * integration_size
            idx_low = idx
        if high < idx * integration_size:
            high = idx * integration_size
            idx_high = idx
        if integral > confidence_level:
            break
    return low, high, idx_low, idx_high

plt.rc("font", size=12)
plt.rc("axes", titlesize=12)
plt.rc("axes", labelsize=12)
plt.rc("xtick", labelsize=12)
plt.rc("ytick", labelsize=12)
plt.rc("legend", fontsize=12)
plt.rc("figure", titlesize=12)

sampled_data = np.array(
    [
        1,
        1,
        1,
        0,
        0,
        1,
        0,
        0,
        1,
        0,
        1,
        1,
        1,
        0,
        1,
        0,
        1,
        1,
        1,
        0,
        1,
        1,
        1,
        0,
        1,
        1,
        0,
        0,
        0,
        1,
        0,
        0,
        1,
        0,
        0,
    ]
)
prior = np.zeros(1000) + 1
p = np.linspace(0, 1, 1000)
posterior = calculate_posterior(len(sampled_data), np.sum(sampled_data), prior, p)
conf_low, conf_high, conf_idx1, conf_idx2 = calculate_confidence_interval(
    posterior, 0.95
)

fig, ax = plt.subplots(1, figsize=(6, 4))
ax.plot(p, posterior, color="blue", linewidth=3)
ax.fill_between(
    p[conf_idx1:conf_idx2], posterior[conf_idx1:conf_idx2], alpha=0.3, color="green"
)
ax.set_ylim(0, 5)
ax.set_xlim(0, 1)
ax.set_xlabel("p")
ax.set_ylabel("Probability Density")
plt.tight_layout()
plt.grid()
plt.savefig("bayesian_mysterious_stranger.svg")

fig, ax = plt.subplots(1, figsize=(6, 4))
fig.set_tight_layout(True)
ax.set_ylim(0, 5)
ax.set_xlim(0, 1)
ax.set_xlabel("p")
ax.set_ylabel("Probability Density")
plt.grid()

# Make first frame
sampled_data = np.array(
    [
        1,
        1,
        1,
        0,
        0,
        1,
        0,
        0,
        1,
        0,
        1,
        1,
        1,
        0,
        1,
        0,
        1,
        1,
        1,
        0,
        1,
        1,
        1,
        0,
        1,
        1,
        0,
        0,
        0,
        1,
        0,
        0,
        1,
        0,
        0,
    ]
)
p = np.linspace(0, 1, 1000)
prior = np.linspace(0, 1, 1000)
posterior = calculate_posterior(
    len(sampled_data[:1]), np.sum(sampled_data[:1]), prior, p
)
conf_low, conf_high, conf_idx1, conf_idx2 = calculate_confidence_interval(
    posterior, 0.95
)
(probability_density_function,) = ax.plot(p, posterior, color="blue", linewidth=3)
ax.fill_between(
    p[conf_idx1:conf_idx2], posterior[conf_idx1:conf_idx2], alpha=0.3, color="green"
)

def update(i: int) -> None:
    """Update animiate.

    Args:
      i: frame number.
    """
    p = np.linspace(0, 1, 1000)
    prior = np.zeros(1000) + 1
    posterior = calculate_posterior(
        len(sampled_data[:i]), np.sum(sampled_data[:i]), prior, p
    )
    conf_low, conf_high, conf_idx1, conf_idx2 = calculate_confidence_interval(
        posterior, 0.95
    )
    probability_density_function.set_ydata(posterior)
    ax.collections.clear()
    ax.fill_between(
        p[conf_idx1:conf_idx2], posterior[conf_idx1:conf_idx2], alpha=0.3, color="green"
    )
    ax.set_title(
        f"Runs: {len(sampled_data[:i])}; Mysterious Strangers: {np.sum(sampled_data[:i])}"
    )

# Make animation
anim = FuncAnimation(
    fig, update, frames=np.arange(1, len(sampled_data) + 1), interval=300
)
anim.save("ms_bayesian.gif", dpi=400)


#Problema 2

#Primero que nada, se importan los modulos a utilizar

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mp
import math as m
from astropy import constants as astro   #modulo con las constantes que se usan en la funcion de Planck

#Funcion de Planck: B = ((2*h*v**3)/c**2)/(np.exp((h*v)/(k_B*T) - 1)

#c: Rapidez de la luz en el vacio
c = astro.c.value
#h: Constante de Planck
h = astro.h.value
#v: Frecuencia
#k_B: Constante de Boltzmann
k_B = astro.k_B.value
#T: Temperatura

#Parte 1

#Se debe graficar los datos que se encuentran en el archivo dado, cuidando las unidades de medida
#Para esto se lee el archivo y se transforma en un array con numpy.loadtxt('nombre del archivo')

archivo = np.loadtxt('firas_monopole_spec_v1.txt')

#Del archivo, se tiene que cada columna significa:

#Columna 1: Frecuencia [cm^-1]
#Columna 2: Espectro del monopolo medido por el instrumento FIRAS del satelite COBE [MJy/sr], donde 1 [MJy] = 10^−20 [Wm^−2Hz^−1]
#Columna 3: Espectro residual del monopolo [kJy/sr]
#Columna 4: Incertidumbre del espectro medido [kJy/sr]
#Columna 5: Modelo del espectro de la galaxia en los polos galacticos [kJy/sr]

#De estas columnas nos interesan las columnas 1 (frecuencia), 2 (espectro medido) y 4 (incertidumbre/error) para la
#realizacion del grafico.

frecuencia = archivo[:,0]*100*c          #primera columna, es de tipo np.ndarray
espectro = archivo[:,1]                              #segunda columna
error = archivo[:,3]/1000                            #cuarta columna

#Se tiene que el espectro medido esta en [MJy/sr] y el error en [kJy/sr], por lo que se divide el error por mil. La frecuencia
#esta en [cm^-1], por lo que se multiplica por 100 y por c para quedar en [1/s = Hz]

#Como el error es muy pequeño por la precision del FIRAS, se multiplica por 400 (factor sugerido como ejemplo en el enunciado)

error_400 = 400*error

#Grafico:
"""
plt.plot(frecuencia, espectro)
plt.title('Espectro de monopolo medido por FIRAS')
plt.xlabel('Frecuencia [Hz]')
plt.ylabel('Espectro del monopolo [MJy/sr]')
plt.errorbar(frecuencia, espectro, xerr=0, yerr=error_400)

plt.savefig('espectro_monopolo_1.png')
plt.show()
"""

#Parte 2

#P = (2*h/c)*((k_B*T/h)**4)*integral_{0}^{infinito}(x^3/(exp(x)-1))dx = K(T)*I

integral_analitica = (np.pi**4)/15

#Para resolver la integral, se utiliza el cambio de variable sugerido y = arctan(x) y asi poder cambiar lo limites de integracion

#y = arctan(x); x = tan(y)
#dy = (1/(1+x^2))dx; dx = (1+x^2)dy

#x = 0 entonces y = arctan(0) = 0
#x = infinito entonces lim_{x = infinito}arctan(x) = pi/2

#P = K(T)*integral_{0}^{pi/2}(tan^3(y))(1+tan^2(y))/(exp(tan(y)) - 1)dy = K(T)*integral_{0}^{pi/2}f(y)dy

#Se tiene que f(y) tiene numerador y denominador infinitos en y = 0 y solo denominador infinito en en y = pi/2, por lo que
#se usara el metodo del valor medio en ambos extremos. Se resolvera con el metodo de Simpson 1/3 para lo demas

#Definimos la función dentro de la integral

def f_integral(x):
    out = (np.tan(x)**3 + np.tan(x)**5)/(np.exp(np.tan(x)) - 1)
    return out

#Para calcular con dicho metodo, se debe de escoger un h tal que la integral se acerque a su valor analitico, por lo que
#se refinarra el valor de la integral al ir aumentando al doble el numero de terminos que se dividira (b - a) = (pi/2 - 0) = pi/2

#Sabemos que la integral tiene un valor analitico igual a (np.pi**4)/15, por lo que se generara un particion cada vez
# la mitad de tamaño que la anterior hasta estar cerca de ese valor. Se debe definir entonces una tolerancia

x_0 = 0
x_n = np.pi/2

def valor_integral(a,b, N=10 , toler=1e-7):
    integral = 0
    while np.abs(integral - integral_analitica) > toler:
        integral = 0
        h = (b-a)/N
        for i in range(N+1):
            x = a + h*i    #valor a evaluar la funcion f_integral
            if x == a:
                integral += h*f_integral(x + h/2)   #valor medio en el inicio
            elif x == b:
                integral += h*f_integral(x - h/2)   #valor medio en el final
            elif i % 2 == 0:   #si el numero de termino es par
                integral += 2*f_integral(x)
            else: #si el numero de termino es impar y no se esta evaluando en los extremos
                integral += 4*f_integral(x)
        integral += f_integral(a+h) + f_integral(b-h)  #se le suman los terminos inicial y final del metodo de Simpson 1/3
        integral *= h/3
        N *= 2  #se duplica el numero de divisiones, disminuye a la mitad h
    return integral

integral_2 = valor_integral(x_0,x_n)

#Parte 3

#De la parte 2: P = (2*h/c**2)*((k_B*T/h)**4)*valor_integral, donde integral ya fue calculado

#Se quiere calcular T, para lo cual se calcula la integral de lo graficado en la parte 1

#Para calcular la integral del espectro (del archivo) respecto a la frecuencia se usara el metodo del trapecio. Hay que
#tener en cuenta que para poder igualarlo a P, se utilizaran dimensiones del sistema internacional, asi que hay que
#hacer las conversiones pertinentes. Ya se habia convertido la frecuencia a Hz, pero falta pasar el espectro de MJy/sr a Wm^−2Hz^−1/sr

#Recordar: [MJy] = 10^−20 [Wm^−2Hz^−1] , donde el espectro es la distribucion de energia por unidad de frecuencia

espectro_2 = espectro*10**(-20)

#Teniendo ya bien las dimensiones, se integra con el metodo del trapecio. Para usarlo no es necesario tener un h constante,
#la distancia entre cada termino a evaluar puede ser variable y adaptarse a los puntos si es que ya se dispone de ellos.
#h vendra dado por la distancia entre 2 frecuenciaa consecutivas, siendo el largo de las bases de los trapecios el valor del
#espectro en dichas frecuencias

integral_espectro = 0

for i in range(len(frecuencia)-1):
    integral_espectro += (espectro_2[i]+espectro_2[i+1])*(frecuencia[i+1]-frecuencia[i])/2

#Igualando este resultado con P: integral_espectro = (2*h/c**2)*((k_B*T/h)**4)*valor_integral

#Despejando T: T = (h/k_B)*((integral_espectro*c**2)/(valor_integral*2*h))**(1/4)

T = (h/k_B)*((integral_espectro*c**2)/(valor_integral(x_0,x_n)*2*h))**(1/4)

print('T =', T, '[K]')

#Da T = 2.684 K, siendo que el valor real es 2.725 K, esto ocurre por arrastre de errores de aproximacion

#Parte 4

#Para graficar lo echo en la parte 1 junto a los resultados obtenidos, es conveniente definir la funcion de Planck dependiente
#de la frecuencia y de la temperatura, escrita al principio.

def planck(v,T):
    output = ((2*h*v**(3))/c**(2))/(np.exp((h*v)/(k_B*T)) - 1)
    return output

#Grafico:
"""
plt.plot(frecuencia, espectro_2, label='Medicion FIRAS (S.I.)')              #grafico parte 1 pero en S.I.
plt.plot(frecuencia, planck(frecuencia,T), label='T calculada')       #T es la temperatura calculada en la parte 3
plt.plot(frecuencia, planck(frecuencia,2.725), label='2.725 K')       #2.725 K es la temperatura a la que se queria llegar con T

plt.title('Espectro segun FIRAS y segun Planck')
plt.xlabel('Frecuencia [Hz]')
plt.ylabel('Espectro del monopolo [$W m^{−2} Hz^{−1}/sr$]')

plt.legend()
plt.savefig('espectro_monopolo_2.png')
plt.show()
"""

#Como de grafico de las mediciones de FIRAS son muy cercanos a los de la funcion de Planck en T=2.725 K, es conveniente realizar
#un grafico de la diferencia de estos.

diferencia_espectro = [m.fabs(espectro_2[i] - planck(frecuencia[i],2.725)) for i in range(len(frecuencia))]

"""
plt.title("Diferencia del espectro de FIRAS y el de Planck")

plt.plot(frecuencia, diferencia_espectro)

plt.yscale('log')

plt.xlabel('Frecuencia [Hz]', fontsize=15)
plt.ylabel('Medición FIRAS - Planck en T = 2.725 [K]', fontsize=10)

plt.savefig('diferencia_espectro.png')
plt.show()
"""

#Parte 5

import scipy.integrate as sc

#sc.trapz(y,x) realiza el algortimo del metodo del trapecio sobre la funcion y en los puntos dados por x,
#por lo que se usara en la integral de la parte 3, es decir, en los datos del archivo de FIRAS.

integral_parte3 = sc.trapz(espectro_2,frecuencia)
print('integral_parte3 =',integral_parte3)

#sc.quad(func,a,b) calcula la integral de la funcion func desde a hasta b. Este se usara para la integral de P (parte 2).
#Para esto, se define la funcion de la integral sin el cambio de variable.

def func(x):
    y = (x**3)/(np.exp(x) - 1)
    return y

integral_parte2 = sc.quad(func, x_0, np.inf)
print('integral_parte2 =', integral_parte2)


import superimport

import numpy as np
from scipy.spatial.distance import cdist
import math
import matplotlib.pyplot as plt
from cycler import cycler
import pyprobml_utils as pml

CB_color = ['#377eb8', '#ff7f00', '#4daf4a']

cb_cycler = (cycler(linestyle=['-', '--', '-.']) * cycler(color=CB_color))
plt.rc('axes', prop_cycle=cb_cycler)

np.random.seed(0)
N = 100
x = 10 * (np.linspace(-1, 1, 100).reshape(-1, 1))
ytrue = np.array([math.sin(abs(el)) / (abs(el)) for el in x]).reshape(-1, 1)
noise = 0.1
y = ytrue + noise * np.random.randn(N, 1)
x = (x - x.mean()) / x.std()  # normalizing.

plt.plot(x, ytrue)
plt.plot(x, y, 'kx')

def rbf_features(X, centers, sigma):
    dist_mat = cdist(X, centers, 'minkowski', p=2.)
    return np.exp((-0.5 / (sigma ** 2)) * (dist_mat ** 2))

# Nadaraya-Watson Kernel Regressor
# using rbf kernel with autosSelected bandwidth given a range.
class NdwkernelReg:

    def __init__(self, gammas=None):
        self.gammas = gammas
        self.gamma = None

    def fit(self, X, y):
        self.X = X
        self.y = y

        self.gamma = self.select_gamma(self.gammas)

    def predict(self, X):
        K = rbf_features(self.X, X, self.gamma)
        return (K * self.y).sum(axis=0) / K.sum(axis=0)

    # leave-one-out crossValidation
    def select_gamma(self, gammas):
        mse = []

        for gamma in gammas:
            K = rbf_features(self.X, self.X, gamma)
            K = K - np.diag(np.diag(K))  # vanishing the diagonal elements
            y_pred = (K * self.y).sum(axis=0) / K.sum(axis=0)
            mse.append(((y_pred[:, np.newaxis] - self.y) ** 2).mean())

        return gammas[np.argmin(mse)]

nws = NdwkernelReg(gammas=np.linspace(0.1, 1, 10))
nws.fit(x, y)
y_estimate = nws.predict(x)
plt.plot(x, y_estimate)
plt.legend(['true', 'data', 'estimate'], fontsize=12)
pml.savefig("kernelRegressionDemo.pdf")
plt.show()


# Following is the given numpy array return array of odd rows and even columns

# My Solution
import numpy as np

sampleArray = np.array([[3, 6, 9, 12], [15, 18, 21, 24],
                        [27, 30, 33, 36], [39, 42, 45, 48], [51, 54, 57, 60]])

print("Printing Input Array")
print(sampleArray)

rowindex = list(range(0, sampleArray.shape[0], 2))
colindex = list(range(1, sampleArray.shape[1], 2))

print("\nPrinting array of odd rows and even columns")
print(sampleArray[rowindex][:, colindex])

# Given Solution
import numpy

sampleArray = numpy.array([[3, 6, 9, 12], [15, 18, 21, 24],
                           [27, 30, 33, 36], [39, 42, 45, 48], [51, 54, 57, 60]])
print("Printing Input Array")
print(sampleArray)

print("\n Printing array of odd rows and even columns")
newArray = sampleArray[::2, 1::2]
print(newArray)


import numpy as np
import pandas as pd
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import ui

print("构建一个逻辑回归模型来预测，某个学生是否被大学录取。设想你是大学相关部分")
print("的管理者，想通过申请学生两次测试的评分，来决定他们是否被录取。现在你拥有")
print("之前申请学生的可以用于训练逻辑回归的训练样本集。对于每一个训练样本，你有")
print("他们两次测试的评分和最后是被录取的结果。为了完成这个预测任务，我们准备构")
print("建一个可以基于两次测试评分来评估录取可能性的分类模型。")
ui.split_line1()
print("载入数据并查看分布")
path = 'data/ex2data1.txt'
data = pd.read_csv(path, header=None, names=['Exam 1', 'Exam 2', 'Admitted'])
print("查看数据前五行")
print(data.head())
print("创建两个分数的散点图，并使用颜色编码来可视化")
positive = data[data['Admitted'].isin([1])]
negative = data[data['Admitted'].isin([0])]

fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(positive['Exam 1'], positive['Exam 2'], s=50, c='b', marker='o', label='Admitted')
ax.scatter(negative['Exam 1'], negative['Exam 2'], s=50, c='r', marker='x', label='Not Admitted')
ax.legend()
ax.set_xlabel('Exam 1 Score')
ax.set_ylabel('Exam 2 Score')
plt.show()
ui.split_line2()
print("sigmoid 函数")

def sigmoid(z):
    return 1 / (1 + np.exp(-z))
print("输出函数图像")
nums = np.arange(-10, 10, step=1)

fig, ax = plt.subplots(figsize=(12,8))
ax.plot(nums, sigmoid(nums), 'r')
plt.show()
ui.split_line2()
print("定义代价函数")

def cost(theta, X, y):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)
    first = np.multiply(-y, np.log(sigmoid(X * theta.T)))
    second = np.multiply((1 - y), np.log(1 - sigmoid(X * theta.T)))
    return np.sum(first - second) / (len(X))

ui.split_line2()
print("设置 X, theta, y")
# add a ones column - this makes the matrix multiplication work out easier
data.insert(0, 'Ones', 1)

# set X (training data) and y (target variable)
cols = data.shape[1]
X = data.iloc[:,0:cols-1]
y = data.iloc[:,cols-1:cols]

# convert to numpy arrays and initalize the parameter array theta
X = np.array(X.values)
y = np.array(y.values)
theta = np.zeros(3)
print("检查 X, theta, y 的维度")
print(X.shape, theta.shape, y.shape)
print("计算优化前的代价函数")
print(cost(theta, X, y))
ui.split_line1()
print("定义梯度下降函数，注意，这里指计算出梯度步长，实际由 SciPy 的 optimize 函数计算 Cost 和梯度")

def gradient(theta, X, y):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)

    parameters = int(theta.ravel().shape[1])
    grad = np.zeros(parameters)

    error = sigmoid(X * theta.T) - y

    for i in range(parameters):
        term = np.multiply(error, X[:,i])
        grad[i] = np.sum(term) / len(X)

    return grad

print("第一次执行梯度下降的结果")
print(gradient(theta, X, y))
print("用SciPy's truncated newton（TNC）实现寻找最优参数")
import scipy.optimize as opt
result = opt.fmin_tnc(func=cost, x0=theta, fprime=gradient, args=(X, y))
print("结果为", result)
print("重新计算 Cost")
print(cost(theta, X, y))
ui.split_line2()
print("编写预测函数")

def predict(theta, X):
    probability = sigmoid(X * theta.T)
    return [1 if x >= 0.5 else 0 for x in probability]

print("计算当前 theta 在训练集上的准确率")
theta_min = np.matrix(result[0])
predictions = predict(theta_min, X)
correct = [1 if ((a == 1 and b == 1) or (a == 0 and b == 0)) else 0 for (a, b) in zip(predictions, y)]
accuracy = (sum(map(int, correct)) % len(correct))
print ('accuracy = {0}%'.format(accuracy))

ui.split_line1()
print("设想你是工厂的生产主管，你有一些芯片在两次测试中的测试结果。对于这两次测试，你想")
print("决定是否芯片要被接受或抛弃。为了帮助你做出艰难的决定，你拥有过去芯片的测试数据集，")
print("从其中你可以构建一个逻辑回归模型")
ui.split_line2()
print("载入数据并可视化")
path = 'data/ex2data2.txt'
data2 = pd.read_csv(path, header=None, names=['Test 1', 'Test 2', 'Accepted'])
print("查看数据前 5 行")
print(data2.head())
print("绘制数据分布")
positive = data2[data2['Accepted'].isin([1])]
negative = data2[data2['Accepted'].isin([0])]

fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(positive['Test 1'], positive['Test 2'], s=50, c='b', marker='o', label='Accepted')
ax.scatter(negative['Test 1'], negative['Test 2'], s=50, c='r', marker='x', label='Rejected')
ax.legend()
ax.set_xlabel('Test 1 Score')
ax.set_ylabel('Test 2 Score')
plt.show()
print("构造一组多项式特征")
degree = 5
x1 = data2['Test 1']
x2 = data2['Test 2']

data2.insert(3, 'Ones', 1)

for i in range(1, degree):
    for j in range(0, i):
        data2['F' + str(i) + str(j)] = np.power(x1, i-j) * np.power(x2, j)

data2.drop('Test 1', axis=1, inplace=True)
data2.drop('Test 2', axis=1, inplace=True)
print("查看新数据的前 5 行")
print(data2.head())
ui.split_line2()
print("定义带正则化项的 Cost 函数")

def costReg(theta, X, y, learningRate):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)
    first = np.multiply(-y, np.log(sigmoid(X * theta.T)))
    second = np.multiply((1 - y), np.log(1 - sigmoid(X * theta.T)))
    reg = (learningRate / (2 * len(X))) * np.sum(np.power(theta[:,1:theta.shape[1]], 2))
    return np.sum(first - second) / len(X) + reg

print("定义梯度下降函数")

def gradientReg(theta, X, y, learningRate):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)

    parameters = int(theta.ravel().shape[1])
    grad = np.zeros(parameters)

    error = sigmoid(X * theta.T) - y

    for i in range(parameters):
        term = np.multiply(error, X[:,i])

        if (i == 0):
            grad[i] = np.sum(term) / len(X)
        else:
            grad[i] = (np.sum(term) / len(X)) + ((learningRate / len(X)) * theta[:,i])

    return grad

print("准备好训练需要用的数据")
# set X and y (remember from above that we moved the label to column 0)
cols = data2.shape[1]
X2 = data2.iloc[:,1:cols]
y2 = data2.iloc[:,0:1]

# convert to numpy arrays and initalize the parameter array theta
X2 = np.array(X2.values)
y2 = np.array(y2.values)
theta2 = np.zeros(11)
print("设置初始学习率")
learningRate = 1
print("优化前的 Cost 为")
print(costReg(theta2, X2, y2, learningRate))
print("第一步的梯度步长为")
print(gradientReg(theta2, X2, y2, learningRate))
print("计算优化后的结果")
result2 = opt.fmin_tnc(func=costReg, x0=theta2, fprime=gradientReg, args=(X2, y2, learningRate))
print(result2)
print("预测准确率")
theta_min = np.matrix(result2[0])
predictions = predict(theta_min, X2)
correct = [1 if ((a == 1 and b == 1) or (a == 0 and b == 0)) else 0 for (a, b) in zip(predictions, y2)]
accuracy = (sum(map(int, correct)) % len(correct))
print ('accuracy = {0}%'.format(accuracy))

ui.split_line1()
print("用 Scikit-Learn 进行优化")
from sklearn import linear_model#调用sklearn的线性回归包
model = linear_model.LogisticRegression(penalty='l2', C=1.0)
model.fit(X2, y2.ravel())
print("最终的准确率为", model.score(X2, y2))
print("可以看到这个准确率比我们之前优化的低不少")


"""
File: sin_Taylor_series_diffeq.py
Copyright (c) 2016 Andrew Malfavon
Excerise A.14
License: MIT
Computes a Taylor polynomial approximation for sinx and shows the accuracy in a table
"""
import numpy as np

#computes the approximation based on the exercises in the book
def sin_Taylor(x, n):
    i = 1
    an_prev = x
    sn_prev = 0.0
    while i <= n + 1:
        sn = sn_prev + an_prev
        an = (-x**2 * an_prev) / (((2 * i) + 1) * (2 * i))
        sn_prev = sn
        an_prev = an
        i += 1
    return abs(an), sn

#test for small x=0.0001 and n = 2, sin(x) is zero to the third decimal
def test_sin_Taylor():
    assert round(sin_Taylor(0.0001, 2)[1], 3) == 0.0

#create a table to display the approximations for various n and x
def table():
    print '%15s %15s %20s %8s' % ('x Values:', 'n Values:', 'Aproximations:', 'Exact:')
    x_list = [0.01, np.pi/2, np.pi, 3*np.pi/2]
    n_list = [2, 5, 10, 100]
    for x in x_list:
        for n in n_list:
            table = sin_Taylor(x, n)
            print '%15f %15f %15f %15f' % (x, n, table[1], np.sin(x))


import numpy as np

#Questions on NumPy Sorting and Searching
# How to get the indices of the sorted array using NumPy in Python?
#argsort Returns the indices that would sort an array.
np.argsort(np.array([3, 1, 2]))

# Finding the k smallest values of a NumPy array
#sort Return a sorted copy of an array.
k = 4
arr = np.array([23, 12, 1, 3, 4, 5, 6])
arr1 = np.sort(arr)
arr1[:k]

# How to get the n-largest values of an array using NumPy?
k = 4
arr = np.array([23, 12, 1, 3, 4, 5, 6])
arr1 = np.sort(arr)
arr1[-k:]

# Sort the values in a matrix
#sort Return a sorted matrix
i = np.matrix('[4, 1; 12, 3]')
i.sort()
i

# Filter out integers from float numpy array
#astype Copy of the array, cast to a specified type.
ini_array = np.array([1.0, 1.2, 2.2, 2.0, 3.0, 2.0])
result = ini_array[ini_array != ini_array.astype(int)]
result

# Find the indices into a sorted array
#searchsorted Find indices where elements should be inserted to maintain order.
in_arr = [2, 3, 4, 5, 6]
np.searchsorted(in_arr, 4, side='right')

NumpyTutorial/Questions on NumPy Sorting and Searching.py


import numpy as np
import random
from matplotlib import pyplot as plt

class GradientDescentPlot(object):

    def __init__(self,
                random_X=True,
                range_X=range(-20, 21),
                learning_rate=0.1,
                function=lambda x: x**2 + 1,
                function_derivative=lambda x: 2*x,
                verbose=False,
                step_delay=0.5,
                use_seaborn=True):

        if use_seaborn:
            import seaborn as sns
            sns.set()
            sns.set_context('paper')

        plt.ion()

        # generating data
        self.X = range_X
        self.learning_rate = learning_rate
        self.function = function
        self.function_derivative = function_derivative
        self.verbose = verbose
        self.step_delay = step_delay

        # init gradient values
        self.gradient_X = random.choice(self.X) if random_X else np.max(self.X)
        self.gradient_Y = self.function(self.gradient_X)

    def show(self, loop_limit=15, save_fig=False, save_img_name='./%d-gradient-descent-plot.png'):
        plt.plot(self.X, [self.function_derivative(x) for x in self.X], color='gray')
        plt.plot(self.X, [self.function(x) for x in self.X], color='black')

        # show the current gradient point
        plt.scatter([self.gradient_X], [self.gradient_Y], color='red')

        if save_fig:
            plt.savefig(save_img_name % 0)

        for i in range(loop_limit):
            self.update_plot()

            if save_fig:
                plt.savefig(save_img_name % (i + 1))

    def update_plot(self):
        self.compute_gradient()
        plt.scatter([self.gradient_X], [self.gradient_Y])
        plt.pause(self.step_delay)

    def compute_gradient(self):
        self.gradient_X += - self.learning_rate * self.function_derivative(self.gradient_X)
        self.gradient_Y = self.function(self.gradient_X)

        if self.verbose:
            print('X:', self.gradient_X)
            print('Y:', self.gradient_Y)
            print()

if __name__ == '__main__':
    plt.title('Quadratic function')
    quadratic = GradientDescentPlot(
        random_X=False,
        step_delay=0.3
    ).show(loop_limit=5)
    plt.clf()

    plt.title('Stucking in local minima with small learning rate.')
    GradientDescentPlot(
        random_X=False,
        range_X=np.arange(-4, 5, 0.08),
        function=lambda x: x**3 + x**2 - x + 1,
        function_derivative=lambda x: 3*(x**2) + 2*x - 1,
        verbose=True,
        learning_rate=0.07
    ).show(loop_limit=7)
    plt.clf()

    plt.title('Going through local minima with bigger learning rate.')
    GradientDescentPlot(
        random_X=False,
        range_X=np.arange(-4, 5, 0.08),
        function=lambda x: x**3 + x**2 - x + 1,
        function_derivative=lambda x: 3*(x**2) + 2*x - 1,
        verbose=True,
        learning_rate=0.0727,
        step_delay=0.25
    ).show(loop_limit=13, save_fig=False, save_img_name='./%d-plot.png')