# This Python 3 environment comes with many helpful analytics libraries installed
# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python
# For example, here's several helpful packages to load

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
# Input data files are available in the read-only "../input/" directory
# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory

import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))

# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using "Save & Run All" 
# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session
"""
# Load
"""
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize']=[20,8]
import seaborn as sns
from sklearn.model_selection import train_test_split

##
df=pd.read_csv('../input/student-study-hour-v2/Student Study Hour V2.csv')
display(df.shape)
display(df.corr())
display(df.describe())

##
# Let consider 50% of Score as pass mark
pass_mark=df.Scores.quantile(0.5)
df['Pass']=(df.Scores>pass_mark).astype(int)
plt.title('Student Studies',fontsize=24)
plt.xlabel('Hours',fontsize=24)
plt.ylabel('Pass',fontsize=24)
sns.scatterplot(data=df,x='Hours',y='Pass',hue="Pass",style="Pass",size="Pass",sizes=(200, 200))
y=df['Pass']
X=df['Hours']
"""
# Changing slope and  y-intercept in Each Try (Hours Vs Pass)
"""
"""
# Gradient descent Try-1

delta_m=1

delta_b=1

m=m+delta_m

b=b+delta_b

"""
def gradient_descent(all_X,all_y,m,b):
    total_error=0;y_pred=list()
    for x,y in zip(all_X,all_y):
        y_predict=m*x+b
        y_pred.append(y_predict)
        error=y_predict-y
        total_error+=error
        delta_m=1
        delta_b=1
        m=m+delta_m
        b=b+delta_b
    return m,b,y_pred,total_error
# m=0;b=0
# m,b,y_pred,total_error=gradient_descent(X,y,m,b)
# print('m:',m,'b:',b,'total_error:',total_error)
# plt.title('Student Studies',fontsize=24)
# plt.xlabel('Hours',fontsize=24)
# plt.ylabel('Pass',fontsize=24)
# sns.scatterplot(x=X,y=y_pred)
# plt.plot(X,y_pred)
plt.title('Student Studies',fontsize=24)
plt.xlabel('Hours',fontsize=24)
plt.ylabel('Pass',fontsize=24)

m=0;b=0
iteration=20
for i in range(iteration):
    m,b,y_pred,total_error=gradient_descent(X,y,m,b)
    print('m:',m,'b:',b,'total_error:',total_error)
    sns.scatterplot(x=X,y=y_pred)
    plt.plot(X,y_pred)
"""
# Result 1

from above diagram we get that slope and  y-intercept  **Increases Largely**

Point 1 : Delta become constant

Point 2:  On each Step Slope and y-intecept increases by 1 

for

delta_m=1  # constant

delta_b=1

m=m+delta_m

b=b+delta_b
"""
"""
# Gradient descent Try-2

delta_m=error*x

delta_b=error

m=m+delta_m

b=b+delta_b
"""
def gradient_descent(all_X,all_y,m,b):
    total_error=0;y_pred=list()
    for x,y in zip(all_X,all_y):
        y_predict=m*x+b
        y_pred.append(y_predict)
        error=y_predict-y
        total_error+=error
        delta_m=error*x
        delta_b=error
        m=m+delta_m
        b=b+delta_b
    return m,b,y_pred,total_error
plt.title('Student Studies',fontsize=24)
plt.xlabel('Hours',fontsize=24)
plt.ylabel('Pass',fontsize=24)

m=0;b=0
iteration=20
for i in range(iteration):
    m,b,y_pred,total_error=gradient_descent(X,y,m,b)
    print('m:',m,'b:',b,'total_error:',total_error)
    sns.scatterplot(x=X,y=y_pred)
    plt.plot(X,y_pred)
"""
# Result 2

from above diagram we get that slope and  y-intercept  **Negative**

Point 1 : Delta varies dynamic to error and x value

Point 2:  On each Step Slope and y-intecept increases by delta value

for

delta_m=error*x  # result dynamic to x and error

delta_b=error

m=m+delta_m

b=b+delta_b
"""
"""
# Gradient descent Try-2.1

delta_m=error*x

delta_b=error

m=m + delta_m * 0.001
        
b=b + delta_b * 0.001
"""
def gradient_descent(all_X,all_y,m,b):
    total_error=0;y_pred=list()
    for x,y in zip(all_X,all_y):
        y_predict=m*x+b
        y_pred.append(y_predict)
        error=y_predict-y
        total_error+=error
        delta_m=error*x
        delta_b=error
        m=m + delta_m * 0.001
        b=b + delta_b * 0.001
    return m,b,y_pred,total_error

plt.title('Student Studies',fontsize=24)
plt.xlabel('Hours',fontsize=24)
plt.ylabel('Pass',fontsize=24)

m=0;b=0
iteration=20
for i in range(iteration):
    m,b,y_pred,total_error=gradient_descent(X,y,m,b)
    print('m:',m,'b:',b,'total_error:',total_error)
    sns.scatterplot(x=X,y=y_pred)
    plt.plot(X,y_pred)
"""
# Result 2.1

from above diagram we get that slope and  y-intercept  **Negative**

Point 1 : Delta varies dynamic to error and x value

Point 2:  On each Step Slope and y-intecept are increases small by delta value

for

delta_m=error*x  

delta_b=error

m=m + delta_m * 0.001
        
b=b + delta_b * 0.001
"""
"""
# Gradient descent - Correct

delta_m=error*x

delta_b=error

m=m - delta_m * 0.001
        
b=b - delta_b * 0.001
"""
def gradient_descent(all_X,all_y,m,b):
    total_error=0;y_pred=list()
    for x,y in zip(all_X,all_y):
        y_predict=m*x+b
        y_pred.append(y_predict)
        error=y_predict-y
        total_error+=error
        delta_m=error*x
        delta_b=error
        m=m - delta_m * 0.001
        b=b - delta_b * 0.001
    return m,b,y_pred,total_error

plt.title('Student Studies',fontsize=24)
plt.xlabel('Hours',fontsize=24)
plt.ylabel('Pass',fontsize=24)

m=0;b=0
iteration=20
for i in range(iteration):
    m,b,y_pred,total_error=gradient_descent(X,y,m,b)
    print('m:',m,'b:',b,'total_error:',total_error)
    sns.scatterplot(x=X,y=y_pred)
    plt.plot(X,y_pred)
"""
# Conclusion

from above diagram we get that slope and  y-intercept  

Point 1 : Delta varies dynamic to error and x value

Point 2:  On each Step Slope and y-intecept are decrases by small delta value

for
delta_m=error*x

delta_b=error

m=m - delta_m * 0.001
        
b=b - delta_b * 0.001
"""
"""
# Gradient Descent
"""
def gradient_descent(x,y,iteration=30):
    m_current=b_current=0  #intialize m and b 
    learning_rate =0.0004 # step's 
    n=len(x)
    for i in range(iteration):
        y_predict = (m_current*x)+b_current #y=mx+b
        cost = (1/n) * sum( [val**2  for val in ( y-y_predict )]) #https://scikit-learn.org/stable/modules/generated/sklearn.metrics.mean_squared_error.html
        m_next = -(2/n)*sum(x*(y-y_predict)) #(∂/∂m) refer above pic
        b_next = -(2/n)*sum(y-y_predict) #(∂/∂b)
        m_current = m_current - (learning_rate *m_next)
        b_current = b_current - (learning_rate *b_next)
        print("m  {}  ,b  {}  ,cost  {}  ,iteration  {} ".format(m_current,b_current,cost,i))
        sns.scatterplot(x=x,y=y_pred)
        plt.plot(x,y_pred)

plt.title('Student Studies',fontsize=24)
plt.xlabel('Hours',fontsize=24)
plt.ylabel('Pass',fontsize=24)
gradient_descent(X,y)
"""
# 2. II K Means Clustering

1.Convergence

We should take care of Outlier and Intialization of centroid (which randomly selected) may centroid is introduced to be a "far away" point  in K mean 

init='k-means++' > smart way to Intialization of centroid

init{‘k-means++’, ‘random’}, callable or array-like of shape (n_clusters, n_features), default=’k-means++’

K-means++ is the algorithm which is used to overcome the drawback posed by the k-means algorithm.
(likelihood of picking a point as centroid is corresponding to the distance from the closest, recently picked centroid.)

K  > select by elbow method

Take care of all point in one cluster are close together(distance small) and distance between two cluster is large

As it is unsupervised there is no target and we can measure it by silhoutte score 

https://scikit-learn.org/stable/auto_examples/cluster/plot_kmeans_silhouette_analysis.html


![](https://www.unioviedo.es/compnum/labs/new/d1.png)


Silhouette Coefficient 

Silhouette Coefficient or silhouette score is a metric used to calculate the goodness of a clustering technique. Its value ranges from -1 to 1. 

![](https://miro.medium.com/max/712/1*cUcY9jSBHFMqCmX-fp8BvQ.jpeg)

https://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html
"""
"""
**What does Kmeans inertia mean?**

Inertia measures how well a dataset was clustered by K-Means. It is calculated by measuring the distance between each data point and its centroid, squaring this distance, and summing these squares across one cluster. A good model is one with low inertia AND a low number of clusters ( K ).

![](https://editor.analyticsvidhya.com/uploads/62725cluster0.PNG)
"""
"""
# Elbow Method
"""
##
df=pd.read_csv('../input/student-study-hour-v2/Student Study Hour V2.csv')
sns.scatterplot(x='Hours',y='Scores',data=df)

##
from sklearn.cluster import KMeans
indiviual_cluster=[]
num_cluster =5
for i in range(1,num_cluster):
    kmeans = KMeans(n_clusters=i, random_state=0)
    kmeans.fit(df)
    indiviual_cluster.append(kmeans.inertia_)
##    
plt.figure(figsize=(16,5))
plt.plot(range(1,num_cluster),indiviual_cluster)
plt.title('Elbow Method',fontsize=20)
plt.ylabel("Cluster Score",fontsize=14)
plt.xlabel('Number of cluster',fontsize=14)
##
print('indiviual_cluster ',indiviual_cluster)
n_clusters=2
kmeans = KMeans(n_clusters=n_clusters,init='random',random_state=0).fit(df)
pred=kmeans.predict(df)

##
print('predict\n',pred)
df['Cluster']=pd.DataFrame(pred,columns=['cluster'])
##
sns.lmplot(x='Hours',y='Scores',data=df,hue='Cluster')
sns.lmplot(x='Hours',y='Scores',data=df,fit_reg=False,hue='Cluster',legend=True)
##
print('Labels\n',kmeans.labels_,'\ncluster_centers_\n',kmeans.cluster_centers_,'\ninertia_\n',kmeans.inertia_,'in n_clusters ',n_clusters)
"""
# Reference

https://youtu.be/PONM8A7Gwl4

https://youtu.be/vsWrXfO3wWw

https://youtu.be/XtE7hqFsYc4

https://youtu.be/_jg1UFoef1c
"""