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

data = [[6.1101, 12.5920], [5.5277, 9.1302], [8.5186, 17.6620], [6.5186, 11.6620], [7.5186, 15.6620]]
df = pd.DataFrame(data, columns=['X', 'Y'])
m = len(df)

def hypothesis(theta_h, x):
    return theta_h[0] + theta_h[1] * x

def cost_calc(theta_x, x, y):
    return (1 / 2 * m) * np.sum((hypothesis(theta_x, x) - y) ** 2)

def gradient_descent(theta_g, x, y, epoch, alpha):
    cost_g = []
    i = 0
    while i < epoch:
        hx = hypothesis(theta_g, x)
        theta_g[0] -= alpha * (sum(hx - y) / m)
        theta_g[1] -= (alpha * np.sum((hx - y) * x)) / m
        cost_g.append(cost_calc(theta_g, x, y))
        i += 1
    return theta_g, cost_g

def predict(theta_p, x, y, epoch, alpha):
    theta_p, cost_p = gradient_descent(theta_p, x, y, epoch, alpha)
    return hypothesis(theta_p, x), cost_p, theta_p

theta = [0, 0]
y_hat, cost, theta = predict(theta, df['X'], df['Y'], 2000, 0.01)

plt.figure()
plt.scatter(df['X'], df['Y'], label='Y')
plt.scatter(df['X'], y_hat, label='Y\u0302')
plt.legend(loc="upper left")
plt.xlabel("Independent variable X")
plt.ylabel("Outcome variable Y")
plt.show()

plt.figure()
plt.scatter(range(0, len(cost)), cost)
plt.show()


import numpy as np
from scipy.sparse import diags

def d2_mat_dirichlet(nx, dx):
    """
    Constructs the centered second-order accurate second-order derivative for
    Dirichlet boundary conditions.

    Parameters
    ----------
    nx : integer
        number of grid points
    dx : float
        grid spacing

    Returns
    -------
    d2mat : numpy.ndarray
        matrix to compute the centered second-order accurate first-order deri-
        vative with Dirichlet boundary conditions on both side of the interval
    """
    # We construct a sequence of main diagonal elements,
    diagonals = [[1.], [-2.], [1.]]
    # and a sequence of positions of the diagonal entries relative to the main
    # diagonal.
    offsets = [-1, 0, 1]

    # Call to the diags routine; note that diags return a representation of the
    # array; to explicitly obtain its ndarray realisation, the call to .toarray()
    # is needed. Note how the matrix has dimensions (nx-2)*(nx-2).
    d2mat = diags(diagonals, offsets, shape=(nx-2,nx-2)).toarray()

    # Return the final array divided by the grid spacing **2.
    return d2mat / dx**2


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

# First we load the entire CSV file into an m x n matrix
D = np.matrix(pd.read_csv("linreg-scaling-synthetic.csv", header=None).values)

# Make a convenient variable to remember the number of input columns
n = 2

# We extract all rows and the first n columns into X_data
# Then we flip it
X_data = D[:, 0:n].transpose()

# We extract all rows and the last column into y_data
# Then we flip it
y_data = D[:, n].transpose()

# We compute the mean and standard deviation of each feature
means = X_data.mean(axis=1)
deviations = X_data.std(axis=1)

print(means)
print(deviations)

# Define data placeholders
x = tf.placeholder(tf.float32, shape=(n, None))
y = tf.placeholder(tf.float32, shape=(1, None))

# Apply the rescaling
x_scaled = (x - means) / deviations

# Define trainable variables
A = tf.get_variable("A", shape=(1, n))
b = tf.get_variable("b", shape=())

# Define model output, using the scaled features
y_predicted = tf.matmul(A, x_scaled) + b

# Define the loss function
L = tf.reduce_sum((y_predicted - y)**2)

# Define optimizer object
optimizer = tf.train.AdamOptimizer(learning_rate=0.1).minimize(L)

# Create a session and initialize variables
session = tf.Session()
session.run(tf.global_variables_initializer())

# Main optimization loop
for t in range(2000):
    _, current_loss, current_A, current_b = session.run([optimizer, L, A, b], feed_dict={
        x: X_data,
        y: y_data
    })
    print("t = %g, loss = %g, A = %s, b = %g" % (t, current_loss, str(current_A), current_b))


import os
import numpy as np
import sys
import math

def nCr(r, n):
    return math.factorial(n) // (math.factorial(r) * math.factorial(n - r))

if __name__ == '__main__':
    dataset_dir = sys.argv[1]
    classes = os.listdir(dataset_dir)
    num_classes = len(classes)
    num_files = np.zeros(num_classes, dtype=np.int32)

    for i in range(num_classes):
        files = os.listdir(os.path.join(dataset_dir, classes[i]))
        num_files[i] = len(files)

    num_files_indices = np.flip(np.argsort(num_files))

    offset = int(sys.argv[2])
    num_selected_classes = int(sys.argv[3])
    selected_num_files = num_files[num_files_indices][offset:offset + num_selected_classes].tolist()
    print('selected_num_files:', selected_num_files)
    # print(num_files_indices[offset:offset + 20])
    unique_selected_num_files = np.unique(np.array(selected_num_files)).tolist()
    print("unique_selected_num_files:", unique_selected_num_files)
    count_dict = {}
    for unique_num_files in unique_selected_num_files:
        count_dict[unique_num_files] = selected_num_files.count(unique_num_files)
    print(count_dict)
    num_same_pairs = 0
    for key in count_dict.keys():
        num_same_pairs += count_dict[key] * nCr(2, key)
    print('num_same_pairs:', num_same_pairs)
    selected_class_indices = num_files_indices[offset:offset + num_selected_classes]
    print('selected_class_indices:', selected_class_indices)


import numpy as np

def softmax(logit, y=None):
    N = logit.shape[0]
    norm = logit - np.max(logit, axis=1, keepdims=True)
    Z = np.sum(np.exp(norm), axis=1, keepdims=True)
    log_probs = norm - np.log(Z)
    probs = np.exp(log_probs)
    if y is None:
        return probs

    if y.ndim == 1:  # Raw categories
        loss = -np.mean(log_probs[np.arange(N), y])
        probs[np.arange(N), y] -= 1
    else:  # One-hot encoded categories
        loss = -np.mean(log_probs[y.astype('bool')])
        probs[y.astype('bool')] -= 1
    return loss, (probs/N, )

def mean_squared_error(logit, y=None):
    # J = (y-yHat)**2
    N = logit.shape[0]
    if y is None:
        return logit
    dx = logit - y
    squared_error = dx**2
    loss = squared_error.mean()
    return loss, (2*dx/N, )


import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from sklearn import preprocessing
from sklearn.decomposition import PCA

# Load the excel file
df = pd.read_excel("pca_uk.xlsx")

# Convert the features into a list of lists
X = np.array([df[i].values for i in df.columns if i != "Features"])

print(X)

# fit the current features into two dimensions
pca = PCA(n_components=2)
X_r = pca.fit_transform(X)

# The amount of variance (information preserverd) within the system
print("Print ing the explained variance (same as the std?)")
print(pca.explained_variance_)

# THe amount of information preserved within the system
print(
    "printing the explained variance ratio (information preserved through each component)"
)
print(pca.explained_variance_ratio_)

# The cumulative sum of the ratios for each ratio
print("Printing the cumsum of the explained variance ratio")
print(pca.explained_variance_ratio_.cumsum())

# X_r is X reduced down to two dimensions
print(X_r)

# Create our scatter plot
sns.scatterplot(X_r[:, 0], X_r[:, 1])

# The columns that we want
wanted_cols = [col for col in df.columns if col != "Features"]

# Add a label to each point
for point in zip(X_r[:, 0], X_r[:, 1], wanted_cols):
    """
        Add text to every single point
    """
    plt.text(point[0] + 5, point[1] + 20, point[2])

print("Plotting our chart")
plt.show()

A = np.array([[2, 0], [1, 5]])
v = np.array([3, 4])
print(np.dot(A, v))

eig_value, eig_vector = np.linalg.eig(A)
print(eig_value)
print(eig_vector)

def PCA_calculation(data, n_components):
    M = np.mean(data, axis=0)
    C = data - M
    covariance_matrix = np.cov(C.T)
    print(covariance_matrix)
    eig_value, eig_vector = np.linalg.eig(covariance_matrix)

    index = np.argsort(eig_value)[::-1]
    eig_vector


"""
Please see
https://computationalmindset.com/en/neural-networks/ordinary-differential-equation-solvers.html#sys1
for details
"""

import numpy as np
import matplotlib.pyplot as plt

import tensorflow as tf
from tfdiffeq import odeint

def ode_sys(t, X):
	x=X[0]
	dx_dt=X[1]
	d2x_dt2=-dx_dt - 2*x
	return tf.stack([dx_dt, d2x_dt2])

an_sol_x = lambda t : \
	np.exp(-t/2.) * (np.cos(np.sqrt(7) * t / 2.) + \
	np.sin(np.sqrt(7) * t / 2.)/np.sqrt(7.))

t_begin=0.
t_end=12.
t_nsamples=100
t_space = np.linspace(t_begin, t_end, t_nsamples)
x_init = tf.constant([1.])
dxdt_init = tf.constant([0.])

x_an_sol = an_sol_x(t_space)

num_sol = odeint(
	ode_sys,
	tf.convert_to_tensor([x_init, dxdt_init], dtype=tf.float64),
	tf.constant(t_space)).numpy()

plt.figure()
plt.plot(t_space, x_an_sol,'--', linewidth=2, label='analytical')
plt.plot(t_space, num_sol[:,0], linewidth=1, label='numerical')
plt.title('ODE 2nd order IVP solved by TensorFlowDiffEq')
plt.xlabel('t')
plt.ylabel('x')
plt.legend()
plt.show()


import numpy as np

def leastSquares(data, label):
    # Sum of squared error shoud be minimized
    #that trains a least-squares classifier based on a data matrix data and its class label vector label . It provides as output the linear classifier weight vector weight and bias.
    # page 29 lecture 6
    #
    # INPUT:
    # data        : Training inputs  (num_samples x dim)
    # label       : Training targets (num_samples x 1)
    #
    # OUTPUT:
    # weights     : weights   (dim x 1)
    # bias        : bias term (scalar)

    #####Insert your code here for subtask 1a#####
    # Extend each datapoint x as [1, x]
    # (Trick to avoid modeling the bias term explicitly)

    # D = Number of attributes
    D = data.shape[0]

    ones = np.full(D,1)
    data = np.column_stack((ones,data))

    # Calculating the weights
    Weight = np.linalg.pinv(data)@label

    weight = Weight[1:]
    bias = Weight[0]

    return weight, bias


import numpy as np

def normalize_features(X, mean_vector=None, std_vector=None):
    """
        :param X            : two-dimensional array of our dataset, with shape NxD. N is the number of rows (each row is a sample) and D is the number of columns
        :param mean_vector  : optionally given, precomputed vector of mean values.
        :param std_vector   : optionally given, precomputed vector of standard deviation values.

        If the function is called passing *only X* as input argument, then the mean and std values will be computed from X, and then X will be normalized using these values.
        If the function is called passing *all three* input arguments, X array will be normalized using the precomputed mean and std values that were passed as input arguments.

        Normalization is performed by subtracting the mean and dividing by the standard deviation: x_normalized = (x - x_mean)/(x_std+ε)
    """

    if (mean_vector is None) and (std_vector is None):
        return_mean_std = True
        # get the mean for every column
        mean_vector = np.mean(X, axis=0)
        # get the std for every column
        std_vector = np.std(X, axis=0)

        # insert extra singleton dimension, to obtain 1xD shape
        mean_vector = np.expand_dims(mean_vector, axis=0)
        std_vector = np.expand_dims(std_vector, axis=0)
    else:
        return_mean_std = False

    # get the number of samples in the dataset
    N = X.shape[0]

    # repeat N times across first dimension, to obtain NxD shape
    repeated_mean = np.repeat(mean_vector, N, axis=0)
    repeated_std = np.repeat(std_vector, N, axis=0)

    #if np.sum(1.0*(repeated_std==0))>0:
    if np.any(repeated_std==0):
        print('Adding epsilon to avoid division by zero during normalization')
        repeated_std += np.finfo(float).eps
    # subtract column mean and divide each element by column standard deviation
    X_normalized = (X - repeated_mean) / repeated_std
    print('Dataset normalization complete.')

    if return_mean_std==True:
        return X_normalized, mean_vector, std_vector
    else:
        return X_normalized


# coding: utf-8

# # Test pyIAST for match with competitive Langmuir model

# In the case that the pure-component isotherms $N_{i,pure}(P)$ follow the Langmuir model with the same saturation loading $M$:
#
# $N_{i,pure} = M \frac{K_iP}{1+K_iP},$
#
# The mixed gas adsorption isotherm follows the competitive Langmuir isotherm:
#
# $N_i = M \frac{K_i p_i}{1 + \sum_j K_jp_j},$
#
# where $p_i$ is the partial pressure of component $i$. Here, we generate synthetic pure-component adsorption isotherm data and confirm that pyIAST agrees with the competitive Langmuir isotherm for 3 components.

# In[1]:

from __future__ import absolute_import
import numpy as np
import pyiast
import pandas as pd
import matplotlib.pyplot as plt
from six.moves import range
plt.style.use('fivethirtyeight')

colors = ['b', 'g', 'r']  # for representing each component
component_names = {0: 'A', 1: 'B', 2: 'C'}

# ## Generate synthetic pure-component isotherm data, fit Langmuir models to them.

# Model parameters ($M$, $\{K_i\}$)

# In[2]:

M = 1.0
langmuirKs = [2.0, 10.0, 20.0]  # K_i

# Generate data according to Langmuir model, store in list of Pandas DataFrames

# In[3]:

pressure = np.logspace(-3, np.log10(10), 20)
dfs = [
    pd.DataFrame({
        'P':
        pressure,
        'L':
        M * langmuirKs[i] * pressure / (1.0 + langmuirKs[i] * pressure)
    }) for i in range(3)
]

# Use pyIAST to fit Lanmguir models to the data, then plot fits

# In[4]:

isotherms = [
    pyiast.ModelIsotherm(
        dfs[i], pressure_key='P', loading_key='L', model='Langmuir')
    for i in range(3)
]

for i in range(len(isotherms)):
    isotherms[i].print_params()
    pyiast.plot_isotherm(isotherms[i])

# Plot synthetic data all in one plot for paper

# In[5]:

p_plot = np.logspace(-3, np.log10(11))  # for plotting

fig = plt.figure(facecolor='w')
for i in range(len(isotherms)):
    plt.scatter(dfs[i]['P'], dfs[i]['L'], color=colors[i], s=50, label=None)
    plt.plot(
        p_plot,
        M * langmuirKs[i] * p_plot / (1.0 + langmuirKs[i] * p_plot),
        color=colors[i],
        linewidth=2,
        label=r'$N_%s(P) = \frac{%d P}{1+%dP}$' %
        (component_names[i], langmuirKs[i], langmuirKs[i]))

plt.xlim([-.05 * 10, 1.05 * 10])
plt.ylim([-.05 * M, M * 1.05])

plt.xlabel('Pressure (bar)')
plt.ylabel('Gas uptake (mmol/g)')

plt.legend(loc='lower right')

plt.tight_layout()
plt.savefig(
    'pure_component_Langmuir.png',
    format='png',
    dpi=300,
    facecolor=fig.get_facecolor())
plt.show()

# ## Compare pyIAST predicted component loadings to that of competitive Langmuir

# Let us consider a tertiary mixture of components 0, 1, and 2 above at a total pressure of `total_pressure` bar.

# In[6]:

total_pressure = 1.0

# We will explore gas phase composition space ($\{y_i\}$) by generating random compositions and checking that they are within the triangle. We do not want to get too close to a pure phase boundary becuase of numerical instability, so we keep a distance `dx` away from pure phases. We will perform `num_tests` tests.

# In[15]:

dx = 0.0001
num_tests = 100

# Generate the compositions and store in list `compositions`

# In[16]:

compositions = []
test_no = 0
while test_no < num_tests:
    # generate random compoisitions
    y1 = np.random.uniform(dx, 1.0 - dx)
    y2 = np.random.uniform(dx, 1.0 - dx)
    y3 = 1.0 - y2 - y1
    # check that composition is within the triangle
    if y3 < dx:
        continue
    # viable composition
    compositions.append([y1, y2, y3])
    # keep generating until we have num_tests
    test_no += 1

# Next, we assert that pyIAST gives the same result as the competitive Langmuir isotherm for each of these compositions.

# Function to compute loading according to competitive Langmuir

# In[17]:

def competitive_langmuir_loading(partial_pressures, i):
    """
    Calculate loading of component i according to competitive Langmuir
    """
    return M * langmuirKs[i] * partial_pressures[i] / (
        1.0 + np.dot(langmuirKs, partial_pressures))

# Function to compute loading according to pyIAST

# In[10]:

def iast_loading(partial_pressures, i):
    """
    Calculate loading of component i according to IAST

    partial_pressures: Array, partial pressures of each component
    i: component in the mixture
    """
    component_loadings = pyiast.iast(partial_pressures, isotherms)
    return component_loadings[i]

# Loop over compositions, assert pyIAST agrees with competitive Langmuir for each component. If this runs, then there is agreement!

# In[14]:

for i in range(num_tests):
    partial_pressure = np.array(compositions[i]) * total_pressure
    # for each component...
    for c in range(len(langmuirKs)):
        np.testing.assert_almost_equal(
            competitive_langmuir_loading(partial_pressure, c),
            iast_loading(partial_pressure, c),
            decimal=4)

# ### This is using a custom library to plot the phase diagrams for the paper.

# Use ternary to plot phase diagram
# https://github.com/marcharper/python-ternary

# In[19]:

import ternary
scale = 10  # resolution in triangle
axis_colors = {'l': colors[1], 'r': colors[0], 'b': colors[2]}
cmaps = ["Blues", "Greens", "Reds"]
iast_or_lang = 'lang'

for c in range(3):
    if iast_or_lang == 'lang':
        f = lambda p: competitive_langmuir_loading(p, c)
    else:
        f = lambda p: iast_loading(p, c)

    # loop over component
    fig, ax = plt.subplots(facecolor='w')
    ax.axis("off")
    figure, tax = ternary.figure(ax=ax, scale=scale)

    tax.heatmapf(
        f,
        boundary=False,
        style="hexagonal",
        cmap=plt.cm.get_cmap(cmaps[c]),
        vmax=M,
        vmin=0.0,
        cbarlabel="%s uptake (mmol/g)" % component_names[c])

    tax.boundary(linewidth=2.0, color_dict=axis_colors)

    tax.left_axis_label("$p_1$ (bar)", color=axis_colors['l'], offset=0.16)
    tax.right_axis_label("$p_0$ (bar)", color=axis_colors['r'], offset=0.16)
    tax.bottom_axis_label("$p_2$ (bar)", color=axis_colors['b'], offset=-0.06)

    tax.gridlines(
        color="blue",
        multiple=1,
        linewidth=2,
        horizontal_kwargs={'color': axis_colors['b']},
        left_kwargs={'color': axis_colors['l']},
        right_kwargs={'color': axis_colors['r']},
        alpha=0.7)  # Every 5th gridline, can be a float

    tax.ticks(
        axis='rlb',
        linewidth=1,
        locations=np.arange(scale + 1),
        clockwise=True,
        color_dict=axis_colors,
        ticks=["%.1f" % (1.0 - 1.0 * i / scale) for i in range(scale + 1)],
        offset=0.03)
    tax.clear_matplotlib_ticks()

    tax._redraw_labels()
    # if iast_or_lang == 'iast':
    #     tax.set_title("IAST uptake, component %d" % c, y=1.08, fontsize=14)
    # if iast_or_lang == 'lang':
    #     tax.set_title("Competitive Langmuir uptake, component %d" % c, y=1.08, fontsize=14)
    plt.tight_layout()
    if iast_or_lang == 'iast':
        plt.savefig(
            "Tertiary_diagram_IAST_component_%d.png" % c,
            format='png',
            dpi=300,
            facecolor=fig.get_facecolor())
    if iast_or_lang == 'lang':
        plt.savefig(
            "Tertiary_diagram_Langmuir_component_%d.png" % c,
            format='png',
            dpi=300,
            facecolor=fig.get_facecolor())
    tax.show()

# In[ ]:

manual_tests/python_scripts/Test IAST for Langmuir case.py


# -*- coding: utf-8 -*-
"""Feature_Selection_Categorical_Chi_Squared.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1LrcTaAKZx0I_8yeLjG2E-cNs_4aTOzI4

# How to choose categorical columns in dataset

## Load Data
"""

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
sns.set()
import warnings
warnings.simplefilter(action='ignore')

churn_master_df = pd.read_csv('https://raw.githubusercontent.com/srivatsan88/YouTubeLI/master/dataset/churn_data_st.csv')

churn_df = churn_master_df.copy()

churn_df.head()

"""customerID - Customer ID

gender - Whether the customer is a male or a female

SeniorCitizen - Whether the customer is a senior citizen or not (1, 0)

Partner - Whether the customer has a partner or not (Yes, No)

Dependents - Whether the customer has dependents or not (Yes, No)

tenure - Number of months the customer has stayed with the company

PhoneService - Whether the customer has a phone service or not (Yes, No)

MultipleLines - Whether the customer has multiple lines or not (Yes, No, No phone service)

InternetService - Customer’s internet service provider (DSL, Fiber optic, No)

OnlineSecurity - Whether the customer has online security or not (Yes, No, No internet service)

OnlineBackup - Whether the customer has online backup or not (Yes, No, No internet service)

DeviceProtection - Whether the customer has device protection or not (Yes, No, No internet service)

TechSupport - Whether the customer has tech support or not (Yes, No, No internet service)

StreamingTV - Whether the customer has streaming TV or not (Yes, No, No internet service)

StreamingMovies - Whether the customer has streaming movies or not (Yes, No, No internet service)

Contract - The contract term of the customer (Month-to-month, One year, Two year)

PaperlessBilling - Whether the customer has paperless billing or not (Yes, No)

PaymentMethod - The customer’s payment method (Electronic check, Mailed check, Bank transfer (automatic), Credit card (automatic))

MonthlyCharges - The amount charged to the customer monthly

TotalCharges - The total amount charged to the customer

Churn - Whether the customer churned or not (Yes or No)

## Feature Selection
"""

churn_df.info()

print("Number of Rows: %s "%(churn_df.shape[0]))
print("Number of Columns: %s "%(churn_df.shape[1]))
print("\n Features: ", churn_df.columns.to_list())
print("\n Missing Values: %s"%(churn_df.isnull().any()))
print("\n Unique values: %s"%(churn_df.nunique()))

"""## Null Hypothesis: -
There is no relationship between the two categorical values.

## Alternate Hypothesis: -
There is a relationship between the two categorical values.

Use p value = 0.05 to make the decision

Questions to answer: -

- Is there any relationship between users who have churned to genders of the user ?

- Is there any relationship between users who opted for paperless billing to users who have churned ?

# Chi-Squared Test: -

There are multiple relationship tests that are available. But here we use the chi-squared test.

This test can be applied to any 2 categorical columns. It need not be supervised/unsupervised.

Chi-squared test shows the significance of the relationship between two categorical data.
"""

churn_df['gender'].value_counts()

fig, ax = plt.subplots()
ax = sns.countplot(x="gender", hue="Churn", data=churn_df)

for p in ax.patches:
    height = p.get_height()
    ax.text(p.get_x() + p.get_width()/2., height+3, '{:1.2f}'.format(height/churn_df.shape[0]), ha="center")

"""## Is there any relationship between users who have churned to genders of the user ?

Statistics alone do not prove anything. All tests are relative likelihood

- To do a chi-squared test. We need to create a contigency table.

- Also called cross-tab.
"""

gender_churn_ct = pd.crosstab(index=churn_df["gender"], columns=churn_df["Churn"])

gender_churn_ct

print(gender_churn_ct.iloc[0].values)
print(gender_churn_ct.iloc[1].values)

from scipy import stats

"""scipy.stats.chi2_contingency(observed, correction=True, lambda_=None)

Chi-square test of independence of variables in a contingency table.

This function computes the chi-square statistic and p-value for the hypothesis test of independence of the observed frequencies in the contingency table [1] observed.

The expected frequencies are computed based on the marginal sums under the assumption of independence; see scipy.stats.contingency.expected_freq.

The number of degrees of freedom is (expressed using numpy functions and attributes):
"""

(chi2, p, dof, _) = stats.chi2_contingency([gender_churn_ct.iloc[0].values, gender_churn_ct.iloc[1].values])

print("chi2: ",chi2)
print("p-value: ",p)
print("Degree of freedom",dof)

"""chi ^ 2 statistics =

X ^ 2 = sum((observed - Expected) ^ 2 / Expected)

Higher the chi2 value more is the correlation between the two variables.

dof is number of categories - 1

p-value > 0.05
hence we do not accept the alternate hypothesis and choose the null hypothesis.

That means we can drop the gender column it is not very useful.
"""

pd.crosstab(index=churn_df['gender'], columns=churn_df['Churn'], margins=True)

"""## Is there any relationship between users who opted for paperless billing to users who have churned ?"""

churn_df['PaperlessBilling'].value_counts()

fig, ax = plt.subplots()
ax = sns.countplot(x="PaperlessBilling", hue="Churn", data=churn_df)

for p in ax.patches:
    height = p.get_height()
    ax.text(p.get_x() + p.get_width()/2., height+3, '{:1.2f}'.format(height/churn_df.shape[0]), ha="center")

pb_churn_ct = pd.crosstab(index=churn_df['PaperlessBilling'], columns=churn_df['Churn'])

pb_churn_ct

print(pb_churn_ct.iloc[0])
print(pb_churn_ct.iloc[1])

(chi2, p, dof, _) = stats.chi2_contingency([pb_churn_ct.iloc[0].values, pb_churn_ct.iloc[1].values])

print("chi2: ",chi2)
print("p-value: ",p)
print("Degree of freedom",dof)

"""Practically it may not make much sense.

But here p-value < 0.05 hence we reject the null hypothesis in favour of alternate hypothesis.

That is there is a significant relationship in this data.
"""


#Exploring fancy indexing
import numpy as np
rand = np.random.RandomState(42)

x = rand.randint(100, size=10)
print(x)

ind = [3,7,4]
print(x[ind])

#shape of the result reflects the indexing array
ind = np.array([[3,7],[4,5]])
print("x:", x)
print(x[ind])

#this works in multiple dimesnions
X = np.arange(12).reshape((3,4))
print("X:",X)

row = np.array([0,1,2])
col = np.array([2,1,3])
print("X[row,col]:", X[row,col])
#prints (0,2), (1,1), (2,3)

print(X[row[:, np.newaxis], col])
print(row[:, np.newaxis] * col)

#combined indexing

print("X:",X)
#combining fancy and simple indicies
print(X[2, [2,0,1]])
print(X[1:, [2,0,1]])
print("we can combine fancy indices with masking as well")
mask = np.array([1,0,1,0], dtype=bool)
print(X[row[:, np.newaxis], mask])

print("Example: selecting random points")
mean = [0,0]
cov = [[1,2],[2,5]]
X= rand.multivariate_normal(mean, cov, 100)
print(X.shape) #(100,2)

import matplotlib
matplotlib.use("TKagg")
import matplotlib.pyplot as plt
import seaborn; seaborn.set() # for plot styling

plt.scatter(X[:, 0],X[:,1])
plt.show()

#we can use fancy indixing to select 20 random points

indicies = np.random.choice(X.shape[0], 20, replace=False)
print(indicies)
selection = X[indicies] #fancy indexing
print(selection.shape) #(20,2)

plt.scatter(selection[:,0], selection[:,1], facecolor='red', s=200)
plt.scatter(X[:,0],X[:,1], alpha=0.3)
plt.show()

print("#Modifying values with fancy indexing")

x = np.arange(10)
i = np.array([2,1,8,4])
x[i] = 99
print(x)
x[i] -=10
print(x)

x = np.zeros(10)
x[[0,0]] = [4,6]
print(x)
i = [2,3,3,4,4,4]
x[i] +=1
print(x)

x = np.zeros(10)
np.add.at(x, i, 10)
print(x)

print("Example: Binnng Data")
np.random.seed(42)
x=np.random.randn(100)

#compute a histogram by hand
bins = np.linspace(-5,5,20)
counts = np.zeros_like(bins)

#find the appropriate bin for each x
i = np.searchsorted(bins,x)

#add 1 to each of these bins
np.add.at(counts, i, 1)

#plot the results
plt.plot(bins, counts, linestyle='steps')
plt.show()

#or you can use plt.hist()

plt.hist(x, bins, histtype='step')
plt.show()


import numpy as np
from pyntcloud.geometry.coord_systems import (
    spherical_to_cartesian,
    cartesian_to_spherical,
    cylindrical_to_cartesian,
    cartesian_to_cylindrical,
    cylindrical_to_spherical,
    spherical_to_cylindrical
)

def test_spherical_to_cartesian():
    expected = np.array([[0.5, 0.5, 0.7071], [0.5, -0.5, -0.7071]])

    data = np.array([[1, 45, 45], [1, -45, 135]], dtype=np.float32)
    result = spherical_to_cartesian(data[:, 0], data[:, 1], data[:, 2])

    assert np.all(np.isclose(result, expected))

    data[:, 1:] = np.deg2rad(data[:, 1:])

    result = spherical_to_cartesian(
        data[:, 0], data[:, 1], data[:, 2], degrees=False)

    assert np.all(np.isclose(result, expected))

def test_cartesian_to_spherical():
    expected = np.array([[1, 45, 45], [1, -45, 135]], dtype=np.float32)

    data = np.array([[0.5, 0.5, 0.7071], [0.5, -0.5, -0.7071]])

    result = np.zeros_like(expected)
    radial, azimuthal, polar = cartesian_to_spherical(data)
    result[:, 0] = radial
    result[:, 1] = azimuthal
    result[:, 2] = polar

    assert np.all(np.isclose(result, expected))

    expected[:, 1:] = np.deg2rad(expected[:, 1:])

    radial, azimuthal, polar = cartesian_to_spherical(data, degrees=False)
    result[:, 0] = radial
    result[:, 1] = azimuthal
    result[:, 2] = polar

    assert np.all(np.isclose(result, expected))

def test_cylindrical_to_cartesian():
    expected = np.array([[0.5, 0.5, 0.7071], [0.5, -0.5, -0.7071]])

    data = np.array(
        [[0.7071, 45, 0.7071], [0.7071, -45, -0.7071]], dtype=np.float32)
    result = cylindrical_to_cartesian(data[:, 0], data[:, 1], data[:, 2])

    assert np.all(np.isclose(result, expected))

    data[:, 1] = np.deg2rad(data[:, 1])

    result = cylindrical_to_cartesian(
        data[:, 0], data[:, 1], data[:, 2], degrees=False)

    assert np.all(np.isclose(result, expected))

def test_cartesian_to_cylindrical():
    expected = np.array(
        [[0.7071, 45, 0.7071], [0.7071, -45, -0.7071]], dtype=np.float32)

    data = np.array([[0.5, 0.5, 0.7071], [0.5, -0.5, -0.7071]])

    result = np.zeros_like(expected)
    radial, angular, height = cartesian_to_cylindrical(data)
    result[:, 0] = radial
    result[:, 1] = angular
    result[:, 2] = height

    assert np.all(np.isclose(result, expected))

    expected[:, 1] = np.deg2rad(expected[:, 1])

    radial, angular, height = cartesian_to_cylindrical(data, degrees=False)
    result[:, 0] = radial
    result[:, 1] = angular
    result[:, 2] = height

    assert np.all(np.isclose(result, expected))

def test_cylindrical_to_spherical():
    expected = np.array([[1, 45, 45], [1, -45, 135]], dtype=np.float32)

    data = np.array(
        [[0.7071, 45, 0.7071], [0.7071, -45, -0.7071]], dtype=np.float32)

    result = np.zeros_like(expected)
    r, theta, phi = cylindrical_to_spherical(
        data[:, 0], data[:, 1], data[:, 2])
    result[:, 0] = r
    result[:, 1] = theta
    result[:, 2] = phi

    assert np.all(np.isclose(result, expected))

    expected[:, 1:] = np.deg2rad(expected[:, 1:])

    data[:, 1] = np.deg2rad(data[:, 1])

    r, theta, phi = cylindrical_to_spherical(
        data[:, 0], data[:, 1], data[:, 2], degrees=False)
    result[:, 0] = r
    result[:, 1] = theta
    result[:, 2] = phi

    assert np.all(np.isclose(result, expected))

def test_spherical_to_cylindrical():
    expected = np.array(
        [[0.7071, 45, 0.7071], [0.7071, -45, -0.7071]], dtype=np.float32)

    data = np.array([[1, 45, 45], [1, -45, 135]], dtype=np.float32)

    result = np.zeros_like(expected)
    ro, phi, z = spherical_to_cylindrical(data[:, 0], data[:, 1], data[:, 2])
    result[:, 0] = ro
    result[:, 1] = phi
    result[:, 2] = z

    assert np.all(np.isclose(result, expected))

    expected[:, 1] = np.deg2rad(expected[:, 1])

    data[:, 1:] = np.deg2rad(data[:, 1:])

    ro, phi, z = spherical_to_cylindrical(
        data[:, 0], data[:, 1], data[:, 2], degrees=False)
    result[:, 0] = ro
    result[:, 1] = phi
    result[:, 2] = z

    assert np.all(np.isclose(result, expected))


# -*- coding: utf-8 -*-
"""
Regula Falsi

Controllo su:
    - nIter
    - Residuo
    - Intervallo con tolleranza + eps * max(a,b)
"""
import numpy as np
import math

def sign(x) : return math.copysign(1, x)

def regula_falsi(fname, a, b, tol, nMax):
    # A e B devono avere segni discordi
    if sign(fname(a)) == sign(fname(b)):
        print("Algoritmo non applicabile")
        return [], 0, []
    else:
        xks = []
        it = 0
        eps = np.spacing(1)
        fk = fname(a)
        while it < nMax and abs(b-a) >= tol + eps * max(abs(a), abs(b)) and abs(fk) >= tol:
            # Calcolo il nuovo punto Xk
            xk = a - fname(a) * ((b - a)/(fname(b) - fname(a)))
            xks.append(xk)
            fk = fname(xk)
            if sign(xks[it]) == sign(a):
                a = xks[it]
            elif sign(xks[it]) == sign(b):
                b = xks[it]
            elif fname(xks[it]) == 0:
                break;
            it += 1
        return xk, it+1, xks


import numpy as np
#one dimensional array
np.array([1,2,3])
#two dimensional array
np.array([(1,2,3),(4,5,6)])
#1D array of length 3 all values 0
np.zeros(3)
#3x4 array with all values 1
np.ones((3,4))
#5x5 array of 0 with 1 on diagonal
np.eye(5)
#array of 6 evenly divided values from 0 to 100
np.linspace(0,100,6)
#array of values from 0 to less than 10 with step 3
np.arange(0,10,3)
#2x3 array with all values 8
np.full((2,3), 8)
#4x5 array of random floats between 0-1
np.random.rand(4,5)
#6x7 array of random floats between 0-100
np.random.rand(6,7)*100
#2x3 array with random ints between 0-4
np.random.randint(5,size=(2,3))


import networkx as nx
import numpy as np

def get_graph_features(G):
    """
    Generates a list of features for the given graph

    Args:
        G (object): networkx graph object

    Returns:
        features (dict): a dictionary of the features in the given graph
    """

    features = {}

    L = nx.laplacian_matrix(G, weight="cost")
    # L  doesn't work for what we're triyng to do here (so e will not either)
    e = np.linalg.eigvals(L.A)

    features["acyclic"] = nx.is_directed_acyclic_graph(G)
    features[
        "algebraic_connectivity"
    ] = nx.linalg.algebraicconnectivity.algebraic_connectivity(G)
    features["average_distance"] = nx.average_shortest_path_length(G)
    features["bipartite"] = nx.is_bipartite(G)

    # features['Chromatic Index'] =
    # features['Chromatic Number'] =
    # features['Circumference'] =
    features["clique_number"] = nx.graph_clique_number(G)
    features["connected"] = nx.algorithms.components.is_connected(G)
    features["density"] = nx.classes.function.density(G)
    features["diameter"] = nx.algorithms.distance_measures.diameter(G)
    features[
        "edge_connectivity"
    ] = nx.algorithms.connectivity.connectivity.edge_connectivity(G)
    features["eulerian"] = nx.algorithms.euler.is_eulerian(G)
    # features['Genus'] =
    # features['Girth'] =
    # features['Hamiltonian'] =
    # features['independence_number'] = nx.algorithms.mis.maximal_independent_set(G)
    # features['Index'] =
    features["laplacian_largest_eigenvalue"] = max(e)
    # features['Longest Induced Cycle'] =
    # features['Longest Induced Path'] =
    # features['Matching Number'] =

    features["maximum_degree"] = max([G.degree[i] for i in G.nodes])
    features["minimum_degree"] = min([G.degree[i] for i in G.nodes])
    features["minimum_dominating_set"] = len(nx.algorithms.dominating.dominating_set(G))
    features[
        "number_of_components"
    ] = nx.algorithms.components.number_connected_components(G)
    features["number_of_edges"] = G.number_of_edges()
    # features['number_of_triangles'] = nx.algorithms.cluster.triangles(G)
    features["number_of_vertices"] = G.number_of_nodes()
    features["planar"] = nx.algorithms.planarity.check_planarity(G)[0]
    features["radius"] = nx.algorithms.distance_measures.radius(G)
    features["regular"] = nx.algorithms.regular.is_regular(G)
    features["laplacian_second_largest_eigenvalue"] = sorted(e)[1]
    features["ratio_of_two_largest_laplacian_eigenvaleus"] = max(e) / sorted(e)[1]
    features["smallest_eigenvalue"] = min(e)
    features[
        "vertex_connectivity"
    ] = nx.algorithms.connectivity.connectivity.node_connectivity(G)

    # Incude features that are typically used in a VRP/TSP (cost matrix, etc.)

    return features


import os
import pandas as pd
import math
import numpy as np
import scipy.optimize as opt
import matplotlib.pyplot as plt

path = os.getcwd() + '/data/ex2data2.txt'
data = np.loadtxt(path, delimiter=",")
temp = np.ones(((data.shape)[0],1), dtype=np.float64)
data = np.insert(data, [0], temp, axis=1)

def addParams(data):
	new_params = np.zeros((data.shape[0],25), dtype=np.float64)
	count = 0
	for i in range(2, 7):
		for j in range(i + 1):
			x1 = data[:,1]
			x2 = data[:,2]
			new_params[:, count] = np.power(x1, i - j) * np.power(x2, j)
			count += 1
	data = np.insert(data, [3], new_params, axis=1)
	return data

def g(z):
	#sigmoid function
	return 1.0 / (1 + math.pow(math.e, -z))

g = np.vectorize(g, otypes=[np.float64])
#now, when g is applied to a vector, it is applied to each cell

def J_Reg(theta, x, y, lamda):
	#using the vector notation
	ones = np.ones((y.shape[0],), dtype=np.float64)
	h = g(x @ theta)
	tmp = y.transpose() @ np.log(h) + (ones - y).transpose() @ np.log(ones - h)
	reg_term = lamda / (2 * x.shape[0]) * (theta[1:].transpose() @ theta[1:])
	j = -(tmp) / x.shape[0] + reg_term
	return j

def gradientReg(theta, x, y, lamda):
	tmp = x.transpose() @ (g(x @ theta) - y)
	reg_term = (lamda / x.shape[0]) * theta
	reg_term[0] = 0
	grad = tmp + reg_term
	return grad

data = addParams(data)
x = data[:, :-1]
y = data[:, -1]
theta = np.zeros((data.shape[1] - 1,), dtype=np.float64)
lamda = 1
#print(J_Reg(theta, x, y, lamda))
result = opt.fmin_tnc(func=J_Reg, x0=theta, fprime=gradientReg, args=(x, y, lamda))
theta = result[0]

delta = 0.025
xrange = np.arange(-5.0, 20.0, delta)
yrange = np.arange(-5.0, 20.0, delta)
X1, X2 = np.meshgrid(xrange,yrange)

# F is one side of the equation, G is the other
F = 0
count = 0
for i in range(7):
	for j in range(i + 1):
		F += theta[count] * np.power(X1, i - j) * np.power(X2, j)
		count += 1
'''
F = theta[0] + theta[1]*X1 + theta[2]*X2 + theta[3]*X1*X1 + theta[4]*X1*X2 + theta[5]*X2*X2 + \
theta[6]*X1*X1*X1 + theta[7]*X1*X1*X2 + theta[8]*X1*X2*X2 + theta[9]*X2*X2*X2 + \
theta[10]*X1*X1*X1*X1 + theta[11]*X1*X1*X1*X2 + theta[12]*X1*X1*X2*X2 + theta[13]*X1*X2*X2*X2 + theta[14]*X2*X2*X2*X2 + \
theta[15]*X1*X1*X1*X1*X1 + theta[16]*X1*X1*X1*X1*X2 + theta[17]*X1*X1*X1*X2*X2 + theta[18]*X1*X1*X2*X2*X2 + theta[19]*X1*X2*X2*X2*X2 + theta[20]*X2*X2*X2*X2*X2 + \
theta[21]*X1*X1*X1*X1*X1*X1 + theta[22]*X1*X1*X1*X1*X1*X2 + theta[23]*X1*X1*X1*X1*X2*X2 + theta[24]*X1*X1*X1*X2*X2*X2 + theta[25]*X1*X1*X2*X2*X2*X2 + theta[26]*X1*X2*X2*X2*X2*X2 + theta[27]*X2*X2*X2*X2*X2*X2
'''

plt.contour(X1, X2, (F), [0], colors=["k"])
#x1 on x-axis and x2 on y-axis
plt.plot([row[1] for row in data if row[-1] == 0], [row[2] for row in data if row[-1] == 0], "yo")
plt.plot([row[1] for row in data if row[-1] == 1], [row[2] for row in data if row[-1] == 1], "k+")
plt.ylabel('admissions')
plt.show()

'''
positive = data[data['Accepted'].isin([1])]
negative = data[data['Accepted'].isin([0])]

fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(positive['Test 1'], positive['Test 2'], s=50, c='b', marker='o', label='Accepted')
ax.scatter(negative['Test 1'], negative['Test 2'], s=50, c='r', marker='x', label='Rejected')
ax.legend()
ax.set_xlabel('Test 1 Score')
ax.set_ylabel('Test 2 Score')
plt.show()
'''


import numpy as np
import pandas as pd
from pandas_datareader import data
import matplotlib.pyplot as plt

'''
The Simple Moving Average (SMA) is calculated
by adding the price of an instrument over a number of time periods
and then dividing the sum by the number of time periods. The SMA
is basically the average price of the given time period, with equal
weighting given to the price of each period.

Simple Moving Average
SMA = ( Sum ( Price, n ) ) / n

Where: n = Time Period

'''

start_date = '2010-01-01'
end_date = '2020-09-01'
symbol = 'MSFT'
source = 'yahoo'
stock_data = 'msft_data.pkl'

try:
	msft_data1 = pd.read_pickle(stock_data)
	print('Data found and successfully loaded')
except FileNotFoundError:
	print("Data Not Found, downloading data...")
	msft_data1 = data.DataReader(symbol, source, start_date, end_date)

msft_data = msft_data1.tail(620)
close = msft_data['Close']

time_period = 100
history = []
sma_values = []

for close_price in close:
	history.append(close_price)
	if len(history) > time_period:
		del (history[0])

	sma_values.append(np.mean(history))

msft_data = msft_data.assign(ClosePrice=pd.Series(close, index=msft_data.index))
msft_data = msft_data.assign(SMA100Days=pd.Series(sma_values, index=msft_data.index))

close_price = msft_data['ClosePrice']
sma = msft_data['SMA100Days']

fig = plt.figure()
ax1=fig.add_subplot(111, ylabel='Microsoft stock prices in $')
close_price.plot(ax=ax1, color='g', lw=2., legend=True)
sma.plot(ax=ax1, color='r', lw=2., legend=True)
plt.show()


# Gradient Descent application for Optimization Problem
# using Rosenbrock Function

# we ask help for mathematics calculation
import numpy as np

# First case: we set a=0 and b=100
def DerrivRosenbrock0 ( point ):
    dx = 2*point[0] - 400*point[0]*(point[1] - (point[0]**2))
    dy = 200*(point[1] - (point[0]**2))
    return dx, dy

# Second case: we set a=1 and b=100
def DerrivRosenbrock1 ( point ):
    dx = (-2*(1 - point[0]) - 400*(point[1] - (point[0]**2)**2))
    dy = 200*(point[1] - (point[0]**2))
    return dx, dy

def main():
    # set the learning rate first
    # this learning set is ad hoc corresponds to objective function
    lrate = 0.002
    # initialize a point
    a = np.array([-.5, .2])
    # set number of epochs (can be changed as you want)
    epoch = 100000
    # we have to record all tuples of the points and its function
    ai = []
    for i in range(epoch):
        # objective function for first case (uncomment code below)
        # f = (a[0]**2) + (100*((a[1] - a[0]**2)**2))
        # objective function for second case
        f = ((1 - a[0])**2) + (100*((a[1] - a[0]**2)**2))
        # append the point and its obj function to ai as 1D list
        ai.append([a,f])
        # compute its derrivative on point a
        # Derrivative for first case (uncomment code below)
        # fi = np.array(DerrivRosenbrock0(a))
        # Derrivative for second case
        fi = np.array(DerrivRosenbrock1(a))
        # set the new point based on its
        a = a - np.dot(lrate,fi)

    # convert ai into a numpy array
    ai = np.array(ai)
    # print(ai.shape) 'uncomment this to check ai shape and delete this sentences'
    # print the last 10 of ai
    print(ai[-10:-1])
    # the minimum value of the function is just the last element of ai
    print(f'the minimum is: {ai[-1, 1]} at point: {ai[-1,0]}')

if __name__ == '__main__':
    main()


"""
SYS-611: Buffon's Needle Experiment Example with Antithetic Variables.

This example performs a Monte Carlo simulation of Buffon's Needle Experiment
to estimate the probability of a needle of certain length crossing lines
on a floor with certain spacing. This probability is proportional to the
mathematical constant pi.

@author: Paul T. Grogan <pgrogan@stevens.edu>
"""

# import the python3 behavior for importing, division, and printing in python2
from __future__ import absolute_import, division, print_function

# import the matplotlib pyplot package and refer to it as `plt`
# see http://matplotlib.org/api/pyplot_api.html for documentation
import matplotlib.pyplot as plt

# import the scipy stats package and refer to it as `stats`
# see http://docs.scipy.org/doc/scipy/reference/stats.html for documentation
import scipy.stats as stats

# import the numpy package and refer to it as `np`
# see http://docs.scipy.org/doc/numpy/reference/ for documentation
import numpy as np

# define the line width and needle length for buffon's experiment
line_width = 3.0
needle_length = 2.5

# define a process generator for the event if a needle crosses a line
def drop_needle():
    r_1 = np.random.rand()
    r_2 = np.random.rand()
    # generate distance between needle centroid and nearest line from uniform
    # distribution between 0 and line_width/2
    d_1 = r_1*line_width/2
    d_2 = (1-r_1)*line_width/2
    # generate acute angle between needle and line from uniform distribution
    # between 0 and pi/2 radians
    theta_1 = r_2*np.pi/2
    theta_2 = (1-r_2)*np.pi/2

    # for each antithetic variable, record 1 if d < needle_length/2*sin(theta)
    # otherwise record 0
    x_1 = 1 if d_1 < needle_length/2*np.sin(theta_1) else 0
    x_2 = 1 if d_2 < needle_length/2*np.sin(theta_2) else 0
    # return the average of the two antithetic variables
    return (x_1+x_2)/2.

# set the random number generator seed to 0
np.random.seed(0)

# generate 850 samples
samples = [drop_needle() for i in range(850)]

# compute the lower and upper-bounds using a 95% confidence interval
confidence_level = 0.05
z_crit = stats.norm.ppf(1-confidence_level/2)

print('P(X) = {:.3f} +/- {:.3f} (95% CI)'.format(
        np.average(samples),
        z_crit*stats.sem(samples)
    ))

# compute the exact solution, as solved by calculus
solution = 2*needle_length/(line_width*np.pi)

# compute running statistics for mean and confidence interval
mean_estimate = np.array([np.average(samples[0:i]) for i in range(len(samples))])
confidence_int = z_crit*np.array([stats.sem(samples[0:i]) for i in range(len(samples))])

# create a plot to show the mean estimate with 95% confidence interval bounds
plt.figure()
plt.plot(range(len(samples)), mean_estimate,
         'b', label='Mean Estimate')
plt.plot(range(len(samples)), mean_estimate-confidence_int,
         'g', label='95% CI Lower Bound')
plt.plot(range(len(samples)), mean_estimate+confidence_int,
         'r', label='95% CI Upper Bound')
plt.plot([0, len(samples)], [solution, solution],
          '-k', label='Analytical Solution')
plt.xlabel('Sample')
plt.ylabel('Estimate of $P(x)$')
plt.legend(loc='best')

#%%

# transform the mean estimate to estimate pi using the solution form
pi_estimate = 2*needle_length/(line_width*mean_estimate)
pi_lower_bound = 2*needle_length/(line_width*(mean_estimate+confidence_int))
pi_upper_bound = 2*needle_length/(line_width*(mean_estimate-confidence_int))

print('pi = {:.3f} +/- {:.3f} (95% CI)'.format(
        pi_estimate[-1],
        pi_upper_bound[-1] - pi_estimate[-1]
    ))

# create a plot to show the pi estimate with 95% confidence interval bounds
plt.figure()
plt.plot(range(len(samples)), pi_estimate,
         'b', label='Mean Estimate')
plt.plot(range(len(samples)), pi_lower_bound,
         'g', label='95% CI Lower Bound')
plt.plot(range(len(samples)), pi_upper_bound,
         'r', label='95% CI Upper Bound')
plt.plot([0, len(samples)], [np.pi, np.pi],
         '-k', label='Analytical Solution')
plt.xlabel('Sample')
plt.ylabel('Estimate of $\pi$')
plt.legend(loc='best')


# -*- coding: utf-8 -*-
"""This module provides functions to convert between different attitude
representations, as well as computing
"""

# Imports
import logging
import copy
import math
import numpy as np

# Brahe Imports
from   brahe.utils import logger
import brahe.constants as _const
from   brahe.epoch import Epoch

#####################
# Rotation Matrices #
#####################

def Rx(angle:float, use_degrees:bool=False) -> np.ndarray:
    """Rotation matrix, for a rotation about the x-axis.

    Args:
        angle (float): Counter-clockwise angle of rotation as viewed
            looking back along the postive direction of the rotation axis.
        use_degrees (bool): Handle input and output in degrees. Default: ``False``

    Returns:
        r (np.ndarray): Rotation matrix

    References:

        1. O. Montenbruck, and E. Gill, *Satellite Orbits: Models, Methods and Applications*, 2012, p.27.
    """

    if use_degrees:
        angle *= math.pi/180.0

    c = math.cos(angle)
    s = math.sin(angle)

    return np.array([[1.0,  0.0,  0.0],
                      [0.0,   +c,   +s],
                      [0.0,   -s,   +c]])

def Ry(angle:float, use_degrees:bool=False) -> np.ndarray:
    """Rotation matrix, for a rotation about the y-axis.

    Args:
        angle (float): Counter-clockwise angle of rotation as viewed
            looking back along the postive direction of the rotation axis.
        use_degrees (bool): Handle input and output in degrees. Default: ``False``

    Returns:
        r (np.ndarray): Rotation matrix

    References:

        1. O. Montenbruck, and E. Gill, *Satellite Orbits: Models, Methods and Applications*, 2012, p.27.
    """

    if use_degrees:
        angle *= math.pi/180.0

    c = math.cos(angle)
    s = math.sin(angle)

    return np.array([[ +c,  0.0,   -s],
                      [0.0, +1.0,  0.0],
                      [ +s,  0.0,   +c]])

def Rz(angle:float, use_degrees:bool=False) -> np.ndarray:
    """Rotation matrix, for a rotation about the z-axis.

    Args:
        angle (float): Counter-clockwise angle of rotation as viewed
            looking back along the postive direction of the rotation axis.
        use_degrees (bool): Handle input and output in degrees. Default: ``False``

    Returns:
        r (np.ndarray): Rotation matrix

    References:

        1. O. Montenbruck, and E. Gill, *Satellite Orbits: Models, Methods and Applications*, 2012, p.27.
    """

    if use_degrees:
        angle *= math.pi/180.0

    c = math.cos(angle)
    s = math.sin(angle)

    return np.array([[ +c,   +s,  0.0],
                      [ -s,   +c,  0.0],
                      [0.0,  0.0,  1.0]])


# -*- coding: utf-8 -*-
"""
A function to compute the cost.
"""

import numpy as np
from functions import sigmoid

def compute_loss(y, tx, w, fn="mse"):
    """compute the loss given the function 'fn'"""

    assert len(y) == len(tx), "y and tx must have the same number of rows"

    # cost given a binary prediction (0 or 1)
    def with_pred(pred):
        """compute the cost by negative log likelihood"""
        return - np.asscalar((y.T @ np.log(pred)) + ((1 - y).T @ np.log(1 - pred)))

    if fn.lower() == "mse":
        N = len(y)
        e = y - (tx @ w)
        return (e.T @ e) / (2 * N)

    if fn.lower() == "rmse":
        return np.sqrt(2 * compute_loss(y, tx, w, fn="mse"))

    if fn.lower() == "mae":
        N = len(y)
        e = y - tx @ w
        return np.mean(np.abs(e))

    if fn.lower() == "sig":
        pred = sigmoid(tx @ w)
        return with_pred(pred)

    raise NameError("Not such fn exists \"" + fn + "\"")

def calculate_loss(e, fn="mse"):
    """Calculate the mse for vector e."""

    if fn.lower() == "mse":
        return (e.T @ e) / (2 * len(e))

    if fn.lower() == "rmse":
        return np.sqrt(2 * calculate_loss(e, fn="mse"))

    if fn.lower() == "mae":
        return np.mean(np.abs(e))

    raise NameError("Not such fn exists \"" + fn + "\"")


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

# code starts here

df = pd.read_csv(path)
total = len(df)

p_a = len(df[df.fico > 700]) / total
p_b = len(df[df.purpose == 'debt_consolidation']) / total
print(p_a)
print(p_b)
df1 = df[df.purpose == 'debt_consolidation']
p_a_b = len(df1[df1.fico > 700]) / len(df[df.fico > 700])
print(p_a_b)
p_b_a = p_a_b * p_a / p_b
print(p_b_a)
result = p_b_a == p_a
#if p_b_a == p_a:
#    result = "Independent"
#else:
#    result = "Not Independent"

print(result)
# code ends here

# --------------
# code starts here

prob_lp = len(df[df['paid.back.loan'] == 'Yes']) / len(df)
prob_cs = len(df[df['credit.policy'] == 'Yes']) / len(df)

new_df = df[df['paid.back.loan'] == 'Yes']

prob_pd_cs = len(new_df[new_df['credit.policy'] == 'Yes']) / len(new_df)

bayes = (prob_pd_cs * prob_lp) / prob_cs
print(bayes)
# code ends here

# --------------
# code starts here
df1 = df[df['paid.back.loan'] == 'No']
purpose = df1['purpose'].value_counts(ascending=False)
purpose.plot(kind='bar')
plt.xlabel('Purpose')
plt.ylabel('No of Loan Defaulters')
plt.title('Probability of the Loan Defaulters')
plt.show()

# code ends here

# --------------
# code starts here

inst_median = df['installment'].median()
inst_mean = df['installment'].mean()
inst_mean

plt.hist(df['installment'], bins=10)
plt.axvline(x=inst_median, color='green')
plt.axvline(x=inst_mean, color='black')
plt.show()

plt.hist(df['log.annual.inc'], bins=10)

# code ends here


import numpy as np

def is_quadratic(num):
    return np.sqrt(float(num)).is_integer()

def matrix_argmax(mat):
    return np.unravel_index(mat.argmax(), mat.shape)

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

def inv_sigmoid(x, envelop=np.Inf):
    assert (envelop > 0)
    return np.maximum(np.minimum(np.log(x / (1 - x)), envelop), -envelop)

def linear_transform_01(A):
    max_A = np.max(A).astype(np.float)
    min_A = np.min(A).astype(np.float)
    return (A - min_A) / (max_A - min(A))

def Jsoftmax(w, t=1.0, axis=0):
    w = np.array(w)  # n, c, h, w
    ndim = len(w.shape)
    w_sh = np.array(w.shape)
    w_sh[axis] = 1
    reshapedim = np.array([1] * ndim, dtype=np.int)
    reshapedim[axis] = w.shape[axis]

    w_max = np.amax(w, axis=axis, keepdims=True)
    w -= np.tile(w_max, reshapedim)  # to avoid numerical issues (exploding exp)

    e = np.exp(np.array(w) / t)
    Z = np.sum(e, axis=axis, keepdims=True)

    out = e / np.tile(Z, reshapedim)
    return out

def proj_lp(v, xi, p):
    if np.linalg.norm(v.reshape(-1), p) > xi:
        if p == np.inf:
            v_projected = np.sign(v) * np.minimum(np.abs(v), xi)
        elif p == 2:
            v_projected = v / np.linalg.norm(v.reshape(-1)) * xi
        else:
            raise
        return v_projected
    else:
        return v

def compute_percentiles(x, thres=0.25):
    x = np.array(x)
    p = np.sort(x.reshape(-1))
    N = float(x.size)
    low_n, upp_n = int(thres * N), int((1 - thres) * N)
    return p[low_n], p[upp_n]


__author__ = "Haim Adrian"

import cv2
import numpy as np

def log(consumer, *message):
    text = ' '.join(message)
    if consumer is not None:
        consumer(text)

def do_progress(consumer, current_progress, step):
    if consumer is not None:
        current_progress += step
        consumer(current_progress)

    return current_progress

# I have created this method to compare matrices between py3.5 and py3.7 in order to find a bug I had in py3.5,
# where I used a uint8 matrix rather than float64 and had overflows occurred in py3.5
def dump_matrix_to_disk(mat, file_name):
    with open(file_name, 'wb') as f:
        f.write(str.encode(str(mat.shape)))
        f.write(str.encode('\n'))
        for line in mat:
            np.savetxt(f, line, fmt='%.2f')

def find_harris_corners(image, k, window_size, console_consumer=None):
    """
    Harris Corner Detector algorithm implementation.
    Harris detector responses are the CRF(x, y) values from the equation: CRF = det(M) − k*(tr(M)**2) (Corner Response Function)
    where det(M) = Lambda1*Lambda2, which is the value of the determinant, and tr(M) = Lambda1+Lambda2, which is the trace
    of the determinant. (Lambda1, Lambda2 = eigen values).
    When the CRF value of the pixel point is greater than the given threshold, it is determined that the target point is a corner point.
    :param image: Input single-channel 8-bit or floating-point image.
    :param k: Harris detector free parameter in the equation.
    :param window_size: Neighborhood size.
    :param console_consumer: Used for logging purposes. (Passing step updates to a listener)
    :return: Image to store the Harris detector responses. It has the same size as source image. dst(x,y) = detM(x,y) − k*(trM(x,y)**2)
    Corners in the image can be found as the local maxima of this response map.
    """
    if not isinstance(image, np.ndarray):
        log(console_consumer, "find_harris_corners: Not a tensor. Was: Image=", image.__class__)
        return None

    if image.ndim not in [2, 3]:
        log(console_consumer, 'find_harris_corners: Illegal image dimension. Image N-dimension can be 2 or 3 only. Was:', image.ndim)
        return None

    if image.ndim == 3:
        twoD = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    else:
        twoD = image

    # Find all eigen values and vectors for each pixel in the image.
    # Eigen values are used in order to find the determinant and trace, which are needed for Harris Corner equation
    eigen_vals_and_vectors = cv2.cornerEigenValsAndVecs(twoD, window_size, 5)

    # 0 is for first eigen value, and 1 is for second eigen value
    # Use batch operation rather than going in a very slow loop over them and calculate the response for each pixel
    # det(M) = lambda1 * lambda2
    # trace(M) = lambda1 + lambda2
    detM = eigen_vals_and_vectors[:, :, 0] * eigen_vals_and_vectors[:, :, 1]
    traceM = eigen_vals_and_vectors[:, :, 0] + eigen_vals_and_vectors[:, :, 1]
    harris_responses = detM - k * (traceM ** 2)

    return harris_responses

# I leave this code here as originally I have started from it until found cornerEigenValsAndVecs
# Behind the scenes of cv2.cornerEigenValsAndVecs:

# ! offset = int(window_size / 2)
# Pad the image so we can calculate Harris score for the whole image
# Use Copy Padding so we will not accidentally invent corners with a constant padding of 0's
# From now on, go ahead as float64. Otherwise this won't work in py3.5 (It does work in 3.7 however)
# ! padded = np.float64(cv2.copyMakeBorder(twoD, offset, offset, offset, offset, cv2.BORDER_REPLICATE))

# For 2D array, so the result of gradient is two arrays ordered by axis
# ! dy, dx = np.gradient(padded)

# Compute products of derivatives at every pixel
# ! Ixx = dx ** 2
# ! Ixy = dx * dy
# ! Iyy = dy ** 2

# Calculate step size in the area we have to progress in a progressbar
# ! progress_steps_left = max_progress - progress
# ! progress_step = progress_steps_left / image.shape[0]
# ! for x in range(offset, padded.shape[0] - offset):
# ! for y in range(offset, padded.shape[1] - offset):
# The window according to the Harris Equation
# ! window_Ixx = Ixx[x - offset: x + offset + 1, y - offset: y + offset + 1]
# ! window_Ixy = Ixy[x - offset: x + offset + 1, y - offset: y + offset + 1]
# ! window_Iyy = Iyy[x - offset: x + offset + 1, y - offset: y + offset + 1]

# Sum the squares in the sliding window.
# Here is the M = SUM_for-each-x-y(window(x, y)[[Ixx, Ixy], [Ixy, Iyy]]
# ! sum_xx = window_Ixx.sum()
# ! sum_xy = window_Ixy.sum()
# ! sum_yy = window_Iyy.sum()

# Calculate determinant and trace for the sum of squares matrix (M)
# ! detM = (sum_xx * sum_yy) - (sum_xy * sum_xy)  # Multiply main diagonal, minus multiplication of secondary diagonal
# ! traceM = sum_xx + sum_yy  # Main diagonal

# Calculate corner_response_function for Harris Corner equation
# ! corner_response_function = detM - k * (traceM ** 2)

# ! harris_responses[x - offset, y - offset] = corner_response_function

# ! progress = do_progress(progress_consumer, progress, progress_step)

# ! return harris_responses


#! /usr/bin/env python3

import sys
import csv
import argparse
import numpy as np

ROUND_OFF: int = 4
theta: float = 0.0
r: float = 0.0
z: float = 0.0
y: float = 0.0
x: float = 0.0

class DataConverter:
    def __init__(self):

        try:
            self.options = self.read_options(sys.argv[1:])
        except:
            print("Input data file required...")
            exit(1)

        if self.options.input == None:
            print("[+] Please enter the input/origianl data file\n")
            print(self.parser.print_help())
            exit(1)

        self.INPUT_FILE_PATH: str = self.options.input
        self.OUTPUT_FILE_PATH: str =  self.INPUT_FILE_PATH[:-3] + "Rec.csv" # "Rec_LidarScanCSVData.csv"

        try:
            self.cylindrical_file = csv.reader(open(self.INPUT_FILE_PATH, "r"))
        except FileNotFoundError as e:
            print(e)
            exit(1)

        self.rectangular_file = csv.writer(open(self.OUTPUT_FILE_PATH, "w"))
        self.preport("Completed reading contents of file: " + self.INPUT_FILE_PATH)
        self.preport("Created data output save file: " + self.OUTPUT_FILE_PATH)

        self.cylindrical_data: list = list(self.cylindrical_file)
        self.rectangular_data: list = self.cylindrical_data

        self.num_rows: int = len(self.cylindrical_data)
        self.preport("Data processing has began...\n[+] Please wait..........")

        self.data_conversion()

        # Save the new file with elements in rectangular coordinates
        self.rectangular_file.writerows(self.rectangular_data)
        self.preport("Output file '" + self.OUTPUT_FILE_PATH + "' Saved.")
        self.preport("Data conversion completed!!!")

    # declare function to define command-line arguments
    def read_options(self, args=sys.argv[1:]):
        """Returns dictionary of arguments values"""
        self.parser = argparse.ArgumentParser(description="The parsing commands lists.")
        self.parser.add_argument("input", help="Enter the original/input data file")
        return self.parser.parse_args(args)

    def deg2rad(self, pembe: float) -> float:
        return (np.pi * pembe) / 180.0

    def preport(self, mesg: str):
        print("[+] " + mesg + " \r")

    def dreport(self, mesg: str):
        print("[-] " + mesg + "\r")

    def data_conversion(self):
        first_data_row_index = 0

        if type(self.rectangular_data[0][0]) == str:
            first_data_row_index = 1
            self.rectangular_data[0][0] = "X"
            self.rectangular_data[0][1] = "Y"
            self.rectangular_data[0][2] = "Z"

        for row in range(first_data_row_index, self.num_rows):
            theta = float(self.cylindrical_data[row][0])
            r = float(self.cylindrical_data[row][1])
            z = float(self.cylindrical_data[row][2])

            theta = self.deg2rad(theta)

            x = round(r * np.cos(theta), 4)
            y = round(r * np.sin(theta), 4)
            z = round(z, 4)

            self.rectangular_data[row][0] = x
            self.rectangular_data[row][1] = y
            self.rectangular_data[row][2] = z

DataConverter()


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2017-10-14 20:54:56
# @Author  : jingray (lionel_jing@163.com)
# @Link    : http://www.jianshu.com/u/01fb0364467d
# @Version : $Id$

import random as rd

# Coin flips
def simulate_coin_flips(num_trials):
    heads = 0
    tails = 0
    p_heads = 0.5
    for i in range(num_trials):
        random_number = rd.random()
        if random_number < p_heads:
            heads = heads + 1
        else:
            tails += 1
    percent_heads = heads / num_trials
    return percent_heads

percentage = simulate_coin_flips(2000000) # calling the function
print(percentage)

# Simulating Probabilities

def simulate_dice_rolls(N):
    roll_counts = [0,0,0,0,0,0]
    for i in range(N):
        roll = rd.choice([1,2,3,4,5,6])
        index = roll - 1
        roll_counts[index] = roll_counts[index] + 1
    return roll_counts

def show_roll_data(roll_counts):
    number_of_sides_on_die = len(roll_counts)
    for i in range(number_of_sides_on_die):
        number_of_rolls = roll_counts[i]
        number_on_die = i+1
        print(number_on_die, "came up", number_of_rolls, "times")

roll_data = simulate_dice_rolls(60000)
show_roll_data(roll_data)

from matplotlib import pyplot as plt

def visualize_one_die(roll_data):
    roll_outcomes = [1,2,3,4,5,6]
    fig, ax = plt.subplots()
    ax.bar(roll_outcomes, roll_data)
    ax.set_xlabel("Value on Die")
    ax.set_ylabel("# rolls")
    ax.set_title("Simulated Counts of Rolls")
    plt.show()

roll_data = simulate_dice_rolls(500)
visualize_one_die(roll_data)

# Calculating Probabilities for Bayes' rule

# The complement P(~A) function takes in the probability of an event P(A).
def complement(p_A):
    complement = 1 - p_A
    return complement

# Test code - do not change
import solution

test_value = 0.4265

# This line calls your function and stores the output
comp = complement(test_value)
correct_comp = solution.complement(test_value)

# Assertion, comparison test
try:
    assert(comp == correct_comp)
    print('That\'s right!')
except:
    print ('Your code returned that the complement is: ' +str(comp)
           + ', which does not match the correct value: ' +str(correct_comp))

# Test 2
comp2 = complement(solution.test_value)
correct_comp2 = solution.complement(solution.test_value)

# Assertion, comparison test
try:
    assert(comp2 == correct_comp2)
    print('That\'s right!')
except:
    print ('For test 2, your code returned that the complement is: ' +str(comp2)
           + ', which does not match the correct value.')

## Complete this joint function
def joint(p_A, p_B):

    ## calculates the joint probability of
    ## any variables p_A, p_B, WHEN THOSE PROBABILITIES
    ## ARE INDEPENDENT (this code wouldn't work
    ## for probabilities that depend on each other).
    joint = p_A * p_B

    return joint

# Test values
test_A = 0.15
test_B = 0.42

# This line calls your function and stores the output
j = joint(test_A, test_B)
correct_j = solution.joint(test_A, test_B)

# Assertion, comparison test
try:
    assert(j == correct_j)
    print('That\'s right!')
except:
    print ('Your code returned that the joint probability is: ' +str(j)
           + ', which does not match the correct value.')

# The inputs to total probability are given descriptive names

def total_probability(p_disease, p_pos_given_disease, p_pos_given_no_disease):

    ## calculates the total probability of a positive test result
    ## You may use other variable in this function as well as long
    ## as total is correct
    total = p_disease * p_pos_given_disease + (1 - p_disease) * p_pos_given_no_disease

    return total

# Test code - do not change
import solution

p_disease = 0.1
p_pos_given_disease= 0.9
p_pos_given_no_disease= 0.2

# This line calls your function and stores the output
total = total_probability(p_disease, p_pos_given_disease, p_pos_given_no_disease)
correct_total = solution.total_probability(p_disease, p_pos_given_disease, p_pos_given_no_disease)

# Assertion, comparison test
try:
    assert(total == correct_total)
    print('That\'s right!')
except:
    print ('Your code returned that the total probability is: ' +str(total)
           + ', which does not match the correct value.')

# Given three input probabilities, complete this function
# so that it returns the posterior probability

def bayes(p_A, p_B_given_A, p_notB_given_notA):

    ## TODO: Calculate the posterior probability
    ## and change this value
    p_notA = 1-p_A
    p_B_given_notA = 1-p_notB_given_notA
    p_B = p_A*p_B_given_A + p_notA*p_B_given_notA
    posterior = p_A*p_B_given_A/p_B

    return posterior

# Test code - do not change
import solution

test_p_A = 0.4
test_p_B_given_A = 0.7
test_p_notB_given_notA = 0.9

# This line calls your function and stores the output
posterior = bayes(test_p_A, test_p_B_given_A, test_p_notB_given_notA)
correct_posterior = solution.bayes(test_p_A, test_p_B_given_A, test_p_notB_given_notA)

# Assertion, comparison test
try:

    assert(abs(posterior - correct_posterior) < 0.0001)
    print('That\'s right!')
except:
    print ('Your code returned that the posterior is: ' +str(posterior)
           + ', which does not match the correct value.')

# 2D Array
import numpy as np

# A simple robot world can be defined by a 2D array
# Here is a 5x6 world
world = np.array([ [0, 0, 0, 1, 0],
                   [0, 0, 0, 1, 0],
                   [0, 1, 1, 0, 0],
                   [0, 0, 0, 0, 1],
                   [1, 0, 0, 1, 0],
                   [1, 0, 0, 0, 0] ])

# Visualize the world
print(world)
# Print out some information about the world

print('The shape of this array is: ' + str(world.shape))
print('Notice that the x and y dimensions are in the opposite order than usual!')
print('It\'s height is: ' + str(world.shape[1]) +
      ', and it\'s length/width is: ' + str(world.shape[0]))

      # Access a location and read its value
value = world[3][0]
print('\n')
print('Value at index [3, 0] = ' +str(value))

# Read the first three items in the 3rd row
row = 2
column_index = 0
value_left = world[row, column_index]
value_middle = world[row, column_index + 1]
value_right = world[row, column_index + 2]

print('\nThe first three values in row 2 : ' +str(value_left)+', '
                                              +str(value_middle) +', '
                                              +str(value_right) )

# Compare the first two values and print the result
compare = world[0][0] == world[0][1]
print('\nDo the first two values match? ' + str(compare))

# Define a function to plant a tree
# and change the value of the array in that location
def plant_tree(y, x):
    # check that the indices are in the boundaries of the array dimensions
    if(y < world.shape[0] and x < world.shape[1]):
        world[y,x] = 1
        print('\n' + str(world)) # prints a newline and the current world

# Call the function at the location x = 3, and y = 2
# You can call this multiple times in a row
plant_tree(0, 2)

# This function uses nested for loops and knowledge
# about the shape of the array to print out each item with it's index
def iterate2D(world):
    # y-dimension (rows)
    for i in range(0, world.shape[0]):
        # x-dimension (columns)
        for j in range(0, world.shape[1]):
            print('Index ['+str(i)+']['+str(j)+'] = ' +str(world[i][j]))

# Call the iterate function
print('\n')
iterate2D(world)

# This function is similar to our iterate2D function,
# But looks for the first tree in the array and prints its location [x][y]
def first_tree(world):
    # iterates through all indices starting at the top-left [0][0]
    for i in range(0, world.shape[0]):
        for j in range(0, world.shape[1]):
            # check if a tree is found
            if(world[i][j] == 1):
                # if so, print the index and leave the loop with a return statement
                print('First tree found at location: ['+str(i)+']['+str(j)+']')
                return

# Call the first_tree function
print('\n')
first_tree(world)

import numpy as np

# A 5x4 robot world of characters 'o' and 'b'
world = np.array([ ['o', 'b', 'o', 'o', 'b'],
                   ['o', 'o', 'b', 'o', 'o'],
                   ['b', 'o', 'o', 'b', 'o'],
                   ['b', 'o', 'o', 'o', 'o'] ])

# Sensor measurement
measurement = ['b', 'o']

# This function takes in the world and the sensor measurement.
# Complete this function so that it returns the indices of the
# likely robot locations, based on matching the measurement
# with the color patterns in the world

def find_match(world, measurement):
    # Empty possible_locations list
    possible_locations = []

    ## TODO: Iterate through the world
    ## Look at two adjacent indices at a time - the square the robot is
    ## on top of and the square to its right
    ## (Making sure not to go past the bounds of the world)
    for i in range(0,world.shape[0]):
        for j in range(0,world.shape[1]):
            if j<world.shape[1]-1 and [world[i][j],world[i][j+1]] == measurement:
                possible_locations.append([i,j])
    ## TODO: If two adjacent colors in the world match
    ## the two colors in the sensor measurement
    ## Add those indices to the possible_locations list
    ## Append them in the format [row_index, column_index], i.e. [0, 0]

    return possible_locations

# This line runs the function and stores the output - do not delete
locations = find_match(world, measurement)

# In numpy, this is easy to print COLUMN 0?

import numpy as np

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

# The ':' usually means "*all values*
print(np_grid[:,0])

# What if you wanted to change the size of the array?

# For example, we can turn the 2D grid from above into a 1D array
# Here, the -1 means fit all values into the length of this array
np_1D = np.resize(np_grid, (1, -1))

print(np_1D)

# We can also create a 2D array of zeros or ones
# which is useful for car world creation and analysis

# Create a 5x4 array
zero_grid = np.zeros((5, 4))

print(zero_grid)


# %%
import numpy as np

# %%
vector = np.array([1, 2, 3, 4])

# %%
vector

# %%
vector.shape

# %%
mat = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])

# %%
mat

# %%
mat.shape

# %%
mat.sum()

# %%
mat.sum(axis=0)

# %%
mat[0]

# %%
mat[1]

# %%
mat.sum(axis=1)

# %%
mat

# %%
mat.mean()

# %%
mat.mean(axis=1)

# %%
mat.mean(axis=0)

# %%
tensor = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])

# %%
tensor

# %%
tensor.shape

# %%
list(range(10))

# %%
np.array(range(10))

# %%
range(1.0, 10.0)

# %%
np.arange(10)

# %%
np.arange(10.0)

# %%
np.arange(10).dtype

# %%
np.arange(10.0).dtype

# %%
np.arange(2, 10)

# %%
np.arange(2, 10, 3)

# %%
np.linspace(0.1, 1.0, 10)

# %%
np.arange(0.1, 1.1, 0.1)

# %%
np.linspace(0.0, 1.0, 10)

# %%
np.zeros(3)

# %%
np.zeros(3).shape

# %%
np.zeros((3,))

# %%
np.zeros((3, 3))

# %%
np.zeros((2, 3, 4))

# %%
np.ones(3)

# %%
np.ones((1, 2, 3))

# %%
np.ones(1, 2, 3)

# %%
id = np.eye(4)

# %%
id

# %%
np.eye(3, 4)

# %%
np.identity(3)

# %%


import numpy as np

# np.random.seed(100)
# print(np.random.rand(10))

# np.random.seed(100)
# print(np.random.rand(10))

# print(np.random.randn(10))
# print(np.random.randint(1, 10, size=(2,3)))

# print(np.array([1, 2, 3, 4, 5]))
# print(np.arange(1, 10, 2))

# print(np.empty((4, 4)))
# print(np.ones((4, 4)))
# print(np.zeros((4, 4)))
# print(np.full((4, 4), 10))

# print(np.eye(4))

# print(np.linspace(1, 10, 5))

# print(np.random.uniform(1., 10., 10))
# print(np.random.normal(size=(3, 4)))
# print(np.random.choice(100, size=(3, 4)))

x = np.arange(10).reshape(2, 5)
print(x)
# print(type(x[1, -1]))

# print(x[:, -1:])

# print(y)
# print(type(y[1, 1, 1]))

# print(y)

# print(y[:, -1:, :])

# ravel, flatten

# copy no
# print(x.ravel())
# print(np.ravel(x))

# copy yes
# print(x.flatten())
# print(x.ndim)

print(np.sum(x, axis=1))
print(np.mean(x, axis=1))

# z = np.random.randn(10).reshape(2, 5)
# print(z)
# print(np.any( z > 0 ))
# print(np.all( z > 0 ))

# print(np.where( z > 0, z, 0))

print(np.argmax(x, axis=1))


import numpy as np
import pandas as pd
from statsmodels.tsa.stattools import coint
import matplotlib.pyplot as plt
# Set a seed value to make the experience reproducible
np.random.seed(123)

# Generate Symbol1 daily returns
Symbol1_returns = np.random.normal(0, 1, 100)
# Create a series for Symbol1 prices
Symbol1_prices = pd.Series(np.cumsum(Symbol1_returns),\
                           name='Symbol1') + 10
Symbol1_prices.plot(figsize=(15,7))
plt.show()

# Create a series for Symbol2 prices
# We are going to mimikate the Symbol1 behavior
noise = np.random.normal(0, 1, 100)
Symbol2_prices = Symbol1_prices + 10 + noise
Symbol2_prices.name = 'Symbol2'

plt.title("Symbol 1 and Symbol 2 prices")
Symbol1_prices.plot()
Symbol2_prices.plot()
plt.legend()
plt.show()

def zscore(series):
    return (series - series.mean()) / np.std(series)

score, pvalue, _ = coint(Symbol1_prices, Symbol2_prices)
print(pvalue)
ratios = Symbol1_prices / Symbol2_prices
plt.title("Ration between Symbol 1 and Symbol 2 price")

ratios.plot()
plt.show()

#plt.axhline(ratios.mean())
#plt.legend([' Ratio'])

zscore(ratios).plot()
plt.title("Z-score evolution")
plt.axhline(zscore(ratios).mean(),color="black")
plt.axhline(1.0, color="red")
plt.axhline(-1.0, color="green")
plt.show()

ratios.plot()
buy = ratios.copy()
sell = ratios.copy()
buy[zscore(ratios)>-1] = 0
sell[zscore(ratios)<1] = 0
buy.plot(color="g", linestyle="None", marker="^")
sell.plot(color="r", linestyle="None", marker="v")
x1,x2,y1,y2 = plt.axis()
plt.axis((x1,x2,ratios.min(),ratios.max()))
plt.legend(["Ratio", "Buy Signal", "Sell Signal"])
plt.show()

symbol1_buy=Symbol1_prices.copy()
symbol1_sell=Symbol1_prices.copy()
symbol2_buy=Symbol2_prices.copy()
symbol2_sell=Symbol2_prices.copy()

Symbol1_prices.plot()
symbol1_buy[zscore(ratios)>-1] = 0
symbol1_sell[zscore(ratios)<1] = 0
symbol1_buy.plot(color="g", linestyle="None", marker="^")
symbol1_sell.plot(color="r", linestyle="None", marker="v")

Symbol2_prices.plot()
symbol2_buy[zscore(ratios)<1] = 0
symbol2_sell[zscore(ratios)>-1] = 0
symbol2_buy.plot(color="g", linestyle="None", marker="^")
symbol2_sell.plot(color="r", linestyle="None", marker="v")

x1,x2,y1,y2 = plt.axis()
plt.axis((x1,x2,Symbol1_prices.min(),Symbol2_prices.max()))
plt.legend(["Symbol1", "Buy Signal", "Sell Signal","Symbol2"])
plt.show()


# Value iteration, Policy iteration
import numpy as np

def policy_eval(pi: np.ndarray, v: np.ndarray, num_states: int, T: np.ndarray, R: np.ndarray, gamma,
               threshold: float = 1e-5) -> np.ndarray:
    """
    Given certain policy probabilities, return the associated values of all states.
    :param pi: Current policy function as an array, where (action = pi[state]).
    :param v: Current value function as an array, where (value = v[state]).
    :param num_states: Number of states in the environment.
    :param T: Transition probabilities, as a 2D array.
    :param R: Rewards for certain state transitions, as a 2D array.
    :param gamma: Discount factor.
    :param threshold:
    :return: The new value of all states.
    """
    v_new = np.copy(v)
    v += 100

    while np.max(np.abs(v_new - v)) > threshold:
        v = np.copy(v_new)
        for s in range(num_states):
            # Deterministic policy
            a = pi[s]
            # Use in-place updating for policy evaluation
            v_new[s] = np.sum(T[s, a, :] * (R[s, a, :] + gamma * v))

    return v_new

def policy_iter(pi: np.ndarray, v: np.ndarray, num_states: int, T: np.ndarray, R: np.ndarray, gamma=1.0) -> tuple[
    np.ndarray, np.ndarray]:
    """
    Perform one iteration of the policy iteration algorithm
    :param pi: Current policy function as an array, where (action = pi[state]).
    :param v: Current value function as an array, where (value = v[state]).
    :param num_states: Number of states in the environment.
    :param T: Transition probabilities, as a 2D array.
    :param R: Rewards for certain state transitions, as a 2D array.
    :param gamma: Discount factor.
    :return: Updated pi and v.
    """
    # Evaluate the policy
    v = policy_eval(pi=pi, v=v, num_states=num_states, T=T, R=R, gamma=gamma)

    # Improve the policy
    pi_new = np.copy(pi)
    pi += 100
    while np.any(pi_new != pi):
        pi = np.copy(pi_new)
        for s in range(num_states):
            pi_new[s] = np.argmax(np.sum(T[s, :, :] * (R[s, :, :] + gamma * v[np.newaxis, :]), axis=1))

    return pi, v

def value_iter(v: np.ndarray, num_states: int, T: np.ndarray, R: np.ndarray, gamma=1.0) -> tuple[np.ndarray, np.ndarray]:
    """
    Perform one iteration of the value iteration algorithm
    :param v: Current value function as an array, where (value = v[state].
    :param num_states: Number of states in the environment.
    :param T: Transition probabilities, as a 2D array.
    :param R: Rewards for certain state transitions, as a 2D array.
    :param gamma: Discount factor.
    :return: Updated pi and v.
    """
    delta = 0
    v_new = np.copy(v)
    for s in range(num_states):
        v = np.copy(v_new)
        v_new[s] = np.max(np.sum(T[s, :, :] * (R[s, :, :] + gamma * v[np.newaxis, :]), axis=1))
        delta = max([delta, np.abs(v[s] - v_new[s])])

    v = np.copy(v_new)

    # Assuming a deterministic policy, so pi in Z^num_states instead of pi in [0, 1]^(num_states * num_actions)
    pi = np.zeros(num_states, dtype=np.int32)
    for s in range(num_states):
        pi[s] = np.argmax(np.sum(T[s, :, :] * (R[s, :, :] + gamma * v[np.newaxis, :]), axis=1))

    return pi, v