import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt
import os

def calculateConvOutputSize(I, P, F, S):
	O = (I + 2 * P - F) // S + 1
	return O

def im2col(X, FH, FW, S = 1, P = 0):
	N, C, H, W = X.shape
	OH = (H + 2 * P - FH) // S + 1
	OW = (W + 2 * P - FW) // S + 1

	X_padded = np.pad(X, ((0, 0), (0, 0), (P, P), (P, P)), mode = 'constant')

	k = np.repeat(np.arange(C), FH * FW).reshape(-1, 1)
	i1 = np.tile(np.repeat(np.arange(FH), FW), C).reshape(-1, 1)
	i2 = np.repeat(np.arange(OH) * S, OW).reshape(1, -1)
	i = i1 + i2
	j1 = np.tile(np.arange(FW), FH * C).reshape(-1, 1)
	j2 = np.tile(np.arange(OW) * S, OH).reshape(1, -1)
	j = j1 + j2

	X_col = X_padded[:, k, i, j].transpose(1, 2, 0).reshape(FH * FW * C, OH * OW * N)
	return X_col

def col2im(col, X_shape, FH, FW, S = 1, P = 0):
	N, C, H, W = X_shape
	H_padded = H + 2 * P
	W_padded = W + 2 * P
	OH = (H + 2 * P - FH) // S + 1
	OW = (W + 2 * P - FW) // S + 1

	X_padded = np.zeros((N, C, H_padded, W_padded))

	k = np.repeat(np.arange(C), FH * FW).reshape(-1, 1)
	i1 = np.tile(np.repeat(np.arange(FH), FW), C).reshape(-1, 1)
	i2 = np.repeat(np.arange(OH) * S, OW).reshape(1, -1)
	i = i1 + i2
	j1 = np.tile(np.arange(FW), FH * C).reshape(-1, 1)
	j2 = np.tile(np.arange(OW) * S, OH).reshape(1, -1)
	j = j1 + j2

	col_reshape = col.reshape(C * FH * FW, OH * OW, N)

	col_reshape = col_reshape.transpose(2, 0, 1)

	np.add.at(X_padded, (slice(None), k, i, j), col_reshape)

	if(P == 0):
		return X_padded
	else:
		return X_padded[:, :, P:-P, P:-P]

def lerp(a0, a1, t):
	a = a0 + (a1 - a0) * t
	return a

def networkSaver(network, DIR):
	params, unlearnable_params = network.returnParams()
	for key, val in params.items():
		np.save(DIR + 'learnable_' + key + '.npy', val)
	for key, val in unlearnable_params.items():
		np.save(DIR + 'unlearnable_' + key + '.npy', val)

def networkLoader(network, DIR):
	params = {}
	unlearnable_params = {}

	for cur_file in os.listdir(DIR):
		if('unlearnable' in cur_file):
			cur_unlearnable_params = np.load(DIR + cur_file)
			unlearnable_params[cur_file[:-4].partition('unlearnable_')[2]] = cur_unlearnable_params
		elif('learnable' in cur_file):
			cur_learnable_params = np.load(DIR + cur_file)
			params[cur_file[:-4].partition('learnable_')[2]] = cur_learnable_params

	network.paramInit_pretrained(params, unlearnable_params)
	network.layerInit()

def categorical(X, category_num):
	X_categorical = np.zeros((category_num, X.shape[1]))
	X_ravel = np.ravel(X)
	X_arange = np.arange(X.shape[1])
	X_categorical[X_ravel, X_arange] = 1

	return X_categorical

def shuffle_data(X, Y):
	if(X.ndim == 2):
		perm = np.random.permutation(X.shape[1])
		X_shuffled = X.T[perm].T
		Y_shuffled = Y.T[perm].T
		return X_shuffled, Y_shuffled

	elif(X.ndim == 4):
		perm = np.random.permutation(X.shape[0])
		X_shuffled = X[perm]
		Y_shuffled = Y.T[perm].T
		return X_shuffled, Y_shuffled

def data_augmentation(X, Y, rotate = 15.0, flipHor = False, image_size = None):
	if(X.ndim == 2):
		m = X.shape[1]
		if(rotate != False):
			X_rotate = []
			Y_rotate = []
		if(flipHor == True):
			X_flipHor = []
			Y_flipHor = []

		for i in range(m):
			if(rotate != False):
				cur_X_rotate1 = ndimage.interpolation.rotate(X.T[i].reshape(image_size), angle = rotate, reshape = False)
				cur_X_rotate2 = ndimage.interpolation.rotate(X.T[i].reshape(image_size), angle = -rotate, reshape = False)
				X_rotate.append(cur_X_rotate1)
				X_rotate.append(cur_X_rotate2)
				Y_rotate.append(Y.T[i].T)
				Y_rotate.append(Y.T[i].T)

			if(flipHor == True):
				cur_X_flipHor = np.flip(X.T[i].reshape(image_size), axis = 1)
				X_flipHor.append(cur_X_flipHor)
				Y_flipHor.append(Y.T[i].T)

		if(rotate != False):
			X_rotate = np.asarray(X_rotate)
			X_rotate = X_rotate.transpose(1, 2, 0)
			X_rotate = X_rotate.reshape(X.shape[0], X.shape[1] * 2)
			Y_rotate = np.asarray(Y_rotate).T

		if(flipHor == True):
			X_flipHor = np.asarray(X_flipHor)
			X_flipHor = X_flipHor.transpose(1, 2, 0)
			X_flipHor = X_flipHor.reshape(X.shape[0], X.shape[1])
			Y_flipHor = np.asarray(Y_rotate).T

		X_augmented = np.copy(X)
		Y_augmented = np.copy(Y)

		if(rotate != False):
			X_augmented = np.concatenate((X_augmented, X_rotate), axis = 1)
			Y_augmented = np.concatenate((Y_augmented, Y_rotate), axis = 1)

		if(flipHor == True):
			X_augmented = np.concatenate((X_augmented, X_flipHor), axis = 1)
			Y_augmented = np.concatenate((Y_augmented, Y_flipHor), axis = 1)

		X_augmented, Y_augmented = shuffle_data(X_augmented, Y_augmented)

	return X_augmented, Y_augmented

def draw_border(network, X_train, Y_train, X_val, Y_val, h = 0.01, c_map = 'coolwarm', c_edge = 'black', show_plt = True):
	colormap = plt.get_cmap(c_map)
	#X[0] is the x axis of data X, and use it to get max & min x axis value
	#X[1] is the y axis of data X, and use it to get max & min y axis value
	x_min, x_max = min(X_train[0].min(), X_val[0].min()) - 1, max(X_train[0].max(), X_val[0].max()) + 1
	y_min, y_max = min(X_train[1].min(), X_val[1].min()) - 1, max(X_train[1].max(), X_val[1].max()) + 1

	#Y should be an one-hot vector, therefore turn it into a label.
	Y_train_label = np.argmax(Y_train, axis = 0)
	Y_val_label = np.argmax(Y_val, axis = 0)

	#create xx, yy. These are each x axis values, and y axis values of the final mesh. This also means that xx and yy has the same shape with the final mesh vector
	#these xx, yy are created with max & min x axis, y axis values with the distance of h
	xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))

	#first, ravel xx and yy to get the 1D vector each containing x axis, y axis values of the final mesh.
	#Then, put these all together to get a (2, data_num) vector, which is a vector holding all the positions of the final mesh points.
	Z = np.c_[xx.ravel(), yy.ravel()].T

	#this will be fed into the network, in order to predict the output value for all these mesh points.
	#the network will return a (category_num, data_num) vector, and the label that has the biggest values will be the final label of that point.
	#Also, the final vector didn't go through the final layers, softmax and loss functions. However, this does not matter because applying softmax does not change the small & large relationship at all.
	A = network.predict(Z)

	#to get the final label, use argmax. Also, reshape it back to the shape of xx, which is the shape of our final meshgrid
	A_label = np.argmax(A, axis = 0).reshape(xx.shape)

	#plot the meshgrid using A_label, containing the label for all the mesh points.
	plt.contourf(xx, yy, A_label, cmap = colormap)

	#set the x & y label of the plot
	plt.ylabel('x2')
	plt.xlabel('x1')

	#plot the scatter plot for data point X, and label it using Y_label,
	plt.scatter(X_train[0], X_train[1], c = Y_train_label, cmap = colormap, edgecolors = c_edge)
	plt.scatter(X_val[0], X_val[1], c = Y_val_label, cmap = colormap, edgecolors = c_edge, marker = '^')

	#finally show the plot(meshgrid + scatter plot), using plt.show()
	if(show_plt == True):
		plt.show()


from sympy import *
from math import *
import sys

from lib_rungekutta import *

# Cấp chính xác
precision_x   = 3;
precision_y   = 7;
precision_eps = 12;

# Nhập dữ liệu
expr      = input("Nhập hàm f(x, y)trong phương trình y' = f(x, y) = ");
x_0       = float(input("Nhập giá trị ban đầu x_0 = "));
y_0       = float(input(f"Nhập giá trị ban đầu y({x_0}) = "));
h         = float(input("Nhập khoảng cách giữa 2 điểm liên tiếp h = "));
n         = int(input("Nhập số mốc của lưới điểm n = "));
s         = input("Nhập số nấc Runge-Kutta (1, 2, 3, 4 hoặc Heun):");

# Giải PTVP
s  = s if(s == "heun" or s == "Heun") else int(s);
uu = rungekutta_oop(expr, x_0, y_0, h, n, s);
g  = (uu.Solve());

# In ra sai số và kết quả
if(s == "heun" or s == "Heun"):
#{
    print(f"Phương pháp Runge-Kutta 3 nấc với cấu hình hệ số kiểu Heun hoàn tất với lưới điểm sau, sai số toàn cục O(h^3) = {round(h**3, precision_eps)}:");
#}
else:
#{
    print(f"Phương pháp Runge-Kutta {s} nấc với hoàn tất với lưới điểm sau, sai số toàn cục O(h^{s}) = {round(h**s, precision_eps)}:");
#}
for x in g: print(round(x[0], precision_x), round(x[1], precision_y));

# Đồ thị
uu.getPlot();

# Nghiệm kiểm chứng
print("====================================================================")
sol_expr  = input("Nhập phương trình nghiệm kiểm chứng y(x) hoặc NONE nếu không có: y(x) = ");
if(sol_expr != "NONE"):
#{
    sol = lambdify(symbols("x"), sympify(sol_expr), "math");
    print("So sánh với giá trị đúng của nghiệm:");
    for x in g: print(f"y*({round(x[0], precision_x)}) = {round(x[1], precision_y)}, so sánh với giá trị chuẩn y({round(x[0], precision_x)}) = {round(sol(x[0]), precision_y)}, sai số {round(abs(sol(x[0]) - x[1]), precision_eps)}");
#}

Topic 5 - Solving Differential Equations/28.Runge_Kutta/main_rungekutta.py


#!/usr/bin/env python
# coding: utf-8

# # Machine Learning using Univariate Linear Regression (Ex1)
#
# Estimating the profit from a city basing on its population size.
#

# In[1]:

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from pylab import meshgrid, cm
from mpl_toolkits.mplot3d import axes3d, Axes3D
from matplotlib.ticker import LinearLocator, FormatStrFormatter

sns.set(style="whitegrid")

# ## ======================= Part 2: Plotting =======================

# In[2]:

print('Plotting Data ...\n')
data = pd.read_csv('ex1data1.txt', names = ['population_size', 'profit'])
print(data.head())

# In[3]:

x = data['population_size'] # training set
y = data['profit'] # labels
m = len(y) # training size

# In[4]:

# plots the data
# f, ax = plt.subplots(figsize=(6.5, 6.5))
# sns.despine(f, left=True, bottom=True)
# sns.scatterplot(data=data, x="population_size", y="profit", sizes=(1, 8))
sns.jointplot('population_size', 'profit', data=data, color="m", height=7)

# ## =================== Part 3: Cost and Gradient descent ===================

# Add a column of ones to X to facilitate the manipulation.
#
# Each row is a input with the following format:
#
# $X[0] = [ x_0, x_1 ]$ where $x_0 = 1$

# In[5]:

# Add a column of ones to X to facilitate the manipulation
X = np.column_stack((np.ones(m), x))

# Initialize fitting parameters $\theta = [0, 1]^T$ (fixed to 0 to validated the output from computCost and gradient descent).
#
# Learning rate $\alpha = 0.01$.

# In[6]:

theta = np.zeros([2])
# Some gradient descent settings
iterations = 1500;
alpha = 0.01; # learning rate

# In[7]:

print(np.shape(X))
print(X[:5])
print(np.shape(y))
print(y[:5])
print(np.shape(theta))
print(theta)

# ### Hypothesis Function
# Function that defines our linear model.
#
# Definition:
#
# $h_\theta(x) = \theta_0 + \theta_1 * x_1$
#
# Vectorial form:
#
# $h_\theta(x) = \theta^{T} * x$
#
# where:
# $x = [x_0, x_1]$; $x_0 = 1$ and $\theta = [\theta_0, \theta_1]$

# In[8]:

def hypothesis(X, theta):
    # return [np.dot(xi, theta) for xi in X]
    return X.dot(theta)

# ### Compute cost for linear regression
# `computeCost` computes the cost of using theta as the parameter for linear regression to fit the data points in X and y.
#
# Function cost:
#
# $ J(\theta) = \frac{1}{2m} \sum_{i=0}^{m} (h_\theta(x^{(i)}) - y^{(i)})^{2} $
#
# Expecting 32.07 at first iteration as $\theta$ was initialized with $[0, 0]$.

# In[9]:

"""
Inputs:
X = [
  [ x0, x1 ]
]
y = [
  [ ]
]
theta = [ theta_0, theta_1 ]
"""
def computeCost(X, y, theta):
    m = len(y)
    h_theta = hypothesis(X, theta)
    # return (1 / (2 * m)) * ((h_theta - y) ** 2).sum()
    delta = (h_theta - y)
    return (1 / (2 * m)) * delta.T.dot(delta)

# Testing the cost function with $\theta = [0, 0]^T$

# In[10]:

# compute and display initial cost
J = computeCost(X, y, [0, 0]);
print('Cost computed = %f' % J);
print('Expected cost value (approx) 32.07\n');

# Testing the cost function with $\theta = [-1, 2]^T$

# In[11]:

# further testing of the cost function
J = computeCost(X, y, [-1, 2]);
print('Cost computed = %f' % J);
print('Expected cost value (approx) 54.24\n');

# ### Running Gradient Descent
# We use gradient descent to find the parameters values $\theta$ that **minimize** $J$.
# In each iteration we calculate a $\theta'$ where $J(\theta') < J(\theta)$.
#
# This $\Theta'$ defined by $\theta' = \theta - \alpha * \nabla h_\theta$, where $\nabla h_\theta$ is the amount we need change to *minimize* $J(\theta)$ and $\alpha$ is the step we will take.
#
# If $\alpha$ is too large we could ending increasing $J$, it need to be small enough to converge quickly to the points the $J(\Theta)$ is minimum.
#
#
# Step to update each parameter:
#
# $\theta_j := \theta_j - \alpha * \frac{\partial J}{\partial \theta_j} $
#
# Where:
#
# $\frac{\partial J}{\partial \theta_j} = \frac{1}{m} \sum_{i=1}^{m} [( h_\theta(x^{(i)}) - y^{(i)})$ when $j = 0$ (bacause it is the bias - doesn't have a feature).
#
# $\frac{\partial J}{\partial \theta_j} = \frac{1}{m} \sum_{i=1}^{m} [( h_\theta(x^{(i)}) - y^{(i)}) * x^{(i)}]$ when $j = 1$.
#
# Metrix form:
#
# $ \frac{\partial J}{\partial \theta_j} = \frac{1}{m} = X^{T} ( h_\theta(x^{(i)}) - y^{(i)}) $
#
#
# `gradientDescent(X, y, theta, alpha, num_iters)` performs gradient descent to learn $\theta$ parameters.
#
# It return the an array with $\theta$ containing the values found by taking num_iters gradient steps with learning rate alpha.
#
# Also it return a array with the history of $J(\theta)$ to be plotted.
#

# In[12]:

# calculate the theta_0 and theta_1 individually
def gradientDescentManualWay(X, y, theta, alpha, num_iters):
    m = len(y)
    J_history = np.zeros(num_iters)

    for i in range(num_iters):
        h_theta = hypothesis(X, theta)
        # update the theta individually
        nabla_theta_0 = (1 / m) * (h_theta - y).sum() # theta 0
        nabla_theta_1 = (1 / m) * ((h_theta - y) * X.transpose()[1]).sum() # theta 1

        # theta[0] = theta[0] - alpha * nabla_theta_0
        # theta[1] = theta[1] - alpha * nabla_theta_1
        theta = theta - alpha * np.array([nabla_theta_0, nabla_theta_1])

        # Save the cost J in every iteration
        J_history[i] = computeCost(X, y, theta);
    return theta, J_history

# better way using the convenient x_0 = 1
def gradientDescentVectorialWay(X, y, theta, alpha, num_iters):
    m = len(y)
    J_history = np.zeros(num_iters)

    for i in range(num_iters):
        h_theta = hypothesis(X, theta)
        nabla = (1 / m) * np.dot((h_theta - y).transpose(), X);

        theta = theta - alpha * nabla.transpose()

        # Save the cost J in every iteration
        J_history[i] = computeCost(X, y, theta);
    return theta, J_history

# In[13]:

print('\nRunning Gradient Descent ...\n')
theta = np.zeros(2)
theta, J_history = gradientDescentVectorialWay(X, y, theta, alpha, iterations);

# print theta to screen
print('Theta found by gradient descent:');
print(theta);
print('\nExpected theta values (approx)');
print('[ -3.6303,  1.1664]');

# In[14]:

print('Predict values for population sizes of 35,000 and 70,000')

predict1 = hypothesis(np.array([[1, 3.5]]), theta)
print('For population = 35,000, we predict a profit of %f\n' % (predict1[0] * 10000))

predict2 = hypothesis(np.array([[1, 7]]), theta)
print('For population = 70,000, we predict a profit of %f\n' % (predict2[0] * 10000))

# ## ============= Part 4: Visualizing $J(\theta_0, \theta_1)$ =============

# In[15]:

theta0_vals = np.arange(-10, 10, 2)
theta1_vals = np.arange(-1, 4, 0.5)

# initialize J_vals to a matrix of 0's
J_vals = np.zeros([len(theta0_vals), len(theta1_vals)])

# Fill out J_vals
for t0 in range(len(theta0_vals)):
    for t1 in range(len(theta1_vals)):
        J_vals[t0][t1] = computeCost(X, y, [theta0_vals[t0], theta1_vals[t1]])

gX, gY = meshgrid(theta0_vals, theta1_vals)

# In[16]:

print('Visualizing J(theta_0, theta_1) ...')
fig = plt.figure()
ax = Axes3D(fig)

surf = ax.plot_surface(gX, gY, J_vals, rstride=1, cstride=1, cmap=cm.RdBu, linewidth=0, antialiased=False)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
fig.colorbar(surf, shrink=0.5, aspect=5)

# In[17]:

# Plot J_vals as 15 contours spaced logarithmically between 0.01 and 100
fig = plt.figure()
ax = Axes3D(fig)

cset = ax.contour(gX, gY, J_vals.transpose(), 16, extend3d=True)
ax.clabel(cset, fontsize=9, inline=1)
fig.colorbar(cset, shrink=0.5, aspect=5)

# ### ============= Extra: Visualizing Learning Curve =============

# In[18]:

# Plot the linear fit
sns.lineplot(range(iterations), J_history)

# ### ============= Extra: Plotting Hypothesis Model =============

# In[19]:

population_size = np.arange(0, 25, 2.5)
profit = np.zeros(len(population_size))

for i in range(len(population_size)):
    predict = hypothesis(np.array([[1, population_size[i]]]), theta)
    profit[i] = predict[0]

g = sns.FacetGrid(data, height=6)
g = g.map(plt.scatter, 'population_size', 'profit', edgecolor="w")
plt.plot(population_size, profit, color='r')

# In[ ]:

Machine Learning - Andrew Ng/assignments/machine-learning-ex1/ex1_python/Machine_Learning_Univariate_Linear_Regression.py


# you will often need in your calculation to generate a series of numbers
# from say, 0 to 100 in steps of 1 (i.e, 0,1,2,3,4,5...100)
# or 1 to 10 in steps of 0.1 (i.e, 1,1.1,1.2,1.3,1.4...10 )
# This example shows you how to do it

# for this you need to install numpy module (in case you have not installed it)
# to know how to install it using anaconda navigator, go to https://github.com/deepaksamuel/python-tutorials and see the README.md

# In c/c++, we use the #include statement. In python we use import statement to include modules
# the numpy module contains important mathematical functions that you will learn soon.
# First step is to import numpy as np (np will now be a shortcut for numpy)

import numpy as np

# in python variables have no identifiers like we have in C/C++ (int, float, double etc.) They are automatically done by the interpreter for you
# Here ser is a variable, arange is a function in the numpy module (np is the shortcut for numpy as specified in line 13)
# The following statements will print numbers from 0-99 in steps of 1. The third argument is the step size, which if you omit, will be taken as 1.

ser = np.arange(0,100)
print(ser)

# The following statements will print numbers from 1-9 in steps of 0.1

ser = np.arange(1,10,0.1)
print(ser)

# Assignment:
# generate a series of numbers between 100 and 1000 with a step size of 100
# generate a series of numbers between 1000 and 100 with a step size of 100
# more information on how to merge arrays and exrtact arrays can be found in fft example


## =========================================================================
## @author Leonardo Florez-Valencia (florez-l@javeriana.edu.co)
## =========================================================================

import numpy, sys
import matplotlib.pyplot as plotter

# Check input arguments
if len( sys.argv ) < 2:
  print( "Usage:", sys.argv[ 0 ], "input.csv" )
  sys.exit( 1 )
# end if

# Load data
D = numpy.genfromtxt( sys.argv[ 1 ], delimiter = ',' )
X = D[ : , 0 : D.shape[ 1 ] - 1 ]
Y = D[ : , D.shape[ 1 ] - 1 : ]

# Initial parameters
w = numpy.matrix( [ 1, 0 ] )
b = 0.01
alpha = 1e-2
L = 1

# Show initial configuration
fig, ax1 = plotter.subplots( )
ax1.axis( 'equal' )
dataX, dataY = zip( *X )
plotter.scatter(
  dataX, dataY, c = "#ff0000", marker = "x"
  )
plotter.quiver(
  [ 0 ], [ 0 ],
  [ -w.item( ( 0, 1 ) ) ], [ w.item( ( 0, 0 ) ) ]
  )
plotter.show( )

# Initial cost
m = float( X.shape[ 0 ] )
H = numpy.multiply( Y.T, ( w @ X.T ) - b )
mH = numpy.array( H < 1 ).flatten( ).tolist( )
Xp = X[ mH, : ]
Yp = Y[ mH, : ]
J = ( ( w @ w.T ) * L ) + ( b * b * L ) + ( ( 1 - H[ : , mH ] ).sum( ) / m )

# Main loop
stop = False
nIter = 0
while not stop:

  dw = ( 2 * L * w ) - \
       numpy.multiply( Yp, Xp ).sum( axis = 0 )
  db = ( Yp.sum( ) * b ) + ( 2 * b * L )

  w = w - ( ( alpha / m ) * dw )
  b = b - ( ( alpha / m ) * db )

  H = numpy.multiply( Y.T, ( w @ X.T ) - b )
  mH = numpy.array( H < 1 ).flatten( ).tolist( )
  Xp = X[ mH, : ]
  Yp = Y[ mH, : ]
  Jn = ( ( w @ w.T ) * L ) + ( ( 1 - H[ : , mH ] ).sum( ) / m )

  if J - Jn < 1e-6:
    stop = True
  # end if
  print( "dJ =", ( J - Jn ).item( ), "Iteration =", nIter )
  J = Jn
  nIter = nIter + 1
# end while

plotter.scatter(
  dataX, dataY, c = "#ff0000", marker = "x"
  )
plotter.quiver(
  [ 0 ], [ 0 ],
  [ -w.item( ( 0, 1 ) ) ], [ w.item( ( 0, 0 ) ) ]
  )
plotter.show( )

# eof - $RCSfile$


from typing import Tuple

import numpy as np

def get_dataset() -> Tuple[np.ndarray, np.ndarray]:
    """OR dataset."""
    x = np.array([[0, 0], [1, 0], [0, 1], [1, 1]])
    y = np.array([[0], [1], [1], [1]])
    return x, y

def to_categorical(y: np.ndarray, num_classes: int) -> np.ndarray:
    y_categorical = np.zeros(shape=(len(y), num_classes))
    for i, yi in enumerate(y):
        y_categorical[i, yi] = 1
    return y_categorical

if __name__ == "__main__":
    x, y = get_dataset()
    print(y.shape)
    y_categorical = to_categorical(y, num_classes=2)
    print(y_categorical.shape)
    print(y_categorical)


"""
GeometryFunctions.py

Several functions from geometry which are useful for modelling radar geometries
Some of these were originally in BistaticFunctions.py

Created by: Ashiv Dhondea
Created on: 26 April 2017
Edited:
28/04/17: Added fnCalculate_Projection_3D_2D
08/05/17: created the function fnCalculate_CircumferencePoints
09/05/17: created the function fnCheck_IsInCircle
09/05/17: added the function fnSmoothe_AngleSeries
12/05/17: fixed a sign bug in fnCalculate_ClosestPointOnLine
12/05/17: debugged the function fnSmoothe_AngleSeries
24/05/17: commented out print statements in the function fnSmoothe_AngleSeries
30/05/17: added the function fnCalculate_BeamFoV

29.08.17: moved the rotation functions from AstFn to this library

Ref:
1. Coordinate conversion and tracking for very long range radars. Tian, Bar-Shalom

Books on vector analysis may also be useful.
"""
import math
import numpy as np
# ----------------------------------------------------------------------------------------------- #
def fnCalculate_PlaneNormal(p,q,r):
	"""
	Calculate the normal vector to a plane containing three points

	Created: 21 April 2017
	Added to BD on 22 April 2017. Validation in testbistaticplane.py
	Moved to GF on 26/04/17
	"""
	pq = q - p;
	pr = r - p;
	normal_vec = np.cross(pq,pr);
	return normal_vec

def fnCalculate_Coords_Plane3D(p,q,r):
	"""
	Calculate equation of plane containing the 3 points p,q,r

	matches results from http://keisan.casio.com/exec/system/1223596129

	Created: 26 April 2017
	"""
	normal_vec = fnCalculate_PlaneNormal(p,q,r);
	d = -q.dot(normal_vec);
	return normal_vec, d

def fnCalculate_LawOfSines_Angles(a,A,B,C):
	"""
	Implements the Law of Sines given
	the 3 angles + 1 side

	A,B,C = angles in the triangle
	a,b,c = length of the side opposite the angle

	Be careful about which angle corresponds to
	which side of the bistatic triangle.

	Created: 22 April 2017
	"""
	ratio = a/math.sin(A);
	b = math.sin(B)*ratio;
	c = math.sin(C)*ratio;
	return b,c
# -----------------------------------------------------------------------------------------------#
def fnCalculate_Coords_Line3D(direction_vec,pos_line,mu):
    """
    Calculates coordinates of a point on a line in 3D.
    direction_vec is the direction vector of the line
    pos_line is the position vector of a point on the line.
    mu is the free parameter which determines the coordinates of the desired
    point on the line.

    Created: 26/04/17 in testplanetransformations.py
    """
    pos_new = pos_line + mu*direction_vec;
    return pos_new # validated in testplanetransformation.py on 26/04/17

def fnCalculate_ClosestPointOnLine(a,b,p):
	"""
	Based on: https://gamedev.stackexchange.com/questions/72528/how-can-i-project-a-3d-point-onto-a-3d-line

	Created: 26 April 2017
	"""
	ap = (p-a);
	ab = (b-a);
	closest_point = a + np.dot(ap,ab) / np.dot(ab,ab) * ab; # bug: 12/5/17: flipped the sign
	return closest_point # validated in testplanetransformations.py

def fnCalculate_Projection_3D_2D(r_0,e_1,e_2,r_p):
    """
    Calculate the projection of a 3D point onto a 2D plane
    r_0 = normal vector to plane
    e_1 = 3D vector which represents the x-axis of the 2D plane
    e_2 = 3D vector which represents the y-axis of the 2D plane

    http://stackoverflow.com/questions/23472048/projecting-3d-points-to-2d-plane

    Created: 28 April 2017
    """
    n = r_0/np.linalg.norm(r_0);
    pvec = r_p - r_0;
    # 2D coordinates of P are
    t_1 = np.dot(e_1,pvec);
    t_2 = np.dot(e_2,pvec);
    # out of plane separation
    s = np.dot(n,pvec);
    return t_1,t_2,s
# ----------------------------------------------------------------------------------------------------------- #
def fnCalculate_CircumferencePoints(centre,radius,numpoints):
    """
    Calculate the coordinates of points on the circumference of a circle.

    	(x-centrex)^2 + (y-centrey)^2 = radius^2

    	Created: 08 May 2017
	"""
    circpts = np.zeros([2,numpoints]);
    theta = np.linspace(0,2*math.pi,numpoints,endpoint=True);
    xcomp = radius*np.cos(theta) + centre[0];
    ycomp = radius*np.sin(theta) + centre[1];
    circpts = np.vstack((xcomp,ycomp));
    return circpts # validated in main_007_fengyun_06.py
# ----------------------------------------------------------------------------------------------------------- #
def fnCheck_IsInCircle(centre,radius,testpoint):
    """
    Checks if a testpoint lies within a circle
    Created: 9 May 2017
    """
    if (np.linalg.norm(testpoint-centre) <= radius):
        return True
    else:
        return False # validated in main_007_fengyun_06.py

def fnSmoothe_AngleSeries(y,angle):
    """
    Smoothe a time indexed vector of angles.
    angle is the angle about which wrapping occurs.
    Created: 9 May 2017
    Edited:
    24/05/17: commented out print statements
    """
    leny = len(y)
    #print leny
    az_rx_argmin = np.argmin(y);
    #print az_rx_argmin
    az_rx_jump_before = y[az_rx_argmin] - y[az_rx_argmin-1];

    if az_rx_argmin < leny-1: # edited: 12 May 2017: to account for reaching the end of the time series at az_rx_argmin
		az_rx_jump_after = y[az_rx_argmin+1] - y[az_rx_argmin];
    else:
		return y # no need to smoothe anything

    if az_rx_jump_before < -angle/2.:
        # compensate after
        az_rx_comp = 1.;
        az_rx_corr = np.hstack((np.zeros([az_rx_argmin],dtype=np.float64),az_rx_comp*angle*np.ones([leny-az_rx_argmin],dtype=np.float64)));
        az_rx_nice = np.add(y,az_rx_corr);
    else:
        if az_rx_jump_after > angle/2.:
            az_rx_comp = -1.;
            az_rx_corr = np.hstack((np.zeros([az_rx_argmin+1],dtype=np.float64),az_rx_comp*angle*np.ones([leny-az_rx_argmin-1],dtype=np.float64)));
            az_rx_nice = np.add(y,az_rx_corr);
        else:
            az_rx_comp = 0;
            az_rx_nice = y;
    return az_rx_nice # validated in testsmooth.py

# ----------------------------------------------------------------------------------------------------- #
def fnCalculate_BeamFoV(a,b):
	"""
	Area covered by beam in degree^2 or radians^2

	Created: 30 May 2017
	"""
	area = math.pi*a*b;
	return area

# -- rotation functions moved on 29.08.17 ------------------ #
# -- Rotation functions  --------------------------------#
def fnRotate1(alpha_rad):
    T = np.array([[1,                 0   ,          0],
                  [0,  math.cos(alpha_rad)  ,math.sin(alpha_rad)],
                  [0, -math.sin(alpha_rad)  ,math.cos(alpha_rad)]],dtype=np.float64);
    return T # Validated against Vallado's example 2-3. 20/06/16

def fnRotate2(alpha_rad):
    T = np.array([[math.cos(alpha_rad), 0, -math.sin(alpha_rad)],
                  [                0, 1,                  0],
                  [math.sin(alpha_rad), 0,  math.cos(alpha_rad)]],dtype=np.float64);
    return T

def fnRotate3(alpha_rad):
    T = np.array([[ math.cos(alpha_rad),math.sin(alpha_rad),0],
                  [-math.sin(alpha_rad),math.cos(alpha_rad),0],
                  [                 0,                0,1]],dtype=np.float64);
    return T # Validated against Vallado's example 2-3. 20/06/16
    # Edited: 28 September 2016: use math instead of np for non-array operations.

# --------------------------------------------------------------------------- #
def fnAngleModular(a,b):
    """
    Modular arithmetic.
    Handling angle differences when target flies from one quadrant to another
    and there's a kink in the az angles
    Created: 29 April 2017
    """
    diffabs = abs(b) - abs(a);
    return diffabs # see testmodulararithmetic.py

# -------------------------------------------------------------------------------------------------------------------------------- #
def fnAngleDiff(ang1,ang2,wrappedangle):
    """
    in [deg] not [rad]

    if wrappedangle = 360 [deg], then keep the result in the range [0,360).
                      180 [deg],                                   [0,180)

    http://gamedev.stackexchange.com/questions/4467/comparing-angles-and-working-out-the-difference

    13 Nov 2016. used when finding the error in estimated keplerians.
    Edited: 14 Nov 2016: the angle to wrap about.
    """
    diffangle = (wrappedangle/2.) - abs(abs(ang1 - ang2) - (wrappedangle/2.));
    return diffangle

def fnAngle(v1, v2, acute):
    """
    Calculates the angle between two vectors in 3D in Python.
    from http://stackoverflow.com/questions/39497496/angle-between-two-vectors-3d-python
    12 Nov 2016
    """
    # v1 is your first vector
    # v2 is your second vector
    angle = np.arccos(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)))
    if (acute == True):
        return angle
    else:
        return 2 * np.pi - angle

def fnZeroTo2Pi(rotangle):
    """
    Wraps angle to fit in [0,2pi).
    Works in [rad] not [deg]
    Date: 7 October 2016
    """
    wrappedangle = rotangle % (2*math.pi);
    return wrappedangle

def fnZeroToPi(rotangle):
    """
    Wraps angle to fit in [0,pi).
    Works in [rad] not [deg]
    Date: 15 October 2016
    """
    wrappedangle = rotangle % (math.pi);
    return wrappedangle

def fnwrapToPi(angle):
    """
    Wrap angle to fit in [-pi,pi)
    Works in [rad] not [deg]
    Created: 16 November 2016
    """
    wrappedangle = (angle + math.pi) % 2*math.pi - math.pi;
    return wrappedangle


#############################################################################
### Python ODE solver, using the Runge-Kutta 4th order method
### Solves ordinary differential equations of the form (dy/dx) = f(x,y)
###
### User must input the following parameters:
### 1. Via command line arguments: time step, total time, output interval
### 2. The right-hand side of the differential equation
###    inside the rhs function, in the rungekutta.py file.
### 3. Initial conditions x_0, y_0 (example: y(x_0) = y_0),
###    at the beginning of the code, right below the imports
###
### Example: python ODE.py 0.1 100 10
### This will solve the ODE with a timestep of dt = 0.1, for 100 time units,
### writing output every 10 time steps.
###
### Output: File "RK4_output.txt", also plots results.
###
### Author: Nikos Tryfonidis, November 2015
### The MIT License (MIT)
### Copyright (c) 2015 Nikos Tryfonidis
### See LICENSE.txt
#############################################################################

# Import necessary packages
import numpy as np
import sys

# Import runge kutta and right-hand-side function
# from rungekutta.py
from rungekutta import RK4, rhs
from plot1D import plot1D

##### Set Initial Conditions Here: #####
x_0 = 0
y_0 = 1
########################################

#################################################################################
### Command line arguments:
#################################################################################

# Check number of command line arguments
if len(sys.argv) != 4:
	print "Usage: python ODE.py <timestep> <total time> <output interval>"
	print "Please run again following the command line input format above."
	print "Exiting..."
	sys.exit(1)

# Get command line arguments
h = float(sys.argv[1])
totalTime = float(sys.argv[2])
interval = int(sys.argv[3])

#################################################################################

# Calculate number of time steps
totalSteps = totalTime/h
# Find size of output
size = int(totalSteps/interval) + 1

# Print a summary of parameters
print "Running Runge-Kutta with the following parameters:"
print "Time step: %f\tTotal time: %f\t" %(h, totalTime)
print "Output interval: %d" %interval

# Initialize variable arrays:
x = np.linspace(x_0, x_0 + totalTime, size)
y = np.zeros(size)
# Initial y value:
y[0] = y_0

# Call Runge Kutta
xOld = x[0]
yOld = y[0]
for t in xrange(1, size):

    for i in xrange(0, interval):
        xOld = xOld + h
        yOld = RK4(xOld, yOld, h, rhs)

    y[t] = yOld

# Save x and y into output file (each array is a column)
np.savetxt('RK4_output.txt', np.c_[x, y], fmt='%.10f')

# Plot x, y (plot1d function in plot1D.py)
plot1D(size, "RK4_output.txt")


#!/usr/bin/env python

# Generate the data points for the boxplots

# This is a python "module" - it simply contains functions,
# but no code that is not inside a function.  We can then import
# it just like any other python module, and call the functions as
# we would call any module functions.
#
# This is an excellent way to provide a function that you want to
# call in two or more different python programs.  Take a look at
# the "boxplot" python programs to see how this is called

import sys
import numpy as np
import math
import random

## Randomly generate some data according to different distribution types
def generate_points(filename):

	# open a file to save our data to
	with open(filename, "w") as dfile:

		# the "dfile" variable is valid within this indented block,
		# once the block completes, the file will be closed

		# write a header to the file
		dfile.write("Group,Source,Measure\n")

		# loop 35 times -- we will end up with 70 points
		# of each distribution, as we write pairs of points
		# in the loop
		for i in range(0,35):

			# Gaussian data, mu=10, sigma=4
			dfile.write("G,A,%f\n" % random.gauss(10,4))
			dfile.write("G,A,%f\n" % random.gauss(10,4))

			# Laplacian data, mu=10, lambda=2.5
			dfile.write("G,B,%f\n" % np.random.laplace(10,2.5))
			dfile.write("G,B,%f\n" % np.random.laplace(10,2.5))

			# Gaussian within Gaussian: N_0(4,2), N_1(16,1)
			dfile.write("G,C,%f\n" % random.gauss(4,2))
			dfile.write("G,C,%f\n" % random.gauss(16,1))

			# two separate blocks of equally and densely spaced points
			dfile.write("U,D,%f\n" % ((i/4.0)-2.0))
			dfile.write("U,D,%f\n" % ((i/4.0)+20.0))

			# two overlapping blocks of equally and densely spaced points
			dfile.write("U,E,%f\n" % ((i/3.0)-1.0))
			dfile.write("U,E,%f\n" % ((i/3.0)+2.0))

			# two overlapping uniform density blocks
			dfile.write("U,F,%f\n" % random.uniform(2,20))
			dfile.write("U,F,%f\n" % random.uniform(2,20))

			# exponential distribution (lambda = 0.125)
			dfile.write("P,G,%f\n" % random.expovariate(0.125))
			dfile.write("P,G,%f\n" % random.expovariate(0.125))


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Mar  5 11:32:03 2021

@author: nick
"""

import numpy as np
from mnist import MNIST

def loadMNIST():

    mndata = MNIST('./data/')
    X_train, y_train = map(np.array, mndata.load_training())
    X_test, y_test = map(np.array, mndata.load_testing())
    X_train = X_train/255.0
    X_test = X_test/255.0

    return X_train,y_train,X_test,y_test

def getEig(X):

    # compute mean of training set
    mu = np.mean(X,axis=0)

    # compute sample covar matrix
    diff = X - mu
    sigma = diff.T @ diff / X.shape[0]

    Lambda, V = np.linalg.eig(sigma)

    return Lambda, V

def project(X,basis):

    # project X onto basis w/ inner products
    proj = X @ basis

    # compute reconstruction as linear comb of eigenvectors
    # reconstr = proj @ basis.T

    return proj

def getRepresentation(X,d,V=None,retV=False):
    # Get a d-dimensional representation of X using PCA

    if V is None:
        Lambda, V = getEig(X)

    basis = np.real(V[:,:d])
    representation = project(X,basis)

    if retV:
        return representation,V
    else:
        return representation

def rescale(X):
    # Rescale the rows of X to have unit L2 norm

    return X/np.linalg.norm(X,ord=2,axis=1)[:,None]


import numpy as np

def naive_relu(x):
    # X must be 2D Numpy tensor
    assert len(x.shape) == 2
    # Avoid overwriting the input tensor
    x = x.copy()

    for row in range(x.shape[0]):
        for col in range(x.shape[1]):
            x[row, col] = max(x[row, col], 0)

    return x

def naive_add(x, y):
    # X must be 2D Numpy tensor
    assert len(x.shape) == 2
    assert x.shape == y.shape
    # Avoid overwriting the input tensor
    x = x.copy()

    for row in range(x.shape[0]):
        for col in range(x.shape[1]):
            x[row, col] += y[row, col]

    return x

def naive_add_matrix_and_vector(x, y):
    # X must be 2D Numpy tensor
    assert len(x.shape) == 2
    # Y must be Numpy vector
    assert len(y.shape) == 1
    assert x.shape[1] == y.shape[0]
    # Avoid overwriting the input tensor
    x = x.copy()

    for row in range(x.shape[0]):
        for col in range(x.shape[1]):
            x[row, col] = y[col]

    return x

def naive_vector_dot(x, y):
    assert len(x.shape) == 1
    assert len(y.shape) == 1
    assert x.shape[0] == y.shape[0]

    z = 0.

    for row in range(x.shape[0]):
        z += x[row] * y[row]

    return z

def naive_matrix_vector_dot(x, y):
    assert len(x.shape) == 2
    assert len(y.shape) == 1
    assert x.shape[1] == y.shape[0]

    z = np.zeros(x.shape[0])

    for row in range(x.shape[0]):
        for col in range(x.shape[1]):
            z[row] += x[row, col] * y[col]

    return z

def naive_matrix_dot(x, y):
    assert len(x.shape) == 2
    assert len(y.shape) == 2
    assert x.shape[1] == y.shape[0]

    z = np.zeros((x.shape[0], y.shape[1]))

    for row in range(x.shape[0]):
        for col in range(y.shape[1]):
            row_x = x[row, :]
            column_y = y[:, col]
            z[row, col] = naive_vector_dot(row_x, column_y)

    return z

# 2D Numpy tensor
print('*** Naive ReLU ***')
input_x_2d_tensor = np.array(
    [
        [5, 78, 2, 34, 0],
        [6, 79, 3, 35, 1],
        [7, 80, 4, 36, 2]
    ]
)
print('Input 2D tensor shape: ', input_x_2d_tensor.shape)
print('Input 2D tensor ndim: ', input_x_2d_tensor.ndim)
output_2d_tensor = naive_relu(input_x_2d_tensor)
print('ReLU output: ', output_2d_tensor)
print('*** ---------- ***')
print()
print('*** Naive ADD operation ***')
input_y_2d_tensor = np.array(
    [
        [1, 7, 22, 31, 0],
        [3, 9, 33, 35, 1],
        [4, 8, 44, 36, 2]
    ]
)
output_2d_tensor = naive_add(input_x_2d_tensor, input_y_2d_tensor)
print('Naive ADD output: ', output_2d_tensor)
print('*** ---------- ***')
print()
print('*** Naive ADD operation - Matrix and Vector ***')
input_x_2d_tensor = np.array(
    [
        [1, 2, 3, 4],
        [5, 6, 7, 8]
    ]
)
input_y_2d_tensor = np.array([9, 10, 11, 12])
output_2d_tensor = naive_add_matrix_and_vector(input_x_2d_tensor, input_y_2d_tensor)
print('Naive ADD operation - Matrix and Vector output: ', output_2d_tensor)
print('*** ---------- ***')
print()
print('*** Naive DOT operation - Vector ***')
x_vector = np.array([1, 2, 3])
y_vector = np.array([4, 5, 6])
dot_product = naive_vector_dot(x_vector, y_vector)
print('Naive DOT output: ', dot_product)
print('*** ---------- ***')
print()
print('*** Naive DOT operation - Matrix and Vector ***')
x_matrix = np.array(
    [
        [1, 2, 3, 4],
        [5, 6, 7, 8]
    ]
)
y_vector = np.array([1, 2, 1, 2])
dot_product = naive_matrix_vector_dot(x_matrix, y_vector)
print('Naive DOT operation - Matrix and Vector output: ', dot_product)
print('*** ---------- ***')
print()
print('*** Naive DOT operation - Matrix ***')
x_matrix = np.array(
    [
        [1, 2, 3, 4],
        [5, 6, 7, 8]
    ]
)
y_matrix = np.array(
    [
        [1, 1, 1, 1],
        [0, 0, 0, 0],
        [1, 1, 1, 1],
        [0, 0, 0, 0]
    ]
)
dot_product = naive_matrix_dot(x_matrix, y_matrix)
print('Naive DOT operation - Matrix: ', dot_product)
print('*** ---------- ***')
print()


from PIL import Image as I
import numpy as np

from skimage.util import random_noise as imnoise
from scipy.misc import imresize

def whiten(img):
    """
	Function that takes as input one grayscale image array and returns a whitenend image. It applies
	the following transform:

	.. math::

		I = \frac{I - mean(I)}{var(I)} + min(\frac{I - mean(I)}{var(I)})

	Args:
		img: ``numpy ndarray`` of a two-dimensional image  of the form
									< height, width >

	Returns:
		numpy ndarray: gray
	"""
    img = (img - img.mean()) / img.std()
    img = img + img.min()
    return img

def normalize(img):
    """
	Function that takes as input one grayscale image array and returns a normalized image. It applies
	the following transform:

	.. math::

		I = \frac{I}{max(I)}

	Args:
		img: ``numpy ndarray`` of a two-dimensional image  of the form
									< height, width >

	Returns:
		numpy ndarray: gray
	"""
    return img / img.max()

def imread(infile):
    """
    Takes a string as input and outputs a image.

    Args:
        infile: input file string

    Returns:
        numpy.ndarray: image
    """
    img = I.open(infile)
    img.load()
    data = np.asarray(img, dtype="uint8")
    return data

def imwrite(img, outfile):
    """
    Takes an image and string as input and and writes the image down.

    Args:
        img: image numpy.ndarray
        outfile: output file string
    """
    if not img.dtype == 'uint8':
        np.clip(img, a_min=0, a_max=255, out=img)
        img = np.asarray(img, dtype='uint8')
    img = I.fromarray(img)
    img.save(outfile)

def imshow(img, window='image'):
    """
    Takes an image as input and displays it.

    Args:
        img: image numpy.ndarray
        window: (optional) title to name the window by.
    """
    I.fromarray(img).show(title=window)

def rgb2gray(rgb):
    """
	Function that takes as input one rgb image array and returns a grayscale image. It applies
	the following transform:

	.. math::

		I_{gray} = 0.2989I_r + 0.5870I_g + 0.1140I_b

	Args:
		rgb: ``numpy ndarray`` of a four-dimensional image batch of the form
									< (number of images??), height, width, channels>

	Returns:
		numpy ndarray: gray
	"""
    if len(rgb.shape) == 4:
        r, g, b = rgb[:, :, :, 0], rgb[:, :, :, 1], rgb[:, :, :, 2]
    elif len(rgb.shape) == 3:
        r, g, b = rgb[:, :, 0], rgb[:, :, 1], rgb[:, :, 2]
    gray = 0.2989 * r + 0.5870 * g + 0.1140 * b
    return gray

if __name__ == '__main__':
    pass


import os
import inspect
import shutil
import tensorflow as tf
import numpy as np

# Calculate LOG_DIR according to current file
CUR_FILE = inspect.getfile(inspect.currentframe())
LOG_DIR = os.path.join(
os.path.dirname(os.path.abspath(CUR_FILE)), 'logs',
os.path.splitext(os.path.basename(CUR_FILE))[0])

# Check is the LOG_DIR empty. If not ask for clean.
def clean_logs(logdir):
    if logdir == None or len(logdir) < 4:
        return
    if os.path.exists(logdir) and len(os.listdir(logdir)) > 0:
        answer = input('Log Folder: ' + logdir + ' is not empty. Clean it? [y/N]')
        if answer in ['Y', 'y']:
            shutil.rmtree(logdir)
clean_logs(LOG_DIR)

print('TensorFlow Version: ' + tf.__version__)

# Create input data using NumPy.
# y = x * 0.3 + 0.5 + noise
data_x = np.random.rand(100).astype(np.float32)
data_noise = np.random.normal(scale=0.01, size=len(data_x))
data_y = data_x * 0.3 + 0.5 + data_noise

# Build TensorFlow learning graph

# Linear model
# y = w * x + b
w = tf.Variable(tf.random_uniform([], 0.0, 1.0), name="weight")
b = tf.Variable(tf.zeros([]), name="bias")
y = w * data_x + b

# Training graph
# We need an Error function to evaluate learning mistake
loss = tf.reduce_mean(tf.square(y - data_y))
# We need a way to optimize learning result
learning_rate = 0.5
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
# We want a minimized loss
train = optimizer.minimize(loss)

# Define summary
with tf.name_scope('summaries'):
    tf.summary.scalar('weight', w)
    tf.summary.scalar('bias', b)
    tf.summary.scalar('loss', loss)
    tf.summary.histogram('weight', w)
    tf.summary.histogram('bias', b)

# Merge all summaries for writer later
merged_summary = tf.summary.merge_all()

# Define Initializer for Variables. Important!!!
init = tf.global_variables_initializer()

with tf.Session() as sess:
    writer = tf.summary.FileWriter(LOG_DIR, sess.graph)
    # Execute Initializer before training. Important!!!
    sess.run(init)

    l_, w_, b_ = sess.run([loss, w, b])
    print('step init, loss: {}, w: {}, b: {}'.format(l_, w_, b_))

    for step in range(201):
        _, summary = sess.run([train, merged_summary])
        writer.add_summary(summary, global_step=step)
        if step % 20 == 0:
            l_, w_, b_ = sess.run([loss, w, b])
            print('step {}, loss: {}, w: {}, b: {}'.format(step, l_, w_, b_))

    print("final results: w: {}, b: {}".format(sess.run(w), sess.run(b)))
    writer.close()

# Automatically show TensorBoard if needed.
def show_tensorboard(auto_show=False):
    cmd = 'tensorboard --logdir=' + LOG_DIR
    if auto_show:
        answer = input('Show tensorboard? [Y/n]')
        if not answer in ['N', 'n']:
            os.system(cmd)
    else:
        print("\nRun this command to see logs:\n" + cmd)
show_tensorboard(True)


# %% [markdown]
# # Linear regression with non-linear link between data and target
#
# In the previous exercise, you were asked to train a linear regression model
# on a dataset where the matrix `data` and the vector `target` do not have a
# linear link.
#
# In this notebook, we show that even if the parametrization of linear models
# is not natively adapated to data with non-linearity, it is still possible
# to make linear model more flexible and expressive.
#
# To illustrate these concepts, we will reuse the same dataset generated in the
# previous exercise.

# %%
import numpy as np

rng = np.random.RandomState(0)

n_sample = 100
data_max, data_min = 1.4, -1.4
len_data = (data_max - data_min)
# sort the data to make plotting easier later
data = np.sort(rng.rand(n_sample) * len_data - len_data / 2)
noise = rng.randn(n_sample) * .3
target = data ** 3 - 0.5 * data ** 2 + noise

# %% [markdown]
# ```{note}
# To ease the plotting, we will create a Pandas dataframe containing the data
# and target
# ```

# %%
import pandas as pd
full_data = pd.DataFrame({"data": data, "target": target})

# %%
import seaborn as sns

_ = sns.scatterplot(data=full_data, x="data", y="target")
# %% [markdown]
# We will highlight the limitations of fitting a linear regression model as
# done in the previous exercise.
#
# ```{warning}
# In scikit-learn, by convention `data` (also called `X` in the scikit-learn
# documentation) should be a 2D matrix of shape `(n_samples, n_features)`.
# If `data` is a 1D vector, you need to reshape it into a matrix with a
# single column if the vector represents a feature or a single row if the
# vector represents a sample.
# ```

# %%
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

linear_regression = LinearRegression()
# X should be 2D for sklearn
data_2d = data.reshape((-1, 1))
linear_regression.fit(data_2d, target)
target_predicted = linear_regression.predict(data_2d)

# %%
mse = mean_squared_error(target, target_predicted)

# %%
ax = sns.scatterplot(data=full_data, x="data", y="target")
ax.plot(data, target_predicted, color="tab:orange")
_ = ax.set_title(f"Mean squared error = {mse:.2f}")

# %% [markdown]
# Here the coefficients learnt by `LinearRegression` is the best "straight
# line" that fits the data. We can inspect the coefficients using the
# attributes of the model learnt as follows:

# %%
print(f"weight: {linear_regression.coef_[0]:.2f}, "
      f"intercept: {linear_regression.intercept_:.2f}")

# %% [markdown]
# It is important to note that the model learnt will not be able to handle the
# non-linear relationship between `data` and `target` since linear models
# assume the relationship between `data` and `target` to be linear.
#
# Indeed, there are 3 possibilities to solve this issue:
#
# 1. choose a model that natively can deal with non-linearity,
# 2. "augment" features by including expert knowledge which can be used by
#    the model, or
# 3. use a "kernel" to have a locally-based decision function instead of a
#    global linear decision function.
#
# Let's illustrate quickly the first point by using a decision tree regressor
# which can natively handle non-linearity.

# %%
from sklearn.tree import DecisionTreeRegressor

tree = DecisionTreeRegressor(max_depth=3).fit(data_2d, target)
target_predicted = tree.predict(data_2d)
mse = mean_squared_error(target, target_predicted)

# %%
ax = sns.scatterplot(data=full_data, x="data", y="target")
ax.plot(data, target_predicted, color="tab:orange")
_ = ax.set_title(f"Mean squared error = {mse:.2f}")

# %% [markdown]
# Instead of having a model which can natively deal with non-linearity, we
# could also modify our data: we could create new features, derived from the
# original features, using some expert knowledge. In this example, we know that
# we have a cubic and squared relationship between `data` and `target` (because
# we generated the data). Indeed, we could create two new features (`data ** 2`
# and `data ** 3`) using this information.

# %%
data_augmented = np.concatenate([data_2d, data_2d ** 2, data_2d ** 3], axis=1)

linear_regression.fit(data_augmented, target)
target_predicted = linear_regression.predict(data_augmented)
mse = mean_squared_error(target, target_predicted)

# %%
ax = sns.scatterplot(data=full_data, x="data", y="target")
ax.plot(data, target_predicted, color="tab:orange")
_ = ax.set_title(f"Mean squared error = {mse:.2f}")

# %% [markdown]
# We can see that even with a linear model, we can overcome the linearity
# limitation of the model by adding the non-linear component into the design of
# additional features. Here, we created new features by knowing the way the
# target was generated. In practice, this is usually not the case.
#
# Instead, one is usually creating interaction between features (e.g. $x_1
# \times x_2$) with different orders (e.g. $x_1, x_1^2, x_1^3$), at the risk of
# creating a model with too much flexibility where the polynomial terms allows
# to fit noise in the dataset and thus lead overfit. In scikit-learn, the
# `PolynomialFeatures` is a transformer to create such feature interactions
# which we could have used instead of manually creating new features.
#
# To demonstrate `PolynomialFeatures`, we are going to use a scikit-learn
# pipeline which will first create the new features and then fit the model.
# We come back to scikit-learn pipelines and discuss them in more detail later.

# %%
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures

model = make_pipeline(PolynomialFeatures(degree=3),
                      LinearRegression())
model.fit(data_2d, target)
target_predicted = model.predict(data_2d)
mse = mean_squared_error(target, target_predicted)

# %%
ax = sns.scatterplot(data=full_data, x="data", y="target")
ax.plot(data, target_predicted, color="tab:orange")
_ = ax.set_title(f"Mean squared error = {mse:.2f}")

# %% [markdown]
# Thus, we saw that `PolynomialFeatures` is actually doing the same
# operation that we did manually above.
#
# The last possibility is to make a linear model more expressive is to use a
# "kernel". Instead of learning a weight per feature as we previously
# emphasized, a weight will be assign by sample instead. However, not all
# samples will be used. This is the base of the support vector machine
# algorithm.

# %%
from sklearn.svm import SVR

svr = SVR(kernel="linear")
svr.fit(data_2d, target)
target_predicted = svr.predict(data_2d)
mse = mean_squared_error(target, target_predicted)

# %%
ax = sns.scatterplot(data=full_data, x="data", y="target")
ax.plot(data, target_predicted, color="tab:orange")
_ = ax.set_title(f"Mean squared error = {mse:.2f}")

# %% [markdown]
# The algorithm can be modified so that it can use non-linear kernel. Then,
# it will compute interaction between samples using this non-linear
# interaction.

# %%
svr = SVR(kernel="poly", degree=3)
svr.fit(data_2d, target)
target_predicted = svr.predict(data_2d)
mse = mean_squared_error(target, target_predicted)

# %%
ax = sns.scatterplot(data=full_data, x="data", y="target")
ax.plot(data, target_predicted, color="tab:orange")
_ = ax.set_title(f"Mean squared error = {mse:.2f}")

# %% [markdown]
# A method supporting kernel, as SVM, allows to efficiently create a non-linear
# model.


import numpy as np
import matplotlib.pyplot as plt

def feval(funcName, *args):
    return eval(funcName)(*args)

def RungeKutta4thOrder(func, yinit, xspan, h):
    m = len(yinit)
    n = int((xspan[-1] - xspan[0]) / h)

    x = xspan[0]
    y = yinit

    xsol = np.empty((0))
    xsol = np.append(xsol, x)

    ysol = np.empty((0))
    ysol = np.append(ysol, y)

    for i in range(n):
        k1 = feval(func, x, y)

        yp2 = y + k1*(h/2)

        k2 = feval(func, x+h/2, yp2)

        yp3 = y + k2*(h/2)

        k3 = feval(func, x+h/2, yp3)

        yp4 = y + k3*h

        k4 = feval(func, x+h, yp4)

        for j in range(m):
            y[j] = y[j] + (h/6)*(k1[j] + 2*k2[j] + 2*k3[j] + k4[j])

        x = x + h
        xsol = np.append(xsol, x)

        for r in range(len(y)):
            ysol = np.append(ysol, y[r])  # Save all new y's

    return [xsol, ysol]

def ABM4thOrder(func, yinit, xspan, h):

    m = len(yinit)

    dx = int((xspan[-1] - xspan[0]) / h)

    xrk = [xspan[0] + k * h for k in range(dx + 1)]

    [xx, yy] = RungeKutta4thOrder(func, yinit, (xrk[0], xrk[3]), h)

    x = xx
    xsol = np.empty(0)
    xsol = np.append(xsol, x)

    y = yy
    yn = np.array([yy[0]])
    ysol = np.empty(0)
    ysol = np.append(ysol, y)

    for i in range(3, dx):
        x00 = x[i]; x11 = x[i-1]; x22 = x[i-2]; x33 = x[i-3]; xpp = x[i]+h

        y00 = np.array([y[i]])
        y11 = np.array([y[i - 1]])
        y22 = np.array([y[i - 2]])
        y33 = np.array([y[i - 3]])

        y0prime = feval(func, x00, y00)
        y1prime = feval(func, x11, y11)
        y2prime = feval(func, x22, y22)
        y3prime = feval(func, x33, y33)

        ypredictor = y00 + (h/24)*(55*y0prime - 59*y1prime + 37*y2prime - 9*y3prime)
        ypp = feval(func, xpp, ypredictor)

        for j in range(m):
            yn[j] = y00[j] + (h/24)*(9*ypp[j] + 19*y0prime[j] - 5*y1prime[j] + y2prime[j])

        xs = x[i] + h
        xsol = np.append(xsol, xs)

        x = xsol

        for r in range(len(yn)):
            ysol = np.append(ysol, yn)

        y = ysol

    return [xsol, ysol]

def myFunc(x, y):
    dy = np.zeros((len(y)))
    dy[0] = (1/2)*(x - y[0])
    return dy

h = 0.2
xspan = np.array([0.0, 3.0])
yinit = np.array([1.0])

[ts, ys] = ABM4thOrder('myFunc', yinit, xspan, h)

dt = int((xspan[-1]-xspan[0])/h)
t = [xspan[0]+i*h for i in range(dt+1)]
yexact = []
for i in range(dt+1):
    ye = t[i] - 2 + 3*np.exp(-0.5*t[i])
    yexact.append(ye)

diff = ys - yexact
print("Maximum difference =", np.max(abs(diff)))
plt.plot(ts, ys, 'r')
plt.plot(t, yexact, 'b')
plt.xlim(xspan[0], xspan[1])
plt.legend(["ABM 4th Order ", "Exact solution"], loc=2)
plt.xlabel('x', fontsize=17)
plt.ylabel('y', fontsize=17)
plt.tight_layout()
plt.show()


from __future__ import annotations

import math

import numpy
import numpy as np
from numpy.linalg import inv, det, slogdet

class UnivariateGaussian:
    """
    Class for univariate Gaussian Distribution Estimator
    """
    def __init__(self, biased_var: bool = False) -> UnivariateGaussian:
        """
        Estimator for univariate Gaussian mean and variance parameters

        Parameters
        ----------
        biased_var : bool, default=False
            Should fitted estimator of variance be a biased or unbiased estimator

        Attributes
        ----------
        fitted_ : bool
            Initialized as false indicating current estimator instance has not been fitted.
            To be set as True in `UnivariateGaussian.fit` function.

        mu_: float
            Estimated expectation initialized as None. To be set in `UnivariateGaussian.fit`
            function.

        var_: float
            Estimated variance initialized as None. To be set in `UnivariateGaussian.fit`
            function.
        """
        self.biased_ = biased_var
        self.fitted_, self.mu_, self.var_ = False, None, None

    def fit(self, X: np.ndarray) -> UnivariateGaussian:
        """
        Estimate Gaussian expectation and variance from given samples

        Parameters
        ----------
        X: ndarray of shape (n_samples, )
            Training data

        Returns
        -------
        self : returns an instance of self.

        Notes
        -----
        Sets `self.mu_`, `self.var_` attributes according to calculated estimation (where
        estimator is either biased or unbiased). Then sets `self.fitted_` attribute to `True`
        """
        self.mu_ = np.mean(X)

        # Now calculate the expected variance based on whether the sample is biased or not
        the_divisor = X.size if self.biased_ == True else X.size - 1
        val_minus_mu_squared = lambda t: (t - self.mu_)**2
        sum_of_values_minus_mu_squared = 0
        for val in X:
            sum_of_values_minus_mu_squared += val_minus_mu_squared(val)
        self.var_ = (1/the_divisor)*sum_of_values_minus_mu_squared

        self.fitted_ = True
        return self

    def pdf(self, X: np.ndarray) -> np.ndarray:
        """
        Calculate PDF of observations under Gaussian model with fitted estimators

        Parameters
        ----------
        X: ndarray of shape (n_samples, )
            Samples to calculate PDF for

        Returns
        -------
        pdfs: ndarray of shape (n_samples, )
            Calculated values of given samples for PDF function of N(mu_, var_)

        Raises
        ------
        ValueError: In case function was called prior fitting the model
        """
        if not self.fitted_:
            raise ValueError("Estimator must first be fitted before calling `pdf` function")

        calc_pdf = lambda t: (1/math.sqrt(2*math.pi*self.var_))*math.exp((-1/2*self.var_)*((t - self.mu_)**2))

        # Apply the lambda function over all the sample values
        map_pdf = np.vectorize(calc_pdf)
        return map_pdf(X)

    @staticmethod
    def log_likelihood(mu: float, sigma: float, X: np.ndarray) -> float:
        """
        Calculate the log-likelihood of the data under a specified Gaussian model

        Parameters
        ----------
        mu : float
            Expectation of Gaussian
        sigma : float
            Variance of Gaussian
        X : ndarray of shape (n_samples, )
            Samples to calculate log-likelihood with

        Returns
        -------
        log_likelihood: float
            log-likelihood calculated
        """
        sum_of_vars_minus_mu_squared = 0
        for var in X:
            sum_of_vars_minus_mu_squared += (var-mu)**2

        return ((-X.size)/2)*math.log(2*math.pi*sigma)-(1/2)*sum_of_vars_minus_mu_squared

class MultivariateGaussian:
    """
    Class for multivariate Gaussian Distribution Estimator
    """
    def __init__(self):
        """
        Initialize an instance of multivariate Gaussian estimator

        Attributes
        ----------
        fitted_ : bool
            Initialized as false indicating current estimator instance has not been fitted.
            To be set as True in `MultivariateGaussian.fit` function.

        mu_: ndarray of shape (n_features,)
            Estimated expectation initialized as None. To be set in `MultivariateGaussian.fit`
            function.

        cov_: ndarray of shape (n_features, n_features)
            Estimated covariance initialized as None. To be set in `MultivariateGaussian.fit`
            function.
        """
        self.mu_, self.cov_ = None, None
        self.fitted_ = False

    def fit(self, X: np.ndarray) -> MultivariateGaussian:
        """
        Estimate Gaussian expectation and covariance from given samples

        Parameters
        ----------
        X: ndarray of shape (n_samples, n_features)
            Training data

        Returns
        -------
        self : returns an instance of self

        Notes
        -----
        Sets `self.mu_`, `self.cov_` attributes according to calculated estimation.
        Then sets `self.fitted_` attribute to `True`
        """

        # Calculating the expectation vector and covariance matrix
        self.mu_ = np.mean(X,axis=0)
        self.cov_ = np.cov(X, rowvar=False)

        self.fitted_ = True
        return self

    def pdf(self, X: np.ndarray):
        """
        Calculate PDF of observations under Gaussian model with fitted estimators

        Parameters
        ----------
        X: ndarray of shape (n_samples, n_features)
            Samples to calculate PDF for

        Returns
        -------
        pdfs: ndarray of shape (n_samples, )
            Calculated values of given samples for PDF function of N(mu_, cov_)

        Raises
        ------
        ValueError: In case function was called prior fitting the model
        """
        if not self.fitted_:
            raise ValueError("Estimator must first be fitted before calling `pdf` function")

        # Calculates the pdf for a multivariate sample vector
        num_of_features = X[0].size
        inverse_cov = np.linalg.inv(self.cov_)

        first_part = (1/math.sqrt(((2*math.pi)**num_of_features)*np.linalg.det(self.cov_)))
        calc_pdf = lambda t: first_part*math.exp((t-self.mu_) @ inverse_cov * (t-self.mu_))
        # Map this function over input array
        map_pdf = np.vectorize(calc_pdf)
        return map_pdf(X)

    @staticmethod
    def log_likelihood(mu: np.ndarray, cov: np.ndarray, X: np.ndarray) -> float:
        """
        Calculate the log-likelihood of the data under a specified Gaussian model

        Parameters
        ----------
        mu : ndarray of shape (n_features,)
            Expectation of Gaussian
        cov : ndarray of shape (n_features, n_features)
            covariance matrix of Gaussian
        X : ndarray of shape (n_samples, n_features)
            Samples to calculate log-likelihood with

        Returns
        -------
        log_likelihood: float
            log-likelihood calculated over all input data and under given parameters of Gaussian
        """
        n,d = X.shape
        inverse_cov = np.linalg.inv(cov)
        bar = X - np.full((n,d), mu)
        the_sum = np.sum(bar @ inverse_cov * bar)

        return (-n)*(d/2)*math.log(2*math.pi) - (n/2)*math.log(np.linalg.det(cov)) - (1/2)*the_sum


#  Written by 2018 Yingfeng Yu < yuyingfeng (at) cueb.edu.cn >
#  Capital University of Economics and Business(CUEB),Beijing, China
#  School of Finance, Dept. of International Finance
#  Quantitative Finance and MacroEconomics Group(QFnME) teaching materials

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#==========================================================================

import numpy as np
import pandas as pd
import scipy.stats as ss
import matplotlib.pyplot as plt
from scipy.optimize import fmin_slsqp

def gjr_garch_likelihood(parameters, data, sigma2, out = None):
        mu = parameters[0]
        omega = parameters[1]
        alpha = parameters[2]
        gamma = parameters[3]
        beta = parameters[4]

        T = np.size(data,0)
        #print('What is T=',T)
        eps = data - mu
        # Data and Sigma2 are T by 1 vectors
        for t in range(1,T):
            sigma2[t]=(omega+alpha*eps[t-1]**2+gamma*eps[t-1]**2 * (eps[t-1]<0)+beta*sigma2[t-1])

        logliks = 0.5*(np.log(2*np.pi)+np.log(sigma2)+eps**2/sigma2)
        loglik = np.sum(logliks)

        if out is None:
            return loglik
        else:
            return loglik, logliks, np.copy(sigma2)

def gjr_constraint(parameters, data, sigma2, out=None):
        alpha = parameters[2]
        gamma = parameters[3]
        beta = parameters[4]
        return np.array([1-alpha-gamma/2-beta])

def garch_likelihood(parameters, data, sigma2, out = None):
        mu = parameters[0]
        alpha = parameters[1]
        beta = parameters[2]

        T = np.size(data,0)
        eps = data - mu
        # Data and Sigma2 are assumed as T by 1 vectors
        for t in range(1,T):
            sigma2[t]=(alpha*eps[t-1]**2+beta*sigma2[t-1])

        logliks = 0.5*(np.log(2*np.pi)+np.log(sigma2)+eps**2/sigma2)
        loglik = np.sum(logliks)

        if out is None:
            return loglik
        else:
            return loglik, logliks, np.copy(sigma2)

def garch_constraint(parameters, data, sigma2, out=None):
        alpha = parameters[1]
        beta = parameters[2]
        return np.array([1-alpha-beta])


from __future__ import division

import numpy as np
import scipy
import scipy.signal
import scipy.optimize
import scipy.special
from scipy.special import factorial

__all__ = ['Morlet',]

class Morlet(object):
    """ Definition of Morlet wavelet function

    :param int w0:
        Nondimensional frequency constant. If this is
        set too low then the wavelet does not sample very well: a
        value over 5 should be ok; Terrence and Compo set it to 6.
    """
    def __init__(self, w0=6):

        self.w0 = w0
        if w0 == 6:
            # value of C_d from TC98
            self.C_d = 0.776

    def __call__(self, *args, **kwargs):
        return self.time(*args, **kwargs)

    def time(self, t, s=1.0, complete=False):
        """
        Complex Morlet wavelet, centred at zero.

        :param float t:
            Time. If s is not specified, this can be used as the
            non-dimensional time t/s.
        :param float s:
            Scaling factor. Default is 1.
        :param bool complete:
            Whether to use the complete or the standard version.

        :return:
            Value of the Morlet wavelet at the given time

        The standard version:

        .. math::

            \pi^{-0.25} \, \\text{exp}(iwx) \, \\text{exp}(-0.5(x^2))

        This commonly used wavelet is often referred to simply as the
        Morlet wavelet.  Note that this simplified version can cause
        admissibility problems at low values of `w`.

        The complete version:

        .. math::

            \pi^{-0.25} \, ( \\text{exp}(iwx) - \\text{exp}(-0.5(w^2))) \, \\text{exp}(-0.5(x^2))

        The complete version of the Morlet wavelet, with a correction
        term to improve admissibility. For `w` greater than 5, the
        correction term is negligible.

        Note that the energy of the return wavelet is not normalised
        according to `s`.

        The fundamental frequency of this wavelet in Hz is given
        by ``f = 2*s*w*r / M`` where r is the sampling rate.
        """
        w = self.w0

        x = t / s

        output = np.exp(1j * w * x)

        if complete:
            output -= np.exp(-0.5 * (w ** 2))

        output *= np.exp(-0.5 * (x ** 2)) * np.pi ** (-0.25)

        return output

    # Fourier wavelengths
    def fourier_period(self, s):
        """Equivalent Fourier period of Morlet"""
        return 4 * np.pi * s / (self.w0 + (2 + self.w0 ** 2) ** .5)

    def scale_from_period(self, period) -> np.array:
        """Compute the scale from the fourier period.
        """
        # Solve 4 * np.pi * scale / (w0 + (2 + w0 ** 2) ** .5)
        #  for s to obtain this formula
        coeff = np.sqrt(self.w0 * self.w0 + 2)
        return (period * (coeff + self.w0)) / (4. * np.pi)

    # Frequency representation
    def frequency(self, w, s=1.0):
        """Frequency representation of Morlet.

        :param float w:
            Angular frequency. If `s` is not specified, i.e. set to 1,
            this can be used as the non-dimensional angular
            frequency w * s.
        :param float s:
            Scaling factor. Default is 1.

        :return:
            Value of the Morlet wavelet at the given frequency
        """
        x = w * s
        # Heaviside mock
        Hw = np.array(w)
        Hw[w <= 0] = 0
        Hw[w > 0] = 1
        return np.pi ** -.25 * Hw * np.exp((-(x - self.w0) ** 2) / 2)

    def coi(self, s):
        """The e folding time for the autocorrelation of wavelet
        power at each scale, i.e. the timescale over which an edge
        effect decays by a factor of 1/e^2.

        This can be worked out analytically by solving

        .. math::

            |Y_0(T)|^2 / |Y_0(0)|^2 = 1 / e^2
        """
        return 2 ** .5 * s


import numpy as np

from scipy import stats
from scipy.linalg import toeplitz

def boot_sd_residuals(data, n_samples):
    """
    Returns bootstrapped standard deviation of the residuals.
    """
    sample_num = 1
    sd_residuals_array = np.empty([0, 1])

    while sample_num <= n_samples:
        sample = np.random.choice(data, len(data))
        residuals = np.diff(sample)
        residuals_sd = np.std(residuals)
        sd_residuals_array = np.vstack((sd_residuals_array, residuals_sd))
        sample_num += 1

    bootstrap_sd_residuals = np.mean(sd_residuals_array)

    return bootstrap_sd_residuals

def acf_corr(data, max_lags="default", ci=True, level=0.95):
    """
    Returns autocorrelation coefficients and their bounds
    of length max_lags.
    """
    n = len(data)
    mean = np.mean(data)
    c0 = np.sum((data - mean) ** 2) / float(n)

    if max_lags is "default":
        max_lags = int(10 * np.log10(n))
    else:
        max_lags = int(max_lags)

    def corr(h):
        acf_coeff = np.sum(((data[: n - h] - mean) * (data[h:] - mean))) / n / c0
        return acf_coeff

    t_crit = stats.t.ppf(q=level, df=(n - 3))
    acf_coeff_lb = np.negative(t_crit) / np.sqrt(n)
    acf_coeff_ub = t_crit / np.sqrt(n)

    acf_coeffs = np.empty([0, 3])
    for k in np.arange(max_lags):
        acf_coeff = corr(k)
        if ci is False:
            acf_coeffs = np.vstack((acf_coeffs, (np.nan, acf_coeff, np.nan)))
        else:
            acf_coeffs = np.vstack(
                (acf_coeffs, (acf_coeff_lb, acf_coeff, acf_coeff_ub))
            )

    dtypes = np.dtype(
        [("lower", data.dtype), ("point", data.dtype), ("upper", data.dtype)]
    )
    coeffs = np.empty(len(acf_coeffs), dtype=dtypes)
    coeffs["lower"] = acf_coeffs[:, 0]
    coeffs["point"] = acf_coeffs[:, 1]
    coeffs["upper"] = acf_coeffs[:, 2]

    return coeffs

def pacf_ols(data, max_lags="default", ci=True, level=0.95):
    """
    Returns partial autocorrelation coefficients estimated via OLS along with
    their bounds of length max_lags.
    """
    n = len(data)
    x0 = data

    if max_lags is "default":
        max_lags = int(10 * np.log10(n))
    else:
        max_lags = int(max_lags)

    xlags = np.ones((n, max_lags))
    for i in range(1, max_lags):
        xlags[:, i] = np.roll(data, i)

    xlags[np.triu_indices(xlags.shape[1], 1)] = 0

    t_crit = stats.t.ppf(q=level, df=(n - 3))
    pacf_coeff_lb = np.negative(t_crit) / np.sqrt(n)
    pacf_coeff_ub = t_crit / np.sqrt(n)

    pacf_coeffs = np.empty([0, 3])
    for k in range(1, max_lags + 1):
        pacf_coeff = np.linalg.lstsq(xlags[k:, : k + 1], x0[k:])[0][-1]
        if ci is False:
            pacf_coeffs = np.vstack((pacf_coeffs, (np.nan, pacf_coeff, np.nan)))
        else:
            pacf_coeffs = np.vstack(
                (pacf_coeffs, (pacf_coeff_lb, pacf_coeff, pacf_coeff_ub))
            )

    dtypes = np.dtype(
        [("lower", data.dtype), ("point", data.dtype), ("upper", data.dtype)]
    )
    coeffs = np.empty(len(pacf_coeffs), dtype=dtypes)
    coeffs["lower"] = pacf_coeffs[:, 0]
    coeffs["point"] = pacf_coeffs[:, 1]
    coeffs["upper"] = pacf_coeffs[:, 2]

    return coeffs

def yule_walker(data, order, method="unbiased", demean=True):
    """
    Returns partial autocorrelation coefficients obtained via Yule-Walker
    equations. Code mostly from statsmodels.
    """
    n = len(data)

    if demean is True:
        data = data - np.mean(data)
    else:
        pass

    if method == "unbiased":

        def denom(k):
            return n - k

    else:

        def denom(k):
            return n

    r = np.zeros(order + 1)
    r[0] = np.sum(data ** 2) / denom(0)

    for k in range(1, order + 1):
        r[k] = np.sum(data[0:-k] * data[k:]) / denom(k)

    R = toeplitz(r[:-1])
    rho = np.linalg.solve(R, r[1:])

    return rho

def pacf_yule_walker(data, max_lags="default", method="unbiased", ci=True, level=0.95):
    """
    Returns autocorrelation coefficients estimated via Yule-Walker equations
    and their bounds of length max_lags.
    """
    n = len(data)

    if max_lags is "default":
        max_lags = int(10 * np.log10(n))
    else:
        max_lags = int(max_lags)

    t_crit = stats.t.ppf(q=level, df=(n - 3))
    pacf_coeff_lb = np.negative(t_crit) / np.sqrt(n)
    pacf_coeff_ub = t_crit / np.sqrt(n)

    pacf_coeffs = np.empty([0, 3])
    for k in range(1, max_lags + 1):
        pacf_coeff = yule_walker(data, order=k, method=method, demean=True)[-1]
        if ci is False:
            pacf_coeffs = np.vstack((pacf_coeffs, (np.nan, pacf_coeff, np.nan)))
        else:
            pacf_coeffs = np.vstack(
                (pacf_coeffs, (pacf_coeff_lb, pacf_coeff, pacf_coeff_ub))
            )

    dtypes = np.dtype(
        [("lower", data.dtype), ("point", data.dtype), ("upper", data.dtype)]
    )
    coeffs = np.empty(len(pacf_coeffs), dtype=dtypes)
    coeffs["lower"] = pacf_coeffs[:, 0]
    coeffs["point"] = pacf_coeffs[:, 1]
    coeffs["upper"] = pacf_coeffs[:, 2]

    return coeffs

def trend(data, order, center=True):
    """
    Returns array consisting of series trend.
    """
    even_order = order % 2 == 0
    trends = np.empty([0, 1])
    k = len(data)
    a = int(order / 2)

    if center is False:
        if even_order is True:
            b = int(a - 1)
        else:
            b = a
        trends = np.convolve(
            data.reshape((k,)), np.ones((order,)) / order, mode="valid"
        )
        trends = np.pad(trends, (b, a), "constant", constant_values=(np.nan,)).reshape(
            k, 1
        )

    else:
        j = order
        for i in np.arange(k):
            multiplier = 1 / order
            if even_order is True:
                w1 = multiplier * np.sum(data[i:j])
                w2 = multiplier * np.sum(data[i + 1 : j + 1])
                trend = np.mean((w1, w2))
                trends = np.vstack((trends, trend))
            else:
                b = a
                trends = np.convolve(
                    data.reshape((k,)), np.ones((order,)) / order, mode="valid"
                )
                trends = np.pad(
                    trends, (b, a), "constant", constant_values=(np.nan,)
                ).reshape(k, 1)

            j += 1

        pad = int(order / 2)

        if order % 2 == 0:
            trends = np.roll(trends, pad)
        else:
            pass

        trends[:pad,] = np.nan
        trends[-pad:,] = np.nan

    return trends[:, 0]

def detrend(data, order, center=True, model="additive"):
    """
    Returns array of detrended series.
    """
    k = len(data)

    if model == "additive":
        data_detrended = data - trend(data, order, center)
    elif model == "multiplicative":
        data_detrended = data / trend(data, order, center)
    else:
        raise ValueError("Model must be additive or multiplicative.")

    return data_detrended

def seasonality(data, order, center=True, model="additive", median=False):
    """
    Returns array of series seasonality.
    """
    j = len(data)
    k = int(j / order)

    if j < (order * 2):
        raise ValueError("Series has no or less than 2 periods.")
    else:
        pass

    de_series = detrend(data, order, center, model)

    if median is False:
        arr1 = np.nanmean(np.resize(de_series, (k, order)), axis=0)
        arr2 = np.resize(arr1, (j, 1))
    else:
        arr1 = np.nanmedian(np.resize(de_series, (k, order)), axis=0)
        arr2 = np.resize(arr1, (j, 1))

    return arr2[:, 0]

def remainder(data, order, center=True, model="additive", median=False):
    """
    Returns array of left behind random noise.
    """
    k = len(data)
    trends = trend(data, order, center)
    avg_seasonality = seasonality(data, order, center, model, median)

    if model == "additive":
        remainder = data - trends - avg_seasonality
    elif model == "multiplicative":
        remainder = data / (trends * avg_seasonality)
    else:
        raise ValueError("Model must be additive or multiplicative.")

    return remainder

def nan_mean(data, trailing=True):
    """
    Fills missing values with mean of series up to that point.
    """
    for idx, value in enumerate(data, 0):
        if np.isnan(value):
            if trailing == True:
                data[idx] = np.mean(data[:idx])
            else:
                data[idx] = np.nanmean(data)

    return data

def nan_median(data, trailing=True):
    """
    Fills missing values with median of series up to that point.
    """
    for idx, value in enumerate(data, 0):
        if np.isnan(value):
            if trailing == True:
                data[idx] = np.median(data[:idx])
            else:
                data[idx] = np.nanmedian(data)

    return data

def nan_random(data, trailing=True):
    """
    Fills missing values with a random number from the series
    up to that point.
    """
    for idx, value in enumerate(data, 0):
        if np.isnan(value):
            if trailing == True:
                data[idx] = np.random.choice(data[:idx])
            else:
                data[idx] = np.random.choice(data)

    return data

def nan_value(data, replacement):
    """
    Fills missing values with a specific value.
    """
    for idx, value in enumerate(data, 0):
        if np.isnan(value) == True:
            data[idx] = replacement

    return data

def nan_locf(data):
    """
    Fills missing values with the most recent non-missing value.
    """
    for idx, value in enumerate(data, 0):
        if np.isnan(value) == True:
            # data[idx] = data[:idx][0]
            data[idx] = data[:idx][-1]

    return data

def nan_nocb(data):
    """
    Fills missing values with the next non-missing value.
    """
    for idx, value in enumerate(data[::-1], 0):
        if np.isnan(value) == True:
            data[::-1][idx] = data[::-1][:idx][-1]

    return data

def nan_linear_interpolation(data):
    """
    Fills missing values via linear interpolation.
    """
    mask = np.logical_not(np.isnan(data))
    data = np.interp(np.arange(len(data)), np.arange(len(data))[mask], data[mask])

    return data

def mean_error(y_point, y_true):
    """
    Returns mean error of forecast.
    """
    error = y_point - y_true
    me = np.mean(error)

    return me

def root_mean_squared_error(y_point, y_true):
    """
    Returns root_mean_squared_error of forecast.
    """
    error = y_point - y_true
    rmse = np.sqrt(np.mean((error) ** 2))

    return rmse

def mean_absolute_error(y_point, y_true):
    """
    Returns mean absolute error of forecast.
    """
    error = y_point - y_true
    mae = np.mean(np.absolute(error))

    return mae

def mean_squared_error(y_point, y_true):
    """
    Returns mean squared error of forecast.
    """
    error = y_point - y_true
    mse = np.mean((error) ** 2)

    return mse

def mean_absolute_percentage_error(y_point, y_true):
    """
    Returns mean absolute percentage error of forecast.
    """
    error = y_point - y_true
    mape = np.mean(np.absolute(((error) / y_true) * 100))

    return mape

def symmetric_mean_absolute_percentage_error(y_point, y_true):
    """
    Returns symmetric mean absolute percentage error of forecast.
    """
    error = y_point - y_true
    smape = np.mean(np.absolute(error) / (y_point + y_true) * 200)

    return smape

def median_absolute_error(y_point, y_true):
    """
    Returns median absolute error of forecast.
    """
    error = y_point - y_true
    mdae = np.median(np.absolute(error))

    return mdae

def mad_mean_ratio(y_point, y_true):
    """
    Returns mad mean ratio of forecast.
    """
    error = y_point - y_true
    mmr = np.mean(np.absolute(error)) / np.mean(y_point)

    return mmr

import numpy,from scipy,from statsmodels


import numpy as np
from numpy.linalg import inv, norm
import matplotlib.pyplot as plt
from Data import Data

"""
CS383: Hw6
Instructor: Ian Gemp
TAs: Scott Jordan, Yash Chandak
University of Massachusetts, Amherst

README:

Feel free to make use of the function/libraries imported
You are NOT allowed to import anything else.

Following is a skeleton code which follows a Scikit style API.
Make necessary changes, where required, to get it correctly running.

Note: Running this empty template code might throw some error because
currently some return values are not as per the required API. You need to
change them.

Good Luck!
"""

class LinearRegression:
    def __init__(self, flag, alpha=1e-2):
        self.flag = flag        # flag can only be either: 'GradientDescent' or 'Analytic'
        self.alpha = alpha      # regularization coefficient
        self.w = np.zeros(2)    # initializing the weights

    def fit(self, X, Y, epochs=10000):
        """
        :param X: input features , shape: (N,2)
        :param Y: target, shape: (N,)
        :return: None

        IMP: Make use of self.w to ensure changes in the weight are reflected globally.
            We will be making use of get_params and set_params to check for correctness
        """
        if self.flag == 'GradientDescent':
            step = 0.001
            for t in range(epochs):
                # WRITE the required CODE HERE to update the parameters using gradient descent
                # make use of self.loss_grad() function
                if t%100 == 0:
                    print("Epoch: {} :: loss: {}".format(t, self.loss(self.w, X, y)))

                # compute gradient
                grad = self.loss_grad(self.w, X, Y)

                #perform gradient descent
                self.w[0] = self.w[0] - step * grad[0]
                self.w[1] = self.w[1] - step * grad[1]

        elif self.flag == 'Analytic':
            pass
            self.w = np.linalg.pinv(X).dot(y)
        else:
            raise ValueError('flag can only be either: ''GradientDescent'' or ''Analytic''')

    def predict(self, X):
        """
        :param X: inputs, shape: (N,2)
        :return: predictions, shape: (N,)

        Return your predictions
        """
        # WRITE the required CODE HERE and return the computed values
        predictions = np.zeros(len(X))
        for i in range(len(predictions)):
            predictions[i] = np.dot(self.w.T, X[i])
        return predictions

    def loss(self, w, X, Y):
        """
        :param W: weights, shape: (2,)
        :param X: input, shape: (N,2)
        :param Y: target, shape: (N,)
        :return: scalar loss value

        Compute the loss loss. (Function will be tested only for gradient descent)
        """
        # WRITE the required CODE HERE and return the computed values
        loss_sum = 0
        for i in range(len(X)):
            loss_sum = loss_sum + ((np.dot(w.T, X[i])) - Y[i])**2
        return loss_sum / 2 + self.alpha * np.linalg.norm(w, 2)

    def loss_grad(self, w, X, y):
        """
        :param W: weights, shape: (2,)
        :param X: input, shape: (N,2)
        :param Y: target, shape: (N,)
        :return: vector of shape: (2,) containing gradients for each weight

        Compute the gradient of the loss. (Function will be tested only for gradient descent)
        """
        # WRITE the required CODE HERE and return the computed values
        ret_grad = (np.dot(X.T, X) + 2 * self.alpha * np.eye(X.shape[1])).dot(w) - np.dot(X.T, y)

        return ret_grad

    def get_params(self):
        """
        ********* DO NOT EDIT ************
        :return: the current parameters value
        """
        return self.w

    def set_params(self, w):
        """
        ********* DO NOT EDIT ************
        This function Will be used to set the values of weights externally
        :param w:
        :return: None
        """
        self.w = w
        return 0

if __name__ == '__main__':
    # Get data
    data = Data()
    X, y = data.get_linear_regression_data()

    # Linear regression with gradient descent
    model = LinearRegression(flag='GradientDescent')
    model.fit(X,y)
    y_grad = model.predict(X)
    w_grad = model.get_params()

    # Analytic Linear regression
    model = LinearRegression(flag='Analytic')
    model.fit(X,y)
    y_exact = model.predict(X)
    w_exact = model.get_params()

    # Compare the resultant parameters
    diff = norm(w_exact - w_grad)
    print(diff, w_exact, w_grad)

    # Plot the results
    plt.plot(X[:, 0], y, 'o-', label='true')
    plt.plot(X[:, 0], y_exact, '-', label='pinv')
    plt.plot(X[:, 0], y_grad, '-.', label='grad')
    plt.legend()
    plt.savefig('figures/Q1.png')
    plt.close()


#!/usr/bin/env
# -*- coding: utf-8 -*-
# Copyright (C) Victor M. Mendiola Lau - All Rights Reserved
# Unauthorized copying of this file, via any medium is strictly prohibited
# Proprietary and confidential
# Written by Victor M. Mendiola Lau <ryuzakyl@gmail.com>, August 2016

"""Module containing the utilities to compute Euclidean distance."""

import numpy as np

def euclidean(x, y):
    """Euclidean distance among two vectors.

    Args:
        x (list): The first vector.
        y (list): The second vector.

    Returns:
        float: The euclidean distance between vectors x and y.

    """

    # converting python lists to numpy arrays
    x_arr = np.array(x)
    y_arr = np.array(y)

    # computing euclidean distance
    return np.linalg.norm(x_arr - y_arr)


import numpy as np
from scipy.signal import convolve, fftconvolve

def cyclic_convolution(x, h):
    L = len(x)
    N = len(h)
    if L > N:
        t = x
        x = h
        h = t
        t = N
        N = L
        L = t

    S0 = np.concatenate((x[::-1], np.zeros(N-L, dtype=h.dtype.name)))
    sy = np.array([np.roll(S0, -L+1+i) for i in range(N)])
    return sy.dot(h)

def linear_convolution(x, h):
    L = len(x)
    N = len(h)
    M = L + N - 1
    if L > N:
        t = x
        x = h
        h = t
        t = N
        N = L
        L = t

    S0 = np.concatenate((x[::-1], np.zeros(N - 1, dtype=h.dtype.name)))
    sy = np.array([np.roll(S0, i)[L - 1:] for i in range(M)])
    return sy.dot(h)

print('Nhap cac phan tu cua mang x:')
x = np.array([int(it) for it in input().split(' ')])
print('Nhap cac phan tu cua mang h:')
h = np.array([int(it) for it in input().split(' ')])
L = len(x)
N = len(h)

"""
TEST DATA
"""
# x = np.array([1, 0, 1])
# h = np.array([1, 0, 0, 1, 8, 2])

y1 = linear_convolution(x, h)
print('a. Linear convolution:', y1, sep='\n')
y1_c = convolve(x, h)
print('a. Linear convolution (ham san co) :', y1_c, sep='\n')

y2 = cyclic_convolution(x, h)
print('b. Cyclic convolution:', y1, sep='\n')

xp = np.append(x, np.zeros(N-1, dtype=x.dtype.name))
hp = np.append(h, np.zeros(L-1, dtype=h.dtype.name))
y3 = cyclic_convolution(xp, hp)
print('b. Linear convolution by Cyclic convolution:', y1, sep='\n')


# exercise 5.2.2

from matplotlib.pyplot import figure, plot, xlabel, ylabel, legend, show
import sklearn.linear_model as lm
import numpy as np

# Use dataset as in the previous exercise
N = 100
X = np.array(range(N)).reshape(-1,1)
eps_mean, eps_std = 0, 0.1
eps = np.array(eps_std*np.random.randn(N) + eps_mean).reshape(-1,1)
w0 = -0.5
w1 = 0.01
y = w0 + w1*X + eps
y_true = y - eps

# Fit ordinary least squares regression model
model = lm.LinearRegression(fit_intercept=True)
model = model.fit(X,y)
# Compute model output:
y_est = model.predict(X)
# Or equivalently:
#y_est = model.intercept_ + X @ model.coef_

# Plot original data and the model output
f = figure()

plot(X,y,'.')
plot(X,y_true,'-')
plot(X,y_est,'-')
xlabel('X'); ylabel('y')
legend(['Training data', 'Data generator', 'Regression fit (model)'])

show()

print('Ran Exercise 5.2.2')


import numpy as np
import pandas as pd
import sklearn.metrics

def calc_confusion_matrix_for_probas_and_threshold(ytrue_N, yproba1_N, thr):
    ''' Compute confusion matrix for particular probabilistic predictions.

    User provides the predicted probabilities of the class labeled 1,
    as well as the specific threshold at which to predict class 1.

    Args
    ----
    ytrue_N : 1D array, shape (n_examples,) = (N,)
        Each entry is binary value (0 or 1) of 'true' label of one example
        One entry per example in current dataset
    yproba1_N : 1D array, shape (n_examples,) = (N,)
        One entry per example in current dataset.
        Each entry must be between 0.0 and 1.0 (inclusive).
        Each entry is a probability that correct label is positive (class 1).
        Needs to be same size as ytrue_N.
    thr : float, between 0.0 and 1.0 (inclusive)
        Scalar threshold for converting probabilities into hard decisions
        Calls an example "positive" if yproba1_N >= thr.

    Returns
    -------
    cm_df : Pandas DataFrame
        Can be printed like print(cm_df) to easily display results

    Examples
    --------
    # Example with probabilistic predictions
    >>> ytrue_N    = np.asarray([0.0, 0.0, 0.0, 0.00, 1.0, 1.0, 1.00, 1.00])
    >>> yproba1_N  = np.asarray([0.1, 0.2, 0.8, 0.45, 0.6, 0.7, 0.05, 0.08])
    >>> calc_confusion_matrix_for_probas_and_threshold(ytrue_N, yproba1_N, 0.5)
    Predicted  0  1
    True
    0          3  1
    1          2  2

    # Example with hard binary predictions already
    >>> ytrue_N = np.asarray([0., 0., 0., 0., 1., 1., 1., 1.])
    >>> yhat_N  = np.asarray([0., 0., 1., 0., 1., 1., 0., 0.])
    >>> calc_confusion_matrix_for_probas_and_threshold(ytrue_N, yhat_N, 0.5)
    Predicted  0  1
    True
    0          3  1
    1          2  2
    '''
    ytrue_N = np.asarray(ytrue_N, dtype=np.int32)

    # Determine hard predictions given probabilities
    yproba1_N = np.asarray(yproba1_N, dtype=np.float64)
    yhat_N = np.asarray(yproba1_N >= thr, dtype=np.int32)

    cm = sklearn.metrics.confusion_matrix(ytrue_N, yhat_N)
    cm_df = pd.DataFrame(data=cm, columns=[0, 1], index=[0, 1])
    cm_df.columns.name = 'Predicted'
    cm_df.index.name = 'True'
    return cm_df


import pandas as pd;
import numpy as np;

data = [[1.4, np.nan], [8.3, -2.1], [0.02, -1.11], [np.nan, np.nan]];
x = pd.DataFrame(data, columns=['one', 'two'], index=['a', 'b', 'c', 'd']);

print(x);
print('------------------');
print(x.sum());
print(x.sum(axis=0,skipna=False));
print(x.sum(axis=1,skipna=False));
print('------------------');
print(x['one'].sum());
print(x.loc['a'].sum());
print('------------------');
print(x.mean());
print(x.mean(axis=1));
print('------------------------------------------------');

data = {
    'name':['james1','james2','james3','james4','james5'],
    'ko':[100,90,89,99,89],
    'en':[99,91,87,89,81],
    'ma':[80,92,88,99,87],
    'si':[99,100,87,93,86],
};

df = pd.DataFrame(data);
print(df);

# 1. 인덱스 값을 이름으로 변경 하시오
df.index = data['name'];
print(df);
del df['name'];
print(df);

# 학생 별 합(sum), 평균(avg) 컬럼을 추가 한다.
# 과목 별 합(sum), 평균(avg) row를 추가 한다.
new_df = df.copy();
new_df['sum'] = new_df.iloc[:,0:4].sum(axis=1);
new_df['avg'] = new_df.iloc[:,0:4].mean(axis=1);
print('-----');
new_df.loc['sum'] = new_df.iloc[0:new_df.shape[0],:].sum();
new_df.loc['avg'] = new_df.iloc[0:new_df.shape[0]-1,:].mean();
print(new_df);

# 국어 평균 점수 보다 높은 학생들을 조회 하시오
print(df);
df2 = df[df['ko'] > df['ko'].mean()];
print(df2);

# df2 = df[df['ko'] > df.loc['avg'][0]];
# df2 = df2.drop('sum',axis=0);
# print(df2);
# 전체 평균 보다 높은 학생들을 조회 하시오
# df3 = df[df['avg'] > df.loc['avg'][5]];
# df3 = df3.drop('sum',axis=0);
# print(df3);

print('------------------------------------------');
print(df);
print(df.min());
print(df.max());
print(df.min(axis=1));
print(df.max(axis=1));
print(df.idxmin());
print(df.idxmax());

df5 = df.idxmax();
df5 = pd.DataFrame(df5,columns=['name']);
df5['score'] = df.max();
print(df5);

# dic 만들어 보자
dic1 = {df5.columns[0]:df5['name'].tolist(),
        df5.columns[1]:df5['score'].tolist()};

print(dic1);


from matplotlib import pyplot as plt
import numpy as np

def set_current_axes(ax):
    if ax is not None:
        plt.sca(ax)

def show_1d_function(fct, min_val=-5, max_val=5, step=.1, constante_x=None, constante_y=None, ax=None, **kwargs):
    x_values = np.linspace(min_val, max_val, int((max_val-min_val)/step)+1)
    fct_values = fct(x_values)

    set_current_axes(ax)
    plt.plot(x_values, fct_values, **kwargs)

    if 'label' in kwargs:
        plt.legend()

    if constante_x is not None:
        plt.plot([min_val, max_val], [constante_x,constante_x], 'k--')

    if constante_y is not None:
        plt.plot([constante_y, constante_y], [min(fct_values),max(fct_values)], 'k--')

def show_2d_function(fct, min_val=-5, max_val=5, mesh_step=.01, optimal=None, bar=True, ax=None, **kwargs):
    x1_min, x2_min = np.ones(2) * min_val
    x1_max, x2_max = np.ones(2) * max_val

    x1_values = np.arange(x1_min, x1_max+mesh_step, mesh_step)
    x2_values = np.arange(x2_min, x2_max+mesh_step, mesh_step)

    fct_values = np.array([[fct(np.array((x1,x2))) for x1 in x1_values] for x2 in x2_values])

    set_current_axes(ax)
    if 'cmap' not in kwargs: kwargs['cmap'] = 'RdBu'
    plt.contour(x1_values, x2_values, fct_values, 40, **kwargs)
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))

    if bar:
        plt.colorbar()

    if optimal is not None:
        plt.scatter(*optimal, s=200, marker='*', c='r')

def show_2d_dataset(x, y, ax=None):
    set_current_axes(ax)
    labels = np.unique(y)
    for one_label, one_color in zip(labels, "rby"):
        mask = (y==one_label)
        plt.scatter(x[mask, 0], x[mask, 1], c=one_color, edgecolors='k', label=f'$y={one_label}$')

    plt.legend()

def show_2d_predictions(x, y, predict_fct, ax=None):
    # Inspired by: http://scikit-learn.org/stable/auto_examples/classification/plot_classifier_comparison.html
    mesh_step = .02  # step size in the mesh
    x0_min, x0_max = x[:, 0].min() - .5, x[:, 0].max() + .5
    x1_min, x1_max = x[:, 1].min() - .5, x[:, 1].max() + .5

    set_current_axes(ax)
    if predict_fct is not None:
        x0_values = np.arange(x0_min, x0_max, mesh_step)
        x1_values = np.arange(x1_min, x1_max, mesh_step)
        x0_grid, x1_grid = np.meshgrid(x0_values, x1_values)
        x_grid_pairs = np.c_[x0_grid.ravel(), x1_grid.ravel()]
        grid_predictions = predict_fct(x_grid_pairs)

        # Put the result into a color plot
        grid_predictions = grid_predictions.reshape(x0_grid.shape)
        plt.contourf(x0_grid, x1_grid, grid_predictions, cmap='RdBu', alpha=.8)

    # Plot also the training points
    show_2d_dataset(x, y)
    return x0_min, x0_max, x1_min, x1_max

def show_2d_vector_field(grad_fct, min_val=-5, max_val=5, mesh_step=.5, ax=None, **kwargs):
    x1_min, x2_min = np.ones(2) * min_val
    x1_max, x2_max = np.ones(2) * max_val

    x1_values = np.arange(x1_min, x1_max+mesh_step, mesh_step)
    x2_values = np.arange(x2_min, x2_max+mesh_step, mesh_step)

    grad_values = np.array([[grad_fct(np.array((x1,x2))) for x1 in x1_values] for x2 in x2_values])

    set_current_axes(ax)
    plt.quiver(x1_values, x2_values, grad_values[:,:,0], grad_values[:,:,1], **kwargs)
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))

# Graphique de la trajectoire
def sgd_trajectoire(w_list, fonction_objectif, min_val=-5, max_val=5, mesh_step=.5, w_opt=None, ax=None):
    show_2d_function(fonction_objectif, min_val, max_val, mesh_step, optimal=w_opt, ax=ax)

    if len(w_list) > 0:
        trajectoire = np.array(w_list)
        plt.plot(trajectoire[:,0], trajectoire[:,1], 'o--', c='g')

    plt.title(f'Trajectoire de la descente en gradient'); plt.xlabel('$w_1$'); plt.ylabel('$w_2$')

# Graphique des valeurs de la fonction objectif
def sgd_courbe_objectif(obj_list, obj_opt=None, ax=None):
    set_current_axes(ax)
    plt.plot(obj_list, 'o--', c='g', label='$F(\mathbf{w})$')
    if obj_opt is not None: plt.plot([0, len(obj_list)], 2*[obj_opt], '*--', c='r', label='optimal');
    plt.title(f'Valeurs de la fonction objectif'); plt.xlabel('$t$')
    plt.legend()

def code_button():
    from IPython.display import HTML
    return HTML('''
    voir/cacher le code.''')

def center_images():
    from IPython.display import display, HTML
    display(HTML("""

    """))

if __name__ == '__main__':
    show_2d_function(lambda x: x @ x)
    plt.show()


import numpy as np

def mkimgidxs(nrows,ncols,dtype=int):

    '''

    To generate indice grids for an image

    Input Parameters
    ----------------
    nrows : int
        The number of rows in the image

    ncols : int
        The number of columns in the image

    dtype : dtype, default, `int`, optional
        The type of the output grids

    Returns
    --------
    ximg, yimg : numpy.ndnarray, numpy.ndnarray of `dtype`
         A list of integers giving the individual file numbers

    Notes
    -----
    https://stackoverflow.com/questions/1550130/\
    cloning-row-or-column-vectors

    Examples
    --------
    > ximg, yimg = mkimgidxs(3,5)
    > print(ximg)
      [[0 1 2 3 4]
       [0 1 2 3 4]
       [0 1 2 3 4]]

    > print(yimg)
      [[0 0 0 0 0]
      [1 1 1 1 1]
      [2 2 2 2 2]]

    Modification History
    --------------------
    2022-06-17 - Written by M. Cushing, University of Toledo.
    Just the old reform/rebin trick from IDL.

    '''

    ximg = np.tile(np.arange(ncols,dtype=dtype),(nrows,1))
    yimg = np.tile(np.reshape(np.arange(nrows,dtype=dtype),\
                   (nrows,1)),(1,ncols))

    return (ximg, yimg)