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

#Initialize argument parser
parser = argparse.ArgumentParser()
parser.add_argument("-f", "--filename", help="input file name")
parser.add_argument("-l", "--learning_rate", type=float, help="learning rate")
parser.add_argument("-i", "--iterations", type=int, help="number of iteration")
args = parser.parse_args()

if args.filename:
    filename = args.filename
else:
    print('Please specify a file name')
    print('Execute "python logistic.py -h" for help')
    exit()

if args.learning_rate:
    learning_rate = args.learning_rate
else:
    learning_rate = 1
    print('Using default learning rate', learning_rate)

if args.iterations:
    iterations = args.iterations
else:
    iterations = 100
    print('Using default iterations', iterations)

def sigmoid(value):
    return logistic.cdf(value)

def hypothesis(X, theta):
    hx = np.dot(theta, X)
    return sigmoid(hx)

def predict_proba(X,theta):
    return sigmoid(np.inner(theta[1:], X) + theta[0])

#read data from csv file
data_csv = pd.read_csv(filename)

#number of training examples
number_of_examples = data_csv.shape[0]

#number of parameters in training set
number_of_parameters = data_csv.shape[1]

#add column of ones to dataframe  make looping symmetric
ones = np.ones(number_of_examples)
data_csv.insert(0, 'theta0',ones)

#initialize theta to zeroes array
theta = np.zeros(number_of_parameters)

#convert dataframe to numpy array
data_array = data_csv.as_matrix()

for i in range(iterations):
    theta_diff = []
    for j in range(len(theta)):
        summition = 0
        for sample in data_array:
            summition += (hypothesis(sample[:-1], theta) - sample[-1])* sample[j]
        if j != 0:
            summition -= theta[j]/number_of_examples
        theta_diff.append(learning_rate * summition / number_of_examples)
    theta = theta - theta_diff

for i in range(len(theta)):
    print('coefficient for theta ' + str(i), theta[i])
if len(data_csv.columns.tolist()) == 4:
    # plot decision boundary
    # create 5 by 5 grid
    xx, yy = np.mgrid[-4:4:.01, -4:4:.01]
    grid = np.c_[xx.ravel(), yy.ravel()]

    # calculate probabilities of grid points
    probs = predict_proba(grid, theta).reshape(xx.shape)

    # plot probability labels
    f, ax = plt.subplots(figsize=(8, 6))
    contour = ax.contourf(xx, yy, probs, levels=[0.499, 0.501], cmap="Greys",vmin=0, vmax=0.6)

    # plot actual points on dataset
    plt.title('Decision Boundary')
    ax.scatter(data_csv['P1'], data_csv['P2'], c=data_csv['OUT'], s=50,cmap="RdBu", vmin=-.2, vmax=1.2,edgecolor="white", linewidth=1)
    ax.set(aspect="equal",xlim=(-4, 4), ylim=(-4, 4),xlabel="$X_1$", ylabel="$X_2$")
    plt.show()


from scipy.stats import chi2_contingency
import pandas as pd

def chi_squared_report(df, col1, col2):
    """
    Function
    ________
        Prints  - a cross tab of a given two columns of a pandas.DataFrame
                - chi2, p-value, dof, and expected frequencies

    Params
    __________
        args:
            df(pd.DataFrame) : df to modify
            col1(String) : column name of the df
            col2(String) : column name of the df

    Return
    ______
        None
    """

    try:
        observed = pd.crosstab(df[col1], df[col2], margins=False)
        print("contingency table:")
        print(observed)
        print("\n")

        chi2, p_value, dof, expected = chi2_contingency(observed)

        print(f"chi2: {chi2}")
        print(f"p-value: {p_value}")
        print(f"dof: {dof}")
        print(f"\nexpected frequncy:\n {expected}")

    except Exception as e:
        print(e)

if __name__ == "__main__":
    #
    # df = pd.DataFrame({"a":["bat", "cat", "bat"], "b":["home", "away", "away"]})
    #
    # # print(df)
    # # print("\n\n\n")
    # # print(pd.crosstab(df["a"], df["b"], margins=True))
    #
    # chi_squared_report(df, "a", "b")


import silence_tensorflow.auto
import numpy as np
from tensorboard import data
import tensorflow as tf
import tensorflow_probability as tfp

from tqdm import tqdm # progress-meter

def em(dataset, n_classes, n_iterations, random_seed):
    n_samples = dataset.shape[0]

    np.random.seed(random_seed)

    # Initial guesses for the parameters
    mus = np.random.rand(n_classes)
    sigmas = np.random.rand(n_classes)
    # Sampling the dirichlet distribution will return a probability vector that
    # adheres to the property of the categorical that the components have to add
    # up to one.
    class_probs = np.random.dirichlet(np.ones(n_classes))

    for em_iter in tqdm(range(n_iterations)):
        # E-Step

        # The old and the new faster way to query the probability of the normal
        # distribution. The old way was straight-forward: we first create a
        # zero-filled matrix and then set all its values by iterating over it
        # with a double for loop.
        #
        # Double for loops are fairly expensive. We can avoid them by evaluating
        # the batched normal distribution over a broadcasted dataset.

        # responsibilities = np.zeros((n_samples, n_classes))

        # for i in range(n_samples):
        #     for c in range(n_classes):
        #         responsibilities[i, c] = class_probs[c] *\
        #             tfp.distributions.Normal(loc=mus[c], scale=sigmas[c]).prob(dataset[i])

        ################################################
        ## !!!! That part was wrong in the video !!!!
        ## Thanks to Anuj Shah for pointing it out
        ################################################
        ## -> The correct version will also multiply with the class probabilities
        responsibilities = tfp.distributions.Normal(loc=mus, scale=sigmas).prob(
            dataset.reshape(-1, 1)
        ).numpy() * class_probs

        # Normalization over the rows, so that the responsibilitiy mass for one
        # sample sums up to one over all classes.
        responsibilities /= np.linalg.norm(responsibilities, axis=1, ord=1, keepdims=True)

        # Handy quantity
        class_responsibilities = np.sum(responsibilities, axis=0)

        # M-Step
        for c in range(n_classes):
            class_probs[c] = class_responsibilities[c] / n_samples
            mus[c] = np.sum(responsibilities[:, c] * dataset) / class_responsibilities[c]
            sigmas[c] = np.sqrt(
                np.sum(responsibilities[:, c] * (dataset - mus[c])**2) / class_responsibilities[c]
            )

    return class_probs, mus, sigmas

def main():
    class_probs_true = [0.6, 0.4]
    mus_true = [2.5, 4.8]
    sigmas_true = [0.6, 0.3]
    random_seed = 42 # for reproducability
    n_samples = 1000
    n_iterations = 100
    n_classes = 2

    # generate the data
    univariate_gmm = tfp.distributions.MixtureSameFamily(
        mixture_distribution=tfp.distributions.Categorical(probs=class_probs_true),
        components_distribution=tfp.distributions.Normal(
            loc=mus_true,
            scale=sigmas_true,
        )
    )

    # REMARK: I added the random_seed after the video as I unfortunately missed
    # it
    dataset = univariate_gmm.sample(n_samples, seed=random_seed).numpy()

    # print(dataset)

    class_probs, mus, sigmas = em(dataset, n_classes, n_iterations, random_seed)

    print(class_probs)
    print(mus)
    print(sigmas)

if __name__ == "__main__":
    main()


"""
机械求积方法

"""

import sympy as sp

def trapezoid(f, x, interval):
    """
    梯形公式
    :param f: 原函数
    :param x: 未知量
    :param interval: 积分区间
    :return: 近似的积分值
    """
    a, b = interval
    return sp.Rational(1 / 2) * (b - a) * (f.limit(x, a) + f.limit((x, b)))

def mid_rectangle(f, x, interval):
    """
    中矩形公式
    :param f: 原函数
    :param x: 未知量
    :param interval: 积分区间
    :return: 近似的积分值
    """
    a, b = interval
    return (b - a) * f.limit(x, sp.Rational((a + b) / 2))

def simpson(f, x, interval):
    """
    辛普生公式
    :param f: 原函数
    :param x: 未知量
    :param interval: 积分区间
    :return: 近似的积分值
    """
    a, b = interval
    return sp.Rational(1 / 6) * (b - a) * \
           (f.limit(x, a) + 4 * f.limit(x, sp.Rational((a + b) / 2)) + f.limit(x, b))

if __name__ == '__main__':
    from sympy.abc import x
    f = sp.exp(x)
    print(float(trapezoid(f, x, (0, 2))))
    print(float(mid_rectangle(f, x, (0, 2))))
    print(float(simpson(f, x, (0, 2))))


import matplotlib.pyplot as plt
import numpy as np

SEP = "\t"

# ----------------------------------------------------------------
#   Standard Linear Regression
# ----------------------------------------------------------------

def load_data_and_label(file_name):
    data, label = [], []
    with open(file_name) as f:
        for line in f.readlines():
            items = list(map(lambda x: float(x), line.split(SEP)))
            data.append(items[:-1])
            label.append(items[-1])
    data, label = np.array(data), np.array(label)
    return data, label

def standard_regression(data_train, label_train):
    data_train, label_train = np.array(data_train), np.array(label_train).T
    xtx_matrix = np.dot(data_train.T, data_train)
    if np.linalg.det(xtx_matrix) == 0.0:
        print("This is matrix is singular, cannot do inverse")
        return
    weights = np.dot(np.dot(np.linalg.inv(xtx_matrix), data_train.T), label_train)
    return weights

def standard_regression_plot(title, data, label, predict):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    __regression_plot(ax, data, label, predict)
    plt.title(title)
    plt.show()

def __regression_plot(_plt, data, label, predict):
    _plt.scatter(data[:, 1], label)
    sort_index = data[:, 1].argsort()
    _plt.plot(data[:, 1][sort_index], predict[sort_index], color="red", linewidth=3)

# ----------------------------------------------------------------
#   Locally Weighted Linear Regression
# ----------------------------------------------------------------

def lwlr(test_point, data_train, label_train, k=0.1):
    m = data_train.shape[0]
    weights = np.eye(m)
    for j in range(m):
        diff = test_point - data_train[j, :]
        weights[j, j] = np.exp(np.dot(diff, diff.T) / (-2.0 * k ** 2))
    xtx_matrix = np.dot(data_train.T, np.dot(weights, data_train))
    if np.linalg.det(xtx_matrix) == 0.0:
        print()
        return
    weights = np.dot(np.linalg.inv(xtx_matrix), np.dot(data_train.T, np.dot(weights, label_train)))
    return np.dot(test_point, weights)

def lwlr_test(test_data, data_train, label_train, k=0.1):
    predict = [lwlr(data, data_train, label_train, k) for data in test_data]
    predict = np.array(predict)
    return predict

def lwlr_plot(data, label, ks):
    fig = plt.figure()
    ax1 = fig.add_subplot(311)
    ax2 = fig.add_subplot(312)
    ax3 = fig.add_subplot(313)
    predicts = [lwlr_test(data, data, label, k) for k in ks]
    for p, k, predict in zip([ax1, ax2, ax3], ks, predicts):
        __regression_plot(p, data, label, predict)
        p.set_title("Locally Weighted Linear Regression k = %2f" % k)
    plt.subplots_adjust(hspace=0.5)
    plt.show()

# ----------------------------------------------------------------
#   Abalone Test with LWLR
# ----------------------------------------------------------------

def rss_error(label_test, predict):
    return np.sum(np.square(label_test - predict))

def abalone_test():
    data, label = load_data_and_label('data/abalone.txt')
    data_train, label_train = data[0:99], label[0:99]
    data_test, label_test = data[100:199], label[100:199]
    ks = [0.1, 1, 10]
    predicts_train = [lwlr_test(data_train, data_train, label_train, k) for k in ks]
    for predict, k in zip(predicts_train, ks):
        rss = rss_error(label_train, predict)
        print("Residual sum of squares in train data with k = %f is %f" % (k, rss))

    # TODO check why when k=0.1 the rss value is not stable
    predicts_test = [lwlr_test(data_test, data_train, label_train, k) for k in ks]
    for predict, k in zip(predicts_test, ks):
        rss = rss_error(label_test, predict)
        print("Residual sum of squares in test data with k = %f is %f" % (k, rss))

    w = standard_regression(data_train, label_train)
    predict = np.dot(data_test, w)
    rss = rss_error(label_test, predict)
    print("Standard regression's residual sum of square in test data: %f" % rss)

# ----------------------------------------------------------------
#   Ridge Regression
# ----------------------------------------------------------------

def ridge_regression(data_train, label_train, lam=0.2):
    xtx_matrix = np.dot(data_train.T, data_train)
    denom = xtx_matrix + np.eye(data_train.shape[1]) * lam
    if np.linalg.det(denom) == 0.0:
        print("This is matrix is singular, cannot do inverse")
        return
    w = np.dot(np.linalg.inv(denom), np.dot(data_train.T, label_train))
    return w

def regularize(data, label=np.array([])):
    data_mean = data.mean(0)
    data_var = data.var(0)
    data_norm = (data - data_mean) / data_var
    label_mean = label.mean(0)
    label_norm = label - label_mean
    return data_norm, label_norm

def ridge_test(data_train, label_train):
    # normalization
    data_norm, label_norm = regularize(data_train, label_train)

    lam_count = 30
    weights = np.zeros((lam_count, data_train.shape[1]))
    for i in range(lam_count):
        w = ridge_regression(data_norm, label_norm, np.exp(i - 10))
        weights[i, :] = w.T
    return weights

def ridge_plot(weights):
    plt.plot(weights)
    plt.xlabel("log(lambda)")
    plt.ylabel("w(i)")
    plt.show()

# ----------------------------------------------------------------
#   Forward Stepwise Regression
# ----------------------------------------------------------------

def stage_wise(data_train, label_train, eps=0.01, num=100):
    data_norm, label_norm = regularize(data_train, label_train)
    m, n = data_norm.shape
    weights_record = np.zeros((num, n))
    weights = np.zeros((n, 1))
    weights_test, weights_max = weights.copy(), weights.copy()
    for i in range(num):
        print(weights.T)
        lowest_error = np.inf
        for j in range(n):
            for sign in [-1, 1]:
                weights_test = weights.copy()
                weights_test[j] += eps * sign
                label_test = np.dot(data_norm, weights_test)
                rss = rss_error(label_norm, label_test.T)
                if rss < lowest_error:
                    lowest_error = rss
                    weights_max = weights_test
        weights = weights_max.copy()
        weights_record[i, :] = weights.T
    return weights_record

def abalone_standard_regression_test(data_train, label_train):
    data_norm, label_norm = regularize(data_train, label_train)
    weights = standard_regression(data_norm, label_norm)
    print(weights.T)

def stage_wise_plot(data_train, label_train, eps=0.005, num=1000):
    weights = stage_wise(data_train, label_train, eps, num)
    plt.plot(weights)
    plt.show()

    # ----------------------------------------------------------------
    #   LEGO
    # ----------------------------------------------------------------

    # TODO: google API has changed


"""
mapping a surface [xi, et] into (x,y,z)

xi = [-1, 1]
et = [-1, 1]

x = xi + 0.1*sin(2*pi*xi)sin(2*pi*et)
y = et + 0.1*sin(2*pi*xi)sin(2*pi*et)
z = 0.1*sin(2*pi*xi)sin(2*pi*et)

"""

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

import numpy as np
from numpy import sin, cos, pi

def X(xi, et):
    return xi + 0.1*sin(pi*xi) * sin(pi*et)

def Y(xi, et):
    return et + 0.1*sin(pi*xi) * sin(pi*et)

def Z(xi, et):
    return 0.1*sin(pi*xi) * sin(pi*et)

xi = np.linspace(-1,1,1000)
et = np.linspace(-1,1,1000)
xi, et = np.meshgrid(xi, et)

# fig = plt.figure()
# ax = fig.add_subplot(111, projection='3d')
# ax.view_init(45,60)
# ax.plot_surface(X(xi,et), Y(xi,et), Z(xi,et))
# plt.show()

def X_xi(xi, et):
    return 1 + 0.1*pi*cos(pi*xi) * sin(pi*et)

def X_et(xi, et):
    return 0.1*pi*sin(pi*xi) * cos(pi*et)

def Y_xi(xi, et):
    return 0.1*pi*cos(pi*xi) * sin(pi*et)

def Y_et(xi, et):
    return 1 + 0.1*pi*sin(pi*xi) * cos(pi*et)

def Z_xi(xi, et):
    return 0.1*pi*cos(pi*xi) * sin(pi*et)

def Z_et(xi, et):
    return 0.1*pi*sin(pi*xi) * cos(pi*et)

def metric_matrix(xi, et):
    J11 = X_xi(xi, et)
    J12 = X_et(xi, et)
    J21 = Y_xi(xi, et)
    J22 = Y_et(xi, et)
    J31 = Z_xi(xi, et)
    J32 = Z_et(xi, et)

    g_11 = J11**2 + J21**2 + J31**2
    g_22 = J12**2 + J22**2 + J32**2
    g_12 = J11*J12 + J21*J22 + J31*J32
    g_21 = g_12
    return ((g_11, g_12),
            (g_21, g_22))

def metric(xi, et):
    G = metric_matrix(xi, et)
    g_11 = G[0][0]
    g_12 = G[0][1]
    g_21 = g_12
    g_22 = G[1][1]
    return g_11 * g_22 - g_12 * g_21

g = metric(xi, et)

def area_element(xi, et):
    a1 = X_xi(xi, et)
    a2 = Y_xi(xi, et)
    a3 = Z_xi(xi, et)

    b1 = X_et(xi, et)
    b2 = Y_et(xi, et)
    b3 = Z_et(xi, et)

    c1 = a2*b3 - a3*b2
    c2 = a3*b1 - a1*b3
    c3 = a1*b2 - a2*b1

    return np.sqrt(c1**2+c2**2+c3**2)

AE = area_element(xi, et)
print(np.max(np.abs(np.sqrt(g)-AE)))


# some likelihood expressions used for the collapsed gibbs sampler

from scipy.special import gammaln

def beta_binom_llhd(succ, trials, a, b):
    """
    Computes the log likelihood for succ successes under Beta-Binomial with parameters given by other arguments
    :param succ: Number of observed successes, integer
    :param trials: Number of trials, integer
    :param a: Beta parameter, positive real
    :param b: Beta parameter, positive real
    :return: llhd of data, a non-positive real
    """
    return gammaln(trials + 1) + gammaln(succ + a) + gammaln(trials - succ + b) + gammaln(a + b) - \
           (gammaln(succ + 1) + gammaln(trials - succ + 1) + gammaln(a) + gammaln(b) + gammaln(trials + a + b))

def diri_multi_llhd(obs, alphas):
    """
    Computes the log likelihood for dirichlet-multinomial distribution with parameters given by other arguments
    :param obs: observed counts in each category, a vector of integers of length k
    :param alphas: Dirichlet parameter, a vector of positive reals of length k
    :return: llhd of data, a non-positive real
    """
    trials = sum(obs)
    return gammaln(trials+1) + gammaln(sum(alphas)) - gammaln(trials+sum(alphas)) + \
              sum([gammaln(ob + alpha) - gammaln(ob+1)-gammaln(alpha) for ob,alpha in zip(obs,alphas)])


from numpy import sqrt

#Gathers value of variables for calculation using input()
a = float(input("Please enter the value of \"a\": "))
b = float(input("Please enter the value of \"b\": "))
c = float(input("Please enter the value of \"c\": "))

if (b**2 - 4*a*c < 0): #Check if discriminant => 0. If not, says that the problem is invalid and exits
    print("The solutions are in the complex plane.")
    quit()

discriminant = sqrt(b**2 - 4*a*c) # In order to reduce the amount of calculations, the discriminant is only computed one time
sol_1 = (-b + discriminant)/(2*a)
sol_2 = (-b - discriminant)/(2*a)

# Prints both solutions, implying that the initial problem has a solution in the real plane
if (sol_1 != sol_2): #Checks that the two solutions are different. If not, there's only one real solution
    print("The first solution is: " + str(sol_1))
    print("The second solution is: " + str(sol_2))
elif (sol_1 == sol_2):
    print("The only solution is: " + str(sol_1))


# Discrete {Cosine,Fourier} Transform matrix of N elements

import torch
import numpy as np
import pywt
from deepfetal.build.complex import *
from deepfetal.build.dynind import *

def dctmtx(N, inv=False):
    N = float(N)
    rr, cc = torch.meshgrid(
        [torch.arange(0, N), torch.arange(0, N)])  # This is opposite to matlab meshgrid, i.e., ndgrid probably
    c = np.sqrt(2./N)*torch.cos(np.pi*(2.*cc+1)*rr/(2.*N))
    c[0, :] = c[0, :] / np.sqrt(2.)
    c = ccomplex(c)
    if inv:
        c = ch(c)
    return c

def idctmtx(N):
    return dctmtx(N, inv=True)

def dftmtx(N, inv=False):
    N = float(N)
    rr, cc = torch.meshgrid(
        [torch.arange(0, N), torch.arange(0, N)])  # This is opposite to matlab meshgrid, i.e., ndgrid probably
    rr = cc*((2.*np.pi/N)*rr)
    f = ceuler(-rr)/np.sqrt(N)
    if inv:
        f = ch(f)
    return f

def idftmtx(N):
    return dftmtx(N, inv=True)

def dwtmtx(N, typ='db1', L=1, inv=False, over=False):
    N, L = int(N), int(L)
    w = pywt.Wavelet(typ)  #Wavelet object
    #  w.filter_bank == (w.dec_lo, w.dec_hi, w.rec_lo, w.rec_hi)
    ha, hd = torch.tensor(w.dec_lo), torch.tensor(w.dec_hi)
    H = torch.stack((ha, hd), 1)
    H = torch.unsqueeze(H, -1)
    H = torch.transpose(H, 0, 2)
    W = torch.eye(N)
    for l in range(L):
        T = torch.eye(0)
        if not over:
            A = waveletmatrix(int(N/(2**l)), H, over)
        else:
            A = waveletmatrix(int(N), H, over)
        for s in range((2**l)):
            T = torch.block_diag(T, A)
        W = torch.mm(T, W)
    W = ccomplex(W)
    if inv:
        W = ch(W)
    return W

def idwtmtx(N, typ='db1', L=1, over=False):
    return dwtmtx(N, typ=typ, L=L, over=over, inv=True)

def waveletmatrix(N, H, over):
    H = torch.flip(H, [2,])
    Z = torch.zeros((1, 2, N - H.shape[2])).type(H.dtype)
    W = torch.cat((H, Z), 2)
    if not over:
        for n in range(int(N/2)-1):
            W = torch.cat((W, torch.roll(dynind(W, n, 0), 2, 2)), 0)
    else:
        W = torch.roll(W, -np.floor((H.shape[2]-1)/2).astype(int), 2)
        for n in range(int(N)-1):
            W = torch.cat((W, torch.roll(dynind(W, n, 0), 1, 2)), 0)
    W = torch.transpose(W, 1, 0)
    if not over:
        return torch.reshape(W, (N, N))
    else:
        return torch.reshape(W, (2*N, N))/np.sqrt(2.)

# def build_grid(N):
#    # Generates a flattened grid of coordinates from -1 to 1
#    ND = len(N)
#    tensors = []
#    for n in range(ND):
#        tensors = tensors + tuple(torch.linspace(-1, 1, steps=N[n]))
#    mgrid = torch.stack(torch.meshgrid(*tensors), dim=-1)
#    mgrid = mgrid.reshape(-1, dim)
#    return mgrid


import numpy as np
import pandas as pd

def autocorr_single_tp(a: np.array, t: int) -> float:
    """Do autocorrelation for a single time point.

    Parameters
    ----------
    a : np.array
        The array to correlate (complex or real number)
    t : int
        The distance (in the index)

    Returns
    -------
    float
        The autocorrelation as a real number.
    """
    return np.real(np.sum(a[0] * np.conj(a[t])))

def autocorr(df: pd.DataFrame) -> pd.DataFrame:
    """Do autocorrelation for all possible time steps over all columns.

    Parameters
    ----------
    df : pd.DataFrame
        The data frame to correlate

    Returns
    -------
    pd.DataFrame
        The resulting dataframe with timestep as index and one column named autocorr
    """
    df_result = pd.DataFrame()
    df_result['autocorr'] = [autocorr_single_tp(df.values, i) for i in range(df.shape[0])]
    df_result.index.name = 'timestep'
    return df_result

def fourier_transform(df: pd.DataFrame) -> pd.DataFrame:
    """Fourier transform a dataframe column-wise. The shape of the dataframe is
    not changed, only the column names are appended with _ft.

    Parameters
    ----------
    df : pd.DataFrame
        The dataframe to transform

    Returns
    -------
    pd.DataFrame
        The dataframe with the fourier transform of all columns (they are named {}_ft)
    """
    df_result = df.apply(np.fft.fft)
    df_result.index.name = 'frequency'
    df_result.columns = [f'{c}_ft' for c in df_result.columns]
    return df_result


import numpy

def estimate_gaussian(x_array):
    """
    This function estimates the parameters of a Gaussian distribution
    using a provided dataset.

    Parameters
    ----------
    x_array : array_like
        The dataset of shape (m x n) with each n-dimensional
        data point in one row, and each total of m data points.

    Returns
    -------
    mean_mu : array_like
        A vector of shape (n,) containing the means of each dimension.

    sigma2 : array_like
        A vector of shape (n,) containing the computed
        variances of each dimension.

    Instructions
    ------------
    Compute the mean of the data and the variances
    In particular, mean_mu[i] should contain the mean of
    the data for the i-th feature and sigma2[i]
    should contain variance of the i-th feature.
    """
    num_examples, num_features = x_array.shape
    mean_mu = numpy.zeros(num_features)
    sigma2 = numpy.zeros(num_features)

    mean_mu = (1/num_examples)*numpy.sum(x_array, axis=0)
    sigma2 = (1/num_examples)*numpy.sum((x_array - mean_mu)**2, axis=0)

    return mean_mu, sigma2


# Regla de Scott

import pandas as pd
import numpy as np
import math

crabs = pd.read_table("../../../../r-basic/data/datacrab.txt", sep = " ")
cw = crabs.width
n = len(cw)
precision = 0.1

# Número de clases

As = 3.5*np.std(cw)*n**(-1/3)
Ks = math.ceil((max(cw)-min(cw))/As)

# Amplitud de clases (precisión a décimas)

A = round((max(cw)-min(cw))/Ks, 1) + precision

# Establecer limites de intervalos

L1 = min(cw)-1/2*precision
L = [L1]
for lim in range(Ks+1):
    L.append(round(L1+A*(lim+1), 2))
L
X = []
for lim in range(len(L)-2):
    X.append(round((L[lim]+L[lim+1])/2, 1))
X

# Regla de la raíz

import pandas as pd
import numpy as np
import math

crabs = pd.read_table("../../../../r-basic/data/datacrab.txt", sep = " ")
cw = crabs.width
n = len(cw)
precision = 0.1

# Número de clases

Kr = math.ceil(math.sqrt(n))

# Amplitud de clases (precisión a décimas)

A = round((max(cw)-min(cw))/Kr, 1) + precision

# Establecer limites de intervalos

L1 = min(cw)-1/2*precision
L = [L1]
for lim in range(Kr+1):
    L.append(round(L1+A*(lim+1), 2))
L
X = []
for lim in range(len(L)-2):
    X.append(round((L[lim]+L[lim+1])/2, 1))
X

# Regla de Sturges

import pandas as pd
import numpy as np
import math

crabs = pd.read_table("../../../../r-basic/data/datacrab.txt", sep = " ")
cw = crabs.width
n = len(cw)
precision = 0.1

# Número de clases

KS = math.ceil(1+np.log2(n))

# Amplitud de clases (precisión a décimas)

A = round((max(cw)-min(cw))/KS, 1) + precision

# Establecer limites de intervalos
L1 = min(cw)-1/2*precision
L = [L1]
for lim in range(KS+1):
    L.append(round(L1+A*(lim+1), 2))
L
X = []
for lim in range(len(L)-2):
    X.append(round((L[lim]+L[lim+1])/2, 1))
X

# Regla de Freedman-Diaconis

import pandas as pd
import numpy as np
import math

crabs = pd.read_table("../../../../r-basic/data/datacrab.txt", sep = " ")
cw = crabs.width
n = len(cw)
precision = 0.1

# Número de clases

Afd = 2*(np.quantile(cw, 0.75)-np.quantile(cw, 0.25))*n**(-1/3)
Kfd = math.ceil((max(cw)-min(cw))/Afd)

# Amplitud de clases (precisión a décimas)
A = round((max(cw)-min(cw))/Kfd, 1) + precision

# Establecer limites de intervalos
L1 = min(cw)-1/2*precision
L = [L1]
for lim in range(Kfd+1):
    L.append(round(L1+A*(lim+1), 2))
L
X = []
for lim in range(len(L)-2):
    X.append(round((L[lim]+L[lim+1])/2, 1))
X

Curso estadistica descriptiva/tests/agrupacion de datos cuantitativos/adc.py


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

def loss_fn(X, Y, beta):
    return cp.norm2(cp.matmul(X, beta) - Y)**2

def regularizer(beta):
    return cp.norm1(beta)

def objective_fn(X, Y, beta, lambd):
    return loss_fn(X, Y, beta) + lambd * regularizer(beta)

def mse(X, Y, beta):
    return (1.0 / X.shape[0]) * loss_fn(X, Y, beta).value

def generate_data(m: int = 100, n: int = 20, sigma: int = 5, density: float = 0.2):
    "Generates data matrix X and observations Y."
    np.random.seed(1)
    beta_star = np.random.randn(n)
    idxs = np.random.choice(range(n), int((1-density)*n), replace=False)
    for idx in idxs:
        beta_star[idx] = 0
    X = np.random.randn(m,n)
    Y = X.dot(beta_star) + np.random.normal(0, sigma, size=m)
    return X, Y, beta_star

def plot_train_test_errors(train_errors, test_errors, lambd_values):
    plt.plot(lambd_values, train_errors, label="Train error")
    plt.plot(lambd_values, test_errors, label="Test error")
    plt.xscale("log")
    plt.legend(loc="upper left")
    plt.xlabel(r"$\lambda$", fontsize=16)
    plt.title("Mean Squared Error (MSE)")
    plt.show()

def plot_regularization_path(lambd_values, beta_values):
    num_coeffs = len(beta_values[0])
    for i in range(num_coeffs):
        plt.plot(lambd_values, [wi[i] for wi in beta_values])
    plt.xlabel(r"$\lambda$", fontsize=16)
    plt.xscale("log")
    plt.title("Regularization Path")
    plt.show()

if __name__ == "__main__":
    m = 100
    n = 20
    sigma = 5
    density = 0.2

    X, Y, _ = generate_data(m, n, sigma)
    X_train = X[:50, :]
    Y_train = Y[:50]
    X_test = X[50:, :]
    Y_test = Y[50:]

    beta = cp.Variable(n)
    lambd = cp.Parameter(nonneg=True)
    problem = cp.Problem(
                    cp.Minimize(objective_fn(X_train, Y_train, beta, lambd)))

    lambd_values = np.logspace(-2, 3, 50)
    train_errors = []
    test_errors = []
    beta_values = []
    for v in lambd_values:
        lambd.value = v
        problem.solve()
        train_errors.append(mse(X_train, Y_train, beta))
        test_errors.append(mse(X_test, Y_test, beta))
        beta_values.append(beta.value)

    plot_train_test_errors(train_errors, test_errors, lambd_values)
    plot_regularization_path(lambd_values, beta_values)


import numpy as np
import random
"""
D:\FluidSim\FluidSim\MultiGri\Poisson_FDM_Multigrid-master

https://github.com/EnigmaHuang/Poisson_FDM_Multigrid
"""
p = 4
n = 2**p - 1
N = n * n * n
A = np.zeros((N,N))
for k in range(n):
    for j in range(n):
        for i in range(n):
            idx = k*n*n + j*n + i
            A[idx,idx] = 6
            if i < n-1:
                A[idx,idx+1] = -1
            if i > 0:
                A[idx,idx-1] = -1
            if j < n-1:
                A[idx,idx+n] = -1
            if j > 0:
                A[idx,idx-n] = -1
            if k < n-1:
                A[idx,idx+n*n] = -1
            if k > 0:
                A[idx,idx-n*n] = -1

b = np.zeros((N))
directn = 3 * 3 * 3

def Gauss(Amat,bmat,xmat):
    eps = 1e-3 # 可允许误差范围很大
    err = 1
    size = Amat.shape[0]
    while(err > eps):
        err = 0
        for i in range(size):
            summ = 0
            for j in range(size):
                summ += Amat[i,j] * xmat[j]
            xmat[i] = xmat[i] + 0.1 * (bmat[i] - summ)
            err = max(err,bmat[i] - summ)
            test = 1
        # print('err = %f',err)
    return xmat

def MultiGri(size,Amat,bmat,xmat):
    if size < 27:
        xmat = np.dot(np.linalg.inv(Amat),bmat)
        return xmat
    xmat = Gauss(Amat,bmat,xmat)
    fine_n = size
    fine_N = fine_n * fine_n * fine_n
    coarse_n = int(np.floor((fine_n-1)/2))
    coarse_N = coarse_n * coarse_n * coarse_n
    R = np.zeros((coarse_N,fine_N))
    k = 0
    for k in range(1,fine_n,2):
        for j in range(1,fine_n,2):
            for i in range(1,fine_n,2):

                s1 = size
                s2 = size * size

                idx = k * s2 + j * s1 + i

                # z = 0

                R[k,idx-s1-1] = 1/32
                R[k,idx-s1] = 1/16
                R[k,idx-s1+1] = 1/32

                R[k,idx-1] = 1/16
                R[k,idx] = 1/8
                R[k,idx+1] = 1/16

                R[k,idx+s1-1] = 1/32
                R[k,idx+s1] = 1/16
                R[k,idx+s1+1] = 1/32

                # z = -1

                R[k,idx-s2-s1-1] = 1/64
                R[k,idx-s2-s1] = 1/32
                R[k,idx-s2-s1+1] = 1/64

                R[k,idx-s2-1] = 1/32
                R[k,idx-s2] = 1/16
                R[k,idx-s2+1] = 1/32

                R[k,idx-s2+s1-1] = 1/64
                R[k,idx-s2+s1] = 1/32
                R[k,idx-s2+s1+1] = 1/64

                # z = +1
                R[k,idx+s2-s1-1] = 1/64
                R[k,idx+s2-s1] = 1/32
                R[k,idx+s2-s1+1] = 1/64

                R[k,idx+s2-1] = 1/32
                R[k,idx+s2] = 1/16
                R[k,idx+s2+1] = 1/32

                R[k,idx+s2+s1-1] = 1/64
                R[k,idx+s2+s1] = 1/32
                R[k,idx+s2+s1+1] = 1/64

    coarse_A = np.dot(np.dot(R,Amat),8*np.transpose(R))

    P = np.transpose(R) * 8

    r = bmat - np.dot(Amat,xmat)
    coarse_b = np.dot(R,r)
    x0 = np.zeros((coarse_N))
    d_x = MultiGri(coarse_N, coarse_A, coarse_b,x0)

    xmat = xmat + np.dot(P,d_x)
    xmat = Gauss(Amat, bmat,xmat)

    return xmat

for i in range(n):
    b[i] = 1

exactAinv = np.linalg.inv(A)
exactx = np.dot(exactAinv,b)

eps = 0.0028
norm = 1
x = np.zeros((N))
while(norm > eps):
    x = MultiGri(n,A,b,x)
    r = b - np.dot(A,x)
    norm = np.linalg.norm(r)
    print('norm = %f',norm)


import numpy as np
from decimal import Decimal

def random_uniform_ring(center: np.ndarray, outer_radius: float = 1.0, inner_radius: float = 0,
                        n_samples: int = 1) -> np.ndarray:
    """Generate point uniformly distributed in a ring.

    center is a 1D vector whose entries are the coordinates of the center point in the respective dimension

    Taken from
    https://stackoverflow.com/questions/47472123/sample-uniformly-in-a-multidimensional-ring-without-rejection
    """
    n_dims = len(center)
    samples = np.random.normal(size=(n_samples, n_dims))
    samples /= np.linalg.norm(samples, axis=1)[:, np.newaxis]  # push samples to unit sphere
    # Using the inverse cdf method
    u = np.random.uniform(size=n_samples)
    # This is inverse the cdf of ring volume as a function of radius
    sc = (u * (outer_radius ** n_dims - inner_radius ** n_dims) + inner_radius ** n_dims) ** (1 / n_dims)
    samples = samples * sc[:, None] + center
    return samples.astype(np.float)


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

"""
Make a histogram using a logarithmic scale on X axis

See:

- http://stackoverflow.com/questions/6855710/how-to-have-logarithmic-bins-in-a-python-histogram
"""

import numpy as np
import matplotlib.pyplot as plt

# SETUP #######################################################################

# histtype : [‘bar’ | ‘barstacked’ | ‘step’ | ‘stepfilled’]
HIST_TYPE='bar'
ALPHA=0.5

# MAKE DATA ###################################################################

data = np.random.exponential(size=1000000)
#data = np.abs(np.random.normal(size=1000000) * 10000.)
#data = np.random.chisquare(10, size=1000000)

# INIT FIGURE #################################################################

fig = plt.figure(figsize=(8.0, 6.0))

# AX1 #########################################################################

ax1 = fig.add_subplot(211)

res_tuple = ax1.hist(data,
                     bins=50,
                     histtype=HIST_TYPE,
                     alpha=ALPHA)

ax1.set_title("Normal scale")
ax1.set_xlabel("Value")
ax1.set_ylabel("Count")

# AX2 #########################################################################

ax2 = fig.add_subplot(212)

vmin = np.log10(data.min())
vmax = np.log10(data.max())
bins = np.logspace(vmin, vmax, 50)  # <- make a range from 10**vmin to 10**vmax

print(bins)

res_tuple = ax2.hist(data,
                     bins=bins,
                     histtype=HIST_TYPE,
                     alpha=ALPHA)

ax2.set_xscale("log")               # <- Activate log scale on X axis

ax2.set_title("Log scale")
ax2.set_xlabel("Value")
ax2.set_ylabel("Count")

# SHOW AND SAVE FILE ##########################################################

plt.tight_layout()

plt.savefig("hist_logscale_x.png")
plt.show()


import numpy as np
import scipy as sp
import matplotlib.pyplot as plt
import scipy.linalg as la
from scipy.linalg import svd, norm

def svd_approx(A, k):
    '''
    Calculate the best rank k approximation to A with respect to the induced
    2-norm. Use the SVD.
    Inputs:
        A -- array of shape (m,n)
        k -- positive integer
    Returns:
        Ahat -- best rank k approximation to A obtained via the SVD
    '''
    #compute the reduced SVD
    U,s,Vh = svd(A,full_matrices=False)

    #keep only the first k singular values
    S = np.diag(s[:k])

    #reconstruct the best rank k approximation
    return U[:,:k].dot(S).dot(Vt[:k,:])

def lowest_rank_approx(A,e):
    '''
    Calculate the lowest rank approximation to A that has error stricly less than e.
    Inputs:
        A -- array of shape (m,n)
        e -- positive floating point number
    Returns:
        Ahat -- the best rank s approximation of A constrained to have error less than e,
                where s is as small as possible.
    '''
    #calculate the reduced SVD
    U,s,Vh = svd(A,full_matrices=False)

    #find the index of the first singular value less than e
    k = np.where(s<e)[0][0]

    #now recreate the rank k approximation
    S = np.diag(s[:k])
    return U[:,:k].dot(S).dot(Vt[:k,:])

def readimg(filename, channel=None):
    if channel is not None:
        return sp.misc.imread(filename)[:,:,channel]
    else:
        return sp.misc.imread(filename)

def compressSVD(filename, rank, random=False, channel=None):
    img = readimg(filename, channel)

    try:
        isize = img[:,:,0].shape
        colors = [la.svd(img[:,:,i]) for i in range(3)]
    except IndexError:
        isize = img.shape
        plt.gray()
        colors = la.svd(img)

    plt.ion()
    imgc = plt.imshow(img)
    newimg = sp.zeros_like(img)

    rank = range(1,rank+1)

    if random is True:
        sp.random.shuffle(rank)

    for r in rank:
        col_res = hat(colors, r-1, r)
        try:
            #col_res[0] = sp.where(col_res[0]>255, col_res[0], 255)
            #col_res[1] = sp.where(col_res[1]>255, col_res[1], 255)
            #col_res[2] = sp.where(col_res[2]>255, col_res[2], 255)

            #col_res[0] = sp.where(col_res[0]<0, col_res[0], 0)
            #col_res[1] = sp.where(col_res[1]<0, col_res[1], 0)
            #col_res[2] = sp.where(col_res[2]<0, col_res[2], 0)

            newimg[:,:,0] += col_res[0]
            newimg[:,:,1] += col_res[1]
            newimg[:,:,2] += col_res[2]

            ## for ch in range(3):
            ##     newimg[newimg[:,:,ch]<1]=0
            ##     newimg[newimg[:,:,ch]>254]=255

        except IndexError:
            newimg += col_res[0]
            ## newimg[newimg<1]=0
            ## newimg[newimg>254]=255

        imgc.set_data(newimg)
        plt.draw()
    plt.ioff()
    plt.show()

    return newimg

def hat(color_svd, lrank, urank):
    results = []
    if len(color_svd) == 3:
        r = 3
    else:
        r = 1

    for c in range(r):
        U = color_svd[c][0]
        S = sp.diag(color_svd[c][1])
        Vt = color_svd[c][2]
        results.append(U[:,lrank:urank].dot(S[lrank:urank, lrank:urank]).dot(Vt[lrank:urank,:]))

    return results

def matrix_rank(X):
    """Compute the rank of a matrix using the SVD"""

    S = la.svd(X, compute_uv=False)
    tol = S.max()*sp.finfo(S.dtype).eps
    return sum(S>tol)


import numpy as np
alist = [1, 2, 3, 4, 5]
narray = np.array([1, 2, 3, 4])

# print(alist)
# print(narray)
# print(type(alist))
# print(type(narray))

# print(narray + narray)
# print(alist + alist)

# print("\n", narray * 3)
# print(alist * 3)

# npmatrix1 = np.array([narray, narray, narray])
# npmatrix2 = np.array([alist, alist, alist])
# npmatrix3 = np.array([narray, [1, 1, 1, 1], narray])

# print(npmatrix1, "\n")
# print(npmatrix2, "\n")
# print(npmatrix3, "\n")

okmatrix = np.array([[1, 2], [3, 4]])
# print(okmatrix)
# print(okmatrix * 2)

# badmatrix = np.array([[1, 2], [3, 4], [5, 6, 7]])
# print("\n", badmatrix)
# print(badmatrix * 2)

# result = okmatrix * 2 + 1
# print(result, "\n")

# result1 = okmatrix + okmatrix
# print(result1, "\n")

# result2 = okmatrix - okmatrix
# print(result2, "\n")

# result = okmatrix * okmatrix
# print(result)

# matrix3x2 = np.array([[1, 2], [3, 4], [5, 6]])
# print('Original matrix 3 x 2 ')
# print(matrix3x2)
# print('Transposed matrix 2 x 3')
# print(matrix3x2.T)

# nparray = np.array([1, 2, 3, 4])
# print('Original array')
# print(nparray)
# print('Transposed array')
# print(nparray.T)

# nparray = np.array([[1, 2, 3, 4]])
# print('Original array')
# print(nparray)
# print('Transposed array')
# print(nparray.T)

# nparray1 = np.array([1, 2, 3, 4])
# norm1 = np.linalg.norm(nparray1)
# nparray2 = np.array([[1, 2], [3, 4]])
# norm2 = np.linalg.norm(nparray2)
# print(norm1)
# print(norm2)

# nparray2 = np.array([[1, 1], [2, 2], [3, 3]])
# normByCols = np.linalg.norm(nparray2, axis=0)
# normByRows = np.linalg.norm(nparray2, axis=1)
# print(normByCols)
# print(normByRows)

# nparray1 = np.array([0, 1, 2, 3]) # Define an array
# nparray2 = np.array([4, 5, 6, 7]) # Define an array
# flavor1 = np.dot(nparray1, nparray2) # Way-1
# print(flavor1)
# flavor2 = np.sum(nparray1 * nparray2) # Way-2
# print(flavor2)
# flavor3 = nparray1 @ nparray2 # Way-3
# print(flavor3)
# # As you never should do: #Way-4
# flavor4 = 0
# for a, b in zip(nparray1, nparray2):
#     flavor4 += a * b
# print(flavor4)

# norm1 = np.dot(np.array([1, 2]), np.array([3, 4])) # Dot product on nparrays
# norm2 = np.dot([1, 2], [3, 4]) # Dot product on python lists
# print(norm1, '=', norm2 )

# nparray2 = np.array([[1, -1], [2, -2], [3, -3]]) # Define a 3 x 2 matrix.
# sumByCols = np.sum(nparray2, axis=0) # Get the sum for each column. Returns 2 elements
# sumByRows = np.sum(nparray2, axis=1) # get the sum for each row. Returns 3 elements
# print('Sum by columns: ')
# print(sumByCols)
# print('Sum by rows:')
# print(sumByRows)

# nparray2 = np.array([[1, -1], [2, -2], [3, -3]]) # Define a 3 x 2 matrix. Chosen to be a matrix with 0 mean
# mean = np.mean(nparray2) # Get the mean for the whole matrix
# meanByCols = np.mean(nparray2, axis=0) # Get the mean for each column. Returns 2 elements
# meanByRows = np.mean(nparray2, axis=1) # get the mean for each row. Returns 3 elements
# print('Matrix mean: ')
# print(mean)
# print('Mean by columns: ')
# print(meanByCols)
# print('Mean by rows:')
# print(meanByRows)

# nparray2 = np.array([[1, 1], [2, 2], [3, 3]]) # Define a 3 x 2 matrix.
# nparrayCentered = nparray2 - np.mean(nparray2, axis=0) # Remove the mean for each column
# print('Original matrix')
# print(nparray2)
# print('Centered by columns matrix')
# print(nparrayCentered)
# print('New mean by column')
# print(nparrayCentered.mean(axis=0))

nparray2 = np.array([[1, 3], [2, 4], [3, 5]]) # Define a 3 x 2 matrix.
nparrayCentered = nparray2.T - np.mean(nparray2, axis=1) # Remove the mean for each row
nparrayCentered = nparrayCentered.T # Transpose back the result
print('Original matrix')
print(nparray2)
print('Centered by columns matrix')
print(nparrayCentered)
print('New mean by rows')
print(nparrayCentered.mean(axis=1))


'''
Exercise 1 (3 points).
Implement a function, solve_neq(X, y) that implements Algorithm 1. It should return a Numpy vector containing the model parameter estimates.

Recall the steps of the algorithm as previously outlined:

1.Form the Gramian of  X ,  C≡XTX .
2.Form  b≡XTy .
3.Solve  Cθ∗=b  for  θ∗ .
'''

def solve_neq(X, y):
    C = X.T.dot(X)
    b = X.T.dot(y)
    theta_est = sp.linalg.solve(C, b, sym_pos=True)
    return theta_est

theta_neq = solve_neq(X, y)

print("Your implementation's solution versus the true solution:")
show_2vecs_tibble(theta_neq, theta_true, xname='theta_neq', yname='theta_true', error=True)

'''
Exercise 2
(1 point). Write a function to calculate the residual norm,  ∥r∥2=∥Xθ∗−y∥2 .
'''

def calc_residual_norm(X, y, theta):
    ### BEGIN SOLUTION
    from numpy.linalg import norm
    return norm(X.dot(theta) - y, ord=2)
    ### END SOLUTION

r_norm_neq = calc_residual_norm(X, y, theta_neq)
print("\nThe squared residual norm:", r_norm_neq)

'''
Exercise 4
(2 points). Implement a function that returns an  m×n  matrix whose entries are uniformly randomly distributed in the interval,  [0,ϵ]  for a given value of  ϵ .
'''

def random_mat (m, n, eps):

    return np.random.random((m, n)) * eps

print(random_mat(3, 2, 1e-3))

'''
Exercise 5 (2 points).
Use your random_mat() function to write another function, perturb_system(X, y, eps), that creates two "perturbations" to the system defined by X and y.

Let  ΔX  be the first perturbation. It should have the same dimensions as  X , and its entries should lie in the interval  [−ϵ,ϵ] . The value of  ϵ  is given by eps.
The second is  Δy , a small perturbation to the response variable,  y . Its entries should also lie in the same interval,  [−ϵ,ϵ] ,
Your function should return a perturbed system,  X+ΔX  and  y+Δy , as a pair.
'''

def perturb_system(X, y, eps):

    Delta_X = random_mat(X.shape[0], X.shape[1], 2*eps) - eps
    Delta_y = random_mat(y.shape[0], y.shape[1], 2*eps) - eps
    return X + Delta_X, y + Delta_y

EPSILON = 0.1
X_perturbed, y_perturbed = perturb_system(X, y, EPSILON)

Delta_X = X_perturbed - X
Delta_y = y_perturbed - y
display(Math(r'\Delta X = {}, \quad \Delta y = {}'.format(nparray_to_bmatrix(Delta_X[:5, :]),
                                                          nparray_to_bmatrix(Delta_y[:5]))))

EdX/GTx: CSE6040x: FA18 - Computing for Data Analysis/Module 2: The analysis of data/Topic 12: Linear regression/Part_2.py


import math
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

from gradient_descent import GradientDescent

def sigmoid(x):
    a = []
    for item in x:
        a.append(1/(1+math.exp(-item)))
    return a

def plot_sigmoid():
    x = np.arange(-10, 10, 0.2)
    sig = sigmoid(x)
    fig, ax = plt.subplots()
    ax.plot(x, sig, label='g(z) = 1 / 1 - x^-z')
    plt.legend()
    ax.set(xlabel='z', ylabel='g(z)')
    ax.grid()

    plt.show()

def log(x):
    a = []
    b = []
    for item in x:
        a.append(-math.log(item))
        b.append(-math.log(1-item))
    return a, b

def plot_log():
    x = np.arange(0.01, 1, 0.01)
    f1, f2 = log(x)
    fig, ax = plt.subplots()
    ax.plot(x, f1, label='J(\u03B8) = - log( h(x) )')
    ax.plot(x, f2, label='J(\u03B8) = - log( 1 - h(x) )')
    plt.legend()
    ax.set(xlabel='h(x)', ylabel='J(\u03B8)')
    ax.grid()

    plt.show()

def load_data(path, header):
    df = pd.read_csv(path, header=header)
    return df

def accuracy(y_predicted, actual_classes, probab_threshold=0.5):
    predicted_classes = ( y_predicted >=
                         probab_threshold).astype(int)
    predicted_classes = predicted_classes.flatten()
    accuracy = np.mean(predicted_classes == actual_classes)
    return accuracy * 100

def raw_plot(malignant, benign):
    plt.scatter(malignant.iloc[:, 1], malignant.iloc[:, 2], s=10, label='Malignant')
    plt.scatter(benign.iloc[:, 1], benign.iloc[:, 2], s=10, label='Benign')
    plt.legend()
    plt.xlabel('Mean Area')
    plt.ylabel('Mean Compactness')
    plt.show()

def plot_decision_boundary(parameters, malignant, benign):
    x_values = [0.1, 0.4]
    y_values = - (parameters[0] + np.dot(parameters[1], x_values)) / parameters[2]

    plt.scatter(malignant.iloc[:, 1], malignant.iloc[:, 2], s=10, label='Malignant')
    plt.scatter(benign.iloc[:, 1], benign.iloc[:, 2], s=10, label='Benign')
    plt.plot(x_values, y_values, label='Decision Boundary')
    plt.xlabel('Mean Area')
    plt.ylabel('Mean Compactness')
    plt.legend()
    plt.show()

def get_data_frame():
    df = pd.read_csv('breast cancer.csv',
                     delimiter=',',
                     names=["id", "diagnosis", "radius_mean", "texture_mean", "perimeter_mean",
                            "area_mean", "smoothness_mean", "compactness_mean", "concavity_mean",
                            "concave points_mean", "symmetry_mean", "fractal_dimension_mean", "radius_se",
                            "texture_se", "perimeter_se", "area_se", "smoothness_se", "compactness_se",
                            "concavity_se", "concave points_se", "symmetry_se", "fractal_dimension_se",
                            "radius_worst", "texture_worst", "perimeter_worst", "area_worst", "smoothness_worst",
                            "compactness_worst", "concavity_worst", "concave points_worst", "symmetry_worst",
                            "fractal_dimension_worst", ])

    df["diagnosis"] = df["diagnosis"].replace('M', 1)
    df["diagnosis"] = df["diagnosis"].replace('B', 0)

    df.insert(1, 'Theta 0', 1)
    X = df.iloc[:, [1, 6, 7]]
    # X = df.iloc[:, :]

    X['area_mean'] = X['area_mean'] / np.max(X['area_mean'])
    X['smoothness_mean'] = X['smoothness_mean'] / np.max(X['smoothness_mean'])

    # y = target values, last column of the data frame
    y = df.iloc[:, 2]

    # filter out the applicants that got admitted
    malignant = X.loc[y == 1]

    # filter out the applicants that din't get admission
    benign = X.loc[y == 0]

    return X, y, malignant, benign

if __name__ == "__main__":
    plot_sigmoid()
    plot_log()

    X, y, malignant, benign = get_data_frame()

    raw_plot(malignant, benign)

    regression_model = GradientDescent()
    model = regression_model.fit(X, y)
    y_predicted = regression_model.predict(X)

    plot_decision_boundary(model.theta, malignant, benign)

    print('Parameters', model.theta)
    print('Accuracy', accuracy(y_predicted, y))


import numpy as np

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

def sigmoidDerivative(x):
    return sigmoid(x)*(1.0-sigmoid(x))

def tanh(x):
    return np.tanh(x)

def tanhDerivative(x):
    return 1.0 - x**2

class NeuralNetwork:

    def __init__(self,layers,activeFn = 'sigmoid'):
        """layers is list of layer lengths"""
        if activeFn == 'tanh':
            self.activeFn = tanh
            self.activeFnDerivative = tanhDerivative
        elif activeFn == 'sigmoid':
            self.activeFn = sigmoid
            self.activeFnDerivative = sigmoidDerivative
        self.layers = layers
        self.theta = []
        for i in range(1,len(layers)):
            if i != len(layers)-1:
                weight_matrix = 2*np.random.rand(layers[i-1] + 1, layers[i] + 1) -1
                #np.random.uniform(low = -1, high = 1, size=(layers[i-1]+1,layers[i]+1))
            else:
                weight_matrix = 2*np.random.rand(layers[i-1] + 1, layers[i]) -1
            #print('weight_matrix = ' +str(weight_matrix))
            self.theta.append(weight_matrix)

    def trainBatch(self,X,y,learning_rate=1.0,intervals = 100):
        pass
    def predict(self,x):
        return self.forwardProp(x)[-1]
    def train(self,x,y,learning_rate=1.0):
        a = self.forwardProp(x)
        #print('a = ' + str(a))
        self.backProp(a,y,learning_rate)

    def forwardProp(self,x):
        x = np.array(x)
        a = [np.append([1],x)]

        for l in range(len(self.theta)):
            #print('a['+str(l)+'] = ' + str(a[l]))
            #print('self.theta['+str(l)+'] = ' + str(self.theta[l]))
            inner = np.dot(a[l],self.theta[l])
            a.append( self.activeFn( inner) )
        return a
    def backProp(self,a,y,learning_rate):
        #StartHere
        y = np.array(y)
        delta = y - a[-1]
        deltas = [delta * self.activeFnDerivative(a[-1])]

        for layer in range(len(a)-2,0,-1):
            #print('deltas['+str(-1)+'] = ' + str(deltas[-1]))
            #print('self.theta['+str(layer)+'].T = ' + str(self.theta[layer].T))
            #print('self.theta['+str(l)'].T = ' + str(self.theta[l].T))
            deltas.append(deltas[-1].dot(self.theta[layer].T)*self.activeFnDerivative(a[layer]))
        deltas.reverse()
        #print('deltas = ' + str(deltas))

        for i in range(len(self.theta)):
            active_layer = np.atleast_2d(a[i])
            delta = np.atleast_2d(deltas[i])
            self.theta[i] += learning_rate * active_layer.T.dot(delta)

def main():
    #net = NeuralNetwork([2,2,1])
    #net.train([1,0],[1])
    neuralOrTest()

def neuralOrTest():
    net = NeuralNetwork([2,2,1],'tanh')
    #print('Initial Weights: ')
    #net.printWeights()
    #print('----')
    X = [[0,0],[1,0],[0,1],[1,1]];
    Y = [[0],[1],[1],[0]]
    J = []
    for i in range(10000):
        for j in range(len(Y)):
            net.train(X[j],Y[j])
            #J.append( net.lossFunction(X[j],Y[j]))
    for i in range(len(Y)):
        y_out = net.predict(X[i])
        print('Test Case: ' + str(X[i]) + ', Result: ' + str(y_out) )
    #print()
    #print('----')
    #print('Final Weights: ')
    #net.printWeights()
    #plt.plot(J)
    #plt.ylabel('Cost')
    #plt.xlabel('Training Sample')
    #plt.show()

if __name__ == '__main__':
    main()


# -*- coding: utf-8 -*-
"""
Created on Tue Jul 10 10:51:18 2018

@author: olmer.garciab
"""

# implementing class example

P=[[0,0.5,0,0,0.,0.5,0],
   [0,0,0.8,0,0.0,0,0.2],
   [0,0.,0,0.6,0.4,0,0],
   [0,0.,0,0,0.,0,1],
   [0.2,0.4,0.4,0,0,0,0],
   [0.1,0.,0,0,0.,0.9,0],
   [0,0.,0,0,0.,0,1]]
R=[-2,-2,-2,10,1,-1,0]

# total dicount reward
def G_t(S,R,gamma):
    g=0
    for s,i in zip(S,range(len(S))):
        g=g+R[s]*gamma**i
    return g

#g = lambda y: sum(  f(y) for f in (lambda x: x**i for i in range(n))  )
# for example for the chain of state S_1
gamma=0.5
S_1=[0,1,2,3,6]
print(G_t(S_1,R,gamma))

#dynamic programming
#based in #https://harderchoices.com/2018/02/26/dynamic-programming-in-python-reinforcement-learning/
def iterative_value_function(N, theta=0.0001, gamma=0.9):
    V_s =R.copy() # 1.
    probablitiy_map = P # 2.
    delta = 100 # 3.
    while not delta < theta: # 4.
        delta = 0 # 5.
        for state in range(0,N): # 6.
            v = V_s[state] # 7.

            total =R[state] # 8.
            for state_prime in range(0,N):
                total += probablitiy_map[state][state_prime] * (gamma * V_s[state_prime])
                #print(total)

            V_s[state] =total # 9.
            delta = max(delta, abs(v - V_s[state])) # 10.
            #print(delta)
    V_s=[round(v,2) for v in V_s]
    return V_s # 11.

N=len(R)

print('gamma',0.9,iterative_value_function(N,gamma=0.9))
print('gamma',1,iterative_value_function(N,gamma=1))
print('gamma',0,iterative_value_function(N,gamma=0))

#vectorial way
import numpy as np
from numpy.linalg import inv
gamma=0.9
P=np.array(P)
R=np.array(R).reshape((-1,1))
v=np.matmul(inv(np.eye(N)-gamma*P),R)
print(v)


# coding: utf-8

# In[1]:

get_ipython().magic('matplotlib inline')

# In[2]:

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

# El objetivo de este ejemplo es encontrar el máximo de la función $f(x) = x \, \sin(4x \, - \, 2$), evaluada dentro del intervalo $[-2, 3]$. Supongamos que los números con los que vamos a trabajar los queremos representar con una presición de dos dígitos decimales (es decir, no habrán valores representables entre 0 y 0.01, por ejemplo).
#
# Para comenzar, definimos la función en cuestión:

# In[3]:

maximo = 3
minimo = -2
prec = 0.01

def func(x):
    return x * np.sin(4 * x - 2)

# En este caso, que es un problema ficticio, podemos graficar la función y observar donde se encuentra el máximo.
#
# O mejor, aplicando análisis matemático, podemos calcular la derivada de la función y calcular sus ceros, para luego ver cual de ellos es el máximo global.
#
# $\frac{df(x)}{dx} = \frac{d \left( x\,\sin(4x\,-\,2) \right)}{dx} = 4x\,\cos(2\,-\,4x)\,-\,\sin(2\,-\,4x) = 0$
#
# Esto permite estimar el máximo en $ x \approx 2.48852668729269 $

# In[4]:

X = np.arange(minimo, maximo, prec)
plt.plot(X, func(X))

maximo_x = 2.48852668729269
plt.plot(maximo_x, func(maximo_x), 'or')

# Una posible heurística que podemos usar es la denominada *Hill Climbing*. Para usarla, debemos definir tres cuestiones fundamentales:
#
# 1. Cómo se van a representar las potenciales soluciones al problema y de que manera se podrán generar soluciones al azar.
#
# 2. Cómo calificar una soluciones potencial, para comparar dos soluciones representadas y poder determinar cual de las dos es mejor que la otra intentando resolver el problema (aunque ninguna de las dos realmente lo resuelva).
#
# 3. Cómo generar a partir de una solución dada un conjunto de nuevas soluciones vecinas a esta, lo que implica definir además una métrica de distancia entre soluciones.
#
# Pero primero, implementamos el algoritmo:

# In[6]:

def hill_climb(solucion_inicial, evaluacion, obtener_vecinos):
    """
    Hill climbing determinístico.
    """
    soluciones_evaluadas = 1
    solucion_actual = solucion_inicial
    evaluacion_actual = evaluacion(solucion_actual)

    optimo_local = False
    while not optimo_local:
        vecinos = obtener_vecinos(solucion_actual)
        optimo_local = True
        for vecino in vecinos:
            evaluacion_vecino = evaluacion(vecino)
            soluciones_evaluadas += 1
            if evaluacion_vecino > evaluacion_actual:
                solucion_actual = vecino
                evaluacion_actual = evaluacion_vecino
                optimo_local = False

    return solucion_actual, soluciones_evaluadas

# Si a las soluciones se las representan como valores en punto flotante, puede generarse una solución al azar usando la función `random()` y escalando el valor al intervalo de trabajo, redondeando con `round()` a la cantidad de decimales que define la precisión.

# In[7]:

def nueva():
    return round(minimo + random.random() * (maximo - minimo), 2)

# In[8]:

print(nueva(), nueva(), nueva(), nueva(), nueva(), nueva(), sep='    ')

# Para evaluar una solución, podemos usar directamente la función que queremos maximizar ya que si la calificación de un punto será el valor de la función evaluada en ese punto, cuanto mayor sea mejor será resolviendo el problema.

# In[12]:

def evaluacion(x):
    return func(x)

# Para generar soluciones dentro de la vecindad de una solución dada, simplemente generamos 4 nuevas soluciones: $[ x-0.03, x-0.01, x+0.01, x+0.03]$. Estas cuatro soluciones tienen la particularidad que se encuentran dentro del entorno $(x \pm 0.05)$ y son valores representados con la precisión deseada.
#
# Es importante tomar la precaución de no generar soluciones fuera del rango permitido. Por ejemplo, si $x=3$, no deberían generarse soluciones mayores que x pues se encontrarían fuera del rango fijado para el problema.

# In[13]:

def vecinos(x):
    v = []
    d = -0.03
    for i in range(4):
        n = x + d
        if n >= minimo and n <= maximo:
            v.append(n)
        d += 0.02
    return v

# In[14]:

print(vecinos(0.5), vecinos(1), vecinos(minimo), vecinos(maximo), sep="\n")

# Finalmente, podemos invocar al algoritmo generando una solución inicial al azar y pasando la función de evaluación y la función generadora de vecinos:

# In[18]:

inicial = nueva()
mejor, evaluaciones = hill_climb(inicial, evaluacion, vecinos)

# Podemos ver el resultado de la ejecución junto con la cantidad de soluciones que tuvieron que evaluarse para conseguirla:

# In[19]:

plt.plot(X, func(X),
         maximo_x, func(maximo_x), 'or',  # Punto rojo: Máximo de la función
         inicial, func(inicial), 'oy',    # Punto amarillo: Solución inicial
         mejor, func(mejor), 'ob')        # Punto azul: Mejor solución encontrada

print("Cantidad de soluciones evaluadas:", evaluaciones)
print("Solución inicial:", inicial)
print("Solución obtenida:", mejor)
print("Valor de func para esa solución:", func(mejor))

# Si repetimos varias veces el algoritmo:

# In[13]:

plt.plot(X, func(X))

for n in range(10):
    i = nueva()
    m, _ = hill_climb(i, evaluacion, vecinos)
    plt.plot(i, func(i), 'oy', m, func(m), 'ob')

plt.plot(maximo_x, func(maximo_x), 'or')

# In[20]:

def evaluacion_para_minimo(x):
    return -func(x)

# In[21]:

plt.plot(X, -func(X))

# In[30]:

inicial = nueva()
mejor, evaluaciones = hill_climb(inicial, evaluacion_para_minimo, vecinos)

plt.plot(X, func(X),
         maximo_x, func(maximo_x), 'or',  # Punto rojo: Máximo de la función
         inicial, func(inicial), 'oy',    # Punto amarillo: Solución inicial
         mejor, func(mejor), 'ob')        # Punto azul: Mejor solución encontrada

print("Cantidad de soluciones evaluadas:", evaluaciones)
print("Solución inicial:", inicial)
print("Solución obtenida:", mejor)
print("Valor de func para esa solución:", func(mejor))

# In[ ]:

Argentina - Mondiola Rock - 90 pts/Practica/Hill Climbing/Hill Climbing.py

Argentina - Mondiola Rock - 90 pts/Practica/TP2/Implementaciones de German/Hill Climbing/Hill Climbing.py


import numpy as np
import pandas as pd
import scipy
import statsmodels

"""
In this optional exercise, you should complete the function called
predictions(turnstile_weather). This function takes in our pandas
turnstile weather dataframe, and returns a set of predicted ridership values,
based on the other information in the dataframe.

In exercise 3.5 we used Gradient Descent in order to compute the coefficients
theta used for the ridership prediction. Here you should attempt to implement
another way of computing the coeffcients theta. You may also try using a reference implementation such as:
http://statsmodels.sourceforge.net/devel/generated/statsmodels.regression.linear_model.OLS.html

One of the advantages of the statsmodels implementation is that it gives you
easy access to the values of the coefficients theta. This can help you infer relationships
between variables in the dataset.

You may also experiment with polynomial terms as part of the input variables.

The following links might be useful:
http://en.wikipedia.org/wiki/Ordinary_least_squares
http://en.wikipedia.org/w/index.php?title=Linear_least_squares_(mathematics)
http://en.wikipedia.org/wiki/Polynomial_regression

This is your playground. Go wild!

How does your choice of linear regression compare to linear regression
with gradient descent computed in Exercise 3.5?

You can look at the information contained in the turnstile_weather dataframe below:
https://www.dropbox.com/s/meyki2wl9xfa7yk/turnstile_data_master_with_weather.csv

Note: due to the memory and CPU limitation of our amazon EC2 instance, we will
give you a random subset (~10%) of the data contained in turnstile_data_master_with_weather.csv

If you receive a "server has encountered an error" message, that means you are hitting
the 30 second limit that's placed on running your program. See if you can optimize your code so it
runs faster.
"""

def predictions(weather_turnstile):

    dummy_units = pd.get_dummies(weather_turnstile['UNIT'], prefix='unit')
    features = weather_turnstile[['rain', 'Hour', 'mintempi', 'maxtempi', 'meanwindspdi','fog','precipi']].join(dummy_units)
    values = weather_turnstile[['ENTRIESn_hourly']]
    m = len(values)

    """
    Linear Regression with statsmodels:
    R^2 value is: 0.485414650479
    """
    model = sm.OLS(values, features)
    results = model.fit()
    theta = results.params
    prediction = results.fittedvalues
    #print theta, results.rsquared

    return prediction

def compute_r_squared(data, predictions):
    SST = ((data-np.mean(data))**2).sum()
    SSReg = ((predictions-np.mean(data))**2).sum()
    r_squared = SSReg / SST

    return r_squared

if __name__ == "__main__":
    input_filename = "turnstile_data_master_with_weather.csv"
    turnstile_master = pd.read_csv(input_filename)
    predicted_values = predictions(turnstile_master)
    r_squared = compute_r_squared(turnstile_master['ENTRIESn_hourly'], predicted_values)

    print r_squared

import numpy,import scipy,import statsmodels


import numpy as np
from numpy.linalg import svd

def rank_est(A, atol=1e-13, rtol=0):
    """
    Estimate the rank (i.e. the dimension of the nullspace) of a matrix.

    The algorithm used by this function is based on the singular value
    decomposition of `A`.

    Parameters
    ----------
    A : array_like(float, ndim=1 or 2)
        A should be at most 2-D.  A 1-D array with length n will be
        treated as a 2-D with shape (1, n)
    atol : scalar(float), optional(default=1e-13)
        The absolute tolerance for a zero singular value.  Singular
        values smaller than `atol` are considered to be zero.
    rtol : scalar(float), optional(default=0)
        The relative tolerance.  Singular values less than rtol*smax are
        considered to be zero, where smax is the largest singular value.

    Returns
    -------
    r : scalar(int)
        The estimated rank of the matrix.

    Note: If both `atol` and `rtol` are positive, the combined tolerance
    is the maximum of the two; that is:

        tol = max(atol, rtol * smax)

    Note: Singular values smaller than `tol` are considered to be zero.

    See also
    --------
    numpy.linalg.matrix_rank
        matrix_rank is basically the same as this function, but it does
        not provide the option of the absolute tolerance.

    """

    A = np.atleast_2d(A)
    s = svd(A, compute_uv=False)
    tol = max(atol, rtol * s[0])
    rank = int((s >= tol).sum())

    return rank

def nullspace(A, atol=1e-13, rtol=0):
    """
    Compute an approximate basis for the nullspace of A.

    The algorithm used by this function is based on the singular value
    decomposition of `A`.

    Parameters
    ----------
    A : array_like(float, ndim=1 or 2)
        A should be at most 2-D.  A 1-D array with length k will be
        treated as a 2-D with shape (1, k)
    atol : scalar(float), optional(default=1e-13)
        The absolute tolerance for a zero singular value.  Singular
        values smaller than `atol` are considered to be zero.
    rtol : scalar(float), optional(default=0)
        The relative tolerance.  Singular values less than rtol*smax are
        considered to be zero, where smax is the largest singular value.

    Returns
    -------
    ns : array_like(float, ndim=2)
        If `A` is an array with shape (m, k), then `ns` will be an array
        with shape (k, n), where n is the estimated dimension of the
        nullspace of `A`.  The columns of `ns` are a basis for the
        nullspace; each element in numpy.dot(A, ns) will be
        approximately zero.

    Note: If both `atol` and `rtol` are positive, the combined tolerance
    is the maximum of the two; that is:

        tol = max(atol, rtol * smax)

    Note: Singular values smaller than `tol` are considered to be zero.

    """

    A = np.atleast_2d(A)
    u, s, vh = svd(A)
    tol = max(atol, rtol * s[0])
    nnz = (s >= tol).sum()
    ns = vh[nnz:].conj().T

    return ns


from scipy import *
from numpy import *
from scipy.linalg import *
from numpy.linalg import *

def hatfun(x,t,k):
	"""
    hatfun(x,t,k)

	Evaluate a piecewise linear "hat" function at `x`, where `t` is a vector of
	n+1 interpolation nodes and `k` is an integer in 0:n giving the index of the node
	where the hat function equals one.
	"""

	n = len(t)-1
    # Return correct node given mathematical index k, including fictitious choices.
	def node(k):
		if k < 0:
			return 2*t[0]-t[1]
		elif k > n:
			return 2*t[n]-t[n-1]
		else:
			return t[k]

	H1 = (x-node(k-1)) / (node(k)-node(k-1))   # upward slope
	H2 = (node(k+1)-x) / (node(k+1)-node(k))   # downward slope
	H = minimum(H1,H2)
	return maximum(0,H)

def plinterp(t,y):
	"""
	plinterp(t,y)

	Create a piecewise linear interpolating function for data values in `y` given at nodes
	in `t`.
	"""

	n = len(t)-1
	return lambda x: sum( y[k]*hatfun(x,t,k) for k in range(n+1) )

def spinterp(t,y):
	"""
	spinterp(t,y)

	Create a cubic not-a-knot spline interpolating function for data values in `y` given at nodes in `t`.
	"""
	n = len(t) - 1
	h = [t[i+1]-t[i] for i in range(n)]

	# Preliminary definitions.
	Z = zeros([n,n])
	I = eye(n);  E = I[:n-1,:]
	J = eye(n) + diag(-ones(n-1),1)
	H = diag(h)

	# Left endpoint interpolation:
	AL = hstack([I,Z,Z,Z])
	vL = y[:-1]

	# Right endpoint interpolation:
	AR = hstack([I,H,H**2,H**3])
	vR = y[1:]

	# Continuity of first derivative:
	A1 = E @ hstack([Z,J,2*H,3*H**2])
	v1 = zeros(n-1)

	# Continuity of second derivative:
	A2 = E @ hstack([Z,Z,J,3*H])
	v2 = zeros(n-1)

	# Not-a-knot conditions:
	nakL = hstack([ zeros(3*n), hstack([1,-1,zeros(n-2)]) ])
	nakR = hstack([ zeros(3*n), hstack([zeros(n-2),1,-1]) ])

    # Assemble and solve the full system.
	A = vstack( [ AL, AR, A1, A2, nakL, nakR ] )
	v = hstack( [ vL, vR, v1, v2, 0, 0 ] )
	z = solve(A,v)

	# Break the coefficients into separate vectors.
	rows = arange(n)
	a = z[rows]
	b = z[n+rows];  c = z[2*n+rows];  d = z[3*n+rows]
	S = [ poly1d([d[k],c[k],b[k],a[k]]) for k in range(n) ]

	# This function evaluates the spline when called with a value for x.
	def evaluate(x):
		f = zeros(x.shape)
		for k in range(n):
			# Evaluate this piece's cubic at the points inside it.
			index = (x>=t[k]) & (x<=t[k+1])
			f[index] = S[k](x[index]-t[k])
		return f

	return evaluate

def fdweights(t,m):
	"""
	fdweights(t,m)

	Return weights for the `m`th derivative of a function at zero using values at the
	nodes in vector `t`.
	"""
    # This is a compact implementation, not an efficient one.

	def weight(t,m,r,k):
		# Recursion for one weight.
		# Input:
		#   t   nodes (vector)
		#   m   order of derivative sought
		#   r   number of nodes to use from t (<= length(t))
		#   k   index of node whose weight is found

		if (m<0) or (m>r):        # undefined coeffs must be zero
			c = 0
		elif (m==0) and (r==0):  # base case of one-point interpolation
			c = 1
		else:                     # generic recursion
			if k<r:
				c = (t[r]*weight(t,m,r-1,k) -
					m*weight(t,m-1,r-1,k))/(t[r]-t[k])
			else:
				if r <= 1:
					numer = 1.0
				else:
					numer = prod(t[r-1]-t[:r-1])
				if r <= 0:
					denom = 1.0
				else:
					denom = prod(t[r]-t[:r])
				beta =  numer/denom
				c = beta*(m*weight(t,m-1,r-1,r-1) - t[r-1]*weight(t,m,r-1,r-1))
		return c

	r = len(t)-1
	w = zeros(t.shape)
	return [ weight(t,m,r,k) for k in range(r+1) ]
	#return weight(t,m,r,0)

def trapezoid(f,a,b,n):
	"""
	trapezoid(f,a,b,n)

	Apply the trapezoid integration formula for integrand `f` over interval [`a`,`b`], broken up into `n` equal pieces. Returns estimate, vector of nodes, and vector of integrand values at the nodes.
	"""
	h = (b-a)/n
	t = linspace(a,b,n+1)
	y = f(t)
	T = h * ( sum(y[1:-1]) + 0.5*(y[0] + y[-1]) )

	return T,t,y

def intadapt(f,a,b,tol):
	"""
	intadapt(f,a,b,tol)

	Do adaptive integration to estimate the integral of `f` over [`a`,`b`] to desired
	error tolerance `tol`. Returns estimate and a vector of evaluation nodes used.
	"""
    # Use error estimation and recursive bisection.
	def do_integral(a,fa,b,fb,m,fm,tol):
		# These are the two new nodes and their f-values.
		xl = (a+m)/2;  fl = f(xl)
		xr = (m+b)/2;  fr = f(xr)
		t = array([a,xl,m,xr,b])             # all 5 nodes at this level

		# Compute the trapezoid values iteratively.
		h = (b-a)
		T = array([0.,0.,0.])
		T[0] = h*(fa+fb)/2
		T[1] = T[0]/2 + (h/2)*fm
		T[2] = T[1]/2 + (h/4)*(fl+fr)

		S = (4*T[1:]-T[:-1]) / 3      # Simpson values
		E = (S[1]-S[0]) / 15           # error estimate

		if abs(E) < tol*(1+abs(S[1])):  # acceptable error?
			Q = S[1]                    # yes--done
		else:
			# Error is too large--bisect and recurse.
			QL,tL = do_integral(a,fa,m,fm,xl,fl,tol)
			QR,tR = do_integral(m,fm,b,fb,xr,fr,tol)
			Q = QL + QR
			t = hstack([tL,tR[1:]])    # merge the nodes w/o duplicate
		return Q,t

	m = (b+a)/2
	Q,t = do_integral(a,f(a),b,f(b),m,f(m),tol)
	return Q,t


import numpy as np
from scipy.optimize import minimize

# ##############################################################################
# LoadData takes the file location for the yacht_hydrodynamics.data and returns
# the data set partitioned into a training set and a test set.
# the X matrix, deal with the month and day strings.
# Do not change this function!
# ##############################################################################
def loadData(df):
    data = np.loadtxt(df)
    Xraw = data[:,:-1]
    # The regression task is to predict the residuary resistance per unit weight of displacement
    yraw = (data[:,-1])[:, None]
    X = (Xraw-Xraw.mean(axis=0))/np.std(Xraw, axis=0)
    y = (yraw-yraw.mean(axis=0))/np.std(yraw, axis=0)

    ind = range(X.shape[0])
    test_ind = ind[0::4] # take every fourth observation for the test set
    train_ind = list(set(ind)-set(test_ind))
    X_test = X[test_ind]
    X_train = X[train_ind]
    y_test = y[test_ind]
    y_train = y[train_ind]

    return X_train, y_train, X_test, y_test

# ##############################################################################
# Returns a single sample from a multivariate Gaussian with mean and cov.
# ##############################################################################
def multivariateGaussianDraw(mean, cov):

    sample = np.zeros((mean.shape[0], )) # This is only a placeholder
    # Task 2:
    # TODO: Implement a draw from a multivariate Gaussian here

    sample = np.random.multivariate_normal(mean, cov, 1)

    # Return drawn sample
    return sample

# ##############################################################################
# RadialBasisFunction for the kernel function
# k(x,x') = s2_f*exp(-norm(x,x')^2/(2l^2)). If s2_n is provided, then s2_n is
# added to the elements along the main diagonal, and the kernel function is for
# the distribution of y,y* not f, f*.
# ##############################################################################
class RadialBasisFunction():
    def __init__(self, params):
        self.ln_sigma_f = params[0]
        self.ln_length_scale = params[1]
        self.ln_sigma_n = params[2]

        self.sigma2_f = np.exp(2*self.ln_sigma_f)
        self.sigma2_n = np.exp(2*self.ln_sigma_n)
        self.length_scale = np.exp(self.ln_length_scale)

    def setParams(self, params):
        self.ln_sigma_f = params[0]
        self.ln_length_scale = params[1]
        self.ln_sigma_n = params[2]

        self.sigma2_f = np.exp(2*self.ln_sigma_f)
        self.sigma2_n = np.exp(2*self.ln_sigma_n)
        self.length_scale = np.exp(self.ln_length_scale)

    def getParams(self):
        return np.array([self.ln_sigma_f, self.ln_length_scale, self.ln_sigma_n])

    def getParamsExp(self):
        return np.array([self.sigma2_f, self.length_scale, self.sigma2_n])

    # ##########################################################################
    # covMatrix computes the covariance matrix for the provided matrix X using
    # the RBF. If two matrices are provided, for a training set and a test set,
    # then covMatrix computes the covariance matrix between all inputs in the
    # training and test set.
    # ##########################################################################
    def covMatrix(self, X, Xa=None):
        if Xa is not None:
            X_aug = np.zeros((X.shape[0]+Xa.shape[0], X.shape[1]))
            X_aug[:X.shape[0], :X.shape[1]] = X
            X_aug[X.shape[0]:, :X.shape[1]] = Xa
            X=X_aug

        n = X.shape[0]
        covMat = np.zeros((n,n))

        # Task 1:
        # TODO: Implement the covariance matrix here

        for i in range(n):
            for j in range(n):
                sqdist = np.exp(-0.5 * (1/(self.length_scale)**2)* (np.linalg.norm(X[i,:]-X[j,:]))**2)
                covMat[i,j] = self.sigma2_f *  sqdist

        # If additive Gaussian noise is provided, this adds the sigma2_n along
        # the main diagonal. So the covariance matrix will be for [y y*]. If
        # you want [y f*], simply subtract the noise from the lower right
        # quadrant.
        if self.sigma2_n is not None:
            covMat += self.sigma2_n*np.identity(n)

        # Return computed covariance matrix
        return covMat

class GaussianProcessRegression():
    def __init__(self, X, y, k):
        self.X = X
        self.n = X.shape[0]
        self.y = y
        self.k = k
        self.K = self.KMat(self.X)
        self.L = np.linalg.cholesky(self.K)

    # ##########################################################################
    # Recomputes the covariance matrix and the inverse covariance
    # matrix when new hyperparameters are provided.
    # ##########################################################################
    def KMat(self, X, params=None):
        if params is not None:
            self.k.setParams(params)
        K = self.k.covMatrix(X)
        self.K = K
        self.L = np.linalg.cholesky(self.K)
        return K

    # ##########################################################################
    # Computes the posterior mean of the Gaussian process regression and the
    # covariance for a set of test points.
    # NOTE: This should return predictions using the 'clean' (not noisy) covariance
    # ##########################################################################
    def predict(self, Xa):
        mean_fa = np.zeros((Xa.shape[0], 1))
        mean_fa = np.array(mean_fa.flatten())
        cov_fa = np.zeros((Xa.shape[0], Xa.shape[0]))
        # Task 3:
        # TODO: compute the mean and covariance of the prediction

        #beginning of my solution: ------->
        na = Xa.shape[0]
        params = self.k.getParamsExp()
        Ktotal = self.k.covMatrix(self.X, Xa)
        # Covariance between training sample points (without Gaussian noise)
        Kxx = Ktotal[0:self.n,0:self.n] # + 1 * np.eye(10) if there is  Gaussian noise

        # Covariance between training and test points
        Kxs = Ktotal[self.n:self.n+na, 0:self.n]

        # Covariance between test points
        Kss = self.k.covMatrix(Xa)  #- params[2] * np.eye((na,na))  #Ktotal[self.n:self.n+na,self.n:self.n+na] #- params[2]@ np.eye((na,na))
        for i in range(Kss.shape[0]):
                Kss[i,i] = Kss[i,i] - params[2]

        # The mean of the GP fit (note that @ is matrix multiplcation: A @ B is equivalent to np.matmul(A,B))
        mean = Kxs @ np.linalg.inv(Kxx) @ self.y

        for i in range(mean.shape[0]):
            mean_fa[i] = (mean[i])[0]
        # The covariance matrix of the GP fit
        cov_fa = Kss - Kxs @ np.linalg.inv(Kxx) @ Kxs.T

        # Return the mean and covariance
        return mean_fa, cov_fa

    # ##########################################################################
    # Return negative log marginal likelihood of training set. Needs to be
    # negative since the optimiser only minimises.
    # ##########################################################################
    def logMarginalLikelihood(self, params=None):
        if params is not None:
            self.KMat(self.X, params)

        mll = 0
        # Task 4:
        # TODO: Calculate the log marginal likelihood ( mll ) of self.y

        mll = 0.5* (self.y).T @ np.linalg.solve((self.L).T, np.linalg.solve(self.L, self.y)) + np.sum(np.log((self.L).diagonal())) + 0.5 * self.n * np.log(2 * np.pi)
        mll = mll[0][0]
        # Return mll
        return mll

    # ##########################################################################
    # Computes the gradients of the negative log marginal likelihood wrt each
    # hyperparameter.
    # ##########################################################################
    def gradLogMarginalLikelihood(self, params=None):
        if params is not None:
            K = self.KMat(self.X, params)

        grad_ln_sigma_f = grad_ln_length_scale = grad_ln_sigma_n = 0
        # Task 5:
        # TODO: calculate the gradients of the negative log marginal likelihood
        # wrt. the hyperparameters

        beta_ln_length_scale = np.zeros((self.n, self.n))
        beta_ln_sigma_f = np.zeros((self.n, self.n))
        beta_ln_sigma_n = np.zeros((self.n, self.n))

        param = self.k.getParamsExp() #np.array([self.sigma2_f, self.length_scale, self.sigma2_n])

        alpha = np.linalg.solve(self.K, self.y)
        #inv = np.linalg.solve((self.L).T, np.linalg.solve(self.L, np.eye(self.n,self.n)))
        inv = np.linalg.inv(self.K)
        #L = self.L
        for i in range(self.n):
            for j in range(self.n):
                const = (np.linalg.norm(self.X[i]-self.X[j]))**2
                beta_ln_sigma_f[i,j] = 2 * param[0] * np.exp(-0.5 * const/(param[1]**2))
                beta_ln_length_scale[i,j] = (param[0]/(param[1]**2)) * const * np.exp(-0.5 * const/(param[1]**2))
                beta_ln_sigma_n[i,j] =  2 * param[2] if (i==j) else 0

        #beta_ln_sigma_n =beta_ln_sigma_n @
        grad_ln_sigma_f = -0.5 * np.trace((alpha @ alpha.T - inv) @ beta_ln_sigma_f)
        grad_ln_length_scale = -0.5 * np.trace((alpha @ alpha.T - inv) @ beta_ln_length_scale)
        grad_ln_sigma_n = -0.5 * np.trace((alpha @ alpha.T - inv) @ beta_ln_sigma_n)

        # Combine gradients
        gradients = np.array([grad_ln_sigma_f, grad_ln_length_scale, grad_ln_sigma_n])

        # Return the gradients
        return gradients

    # ##########################################################################
    # Computes the mean squared error between two input vectors.
    # ##########################################################################
    def mse(self, ya, fbar):
        mse = 0
        # Task 7:
        # TODO: Implement the MSE between ya and fbar

        for i in range(ya.shape[0]):
            mse += np.sum((ya[i] - fbar[i])**2)/(ya.shape[0])

        # Return mse
        return mse

    # ##########################################################################
    # Computes the mean standardised log loss.
    # ##########################################################################
    def msll(self, ya, fbar, cov):
        msll = 0
        # Task 7:
        # TODO: Implement MSLL of the prediction fbar, cov given the target ya

        param = self.k.getParamsExp()
        sigma = np.diagonal(cov)

        for i in range(ya.shape[0]):
            msll += 0.5 * np.log(2* np.pi * sigma[i]) + ((ya[i] - fbar[i])**2)/(2 * sigma[i])

        msll = msll/(ya.shape[0])

        return msll

    # ##########################################################################
    # Minimises the negative log marginal likelihood on the training set to find
    # the optimal hyperparameters using BFGS.
    # ##########################################################################
    def optimize(self, params, disp=True):

        res = minimize(self.logMarginalLikelihood, params, method ='BFGS', jac = self.gradLogMarginalLikelihood, options = {'disp':disp})
        return res.x

if __name__ == '__main__':

    np.random.seed(42)

    ##########################
    # You can put your tests here - marking
    # will be based on importing this code and calling
    # specific functions with custom input.
    ##########################


# -*- coding: utf-8 -*-
"""x_PyTorch_for_Deep_Learning-(freeCodeCamp-30Apr-2020).ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/15SWum4GdRiFKXDXA8Z2jcy8OXcldURGh
"""

import torch

"""# Tensors
At its core, PyTorch is a library for processing tensors. A tensor is a number, vector, matrix or any n-dimensional array. Let's create a tensor with a single number:
"""

# Number
t1 = torch.tensor(4.)
t1

"""4. is a shorthand for 4.0. It is used to indicate Python (and PyTorch) that you want to create a floating point number. We can verify this by checking the dtype attribute of our tensor:"""

t1.dtype

"""Let's try  creating slightly more complex tensors:"""

# Vector
t2 = torch.tensor([1., 2, 3, 4])
t2
t2.dtype

#Matrix
t3 = torch.tensor([[5., 6], [7, 8], [9, 10]])
print(t3, '\n',t3.dtype)

# 3-dimensional array
t4 = torch.tensor([
                   [[11, 12, 13],
                    [13, 14, 15]],
                   [[15, 16, 17],
                    [17, 18, 19.]]])
t4

"""tensors can have any number of dimensions, and different lengths along each dimension. We can inspect the length along each dimension using the .shape property of a tensor."""

t1.shape

print(t2)
t2.shape

print(t3)
t3.shape

print(t4)
t4.shape

"""# Tensor operations and gradients
We can combine tensors with the usual arithmetic operations. Let's look an example:
"""

# Create tensors.
x = torch.tensor(3.)
w = torch.tensor(4., requires_grad=True)
b = torch.tensor(5., requires_grad=True)

"""We've created 3 tensors x, w and b, all numbers. w and b have an additional parameter requires_grad set to True. We'll see what it does in just a moment.

Let's create a new tensor y by combining these tensors:
"""

# Arithmetic operations
y = w * x + b
y

"""As expected, y is a tensor with the value 3 * 4 + 5 = 17. What makes
PyTorch special is that we can automatically compute the
derivative of y w.r.t. the tensors that have requires_grad set
to True i.e. w and b. To compute the derivatives, we can call the
.backward method on our result y.
"""

# Compute derivatives
y.backward()

"""The derivatives of y w.r.t the input tensors are stored in the .grad property of the respective tensors."""

# Display gradients
print('dy/dx:', x.grad)
print('dy/dw:', w.grad)
print('dy/db:', b.grad)

"""`1. Note that x.grad is None, because x
doesn't have requires_grad set
to True`

`2. The "grad" in w.grad stands for gradient, which is another term
for derivative, used mainly when dealing with matrices.`

# Interoperability with Numpy

[Numpy](http://www.numpy.org/) is a popular open-source library used for mathematical and scientific computing in Python. It enables efficient operations on large multi-dimensional arrays and has a vast ecosystem of supporting libraries, including:

* [Pandas](https://pandas.pydata.org/) for file I/O and data analysis
* [Matplotlib](https://matplotlib.org/) for plotting and visualization
* [OpenCV](https://opencv.org/) for image and video processing

If you're interested in learning more about Numpy and other data science libraries in Python, check out this tutorial series: https://jovian.ai/aakashns/python-numerical-computing-with-numpy .

Instead of reinventing the wheel, PyTorch interoperates well with Numpy to leverage its existing ecosystem of tools and libraries.

##Here's how we create an array in Numpy:
"""

import numpy as np

x = np.array([[1, 2], [3, 4.]])
x

"""`We can convert a Numpy array to a PyTorch tensor using
torch.from_numpy`
"""

# Convert the numpy array to a torch tensor.
y = torch.from_numpy(x)
y

x.dtype, y.dtype

"""We can convert a PyTorch tensor to a Numpy using the .numpy
method of a tensor.
"""

# Convert a torch tensor to a numpy array
z = y.numpy()
z

"""`The interoperability between PyTorch and Numpy is really
important because most datasets you'll work with will likely be read and preprocessed as Numpy arrays.`
"""


import numpy as np
#Using numpy's version -- double precision
eps = np.finfo(float).eps
#Using mathmatical defenition -- single precision
def macEps(func=np.float32):
    m_eps = func(1)
    while func(1)+func(m_eps) != func(1):
        m_eps_last = m_eps
        m_eps = func(m_eps) / func(2)
    return m_eps_last
#Using CC theory  -- single precision
eps2=2**(-(24-1))#eps=b^-(p-1) where p is the precison(24 bits) and b the base(2)
def mEps():
    e=1
    while (1+e)<1:
        e/=2
    return e*2

print eps #2.220446049250313*e^-16
print np.spacing(1.0) #equal to previous, 2nd numpy defenition of machine eps
print macEps() #1.1920929*e^-7
print eps2 #1.19209289551


import numpy as np
import matplotlib.pyplot as plt

# =========================define sinc
# ---------------normalized
def sinc1(x):
    PI = np.pi
    x = np.array(x)
    y = np.where(np.abs(PI * x) < 1e-38, 1.0, np.sin(PI * x) / (PI * x))
    return y

# ---------------denormalized
def sinc2(x):
    y = np.where(np.abs(x) < 1e-38, 1.0, np.sin(x) / x)
    return y

# =========================test sinc definition
a = [1e-15, 1, 1 / 2]
print(a)

print("by numpy: ", np.sinc(a))
print(a)
print("by sinc1: ", sinc1(a))
print(a)
print("by sinc2: ", sinc2(a))
print(a)

# =========================test sinc definition

Ns = 1024
Tp = 4
t = np.linspace(-10, 10, Ns)
y1 = sinc1(t / Tp)
y2 = sinc2(t / Tp)

y1fft = np.fft.fftshift(np.fft.fft(y1))
y2fft = np.fft.fftshift(np.fft.fft(y2))
f = np.fft.fftshift(np.fft.fftfreq(Ns))

# y1fft = np.fft.fft(y1)
# y2fft = np.fft.fft(y2)
# f = np.fft.fftfreq(Ns)

plt.figure()
plt.subplot(131)
plt.plot(t, y1, 'r')
plt.plot(t, y2, '-b')
plt.title('sinc(t/Tp), ' + "Tp=" + str(Tp))
plt.xlabel('Time/s')
plt.ylabel('Amplitude')
plt.legend(['sin(πt/Tp)/(πt/Tp)', 'sin(t/Tp)/(t/Tp)'])
plt.grid()

plt.subplot(132)
plt.plot(f, np.abs(y1fft), '-r')
plt.plot(f, np.abs(y2fft), '-b')
plt.title('FFT of signal')
plt.xlabel('Frequency/Hz')
plt.ylabel('Amplitude')
plt.legend(['sin(πt/Tp)/(πt/Tp)', 'sin(t/Tp)/(t/Tp)'])
plt.grid()

plt.subplot(133)
plt.plot(f, np.abs(y1fft), '-r')
plt.plot(f, np.abs(y2fft), '-b')
plt.title('FFT of signal (zoom out)')
plt.xlabel('Frequency/Hz')
plt.ylabel('Amplitude')
plt.legend(['sin(πt/Tp)/(πt/Tp)', 'sin(t/Tp)/(t/Tp)'])
plt.grid()
plt.show()


import numpy as np

class Perceptron:
    def __init__(self, input_size, lr=1, epochs=100):
        self.W = np.zeros(input_size + 1)
        # add one for bias
        self.epochs = epochs
        self.lr = lr

    def activation(self, x):  # binary step
        return 1 if x >= 0 else 0

    def predict(self, x):
        z = self.W.T.dot(x)
        a = self.activation(z)
        return a

    def fit(self, X, d):
        for _ in range(self.epochs):
            for i in range(d.shape[0]):
                x = np.insert(X[i], 0, 1)
                y = self.predict(x)
                e = d[i] - y

                self.W = self.W + self.lr * e * x

if __name__ == '__main__':
    X = np.array([
        [0, 0],
        [0, 1],
        [1, 0],
        [1, 1]
    ])
    d = np.array([0, 0, 0, 1])

    perceptron = Perceptron(input_size=2)
    perceptron.fit(X, d)
    print(perceptron.W)


import numpy as np
import matplotlib.pyplot as plt

a = 1.0
b = 2.0
c = 3.0
d = 4.0

abcd = np.array([a,b,c,d])

pqrs = np.matmul(abcd, np.array([[a,b,c,d],[d,a,b,c],[c,d,a,b],[b,c,d,a]]))

def DFT(x):

    N = len(x)
    n = np.arange(N)
    k = n.reshape((N, 1))
    e = np.exp(-2j * np.pi * k * n / N)

    X = np.dot(e, x)

    return X

dft_abcd = np.round(DFT(abcd),0)

dft_pqrs = np.round(DFT(pqrs),0)

predicted_dft_pqrs = dft_abcd**2

print("[Alpha Beta Gama Delta] = ", dft_abcd)
print("[Alpha^2 Beta^2 Gama^2 Delta^2] = ", predicted_dft_pqrs)
print("DFT of [p q r s]", dft_pqrs)


from numpy.linalg import svd
import numpy as np

movieRatings = [
    [2, 5, 3],
    [1, 2, 1],
    [4, 1, 1],
    [3, 5, 2],
    [5, 3, 1],
    [4, 5, 5],
    [2, 4, 2],
    [2, 2, 5],
]

U, singularValues, V = svd(movieRatings)

print(U)
print(singularValues)
print(V)

Sigma = np.vstack([
    np.diag(singularValues),
    np.zeros((5, 3)),
])

print(np.round(movieRatings - np.dot(U, np.dot(Sigma, V)), decimals=10))

U, singularValues, V = svd(movieRatings, full_matrices=False)
print(U)
print(singularValues)
print(V)

Sigma = np.diag(singularValues)
print(np.round(movieRatings - np.dot(U, np.dot(Sigma, V)), decimals=10))


from hetdesrun.component.registration import register
from hetdesrun.datatypes import DataType

import datetime
import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression

def regression_linear(xs: pd.Series, ys: pd.Series):
    """Linear Regression for univariate Remaining Useful Life (RUL) prediction

    train-series: pd.Series with datetime index and float values. The input data to train
        the regression model.
    limit: the limit value against which rul should be made.

    returns: Tuple (pred_y, timestamp_limit_reached, intercept, slope)
    """

    lg = LinearRegression()
    lg.fit(xs.values.reshape(-1, 1), ys.values.reshape(-1, 1))
    intercept, slope = lg.intercept_[0], lg.coef_[0]

    preds = pd.Series(
        lg.predict(xs.values.reshape(-1, 1)).reshape(1, -1)[0], index=xs.index
    )

    diffs = preds - ys

    return preds, diffs, intercept, slope

# ***** DO NOT EDIT LINES BELOW *****
# These lines may be overwritten if input/output changes.
@register(
    inputs={"x_values": DataType.Series, "y_values": DataType.Series},
    outputs={
        "diffs": DataType.Series,
        "intercept": DataType.Float,
        "slope": DataType.Float,
        "preds": DataType.Series,
    },
)
def main(*, x_values, y_values):
    """entrypoint function for this component"""
    # ***** DO NOT EDIT LINES ABOVE *****
    # write your function code here.
    preds, diffs, intercept, slope = regression_linear(x_values, y_values)
    return {"diffs": diffs, "intercept": intercept, "slope": slope, "preds": preds}


# SoftMax Activation
'''
1) Exponenetial of each value to make it positive
2) normalise the values
'''
import math
import numpy as np
layer_outputs= [[4.8, 1.21, 2.385],
                [8.9, -1.81, 0.2],
                [1.41, 1.051, 0.026]]
layer_outputs = layer_outputs - np.max(layer_outputs, axis=1, keepdims=True)
print("New LayerOutput is")
print(layer_outputs)
exp_values = np.exp(layer_outputs)
print(exp_values)
print(np.sum(layer_outputs, axis = 1, keepdims=True))
norm_values = exp_values/np.sum(exp_values, axis=1, keepdims=True)
print(norm_values)


"""
Correlation Statistics

One way to measure the strength of the correlation between continuous numerical variable is by using a method called Pearson correlation.
Pearson correlation method will give you two values: the correlation coefficient and the P-value.
So how do we interpret these values?

"""

# importing the pandas module
import pandas as pd

# importing the stats module from scipy package
from scipy import stats

# get data file from url
# dataFile = 'https://archive.ics.uci.edu/ml/machine-learning-databases/autos/imports-85.data'

# or just from a local repository
data_file = './data/imports-85.data'

# defining headers
headers = ['symboling', 'normalized-losses', 'make', 'fuel-type', 'aspiration', 'num-of-doors', 'body-style', 'drive-wheels', 'engine-location', 'wheel-base', 'length', 'width', 'height', 'curb-weight', 'engine-type', 'num-of-cylinders', 'engine-size', 'fuel-system', 'bore', 'stroke', 'compression-ratio', 'horsepower', 'peak-rpm', 'city-mpg', 'highway-mpg', 'price']

# read file into pandas dataframe
df = pd.read_csv(data_file)

# set the dataframe header
df.columns = headers

"""
we want to look at the correlation between the variable's horsepower and car price.
See how easy you can calculate the Pearson correlation using the SI/PI stats package?
We can see that the correlation coefficient is approximately.8, and this is close to 1.
So there is a strong positive correlation.
We can also see that the P-value is very small, much smaller than.001.
And so we can conclude that we are certain about the strong positive correlation.
"""
pearson_coeif, p_value = stats.pearsonr(df['horsepower'], df['price'])

print(pearson_coeif, p_value)


"""Gisin state."""
import numpy as np

def gisin(lambda_var: float, theta: float) -> np.ndarray:
    r"""
    Produce a Gisin state [GIS96]_.

    Returns the Gisin state described in [GIS96]_. Specifically, the Gisin state can be defined as:

    .. math::
        \begin{equation}
            \rho_{\lambda, \theta} = \lambda
                                    \begin{pmatrix}
                                        0 & 0 & 0 & 0 \\
                                        0 & \sin^2(\theta) &
                                        -\sin(\theta)\cos(\theta) & 0 \\
                                        0 & -\sin(\theta)\cos(\theta) &
                                        \cos^2(\theta) & 0 \\
                                        0 & 0 & 0 & 0
                                    \end{pmatrix} +
                                    \frac{1 - \lambda}{2}
                                    \begin{pmatrix}
                                        1 & 0 & 0 & 0 \\
                                        0 & 0 & 0 & 0 \\
                                        0 & 0 & 0 & 0 \\
                                        0 & 0 & 0 & 1
                                    \end{pmatrix}.
        \end{equation}

    Examples
    ==========

    The following code generates the Gisin state :math:`\rho_{0.5, 1}`.

    >>> from toqito.states import gisin
    >>> gisin(0.5, 1)
    [[ 0.25      ,  0.        ,  0.        ,  0.        ],
     [ 0.        ,  0.35403671, -0.22732436,  0.        ],
     [ 0.        , -0.22732436,  0.14596329,  0.        ],
     [ 0.        ,  0.        ,  0.        ,  0.25      ]]

    References
    ==========
    .. [GIS96] N. Gisin.
        Hidden quantum nonlocality revealed by local filters.
        (http://dx.doi.org/10.1016/S0375-9601(96)80001-6). 1996.

    :param lambda_var: A real parameter in [0, 1].
    :param theta: A real parameter.
    :return: Gisin state.
    """
    if lambda_var < 0 or lambda_var > 1:
        raise ValueError("InvalidLambda: Variable lambda must be between 0 and 1.")

    rho_theta = np.array(
        [
            [0, 0, 0, 0],
            [0, np.sin(theta) ** 2, -np.sin(2 * theta) / 2, 0],
            [0, -np.sin(2 * theta) / 2, np.cos(theta) ** 2, 0],
            [0, 0, 0, 0],
        ]
    )

    rho_uu_dd = np.array([[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1]])

    return lambda_var * rho_theta + (1 - lambda_var) * rho_uu_dd / 2


# -*- coding: utf-8 -*-
"""
Created on Mon Sep 17 10:24:16 2018

@author: Thiago, Renan
"""
import numpy as np

num_passos_bis = 0
num_passos_fp = 0

### Funcao pra achar a raiz ###

def F(x):
    return x**10 - 1.0

## Intervalo ##

a = np.dtype('f8')
b = np.dtype('f8')
E = np.dtype('f8')

a = 0.0
b = 2.0
ordem = 13
E = 10**-ordem

def insert_dash(string, index):
    return string[:index] + '|' + string[index:]

## Métodos ##

def bissecao(_a, _b):

    _x = np.dtype('f8')
    e = _b - _a
    mx = e / 2.0
    _x = mx + _a
    iteracoes = 0

    global num_passos_bis

    #checa se a raiz esta nos intervalos
    if(F(_a) == 0.0):
        return _a
    if(F(_b) == 0.0):
        return _b

    while(e > E):

        #acha meio
        e = _b - _a
        mx = e / 2.0
        _x = mx + _a

        #determina valores
        fa = F(_a)
        fb = F(_b)
        fx = F(_x)

        #se a raiz estiver entre um dos intervalos formados, ajusta intervalo (_a, _b)
        if((fa >= 0 and fx < 0) or (fa < 0 and fx >= 0)):
            #intervalo a-x
            _b = _x
        else:
            #intervalo x-b
            _a = _x

        iteracoes += 1

        if(iteracoes > 10000):
            print("saiu no break da bissessao")
            break

    num_passos_bis = iteracoes

    #atingiu condicao de parada
    return _x

def FalsaPosicao(_a,_b):

    x = np.dtype('f8')
    xa = np.dtype('f8')

    xa = 0
    x = 0
    iteracoes = 0

    global num_passos_fp

    #Checa a condição de existencia
    if (F(_a) * F(_b) < 0):

        while (abs(F(x)) > E or iteracoes==0):

            x = (_a * F(_b) - _b * F(_a)) / (F(_b) - F(_a))

            if(F(_a) * F(x) < 0):
                _b = x
            else:
                _a = x

            xa = x
            iteracoes += 1

            if(iteracoes > 10000):
                print("saiu no break da falsa posicao")
                break

        num_passos_fp = iteracoes

        return x

raiz_bissecao = bissecao(a, b)
raiz_falsa_pos = FalsaPosicao(a, b)

string_bis = repr(raiz_bissecao)
string_fp = repr(raiz_falsa_pos)

print("\nBissecao:\n")
print("Raiz:"+insert_dash(string_bis, ordem + string_bis.find('.') + 1)+"")
print("Precisao:"+str(E)+"")
print("Num de Iteracoes:"+str(num_passos_bis)+"")

print("\nFalsa Posicao:\n")
print("Raiz:"+insert_dash(string_fp, ordem + string_fp.find('.') + 1)+"")
print("Precisao:"+str(E)+"")
print("Num de Iteracoes:"+str(num_passos_fp)+"")