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

# # Descriptive Statistics: The Central Tendency Using Pandas

# In[1]:

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

# In[2]:

height_weight_data = pd.read_csv('datasets/500_Person_Gender_Height_Weight_Index.csv')

height_weight_data.head(5)

# In[3]:

height_weight_data.drop('Index', inplace=True, axis=1)

# In[4]:

height_weight_data.shape

# In[5]:

height_weight_data.isnull().sum()

# In[6]:

min_height = height_weight_data['Height'].min()

min_height

# In[7]:

max_height = height_weight_data['Height'].max()

max_height

# In[8]:

min_weight = height_weight_data['Weight'].min()

min_weight

# In[9]:

max_weight = height_weight_data['Weight'].max()

max_weight

# In[10]:

range_of_height = max_height - min_height

range_of_height

# In[11]:

range_of_weight = max_weight - min_weight

range_of_weight

# In[12]:

weight = height_weight_data['Weight']

weight.head()

# In[13]:

sorted_weight = weight.sort_values().reset_index(drop=True)

sorted_weight.head()

# In[14]:

def mean(data):
    num_elements = len(data)
    print('Number of elements: ', num_elements)

    weight_sum = data.sum()
    print('Sum: ', weight_sum)

    return weight_sum / num_elements

# In[15]:

def median(data):
    num_elements = len(data)

    if(num_elements % 2 == 0):
        return  (data[(num_elements / 2) - 1] + data[(num_elements / 2)]) / 2

    else:
        return (data[((num_elements + 1) / 2) - 1])

# In[16]:

mean(height_weight_data['Weight'])

# In[17]:

weight_mean = height_weight_data['Weight'].mean()

weight_mean

# In[18]:

median(height_weight_data['Weight'])

# In[19]:

median(sorted_weight)

# In[20]:

weight_median = height_weight_data['Weight'].median()

weight_median

# In[21]:

plt.figure(figsize=(12, 8))

height_weight_data['Weight'].hist(bins=30)

plt.axvline(weight_mean, color='r', label='mean')

plt.legend()

# In[22]:

plt.figure(figsize=(12, 8))

height_weight_data['Weight'].hist(bins=30)

plt.axvline(weight_median, color='g', label='median')

plt.legend()

# In[23]:

plt.figure(figsize=(12, 8))

plt.bar(height_weight_data['Height'], height_weight_data['Weight'])

plt.axhline(weight_mean, color='r', label='mean')
plt.axhline(weight_median, color='b', label='median')

plt.show()

# In[24]:

height_weight_data

# In[25]:

listOfSeries = [pd.Series(['Male', 205, 460], index=height_weight_data.columns ),
                pd.Series(['Female', 202, 390], index=height_weight_data.columns ),
                pd.Series(['Female', 199, 410], index=height_weight_data.columns ),
                pd.Series(['Male', 202, 390], index=height_weight_data.columns ),
                pd.Series(['Female', 199, 410], index=height_weight_data.columns ),
                pd.Series(['Male', 200, 490], index=height_weight_data.columns )]

# In[26]:

height_weight_updated = height_weight_data.append(listOfSeries , ignore_index=True)

height_weight_updated.tail()

# In[27]:

updated_weight_mean = height_weight_updated['Weight'].mean()

updated_weight_mean

# In[28]:

weight_mean

# In[29]:

updated_weight_median = height_weight_updated['Weight'].median()

updated_weight_median

# In[30]:

weight_median

# In[31]:

plt.figure(figsize=(12, 8))

plt.bar(height_weight_updated['Height'], height_weight_updated['Weight'])

plt.axhline(updated_weight_mean, color='r', label='mean')
plt.axhline(updated_weight_median, color='b', label='median')

plt.show()

# In[32]:

plt.figure(figsize=(12, 8))

height_weight_data['Weight'].hist(bins=20)

plt.axvline(updated_weight_mean, color='r', label='mean')
plt.axvline(updated_weight_median, color='g', label='median')

plt.legend()

# In[33]:

plt.figure(figsize=(12, 8))

height_weight_updated['Weight'].hist(bins=100)

plt.axvline(updated_weight_mean, color='r', label='mean')
plt.axvline(updated_weight_median, color='g', label='median')

plt.legend()

# In[34]:

height_counts = {}

for p in height_weight_data['Height']:
    if p not in height_counts:
        height_counts[p] = 1
    else:
        height_counts[p] += 1

# In[35]:

height_counts

# In[36]:

plt.figure(figsize=(25, 10))

x_range = range(len(height_counts))

plt.bar(x_range, list(height_counts.values()), align='center')
plt.xticks(x_range, list(height_counts.keys()))

plt.xlabel('Height')
plt.ylabel('Count')

plt.show()

# In[37]:

count = 0
size = 0

for s, c in height_counts.items():
    if count < c:
        count = c
        size = s

print('Size: ', size, '\nFrequency: ', count)

# In[38]:

height_weight_data['Height'].mode()

# In[39]:

height_weight_data['Weight'].mode()

# In[40]:

height_mean = height_weight_data['Height'].mean()

height_median = height_weight_data['Height'].median()

height_mode = height_weight_data['Height'].mode().values[0]

# In[41]:

plt.figure(figsize=(12, 8))

height_weight_data['Height'].hist()

plt.axvline(height_mean, color='r', label='mean')
plt.axvline(height_median, color='g', label='median')
plt.axvline(height_mode, color='y', label='mode')

plt.legend()

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:


"""
dot

The dot tool returns the dot product of two arrays.

import numpy

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

print numpy.dot(A, B)       #Output : 11

cross

The cross tool returns the cross product of two arrays.

import numpy

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

print numpy.cross(A, B)     #Output : -2

Task

You are given two arrays
and . Both have dimensions of X

.
Your task is to compute their matrix product.

Input Format

The first line contains the integer
.
The next lines contains space separated integers of array .
The following lines contains space separated integers of array

.

Output Format

Print the matrix multiplication of
and

.

Sample Input

2
1 2
3 4
1 2
3 4

Sample Output

[[ 7 10]
 [15 22]]
"""

import numpy

a=int(input())

arr1=numpy.array([list(map(int,input().split())) for _ in range(a)])

arr2=numpy.array([list(map(int,input().split())) for _ in range(a)])

print(numpy.dot(arr1,arr2))


import numpy as np
from scipy import stats

def create_data(N = 10):
    a = np.random.randn(N) + 2 #mean = 2, var = 1
    b = np.random.randn(N)  #mean = 0, var = 1

    return a, b

def variance(a,b):
    var_a = a.var(ddof = 1) #Numpy uses the population (N) and not sample (N-1), thus pass ddof = 1
    var_b = b.var(ddof = 1)

    return var_a, var_b

def pooledstd(var_a, var_b):
    """
    Calculates the pooled standard deviation (Sp)
    """
    sp = np.sqrt((var_a + var_b) / 2)

    return sp

def t_stat(a, b, sp, N = 10):
    """
    Calculates the t-statistic and the degrees of freedom (df)
    """
    t = (a.mean() - b.mean())/(sp * np.sqrt(2/N))

    df = 2*N - 2

    return t, df

def p_value():
    N = 10
    a, b = create_data(N = N)
    var_a, var_b = variance(a, b)
    sp = pooledstd(var_a, var_b)
    t, df = t_stat(a, b, sp, N = N)
    p = 1 - stats.t.cdf(t, df)
    p = p*2 #Since it is a 2-tailed test
    print('t-stat: ', t,'P: ', p)
    t2, p2 = stats.ttest_ind(a, b)
    print('t-stat scipy: ', t2,'P scipy: ', p2)

if __name__ == "__main__":
    p_value()


"""
Created on Mon Nov 16 09:57:55 2020

@author: Larisa

Laborator: 6
"""

import numpy as np
import matplotlib.pyplot as plt
import sympy as sym
import sympy.utilities.lambdify as lam

"""
Exercițiul 1:
Fie f(x, y) = x^2 / 4 + y^2 si domeniul (x, y) aparține [-6, 6] X [-4, 4].
"""

# a) Să se construiască această suprafață (z = f(x, y)) pe domeniul dat.
(a, b) = (-6, 6)
(c, d) = (-4, 4)
(Nx, Ny) = (20, 20) #nr de puncte în care să fie împărțit intervalul

x_grafic = np.linspace(a, b, Nx)
y_grafic = np.linspace(c, d, Ny)

[X, Y] = np.meshgrid(x_grafic, y_grafic)
# print(X)
# print(Y)

plt.figure(1)
plt.plot(X, Y, 'o', markerfacecolor = 'red', markersize = 10)
plt.grid(True)
plt.title('Fig 1: Rețeaua de puncte care acoperă domeniul (a, b) X (c, d)')
plt.show()

f = lambda x, y: (x * x) / 4 + y * y
Z = f(X, Y)
# print(Z)

fig = plt.figure(2)
axes = plt.axes(projection = '3d')
surf = axes.plot_surface(X, Y, Z, cmap = plt.cm.jet)
fig.colorbar(surf)

# b) Să se afiseze în plan curbele de nivel Z = 2, 4, 8
fig = plt.figure(3)
axes = plt.axes(projection = '3d')
plt.contour(X, Y, Z, levels = [2, 4, 8])
plt.show()

# c) Afisare linia de nivel care trece prin punctul de coordonate (-2, 1)
x0 = -2
y0 = 1
plt.figure(4)
plt.plot(x0, y0, 'o', markerfacecolor = 'red', markersize = 10)
plt.contour(X, Y, Z, levels = [f(x0, y0)])
plt.show()

# d) Să se afle simbolic gradientul funcției
x, y = sym.symbols('x y')
f_expr = f(x, y)
df1 = [sym.diff(f_expr, x), sym.diff(f_expr, y)]
df2 = sym.lambdify((x, y), [df1[0], df1[1]])

# e) Calculați direcția funcției în raport cu care aceasta creste/descreste cel mai rapid
# Direcția pe care funcția creste cel mai rapid
v_max = df2(x0, y0)
v_max = v_max / np.sqrt(v_max[0] ** 2 + v_max[1] ** 2)
print(f'df calculat pentru ({x0}, {y0}): {v_max}')
plt.plot([x0, x0 + v_max[0]], [y0, y0 + v_max[1]], linewidth = 4)
plt.axis('equal')  # Pentru ca axele sa fie egale
# plt.show()

# Direcția pe care funcția descreste cel mai rapid
v_min = -v_max
print(f'df calculat pentru ({x0}, {y0}): {v_min}')
plt.plot([x0, x0 + v_min[0]], [y0, y0 + v_min[1]], linewidth = 4)
plt.axis('equal')  # Pentru ca axele sa fie egale
# plt.show()

# f) Construiți curba pe suprafața dată, care are reprezentarea parametrică
s = np.linspace(-7, 7, 30)  # parametrul din reprezentarea parametrică a curbei
x_curba = x0 + s * v_max[0]
y_curba = y0 + s * v_max[1]
z_curba = f(x_curba, y_curba)
axes = plt.axes(projection = '3d')
axes.plot3D(x_curba, y_curba, z_curba, linewidth = 3)
plt.show()

# g) Să se afle punctul de minim local de la ex1, folosind gradient descent
A = np.array([[1/2, 0], [0, 2]])
b = np.array([[0], [0]])
x_old = np.array([[x0], [y0]])
eps = 10 ** (-3)
k = 0
xk = []
xk.append([x_old[0], x_old[1]])

while True:
    v = df2(x_old[0], x_old[1])  # directia gradientului
    if np.sqrt(v[0] ** 2 + v[1] ** 2) < eps:
        break

    s = (np.transpose(v) @ (A @ x_old - b)) / (np.transpose(v) @ (A @ v))
    x_new = x_old - s * v
    xk.append([x_new[0], x_new[1]])
    k += 1
    x_old = x_new

print(x_old)  # soluția aproximativă
x_array = np.asarray(xk)

plt.figure(6)
for i in range(np.shape(x_array)[0]):
    plt.plot(x_array[i][0][0], x_array[i][1][0], '-o', linewidth = 3,
             markerfacecolor = 'red', markersize = 10)
    plt.contour(X, Y, Z, levels=[f(x_array[i][0][0], x_array[i][1][0])])
    print(f(x_array[i][0][0], x_array[i][1][0]))
plt.axis('equal')
plt.show()

Calcul Numeric (CN)/Laborator/Laborator 6/lab6.py


# Exercise 5.14
# Author: Noah Waterfield Price

import numpy as np

def L_k(x, k, xp, yp):
    ans = 1
    # if x > xp[-1]:
    #   raise ValueError('x out of range of function domain')
    for i in range(len(xp)):
        if i != k:
            ans *= (x - xp[i]) / (xp[k] - xp[i])
    return ans

def p_L(x, xp, yp):
    ans = 0
    # if x > xp[-1]:
    #   raise ValueError('x out of range of function domain')
    for k in range(len(yp)):
        ans += yp[k] * L_k(x, k, xp, yp)
    return ans

def verify(xp, yp):
    status = 'Verified!'
    for k in range(len(xp)):
        if abs(p_L(xp[k], xp, yp) - yp[k]) > 1e-15:
            status = 'Error!'
            break
    print status

def verbose_verify(xp, yp):
    verify(xp, yp)
    print '%10s %10s %10s %10s' % ('x', 'Exact', 'Approx', 'Difference')
    for k in range(len(xp)):
        x = xp[k]
        exact = yp[k]
        approx = p_L(xp[k], xp, yp)
        diff = abs(p_L(xp[k], xp, yp) - yp[k])
        print '%10f %10f %10f %10f' % (x, exact, approx, diff)

xlist = np.linspace(0, np.pi, 5)
ylist = np.sin(xlist)

if __name__ == '__main__':
    verify(xlist, ylist)
    verbose_verify(xlist, ylist)

"""
Sample run:
python Lagrange_poly1.py
Verified!
         x      Exact     Approx Difference
  0.000000   0.000000   0.000000   0.000000
  0.785398   0.707107   0.707107   0.000000
  1.570796   1.000000   1.000000   0.000000
  2.356194   0.707107   0.707107   0.000000
  3.141593   0.000000   0.000000   0.000000
"""


# moment statistics python  'IMPORTANT'
# https://www.google.com/search?q=moment+statistics+python&sxsrf=ALeKk03IR29YXksd2BrmuqG_1yKbLknFXQ%3A1621251343715&ei=D1WiYJT0Ko2ImAWc-6uwBg&oq=moment+statistics+python&gs_lcp=Cgdnd3Mtd2l6EAMyBggAEBYQHjoHCAAQRxCwAzoHCAAQsAMQQzoCCABQxDhYv0JgjERoAXACeACAAZ8CiAGsC5IBBTAuNi4ymAEAoAEBqgEHZ3dzLXdpesgBCsABAQ&sclient=gws-wiz&ved=0ahUKEwjUt7vzz9DwAhUNBKYKHZz9CmYQ4dUDCA4&uact=5
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.moment.html
# https://math.stackexchange.com/questions/190531/what-is-the-use-of-moments-in-statistics

'''
What do moments represent?
Moments [of a statistical distribution]

1. The mean, which indicates the central tendency of a distribution.

2. The second moment is the variance, which indicates the width or deviation.

3. The third moment is the skewness, which indicates any asymmetric 'leaning'
to either left or right.
'''
from scipy import stats
import numpy as np

arr = np.random.randint(1, 10, 10)
arr = list(arr)
print(arr)
print(stats.moment(arr, moment=1))  # ???
print(stats.moment(arr, moment=2))

# print(stats.moment(arr, moment=3))
# print(stats.skew(arr))


from math import sqrt

import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate
from scipy.optimize import root
from scipy.interpolate import BarycentricInterpolator

from solutions import cheb

# Solving the river crossing problem with end states
# z(-1) = 0, z(1) = z1
z1 = 5.

# Function that describes the current of the river
def r(x):
	return -.7*(x-1.)*(x+1.)

# a = (1-r)^{1/2}
def a(x):
	return (1.-r(x)**2.)**(-1./2)

# Family of possible trajectories across the river satisfying the
# given end-states. Also their derivatives.
def y(x,eps):
	return z1/2.*(x-(-1.)) + eps *(x-1.)*(x+1)*x

def yp(x,eps):
	return z1/2. + eps*(2.*x*x + (x-1.)*(x+1))

def current_plot():
	x0 = np.linspace(-1,1,8)
	r0_ = r(x0)
	x1 = np.linspace(-1,1,100)
	r_, y_ = r(x1), z1/2.*(x1-(-1.)) #y(x1,-.7)
	plt.quiver( x0, np.zeros(x0.shape), np.zeros(x0.shape),  r0_, pivot='tail', color='b', scale_units='xy',scale=1.)
	plt.axis([-1.1,1.1,0. -.05,z1 +.05])

	# # Plots the function r to ensure that the
	# # length of the arrows above are correct.
	# plt.plot(x1,r_,'-k')

	# # Plots a trajectory y_
	y2 = np.linspace(0-.05,z1+.05,200)
	plt.plot(-np.ones(y2.shape),y2,'-k',linewidth=2.5)
	plt.plot(np.ones(y2.shape),y2,'-k',linewidth=2.5)
	plt.plot(x1,y_,'-g',linewidth=2.)
	plt.plot(-1,0,'*g',markersize=10.)
	plt.plot(1,z1,'*g',markersize=10.)
	# plt.savefig('rivercurrent.pdf')
	# plt.show()
	plt.clf()

def time_functional():
	# The integrand of the functional that gives the time required to cross
	# the river on a given trajectory.
	def L(x,eps):
		a_, yp_ = a(x), yp(x,eps)
		return a_*(1 + a_**2.*yp_**2.)**(1./2) - a_**2.*r(x)*yp_

	list = range(-50,60)
	eps = np.array([item/100. for item in list])
	out = 10.*np.ones(eps.shape)
	for j in range(len(list)):
		out[j] = integrate.quad(lambda x:L(x,eps[j]),-1,1)[0]
	min_time = np.min(out)
	index = np.argmin(out)
	eps = eps[index]
	return eps, min_time

def trajectory():
	N = 200
	D, x = cheb(N)
	eps = 0.

	def L_yp(x,yp_):
		a_ = a(x)
		return a_**3.*yp_*(1 + a_**2.*yp_**2.)**(-1./2) - a_**2.*r(x)

	def g(z):
		out = D.dot(L_yp( x,D.dot(z) ))
		out[0], out[-1] = z[0] - z1, z[-1] - 0
		return out

	# Use the straight line trajectory as an initial guess.
	# Another option would be to use the shortest-time trajectory found
	# in heuristic_tractory() as an initial guess.
	eps, time = time_functional()
	sol = root(g,y(x,eps))
	# sol =  root(g,z1/2.*(x-(-1.)))
	# print sol.success
	z = sol.x
	poly = BarycentricInterpolator(x,D.dot(z))
	num_func = lambda inpt: poly.__call__(inpt)

	def L(x):
		a_, yp_ = a(x), num_func(x)
		return a_*(1 + a_**2.*yp_**2.)**(1./2) - a_**2.*r(x)*yp_

	print "The shortest time is approximately "
	print integrate.quad(L,-1,1,epsabs=1e-10,epsrel=1e-10)[0]

	# x0 = np.linspace(-1,1,200)
	# y0 = y(x0,eps)
	# plt.plot(x0,y0,'-g',linewidth=2.,label='Initial guess')
	# plt.plot(x,z,'-b',linewidth=2.,label="Numerical solution")
	# ax = np.linspace(0-.05,z1+.05,200)
	# plt.plot(-np.ones(ax.shape),ax,'-k',linewidth=2.5)
	# plt.plot(np.ones(ax.shape),ax,'-k',linewidth=2.5)
	# plt.plot(-1,0,'*b',markersize=10.)
	# plt.plot(1,z1,'*b',markersize=10.)
	# plt.xlabel('$x$',fontsize=18)
	# plt.ylabel('$y$',fontsize=18)
	# plt.legend(loc='best')
	# plt.axis([-1.1,1.1,0. -.05,z1 +.05])
	# # plt.savefig("minimum_time_rivercrossing.pdf")
	# plt.show()
	# plt.clf()
	return x, z, N

def angle():
	x,z, N = trajectory()
	num = int(N/20)
	D, _ = cheb(N)
	yp = D.dot(z)
	r_ = r(x)

	theta1 = np.arctan(yp) # This is incorrect
	theta2 = (1-r_**2.)**(-1)*(-r_*yp + np.sqrt( (r_*yp)**2. + (1-r_**2)*(1. + yp**2.)))
	theta2 = np.arccos(1./theta2)
	# plt.plot(x,theta1,'-g',linewidth=2.)
	plt.plot(x,theta2,'-b',linewidth=2.)
	plt.xlabel('$x$',fontsize=18)
	plt.ylabel(r'$\theta$',fontsize=18)
	plt.yticks([0, np.pi/6, np.pi/3.,np.pi/2.],
	           ['$0$', r'$\frac{\pi}{6}$', r'$\frac{\pi}{3}$', r'$\frac{\pi}{2}$'])
	# plt.savefig('trajectory_angle.pdf')
	plt.show()
	plt.clf()

	theta = theta1
	# Checking that the angle is correct.
	print 0., np.min(theta), np.max(theta), np.pi/2.
	xdist, ydist = np.cos(theta), np.sin(theta)
	plt.quiver( x[::num], z[::num], xdist[::num],  ydist[::num],
				pivot='tail', color='b', scale_units='xy',scale=3., angles='xy')
	# plt.plot(x,z,'-k',linewidth=2.)
	plt.axis([-1.1,1.1,0. -.5,z1 +1.])
	plt.show()

def newfunc():
	# The integrand of the functional that gives the time required to cross
	# the river on a given trajectory.
	def yp(x):
		return 2.5

	def L(x,yp):
		a_, yp_ = a(x), yp(x)
		return a_*(1 + a_**2.*yp_**2.)**(1./2) - a_**2.*r(x)*yp_

	print integrate.quad(lambda x:L(x,yp),-1,1)[0]
	return

if __name__=="__main__":
	# current_plot()
	print time_functional()
	trajectory()
	angle()
	# newfunc()


import numpy as np
np.random.seed(0)

def compute_reciprocals(values):
    output = np.empty(len(values))
    for i in range(len(values)):
        output[i] = 1.0 / values[i]
    return output

values = np.random.randint(1,10,size=5)
print(compute_reciprocals(values))
big_array = np.random.randint(1,100,size=1000000)
# %timeit compute_reciproals(big_array)

#introducing Ufunctions
###vectorized operation part of numpy###
#%timeit (1/big_array)

#vectorized operations are implemented via the set of ufuncs in numpy

print(np.arange(5) / np.arange(1,6))
#not limited to only 1d arrays
x = np.arange(9).reshape((3,3))
print( 2 ** x)

###exploring Numpy's UFuncs###

#array Arithmetic
x = np.arange(4)
print("x =", x)
print("x + 5=", x+5)
print("x - 5=", x-5)
print("x * 2=", x*2)
print("x / 2=", x/2)
print("x // 2=", x//2) #floor division

print("-x=", -x)
print("x ** 2=", x ** 2)
print("x % 2=", x%2)

#these are rappers for np. functions such as np.add
print("np.add(x, 2)=",np.add(x, 2))

#absolute value
x = np.array([-2,-1,0,1,2])
print(x)
print("abs x",abs(x))
#coresponding NumPy ufunc is np.absolute/ np.abs
print("np abs x", np.abs(x))

#can also handle complex data, returning the magnitude
x = np.array([3-4j, 4-3j, 2+0j, 0 +1j])
print(np.abs(x))

##Trigonometric functions

theta = np.linspace(0, np.pi, 3)

print("theta      =", theta)
print("sin(theta) =", np.sin(theta))
print("cos(theta) =", np.cos(theta))
print("tan(theta) =", np.tan(theta))

theta = [-1, 0, 1]
print("arcsin(theta) =", np.arcsin(theta))
print("arccos(theta) =", np.arccos(theta))
print("arctan(theta) =", np.arctan(theta))

##exponets and logs

x = [1,2,3]
print("x    =",x)
print("e^x  =", np.exp(x))
print("2^x  =", np.exp2(x))
print("3^x  =", np.power(3,x))

x = [1,2,4,10]

print("x=", x)
print("lnx=" ,np.log(x))
print("log2(x)=", np.log2(x))
print("log10(x)=", np.log10(x))

#special cases for percision

x=[0,0.001,0.01, 0.1]
print(x)
print("exp(x)-1", np.expm1(x))
print("log(1+x)", np.log1p(x))

##specialized ufuncs
from scipy import special
#gama functions (generalized factorials) and related
x= [1,5,10]
print("gamma(x)", special.gamma(x))
print("ln|gamma(x)", special.gammaln(x))
print("beta(x,2)", special.beta(x,2))

#error function (intergral of Gaussian)
#it's complement and its inverse
x= np.array([0,0.3,0.7,1.0])
print("erf(x)", special.erf(x))
print("erfc(x)", special.erfc(x))
print("erfinv(x)", special.erfinv(x))

##advance ufunc features
#specifying output
print("advance features")

x= np.arange(5)
y = np.empty(5)
np.multiply(x, 10, out=y)
print(y)

y = np.zeros(10)
np.power(2,x,out=y[::2])
print(y)
# for large arrays this is a significant savings vs y[::2] = 2 ** x

print("Aggregates")
x= np.arange(1,6)
print(np.add.reduce(x))
print(np.multiply.reduce(x))
print(np.add.accumulate(x))
print(np.multiply.accumulate(x))

print("outer products")
x = np.arange(1,6)
print(np.multiply.outer(x,x))


'''
Programa DNL para analisis de campos en dos dimensiones.
author:@julianamette3@gmail.com

El objetivo de este programa es tener herramientas que permitan un analisis rapido
para sistemas de escuaciones de dos dimensiones.

'''
import numpy as np
import matplotlib.pyplot as plt
from numpy import linalg as LA

#Aca se define la funcion de tu campo de dos cordenadas!
def f(x,y):
    return y**3-4*x, y**3-y-3*x

### este es un ejemplo copado para mas adelante
def funcion_linda(x,y,a,b):
    return y , a + b*x + x**2 -x*y
####

#Esta funcion, dada una matriz te devuelve los autovalores y autovectores de la misma
#Sirve para ver la parte lineal y la usamos mas delante para otras funcioens
def aval_avec(matrix):
    '''
    input: matriz de la parte lineal
    output: w vector de los autovalres y v vector que tiene los autovectores en columnas
    '''
    w,v = LA.eig(matrix)
    print('Los autovalores son ', w )
    print('Los autovectores son ', v[:,0], ' y ', v[:,1])
    return w,v

#La siguiente funcion es la que uno mas va a usar y es para graficar un campo y sus mulclinas
def nulclinas_y_campo(fn,xmin,xmax,ymin,ymax,nro_puntos, plot = 'si',m_lin = 'none',ax = 'none'):

    '''
    input:
    fn: funcion que quieras graficar
    xmin: minimo en el eje x que se quiere realizar el grafico
    xmax: maximo de x
    ymin: minimo de y
    ymax: maximo de y
    nro_puntos:  es la resolucion en la que queres que se grafiquen las cosas, recomendada: 1000
    plot: default: si, si pones plot = 'no' no va a graficar y podes usar lo que te devuelve para
          lo que quieras.
    m_lin: default : 'none',  si se introduce la matriz de la parte lineal te va a graficar
            los autovectores y decir sus autovalores, esta bueno a veces. Si no podes nada
            no los va a graficar.
    ax :  default: 'none' Es el par de ejes sobre el que queres que grafique, a menos que
    quieras usar la funcion para meterla adentro de otra es mejor no poner ningun input.

    RECORDAR: si la funcion ya tiene un default no es necesario poner de input esos parametros
    NOTA: esta funcion tiene un parametro magico en la parte de ax.streamplot que es el de density
    ahora esta seteado en 1.5, si queres mas resolucion de las lineas de corriente, subilo,
    1.7 o 1.9 es algo ya bien denso, el tema es que va a ser re caro hacer la animacion rapudo
    asi que no es tan trivial.
    '''
    x = np.linspace(xmin,xmax,nro_puntos)
    y = np.linspace(ymin,ymax,nro_puntos)
    X, Y = np.meshgrid(x,y)
    (u,v) = fn(X,Y)
    if ax == 'none':
        fig, ax = plt.subplots()
    ax.streamplot(X, Y, u, v,density=1.5,color = 'teal')
    cs_u = ax.contour(X,Y,u,[0],colors=['navy'])
    cs_v = ax.contour(X,Y,v,[0])
    ax.grid()
    try:
        ax.clabel(cs_u ,fmt = 'Nulclina x', colors=['navy'])
        ax.clabel(cs_v ,fmt = 'Nulclina y')
    except:
        pass
    if m_lin != 'none':
        w,v = aval_avec(m_lin)
        ax.plot((0,v[:,0][0]),(0,v[:,0][1]),'.-' , label = 'Avec con aval %f' %w[0])
        ax.plot((0,v[:,1][0]),(0,v[:,1][1]),'.-',  label = 'Avec con aval %f' %w[1])
        ax.legend()
    if plot == 'si':
        plt.show()

##La proxima seccion es para analizar como cambia el campo en funcion de los parametros
#y las diferentes bifurcaciones que tiene
#IMPORTANTE: Esta escrita para f(x,y,a,b) por lo que si es ese tu caso con muy pocos cambios
#lo vas a poder hacer pero la logica es la misma para mas variables asi que es muy analogo

#Supongo que tengo un campo que depende de dos parametros a y b, propongo una parametrizacion
#para ir moviendo estos. La siguiente es un circulo de radio 1.
def param(theta):
    return np.cos(theta) , np.sin(theta) #la primera coordenada es a y la segunda b

#Como la parametrizacion depende de theta pongo los valores entre los que me voy a mover
theta = np.linspace(0,2*np.pi,100)

#Ahora igualo los valores a la parametrizacion para ya tenerlos guardados
val = param(theta)

#La siguiente funcion es la animacion para ir variando los parametros
def animacion(fn,array,xmin,xmax,ymin,ymax):
    '''
    fn: funcion que quieras ver fn(x,y,a,b)
    array:  es el array de los valores que voy a ir variando.
    xmin, xmax, ymin, ymax son todos los bordes otra vez
    '''
    fig, ax = plt.subplots()
    for i in range(len(theta)):
        def g(x,y):
            return fn(x,y, array[0][i], array[1][i])
        nulclinas_y_campo(g,xmin,xmax,xmin,xmax,1000,plot = 'no', ax = ax)
        ax.set_title('a = %.2f , b = %.2f' %(array[0][i],array[1][i]))
        plt.pause(0.1)
        ax.clear()

animacion(funcion_linda,val,-5,5,-5,5)
#nulclinas_y_campo(f,0,20,0,20,1000)


import numpy as np
import pickle as pk
import matplotlib.pyplot as pl
from scipy.optimize import curve_fit as cf

size = [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]

# Obtain standard deviation
f_file = open('T2f_sdheight.pickle', 'rb')
sd = pk.load(f_file)
f_file.close()

pl.figure()
pl.loglog(size, sd, 'bo', label = r"Standard deviation of the height $\sigma_h$")
pl.xlabel(r"System size $L$")
pl.ylabel(r"Standard deviation $\sigma_h$")
pl.grid()
pl.ylim(0.1, 10)

# Optimise a scaling funvtion for it
def scaling(L, a0, w0, a1, w1):
    return a0*(L**w0)*(1 - a1*L**-w1)

para, var = cf(scaling, np.log(size)/np.log(10), np.log(sd)/np.log(10), maxfev = 4000)

xpts = np.logspace(0.2, 3.1, 5000)
ypts = 10**(para[0]*(np.log(xpts)/np.log(10))**para[1]*(1 - para[2]*(np.log(xpts)/np.log(10))**(-para[3])))

pl.loglog(xpts, ypts, 'r-', label = r"Scaling function $a_0 L^{\omega_0} (1 - a_1 L^{-\omega_1})$ fit with"
          + "\n" + r"$a_0 = %.3f \pm %.3f$" %(para[0], np.sqrt(np.diag(var)[0])) + "\n" + r"$\omega_0 = %.3f \pm %.3f$" %(0.2388, np.sqrt(np.diag(var)[1]))
          + "\n" + r"$a_1 = %.3f \pm %.3f$" %(para[2], np.sqrt(np.diag(var)[2])) + "\n" + r"$\omega_1 = %.3f \pm %.3f$" %(para[3], np.sqrt(np.diag(var)[3])))
pl.legend()

# Divide by a0*L**w0 to obtain trend for large L - expect SD to tend towards a0*L**w0
yptsdiv = ypts / 10**(para[0]*(np.log(xpts)/np.log(10))**para[1])
sddiv = sd / 10**(para[0]*(np.log(size)/np.log(10))**para[1])

pl.figure()
pl.loglog(size, sddiv, 'bo', label = r"Scaled standard deviation $\sigma_h / a_0 L^{\omega_0}$")
pl.xlabel(r"System size $L$")
pl.ylabel(r"Scaled standard deviation $\sigma_h / a_0 L^{\omega_0}$")
pl.grid()
pl.loglog(xpts, yptsdiv, 'r-', label = r"Scaling function $(1 - a_1 L^{-\omega_1})$ fit")
pl.legend()
pl.ylim(0.1, 10)

# Save para and var for use in Task 2g
f_file1 = open('T2f_para.pickle', 'wb')
pk.dump(para, f_file1)
f_file1.close()


#%% [markdown]
# # K-means Algorithm

#%%
# Libraries Imports
import numpy as np
import pandas as pd

#%%
# Initialize Data Points
""" (x-axis, y-axis) """
data_points = [(5.9, 3.2), (4.6, 2.9), (6.2, 2.8), (4.7, 3.2), (5.5, 4.2), (5.0, 3.0), (4.9, 3.1), (6.7, 3.1), (5.1, 3.8), (6.0, 3.0)]
len(data_points)

#%%
# Euclidian distance
def euclidian_dist(p1, p2):
  p1_arr = np.array(p1)
  p2_arr = np.array(p2)
  return np.linalg.norm(p1_arr-p2_arr)

""" Tests: euclidian_dist(p1, p2) """
p1 = (5.9, 3.2)
p2 = (5.9, 3.2)
print('Test 1 - p1 = p1: ' + str(euclidian_dist(p1, p2)))

p1 = (1.0, 0.0)
p2 = (0.0, 1.0)
print('Test 2: root(2): ' + str(euclidian_dist(p1, p2)))

p1 = (2.0, 0.0)
p2 = (1.0, 3.0)
print('Test 3: root(10): ' + str(euclidian_dist(p1, p2)))

p1 = (6.2, 3.2)
p2 = (5.9, 3.2)
print('Test 4: dist(c1, dp1): ' + str(euclidian_dist(p1, p2)))

p1 = (6.6, 3.7)
p2 = (5.9, 3.2)
print('Test 5: dist(c2, dp1): ' + str(euclidian_dist(p1, p2)))

p1 = (6.5, 3.0)
p2 = (5.9, 3.2)
print('Test 6: dist(c3, dp1): ' + str(euclidian_dist(p1, p2)))

#%%
# Initialize centroids: K = 3
c1 = (6.2, 3.2) # red
c2 = (6.6, 3.7) # green
c3 = (6.5, 3.0) # blue

centroids = [c1, c2, c3]
centroids

#%%
# Calculate the distance of all data points
def calculate_dist_all(data_points, centroids):
  distances = []
  for point in data_points:
    distance_per_point = []
    for centroid in centroids:
      dist = euclidian_dist(centroid, point)
      distance_per_point.append(dist)
    distances.append(distance_per_point)
  return distances

distances = calculate_dist_all(data_points, centroids)

df = pd.DataFrame(data=distances)
df.head()

#%%
# Calculate the nearest centroid to each data point
groups = df.idxmin(axis=1)
groups.head()

#%%
# Assign data points to each centroid
clusters = [[], [], []]
count = 0
for dp in groups.values:
  clusters[dp].append(data_points[count])
  count += 1

clusters

#%%
# Recalculate centroids
c_centroids = 0
for cluster in clusters:
  x_axis = 0
  y_axis = 0
  for dp in cluster:
    x_axis += dp[0] / len(cluster)
    y_axis += dp[1] / len(cluster)
  centroids[c_centroids] = (x_axis, y_axis)
  c_centroids += 1

centroids


import numpy as np
import scipy.stats as sct
import time
import itertools
import sys

#----- normal distribution

def normal_model_log_prob(_x, _theta):
    #-- parameters
    _mu = _theta[0]
    _sigma_sq = _theta[1]
    #-- log probability
    _p = -(_x - _mu) ** 2 / (2 * _sigma_sq) - np.log(2 * np.pi * _sigma_sq) / 2
    return _p

def normal_max_likelihood_est(_x):
    _mu = np.mean(_x)
    _sigma_sq = np.var(_x)
    _theta = [_mu, _sigma_sq]
    return _theta

def normal_log_likelihood(_x, _theta):
    _l = np.sum(normal_model_log_prob(_x, _theta))
    return _l

#-----

def model_log_prob(_x, _theta):
    _beta = _theta[0]
    _sigma_sq = _theta[1]
    _X = _x[:, 0:-1]
    _Y = _x[:, -1]
    _z = _Y - _X @ _beta
    _prob = normal_model_log_prob(_z, [0, _sigma_sq])
    return _prob

def max_likelihood_est(_x, _k):
    _X = _x[:, 0:_k]
    _Y = _x[:, -1]
    _beta = np.linalg.solve(_X.T @ _X, _X.T @ _Y)
    _beta.resize(_x.shape[1] - 1)
    _sigma_sq = np.mean((_Y - _x[:, 0:-1] @ _beta) ** 2)
    _theta = [_beta, _sigma_sq]
    return _theta

def bootstrap_residual(_x, _ids, _theta):
    return _x

#-----

def log_likelihood(_x, _theta):
    _l = np.sum(model_log_prob(_x, _theta))
    return _l

def bootstrap_sample(_x, _theta):
    _n = _x.shape[0]
    _ids = np.random.randint(0, _n, _n)
    _x_ast = _x[_ids, :]

    _x_ast_r = bootstrap_residual(_x_ast, _ids, _theta)

    return _x_ast_r

def EIC_biasE(_x, _k, _B):
    _D_ast = np.zeros((_B, 2))
    _theta = max_likelihood_est(_x, _k)
    for i in range(_B):
        _x_ast = bootstrap_sample(_x, _theta)
        _theta_ast = max_likelihood_est(_x_ast, _k)
        _D_ast[i, 0] = log_likelihood(_x_ast, _theta_ast) - log_likelihood(_x_ast, _theta)
        _D_ast[i, 1] = log_likelihood(_x, _theta) - log_likelihood(_x, _theta_ast)

    _b_b = np.mean(_D_ast[:, 0] + _D_ast[:, 1])
    return _b_b

#-----

def main(T, n, K, k, B):
    t_l = np.zeros(T)
    t_bias = np.zeros(T)

    prv_ut = time.time()
    for t in range(T):
        ut = time.time()
        if ut - prv_ut > 5.0:
            prv_ut = ut
            print("---", t, file=sys.stderr)

        #-- samples from true distribution
        #X = np.random.uniform(-1.0, 1.0, (n, K))
        X = np.random.normal(0.0, 1.0, (n, K))
        Y = np.random.normal(0.0, 1.0, (n, 1))
        x = np.hstack((X, Y))

        theta = max_likelihood_est(x, k)
        t_l[t] = log_likelihood(x, theta)

        t_bias[t] = EIC_biasE(x, k, B)

    print(k, # k
          np.mean(t_bias), # bias, mean
          np.mean(t_l), # likelihood, mean
          )

if __name__ == '__main__':

    T = 10 #1000

    n = 100
    K = 20

    B = 100

    for k in range(21):
        main(T, n, K, k, B)


import math
import numpy as np

class AdaBoostHelper:
    def __init__(self, X, y, n, m):
        self.cache = dict()
        self.X = X.toarray()

        self.y = np.ones(n)
        self.y[y == 0] = -1
        self.n = n
        self.m = m
        self.D = np.array([1 / n for i in range(n)])

    def calc_yhat(self, j, c):
        y_hat_arr = np.ones(self.n)
        dimension = self.X[:, j]
        y_hat_arr[dimension <= c] = -1

        if np.sum(y_hat_arr == self.y) < self.n / 2:
            return -y_hat_arr, -1
        return y_hat_arr, 1

    def calc_ht(self):
        j_t = None
        c_t = None
        y_hat_arr_t = None
        y_hat_val_t = None
        error = 1

        for j in range(self.m):
            j_arr_sorted = None
            if j in self.cache:
                j_arr_sorted = self.cache[j]
            else:
                # j_arr_sorted = np.sort(np.unique(self.X[:, j]))
                j_arr_sorted = np.sort(self.X[:, j])
                self.cache[j] = j_arr_sorted

            c_list = np.unique((j_arr_sorted[:self.n - 1] + j_arr_sorted[1:]) / 2)
            # print(c_list)
            # jl = len(j_arr_sorted)
            # print(jl)

            for c in c_list:
                # c = (j_arr_sorted[k] + j_arr_sorted[k + 1]) / 2
                y_hat_arr, y_hat_val = self.calc_yhat(j, c)
                curr_error_arr = np.zeros(self.n)
                curr_error_arr[y_hat_arr != self.y] = 1
                curr_error = np.dot(self.D, curr_error_arr)
                # print('j: {}, k: {}, c: {}, e: {}'.format(j, k, c, curr_error))

                if curr_error < error:
                    j_t = j
                    c_t = c
                    y_hat_arr_t = y_hat_arr
                    y_hat_val_t = y_hat_val
                    error = curr_error

        return j_t, c_t, y_hat_arr_t, y_hat_val_t, error

    def calc_alphat(self, error):
        return 0.5 * math.log((1 / error) - 1)

    def set_next_distribution(self, alpha, y_hat):
        self.D = np.multiply(self.D, np.exp(np.multiply(-alpha * self.y, y_hat)))
        Z = self.D.sum()
        self.D /= Z

    @staticmethod
    def ht(X, h_arg):
        num_of_features = X.shape[0]
        j, c, y_hat_arr, y_hat_val, error = h_arg
        res = np.zeros(num_of_features)
        dimension = X[:, j]
        res[dimension > c] = y_hat_val
        res[dimension <= c] = -y_hat_val
        return res


import sympy
import matplotlib.pyplot as pyplot

class ODE:

    moulton_coeff = [[1, 0, 0, 0, 0, 0, 0, 0],
                     [1 / 2, 1 / 2, 0, 0, 0, 0, 0, 0],
                     [5 / 12, 2 / 3, -1 / 12, 0, 0, 0, 0, 0],
                     [3 / 8, 19 / 24, -5 / 24, 1 / 24, 0, 0, 0, 0],
                     [251 / 720, 323 / 360, -11 / 30, 53 / 360, -19 / 720, 0, 0, 0],
                     [95 / 288, 1427 / 1440, -133 / 240, 241 / 720, -173 / 1440, 3 / 160, 0, 0],
                     [19087 / 60480, 2713 / 2520, -15487 / 20160, 586 / 945, -6737 / 20160, 263 / 2520, -863 / 60480, 0],
                     [5257 / 17280, 139849 / 120960, -4511 / 4480, 123133 / 120960, -88547 / 120960, 1537 / 4480, -11351 / 120960, 275 / 24192]]

    bashforth_coeff = [[1, 0, 0, 0, 0, 0, 0, 0],
                       [3 / 2, -1 / 2, 0, 0, 0, 0, 0, 0],
                       [23 / 12, -4 / 3, 5 / 12, 0, 0, 0, 0, 0],
                       [55 / 24, -59 / 24, 37 / 24, -3 / 8, 0, 0, 0, 0],
                       [1901 / 720, -1387 / 360, 109 / 30, -637 / 360, 251 / 720, 0, 0, 0],
                       [4277 / 1440, -2641 / 480, 4991 / 720, -3649 / 720, 959 / 480, -95 / 288, 0, 0],
                       [198721 / 60480, -18637 / 2520, 235183 / 20160, -10754 / 945, 135713 / 20160, -5603 / 2520, 19087 / 60480, 0],
                       [16083 / 4480, -1152169 / 120960, 242653 / 13440, -296053 / 13440, 2102243 / 120960, -115747 / 13440, 32863 / 13440, -5257 / 17280]]

    inverse_coeff = [[1, 1, 0, 0, 0, 0, 0],
                     [2.0 / 3.0, 4.0 / 3.0, -1.0 / 3.0, 0, 0, 0, 0],
                     [6.0 / 11.0, 18.0 / 11.0, -9.0 / 11.0, 2.0 / 11.0, 0, 0, 0],
                     [12.0 / 25.0, 48.0 / 25.0, -36.0 / 25.0, 16.0 / 25.0, -3.0 / 25.0, 0, 0],
                     [60.0 / 137.0, 300.0 / 137.0, -300.0 / 137.0, 200.0 / 137, -75.0 / 137.0, 12.0 / 137.0, 0],
                     [60.0 / 147.0, 360.0 / 147.0, -450.0 / 147.0, 400.0 / 147.0, -255.0 / 147.0, 72.0 / 147.0, 10.0 / 147.0]]

    def __init__(self, y, t, h, func, order=1):
        self.y = y
        self.t = t
        self.h = h
        self.func = func
        self.order = order

    def euler(self):
        return self.y + self.h * self.func(self.t, self.y)

    def backward_euler(self):
        y1 = self.euler()
        return self.y + self.h * self.func(self.t + self.h, y1)

    def modified_euler(self):
        y1 = self.euler()
        return self.y + (self.h / 2) * (self.func(self.t, self.y) + self.func(self.t + self.h, y1))

    def runge_kutta(self, params=None):
        if params:
            t = params[0]
            y = params[1]
        else:
            t = self.t
            y = self.y

        k1 = self.func(t, y)
        k2 = self.func(t + self.h / 2, y + k1 * self.h / 2)
        k3 = self.func(t + self.h / 2, y + k2 * self.h / 2)
        k4 = self.func(t + self.h, y + k3 * self.h)
        return y + (k1 + 2 * k2 + 2 * k3 + k4) * self.h / 6

    def adam_bashforth(self, i):
        fs = []
        # y = self.h * sum([self.bashforth_coeff[self.order-1][j] * self.func(t_aux[j], self.y[i]) for j in range(self.order)])
        for j in range(self.order):
            t = self.t[i - j]
            y = self.y[i - j]
            fs.append(self.func(t, y))

        y = self.h * sum([(self.bashforth_coeff[self.order - 1][j] * fs[j]) for j in range(self.order)])
        # for j, f in enumerate(fs):
        #    y += self.bashforth_coeff[self.order - 1][j] * f * self.h
        return y

    def adam_multon(self, i):
        params = (self.t[i], self.y[i])
        self.y[i] = self.runge_kutta(params)
        fs = []

        for j in range(self.order):
            t = self.t[i - j]
            y = self.y[i - j]
            fs.append(self.func(t, y))

        y = self.h * sum([(self.moulton_coeff[self.order - 1][j] * fs[j]) for j in range(self.order)])
        return y

    def inverse(self, i):
        params = (self.t[i], self.y[i])
        self.y[i] = self.runge_kutta(params)
        fs = []

        fs.append(self.h * self.func(self.t[i], self.y[i]))
        for j in range(self.order):
            fs.append(self.y[i - j])

        y = sum([a * t for a, t in zip(self.inverse_coeff[self.order - 1], fs)])
        return y

class Solver:

    def __init__(self, method, steps, ode):
        self.method = method
        self.steps = steps
        self.ode = ode
        self.points = []

    def solve(self):
        if self.method == "euler":
            self.points.append((self.ode.t, self.ode.y))
            for i in range(self.steps):
                self.ode.y = self.ode.euler()
                self.ode.t += self.ode.h
                self.points.append((self.ode.t, self.ode.y))
            return self.points

        elif self.method == "euler_inverso":
            self.points.append((self.ode.t, self.ode.y))
            for i in range(self.steps):
                self.ode.y = self.ode.backward_euler()
                self.ode.t += self.ode.h
                self.points.append((self.ode.t, self.ode.y))
            return self.points

        elif self.method == "euler_aprimorado":
            self.points.append((self.ode.t, self.ode.y))
            for i in range(self.steps):
                self.ode.y = self.ode.modified_euler()
                self.ode.t += self.ode.h
                self.points.append((self.ode.t, self.ode.y))
            return self.points

        elif self.method == "runge_kutta":
            self.points.append((self.ode.t, self.ode.y))
            for i in range(self.steps):
                self.ode.y = self.ode.runge_kutta()
                self.ode.t += self.ode.h
                self.points.append((self.ode.t, self.ode.y))
            return self.points

        elif self.method == "adam_bashforth":
            for i in range(self.ode.order):
                self.points.append((self.ode.t[i], self.ode.y[i]))

            for i in range(self.ode.order, self.steps + 1):
                self.ode.y.append(self.ode.y[i - 1] + self.ode.adam_bashforth(i - 1))
                self.ode.t.append(self.ode.t[i] + self.ode.h)
                self.points.append((self.ode.t[i], self.ode.y[i]))

            return self.points

        elif self.method == "adam_bashforth_by_euler":
            ys = []
            ts = []
            ys.append(self.ode.y)
            ts.append(self.ode.t)
            self.points.append((self.ode.t, self.ode.y))
            for i in range(self.ode.order - 1):
                self.ode.y = self.ode.euler()
                self.ode.t += self.ode.h
                ts.append(self.ode.t)
                ys.append(self.ode.y)
                self.points.append((self.ode.t, self.ode.y))
            self.ode.t = ts
            self.ode.y = ys
            for i in range(self.ode.order, self.steps + 1):
                self.ode.y.append(self.ode.y[i - 1] + self.ode.adam_bashforth(i - 1))
                self.ode.t.append(self.ode.t[i - 1] + self.ode.h)
                self.points.append((self.ode.t[i], self.ode.y[i]))
            return self.points

        elif self.method == "adam_bashforth_by_euler_inverso":
            ys = []
            ts = []
            ys.append(self.ode.y)
            ts.append(self.ode.t)
            self.points.append((self.ode.t, self.ode.y))
            for i in range(self.ode.order - 1):
                self.ode.y = self.ode.backward_euler()
                self.ode.t += self.ode.h
                ts.append(self.ode.t)
                ys.append(self.ode.y)
                self.points.append((self.ode.t, self.ode.y))
            self.ode.t = ts
            self.ode.y = ys
            for i in range(self.ode.order, self.steps + 1):
                self.ode.y.append(self.ode.y[i - 1] + self.ode.adam_bashforth(i - 1))
                self.ode.t.append(self.ode.t[i - 1] + self.ode.h)
                self.points.append((self.ode.t[i], self.ode.y[i]))
            return self.points

        elif self.method == "adam_bashforth_by_euler_aprimorado":
            ys = []
            ts = []
            ys.append(self.ode.y)
            ts.append(self.ode.t)
            self.points.append((self.ode.t, self.ode.y))
            for i in range(self.ode.order - 1):
                self.ode.y = self.ode.modified_euler()
                self.ode.t += self.ode.h
                ts.append(self.ode.t)
                ys.append(self.ode.y)
                self.points.append((self.ode.t, self.ode.y))
            self.ode.t = ts
            self.ode.y = ys
            for i in range(self.ode.order, self.steps + 1):
                self.ode.y.append(self.ode.y[i - 1] + self.ode.adam_bashforth(i - 1))
                self.ode.t.append(self.ode.t[i - 1] + self.ode.h)
                self.points.append((self.ode.t[i], self.ode.y[i]))
            return self.points

        elif self.method == "adam_bashforth_by_runge_kutta":
            ys = []
            ts = []
            ys.append(self.ode.y)
            ts.append(self.ode.t)
            self.points.append((self.ode.t, self.ode.y))
            for i in range(self.ode.order - 1):
                self.ode.y = self.ode.runge_kutta()
                self.ode.t += self.ode.h
                ts.append(self.ode.t)
                ys.append(self.ode.y)
                self.points.append((self.ode.t, self.ode.y))
            self.ode.t = ts
            self.ode.y = ys
            for i in range(self.ode.order, self.steps + 1):
                self.ode.y.append(self.ode.y[i - 1] + self.ode.adam_bashforth(i - 1))
                self.ode.t.append(self.ode.t[i - 1] + self.ode.h)
                self.points.append((self.ode.t[i], self.ode.y[i]))
            return self.points

        elif self.method == "adam_multon":
            for i in range(self.ode.order):
                self.points.append((self.ode.t[i], self.ode.y[i]))

            for i in range(self.ode.order, self.steps + 1):
                self.ode.y.append(self.ode.y[i - 1] + self.ode.adam_multon(i - 1))
                self.ode.t.append(self.ode.t[i] + self.ode.h)
                self.points.append((self.ode.t[i], self.ode.y[i]))

            return self.points

        elif self.method == "adam_multon_by_euler":
            ys = []
            ts = []
            ys.append(self.ode.y)
            ts.append(self.ode.t)
            self.points.append((self.ode.t, self.ode.y))
            for i in range(self.ode.order - 1):
                self.ode.y = self.ode.euler()
                self.ode.t += self.ode.h
                ts.append(self.ode.t)
                ys.append(self.ode.y)
                self.points.append((self.ode.t, self.ode.y))
            self.ode.t = ts
            self.ode.y = ys
            for i in range(self.ode.order, self.steps + 1):
                self.ode.y.append(self.ode.y[i - 1] + self.ode.adam_multon(i - 1))
                self.ode.t.append(self.ode.t[i - 1] + self.ode.h)
                self.points.append((self.ode.t[i], self.ode.y[i]))
            return self.points

        elif self.method == "adam_multon_by_euler_inverso":
            ys = []
            ts = []
            ys.append(self.ode.y)
            ts.append(self.ode.t)
            self.points.append((self.ode.t, self.ode.y))
            for i in range(self.ode.order - 1):
                self.ode.y = self.ode.backward_euler()
                self.ode.t += self.ode.h
                ts.append(self.ode.t)
                ys.append(self.ode.y)
                self.points.append((self.ode.t, self.ode.y))
            self.ode.t = ts
            self.ode.y = ys
            for i in range(self.ode.order, self.steps + 1):
                self.ode.y.append(self.ode.y[i - 1] + self.ode.adam_multon(i - 1))
                self.ode.t.append(self.ode.t[i - 1] + self.ode.h)
                self.points.append((self.ode.t[i], self.ode.y[i]))
            return self.points

        elif self.method == "adam_multon_by_euler_aprimorado":
            ys = []
            ts = []
            ys.append(self.ode.y)
            ts.append(self.ode.t)
            self.points.append((self.ode.t, self.ode.y))
            for i in range(self.ode.order - 1):
                self.ode.y = self.ode.modified_euler()
                self.ode.t += self.ode.h
                ts.append(self.ode.t)
                ys.append(self.ode.y)
                self.points.append((self.ode.t, self.ode.y))
            self.ode.t = ts
            self.ode.y = ys
            for i in range(self.ode.order, self.steps + 1):
                self.ode.y.append(self.ode.y[i - 1] + self.ode.adam_multon(i - 1))
                self.ode.t.append(self.ode.t[i - 1] + self.ode.h)
                self.points.append((self.ode.t[i], self.ode.y[i]))
            return self.points

        elif self.method == "adam_multon_by_runge_kutta":
            ys = []
            ts = []
            ys.append(self.ode.y)
            ts.append(self.ode.t)
            self.points.append((self.ode.t, self.ode.y))
            for i in range(self.ode.order - 1):
                self.ode.y = self.ode.runge_kutta()
                self.ode.t += self.ode.h
                ts.append(self.ode.t)
                ys.append(self.ode.y)
                self.points.append((self.ode.t, self.ode.y))
            self.ode.t = ts
            self.ode.y = ys
            for i in range(self.ode.order, self.steps + 1):
                self.ode.y.append(self.ode.y[i - 1] + self.ode.adam_multon(i - 1))
                self.ode.t.append(self.ode.t[i - 1] + self.ode.h)
                self.points.append((self.ode.t[i], self.ode.y[i]))
            return self.points

        elif self.method == "formula_inversa":
            for i in range(self.ode.order):
                self.points.append((self.ode.t[i], self.ode.y[i]))

            for i in range(self.ode.order, self.steps + 1):
                self.ode.y.append(self.ode.y[i - 1] + self.ode.inverse(i - 1))
                self.ode.t.append(self.ode.t[i] + self.ode.h)
                self.points.append((self.ode.t[i], self.ode.y[i]))

            return self.points

        elif self.method == "formula_inversa_by_euler":
            ys = []
            ts = []
            ys.append(self.ode.y)
            ts.append(self.ode.t)
            self.points.append((self.ode.t, self.ode.y))
            for i in range(self.ode.order - 1):
                self.ode.y = self.ode.euler()
                self.ode.t += self.ode.h
                ts.append(self.ode.t)
                ys.append(self.ode.y)
                self.points.append((self.ode.t, self.ode.y))
            self.ode.t = ts
            self.ode.y = ys
            for i in range(self.ode.order, self.steps + 1):
                self.ode.y.append(self.ode.y[i - 1] + self.ode.inverse(i - 1))
                self.ode.t.append(self.ode.t[i - 1] + self.ode.h)
                self.points.append((self.ode.t[i], self.ode.y[i]))
            return self.points

        elif self.method == "formula_inversa_by_euler_inverso":
            ys = []
            ts = []
            ys.append(self.ode.y)
            ts.append(self.ode.t)
            self.points.append((self.ode.t, self.ode.y))
            for i in range(self.ode.order - 1):
                self.ode.y = self.ode.backward_euler()
                self.ode.t += self.ode.h
                ts.append(self.ode.t)
                ys.append(self.ode.y)
                self.points.append((self.ode.t, self.ode.y))
            self.ode.t = ts
            self.ode.y = ys
            for i in range(self.ode.order, self.steps + 1):
                self.ode.y.append(self.ode.y[i - 1] + self.ode.inverse(i - 1))
                self.ode.t.append(self.ode.t[i - 1] + self.ode.h)
                self.points.append((self.ode.t[i], self.ode.y[i]))
            return self.points

        elif self.method == "formula_inversa_by_euler_aprimorado":
            ys = []
            ts = []
            ys.append(self.ode.y)
            ts.append(self.ode.t)
            self.points.append((self.ode.t, self.ode.y))
            for i in range(self.ode.order - 1):
                self.ode.y = self.ode.modified_euler()
                self.ode.t += self.ode.h
                ts.append(self.ode.t)
                ys.append(self.ode.y)
                self.points.append((self.ode.t, self.ode.y))
            self.ode.t = ts
            self.ode.y = ys
            for i in range(self.ode.order, self.steps + 1):
                self.ode.y.append(self.ode.y[i - 1] + self.ode.inverse(i - 1))
                self.ode.t.append(self.ode.t[i - 1] + self.ode.h)
                self.points.append((self.ode.t[i], self.ode.y[i]))
            return self.points

        elif self.method == "formula_inversa_by_runge_kutta":
            ys = []
            ts = []
            ys.append(self.ode.y)
            ts.append(self.ode.t)
            self.points.append((self.ode.t, self.ode.y))
            for i in range(self.ode.order - 1):
                self.ode.y = self.ode.runge_kutta()
                self.ode.t += self.ode.h
                ts.append(self.ode.t)
                ys.append(self.ode.y)
                self.points.append((self.ode.t, self.ode.y))
            self.ode.t = ts
            self.ode.y = ys
            for i in range(self.ode.order, self.steps + 1):
                self.ode.y.append(self.ode.y[i - 1] + self.ode.inverse(i - 1))
                self.ode.t.append(self.ode.t[i - 1] + self.ode.h)
                self.points.append((self.ode.t[i], self.ode.y[i]))
            return self.points

if __name__ == "__main__":

    # Defining equation symbols
    t, y = sympy.symbols("t y")

    # Reading input file
    inFile = open("entrada.txt", "r")
    # outFile = open("saida.txt", "w")
    lines = inFile.readlines()

    for line in lines:
        words = line.split()

        method = words[0]

        if len(words) > 7:
            order = int(words[-1])
            f = sympy.lambdify([t, y], sympy.sympify(words[-2]), "math")
            steps = int(words[-3])
            h = float(words[-4])
            t0 = float(words[-5])
            ts = []
            ts.append(t0)
            ys = []
            for i in range(order):
                ys.append(float(words[i + 1]))
                ts.append(ts[i] + h)
            problem = ODE(ys, ts, h, f, order)
        else:
            y0 = float(words[1])
            t0 = float(words[2])
            h = float(words[3])
            steps = int(words[4])
            # Converts the function string into a lambda function
            f = sympy.lambdify([t, y], sympy.sympify(words[5]), "math")
            if len(words) == 7:
                order = int(words[6])
                problem = ODE(y0, t0, h, f, order)
            elif len(words) < 7:
                problem = ODE(y0, t0, h, f)

        calc = Solver(method, steps, problem)
        print(method)
        points = calc.solve()
        print("step, \tt, \ty")
        for i in range(len(points)):
            print(i, " ", points[i])

        pyplot.title(method)
        pyplot.xlabel('t')
        pyplot.ylabel('y')
        pyplot.plot(*zip(*points))
        pyplot.show()


import numpy as np
import os
import math as m
import matplotlib.pyplot as plt
from task2ab import save_im

def convolve_im(im, kernel):
    """ A function that convolves im with kernel

    Args:
        im ([type]): [np.array of shape [H, W, 3]]
        kernel ([type]): [np.array of shape [K, K]]

    Returns:
        [type]: [np.array of shape [H, W, 3]. should be same as im]
    """
    # Creating output matrix
    convolved_im = np.zeros_like(im)

    # Flip kernel
    kernel = np.flipud(np.fliplr(kernel))

    # Getting the dimensions of the im matrix
    im_rows = im.shape[0]
    im_cols = im.shape[1]

    # Getting the dimensions of the kernel matrix
    kernel_rows = kernel.shape[0]
    kernel_cols = kernel.shape[1]

    # Defining center assuming kernel_rows and kernel_cols are odd numbers
    kernel_center_x = m.floor(kernel_cols/2)
    kernel_center_y = m.floor(kernel_rows/2)

    # Pad the image with the appropriate amount of rows and cols
    image_padded = np.zeros((im_rows + (kernel_rows-1), im_cols + (kernel_cols-1), 3))

    image_padded[kernel_center_y:-kernel_center_y, kernel_center_x:-kernel_center_x, 0] = im[:, :, 0]
    image_padded[kernel_center_y:-kernel_center_y, kernel_center_x:-kernel_center_x, 1] = im[:, :, 1]
    image_padded[kernel_center_y:-kernel_center_y, kernel_center_x:-kernel_center_x, 2] = im[:, :, 2]

    # Iterating through all the cells of the image, convolving with the kernel and placing correctly in the convolved image
    for col in range(im_cols):
        for row in range(im_rows):
            convolved_im[row, col, 0] = (kernel * image_padded[row:row+kernel_rows, col:col+kernel_cols, 0]).sum()
            convolved_im[row, col, 1] = (kernel * image_padded[row:row+kernel_rows, col:col+kernel_cols, 1]).sum()
            convolved_im[row, col, 2] = (kernel * image_padded[row:row+kernel_rows, col:col+kernel_cols, 2]).sum()

    return convolved_im

if __name__ == "__main__":
    # Read image
    impath = os.path.join("images", "lake.jpg")
    im = plt.imread(impath)

    # Define the convolutional kernels
    h_a = np.ones((3, 3)) / 9
    h_b = np.array([
        [1, 4, 6, 4, 1],
        [4, 16, 24, 16, 4],
        [6, 24, 36, 24, 6],
        [4, 16, 24, 16, 4],
        [1, 4, 6, 4, 1],
    ]) / 256
    # Convolve images
    smoothed_im1 = convolve_im(im.copy(), h_a)
    smoothed_im2 = convolve_im(im, h_b)

    # DO NOT CHANGE
    assert isinstance(smoothed_im1, np.ndarray), \
        f"Your convolve function has to return a np.array. " +\
        f"Was: {type(smoothed_im1)}"
    assert smoothed_im1.shape == im.shape, \
        f"Expected smoothed im ({smoothed_im1.shape}" + \
        f"to have same shape as im ({im.shape})"
    assert smoothed_im2.shape == im.shape, \
        f"Expected smoothed im ({smoothed_im1.shape}" + \
        f"to have same shape as im ({im.shape})"

    save_im("convolved_im_h_a.jpg", smoothed_im1)
    save_im("convolved_im_h_b.jpg", smoothed_im2)


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

# # Computação vetorizada

# ## Objetivos
#
# - Compreender as capacidades da computação vetorizada;
# - Associar conceitos abstratos de Matemática a estruturas computacionais;
# - Saber estruturar dados em arrays multidimensionais;

# ## Introdução
#
# - Computação científica, problemas de alta complexidade e recursos computacionais.
# - Resolução eficiente de problemas na ciência de dados
# - Computação vetorizada (arrays x listas)

# ## O pacote *numpy*
#
# - Biblioteca padrão em Python para trabalhar com *arrays* multidimensionais e computação vetorizada.
# - Superpoderes às listas
# > A grande regra: _vetorize seus cálculos numéricos o máximo possível!_

# In[1]:

import numpy as np

# ## Motivação
#
# Este exemplo compara a eficiência de operações feitas com listas comuns e com *numpy*.

# In[2]:

# 1 µs = 1/1e6 segundo
# 1 ns = 1/1e9 segundo

L = range(500)
get_ipython().run_line_magic('timeit', '-n 10 [i**2 for i in L] # executa o laço 10 vezes')

a = np.arange(500)
get_ipython().run_line_magic('timeit', '-n 10 a**2 # eleva ao quadrado diretamente 10 vezes')

# ## Criação de arrays unidimensionais (1D)

# In[3]:

a = [1,2,3]
np.array(a) # a partir de lista

# In[4]:

np.array([1,2,3]) # diretamente

# In[5]:

np.array([2]*5)

# ## Criação de arrays bidimensionais (2D)

# In[6]:

A = [ [1,2], [0,2] ] # lista de listas
np.array(A) # matrix 2 x 2

# In[7]:

np.array([ [1,2], [0,2] ]) # diretamente

# In[8]:

A2 = [[1,2,3],[4,3,2]] # cada lista é uma linha da matriz
np.array(A2) # matriz 2 x 3

# In[9]:

np.array([1,1],[0,1]) # parênteses externos são obrigatórios!

# ### Dimensão, formato e comprimento

# In[11]:

x = np.array(a)
np.ndim(x) # aplica a função ndim

# In[12]:

x.ndim # como método

# In[13]:

np.shape(x) # formato

# In[14]:

x.shape # sem parênteses

# In[15]:

len(x) # comprimento

# In[16]:

X = np.array(A)
np.ndim(X) # array bidimensional

# In[17]:

np.shape(X)

# In[18]:

len(X) # apenas um comprimento. Qual?

# In[19]:

X2 = np.array(A2)
len(X2) # apenas da primeira dimensão. LINHAS

# In[20]:

X2.shape

# ## Funções para criação de arrays

# ### `arange`
#
# **Exemplo:** crie um array de números ímpares positivos menores do que 36.

# In[21]:

np.arange(1,36,2) # start,stop,step

# **Exemplo:** crie um array de números pares positivos menores ou iguais a 62.

# In[22]:

np.arange(0,63,2)

# **Exemplo:** calcule o valor de $f(x) = x^3 + 2x$ para $x$ elementos dos arrays anteriores.

# In[23]:

imp, par = np.arange(1,36,2), np.arange(0,63,2)
f = lambda x: x**3 + 2
fi, fp = f(imp), f(par)
print(fi)
print(fp)

# ### `linspace`
#
# **Exemplo:** crie um array igualmente espaçado de elementos em [0,1] com 11 elementos.

# In[24]:

np.linspace(0,1,num=11)

# In[25]:

np.linspace(0,1,11) # 'num' pode ser omitido

# In[26]:

x = np.linspace(0,1,10,endpoint=False) # exclui último
x

# In[27]:

y = np.arange(0,1,0.1) # equivalente
y

# In[28]:

x == y # comparação é elemento a elemento

# In[29]:

x == -y # apenas 0 é True

# In[30]:

x[1:] == y[1:]

# ### `all` e `any`

# In[31]:

np.all( x == y ) # verifica se todos são 'True'

# In[32]:

np.any( x == -y ) # verifica se pelo menos um é 'True'

# ### `random`
#
# **Exemplo**: crie um *array* 1D com 5 números aleatórios entre [0,1].

# In[33]:

r = np.random.rand(5)
r

# **Exemplo**: crie um *array* 1D com 50 números inteiros aleatórios entre [0,7].

# In[34]:

r2 = np.random.randint(0,7+1,50) # menor, maior é 8 (exclusive), tamanho
r2

# **Exemplo**: crie uma matriz m x n com números inteiros aleatórios entre inteiros [l,h].

# In[35]:

def gera_matriz(m,n,l,h):
    return np.random.randint(l,h,(m,n)) # tupla (m,n)

# In[36]:

gera_matriz(2,2,0,4)

# In[37]:

gera_matriz(3,2,0,4)

# In[38]:

gera_matriz(4,4,-2,7)

# ### `ones`
#
# Criando arrays unitários.

# In[39]:

np.ones(4)

# In[40]:

np.ones((3,2)) # tupla necessária para linhas e colunas

# ### `eye`
#
# Criando arrays 2D identidade. 1 na diagonal e 0 nas demais.

# In[41]:

np.eye(3) # matriz identidade 3 x 3

# ### `zeros`
#
# Arrays nulos.

# In[42]:

np.zeros(3)

# In[43]:

np.zeros((2,4)) # 2 x 4

# ### `full`
#
# Arrays de valor constante.

# In[44]:

np.full(3,0) # 1 x 3 com constante 0

# In[45]:

np.full(shape=(3,),fill_value=0)

# In[46]:

F1 = np.full(shape=(2,2),fill_value=1) # 2 x 2 com 1
F1

# In[47]:

F1 == np.ones(2) # mesmo resultado que ones

# Outras maneiras:

# In[48]:

F2 = 3*np.ones((4,4))
F2

# ## Especificando tipos de dados
#
# Observe o seguinte exemplo:

# In[49]:

F2

# In[50]:

F3 = np.full((4,4),3)
F3

# In[51]:

F2 == F3 # valores iguais

# In[52]:

F2.dtype == F3.dtype # tipos diferentes

# In[53]:

F2.dtype

# In[54]:

F3.dtype

# Especificamos o tipo de dados com `dtype`.

# In[55]:

np.ones((4,2),dtype=bool) # matriz de booleanos

# In[56]:

np.ones((4,2),dtype=str) # matriz de strings; 'U1' diz que há no máximo 1 caracter

# In[57]:

S = np.array(['dias','mes','ano'])
S.dtype # 4 é o no. máximo de caracteres nas strings

# ## Indexação e fatiamento
#
# Funcionam de maneira similar ao uso com listas.

# In[58]:

I = np.linspace(0,20,11)
I

# In[59]:

I[3],I[2:4],I[5:8],I[-4:-1]

# In[60]:

I[::-1] # invertendo o array

# In[61]:

I2 = np.array([I,2*I,3*I,4*I])
I2

# Em arrays bidimensionais, a indexação é feita por meio de uma tupla. Porém, a explicitação dos parênteses é desnecessária.

# In[62]:

I2[(2,3)] # 3a. linha; 4a. coluna

# In[63]:

I2[2,3]

# In[64]:

I2[0,:] # 1a. linha

# In[65]:

I2[1,:] # 2a. linha

# In[66]:

I2[-1,:] # última linha

# In[67]:

I2[:,0] # 1a. coluna

# In[68]:

I2[:,1] # 2a. coluna

# In[69]:

I2[:,8] # 9a. coluna

# In[70]:

I2[:,2:4] # 3a. - 4a. coluna

# In[71]:

I2[1:3,6:10] # submatriz: linhas 2 - 3; 7-10

# ### Alteração de valores
#
# Os arrays são mutáveis por indexação.

# In[72]:

A3 = np.random.rand(4,4)
A3

# In[73]:

A3[0:4,0] = -1
A3

# In[74]:

A3[:,-1] = -1
A3

# In[75]:

A3[1:3,1:3] = 0
A3

# Podemos alterar valores com arrays.

# In[76]:

A3[1,:] = -2*np.ones(4)
A3

# A indexação pode usar um comprimento de passo (*step*).

# In[77]:

A3[0:4:3,1:3] = np.full((1,2),8) # na indexação esquerda, 1a. linha : 4a. linha : step de 3
A3

# ### `newaxis`
#
# `newaxis` é uma instância do `numpy` que permite aumentar de 1 a dimensão de um array existente.

# **Exemplo:** como inserir a diagonal de uma matriz em uma segunda matriz como uma coluna adicional?

# Criamos duas matrizes aleatórias.

# In[78]:

# matriz 4 x 4 de inteiros aleatórios entre 0 e 9
B1 = np.random.randint(0,10,(4,4))
B1

# In[79]:

# matriz 4 x 4 de inteiros aleatórios entre -10 e 9
B2 = np.random.randint(-10,10,(4,4))
B2

# Extraímos a diagonal da primeira.

# In[80]:

# diagonal de B1
db1 = np.diag(B1)
db1

# Notemos agora que as dimensões são diferentes.

# In[81]:

print(B2.ndim)
print(db1.ndim)

# Para podermos aglutinar a diagonal como uma nova coluna na primeira matriz, primeiro temos que transformar o array unidimensional para uma matriz.

# In[82]:

db1 = db1[:,np.newaxis]
print(db1.ndim) # agora o array é bidimensional
db1

# `newaxis` é um "eixo imaginário" incluído *inplace*, mas que altera dinamicamente o array. No caso acima, o array tornou-se em uma coluna.

# Agora, podemos "colar" um array 2D com outro por uma concatenação.

# ### `concatenate`
#
# `concatenate` é usado para concatenar *arrays*. A concatenação requer uma tupla contendo os *arrays* a concatenar e o eixo de referência.

# In[83]:

B3 = np.concatenate((B2,db1), axis=1)
B3

# No caso acima, `axis=1` indica que a concatenação é ao longo da coluna. Dessa forma, inserimos a segunda diagonal como uma coluna adicional na segunda matriz. Claramente, isto só é possível porque ambas as matrizes eram de mesmo formato.

# #### `axis`
#
# Nos arrays multidimensionais do Python, `axis` é usado para indicar a "direção" dos dados. Em arrays bidimensionais, `axis=0` refere-se à direção de cima para baixo (ao longo das linhas), ao passo que `axis=1` refere-se à direção da esquerda para a direita (ao longo das colunas).
#
# **Obs.:** note que a palavra `axis` ("eixo") deve ser usada, e não "axes" ("eixos").

# Para aglutinar uma linha na matriz anterior, fazemos uma concatenação em linha.

# In[84]:

# array de zeros com mesmo número de colunas de B3
db2 = np.zeros(np.shape(B3)[1])
db2

# In[85]:

db2 = db2[np.newaxis,:] # cria o "eixo imaginário" na direção 0

# In[86]:

B4 = np.concatenate((B3,db2),axis=0) # concatena ao longo das linhas
B4

# ## Indexação avançada
#
# Podemos usar máscaras como artifícios para indexação avançada.

# In[87]:

IA1 = np.arange(-10,11)
IA1

# Vamos criar um *array* aleatório de True e False no mesmo formato que o *array* anterior.

# In[88]:

mask1 = np.random.randint(0,2,np.shape(IA1),dtype=bool)
mask1

# Esta *máscara booleana* pode ser aplicada no array para extrair apenas os elementos cujos índices são marcados como `True` pela máscara.

# In[89]:

IA1[mask1]

# Há maneiras mais diretas aplicáveis a filtragens. Para extrair os valores negativos do array:

# In[90]:

IA1 < 0 # máscara booleana

# In[91]:

IA1[IA1 < 0]

# Para extrair os valores positivos do array:

# In[92]:

IA1[IA1 > 0] # máscara booleana para positivos

# Para extrair os valores no intervalo $]-2,5[$, fazemos:

# In[93]:

IA1[(IA1 > -2) & (IA1 < 5)] # & é o operador booleano 'elemento a elemento'

# Para extrair pares e ímpares, poderíamos fazer:

# In[94]:

pares, impares = IA1[IA1 % 2 == 0] , IA1[IA1 % 2 == 1]
pares,impares

# Podemos usar listas como máscaras:

# In[95]:

alguns = pares[[0,2,3,5]] # acessa 1o., 3o. 4o. e 6o. elemento de 'pares'

# In[96]:

impares[alguns] # estude este caso

# No caso acima, por exemplo, -10 é uma indexação reversa que excede o compriemento do array à esquerda. Portanto, ele retorna o primeiro elemento do array, que é -9. O índice -6 acessa o sexto elemento a partir da direita, que é -1. O índice -4 acessa o quarto elemento a partir da direita. Por fim, o índice 0 acessa o primeiro elemento que é -9.

# ## Operações elemento a elemento
#
# As operações aritméticas e de cálculo são feitas elemento a elemento nos *arrays*. Já mostramos alguns exemplos acima, mas vamos tornar isto mais claro aqui.

# In[97]:

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

# In[98]:

# operações elemento a elemento
print(a + b)
print(a - b)
print(a * b)
print(a / b)
print(a ** b)

# In[99]:

2*a + 4*b - 6*b**2 + 1.1/2*a

# ## Funções matemáticas
#
# O `numpy` possui a maioria dass funções disponíveis no módulo `math` e outras mais. As funções são diretamente aplicáveis aos *arrays*. Lembre-se que para fazer o mesmo usando em listas, tínhamos de construir meios de iterar sobre elas e aplicar a função a cada elemento por vez. Isto não é mais necessário com o `numpy`. Eis a beleza da computação vetorizada!
#
# Vejamos uma série de exemplos.

# In[100]:

x = np.arange(10)
x

# In[101]:

np.sqrt(x)

# In[102]:

np.cos(x) + 2*np.sqrt(x)

# In[103]:

y = np.sin(2*x)
z = np.exp(x + y)
y - z

# ## *Broadcasting*
#
# *Broadcasting* é a capacidade que o *numpy* oferece para realizarmos operações em arrays com diferentes dimensões.

# ### Regras do *broadcasting*
#
# 1. Se dois *arrays* tiverem dimensões diferentes, o formato do array com menor dimensão é preenchido por 1 do lado esquerdo.
# 2. Se o formato dos *arrays* não for igual em dimensão alguma, o array com tamanho igual a 1 é esticado nesta direção para ficar no mesmo tamanho correspondente do outro array.
# 3. Se em qualquer direção os tamanhos dos *arrays* forem diferentes e nenhum deles for igual a 1, então um erro é retornado.

# #### Exemplo da Regra 1

# In[104]:

A = np.array([[1, 2, 3],[4, 5, 6]]) # array 2D
b = np.array([10, 20, 30]) # array 1D
print(A.shape)
print(b.shape)

# In[105]:

A + b

# A soma pode ser realizada mesmo assim. O que ocorreu? Cada linha de `A` foi somada à única linha de `b`. O *broadcasting* amplia o array de menor dimensão automaticamente da seguinte forma:
#
# Pela regra 1, o *array* `b` tem dimensão menor. Então, ele é preenchido de modo que:
#
# ```python
# A.shape -> (2, 3)
# b.shape -> (1, 3)
# ```
#
# Pela regra 2, a primeira dimensão de `A` é 2 e a de `b` é 1. Então, a dimensão de `b` é "esticada", de modo que:
#
# ```python
# A.shape -> (2, 3)
# b.shape -> (2, 3)
# ```

# A mesma operação poderia ter sido feita com:

# In[106]:

A + np.array([b,b])

# #### Exemplo da Regra 2

# In[107]:

A = np.arange(3).reshape((3, 1))
b = np.arange(3)
print(A.shape)
print(b.shape)

# In[108]:

A + b

# Neste caso, ambos os arrays sofrem *broadcasting*. Ele ocorre da seguinte forma.
#
# Como
#
# ```python
# A.shape = (3, 1)
# b.shape = (3,)
# ```
# a regra 1 diz que `b` deve ser preenchido de modo que
#
# ```python
# A.shape -> (3, 1)
# b.shape -> (1, 3)
# ```
# e, pela regra 2, cada uma das dimensões 1 deve ser alterada de modo que:
#
# ```python
# A.shape -> (3, 3)
# b.shape -> (3, 3)
# ```
#
# Assim, o *broadcasting* é permitido.

# #### Exemplo da Regra 3

# In[109]:

A = np.ones((3, 2))
b = np.arange(3)
print(A.shape)
print(b.shape)

# In[110]:

A + b

# Neste exemplo, o *broadcasting* não é permitido. O caso é levemente diferente do primeiro exemplo em que `A` é transposta.
#
# Temos que
#
# ```python
# M.shape = (3, 2)
# a.shape = (3,)
# ```
#
# Pela regra 1, devemos ter
#
# ```python
# M.shape -> (3, 2)
# a.shape -> (1, 3)
# ```
#
# e, pela regra 2, a primeira dimensão deve ser esticada para combinar-se com a de `A` enquanto a segunda não é alterada por não ser 1.
#
# ```python
# M.shape -> (3, 2)
# a.shape -> (3, 3)
# ```
# Porém, o formato final de ambos não se combina. Sendo incompatíveis, o *broadcasting* falha.


### Author: Khoong Wei Hao
### Project 2

import numpy as np
import imageio
from scipy.fftpack import dct, idct
from scipy.misc import toimage
import math
import time
from collections import namedtuple
from matplotlib import pyplot as plt

# the following functions phi(n), idct2_1d(data), idct2_2d(dct_output) are from Project 1, for the 2D iDCT
def phi(n):
    if n == 0:
        return (1/2)**(1/2)
    else:
        return 1

def idct2_1d(data):
    N = data.shape[0]
    B = np.zeros([N, N])
    l = np.zeros([N])
    for i in range(N):
        for j in range(N):
            B[i, j] = ((2/N)**(1/2)) * phi(j) * np.cos((i + 1/2) * np.pi * j / N)
    return np.dot(B, data)

def idct2_2d(dct_output):
    start_time = time.time()
    # note that the image matrix is square, so N is the number of rows/columns
    N = dct_output.shape[0]
    d = np.empty([N, N], float)
    x = np.empty([N, N], float)
    for i in range(N):
        d[:,i] = idct2_1d(dct_output[:,i])
    for i in range(N):
        x[i,:] = idct2_1d(d[i,:])

    total_time_taken = time.time() - start_time
    print('Total time taken = ' + str(total_time_taken) + 's')
    return x

###########################################
### Project 2 Functions For imdecomp.py ###
###########################################

# function to partition image into 8 X 8 blocks
def partition_img(img, M, N):
    num_partitions_by_row = M/8
    num_partitions_by_col = N/8

    img_r = np.array(np.split(img, num_partitions_by_row, axis=0))
    img_rc = np.array(np.split(img_r, num_partitions_by_col, axis=2))
    img_final = np.concatenate(img_rc, axis=0)

    return img_final

# function to carry out 2D iDCT for each 8X8 patch
def idct2d_8_by_8(patches):
    return [idct2_2d(p) for p in patches]

# function to construct image J by concatenating all 8X8 patches and convert all entries to unsigned 8-bit integers
def reconstruct_image(idct2d_patches):
    coeff_matrices_decomp_3d = np.array(idct2d_patches)

    # note that each 'row' has 256/8=32 8X8 matrices
    concat_matrices_decomp = []
    for i in range(0,len(idct2d_patches),32):
        # concatenate column-wise
        concat_matrices_decomp += [np.concatenate(idct2d_patches[i:i+32], axis=1)]

    # stack the 32 'rows' of 8X8 matrices to get 256X256
    stack_result_decomp = np.vstack(concat_matrices_decomp)

    # convert all entries to unsigned 8-bit integers
    return np.uint8(stack_result_decomp)

# perform image decompression
def imdecomp(C):
    M = C.shape[0]
    N = C.shape[1]

    # partition compression output into 8X8 patches
    partitions_comp = partition_img(C, M, N)

    # apply 2D iDCT on each 8X8 patch
    idct2d_partitions = idct2d_8_by_8(partitions_comp)

    # reconstruct image by concatenating all 8X8 patches and converting all entries to 8-bit unsigned integers
    reconstructed_image = reconstruct_image(idct2d_partitions)

    return reconstructed_image


import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import numpy as np

plt.close()
#Create plots
fig, (ax0,ax1) = plt.subplots(2,1,sharex=True, figsize=(10,10))
lower,upper = -5,5
#Plot r v h as bifurcation.
x = np.linspace(-10,10,200)
r = np.linspace(lower,upper,200,dtype=complex)
h = np.sqrt(r/3)*r*2/3
ax0.set_xlim(lower,upper)
ax0.plot(r,h,'b.',r,-h,'g.', alpha = .3)
ax0.set_title(r'$r$ = $\frac{2r}{3}\sqrt{\frac{r}{3}}$')
ax0.set_ylabel('h')
ax0.grid(True)
#Plot y as function of r and x
ax1.set_ylim(-4,4)
ax1.set_title(r'$y$ = $rx - x^3:   \   \{r \in{}: -5 ... 5\}$')
ax1.set_ylabel('y')
for i in range(lower,upper):
    y = i*x - x**3
    ax1.plot(-y,x, alpha = .4, label='r = {}'.format(i))
    plt.legend()
#3D
fig = plt.figure(figsize=(10,10))
ax2 = fig.gca(projection='3d')

# Make data.
x = np.arange(-1, 1, 0.01)
r = np.arange(-1, 1, 0.01)
X, R = np.meshgrid(x, r)
Z = X*R - X**3
# Plot the surface.
ax2.set_aspect('equal')
ax2.plot_surface(X, R, Z,
                        rstride=5,cstride=5,
                        cmap=cm.inferno,
                        linewidth=1,
                        antialiased=True,
                        alpha=.5)
cset = ax2.contour(X, R, Z, zdir='z',
                            offset=-2.0,
                            cmap=cm.coolwarm,
                            lw=2,
                            tight=True)
#cset = ax2.contour(X, R, Z, zdir='x', offset=0, cmap=cm.coolwarm)
cset = ax2.contour(X, R, Z, zdir='y',  offset=1, cmap=cm.coolwarm)

# Customize the z axis.
#ax2.set_zlim3d(-500, 500)
ax2.set_title('Cusp Catastrophe',size = 16)
ax2.set_xlabel('$x$', size=14)
ax2.set_ylabel('$r$',size=14)
ax2.set_zlabel('$y = r x - x^3 = -h $',size=14)
ax2.set_xticks([0])
ax2.set_yticks([0])
ax2.set_zticks([0])
ax2.grid(True)
ax2.xaxis.pane.set_edgecolor(None)
ax2.yaxis.pane.set_edgecolor(None)
ax2.zaxis.pane.set_edgecolor(None)
ax2.xaxis.pane.fill = False
ax2.yaxis.pane.fill = False
ax2.zaxis.pane.fill = True
#Rotate the plot
# for angle in range(0, 360):
#     ax2.view_init(30, angle)
#     plt.draw()
#     plt.pause(.001)
plt.show()


from math import cos, tanh
import matplotlib.pyplot as plt	# Субмодуль для создания графиков
from numpy import arange  # Импортируем субмодуль создания массива

# Реализация метода пассивного поиска
def PassiveSearch(f, a, b, Nmax):

	print("Метод пассивного поиска")
	print("+-----+------------+")
	print('|  N  |    xMin    |')
	print("+-----+------------+")

	N = 1               # Стартовая точка
	inaccuracy = []     # Пока что пустой список неточностей
	N_list = []         # Пока что пустой список точек N

# Цикл для нахождения минимума
	while N <= Nmax:
		accuracy = (b - a) / (N + 1)       	# Шаг поиска
		inaccuracy.append(accuracy)       		  	# Добавляем неточность
		N_list.append(N)            		# Добавляем N
		xMin = a                    		# Считаем, что минимум находится в точке a
		x = a
		# Идём по всему интервалу
		while x <= b:
			if f(x) < f(xMin):  # Если очередное значение стало меньше,
				xMin = x        # то обновляем точку минимума
			x += accuracy              # Переходим на следующую точку
		print('| %*d | %*.4f |' % (3, N, 10, xMin))
		N += 1                  # Увеличиваем итерацию на 1
		# N *= 2                # Для ускорения нахождения минимума, т.е. каждый раз умножая на 2
	print("+-----+------------+")

# Рисуем график зависимостей неточности от числа точек N
	plt.plot(N_list, inaccuracy)    # Формируем график по осям
	plt.ylabel('Погрешность')       # Подписываем ось У
	plt.xlabel('N')                 # Подписываем ось Х
	plt.grid(True)					# Включаем сетку
	plt.show()                      # Оторбажаем график

	return xMin                     # Возвращщаем конечную точку минимума

# Реализация метода дихотомии
def Dichotomy(f, a, b, eps):
	delta = 0.01
	k = 1

	print("Метод дихотомии")
	print("+-----+------------+------------+------------+------------+------------+------------+------------+")
	print("|  k  |     ak     |     bk     |     lk     |     x1     |     x2     |   f(x1)    |    f(x2)   |")
	print("+-----+------------+------------+------------+------------+------------+------------+------------+")

	# Запускаем цикл. Пока интервал не сомкнётся...
	while b - a > eps:
		x = (a + b) / 2

		x_Left = x - delta
		x_Right = x + delta

		func_xLeft = f(x_Left)
		func_xRight = f(x_Right)

		print('| %*d | %*.3f | %*.3f | %*.3f | %*.3f | %*.3f | %*.3f | %*.3f |' % (3, k, 10, a, 10, b, 10, b - a, 10, x_Left, 10, x_Right, 10, func_xLeft, 10, func_xRight))
		# Выполняем процедуру исключения отрезка
		if func_xLeft >= func_xRight:
			a = x
		else:
			b = x

		k += 1

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

	return (a + b) / 2      # Возвращает вычисленный минимум

# Заданная унимодальная функция для поиска минимума
if __name__ == "__main__":
	UnimodalFunction = lambda x: cos(x) * tanh(x)

# Интервал поиска [-2, 0]
	a = -2
	b = 0
	eps = 0.1                   # Точность
	Nmax = (2*(b-a)/eps - 1)    # Максимально возможное число точек
	delta = 0.01
	xlist = arange(a, b, delta)
	ylistUni = [UnimodalFunction(x) for x in xlist]

	plt.plot(xlist, ylistUni)
	plt.title("Унимодальная функция")
	plt.grid(True)
	plt.show()

	xMin_ps = PassiveSearch(UnimodalFunction, a, b, Nmax)
	xMin_dic = Dichotomy(UnimodalFunction, a, b, eps)

print('Найденный методом пасс. поиска минимум:', xMin_ps)
print('Найденный методом дихотомии минимум:', xMin_dic)


#!/usr/local/Cellar/python/2.7.6/bin/python
# -*- coding: utf-8 -*-

'''Standard python modules'''
import sys

'''For scientific computing'''
from numpy import *
import scipy

'''For plotting'''
from matplotlib import pyplot, cm
from mpl_toolkits.mplot3d import Axes3D

EX_DIRECTORY_PATH = '/Users/saburookita/Personal Projects/MachineLearningPy/'

def part1():
    A = eye(5)
    print A

def hypothesis(X, theta):
    """Hypothesis function for linear regression
    this should be inlined code, but it's here for clear representation purpsoes
    """
    return X.dot(theta)

def computeCostLoop(X, y, theta):
    """Compute cost but in the slower loop method, in order to show how the
    pre-vectorization algorithm initially works. Always start with loop version first
    and code the vectorized version later
    """
    m = len(y)  # or m = shape(y)[0], since y is 1D

    cumulative_sum = 0
    for i in range(0, m):
        cumulative_sum += (hypothesis(X[i], theta) - y[i]) ** 2
    cumulative_sum = (1.0 / (2 * m)) * cumulative_sum
    return cumulative_sum

def computeCost(X, y, theta):
    """Compute cost, vectorized version"""
    m 	 = len(y)
    term = hypothesis(X, theta) - y
    # sum( term**2 ) in this case ~= term.T.dot( term )
    return (term.T.dot(term) / (2 * m))[0, 0]

def gradientDescentLoop(X, y, theta, alpha, iterations):
    """Gradient descent in loop version"""
    grad = copy(theta)
    m 	 = len(y)
    n 	 = shape(X)[1]

    for counter in range(0, iterations):
        # create n number of cumulative inner sums
        cum_sum = [0 for x in range(0, n)]

        for j in range(0, n):
            for i in range(0, m):
                term 		= (hypothesis(X[i], grad) - y[i])
                cum_sum[j] += X[i, j] * (term)

        # assign new values for each gradient, this should be separate from the loop above
        # in order to achieve simulataneous update effect
        for j in range(0, n):
            grad[j] = grad[j] - cum_sum[j] * (alpha / m)

    return grad

def gradientDescent(X, y, theta, alpha, iterations):
    """Vectorized gradient descent"""
    grad = copy(theta)
    m 	 = len(y)

    for counter in range(0, iterations):
        inner_sum = X.T.dot(hypothesis(X, grad) - y)
        grad 	 -= alpha / m * inner_sum

    return grad

def plot(X, y):
    """Create a plot out of X and y data, X should exclude the intercept units.
    Call pyplot.show(block=True) in order to show the plot window"""
    pyplot.plot(X, y, 'rx', markersize=5 )
    pyplot.ylabel('Profit in $10,000s')
    pyplot.xlabel('Population of City in 10,000s')

def part2_1():
    data = genfromtxt( EX_DIRECTORY_PATH + "ex1data1.txt", delimiter=',')
    X, y = data[:, 0], data[:, 1]
    m 	 = len(y)
    y 	 = y.reshape(m, 1)

    plot(X, y)
    pyplot.show(block=True)

def part2_2():
    data = genfromtxt( EX_DIRECTORY_PATH + 'ex1data1.txt', delimiter=',')
    X, y = data[:, 0], data[:, 1]
    m 	 = len(y)
    y 	 = y.reshape(m, 1)

    X 			= c_[ones((m, 1)), X]
    theta 		= zeros((2, 1))
    iterations 	= 1500
    alpha 		= 0.01

    cost 	= computeCost(X, y, theta)  # should be 32.07
    theta 	= gradientDescent(X, y, theta, alpha, iterations)
    print cost
    print theta

    predict1 = array([1, 3.5]).dot(theta)
    predict2 = array([1, 7]).dot(theta)
    print predict1
    print predict2

    plot(X[:, 1], y)
    pyplot.plot(X[:, 1], X.dot(theta), 'b-')
    pyplot.show(block=True)

def part2_4():
    data = genfromtxt( EX_DIRECTORY_PATH + "/ex1data1.txt", delimiter=',')
    X, y = data[:, 0], data[:, 1]
    m 	 = len(y)
    y 	 = y.reshape(m, 1)
    X 	 = c_[ones((m, 1)), X]

    theta0_vals = linspace(-10, 10, 100)
    theta1_vals = linspace(-4, 4, 100)

    J_vals = zeros((len(theta0_vals), len(theta1_vals)), dtype=float64)
    for i, v0 in enumerate(theta0_vals):
        for j, v1 in enumerate(theta1_vals):
            theta 		 = array((theta0_vals[i], theta1_vals[j])).reshape(2, 1)
            J_vals[i, j] = computeCost(X, y, theta)

    R, P = meshgrid(theta0_vals, theta1_vals)

    fig = pyplot.figure()
    ax 	= fig.gca(projection='3d')
    ax.plot_surface(R, P, J_vals)
    pyplot.show(block=True)

    fig = pyplot.figure()
    # ax 	= fig.gca(projection='3d')
    pyplot.contourf(R, P, J_vals.T, logspace(-2, 3, 20))
    pyplot.plot(theta[0], theta[1], 'rx', markersize = 10)
    pyplot.show(block=True)

def main():
    set_printoptions(precision=6, linewidth=200)

    part1()
    part2_1()
    part2_2()
    part2_4()

    sys.exit()

if __name__ == '__main__':
    main()


import numpy as np
from math import sqrt

# Calculates the root-mean-square for the given data (data = 1-D array)
def calculateRMS(data):
    return sqrt(sum(n * n for n in data) / len(data))

# Calculates the pairwise ratios of the RMS values (one for each channel) for one window
# (every channel with all the others)
def pairwiseRatio(rmsDataOneWindow):
    ratios = []
    for i in range(len(rmsDataOneWindow)):
        for j in range(i, len(rmsDataOneWindow)):
            if i != j:
                ratios.append(rmsDataOneWindow[i] / rmsDataOneWindow[j])
    return ratios

# This method is called for calculating the feature vector for the given window data
# param: windowData = [[channel 1]...[channel n]] (data for one window)
# returns: featureVector = [rms_channel_1, ..., rms_channel_n, ratios]
def calculateFeatureForWindow(windowData, ratio=True):
    rmsData = []
    for channel in windowData:
        rmsData.append(calculateRMS(channel))

    featureVec = rmsData
    if ratio: featureVec = np.concatenate([featureVec, pairwiseRatio(rmsData)], axis=None)
    return featureVec


# IMPORTS
import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
import matplotlib.style as style
from IPython.core.display import HTML

# PLOTTING CONFIG
%matplotlib inline
style.use('fivethirtyeight')
plt.rcParams["figure.figsize"] = (14, 7)
HTML("""
<style>
.output_png {
    display: table-cell;
    text-align: center;
    vertical-align: center;
}
</style>
""")
plt.figure(dpi=100)

##### COMPUTATION #####
# DECLARING THE "TRUE" PARAMETERS UNDERLYING THE SAMPLE
mu_real = 10
sigma_real = 2

# DRAW A SAMPLE OF N=1000
np.random.seed(42)
sample = stats.norm.rvs(loc=mu_real, scale=sigma_real, size=1000)

# ESTIMATE MU AND SIGMA
mu_est = np.mean(sample)
sigma_est = np.std(sample)
print("Estimated MU: {}\nEstimated SIGMA: {}".format(mu_est, sigma_est))

##### PLOTTING #####
# SAMPLE DISTRIBUTION
plt.hist(sample, bins=50,normed=True, alpha=.25)

# TRUE CURVE
plt.plot(np.linspace(2, 18, 1000), norm.pdf(np.linspace(2, 18, 1000),loc=mu_real, scale=sigma_real))

# ESTIMATED CURVE
plt.plot(np.linspace(2, 18, 1000), norm.pdf(np.linspace(2, 18, 1000),loc=np.mean(sample), scale=np.std(sample)))

# LEGEND
plt.text(x=9.5, y=.1, s="sample", alpha=.75, weight="bold", color="#008fd5")
plt.text(x=7, y=.2, s="true distrubtion", rotation=55, alpha=.75, weight="bold", color="#fc4f30")
plt.text(x=5, y=.12, s="estimated distribution", rotation=55, alpha=.75, weight="bold", color="#e5ae38")

# TICKS
plt.tick_params(axis = 'both', which = 'major', labelsize = 18)
plt.axhline(y = 0, color = 'black', linewidth = 1.3, alpha = .7)

# TITLE, SUBTITLE & FOOTER
plt.text(x = 0, y = 0.3, s = "Normal Distribution",
               fontsize = 26, weight = 'bold', alpha = .75)
plt.text(x = 0, y = 0.265,
         s = 'Depicted below is the distribution of a sample (blue) drawn from a normal distribution with $\mu = 10$\nand $\sigma = 2$ (red). Also the estimated distrubution with $\mu \sim {:.3f} $ and $\sigma \sim {:.3f} $ is shown (yellow).'.format(np.mean(sample), np.std(sample)),
         fontsize = 19, alpha = .85)
plt.text(x = 0,y = -0.025,
         s = '   ©Joshua Görner                                                                                github.com/jgoerner   ',
         fontsize = 14, color = '#f0f0f0', backgroundcolor = 'grey');


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

# In[1]:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import scipy.stats as st
import statsmodels.api as sm
import pingouin as pg
get_ipython().run_line_magic('matplotlib', 'inline')

# In[2]:

df = pd.read_csv("/home/atrides/Desktop/R/statistics_with_Python/10_GLM1_ANOVA/Data_Files/Viagra.dat", sep='\t')
df.head()

# In[3]:

# lineplot
_ = sns.lineplot(x='dose', y='libido', data = df, err_style='bars')

# In[4]:

# describing statistical properties
df.groupby('dose')['libido'].describe()

# In[5]:

# ideal value for normal distribution , skewness = 0 , excess of kurtosis(here, same as kurtosis) = 0
for i in sorted(df['dose'].unique()):
    print(f'For group with dose = {i} : ')
    print(st.describe(df[df['dose']==i].libido), '\n')

# In[6]:

df_dose1 = df[df['dose']==1]
df_dose2 = df[df['dose']==2]
df_dose3 = df[df['dose']==3]

# In[7]:

# Levene's Test for Homogeneity of Variance (center = median(by default))
stat, p = st.levene(df_dose1['libido'], df_dose2['libido'], df_dose3['libido'])
print(stat, p)

# #### # since the above p value is not significant , i.e, p>.05, we can say that homogeneity of variance is maintained. just for the sake of visualization I am going to draw the boxplot .

# In[8]:

_ = sns.boxplot(x='dose', y='libido', data=df)

# In[9]:

from statsmodels.formula.api import ols
m01 = ols('libido~C(dose)', data=df).fit()
m01.summary()

# In[10]:

anova_table = sm.stats.anova_lm(m01)
anova_table

# #### # making some plots for assumption checking

# In[11]:

prediction = pd.DataFrame(m01.fittedvalues)
prediction.columns = ['predicted']
prediction['standarized_prediction']  = (prediction['predicted']-prediction['predicted'].mean())/prediction['predicted'].std()
prediction.head()

# In[12]:

import statsmodels.stats.outliers_influence as sms
summary_frame = sms.OLSInfluence(m01).summary_frame()
summary_frame = pd.merge(summary_frame, prediction, how = 'inner', left_index = True, right_index = True)
summary_frame .head()

# In[13]:

_ = sns.scatterplot(y = 'standard_resid', x='standarized_prediction', data = summary_frame)
_ = plt.axhline(y=0)

# #### # This graph can be used for testing homogeneity of variance. We encountered this kind of plot previously; essentially, if it has a funnel shape then we’re in trouble. The plot we have shows points that are equally spread for the three groups, which implies that variances are similar across groups (which was also the conclusion reached by Levene’s test).

# In[14]:

_ = pg.qqplot(summary_frame['standard_resid'], confidence=False)

# #### # The second plot is a Q-Q plot , which tells us something about the normality of residuals in the model. We want our residuals to be normally distributed, which means that the dots on the graph should cling  to the diagonal line. Ours look like they have had a bit of an argument with the diagonal line, which suggests that we may not be able to assume normality of errors and should perhaps use a robust version of ANOVA instead.

# In[15]:

# Doing Welch anova in the case if homogeniety of variance  is violated(our data here dont need this test)
aov = pg.welch_anova(dv='libido', between='dose', data=df)
aov

# ## Robust ANOVA (for independent samples)

# In[16]:

st.kruskal(df_dose1['libido'], df_dose2['libido'], df_dose3['libido'])

# # Planned Comparison

# #### https://www.statsmodels.org/devel/examples/notebooks/generated/contrasts.html#examples-notebooks-generated-contrasts--page-root

# In[17]:

contrast1 = [-2,1,1]
contrast2 = [0,-1,1]

# In[18]:

contrast = np.vstack((contrast1, contrast2))
contrast = contrast.T
print(contrast)

# In[19]:

m02 = ols("libido ~ C(dose, contrast)", data=df).fit()
m02.summary()

# # Trend Analysis

# In[20]:

from patsy.contrasts import Poly
levels = df.dose.unique().tolist()
contrast = Poly().code_without_intercept(levels)
print(contrast.matrix)

# In[21]:

m03 = ols("libido ~ C(dose, Poly)", data=df).fit()
m03.summary()

# # Post-hoc tests

# In[22]:

from statsmodels.sandbox.stats.multicomp import MultiComparison

# In[23]:

multicomp = MultiComparison(df['libido'], df['dose'])   # testfunc

# #### # https://pythonhealthcare.org/2018/04/13/55-statistics-multi-comparison-with-tukeys-test-and-the-holm-bonferroni-method/
# #### # https://www.statsmodels.org/stable/generated/statsmodels.sandbox.stats.multicomp.MultiComparison.html

# In[24]:

# Bonferroni
com = multicomp.allpairtest(st.ttest_ind, method='bonf')
print(com[0])

# In[25]:

# Holm
comp = multicomp.allpairtest(st.ttest_ind, method='Holm')
print (comp[0])

# In[26]:

# Tukey HSD
co = multicomp.tukeyhsd(alpha=0.05)
co.summary()

# ### another library for post-hoc tests
# #### # https://scikit-posthocs.readthedocs.io/en/latest/posthocs_api/

# In[27]:

import scikit_posthocs as sk_ph

# In[28]:

sk_ph.posthoc_tukey_hsd(df['libido'], df['dose'])

# In[29]:

sk_ph.posthoc_tukey(df, val_col = 'libido', group_col='dose')

# ## Robust Post-hoc test

# In[30]:

sk_ph.posthoc_wilcoxon(df, val_col = 'libido', group_col='dose')

# #### # from above table it seems that groups (1,3) and (2,3) are significant.
#

# In[31]:

anova_table

# In[32]:

SSt = 43.73
SSm = 20.133
SSr = 23.6

# In[33]:

R_squared = SSm/SSt    # here R_squared is called eta_squared
R_squared

# In[34]:

effect_size = np.sqrt(R_squared)   #R
effect_size

# #### # a more complex measure is omega-squared , which adjust for the fact that we are doing finding effect size for population and not just our sample

# In[35]:

MSr = 1.967
df_m = 2
ω2 = (SSm-df_m*MSr)/(SSt+MSr)
ω2

# In[36]:

ω = np.sqrt(ω2)       # ω, more accurate measure of R, treat ω as unbiased R
ω

# In[37]:

# effect sizes for different pair of groups
def s_within(s1,s2,n1,n2):
    return np.sqrt((((n1-1)*(s1**2))+((n2-1)*(s2**2)))/(n1+n2-2))

def mes(m1, m2, s1, s2, n1, n2):
    s_ = s_within(s1,s2,n1,n2)
    d = (m1-m2)/s_   # cohen's D
    d_var = (n1+n2)/(n1*n2) + (d**2)/(2*(n1+n2))    # variance of d

    df = n1+n2-2
    J = 1-3/(4*df-1)   # correction factor

    g = J*d          #   Hedges' g

    g_var = (J**2)*d_var     #   variance of g

    a = (n1+n2)**2/(n1*n2)    # a corrects for inbalance in n1 & n2

    r = d/np.sqrt(d**2+a)  # correlation coeffecient

    r_var = (a**2 * d_var)/((d**2 + a)**3)     #   variance of r

    print(f'Cohen\'s d: {d}, var.d: {d_var}')
    print(f'Hedge\'s g: {g}, var.g: {g_var}')
    print(f'Correlation coeffecient: {r}, var.r: {r_var}')

# In[38]:

mes(2.2, 3.2, 1.3038405, 1.3038405, 5, 5)

# In[39]:

mes(2.2, 5, 1.3038405, 1.5811388, 5, 5)

# In[40]:

mes(3.2, 5, 1.3038405, 1.5811388, 5, 5)

# #### # The standard deviation of the effect size is of critical importance, since it indicates how much uncertainty is included in the measurement. A standard deviation that is too large will make the measurement nearly meaningless.

# # Effect sizes for the orthogonal contrasts

# In[41]:

def r_contrast(t,df):
    return np.sqrt(t**2/(t**2+df))

# In[42]:

dof = 15-2-1   # N-p-1 for normal regression , p is the number of predictors (in this case 2, the two con-trast variables
print(r_contrast(2.474, dof))

# In[43]:

dof = 15-2-1   # N-p-1 for normal regression , p is the number of predictors (in this case 2, the two con-trast variables
print(r_contrast(2.029, dof))

# #### # Both effects are fairly large.

# In[ ]:

import numpy,import scipy,import statsmodels,from statsmodels


# exec(open(".//09_Index_Arrays.py"))

import numpy as np
print "Executing: 09_Index_Arrays.py"

'''
NumPy Index Arrays
'''

# Change False to True for each block of code to see what it does

# Using index arrays
if False:
    a = np.array([1, 2, 3, 4])
    b = np.array([True, True, False, False])

    print a[b]
    print a[np.array([True, False, True, False])]

# Creating the index array using vectorized operations
if False:
    a = np.array([1, 2, 3, 2, 1])
    b = (a >= 2)

    print a[b]
    print a[a >= 2]

# Creating the index array using vectorized operations on another array
if False:
    a = np.array([1, 2, 3, 4, 5])
    b = np.array([1, 2, 3, 2, 1])

    print b == 2
    print a[b == 2]

# Time spent in the classroom in the first week for 20 students
time_spent = np.array([
       12.89697233,    0.        ,   64.55043217,    0.        ,
       24.2315615 ,   39.991625  ,    0.        ,    0.        ,
      147.20683783,    0.        ,    0.        ,    0.        ,
       45.18261617,  157.60454283,  133.2434615 ,   52.85000767,
        0.        ,   54.9204785 ,   26.78142417,    0.
])

# Days to cancel for 20 students
days_to_cancel = np.array([
      4,   5,  37,   3,  12,   4,  35,  38,   5,  37,   3,   3,  68,
     38,  98,   2, 249,   2, 127,  35
])

# This is my version which is also correct

# def mean_time_for_paid_students(time_spent, days_to_cancel):
#    '''
#    Fill in this function to calculate the mean time spent in the classroom
#    for students who stayed enrolled at least (greater than or equal to) 7 days.
#    Unlike in Lesson 1, you can assume that days_to_cancel will contain only
#    integers (there are no students who have not canceled yet).

#    The arguments are NumPy arrays. time_spent contains the amount of time spent
#    in the classroom for each student, and days_to_cancel contains the number
#    of days until each student cancel. The data is given in the same order
#    in both arrays.
#    '''
#    return time_spent[days_to_cancel >= 7].mean()

# This is udacity's version

def mean_time_for_paid_students(time_spent, days_to_cancel):
    # create the index array
    is_paid = days_to_cancel >= 7
    paid_time = time_spent[is_paid]
    return paid_time.mean()


# SET OF FUNCTIONS FOR SIGNAL PROCESSING

import numpy as np
import scipy as sp
from matplotlib import pyplot as plt

def FFT_periodic(t, x_t, one_sided = True, rms_scaling=False):
    dt = t[1]-t[0]
    T = t[-1] + dt
    fs = 1/dt
    N = x_t.shape[0]
    df = fs/N
    freq = np.arange(N)*df
    if one_sided: # One-sided spectrum
        Xf = 2*np.fft.fft(x_t)
        Xf[0] = Xf[0]/2
        if np.remainder(N,2)==0:
            Nsel = int(N/2)
        else:
            Nsel = int((N+1)/2)
        freq = freq[0:Nsel]
        Xf = Xf[0:Nsel]
    else: # Two-sided spectrum
        Xf = np.fft.fft(x_t)
    if rms_scaling:
        Xf[1:] = Xf[1:]/np.sqrt(2)
    return freq, Xf/(fs*T)

def FFT_transient(t, x_t, one_sided = True, rms_scaling=False):
    dt = t[1]-t[0]
    # T = t[-1] + dt
    fs = 1/dt
    N = x_t.shape[0]
    df = fs/N
    freq = np.arange(N)*df
    if one_sided: # One-sided spectrum
        Xf = 2*np.fft.fft(x_t)
        Xf[0] = Xf[0]/2
        if np.remainder(N,2)==0:
            Nsel = int(N/2)
        else:
            Nsel = int((N+1)/2)
        freq = freq[0:Nsel]
        Xf = Xf[0:Nsel]
    else: # Two-sided spectrum
        Xf = np.fft.fft(x_t)
    if rms_scaling:
        Xf[1:] = Xf[1:]/np.sqrt(2)
    return freq, Xf/fs

if __name__ == "__main__":

    # SIGNAL AQUISITION PARAMETERS
    Npoints = 2048
    fs = 2048
    df = fs/Npoints
    T = 1/df

    # BUILDING THE SIGNAL
    t = np.arange(0, T, T/Npoints)
    f1, f2, f3 = 10, 20, 40
    A1, A2, A3 = 1, 0.5, 0.25
    x_t = A1*np.sin(2*np.pi*t*f1) + A2*np.sin(2*np.pi*t*f2) + A3*np.sin(2*np.pi*t*f3)

    # PERFORM THE FAST FOURIER TRANSFORM OF SIGNAL
    one_sided = True
    freq, X_f = FFT_periodic(t, x_t, one_sided=one_sided)

    # CHECKING THE RMS OF SIGNAL IN BOTH DOMAINS - SEE THE PARSEVAL'S THEOREM
    x_t_RMS = np.sqrt(np.sum(x_t**2)/Npoints)
    if one_sided:
        _, Xf = FFT_periodic(t,x_t, one_sided=False)
    else:
        Xf = X_f.copy()
    Xf_RMS = np.sqrt(np.sum(np.abs(Xf)**2))
    print("The rms of signal in time domain is {}".format(x_t_RMS))
    print("The rms of signal in frequency domais is {}".format(Xf_RMS))

    # PLOT OF SIGNAL IN THE TIME DOMAIN
    fig = plt.figure(figsize=[12,7])
    ax = fig.add_subplot(1,1,1)
    first_plot = ax.plot(t, x_t, color=[1,0,0], linewidth=2, label="Flow out")
    ax.set_xlabel("Time [s]", fontsize = 14, fontweight = 'bold')
    ax.set_ylabel("Signal Amplitude [--]", fontsize = 14, fontweight = 'bold')
    plt.grid()
    plt.show()

    # PLOT OF SIGNAL IN THE FREQUENCY DOMAIN
    fig = plt.figure(figsize=[12,7])
    ax = fig.add_subplot(1,1,1)
    second_plot = ax.plot(freq, np.abs(X_f), color=[1,0,0], linewidth=2, label="Flow out")
    ax.set_xlabel("Frequency [Hz]", fontsize = 14, fontweight = 'bold')
    ax.set_ylabel("Signal Magnitude [--]", fontsize = 14, fontweight = 'bold')
    plt.grid()
    plt.show()