## 2. Calculating differences ##

female_diff = (10771 - 16280.5) / 16280.5
male_diff = (21790 - 16280.5) / 16280.5

## 3. Updating the formula ##

female_diff = (10771 - 16280.5) ** 2 / 16280.5
male_diff = (21790 - 16280.5) ** 2 / 16280.5
gender_chisq = female_diff + male_diff

## 4. Generating a distribution ##

chi_squared_values = []
from numpy.random import random
import matplotlib.pyplot as plt

for i in range(1000):
    sequence = random((32561,))
    sequence[sequence < .5] = 0
    sequence[sequence >= .5] = 1
    male_count = len(sequence[sequence == 0])
    female_count = len(sequence[sequence == 1])
    male_diff = (male_count - 16280.5) ** 2 / 16280.5
    female_diff = (female_count - 16280.5) ** 2 / 16280.5
    chi_squared = male_diff + female_diff
    chi_squared_values.append(chi_squared)

plt.hist(chi_squared_values)

## 6. Smaller samples ##

female_diff = (107.71 - 162.805) ** 2 / 162.805
male_diff = (217.90 - 162.805) ** 2 / 162.805
gender_chisq = female_diff + male_diff

## 7. Sampling distribution equality ##

chi_squared_values = []
from numpy.random import random
import matplotlib.pyplot as plt

for i in range(1000):
    sequence = random((300,))
    sequence[sequence < .5] = 0
    sequence[sequence >= .5] = 1
    male_count = len(sequence[sequence == 0])
    female_count = len(sequence[sequence == 1])
    male_diff = (male_count - 150) ** 2 / 150
    female_diff = (female_count - 150) ** 2 / 150
    chi_squared = male_diff + female_diff
    chi_squared_values.append(chi_squared)

plt.hist(chi_squared_values)

## 9. Increasing degrees of freedom ##

diffs = []
observed = [27816, 3124, 1039, 311, 271]
expected = [26146.5, 3939.9, 944.3, 260.5, 1269.8]

for i, obs in enumerate(observed):
    exp = expected[i]
    diff = (obs - exp) ** 2 / exp
    diffs.append(diff)

race_chisq = sum(diffs)

## 10. Using SciPy ##

from scipy.stats import chisquare
import numpy as np
observed = np.array([27816, 3124, 1039, 311, 271])
expected = np.array([26146.5, 3939.9, 944.3, 260.5, 1269.8])

chisquare_value, race_pvalue = chisquare(observed, expected)

Data Analyst in Python/Step 5 - Probability and Statistics/5. Hypothesis Testing Fundamentals/2. Chi-squared tests.py


"""
Similar to the file of adjoint_univariate_nonlinear_system.py but here we use
the Automatic Differentiation of Jax to obtain the gradients of the nonlinear
function.
"""

import time

import jax.numpy as jnp
import jax.scipy as jsp
from jax import grad

from scipy import optimize

def residuum(displacement, force, theta):
    r"""
                    /           b              \
     -F + k*(a - v)*|- -------------------- + 1|
                    |     _________________    |
                    |    /          2    2     |
                    \  \/  2*a*v + b  - v      /
    """
    vertical_distance, horizontal_distance, spring_stiffness = theta
    residual_value = (
        spring_stiffness * (vertical_distance - displacement) * (
            1
            -
            (
                horizontal_distance
            ) / (
                jnp.sqrt(horizontal_distance**2 + 2*vertical_distance*displacement - displacement**2)
            )
        )
        -
        force
    )

    return residual_value

if __name__ == "__main__":
    a_true = 1.0
    b_true = 1.0
    k_true = 200.0

    force_value = 10.0

    v_ref = optimize.newton(
        func=residuum,
        fprime=grad(residuum),
        args=(force_value, (a_true, b_true, k_true)),
        x0=1.0
    )

    # print(v_ref)

    ######
    # Solving the Forward Problem
    ######

    theta_guess = jnp.array([0.9, 0.9, 180.0])
    additional_args = (force_value, theta_guess)

    v = optimize.newton(
        func=residuum,
        fprime=grad(residuum),
        args=additional_args,
        x0=1.0,
    )

    # The "J" loss function is the least-squares (quadratic loss)
    def loss_function(v, theta):
        return 0.5 * (v - v_ref)**2

    J = loss_function(v, theta_guess)

    ##### Adjoint Method

    time_adjoint = time.time_ns()

    current_args_adjoint = (v, force_value, theta_guess)

    del_J__del_theta = grad(loss_function, argnums=1)(v, theta_guess).reshape((1, -1))
    del_J__del_x = grad(loss_function, argnums=0)(v, theta_guess)
    del_f__del_theta = grad(residuum, argnums=2)(*current_args_adjoint).reshape((1, -1))
    del_f__del_x = grad(residuum, argnums=0)(*current_args_adjoint)

    # print(del_J__del_theta)
    # print(del_J__del_x)
    # print(del_f__del_theta)
    # print(del_f__del_x)

    adjoint_variable = -1.0 / del_f__del_x * del_J__del_x

    d_J__d_theta_adjoint = del_J__del_theta + adjoint_variable * del_f__del_theta

    time_adjoint = time.time_ns() - time_adjoint

    print("Adjoint Sensitivities using Autodiff for gradients and Jacobians")
    print(d_J__d_theta_adjoint)
    print("Timing")
    print(time_adjoint)


import matplotlib as mpl
mpl.use('TkAgg')
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from featureNormalize import *
from gradientDescent import *
from normalEqn import *

plt.ion()

# ===================== Part 1: Feature Normalization =====================
print('Loading Data...')
# data = np.loadtxt('ex1data2.txt', delimiter=',', dtype=np.int64)
data = pd.read_csv('ex1data2.txt', sep=',', header=None, names=['Size', 'Bedrooms', 'Price'])

cols = data.shape[1]
X1 = data.iloc[:, :cols - 1]
y1 = data.iloc[:, cols - 1:]

# Print out some data points
# print('First 10 examples from the dataset: ')
# for i in range(10):
#     print('x = {}, y = {}'.format(X[i:i+1], y[i:i+1]))
#
# input('Program paused. Press ENTER to continue')

# Scale features and set them to zero mean
print('Normalizing Features ...')

X1, y1, mu, sigma = feature_normalize(X1, y1)

X1.insert(0, 'Ones', 1)  # Add a column of ones to X

# ===================== Part 2: Gradient Descent =====================

# ===================== Your Code Here =====================
# Instructions : We have provided you with the following starter
#                code that runs gradient descent with a particular
#                learning rate (alpha).
#
#                Your task is to first make sure that your functions -
#                computeCost and gradientDescent already work with
#                this starter code and support multiple variables.
#
#                After that, try running gradient descent with
#                different values of alpha and see which one gives
#                you the best result.
#
#                Finally, you should complete the code at the end
#                to predict the price of a 1650 sq-ft, 3 br house.
#
# Hint: At prediction, make sure you do the same feature normalization.
#

print('Running gradient descent ...')

# Choose some alpha value
alpha = 0.03
num_iters = 400

# Init theta and Run Gradient Descent
theta = np.zeros((3, 1))
# theta, J_history = gradient_descent(X1, y1, theta, alpha, num_iters)
theta = np.array([[-1.10160108e-16], [8.82183317e-01], [-5.05961547e-02]])
# Plot the convergence graph
# plt.figure()
# plt.plot(np.arange(J_history.size), J_history)
# plt.xlabel('Number of iterations')
# plt.ylabel('Cost J')
# plt.show()
# Display gradient descent's result
print('Theta computed from gradient descent : \n{}'.format(theta))

# Estimate the price of a 1650 sq-ft, 3 br house
# ===================== Your Code Here =====================
# Recall that the first column of X is all-ones. Thus, it does
# not need to be normalized.

# ==========================================================
price = np.dot(np.array([1, (1650 - mu[0]) / sigma[0], (3 - mu[1]) / sigma[1]]), theta)[0] * sigma[2] + mu[2]
print('Predicted price of a 1650 sq-ft, 3 br house (using gradient descent) : {:0.3f}'.format(price))

input('Program paused. Press ENTER to continue')

# ===================== Part 3: Normal Equations =====================

print('Solving with normal equations ...')

# ===================== Your Code Here =====================
# Instructions : The following code computes the closed form
#                solution for linear regression using the normal
#                equations. You should complete the code in
#                normalEqn.py
#
#                After doing so, you should complete this code
#                to predict the price of a 1650 sq-ft, 3 br house.
#

# Load data
X2 = data.iloc[:, :cols - 1]
y2 = data.iloc[:, cols - 1:]

# Add intercept term to X
X2.insert(0, 'Ones', 1)

theta = normal_eqn(X2, y2)

# Display normal equation's result
print('Theta computed from the normal equations : \n{}'.format(theta))

# Estimate the price of a 1650 sq-ft, 3 br house
# ===================== Your Code Here =====================
price = np.dot(np.array([1, 1650, 3]), theta)
print(price)
# ==========================================================

print('Predicted price of a 1650 sq-ft, 3 br house (using normal equations) : {:0.3f}'.format(price[0]))

input('ex1_multi Finished. Press ENTER to exit')


"""
Computing pi with parallel quadrature formula
"""
import numpy
from mpi4py import MPI
import sys

comm = MPI.COMM_WORLD
mynode = comm.Get_rank()
totalnodes = comm.Get_size()

def fun(x):
    return (4.0/(1.0 + x*x))

truepi = 3.141592653589793238462643
dest = 0
pi = numpy.zeros(1)

# Initialize value of n only if this is rank 0
# we are not checking the input, if the user does something that
# has no meaning we are going to fail badly!
if mynode == 0:
    if len(sys.argv) == 1:
        n = numpy.full(1, 20, dtype=int) # default value
    else:
        n = numpy.full(1,int(sys.argv[1]),dtype=int)
else:
    n = numpy.zeros(1, dtype=int)

# Broadcast n to all processes
comm.Bcast(n, root=0)

# Compute local integral
my_pi = numpy.zeros(1)
h = 1.0/(n*totalnodes)
for i in numpy.arange(1+mynode*n,n*(mynode+1)+1):
    x = h*(i - 0.5)
    my_pi = my_pi + fun(x)
my_pi = h*my_pi

# Send partition back to root process:
comm.Reduce(my_pi, pi, MPI.SUM, dest)

# Only print the result in process 0
if mynode == 0:
    print('The Integral Sum =', pi[0]," The Error is ",numpy.abs(pi[0]-truepi))


"""
This module assumes that all geometrical points are
represented as 1D numpy arrays.

It was designed and tested on 2D points,
but if you try it on 3D points you may
be pleasantly surprised ;-)
"""
import numpy as np

def point_distance(x, y):
    """Returns euclidean distance between points x and y"""
    return np.linalg.norm(x-y)

def point_projected_on_line(line_s, line_e, point):
    """Project point on line that goes through line_s and line_e

    assumes line_e is not equal or close to line_s
    """
    line_along = line_e - line_s

    transformed_point = point - line_s

    point_dot_line  = np.dot(transformed_point, line_along)
    line_along_norm = np.dot(line_along, line_along)

    transformed_projection = (point_dot_line / line_along_norm) * line_along

    return transformed_projection + line_s

def point_segment_distance(segment_s, segment_e, point):
    """Returns distance from point to the closest point on segment
    connecting points segment_s and segment_e"""
    projected = point_projected_on_line(segment_s, segment_e, point)
    if np.isclose(point_distance(segment_s, projected) + point_distance(projected, segment_e),
        point_distance(segment_s, segment_e)):
        # projected on segment
        return point_distance(point, projected)
    else:
        return min(point_distance(point, segment_s), point_distance(point, segment_e))


import numpy as np
import pandas as pd

import matplotlib.pyplot as plt

from O5 import prepare_data_wine

# 1. Standardizing data

X_train, X_test, y_train, y_test, df_wine = prepare_data_wine(standardize=True,
                                                              split=True,
                                                              dataframe=True)

# 2. Constructing covariance matrix

# After completing the mandatory preprocessing by executing the preceding code,
# let's advance to the second step: constructing the covariance matrix.
# The symmetric d×d-dimensional covariance matrix, where d is the number of
# dimensions in the dataset, stores the pairwise covariances between the
# different features. For example, the covariance between two features, x(j)
# and x(k) , on the population level can be calculated via the following
# equation:
#       σ(jk) = (1 / (n - 1) ) * ∑(i=1, n) (x(j, i) - μ(j))(x(k, i) - μ(k))

# Here, μμ jj and μμ kk are the sample means of features j and k, respectively.
# Note that the sample means are zero if we standardized the dataset. A
# positive covariance between two features indicates that the features increase
# or decrease together, whereas a negative covariance indicates that the
# features vary in opposite directions. For example, the covariance matrix of
# three features can then be written as follows (note that Σ stands for the
# Greek uppercase letter sigma, which is not to be confused with summation
# symbol:
#       Σ = [σ(1)^2 σ(1,2) σ(1, 3);σ(2,1) σ(2)^2 σ(2, 3);σ(3,1) σ(3, 2) σ(3)^2]

# The eigenvectors of the covariance matrix represent the principal components
# (the directions of maximum variance), whereas the corresponding eigenvalues
# will define their magnitude. In the case of the Wine dataset, we would obtain
# 13 eigenvectors  and eigenvalues from the 13 × 13 -dimensional covariance
# matrix.

# Now, for our third step, let's obtain the eigenpairs of the covariance
# matrix. As you will remember from our introductory linear algebra classes, an
# eigenvector, v, satisfies the following condition:
#       Σv = λv
# Here, λ is a scalar: the eigenvalue.

cov_mat = np.cov(X_train.T)

# 3. Obtaining the eigenvalues and eigenvectors of the covariance matrix.

eigen_values, eigen_vectors = np.linalg.eig(cov_mat)
print(f"EigenValues \n{eigen_values}")

# The numpy.linalg.eig function was designed to operate on
# both symmetric and non-symmetric square matrices. However,
# you may find that it returns complex eigenvalues in certain
# cases.
# A related function, numpy.linalg.eigh , has been
# implemented to decompose Hermetian matrices, which is a
# numerically more stable approach to working with symmetric
# matrices such as the covariance matrix; numpy.linalg.eigh
# always returns real eigenvalues.

# Since we want to reduce the dimensionality of our dataset by compressing it
# onto a new feature subspace, we only select the subset of the eigenvectors
# (principal components) that contains most of the information (variance). The
# eigenvalues define the magnitude of the eigenvectors, so we have to sort the
# eigenvalues by decreasing magnitude; we are interested in the top k
# eigenvectors based on the values of their corresponding eigenvalues. But
# before we collect those k most informative eigenvectors, let's plot the
# variance explained ratios of the eigenvalues. The variance explained ratio of
# an eigenvalue, λ(j) , is simply the fraction of an eigenvalue, λ(j) and the
# total sum of eigenvalues:
#       Explained variance ratio = λ(j) / ∑(i=1, d)λ(i)

# Using the NumPy cumsum function, we can then calculate the cumulative sum of
# explained variances, which we will then plot via Matplotlib's step function:

total = sum(eigen_values)
explained_variance = [(i / total) for i in sorted(eigen_values, reverse=True)]
cumulative_sum_explained_variance = np.cumsum(explained_variance)

plt.bar(range(1, 14), explained_variance, alpha=0.5, align='center',
        label='Individual explained variance')

plt.step(range(1, 14), cumulative_sum_explained_variance, where='mid',
         label='Cumulative explained variance')
plt.ylabel('Explained variance ratio')
plt.xlabel('Principal component index')
plt.legend(loc='best')
plt.tight_layout()
plt.show()

# The resulting plot indicates that the first principal component alone
# accounts for approximately 40 percent of the variance.
# Also, we can see that the first two principal components combined explain
# almost 60 percent of the variance in the dataset
# we should remind ourselves that PCA is an unsupervised method, which means
# that information about the class labels is ignored. Whereas a random forest
# uses the class membership information to compute the node impurities,
# variance measures the spread of values along a feature axis.

# 4. Sorting the eigenvalues by decreasing order to rank the eigenvectors
eigen_pairs = [(np.abs(eigen_values[i]), eigen_vectors[:, i])
               for i in range(len(eigen_values))]

eigen_pairs = sorted(eigen_pairs, key=lambda x: x[0], reverse=True)

# 5. Select k(here k=2) eigenvectors, which correspond to the k largest
# eigenvalues, where k is the dimensionality of the new feature subspace
# (k <= d)

# Now, we collect the two eigenvectors that correspond to the two largest
# eigenvalues, to capture about 60 percent of the variance in this dataset.
# Note that two eigenvectors have been chosen for the purpose of illustration,
# since we are going to plot the data via a two-dimensional scatter plot later
# in this subsection. In practice, the number of principal components has to be
# determined by a tradeoff between computational efficiency and the performance
# of the classifier.
first_eigenvalue_vector = eigen_pairs[0][1]
second_eigenvalue_vector = eigen_pairs[1][1]

# 6. Construct a projection matrix, W, from the "top" k eigenvectors

w = np.hstack((first_eigenvalue_vector[:, np.newaxis],
               second_eigenvalue_vector[:, np.newaxis]))
print(w)

# 7. Transform the d-dimensional input dataset, X, using the projection matrix,
# W, to obtain the new k-dimensional feature subspace.
#       Transformation: x' = xW for x ∈ X
print(X_train[0].dot(w))

# Or full transformation: X' = XW

X_train_pcs = X_train.dot(w)
colors = ['red', 'blue', 'green']
markers = ['s', 'x', 'o']
for l, c, m in zip(np.unique(y_train), colors, markers):
    plt.scatter(X_train_pcs[y_train == l, 0],
                X_train_pcs[y_train == l, 1],
                c=c, label=l, marker=m)

plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='lower left')
plt.tight_layout()
plt.show()
# As we can see in the resulting plot, the data is more spread along the x-axis
# —the first principal component—than the second principal component (y-axis),
# which is consistent with the explained variance ratio plot that we created in
# the previous subsection. However, we can tell that a linear classifier will
# likely be able to separate the classes well.
# Although we encoded the class label information for the purpose of
# illustration in the preceding scatter plot, we have to keep in mind that PCA
# is an unsupervised technique that doesn't use any class label information.


#!/usr/bin/env python3
# coding=utf-8

import matplotlib.pyplot as plt
import numpy as np

loc = 10.0
scale = 1.0

data = np.random.normal(loc, scale, size=6000)

# http://blog.csdn.net/lanchunhui/article/details/50163669
# 高斯分布（Gaussian Distribution）的概率密度函数（probability density function）
x_data = np.linspace(data.min(), data.max())
plt.plot(x_data, 1. / (np.sqrt(2 * np.pi) * scale) * np.exp(-(x_data - loc) ** 2 / (2 * scale ** 2)))

count, bins, _ = plt.hist(data, 30, normed=True)
# plt.plot(bins, 1. / (np.sqrt(2 * np.pi) * scale) * np.exp(-(bins - loc) ** 2 / (2 * scale ** 2)))

plt.show()

# Build a vector of 10000 normal deviates with variance 0.5^2 and mean 2
mu, sigma = 2, 0.5
v = np.random.normal(mu, sigma, 5000)
# Plot a normalized histogram with 50 bins
plt.hist(v, bins=50, normed=1)  # matplotlib version (plot)

# Compute the histogram with numpy and then plot it
(n, bins) = np.histogram(v, bins=50, normed=True)  # NumPy version (no plot)
plt.plot(.5 * (bins[1:] + bins[:-1]), n)

plt.grid(True)

plt.show()


import math
import random
import scipy.special
import scipy.optimize

PI    = 3.1415926535897932384626433832795
SQRT2 = 1.4142135623730950488016887242097

def Phi(z):
    """
    The cumulative function of the unit gaussian distribution
    """

    return  0.5 * (1.0 + scipy.special.erf(z / SQRT2))

def phi(z):
    """
    PDF of the standard gaussian distribution
    """

    return (1.0 / math.sqrt(2.0*PI)) * math.exp(-0.5 * z * z)

def SampleTG(mu, sigma, a, b):
    """
    Sample from
    """

    if sigma <= 0.0:
        raise ValueError("Sample: non-positive sigma")

    # for a moment, work with true bounded gaussian
    if math.isinf(a):
        raise ValueError("Sample: infinite a")
    if math.isinf(b):
        raise ValueError("Sample: infinite b")

    if a >= b:
        raise ValueError("Sample: a >= b")

    while True:
        r = random.gauss(mu, sigma)
        if r >= a and r <= b:
            return r

    return math.Inf

def Mean(mu, sigma, a, b):
    """
    Mean for truncated gaussian
    """

    if sigma <= 0.0:
        raise ValueError("Mean: non-positive sigma")

    # for a moment, work with true bounded gaussian
    if math.isinf(a):
        raise ValueError("Mean: infinite a")
    if math.isinf(b):
        raise ValueError("Mean: infinite b")

    if a >= b:
        raise ValueError("Mean: a >= b")

    alfa = (a - mu) / sigma
    beta = (b - mu) / sigma

    Z = Phi(beta) - Phi(alfa)

    return mu + sigma*(phi(alfa) - phi(beta))/Z

def f(mu, mean, sigma, a, b):
    """
    Function to search for true mu when particular mean is requested
    Root of this function would be right mu
    """
    return mean - Mean(mu, sigma, a, b)

if __name__ == "__main__":

    random.seed(12345)

    # some test printouts
    # print(phi(0.0))
    # print(Phi(0.0))

    a = 50000.0
    b = 250000.0
    mean = 70000.0
    sigma = 24000.0

    mu = scipy.optimize.brentq(f, a, b, args=(mean, sigma, a, b))
    print("Found mu = {0} for the desired mean {1} and sigma {2}".format(mu, mean, sigma))

    # test sampling

    N  = 100000
    s  = 0.0
    s2 = 0.0
    for k in range(0, N):
        q   = SampleTG(mu, sigma, a, b)
        if q < a:
            raise ValueError("Test: sampled value below a")
        if q > b:
            raise ValueError("Test: sampled value above b")
        s  += q
        s2 += q*q

    print("Sampled {0} truncated gaussians and got observed mean = {1}".format(N, s/float(N)))


"""
The prime factors of 13195 are 5, 7, 13 and 29.
What is the largest prime factor of the number 600851475143 ?

https://projecteuler.net/problem=3
"""

import numpy as np

# To determine if a number n is prime, we need to:
# 1. check whether n is evenly divisible by 2
# 2. check whether n is evenly divisible by one of the uneven numbers from 3 to sqrt(n) + 1
# 3. if neither 1 nor 2 apply, n is prime

# To find the largest prime factor of n, we repeatedly divide n by its smallest prime factor
# until we can't divide it further.

def find_smallest_prime_factor(number):
    upper_bound = int(np.sqrt(number)) + 1

    for i in range(2, upper_bound):
        if number % i == 0:
            return i

    return number

def find_largest_prime_factor(number):
    while True:
        smallest_factor = find_smallest_prime_factor(number)

        if smallest_factor < number:
            number //= smallest_factor
        else:
            return number

result = find_largest_prime_factor(600851475143)
print('result: ', result)


#!/usr/local/bin/python3
###!/usr/local/bin/python3

###	#!/Users/zhiyang/anaconda3/bin/python3

import numpy as np
# From https://docs.python.org/3/library/statistics.html
import statistics as s

print("============================================")

print("= Experiments to determine the arithmetic mean, or average, of the absolute value of numbers in a list.")

# Python code to illustrate
# abs() built-in function

# floating point number
float = -54.26
print('Absolute value of float -54.26 is:', abs(float),"=")

# An integer
int = -94
print('Absolute value of integer -94 is:', abs(int),"=")

# A complex number
complex = (3 - 4j)
print('Absolute value, or magnitude, of complex number (3 - 4j) is:', abs(complex),"=")

"""
	Methods to find the average number of numbers in a list.

	https://www.geeksforgeeks.org/find-average-list-python/
"""

my_list = [2, -3, 5, -9, -6, 8]
print("my_list is:",my_list,"=")
abs_my_list = []
for elem in my_list:
	abs_my_list.append(abs(elem))
print("abs_my_list:",abs_my_list,"=")

abs_my_list_2 = []
for index, elem in enumerate(my_list):
	#print("index:",index,"=")
	abs_my_list_2.insert(index,abs(elem))
print("abs_my_list_2:",abs_my_list_2,"=")

abs_my_list_3 = np.absolute(my_list)
print("abs_my_list_3:",abs_my_list_3,"=")
"""
	Convert the NumPy array into a (Python) list.
	This makes the NumPy.absolute() solution acceptable.

	Reference:
	+ user3654478, Answer to "How to use a decimal range() step value?,"
		Stack Exchange Inc., New York, NY, February 12, 2016.
		Available online from Stack Exchange Inc.: Stack Overflow: Questions
			at: https://stackoverflow.com/a/35362489/1531728 and
			https://stackoverflow.com/questions/477486/how-to-use-a-decimal-range-step-value/35362489#35362489;
			March 4, 2020 was the last accessed date.
"""
abs_my_list_4 = np.absolute(my_list).tolist()
print("abs_my_list_4:",abs_my_list_4,"=")

#sum_of_absolute_numbers = sum(abs(my_list))
sum_of_absolute_numbers = sum(abs_my_list)
print("sum_of_absolute_numbers:",sum_of_absolute_numbers,"=")
#average_of_absolute_numbers = sum(abs(my_list))/len(my_list)
average_of_absolute_numbers = sum(abs_my_list)/len(abs_my_list)
print("average_of_absolute_numbers:",average_of_absolute_numbers,"=")
average_of_absolute_numbers = np.mean(abs_my_list)
print("average_of_absolute_numbers, via NumPy:",average_of_absolute_numbers,"=")
average_of_absolute_numbers = s.mean(abs_my_list)
print("average_of_absolute_numbers, via statistics module/package:",average_of_absolute_numbers,"=")
average_of_absolute_numbers = sum(abs_my_list_3)/len(abs_my_list_3)
print("average_of_absolute_numbers, for abs_my_list_3 - NumPy array:",average_of_absolute_numbers,"=")
print("	The following solution produces a rounded-off/down solution.")
average_of_absolute_numbers = s.mean(abs_my_list_3)
print("average_of_absolute_numbers, via NumPy and absolute():",average_of_absolute_numbers,"=")

print("============================================")

print("= Experiments with the 'None' object, and boolean comparisons using the 'is' operator and the '==' operator.")
print("")

x = None
if x:
	print("if 'x'.")
else:
	print("not 'if x'.")

if x is not None:
	print("if 'x' is not None.")
else:
	print("else 'x' isn't 'not None'; or, 'x' is None.")

if x is None:
	print("if 'x' is None.")
else:
	print("else, 'x' is not None.")

"""
	In this case, they are the same. None is a singleton object (there only ever exists one None).
	is checks to see if the object is the same object, while == just checks if they are equivalent.

	Reference:
		https://stackoverflow.com/a/3257951/1531728
"""
p = [1]
q = [1]
if p is q:
	print("p is q; [1] is [1]; something is wrong!!! They point to different objects.")
else:
	print("False because they are not the same actual object.")
if p == q:
	print("p == q; [1] == [1]")
	print("True because they are equivalent.")
else:
	print("p != q; [1] != [1]; something is wrong!!! They are supposed to be equivalent in value/content.")
# But since there is only one None, they will always be the same, and is will return True.
p = None
q = None
if p is q:
	print("True because they are both pointing to the same 'None'.")
else:
	print("Something is wrong, since 'p' and 'q' are supposed to point to the same/only 'None' object.")

print("--------------------------------------------")

try:
	print("Dividing a number, 23, by 0", 23/0,"=")
except ZeroDivisionError:
	print("Cannot dividing 23 by 0.")


"""
Write a Python code that fulfills the following specification.
dataset: Female_Stats.csv

The Data Are From 214 Females In Statistics Classes At
The University Of California At Davis.

Column1 = Student’s Self-Reported Height,

Column2 = Student’s Guess At Her Mother’s Height, And

Column 3 = Student’s Guess At Her Father’s Height.

All Heights Are In Inches.

task01:
Build A Predictive Model And Conclude If Both Predictors
(Independent Variables) Are Significant For A Students’ Height Or Not?
(Use pvalue concepts).

task02:
When Father’s Height Is Held Constant,
The Average Student Height Increases
By How Many Inches For Each One-Inch Increase In Mother’s Height.

task03:
When Mother’s Height Is Held Constant,
The Average Student Height Increases
By How Many Inches For Each One-Inch Increase In Father’s Height.

"""

# Importing the libraries
import numpy as np
import pandas as pd

# Importing the dataset
dataset = pd.read_csv('Female_Stats.csv')

# Check data Types for each columns
print(dataset.dtypes)

# Seperate Features and Labels
features = dataset.iloc[:,1:].values
labels = dataset.iloc[:, [0]].values

# Check Column wise is any data is missing or NaN
dataset.isnull().any(axis=0)

from sklearn.model_selection import train_test_split
features_train, features_test, labels_train, labels_test = train_test_split(features, labels, test_size = 0.2, random_state = 0)

# Fitting Multiple Linear Regression to the Training set
# Whether we have Univariate or Multivariate, class is LinearRegression

from sklearn.linear_model import LinearRegression
regressor = LinearRegression()
regressor.fit(features_train, labels_train)

Pred = regressor.predict(features_test)

print (pd.DataFrame(zip(np.round(Pred,2), labels_test)))

import statsmodels.api as sm

features_sm = sm.add_constant(features)
est = sm.OLS(labels, features_sm)
est2 = est.fit()

print (est2.summary())

"""
as both columns ( mom and dad are having p values less than 5%, both
                 heights are significant for student's height)
"""

"""

When Father’s Height Is Held Constant,
The Average Student Height Increases
By How Many Inches For Each One-Inch Increase In Mother’s Height.

"""

print (regressor.coef_[0][0])

"""

When Mother’s Height Is Held Constant,
The Average Student Height Increases
By How Many Inches For Each One-Inch Increase In Father’s Height.

"""
print (regressor.coef_[0][1])

# Version 2 of solution

import pandas as pd
import numpy as np

dataset=pd.read_csv("stats_females.csv")

features=dataset.iloc[:,1:]
labels=dataset.iloc[:,0]

from sklearn.model_selection import train_test_split
features_train,features_test,labels_train,labels_test=train_test_split(features,labels,test_size=0.2,random_state=0)

from sklearn.linear_model import LinearRegression
reg=LinearRegression()
reg.fit(features_train,labels_train)

import statsmodels.formula.api as sm
features=np.append(arr=np.ones((214,1)).astype(int),values=features,axis=1)

features_opt=features[:,[0,1,2]]
regressor_OLS=sm.OLS(labels,features_opt).fit()
regressor_OLS.summary()

"""
When Father’s Height Is Held Constant, The Average Student Height Increases
By How Many Inches For Each One-Inch Increase In Mother’s Height.
"""
print("When Father's Height is Held Constant then the average height increase by",regressor_OLS.params[1])

print("When Mother's Height is Held Constant then the average height increase by",regressor_OLS.params[2])


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

def gaussian_2d_kernel(ker_shape, sigm):
    """
    Creates gaussian kernel

    :param ker_shape: Used in defining kernel's shape
    :param sigm: sigma parameter for equation
    :return: Gaussian kernel with shape of `ker_shape`
    """
    ker = np.zeros(ker_shape)
    ind = np.arange(-np.floor(ker_shape[0] / 2), np.floor(ker_shape[1] / 2) + 1, 1)
    XX, YY = np.meshgrid(ind, ind)

    for row in range(ker_shape[0]):
        for col in range(ker_shape[1]):
            ker[row, col] = (1 / 2 * np.pi * sigm ** 2) * np.exp(
                (-1 * (XX[0, col] ** 2 + YY[row, 0] ** 2) / (2 * sigm ** 2)))

    return ker

def laplacian_of_gaussian_2d_kernel(ker_shape, sigm):
    """
    Creates laplacian of gaussian kernel

    :param ker_shape: Used in defining kernel's shape
    :param sigm: sigma parameter for equation
    :return: LoG kernel with shape of `ker_shape`
    """
    ker = np.zeros(ker_shape)
    ind = np.arange(-np.floor(ker_shape[0] / 2), np.floor(ker_shape[1] / 2) + 1, 1)
    XX, YY = np.meshgrid(ind, ind)

    for row in range(ker_shape[0]):
        for col in range(ker_shape[1]):
            ker[row, col] = (-1 / (np.pi * sigm ** 4)) * (1 - (XX[0, col] ** 2 + YY[row, 0] ** 2) / (2 * sigm ** 2)) * \
                               np.exp((-1 * (XX[0, col] ** 2 + YY[row, 0] ** 2) / (2 * sigm ** 2)))

    return -ker

def normalize(arr):
    """
    Normalize image between 0-255

    :param arr: Going to be normalized array
    :return: Normalized array
    """
    arr = arr / arr.max(initial=0) * 255
    arr = np.uint8(arr)
    return arr

def apply_filter(arr, ker):
    """
    Makes convolution operation

    :param arr: Going to be convolved
    :param ker: Convolution operation kernel
    :return: Convolved image
    """
    return sig.convolve2d(arr, ker, mode='same')

def show_image(*argv):
    """
    Plot images in order

    :param argv: Needs to be passed grayscale images
    """
    for arg in argv:
        plt.imshow(arg, cmap='gray')
        plt.show()

if __name__ == '__main__':
    try:
        img = cv2.imread('../test_images/test.png', cv2.IMREAD_GRAYSCALE)

        if img is None:
            raise ValueError("File don't exist!")

        else:
            sigma = 1
            kernel_shape = (7, 7)

            gaussian_kernel = gaussian_2d_kernel(kernel_shape, sigma)
            log_kernel = laplacian_of_gaussian_2d_kernel(kernel_shape, sigma)

            gaussian_result = normalize(apply_filter(img, gaussian_kernel))
            log_result = normalize(apply_filter(img, log_kernel))
            log_after_gaussian_result = normalize(apply_filter(apply_filter(img, gaussian_kernel), log_kernel))

            show_image(img, gaussian_result, log_result, log_after_gaussian_result)

    except ValueError as ve:
        print(ve)


import numpy as np

def sigmoid_activation(z):
    """
    Sigmoid (or “logit”) activation function.

    converts a continuous input into a probability value [0,1],
        which can be interpreted as the class probability,
        or the likelihood that the input example should be classified positively.
    Using this probability along with a threshold value, we can obtain a discrete label prediction.
    """
    return 1 / (1 + np.exp(-z))

# # visualize the sigmoid activation function’s output (to see what it’s really doing):
# nums = np.arange(-10, 10, step=1)
# fig, ax = plt.subplots(figsize=(12,8))
# ax.plot(nums, sigmoid(nums), 'r')

def mse_loss(X, y, theta):
    """
    Mean Squared Error (MSE) loss.
    The cost function for linear regression.
    """
    y_pred = X * theta.T
    error = y_pred - y
    return np.sum(np.power(error, 2)) / (2 * len(X))

def log_loss(theta, x, y, learning_rate=None):
    """
    Binary cross-entropy / log loss.
    The cost function for binary (linear) logistic regression.

    Evaluates the current model's parameters' performance on the training data.
    Determines: "given some candidate solution theta applied to input X,
        how far off is the result from the true desired outcome y".

    Note that we reduce the output down to a single scalar value, which is the sum of the “error” quantified as a
    function of the difference between the class probability assigned by the model and the true label of the example.
    The implementation is completely vectorized – it’s computing the model’s predictions for the whole dataset
    in one statement (sigmoid(X * theta.T)).
    the variable called “reg” is a function of the parameter values.
        As the parameters get larger, the penalization added to the cost function increases.
    the “learning rate” parameter is also part of the regularization term in the equation.
        The learning rate gives us a new hyper-parameter that we can use to tune how much weight the regularization holds in the cost function.
    """
    # theta = np.matrix(theta)
    # x = np.matrix(X)
    # y = np.matrix(y)
    first = np.multiply(-y, np.log(sigmoid_activation(x * theta.T)))
    second = np.multiply((1 - y), np.log(1 - sigmoid_activation(x * theta.T)))
    if learning_rate is None:
        return np.sum(first - second) / (len(x))
    else:
        reg = (learning_rate / 2 * len(x)) * np.sum(np.power(theta[:, 1:theta.shape[1]], 2))
        return np.sum(first - second) / (len(x)) + reg

def softmax_loss(theta, x, y, learning_rate=None):
    """
    Categorical cross-entropy / softmax loss.
    The cost function for multi-class logistic regression.
    """
    # theta = np.matrix(theta)
    # x = np.matrix(X)
    # y = np.matrix(y)
    first = np.multiply(-y, np.log(sigmoid_activation(x * theta.T)))
    second = np.multiply((1 - y), np.log(1 - sigmoid_activation(x * theta.T)))
    if learning_rate is None:
        return np.sum(first - second) / (len(x))
    else:
        reg = (learning_rate / 2 * len(x)) * np.sum(np.power(theta[:, 1:theta.shape[1]], 2))
        return np.sum(first - second) / (len(x)) + reg

def single_gradient_step(theta, x, y, learning_rate=None):  # gradient
    """
    a function to compute the gradient of the model parameters to figure out how to change the parameters
    to improve the outcome of the model on the training data.
    Recall that with gradient descent we don’t just randomly jigger around the parameter values and see what works best.
    At each training iteration we update the parameters in a way that’s guaranteed to move them in a direction
    that reduces the training error (i.e. the “cost”).
    We can do this because the cost function is differentiable.
    Note that we don't actually perform gradient descent in this function - we just compute a single gradient step.
    the gradient function specifies how to change those parameters to get an answer that's slightly better than the one we've already got
    """
    # theta = np.matrix(theta)
    # x = np.matrix(X)
    # y = np.matrix(y)

    error = sigmoid_activation(x * theta.T) - y

    # more generalized way:
    if learning_rate is None:
        grad = ((x.T * error) / len(x)).T
    else:
        reg = (learning_rate / len(x)) * theta
        grad = ((x.T * error) / len(x)).T + reg

    grad[0, 0] = np.sum(np.multiply(error, x[:, 0])) / len(x)  # intercept gradient is not regularized
    return np.array(grad).ravel()

    # more specific way:
    # parameters = int(theta.ravel().shape[1])
    # grad = np.zeros(parameters)
    # for i in range(parameters):
    #     term = np.multiply(error, X[:, i])
    #     if learning_rate is None or i == 0:  # the first parameter is not regularized, it's considered the “bias” or “intercept” of the model and shouldn’t be penalized
    #         grad[i] = np.sum(term) / len(X)
    #     else:
    #         reg = (learningRate / len(X)) * theta[:, i]
    #         grad[i] = (np.sum(term) / len(X)) + reg
    # return grad


import codecademylib3_seaborn
import matplotlib.pyplot as plt
import numpy as np
from sklearn import datasets
from copy import deepcopy

iris = datasets.load_iris()

samples = iris.data
samples = iris.data

x = samples[:,0]
y = samples[:,1]

sepal_length_width = np.array(list(zip(x, y)))

# Step 1: Place K random centroids

k = 3

centroids_x = np.random.uniform(min(x), max(x), size=k)
centroids_y = np.random.uniform(min(y), max(y), size=k)

centroids = np.array(list(zip(centroids_x, centroids_y)))

# Step 2: Assign samples to nearest centroid

def distance(a, b):
  one = (a[0] - b[0]) **2
  two = (a[1] - b[1]) **2
  distance = (one+two) ** 0.5
  return distance

# Cluster labels for each point (either 0, 1, or 2)
labels = np.zeros(len(samples))

# Distances to each centroid
distances = np.zeros(k)

for i in range(len(samples)):
  distances[0] = distance(sepal_length_width[i], centroids[0])
  distances[1] = distance(sepal_length_width[i], centroids[1])
  distances[2] = distance(sepal_length_width[i], centroids[2])
  cluster = np.argmin(distances)
  labels[i] = cluster

# Step 3: Update centroids

centroids_old = deepcopy(centroids)

for i in range(k):
  points = [sepal_length_width[j] for j in range(len(sepal_length_width)) if labels[j] == i]
  centroids[i] = np.mean(points, axis=0)

print(centroids_old)
print("- - - - - - - - - - - - - -")
print(centroids)

Data Scientist Career Path/12. Foundations of Machine Learning Unsupervised Learning/1. KMeans/5. step3.py


import numpy as np

def computeCost(X, y, theta):
    m = len(X)
    # Matrix multiplication of X and theta which is the hypothesis function
    hypothesis_X = np.matmul(X, theta)
    #print('shape', hypothesis_X.shape)
    # Calculating difference of hypothesis and y
    dif = hypothesis_X - y
    # print('shape', dif.shape)
    # Getting the Square of each individual element value
    # Element wise product - we can also use np.multiply(dif, dif)
    dif_sqr = dif * dif
    # Generating a row vector to multiply to get the sum of all the elements in the matrix
    #print('shape', dif_sqr.shape)
    ones = np.ones(m)
    #print('shape', ones.shape)
    # Multiplying row vector and difference square to get cost vector
    cost_mat = (0.5/m) * np.matmul(ones, dif_sqr)
    # Converting matrix 1 by 1 Ex: [5] to scalar value 5
    cost_scalar = np.asscalar(cost_mat)
    return cost_scalar


'''
Power Iteration method of finding dominant eigenvector.
	source : https://www.wikiwand.com/en/Power_iteration
'''
import numpy as np
from numpy.linalg import norm

def power_iteration( A, b0, tol = 0.0000001 ):
	n = len( b0 )
	b1 = getBi( A, b0, n, tol )
	while norm( np.subtract( b1, b0 ), ord = n ) > tol:
	# for i in range(10000):
		b0 = b1
		b1 = getBi( A, b0, n )
	return b1

def getBi( A, b, n, tol = 0.0000001 ):
	b = np.dot( A, b )
	b_norm = norm( b, ord = n )
	if b_norm < tol:
		b /= 0.0000001
	else:
		b /= b_norm
	return b

def __test():
	A = np.array( [ [ 0.5, 0.5 ], [ 0.2, 0.8 ] ] )
	b = np.random.rand( 2 )
	return power_iteration( A, b )#, np.linalg.eig( A )[1]

def __random_test( n ):
	A = np.random.rand( n, n )
	b = np.random.rand( n )
	return power_iteration( A, b )#, np.linalg.eig( A )[1]

if __name__ == '__main__':
	print __test()
	print __test()
	for i in range(10, 151):
		__random_test( i )
	print 'Random square matrices of sizes 10-150 completed.'


"""
Objetivo: Resolver questão 2a do terceiro laboratorio.
    é exatamente o mesmo codigo da questão anterior porém mudando a função a ser passada e o palpite
"""
import matplotlib.pyplot as plt
import numpy as np
from Newton_Raphson import newton_raphson

#PARA X0 = 1
y = lambda x: 180*x**3 - 117*x**2 - 80*x + 52  # f(x) = 180x^3 - 117x^2 - 80x + 52
y_linha = lambda x: 540*x**2 - 234*x - 80  # f'(x) = 540x^2 - 234x - 80
ini = 1  # ponto inicial
#Verificar o arquivo Nexton-Raphson.py para entender o funcionamento dessa função
resp = newton_raphson(ini, y, y_linha, 'q2')
print(f'Para essa X0 = 1 levamos {resp["iterações"] - 1} iterações e temos como valor de f(x) = {resp["modulo"]}')
print('Abaixo seguem os graficos dos valores de f(x) e dos erros, respectivamente:')

# Grafico com os valores de f(x)
x_a = np.linspace(-1.2, 1.2, 30)  # valores escolhidos para melhor visualização do grafico
y_a = [y(i) for i in x_a]
plt.style.use('ggplot')
plt.figure(figsize=(7, 5))
plt.title(f'F(x) por X\ncom X0 = 1')
plt.xlabel('Valores de x')
plt.ylabel('Valores de f(x)')
# função com valores entre 0.4 e 0.8
plt.plot(x_a, y_a, label='Função exata')
# Nossa aproximação Newton-Raphson por iteração
plt.plot(resp['valores'], resp['valores_função'], label='Valores de Newton-Raphson por iteração')
plt.tight_layout()
plt.legend(loc='best')
plt.show()

# Grafico com os valores dos erros
plt.style.use('ggplot')
plt.figure(figsize=(7, 5))
plt.title('Erro por Iterações')
plt.xlabel('Numero de iterações')
plt.ylabel('Valores dos erros')
plt.plot([i for i in range(1, resp['iterações'])], resp['erros'])
plt.tight_layout()
plt.show()

#PARA X0 = -1
y = lambda x: 180*x**3 - 117*x**2 - 80*x + 52  # f(x) = 180x^3 - 117x^2 - 80x + 52
y_linha = lambda x: 540*x**2 - 234*x - 80  # f'(x) = 540x^2 - 234x - 80
ini = -1  # ponto inicial
#Verificar o arquivo Nexton-Raphson.py para entender o funcionamento dessa função
resp = newton_raphson(ini, y, y_linha, 'q2')
print(f'Para essa X0 = -1 levamos {resp["iterações"] - 1} iterações e temos como valor de f(x) = {resp["modulo"]}')
print('Abaixo seguem os graficos dos valores de f(x) e dos erros, respectivamente:')

# Grafico com os valores de f(x)
x_a = np.linspace(-1.2, 1.2, 30)  # valores escolhidos para melhor visualização do grafico
y_a = [y(i) for i in x_a]
plt.style.use('ggplot')
plt.figure(figsize=(7, 5))
plt.title(f'F(x) por X\ncom X0 = -1')
plt.xlabel('Valores de x')
plt.ylabel('Valores de f(x)')
# função com valores entre 0.4 e 0.8
plt.plot(x_a, y_a, label='Função exata')
# Nossa aproximação Newton-Raphson por iteração
plt.plot(resp['valores'], resp['valores_função'], label='Valores de Newton-Raphson por iteração')
plt.tight_layout()
plt.legend(loc='best')
plt.show()

# Grafico com os valores dos erros
plt.style.use('ggplot')
plt.figure(figsize=(7, 5))
plt.title('Erro por Iterações')
plt.xlabel('Numero de iterações')
plt.ylabel('Valores dos erros')
plt.plot([i for i in range(1, resp['iterações'])], resp['erros'])
plt.tight_layout()
plt.show()


#!/usr/bin/python

import sys
import time
import numpy as np

print('MATRIZ UNIDIMENCIONAL Y BIDIMENCIONAL')
a=np.array([1,2,3])
print('1D array:')
print(a)
print()
b=np.array([(1,2,3),(4,5,6)])
print('2D array:')
print(b)

print('-------------')
print('LISTA DE MIL NUMEROS Y CALCULAMOS LA MEMORIA ASIGNADA A ESTA')
s = range(1000)
print('Resultado lista de Python:')
print(sys.getsizeof(5)*len(s))
print()
d = np.arange(1000)
print('Resutado NumPy array:')
print(d.size*d.itemsize)

print('-------------')
print('MODULO PARA EVALUAR LA RAPIDEZ EN LA QUE SE EJECUTAN ESTAS INSTRUCCIONES')
SiZE = 1000000
L1 = range(SiZE)
L2 = range(SiZE)
A1 = np.arange(SiZE)
A2 = np.arange(SiZE)

start = time.time()
result = [(x,y) for x,y in zip(L1, L2)]
print('Resultado lista de Python:')
print((time.time()-start)*1000)
print()
start = time.time()
result = A1 + A2
print('Resultado NumPy array:')
print((time.time()-start)*1000)

print('-------------')
print('CREAR UNA MATRIZ DE UNOS --> 3 FILAS Y 4 COLUMNAS ')
unos = np.ones((3,4))
print(unos)

print('CREAR UNA MATRIZ DE CEROS --> 3 FILAS Y 4 COLUMNAS ')
ceros = np.zeros((3,4))
print(ceros)

print('CREAR UNA MATRIZ DE NUMEROS ALEATORIOS --> 3 FILAS Y 4 COLUMNAS ')
aleatorios = np.random.random((3,4))
print(aleatorios)

print('CREAR UNA MATRIZ VACIA --> 3 FILAS Y 4 COLUMNAS ')
vacia = np.empty((3,4))
print(vacia)

print('CREAR UNA MATRIZ DE UN SOLO VALOR --> 3 FILAS Y 4 COLUMNAS ')
full = np.full((3,4),5)
print(full)

print('CREAR UNA MATRIZ CON VALORES ESPACIADOS UNIFORMEMENTE')
"""
Realiza una matriz de los números que se encuentran entre el cero y 30
haciendo saltos de 5 en 5
"""
espacio1 = np.arange(0,30,5)
print(espacio1)
"""
Realiza una matriz de los de 5 valores con los números que se encuentran
entre el 0 y 2
"""
espacio2 = np.linspace(0,2,5) #
print(espacio2)

print('CREAR UNA MATRIZ ENTIDAD')
identidad1 = np.eye(4,4)
print(identidad1)
identidad2 = np.identity(4)
print(identidad2)

print('CONOCER LAS DIMENCIONES DE UNA MATRIZ')
a = np.array([(1,2,3),(4,5,6)])
print(a.ndim)
print('CONOCER EL TIPO DE LOS DATOS')
print(a.dtype)
print('CONOCER EL TAMAÑO DE LA MATRIZ')
print(a.size)
print('CONOCER EL FORMA DE LA MATRIZ')
print(a.shape)

print('CAMBIO DE FORMA DE UNA MATRIZ')
print(a)
b = a.reshape(3,2)
print(b)

"""
Operaciones matemáticas
"""
print('ENCONTRAR EL MINIMO, MAXIMO Y SUMA')
c = np.array([9,8,7,6,5,4,3,2,1])
print(c.min())
print(c.max())
print(c.sum())
print('CALCULAR LA RAÍZ CUADRADA Y DESVIACIÓN ESTÁNDAR')
d = np.array([(9,8,7,6,5), (4,3,2,1,0)])
print(np.sqrt(d))
print(np.std(d))
print('CALCULAR LA SUMA, RESTA, MULTIPLICACIÓN Y DIVISIÓN DE DOS MATRICES')
x = np.array([(1,3,5,7,9),(2,4,6,8,10)])
y = np.array([(10,9,8,7,6), (5,4,3,2,1)])
print(x+y)
print(x-y)
print(x*y)
print(x/y)


"""
Demonstrate Calculations with Complex Numbers
Learning Example for Dr. Stanier's classes
File: E7_complex_numbers.py

Author: Charles Stanier, charles-stanier@uiowa.edu
Date:  August 27, 2019
Written/Tested In: Python 3.7.3

Program Objective: Demonstrate how to manipulate complex numbers
Showing some of the features of cmath at https://docs.python.org/2/library/cmath.html

Modifications: none so far

"""

import numpy as np  # this is used for math functions
import pylab as pl  # this is used for plotting functions

# super simple, let's take the square root of -1
x = -1
# y = np.sqrt(x) # this generates and error
y1 = x**0.5

# but if we define x as complex and use np.sqrt it works
x = -1+0j
y2 = np.sqrt(x)

print('Square root of -1 using method 1: ', y1)
print('Square root of -1 using method 2: ', y2)

# extract the real and imaginary portions and print

y2r = np.real(y2)
y2i = np.imag(y2)

print('y2 is equal to ',y2r,' plus ',y2i,'i')

# now lets deal with a vector, take the square root, and plot on complex plane
real_vec = np.linspace(-5,5,100)
imag_vec = np.ones(real_vec.size)*1j  # j is "i" the sqrt of -1
x_vec = real_vec + imag_vec
# take the square root of those 100 values
x_sqrt = np.sqrt(x_vec)

# for complex plot, we make the real part the x vector
# and the imag part the y vector
plotx = np.real(x_sqrt)
ploty = np.imag(x_sqrt)

# use pylab to plot x and y
pl.plot(plotx, ploty, 'o', color='black')
pl.xlabel('Real Portion')
pl.ylabel('Imag Portion')

# show the plot on the screen
pl.show()


import math
import pandas as pd
import numpy as np

def prime(num):
    if num >= 1:
        root_num = int(math.sqrt(num))
        # print(root_num)
        for i in range(2, root_num+1, 1):
            # print(i)
            if (num % i) == 0:
                print("Number is not prime")
                break
        else:
            print("Prime Number")

lst = []
def prime_in_range(num_till):
    for i in range(num_till+1):
        if i>=1:
            root_num = int(math.sqrt(i))
            for j in range(2,root_num,1):
                if i%j==0:
                    # print("Not Prime Number")
                    break
            else:
                lst.append(i)

def factorial(num):
    """This is a recursive function that calls
   itself to find the factorial of given number"""

    if num == 1:
        return num
    else:
        # print("lofh")
        return num * factorial(num-1)

if __name__ == "__main__":
    num = int(input("Enter number to find  : "))

    prime(num)
    prime_in_range(num)
    print("Prime Numbers in the Range : ", lst)
    if num < 0:
        print("Factorial cannot be found for negative numbers")
    elif num == 0:
        print("Factorial of 0 is 1")
    else:
        print("Factorial of", num, "is: ", factorial(num))


#!/usr/bin/env python3

# Instalação
# - pip install -r requirements.txt
# - pip install numpy

import numpy as np

# Numpy: biblioteca para computação científica - funções bem otimizadas
a = np.array([1, 2, 3])
print(type(a))
print(a.shape)
print(a[0], a[1], a[2])

a[0] = 5
print(a)

b = np.array([[1, 2, 3],
              [4, 5, 6]])
print(b.shape)
print(b[0, 0], b[0, 1], b[1, 0])

# Tupla: tipo de dados imutável
tupla = (1, 2, 3, 4)
print(tupla[0])

# Cria uma matriz com zeros
a = np.zeros((2, 2))
print(a)

# Cria uma matriz com uns
b = np.ones((2, 2))
print(b)

# Cria uma matriz com 7s
c = np.full((2, 2), 7)
print(c)

# Cria uma matriz identidade
d = np.eye(2)
print(d)

# Cria uma matriz aleatória
e = np.random.random((2, 2))
print(e)

# Seleciona uma fatia da matriz
a = np.array([[1, 2, 3, 4],
              [5, 6, 7, 8],
              [9, 10, 11, 12]])
b = a[:2, 1:3]
print(b)

# lista[2:3]

print('A =\n', a)
print('B =\n', b)

b[0, 0] = 77

print('A =\n', a)

# Indexação booleana
a = np.array([[1, 2], [3, 4], [5, 6]])

bool_idx = (a > 2)
print(bool_idx)

print(a[bool_idx])
print(a[a > 2])

# Tipos de dados
x = np.array([1, 2])
print(x.dtype)

x = np.array([1.0, 2.0])
print(x.dtype)

x = np.array([1, 2], dtype=np.int64)
print(x.dtype)

# """### Operações"""

x = np.array([[1, 2], [3, 4]], dtype=np.float64)
y = np.array([[5, 6], [7, 8]], dtype=np.float64)

# print(x + y)
print(np.add(x, y))

# print(x - y)
print(np.subtract(x, y))

# print(x * y)
print(np.multiply(x, y))

# print(x / y)
print(np.divide(x, y))

print(np.sqrt(x))

x = np.array([[1, 2], [3, 4]])
y = np.array([[5, 6], [7, 8]])

print(x.dot(y))
# print(np.dot(x, y))

v = np.array([9, 10])
w = np.array([11, 12])

print(v.dot(w))
# print(np.dot(v, w))

# Transposta de uma matriz
x = np.array([[1, 2], [3, 4]])

print(x)
print(x.T)


import scipy.stats as scs
import numpy as np
# import statistics as st

sample_1 = [4.15848606,  3.86146363,  4.31545726,  3.3748772,
            4.67023082, 4.45950272,  3.85894915,  4.41089417,
            3.82360986,  3.79889443, 4.75884172,  3.27100914,
            4.08939402,  4.08904694,  5.62589842, 3.71445656,
            3.58463792,  4.42426443,  3.9671448 ,  4.39339124]

sample_2 = [10.81261135, 9.68035252, 9.87293556,  10.06308861,
            9.57381722, 10.00922156, 10.90522431, 9.70843104,
            10.16614481, 10.09447189, 10.51260742, 10.17503686,
            10.38718472, 10.52334431, 9.55808306, 10.24290938,
            10.6048062 , 10.27535938, 9.6329808 ,  9.67338239]

def one_sample_ttest(sample, mu):
    """INPUT:
    - sample(LIST) [Values in the sample]
    - mu(FLOAT) [The hypothesized mean value of the population]

    OUTPUT:
    - results(TUPLE) [Tuple containing t-statistic(FLOAT) and p-value(FLOAT)]
    """
    pass

def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    '''
    compare floats for almost-equality in Python?
    from: http://stackoverflow.com/questions/5595425/
            what-is-the-best-way-to-compare-floats-for-almost-equality-in-python
    '''
    return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)

def scipy_vs_my_ttest(sample, mu):
    '''
    Compare the t-statistic(FLOAT) and p-value(FLOAT) of your samples given
    by your function and the scipy.stats library.

    INPUT:
    - sample(LIST) [Values in the sample]
    - mu(FLOAT) [The hypothesized mean value of the population]

    OUTPUT:
    - BOOL if t-statistic(FLOAT) and p-value(FLOAT) are the same for your
    function and the scipy.stats library

    hint: use the isclose function provided for you
    '''
    pass

if __name__ == '__main__':
    print 'First 5 values of sample 1: ', sample_1[:5]
    print 'First 5 values of sample 2: ', sample_2[:5]


import numpy as np

class Activation:
    def step_function(self,x):
        y = x > 0
        return y.astype(np.int)

    #왜 sigmoid?
    # 출력이 0~1 사이이고, 미분이 쉬운 함수들중 가장 그럴듯 해서 많이 사용하게됨. 미분은 exp이 쉽기때문에
    def sigmoid(self,x):
        return 1/(1 + np.exp(-x))

    def RelU(self,x):
        return np.maximum(0,x)

     #왜 softmax?
    # 출력이 0~1 사이이고, 미분이 쉬운 함수들중 가장 그럴듯 해서 많이 사용하게됨. 미분은 exp이 쉽기때문에
    def softMax(self,xs):
        mx = np.max(xs)
        expx = np.exp( xs - mx )
        expsum = np.sum(expx)
        y = expx / expsum
        return y

if __name__ == "__main__":
    a = [ 1 ,3]
    print(Activation().softMax(a))


#Use of classes, 1: initialization part, we tìdefine the input of parameters, for axample the matrix A or the vector y.

import numpy as np
import matplotlib.pyplot as plt

class SolveMinProbl:
    def __init__(self, y=np.ones((6, 1)), A=np.eye(5)): #initialization
        self.matr=A
        self.Np=y.shape[0]
        self.Nf=A.shape[1]
        self.vect=y
        self.sol=np.zeros((self.Nf, 1), dtype=float)
        return
    def plot_w(self, title='Solution'):
        w=self.sol
        n=np.arange(self.Nf)
        plt.figure()
        plt.plot(n, w)
        plt.xlabel('n')
        plt.ylabel('w(n)')
        plt.title(title)
        plt.grid()
        plt.show()
        return
    def print_result(self, title):
        print(title, '_:')
        print("The optimum weight vector is:_")
        print(self.sol)
        return

    def plot_err(self, title='Square_error', logy=0, logx=0):
        err=self.err
        plt.figure()
        if (logy==0) and (logx==0):
            plt.plot(err[:, 0], err[:, 1])
        if (logy == 1) and (logx == 0):
            plt.plot(err[:, 0], err[:, 1])
        if (logy == 0) and (logx == 1):
            plt.semilogy(err[:, 0], err[:, 1])
        if (logy == 1) and (logx == 1):
            plt.loglog(err[:, 0], err[:, 1])
        plt.xlabel('n')
        plt.ylabel('e(n)')
        plt.title(title)
        plt.margins(0.01, 0.1)
        plt.grid()
        plt.show()
        return

class SolveLLS(SolveMinProbl):
    def run(self):
        A=self.matr
        y=self.vect
        w=np.dot(np.dot(np.linalg.inv(np.dot(A.T, A)), A.T), y)
        self.sol=w
        self.min=np.linalg.norm(np.dot(A, w)-y)

class SolveGrad(SolveMinProbl):
    def run(self, gamma, Nit=1000):
        self.err=np.zeros((Nit, 2), dtype=float)
        A=self.matr
        y=self.vect
        w=np.random.rand(self.Nf, 1)
        for it in range(Nit):
            grad=2*np.dot(A.T, (np.dot(A,w)-y))
            w=w-gamma*grad
            self.err[it, 0]=it
            self.err[it, 1]=np.linalg.norm(np.dot(A, w)-y)
        self.sol=w
        self.min=self.err[it, 1]

class SolveSteep(SolveMinProbl):
    def run(self, Nit=1000):
        self.err = np.zeros((Nit, 2), dtype=float)
        A = self.matr
        y = self.vect
        w = np.random.rand(self.Nf, 1)
        for it in range(Nit):
            grad = 2 * np.dot(A.T, (np.dot(A, w) - y))
            H=4*np.dot(A.T, A)
            w=w-np.linalg.norm(grad)**2/np.dot(np.dot(grad.T, H), grad)*grad
            self.err[it, 0] = it
            self.err[it, 1] = np.linalg.norm(np.dot(A, w) - y)
        self.sol = w
        self.min = self.err[it, 1]

class SolveStocha(SolveMinProbl):
    def run(self,Nit=100, gamma=1e-3):
        self.err=np.zeros((Nit, 2), dtype=float)
        A=self.matr
        y=self.vect
        w=np.random.rand(self.Nf, 1)
        for it in range(Nit):
            for i in range(self.Nf):
                grad=gamma*(np.dot(A[i], w)-y[i])*A[i]
                w=w-grad
            self.err[it, 0]=it
            self.err[it, 1]= np.linalg.norm(np.dot(A, w) - y)
        self.sol=w
        self.min=self.err[it, 1]

if __name__=="__main__":
    Np=7
    Nf=7
    A=np.random.randn(Np, Nf)
    y=np.random.randn(Np, 1)
    m=SolveLLS(y, A)
    m.run()
    m.print_result('LLS')
    m.plot_w('LLS')

    gamma=1e-2
    g=SolveGrad(y, A)
    g.run(gamma)
    g.print_result('Gradient_algo')
    logx=0
    logy=1
    g.plot_err('Gradient_algo:_square_error', logx, logy)

    s=SolveSteep(y, A)
    s.run()
    s.print_result('SDA')
    s.plot_err('Steepest_decent_algo:_square_error', logx, logy)

    st=SolveStocha(y, A)
    st.run()
    s.plot_err('Stochastic Algorithm', logx, logy)
    s.print_result('SA')

#with no.random.seed(N) i obtain also the same random values


#!/usr/bin/python3
import numpy as np

"""
X - observation matrix:
    rows - observations; n - number of rows
    cols - values/factors observed

@ operator - matrix multiplication in numpy
"""

# np.loadtxt(open("cereal_cut.csv", "rb"), delimiter=",")

def center_matrix(X):
    """
    centers a matrix by subtracting matrix A with values 1/n from it
    :param X: :type numpy array: input matrix
    :return: :type numpy array:  - centered matrix
    """
    n = len(X)
    A = np.divide(np.ones((n, n)), n)
    M = A @ X

    return X - M

def reduce(X, threshold = 1, leave = None):
    """
    reduces the dimensionality of a observed data by removing the least significant characteristics
    :param X: :type numpy array: input matrix
    :param threshold: :type float: cutoff value for eigenvalues to be considered significant
    :param leave: :type int: fallback if nothing is cut off, leave a fixed number of the most significant axes, None - don't cut off anything
    :return: :type numpy array: reduced matrix
    """
    n = len(X)
    X_hat = center_matrix(X)

    # Sigma
    cov_mtx = np.cov(X_hat, rowvar = False) # (X_hat.transpose() @ X_hat) * (1 / n)  -- difrerent results ???

    cov_eVal, cov_eVec = np.linalg.eigh(cov_mtx)

    cutoff_index = 0
    for ind,eVal in enumerate(cov_eVal):
        if eVal > threshold:
            cutoff_index = ind
            break

    if cutoff_index == 0 and leave:
        cutoff_index = len(eVal) - leave if len(eVal) - leave >= 0 else 0

    return X_hat @ cov_eVec[ : , cutoff_index : ]

if __name__ == "__main__":
    d = np.loadtxt(open("./data/cereal_cut.csv", "rb"), delimiter=",", skiprows=1)
    print(d.shape, d)
    res = reduce(d)
    print(res.shape, res)


import numpy as np
import matplotlib.pylab as plt
from sklearn.decomposition import PCA, TruncatedSVD
from sklearn.datasets import load_iris
from scipy.linalg import svd as SVD

iris = load_iris().data.copy()
labels = load_iris().target.copy()
m = iris.mean(axis=0)
s = iris.std(axis=0)
ir = iris - m
cv = np.cov(ir, rowvar=False)
val, vec = np.linalg.eig(cv)
val = np.abs(val)
idx = np.argsort(val)[::-1]
ex = val[idx] / val.sum()
print("fraction explained: ", ex)
w = np.vstack((vec[:,idx[0]],vec[:,idx[1]]))
d = np.zeros((ir.shape[0],2))
for i in range(ir.shape[0]):
    d[i,:] = np.dot(w,ir[i])

markers = np.array(["o","s","+"])[labels]
for i in range(len(labels)):
    plt.plot(d[i,0], d[i,1], marker=markers[i], color='k', linestyle='none')
plt.xlabel("$x_0$")
plt.ylabel("$x_1$")
plt.tight_layout(pad=0, w_pad=0, h_pad=0)
plt.savefig("iris_pca.png", dpi=300)
plt.close()

pca = PCA(n_components=2)
pca.fit(ir)
dd = pca.fit_transform(ir)
for i in range(len(labels)):
    plt.plot(dd[i,0], dd[i,1], marker=markers[i], color='k', linestyle='none')
plt.xlabel("$x_0$")
plt.ylabel("$x_1$")
plt.tight_layout(pad=0, w_pad=0, h_pad=0)
plt.savefig("iris_pca_sklearn.png", dpi=300)
plt.close()

svd = TruncatedSVD(n_components=2)
svd.fit(ir)
s = svd.fit_transform(ir)
for i in range(len(labels)):
    plt.plot(s[i,0], s[i,1], marker=markers[i], color='k', linestyle='none')
plt.xlabel("$x_0$")
plt.ylabel("$x_1$")
plt.tight_layout(pad=0, w_pad=0, h_pad=0)
plt.savefig("iris_pca_sklearn_svd.png", dpi=300)
plt.close()

#  truncate manually - exact result as sklearn PCA
n_elements = 2
u,s,vt = SVD(ir)
S = np.zeros((ir.shape[0], ir.shape[1]))
for i in range(4):
    S[i,i] = s[i]
S = S[:, :n_elements]
T = u @ S
for i in range(len(labels)):
    plt.plot(T[i,0], T[i,1], marker=markers[i], color='k', linestyle='none')
plt.xlabel("$x_0$")
plt.ylabel("$x_1$")
plt.tight_layout(pad=0, w_pad=0, h_pad=0)
plt.savefig("iris_pca_truncated_svd.png", dpi=300)


import numpy as np
import matplotlib.pyplot as plt

# The function we Want to Solve
def f(x):
    return np.tan(x) - x / (1 - x**2)

if __name__ == "__main__":

    # Plot the Function to Visualize the Root
    x = np.linspace(2,4.5,1000)
    y = f(x)

    plt.plot(x, y)
    plt.xlabel('x')
    plt.ylabel('f(x)')
    plt.tight_layout
    plt.show()

    # Repetitions
    N = 20

    # Region Edges (Find Them in the Diagram of the Function)
    a, b = [2], [4]

    # Counters
    i, j = 0, 0

    for n in range(N):
        # Center of Interval (a,b)
        c = 0.5 * (a[i] + b[j])
        # Where is the Root? In (a,c) or in (c,b)? (Bolzano Theorem)
        if np.sign(f(a[i])) * np.sign(f(c)) == -1.0:
            b += [c]
            j += 1
        elif np.sign(f(c)) * np.sign(f(b[j])) == -1.0:
            a += [c]
            i += 1

    # We Specify the Root as the Center of the Interval (a,b)
    root = 0.5 * (b[j] + a[i])

    print(f'The root of f is: x = {root:.4f}.')
    print(f'The valus of f for the above x is {f(root):.7f}.')


import numpy as np
import matplotlib.pyplot as plt
from scipy import linalg as la

def PCA(dat,center=False,percentage=0.8):
    M=dat[:,0].size
    N=dat[0,:].size
    if center:
	    mu = np.mean(dat,0)
	    dat -= mu

    U,L,Vh = la.svd(dat,full_matrices=False)

    V = (Vh.T).conjugate()
    SIGMA = np.diag(L)
    X = np.dot(U,SIGMA)
    Lam = L**2

    csum = [np.sum(Lam[:i+1])/np.sum(Lam) for i in range(N)]

    normalized_eigenvalues = Lam/np.sum(Lam)
    n_components = np.array([x < percentage for x in csum]).tolist().index(False)

    return (normalized_eigenvalues,
            V[:,0:n_components],
            SIGMA[0:n_components,0:n_components],
            X[:,0:n_components])

def scree(normalized_eigenvalues):
    plt.plot(normalized_eigenvalues,'b-',normalized_eigenvalues,'bo')
    plt.xlabel("Principal Components")
    plt.ylabel("Percentage of Variance")
    plt.show()
    return


#!usr/bin/env python3

import numpy as np
input_matrix = np.array([
    [1, 0.3, 0.3, 0.3],
    [0.3, 1, 0.3, 0.3],
    [0.3, 0.3, 1, 0.3],
    [0.3, 0.3, 0.3, 1]
    ])

def cholesky_decomp(correlationmatrix):
    cholmat = np.zeros_like(correlationmatrix)
    for i in range(len(cholmat)):
        for j in range(i+1):
            if i==j:
                #this computes the diagonal values
                diag = correlationmatrix[i,i] - np.sum(np.square(cholmat[i,:i]))
                #because you cannot sqrt a negative number:
                if diag<0:
                    return 0.0
                cholmat[i,i] = np.sqrt(diag)
            else:
                #computing the rest of the matrix
                cholmat[i,j] = (correlationmatrix[i,j] - np.sum(cholmat[i,:j]*cholmat[j,:j]))/cholmat[j,j]
    return cholmat

cholesky_from_function = cholesky_decomp(input_matrix)
cholesky_from_np = np.linalg.cholesky(input_matrix)
check = np.allclose(cholesky_from_function,cholesky_from_np)

print("\nCorrelation matrix:")
print(input_matrix)
print("\nCholesky from function:")
print(cholesky_from_function)
print("\nCholesky from numpy:")
print(cholesky_from_np)
print("\nAre the two cholesky matrices essentially identical??")
print(check)


from __future__ import division
import numpy as np
from scipy.sparse import spdiags
from scipy.sparse.linalg import spsolve, cg
import matplotlib.pyplot as plt

def fd_order2_ode(func,a1,a2,a3,a=0.,b=1.,alpha=1.,beta=3.,N=5):
	# A Simple Finite Difference Scheme to solve BVP's of the form
	# a1(x)u''(x) + a2(x)u'(x) + a3(x)u(x) = f(x), x \in [a,b]
	# u(a) = alpha
	# u(b) = beta
	# (Dirichlet boundary conditions)
	#
	# U_0 = alpha, U_1, U_2, ..., U_m, U_{m+1} = beta
	# We use m+1 subintervals, giving m algebraic equations
	m = N-1
	h = (b-a)/N		  # Here we form the diagonals
	x = np.linspace(a,b,N+1)
	D0,Dp,Dm,diags = np.zeros((1,m)), np.zeros((1,m)), np.zeros((1,m)), np.array([0,-1,1])

	D0 += -2.*a1(x[1:-1])*h**(-2.) + a3(x[1:-1])
	Dm += a1(x[1:-1])*h**(-2.) - a2(x[1:-1])*(2.*h)**(-1.)
	Dp += a1(x[1:-1])*h**(-2.) + a2(x[1:-1])*(2.*h)**(-1.)
	# print "\nD0 = \n", D0[0,:5]
	# print "\nDm = \n", Dm[0,:5]
	# print "\nDp = \n", Dp[0,:5]
	# Here we create the matrix A
	data = np.concatenate((D0,np.roll(Dm,-1),np.roll(Dp,1)),axis=0) # This stacks up rows
	A = spdiags(data,diags,m,m).asformat('csr')
	print "\nA = \n", A[:5,:5].todense()
	print "\nA = \n", A[-5:,-5:].todense()
	# print A.shape
	# Here we create the vector B
	B = np.zeros(N+1)
	B[2:-2] = func(x[2:-2])
	xj = a+1.*h
	B[0], B[1] = alpha, func(xj)-alpha *( a1(xj)*h**(-2.) - a2(xj)*(2.*h)**(-1.) )
	xj = a+m*h
	B[-1], B[-2]  = beta, func(xj)-beta*( a1(xj)*h**(-2.) + a2(xj)*(2.*h)**(-1.) )
	print "\nB = \n", B[:5]
	print "\nB = \n", B[-5:]

	# Here we solve the equation AX = B and return the result
	B[1:-1] = spsolve(A,B[1:-1])
	return np.linspace(a,b,m+2), B

def approx_order(num_approx,N,bvp,*args):
	h, max_error = (1.-0)/N[:-1], np.ones(num_approx-1)

	mesh_best, num_sol_best = bvp(*args, subintervals=N[-1])
	for j in range(len(N)-1):
		mesh, num_sol = bvp(*args, subintervals=N[j])
		max_error[j] = np.max(np.abs( num_sol- num_sol_best[::2**(num_approx-j-1)] ) )
	plt.loglog(h,max_error,'.-r',label="$E(h)$")
	plt.loglog(h,h**(2.),'-k',label="$h^{\, 2}$")
	plt.xlabel("$h$")
	plt.legend(loc='best')
	plt.show()
	print "The order of the finite difference approximation is about ", ( (np.log(max_error[0]) -
		np.log(max_error[-1]) )/( np.log(h[0]) - np.log(h[-1]) ) ), "."

#
# def example():
#	# First Code block in the lab manual
#	import numpy as np
#	from scipy.sparse import spdiags
#	from scipy.sparse.linalg import spsolve
#
#	def bvp(func, epsilon, alpha, beta, N):
#		a,b = 0., 1.	# Interval for the BVP
#		h = (b-a)/N		# The length of each subinterval
#
#		# Initialize and define the vector F on the right
#		F = np.empty(N-1.)
#		F[0] = func(a+1.*h)-alpha*(epsilon+h/2.)*h**(-2.)
#		F[N-2] = func(a+(N-1)*h)-beta*(epsilon-h/2.)*h**(-2.)
#		for j in xrange(1,N-2):
#			F[j] = func(a + (j+1)*h)
#
#		# Here we define the arrays that will go on the diagonals of A
#		data = np.empty((3,N-1))
#		data[0,:] = -2.*epsilon*np.ones((1,N-1)) # main diagonal
#		data[1,:]  = (epsilon+h/2.)*np.ones((1,N-1))	 # off-diagonals
#		data[2,:] = (epsilon-h/2.)*np.ones((1,N-1))
#		# Next we specify on which diagonals they will be placed, and create A
#		diags = np.array([0,-1,1])
#		A=h**(-2.)*spdiags(data,diags,N-1,N-1).asformat('csr')
#
#		U = np.empty(N+1)
#		U[1:-1] = spsolve(A,F)
#		U[0], U[-1] = alpha, beta
#		return np.linspace(a,b,N+1), U
#
#	x, y = bvp(lambda x:-1., epsilon=.05,alpha=1, beta=3, N=400)
#	import matplotlib.pyplot as plt
#	plt.plot(x,y,'-k',linewidth=2.0)
#	plt.show()
#
#	num_approx = 5 # Number of Approximations
#	N = 20*np.array([2**j for j in range(num_approx)])
#	h, max_error = (1.-0)/N[:-1], np.ones(num_approx-1)
#
#	mesh_best, num_sol_best = bvp(lambda x:-1, epsilon=.5, alpha=1, beta=3, N=N[-1])
#	for j in range(len(N)-1):
#		mesh, num_sol = bvp(lambda x:-1, epsilon=.5, alpha=1, beta=3, N=N[j])
#		max_error[j] = np.max(np.abs( num_sol- num_sol_best[::2**(num_approx-j-1)] ) )
#	plt.loglog(h,max_error,'.-r',label="$E(h)$")
#	plt.loglog(h,h**(2.),'-k',label="$h^{\, 2}$")
#	plt.xlabel("$h$")
#	plt.legend(loc='best')
#	plt.show()
#	print "The order of the finite difference approximation is about ", ( (np.log(max_error[0]) -
#		np.log(max_error[-1]) )/( np.log(h[0]) - np.log(h[-1]) ) ), "."
#	return
#


import numpy as np
try:
    import matplotlib.pyplot as plt
except:
    import matplotlib
    matplotlib.use('Agg')
    import matplotlib.pyplot as plt

class PolynomialRegression():
    def __init__(self, degree):
        """
        Implement polynomial regression from scratch.

        This class takes as input "degree", which is the degree of the polynomial
        used to fit the data. For example, degree = 2 would fit a polynomial of the
        form:

            ax^2 + bx + c

        Your code will be tested by comparing it with implementations inside sklearn.
        DO NOT USE THESE IMPLEMENTATIONS DIRECTLY IN YOUR CODE. You may find the
        following documentation useful:

        https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.PolynomialFeatures.html
        https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html

        Here are helpful slides:

        http://interactiveaudiolab.github.io/teaching/eecs349stuff/eecs349_linear_regression.pdf

        The internal representation of this class is up to you. Read each function
        documentation carefully to make sure the input and output matches so you can
        pass the test cases. However, do not use the functions numpy.polyfit or numpy.polval.
        You should implement the closed form solution of least squares as detailed in slide 10
        of the lecture slides linked above.

        Usage:
            import numpy as np

            x = np.random.random(100)
            y = np.random.random(100)
            learner = PolynomialRegression(degree = 1)
            learner.fit(x, y) # this should be pretty much a flat line
            predicted = learner.predict(x)

            new_data = np.random.random(100) + 10
            predicted = learner.predict(new_data)

            # confidence compares the given data with the training data
            confidence = learner.confidence(new_data)

        Args:
            degree (int): Degree of polynomial used to fit the data.
        """
        self.features = []
        self.targets = []
        self.degree = degree
        self.coefficients = []

    def fit(self, features, targets):
        """
        Fit the given data using a polynomial. The degree is given by self.degree,
        which is set in the __init__ function of this class. The goal of this
        function is fit features, a 1D numpy array, to targets, another 1D
        numpy array.

        Args:
            features (np.ndarray): 1D array containing real-valued inputs.
            targets (np.ndarray): 1D array containing real-valued targets.
        Returns:
            None (saves model and training data internally)
        """
        self.targets = targets
        x_arr = []
        for feature in range(features.size):
            powers = np.arange(0, self.degree+1)
            x_powers = np.power(np.full(self.degree+1, features[feature]), powers)
            x_arr.append(x_powers)
        x_arr = np.array(x_arr)
        self.features = x_arr
        x_transpose = np.transpose(x_arr)
        first = np.linalg.inv(np.matmul(x_transpose, x_arr))
        sec = np.matmul(x_transpose, targets)
        self.coefficients = np.matmul(first, sec)

    def predict(self, features):
        """
        Given features, a 1D numpy array, use the trained model to predict target
        estimates. Call this after calling fit.

        Args:
            features (np.ndarray): 1D array containing real-valued inputs.
        Returns:
            predictions (np.ndarray): Output of saved model on features.
        """
        x_arr = []
        for feature in range(features.size):
            powers = np.arange(0, self.degree+1)
            x_powers = np.power(np.full(self.degree+1, features[feature]), powers)
            x_arr.append(x_powers)
        x_arr = np.array(x_arr)
        predictions = np.matmul(x_arr, self.coefficients)
        return predictions

    def visualize(self, features, targets):
        """
        This function should produce a single plot containing a scatter plot of the
        features and the targets, and the polynomial fit by the model should be
        graphed on top of the points.

        DO NOT USE plt.show() IN THIS FUNCTION. Instead, use plt.savefig().

        Args:
            features (np.ndarray): 1D array containing real-valued inputs.
            targets (np.ndarray): 1D array containing real-valued targets.
        Returns:
            None (plots to the active figure)
        """
        x = features
        y = self.predict(features)
        plt.title('Polynomial Regression')
        plt.plot(x, y)
        plt.savefig('Polynomial Regression')


import numpy as np

def point_bilinear(x,y,z, pointX, pointY):
    i = index_vec(x, pointX)
    j = index_vec(y, pointY)

    l_ij = (x[i] - pointX)*(y[j] - pointY)
    l_i1j1 = (x[i+1] - pointX)*(y[j+1] - pointY)
    l_i1j = (x[i+1] - pointX)*(y[j] - pointY)
    l_ij1 = (x[i] - pointX)*(y[j+1] - pointY)

    z_ij = z[i,j]
    z_i1j1 = z[i+1,j+1]
    z_i1j = z[i+1,j]
    z_ij1 = z[i, j+1]

    num_1 = l_ij*z_i1j1 + l_i1j1*z_ij
    num_2 = l_i1j*z_ij1 + l_ij1*z_i1j
    den = (x[i+1] - x[i]) * (y[j+1] - y[j])

    return (num_1 - num_2) / den

def bilinear(x, y, z, vectX, vectY):
    dimX, dimY = len(vectX), len(vectY)
    bi = np.zeros((dimX, dimY))
    for indexX in range(dimX):
        for indexY in range(dimY):
            bi[indexX, indexY] = point_bilinear(x, y, z, vectX[indexX], vectY[indexY])

    return bi

def index_vec(vec, point):
    n = len(vec) - 1
    if point < vec[0]:
        return 0
    elif point > vec[n]:
        return n-2
    else:
        for i in range(n):
            if vec[i] <= point and point <= vec[i+1]:
                return i

#def graf()


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

def backtrace(backpointers, node, involved):
    optimal = False
    for P in backpointers[node]:
        if backtrace(backpointers, (P[0], P[1]), involved):
            P[2] = True
            optimal = True
            involved[node[0], node[1]] = 1
    if node[0] == 0 and node[1] == 0:
        print(node)
        return True #Reached the beginning
    return optimal

def LevDist(a, b):
    #Third element in backpointers stores whether this is part
    #of an optimal path
    M = len(a)
    N = len(b)
    D = np.zeros((M+1, N+1))
    D[:, 0] = np.arange(M+1)
    D[0, :] = np.arange(N+1)
    backpointers = {}
    for i in range(0, M+1):
        for j in range(0, N+1):
            backpointers[(i, j)] = []
    for i in range(0, M):
        backpointers[(i+1, 0)].append([i, 0, False])
    for j in range(0, N):
        backpointers[(0, j+1)].append([0, j, False])
    for i in range(1, M+1):
        for j in range(1, N+1):
            delt = 1
            if a[i-1] == b[j-1]:
                delt = 0
            dul = delt + D[i-1, j-1]
            dl = 1 + D[i, j-1]
            du = 1 + D[i-1, j]
            D[i, j] = min(min(dul, dl), du)
            if dul == D[i, j]:
                backpointers[(i, j)].append([i-1, j-1, False])
            if dl == D[i, j]:
                backpointers[(i, j)].append([i, j-1, False])
            if du == D[i, j]:
                backpointers[(i, j)].append([i-1, j, False])
    involved = np.zeros((M+1, N+1))
    backtrace(backpointers, (M, N), involved) #Recursive backtrace from the end
    return (D, backpointers)

def writeChar(fout, i, j, c, bold = False):
    if bold:
        fout.write("\\node at (%g, %g) {\\textbf{%s}};\n"%(j+0.5, i+0.5, c))
    else:
        fout.write("\\node at (%g, %g) {%s};\n"%(j+0.5, i+0.5, c))

def drawPointers(fout, backpointers, M, N):
    for idx in backpointers:
        for P in backpointers[idx]:
            color = 'black'
            if P[2]:
                color = 'red'
            s = [idx[1]+1.4, M-idx[0]+0.8]
            if idx[0]-P[0] == 0: #Left arrow
                fout.write("\\draw [thick, ->, %s] (%g, %g) -- (%g, %g);\n"%(color, s[0], s[1], P[1]+1.6, s[1]))
            elif idx[1]-P[1] == 0: #Up arrow
                fout.write("\\draw [thick, ->, %s] (%g, %g) -- (%g, %g);\n"%(color, s[0], s[1], s[0], M-P[0]+0.2))
            else: #Diagonal Arrow
                fout.write("\\draw [thick, ->, %s] (%g, %g) -- (%g, %g);\n"%(color, s[0], s[1], P[1]+1.6, M-P[0]+0.2))

def drawAllPointers(fout, M, N):
    for i in range(1, M+1):
        for j in range(1, N+1):
            color = 'black'
            s = [j+1.4, M-i+0.8]
            #Left arrow
            fout.write("\\draw [thick, ->, %s] (%g, %g) -- (%g, %g);\n"%(color, s[0], s[1], j-1+1.6, s[1]))
            #Up arrow
            fout.write("\\draw [thick, ->, %s] (%g, %g) -- (%g, %g);\n"%(color, s[0], s[1], s[0], M-(i-1)+0.2))
            #Diagonal Arrow
            fout.write("\\draw [thick, ->, %s] (%g, %g) -- (%g, %g);\n"%(color, s[0], s[1], j-1+1.6, M-(i-1)+0.2))

def LevenshteinExample(a, b, doPointers = False, doAllPointers = False):
    #Make Levenshtein Example
    M = len(a)
    N = len(b)
    (D, backpointers) = LevDist(a, b)
    fout = open("levfig.tex", "w")
    fout.write("\\documentclass[12pt,oneside,a4paper]{article}")
    fout.write("\\usepackage{tikz}")
    fout.write("\\begin{document}")
    fout.write("\\begin{tikzpicture}")
    fout.write("\\draw [help lines] (0, 0) grid (%i,%i);\n"%(N+2, M+2))

    writeChar(fout, M, 0, '\\_')
    for i in range(M):
        writeChar(fout, M-(i+1), 0, a[i])

    writeChar(fout, M+1, 1, '\\_')
    for j in range(N):
        writeChar(fout, M+1, j+2, b[j])

    for i in range(M+1):
        for j in range(N+1):
            if i == M and j == N:
                continue
            writeChar(fout, M-i, j+1, int(D[i, j]))
    writeChar(fout, 0, N+1, int(D[-1, -1]))

    if doPointers:
        drawPointers(fout, backpointers, M, N)
    elif doAllPointers:
        drawAllPointers(fout, M, N)
    fout.write("\\end{tikzpicture}")
    fout.write("\\end{document}")
    fout.close()

    subprocess.call(["pdflatex", "levfig.tex"])

if __name__ == '__main__':
    LevenshteinExample("school", "fools", doPointers=False)
    #s = "razmataz"
    #s2 = s[::-1]
    #LevenshteinExample(s, s2, doPointers=True)


'''

The eccentricity of a node v is the maximum distance from v to all other nodes in G.
'''

import networkx as nx

def calculate(network):
    try:
        n = nx.eccentricity(network)
    except:
        return 0

    if len(n.values()) == 0:
        return 0
    else:
        return round(sum(n.values())/len(n.values()), 7)


# @Author: yican.kz
# @Date: 2019-08-23 23:38:16
# @Last Modified by:   yican.kz
# @Last Modified time: 2019-08-23 23:38:16

# Standard libraries
import os
import sys
import random

# Third party libraries
import pandas as pd
import numpy as np
from numba import jit

sys.path.insert(0, os.path.abspath(".."))
from tabular_buddy.utils import parallelize, Timer

def monte_carlo_pi(nsamples):
    acc = 0
    for i in range(nsamples):
        x = random.random()
        y = random.random()
        if (x ** 2 + y ** 2) < 1.0:
            acc += 1
    return 4.0 * acc / nsamples

@jit(nopython=True)
def monte_carlo_pi_numba(nsamples):
    acc = 0
    for i in range(nsamples):
        x = random.random()
        y = random.random()
        if (x ** 2 + y ** 2) < 1.0:
            acc += 1
    return 4.0 * acc / nsamples

def func(data):
    return data["a"].apply(lambda x: np.sqrt(x)) - data["b"] ** 2

if __name__ == "__main__":
    # ==============================================================================================================
    # Using numba to speed up your function, [21 times!]
    # -----------------------------------------
    # Before speed up : Res is 3.1414 | 21 seconds
    # After speed up  : Res is 3.1419 | 1 seconds
    # ==============================================================================================================
    nsamples = 10000000
    data = pd.DataFrame({"a": np.random.rand(nsamples), "b": np.random.rand(nsamples)})

    tick_tock = Timer()
    print("Before speed up : Res is {:.4f} | {} seconds".format(monte_carlo_pi(50000000), int(tick_tock())))
    print("After speed up  : Res is {:.4f} | {} seconds".format(monte_carlo_pi_numba(50000000), int(tick_tock())))

    # ==============================================================================================================
    # Using python multiprocessing to speed up your function, [3 times!]
    # Actual performance depends on your machine
    # -----------------------------------------
    # Before speed up : Res 3333598.7172 | 16 seconds
    # After speed up  : Res 3333598.7172 | 5 seconds
    # ==============================================================================================================
    res = func(data)
    print("Before speed up : Res {:.4f} | {} seconds".format(res.sum(), int(tick_tock())))
    res = parallelize(data, func)
    print("After speed up  : Res {:.4f} | {} seconds".format(res.sum(), int(tick_tock())))


'''
Exp 11: Program to demonstrate use of NumPy: Array objects.

Theory:
NumPy is a Python library used for working with arrays.
It also has functions for working in domain of linear algebra, fourier transform, and matrices.
NumPy was created in 2005 by Travis Oliphant. It is an open source project and you can use it freely.
NumPy stands for Numerical Python.

In Python we have lists that serve the purpose of arrays, but they are slow to process.
NumPy aims to provide an array object that is up to 50x faster than traditional Python lists.
The array object in NumPy is called ndarray, it provides a lot of supporting functions that make working with ndarray very easy.
0-D Arrays
0-D arrays, or Scalars, are the elements in an array. Each value in an array is a 0-D array.
1-D Arrays
An array that has 0-D arrays as its elements is called uni-dimensional or 1-D array.
These are the most common and basic arrays.
2-D Arrays
An array that has 1-D arrays as its elements is called a 2-D array.
These are often used to represent matrix or 2nd order tensors.

arange() is an inbuilt numpy function that returns an ndarray object containing evenly spaced values within a defined interval.
linspace() function returns number spaces evenly w.r.t interval.
Similar to numpy.arange() function but instead of step it uses sample number.
logspace() function returns number spaces evenly w.r.t interval on a log scale.
zeros() function returns a new array of given shape and type, with zeros.
ones() function returns a new array of given shape and type, with ones.
empty() is used to create an array without initializing the entries of given shape and type.
identity() Return a identity matrix i.e. a square matrix with ones on the main diagonal.

Slicing:
Slicing in python means taking elements from one given index to another given index.
We pass slice instead of index like this: [start:end].
We can also define the step, like this: [start:end:step].
If we don't pass start its considered 0
If we don't pass end its considered length of array in that dimension
If we don't pass step its considered 1

@author: KHAN FARHAN NADEEM (19CO27)
'''

import numpy as np
#############################
#    creating numpy arrays  #
#############################
#########################################
# create 1D arrays using array function #
#########################################
arr1d = np.array([1,2])

#######################################################
# create 2D arrays using array function of type float #
#######################################################
arr2d = np.array([[1,2,3],[4,5,6],[7,8,9]],dtype=float)

######################################
# create array using arange function #
######################################
arr_ar = np.arange(1,100,9)

########################################
# create array using linspace function #
########################################
arr_lp = np.linspace(10,11,10)

########################################
# create array using logspace function #
########################################
arr_logp = np.logspace(10,15,10)

########################################
# create array using various functions #
########################################
arr_zeros = np.zeros((2,3))
arr_ones = np.ones((3,2))
arr_empty = np.empty((2,4))
arr_identity = np.identity(3)

##############################
## accessing array elements ##
##############################
print('\n arr1d =',arr1d)
print('\n arr2d =')

for row in arr2d:
    print(row)

print('\narr_ar =',arr_ar)
print('\narr_lp =',arr_lp,' having size of',arr_lp.size)
print('\narr_logp =',arr_logp,' having size of',arr_logp.size)
print('\narr_zeros =\n',arr_zeros)
print('\narr_ones =\n',arr_ones)
print('\narr_empty =\n',arr_empty)
print('\narr_identity =\n',arr_identity)

###################################
###  performing array operations ##
###################################
print('\nPerforming Array Operations:')
print('Addition of 2D arrays\n',arr2d + arr_identity)
print('Substraction of 2D arrays\n',arr2d - arr_identity)
print('Multiplication of 2D arrays\n',arr2d * arr_identity)
print('Matrix Multiplication of 2D arrays\n',arr2d @ arr_identity)
print('Transpose of 2D arrays\n',arr2d.transpose())

###############################
# performing slice operations #
###############################
print('Elements of Second Row and First 2 Columns',arr2d[1,:2])
print('Elements of Last Row and Second Column onwards',arr2d[-1,1:])
print(arr2d[arr1d])

################################
# performing reshape on arrays #
################################

##################################
# reshaping 1D array to 2D array #
##################################
newarr = arr1d.reshape(1,2)
print('reshaped array : ',newarr)

'''
Conclusion:
NumPy is a fundamental package for scientific computation & mathematical operations in python.
NumPy is way more powerful than Lists.
'''


import numpy as np

def softmax(X):
    e = np.exp(X - np.amax(X, axis=1, keepdims=True))
    return e/np.sum(e, axis=1, keepdims=True)

def categorical_cross_entropy(y_pred, y_true, eps=1e-15):
    # Assumes one-hot encoding.
    y_pred = np.clip(y_pred, eps, 1 - eps)
    # XXX: do we need to normalize?
    y_pred /= y_pred.sum(axis=1, keepdims=True)
    loss = -np.sum(y_true * np.log(y_pred), axis=1)
    return loss

def one_hot_encode(labels, n_classes, out=None):
    out_shape = (labels.size, n_classes)
    if labels.dtype != np.dtype(int):
        raise ValueError('labels.dtype must be int')
    if out is None:
        out = np.empty(out_shape)
    else:
        if out.shape != out_shape:
            raise ValueError('shape mismatch')
    out.fill(0)
    if labels.size == 1:
        out[0, labels] = 1
    else:
        for c in range(n_classes):
            out[labels == c, c] = 1
    return out

def one_hot_decode(one_hot, out=None):
    out_shape = (one_hot.shape[0],)
    if out is None:
        out = np.empty(out_shape, dtype=np.dtype(int))
    else:
        if out.dtype != np.dtype(int):
            raise ValueError('out.dtype must be int')
        if out.shape != out_shape:
            raise ValueError('shape mismatch')
    result = np.argmax(one_hot, axis=1)
    np.copyto(out, result)
    return out


import numpy as np

def euclidean_distance(u, v):
    """
    Args:
        u (array-like): Vector
        v (array-like): Vector

    Returns:
        The euclidean distance between vectors u and v.

    """
    return np.linalg.norm(np.array(u) - np.array(v))

def manhattan_distance(u, v):
    """
    Args:
        u (array-like): Vector
        v (array-like): Vector

    Returns:
        The manhattan distance between vectors u and v.

    """
    return np.abs(np.array(u) - np.array(v)).sum()

def hamming_distance(u, v):
    """
    Args:
        u (array-like): Vector
        v (array-like): Vector

    Returns:
        The hamming distance between vectors u and v.

    """
    return np.count_nonzero(np.array(u) != np.array(v))


import numpy as np

def build_stretch_matrix(x, y):
    stretch_transform = np.array(
        [[x, 0, 0],
         [0, y, 0],
         [0, 0, 1]],
        dtype=np.float32)
    return stretch_transform

def build_shear_matrix(x, y):
    tan_x = np.tan(x)
    tan_y = np.tan(y)
    shear_transform = np.array(
        [[1,     tan_x, 0],
         [tan_y, 1,     0],
         [0,     0,     1]],
        dtype=np.float32)
    return shear_transform

def build_rotate_matrix(t):
    cos_t = np.cos(t)  # t must be in radians
    sin_t = np.sin(t)
    rotate_transform = np.array(
        [[cos_t,  sin_t, 0],
         [-sin_t, cos_t, 0],
         [0,          0, 1]],
        dtype=np.float32)
    return rotate_transform

def build_flip_matrix(h, v):
    # reflect about x and y
    if h and v:
        flip_transform = np.array(
            [[-1,  0, 0],
             [ 0, -1, 0],
             [ 0,  0, 1]], dtype=np.float32)
    # reflect about only x
    elif h and not v:
        flip_transform = np.array(
            [[ 1,  0, 0],
             [ 0, -1, 0],
             [ 0,  0, 1]], dtype=np.float32)
    # reflect about only y
    elif not h and v:
        flip_transform = np.array(
            [[-1,  0, 0],
             [ 0,  1, 0],
             [ 0,  0, 1]], dtype=np.float32)
    # do not reflect
    else:
        flip_transform = np.eye(3, dtype=np.float32)

    return flip_transform

def build_translate_matrix(x, y):
    translate_transform = np.array(
        [[1, 0, x],
         [0, 1, y],
         [0, 0, 1]], dtype=np.float32)
    return translate_transform

def build_transformation_matrix(
        imsize, theta=None, offset=None, flip=None, shear=None, stretch=None):

    # use the identity matrix as default
    if theta is None:
        theta = 0.
    if offset is None:
        offset = (0., 0.)
    if flip is None:
        flip = (False, False)
    if shear is None:
        shear = (0., 0.)
    if stretch is None:
        stretch = (1., 1.)

    cx, cy = np.array(imsize) / 2 - 0.5
    center_matrix = build_translate_matrix(cx, cy)
    stretch_matrix = build_stretch_matrix(*stretch)
    shear_matrix = build_shear_matrix(*shear)
    rotate_matrix = build_rotate_matrix(theta)
    flip_matrix = build_flip_matrix(*flip)
    uncenter_matrix = build_translate_matrix(-cx, -cy)
    translate_matrix = build_translate_matrix(*offset)

    transform_matrix = center_matrix.dot(
        stretch_matrix).dot(
        shear_matrix).dot(
        rotate_matrix).dot(
        flip_matrix).dot(
        uncenter_matrix).dot(
        translate_matrix
    )

    return transform_matrix


"""
Intensive transformation is an basic gray level transformation
Formula:

    X = ( 255 / (high - low) ) * ( X - low)

    X: The intensive in each pixel
    255: The max level intensive of image
    high: The highest gray level of image
    low: The lowest gray level of image

"""

import cv2
import numpy as np
from __utils__.general import show_image
from histogram.plot_histogram import use_calc_hist_in_cv2_function

def contrast_schetching(image):
    # Get the high and low level intensive
    high = image.max()
    low = image.min()

    # Copy gray image
    image_gray = np.copy(image)

    # Contrast stretching
    for pixel in np.nditer(image_gray, op_flags=['readwrite']):
        pixel[...] = np.round(np.abs((255 / float(high - low)) * (pixel - low)))
    return image_gray

if __name__ == '__main__':
    img = cv2.imread('../../asserts/images/wiki.jpg')
    img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    res = contrast_schetching(img)
    show_image(np.hstack((img, res)))


'''
K Nearest Neighbor implementations for the points
    data = [[(1,6), 7], [(2,4), 8], [(3,7), 16], [(6,8),44], [(7,1),50], [(8,4),68]]
Seeking the point
    q = (4,2)
Using two different distance formulas: Manhathan and euclidean_distance
If the the subsequent elements to k have the same distance as k, those are
also included.

'''

import math
import numpy as np

def manh_dist(a, b):
    return abs(a[0]-b[0]) + abs(a[1]-b[1])

def euclidean_dist(a,b):
    return math.sqrt(math.pow(a[0]-b[0], 2)+ math.pow(a[1]-b[1], 2))

def KNN(k, data, q, dist):
    r = sorted([(dist(a[0],q), index) for index, a in enumerate(data)])
    avg = [data[b][1] for a, b in r[:k]]

    # Check if the distance value is the same for the elements after k
    # if so add them to the list to get the averages.
    max_k_value = r[k-1][0]
    start_index = k
    while True:
        if r[start_index][0] == max_k_value:
            avg.append(data[r[start_index][1]][1])
            start_index += 1
        else: break
    average = np.mean(avg)
    print 'Average for {} Neighbors, using {} as distance is: {}'.format(
        k, dist.__name__, average
    )

if __name__ == '__main__':
    data = [[(1,6), 7], [(2,4), 8], [(3,7), 16], [(6,8),44], [(7,1),50], [(8,4),68]]
    q = (4,2)
    KNN(1, data, q, manh_dist)
    KNN(3, data, q, manh_dist)
    KNN(1, data, q, euclidean_dist)
    KNN(3, data, q, euclidean_dist)


"""
Demo of Runge Phenomenon

Author: HearyShen
Date: 2020.10.28
"""
import matplotlib.pyplot as plt
import numpy as np

LEFT_BOUND = -1
RIGHT_BOUND = 1.01

PLT_ROWS = 2
PLT_COLS = 3

def runge_function(x):
    y = 1 / (1 + 25 * x**2)     # Runge Function
    return y

def runge_polyfit(degree):
    # original runge function
    x_orig = np.arange(LEFT_BOUND, RIGHT_BOUND, 0.01)
    y_orig = runge_function(x_orig)

    # sample points
    interval = 2 / degree
    x_sample = np.arange(LEFT_BOUND, RIGHT_BOUND, interval)
    y_sample = runge_function(x_sample)

    plt.title(f"{degree}-degree polyfit")

    # plot original runge function
    plt.plot(x_orig, y_orig, label="runge")

    # plot runge sample points
    plt.plot(x_sample, y_sample, "rx")

    coef = np.polyfit(x_sample, y_sample, degree)
    # y_fit = sum([coef[i] * (x**(degree-i)) for i in range(degree+1)])
    y_fit = np.polyval(coef, x_orig)
    plt.plot(x_orig, y_fit, label=f"polyfit-{degree}")

    plt.legend()
    # plt.show()

def runge_phenomenon(degrees=[1, 5, 9, 13, 15, 17]):
    plt.figure(figsize=(16, 9))
    for i in range(min(PLT_ROWS*PLT_COLS, len(degrees))):
        # plot each polynomial function of each degree
        plt.subplot(PLT_ROWS, PLT_COLS, i+1)

        # plot runge function
        runge_polyfit(degrees[i])

    plt.show()

if __name__ == "__main__":
    runge_phenomenon()


from __future__ import division
import numpy as np

def recursive_trapezoid(n,R,f,a,b):
    '''
    Computes the integral evaluation using the recursive trapezoid rule.
    The function actually evaluates R(n,m) but since m is 0 for us to
    prefer trapezoid rule, we don't accept that as an input.
    It accepts the function 'f' we are evaluating.
    returns R(n,0) added to R.

    The base case of this recursive this function is R(0,0) which
    is assumed to have already been inserted into the dictionary R
    that's why it is not added here.
    '''
    retVal = -1
    if (n,0) in R.keys():
        retVal = R[(n,0)]
    elif n == 0:
        retVal = 0.5 * (b-a) * (f(a) + f(b))
    else:
        h = (b-a) * (0.5 ** n)
        retVal = 0.5 * recursive_trapezoid(n-1, R, f, a, b) + h * sum([ f(a+(2*k-1)*h) for k in xrange(1,(2**(n-1))+1)])
    return retVal

def get_R(n, m, R, f, a, b):
    '''
    This function computes and returns the value of R with R(n,m) added to it.
    It expects the old R as an input and also the function we are evaluating
    '''
    retVal = -1
    if (n,m) in R.keys():
        retVal = R[(n,m)]
    elif m == 0:
        # call recursive trapezoidal function
        retVal = recursive_trapezoid(n,R,f,a,b)
    else:
        retVal = get_R(n,m-1,R,f,a,b) + (1.0/((4**m)-1)) * (get_R(n,m-1,R,f,a,b) - get_R(n-1,m-1,R,f,a,b))
    return retVal

def integrate(f,a,b,nrows):
    # The R "matrix" which would store the values computed from trapezoidal and
    # Romberg's algorithm
    # This is implemented as a dictionary which stores a tuple (n,m) as the key
    # and the corresponding R(n,m) evaluation as the value against the (n,m) key
    R = {}
    rows = nrows
    for i in range(rows):
        for j in range(i+1):
            R[(i,j)] = get_R(i,j,R,f,a,b)
    return R[i,j], R

def printR(R, n):
    for i in range(rows):
        for j in range(i+1):
            print("{0:.10f}".format(R[(i,j)])),
        print
    print

if __name__ == '__main__':
    rows = 9
    #---------function
    print("Analytical Integral is ln(3) = "),
    Actual = 1.0986122886681098
    print(Actual)
    f = lambda x: 1 / (1+x)
    a = 0
    b = 2
    integral, R = integrate(f,a,b,rows)
    print "Numerical Integral is :",integral
    print("Error is {}".format(abs(Actual-integral)))
    printR(R,rows)
    #---------function 2
    print("Analytical Integral is e^1 - 1 = "),
    Actual = 1.718281828459045
    print(Actual)
    f = lambda x: np.e ** x
    a = 0
    b = 1
    integral, R = integrate(f,a,b,rows)
    print "Numerical Integral is", integral
    print("Error is {}".format(abs(Actual-integral)))
    printR(R,rows)
    #---------function 3
    print("Analytical Integral is 2/3 = "),
    Actual = 0.6666666666666666
    print(Actual)
    f = lambda x: np.sqrt(x)
    a = 0
    b = 1
    integral, R = integrate(f,a,b,rows)
    print "Numerical Integral is", integral
    print("Error is {}".format(abs(Actual-integral)))
    printR(R,rows)