<repo_name>prakashabhinav/Data-Science<file_sep>/10. UC - Practice - Python Conditional Probability.py
# -*- coding: utf-8 -*-
"""
Udemy Course - Conditional Probability : 06-Sep-2019
"""
# Conditional Probability - Way to measure the relationship of two things happening  in relation to each other
# Conditional Probability - If we have two events that depend on each other, what's the probability that both will occur
# Conditional Probability - Notation : P(A,B) is the probability of A and B both occurring independent of each other
# Conditional Probability - Notation : P(B|A) is the probability of B given that A has occurred
# Conditional Probability - P(B|A) = P(A,B) / P(A)

# Conditional Probability - Example
# Code to create some fake data on how much stuff people purchase given their age range.
# It generates 100,000 random "people" and randomly assigns them as being in their 20's, 30's, 40's, 50's, 60's, or 70's.
# It then assigns a lower probability for young people to buy stuff.
# In the end, we have two Python dictionaries:
# "totals" contains the total number of people in each age group.
# "purchases" contains the total number of things purchased by people in each age group.
# The grand total of purchases is in totalPurchases, and we know the total number of people is 100,000.

# Import required libraries
from numpy import random                           # Imports random method from numpy

# Setting the random seed
random.seed(0)                                     # Setting the random seed to generate random numbers
totals = {20:0, 30:0, 40:0, 50:0, 60:0, 70:0}      # Creating dictionary for random totals
purchases = {20:0, 30:0, 40:0, 50:0, 60:0, 70:0}   # Creating dictionary for random purchases
totalPurchases = 0                                 # Initializing totalPurchases
# random.random() function                         # Generates random float uniformly in the semi-open range [0.0, 1.0)
# random.choice() function                         # Returns a random element from the non-empty sequence like from a list, Set, Dictionary
for _ in range(100000):                            # Loop to generate random values
    ageDecade = random.choice([20, 30, 40, 50, 60, 70])  # Sorting 100,000 age values into different age groups
    purchaseProbability = float(ageDecade) / 100.0       # Deciding probability based on the age groups
    totals[ageDecade] += 1                               # Taking total of age groups
    if (random.random() < purchaseProbability):          # Comparing probabilities by generating random probabilities through random
        totalPurchases += 1                              # Taking total of purchases
        purchases[ageDecade] += 1                        # Taking total of purchases in a decade

# Display variable values
totals                                                   # Display the total population in each age group (decade)

purchases                                                # Display the total purchases in each age group (decade)

totalPurchases                                           # Display the total purchases

<file_sep>/5. UC - Practice - Python Probability Density and Mass Function.py
# -*- coding: utf-8 -*-
"""
Udemy Course - Probability Density Function and Probability Mass Function : 30-Aug-2019
"""
# Probability Density Function and Probability Mass Function
# Probability Density Function : For Continuous Data, an example is the Normal distribution
# Probability Density Function : Gives the probability of a data point falling within some given range of a given value
# Normal Distribution : -1 SD to +1 SD : 68.2 Percent
# Normal Distribution : -2 SD to +2 SD : 95.4 Percent
# Normal Distribution : -3 SD to +3 SD : 99.6 Percent
# Probability Mass Function : For Discrete Data
# Probability Density Function : Gives the probability of a data point falling within some given range of a given value
# Normal Distribution : -1 SD to +1 SD : 68.2 Percent
# Normal Distribution : -2 SD to +2 SD : 95.4 Percent
# Normal Distribution : -3 SD to +3 SD : 99.6 Percent

# Probability Density Functions
# Distributions for Probability Density Functions
# Import required libraries
import numpy as np                                 # Makes calculation of Mean, Median, Mode, Standard Deviation, Variance
import matplotlib.pyplot as plt                    # Makes graphs for visualisation

# Uniform Distribution - Flat, constant probability 
# Uniform Distribution - Equal chance of a value occuring within a given range
# Uniform Distribution Probability Distribution Function
# Generate random numbers with uniform distribution - Set 1
values = np.random.uniform(-10.0, 10.0, 100000)    # Generate random numbers with uniform distribution

# Segment the income data into 50 buckets, and plot it as a histogram
plt.hist(values, 50)                               # Segments the data into 50 buckets
plt.show()                                         # Display the histogram

# Calculate Standard Deviation
values.std()                                       # Display the standard deviation

# Calculate Variance 
values.var()                                       # Display the variance

# Generate random numbers with uniform distribution - Set 2
values = np.random.uniform(100.0, 50.0, 100000)    # Generate random numbers with uniform distribution

# Segment the income data into 50 buckets, and plot it as a histogram
plt.hist(values, 50)                               # Segments the data into 50 buckets
plt.show()                                         # Display the histogram

# Calculate Standard Deviation
values.std()                                       # Display the standard deviation

# Calculate Variance 
values.var()                                       # Display the variance

# Normal / Gaussian Distribution - Bell curve PDF
# Normal / Gaussian Disribution Probability Distribution Function
# Import required libraries
from scipy.stats import norm                       # Provides pdf function to create probability density function for normal distribution
import matplotlib.pyplot as plt                    # Makes graphs for visualisation

# Generate random numbers in the specified range with a step value of 0.001 - Set 1
x = np.arange(-3, 3, 0.001)                        # Generate random numbers within -3 to + 3 with an increment of 0.001

# Plot the normal distribution curve
plt.plot(x, norm.pdf(x))                           # Display the normal distribution curve

# Generate random numbers in the specified range with a step value of 0.001 - Set 2
x = np.arange(-5, 15, 0.001)                       # Generate random numbers within -5 to + 15 with an increment of 0.001

# Plot the normal distribution curve
plt.plot(x, norm.pdf(x))                           # Display the normal distribution curve

# Normal Distribution Probability Distribution Function
# Generate random numbers with normal distribution, "mu" is the desired mean and "sigma" is the standard deviation - Set 1
# Import required libraries
import numpy as np                                 # Makes calculation of Mean, Median, Mode, Standard Deviation, Variance
import matplotlib.pyplot as plt                    # Makes graphs for visualisation

mu = 5.0                                           # Desired mean value
sigma = 2.0                                        # Desired standard deviation value

values = np.random.normal(mu, sigma, 10000)        # Generate random numbers with the provided mean and standard deviation values

# Segment the income data into 50 buckets, and plot it as a histogram
plt.hist(values, 50)                               # Segments the data into 50 buckets
plt.show()                                         # Display the histogram    
# Curve will not be the actual normal distribution curve as the numbers are still random

# Generate random numbers with normal distribution, "mu" is the desired mean and "sigma" is the standard deviation - Set 2
mu = 15.0                                          # Desired mean value
sigma = 50.0                                       # Desired standard deviation value

values = np.random.normal(mu, sigma, 10000)        # Generate random numbers with the provided mean and standard deviation values

# Segment the income data into 50 buckets, and plot it as a histogram
plt.hist(values, 50)                               # Segments the data into 50 buckets
plt.show()                                         # Display the histogram    
# Curve will not be the actual normal distribution curve as the numbers are still random

# Exponential Probability Distribution Function / Power Law - Falls off in an exponential manner, 
# Exponential Probability Distribution Function / Power Law - Probability Very likely for something to happen near Zero
# Exponential Probability Distribution Function / Power Law - Probability drops off as wemove away from Zero
# Import required libraries
from scipy.stats import expon                      # Provides pdf function to create probability density function for exponential dist
import matplotlib.pyplot as plt                    # Makes graphs for visualisation

# Generate random numbers in the specified range with a step value of 0.001 - Set 1
x = np.arange(0, 10, .001)                         # Generate random numbers within 0 to + 10 with an increment of 0.001

# Plot the exponential distribution curve
plt.plot(x, expon.pdf(x))                          # Display the exponential distribution curve
# Exponential curve from 0 to 10 on X-Axis with probability 1 near Zero

# Generate random numbers in the specified range with a step value of 0.001 - Set 2
x = np.arange(5, 10, .001)                         # Generate random numbers within + 5 to + 10 with an increment of 0.001

# Plot the exponential distribution curve
plt.plot(x, expon.pdf(x))                          # Display the exponential distribution curve
# Exponential curve from 5 to 10 on X-Axis

# Generate random numbers in the specified range with a step value of 0.001 - Set 3
x = np.arange(-5, 0, .001)                         # Generate random numbers within - 5 to 0 with an increment of 0.001

# Plot the exponential distribution curve
plt.plot(x, expon.pdf(x))                          # Display the exponential distribution curve
# No Exponential curve from - 5 to 0 on X-Axis, only a straight line with Zero probability

# Generate random numbers in the specified range with a step value of 0.001 - Set 4
x = np.arange(-5, 5, .001)                         # Generate random numbers within - 5 to + 5 with an increment of 0.001

# Plot the exponential distribution curve
plt.plot(x, expon.pdf(x))                          # Display the exponential distribution curve
# No Exponential curve from - 5 to 0 on X-Axis, only a straight line with Zero probability
# No Exponential curve at 0 on X-Axis, only a straight line with One probability
# Exponential curve from 0 to + 5 on X-Axis

# Probability Mass Functions - Deal with discrete data
# Distributions for Probability Mass Functions
# Binomial Distribution Probability Mass Function
# Import required libraries
from scipy.stats import binom                      # Provides pmf function to create probability mass function for binomial dist     
import matplotlib.pyplot as plt                    # Makes graphs for visualisation

# Generate random numbers in the specified range with a step value of 0.001 - Set 1
x = np.arange (0, 10, 0.001)                       # Generate random numbers within 0 to + 10 with an increment of 0.001

# Shape parameters for creating the plot
n, p = 10, 0.5                                     # Shape parameters for the binomial distribution curve

# Plot the binomial distribution curve
plt.plot(x, binom.pmf(x, n, p))                    # Display the binomial distribution curve

# Generate random numbers in the specified range with a step value of 0.001 - Set 2
x = np.arange (0, 10, 0.001)                       # Generate random numbers within 0 to + 10 with an increment of 0.001

# Shape parameters for creating the plot
n, p = 20, 0.5                                     # Shape parameters for the binomial distribution curve

# Plot the binomial distribution curve
plt.plot(x, binom.pmf(x, n, p))                    # Display the binomial distribution curve

# Poisson Distribution Probability Mass Function
# Poisson Distribution - If we have some average number of things for a given period
# Poisson Distribution - Gives a way to predict other values for another given day
# Poisson Distribution - Example : A website gets on average 500 visits per day, what's the odds of getting 550
# Import required libraries
from scipy.stats import poisson                    # Provides pmf function to create probability mass function for binomial dist     
import matplotlib.pyplot as plt                    # Makes graphs for visualisation 

# Generate random numbers in the specified range with a step value of 0.05 - Set 1
x = np.arange (400, 600, 0.5)                      # Generate random numbers within 400 to 500 with an increment of 0.5

# Average number of visits per day
mu = 500                                           # Average value to be used in the Poisson Distribution

# Plot the poisson distribution curve
plt.plot(x, poisson.pmf(x, mu))                    # Display the poisson distribution curve


# Generate random numbers in the specified range with a step value of 0.05 - Set 2
x = np.arange (400, 1600, 0.5)                     # Generate random numbers within 400 to 1600 with an increment of 0.5

# Average number of visits per day
mu = 800                                           # Average value to be used in the Poisson Distribution

# Plot the poisson distribution curve
plt.plot(x, poisson.pmf(x, mu))                    # Display the poisson distribution curve

# Generate random numbers in the specified range with a step value of 0.05 - Set 3
x = np.arange (400, 1600, 0.5)                     # Generate random numbers within 400 to 1600 with an increment of 0.5

# Average number of visits per day
mu = 1200                                          # Average value to be used in the Poisson Distribution

# Plot the poisson distribution curve
plt.plot(x, poisson.pmf(x, mu))                    # Display the poisson distribution curve

# Generate random numbers in the specified range with a step value of 0.05 - Set 4
x = np.arange (-400, 1600, 0.5)                    # Generate random numbers within -400 to 1600 with an increment of 0.5

# Average number of visits per day
mu = 200                                           # Average value to be used in the Poisson Distribution

# Plot the poisson distribution curve
plt.plot(x, poisson.pmf(x, mu))                    # Display the poisson distribution curve

# Generate random numbers in the specified range with a step value of 0.05 - Set 5
x = np.arange (400, 500, 0.5)                      # Generate random numbers within 400 to 500 with an increment of 0.5

# Average number of visits per day
mu = 450                                           # Average value to be used in the Poisson Distribution

# Plot the poisson distribution curve
plt.plot(x, poisson.pmf(x, mu))                    # Display the poisson distribution curve

## End of Practice ##<file_sep>/2. UC - Practice - Python Functions.py
# -*- coding: utf-8 -*-
"""
Udemy Course - Python Functions : 27-May-2019
"""
# Functions - To repeat a set of operations multiple times with different parameters
# Declared using def keyword
# No braces or brackets to specify the function, only indentation using white spaces

# Declaring a function to give the square of a number
def SquareIt(x):
    return x * x

# Calling the function to return the square of the number passed
print(SquareIt(2))
print(SquareIt(-4))

# Declaring a function to give the cube of a number
def CubeIt(x):
    return x * x * x

# Calling the function to return the square of the number passed
print(CubeIt(2))
print(CubeIt(-2))

# Functions - Pass functions around as parameters
# Declaring the function to pass function as parameters
def DoSomething(f, x):
    return f(x)

# Calling the function to pass function as parameters
print(DoSomething(SquareIt, 3))

# Declaring the function to pass function as parameters
def DoSomethingMore(f, x, f1, y):
    return (f(x),f1(y))
    
# Calling the function to pass function as parameters
print(DoSomethingMore(SquareIt, 3, CubeIt, 3))

# Functions - Lambda function allows us to inline simple functions
# Changes functionality of function at run time
# DoSomething function accepts a function in first parameter
# Lambda defines an unnamed function
print(DoSomething(lambda x: x * x * x, 3))
print(DoSomething(lambda x: x * x * x, -2))
print(DoSomething(lambda x: x, -2))

print(DoSomethingMore(lambda x: x * x, 2, lambda y: y * y * y, 2))
print(DoSomethingMore(lambda x: x * x, -2, lambda y: y * y * y, -2))
print(DoSomethingMore(lambda x: x * x, -2, lambda y: y * y * y, -3))

# Boolean Expressions
# == : Equals
# is : Equals (Python Specific)
# True
# False

# Examples
print(1 == 3)

print(2 == 2)

print(True or False)

print(True and False)

print(True and True)

print(1 is 3)

print(2 is 2)

print(True is False)

print(True or True)

# Another example
if 1 is 3:
    print("How did that happen?")
elif 1 > 3:
    print("Yikes")
else:
    print("All is well with the world")

# Another example
if 3 is 3:
    print("How did that happen?")
elif 1 > 3:
    print("Yikes")
else:
    print("All is well with the world")

# Another example
if 4 is 3:
    print("How did that happen?")
elif 4 > 3:
    print("Yikes")
else:
    print("All is well with the world")
    
# Looping - Repeat a set of operations for specific number of times
# Examples

# Print numbers in range using the Range function
for x in range(10):    # Prints number in the range of 0 to 9
    print(x)    

# Print numbers in range showing the use of Continue and Break statements
for x in range(10):
    if (x is 1):
        continue
    if (x > 5):
        break
    print(x)

# Use of while statement
x = 0
while (x < 10):
    print(x)
    x += 1    
    
# Activity - Write some code that creates a list of integers, loops through each element of the list, and only prints out even numbers 
listOfNumbers = [1, 2, 3, 16, 17, 4, 5, 6, 22, 26, 7, 8, 9, 10, 11, 12]
for number in listOfNumbers:
    if (number % 2 == 0):
        print(number)
    else:
        continue
print ("All done.")

## End of Practice ##<file_sep>/9. UC - Practice - Python Covariance and Correlation.py
# -*- coding: utf-8 -*-
"""
Udemy Course - Covariance and Correlation : 31-Aug-2019
"""
# Covariance and Correlation - Ways to measure whether two attributes are related to each other in a dataset
# Covariance - Measures how two variables vary in tandem from their means
# Covariance - Interpreting covariance is hard, so we use correlation
# Covariance - Magnitude which matters, +ve or -ve just shows the type of covariance
# Correlation - Divide the covariance by the standard deviations of both variables and that normalizes the things
# Correlation - Correlation of - 1 : Perfect inverse correlation
# Correlation - Correlation of 0   : No correlation
# Correlation - Correlation of + 1 : Perfect correlation
# Correlation does not imply causation,it is used to decide what experiments to conduct


# Covariance and Correlation
# Covariance withour using the numpy methods
# Import required libraries
import numpy as np                                 # Makes calculation of Mean, Median, Mode, Standard Deviation, Variance
import matplotlib.pyplot as plt                    # Makes graphs for visualisation

# Define the function to calculate the deviations from mean for a set of values
def de_mean(x):                                    # Function to calculate the deviation from mean
    xmean = np.mean(x)                             # Calculating the mean
    return[xi - xmean for xi in x]                 # Return the list with the deviations form the mean
    
# Define the function to calculate the covariance
def covariance(x, y):                              # Function to calculate the covariance
    n = len(x)                                     # Calculate the length of the list 
    return np.dot(de_mean(x), de_mean(y)) / (n- 1) # Calculating the covariance

pageSpeeds = np.random.normal(3.0, 1.0, 1000)      # Generate random numbers for pageSpeeds with normal distribution; mu = 3.0, sigma = 1.0
purchaseAmount = np.random.normal(50.0, 10.0, 1000)  # Generate random numbers for purchaseAmount with normal dist.; mu = 50, sigma = 10

plt.scatter(pageSpeeds, purchaseAmount)            # Create scatter plot for pageSpeeds and purchaseAmount

covariance(pageSpeeds, purchaseAmount)             # Calculate covariance using the user defined function to calculate covariance
    
# We will now try to introduce a relationship between purchaseAmount and pageSpeeds and induce a correlation
# It will show a negative value meaning that pages that render in less time result in more money spent
purchaseAmount = np.random.normal(50.0, 10.0, 1000) / pageSpeeds  # Introducing -ve relationship between purchaseAmounts and pageSpeeds

plt.scatter(pageSpeeds, purchaseAmount)            # Create scatter plot for pageSpeeds and purchaseAmount which shows a relationship

covariance(pageSpeeds, purchaseAmount)             # Calculate covariance using the user defined function to show the covariance

# Numpy can be used to do this easily using the function numpy.cov
# numpy.cov returns a matrix of the covariance values between every combination of the array values
np.cov(pageSpeeds, purchaseAmount)                 # Gives covariance value similar to the values returned by the function

# Covariance value or Magnitude which matters, +ve or -ve just shows the type of covariance
# Covariance is sensitive to units used in the variables, which makes it difficult to interpret
# Correlation normalizes everything by using the standard deviations
# Correlation gives us easier values that range from -1 (Perfect InverseCorrelation) to +1 (Perfect Correlation)

# Define the function to calculate the correlation
def correlation(x, y):                             # Function to calculate the covariance
    stddevx = x.std()                              # Calculate the standard deviation of x values
    stddevy = y.std()                              # Calculate the standard deviation of x values
    return covariance(x, y) / stddevx / stddevy    # Check for divide by zero here and then return the correlation value 

correlation(pageSpeeds, purchaseAmount)            # Calculate correlation using the user defined function to calculate correlation

# Numpy can be used to do this easily using the function numpy.corrcoef
# numpy.corrcoef returns a matrix of the correlation coefficients between every combination of the array values
np.corrcoef(pageSpeeds, purchaseAmount)            # Gives correlation value similar to the values returned by the function

# Show positive relationship, covariance and correlation
purchaseAmount = np.random.normal(50.0, 10.0, 1000) * pageSpeeds  # Introducing +ve relationship between purchaseAmounts and pageSpeeds

plt.scatter(pageSpeeds, purchaseAmount)            # Create scatter plot for pageSpeeds and purchaseAmount which shows a relationship

covariance(pageSpeeds, purchaseAmount)             # Calculate covariance using the user defined function to show the covariance

np.cov(pageSpeeds, purchaseAmount)                 # Gives covariance value similar to the values returned by the function

correlation(pageSpeeds, purchaseAmount)            # Calculate correlation using the user defined function to calculate correlation

np.corrcoef(pageSpeeds, purchaseAmount)            # Gives correlation value similar to the values returned by the function

# Check for a perfect correlation by fabricating a totally linear relationship : -ve correlation
purchaseAmount = 100 - pageSpeeds * 3              # Introducing a linear relationship between purchaseAmounts and pageSpeeds

plt.scatter(pageSpeeds, purchaseAmount)            # Create scatter plot for pageSpeeds and purchaseAmount which shows a relationship

correlation(pageSpeeds, purchaseAmount)            # Calculate correlation using the user defined function to calculate correlation

np.corrcoef(pageSpeeds, purchaseAmount)            # Gives correlation value similar to the values returned by the function

# Check for a perfect correlation by fabricating a totally linear relationship : +ve correlation
purchaseAmount = 100 + pageSpeeds * 3              # Introducing a linear relationship between purchaseAmounts and pageSpeeds

plt.scatter(pageSpeeds, purchaseAmount)            # Create scatter plot for pageSpeeds and purchaseAmount which shows a relationship

correlation(pageSpeeds, purchaseAmount)            # Calculate correlation using the user defined function to calculate correlation

np.corrcoef(pageSpeeds, purchaseAmount)            # Gives correlation value similar to the values returned by the function

## End of Practice ##<file_sep>/7. UC - Practice - Python Moments.py
# -*- coding: utf-8 -*-
"""
Udemy Course - Moments : 30-Aug-2019
"""
# Moments - Quantitative measures of the shape of a probability density function
# First moment of a dataset  : Mean
# Second moment of a dataset : Variance
# Third moment of a dataset  : Skew - How lopsided is the distribution
# Skew     - Distribution with a longer tail on the left will be skewed left and has a negative skewness
# Skew     - Distribution with a longer tail on the right will be skewed right and has a positive skewness
# Fourth moment of a dataset : Kurtosis - How thick is the tail and how sharp is the peak compared to a normal distribution
# Kurtosis - Higher peak value has a higher kurtosis, measures how peaked is the data

# Moments Examples
# Import required libraries
import numpy as np                                 # Makes calculation of Mean, Median, Mode, Standard Deviation, Variance
import matplotlib.pyplot as plt                    # Makes graphs for visualisation

# Generate random numbers with normal distribution - Set 1
vals = np.random.normal(0, 0.5, 10000)             # Generate random numbers with normal distribution; mu = 0, sigma = 0.5

# Segment the income data into 50 buckets, and plot it as a histogram
plt.hist(vals, 50)                                 # Segments the data into 50 buckets
plt.show()                                         # Display the histogram    

# First moment of a dataset  : Mean
np.mean(vals)                                      # Gives the first moment - Mean

# Second moment of a dataset : Variance
np.var(vals)                                       # Gives the second moment - Variance

# Third moment of a dataset  : Skew
# Import required libraries
import scipy.stats as sp                           # Makes calculations of Skewness and Kurtosis
sp.skew(vals)                                      # Gives the third moment - Skew
# As the data is centered around Zero, it will also be Zero

# Fourth moment of a dataset : Kurtosis
sp.kurtosis(vals)                                  # Gives the fourth moment - Kurtosis
# As it describes the shape of the tail, it will be Zero for a normal distribution

# Generate random numbers with normal distribution - Set 2
vals = np.random.normal(10, 2.5, 10000)            # Generate random numbers with normal distribution; mu = 10, sigma = 2.5

# Segment the income data into 50 buckets, and plot it as a histogram
plt.hist(vals, 50)                                 # Segments the data into 50 buckets
plt.show()                                         # Display the histogram    

# First moment of a dataset  : Mean
np.mean(vals)                                      # Gives the first moment - Mean

# Second moment of a dataset : Variance
np.var(vals)                                       # Gives the second moment - Variance

# Third moment of a dataset  : Skew
# Import required libraries
import scipy.stats as sp                           # Makes calculations of Skewness and Kurtosis
sp.skew(vals)                                      # Gives the third moment - Skew
# As the data is centered around Zero, it will also be Zero

# Fourth moment of a dataset : Kurtosis
sp.kurtosis(vals)                                  # Gives the fourth moment - Kurtosis
# As it describes the shape of the tail, it will be Zero for a normal distribution

# Generate random numbers with uniform distribution - Set 3
vals = np.random.uniform(-10.0, 10.0, 10000)       # Generate random numbers with uniform distribution

# Segment the income data into 50 buckets, and plot it as a histogram
plt.hist(vals, 50)                                 # Segments the data into 50 buckets
plt.show()                                         # Display the histogram

# First moment of a dataset  : Mean
np.mean(vals)                                      # Gives the first moment - Mean

# Second moment of a dataset : Variance
np.var(vals)                                       # Gives the second moment - Variance

# Third moment of a dataset  : Skew
# Import required libraries
import scipy.stats as sp                           # Makes calculations of Skewness and Kurtosis
sp.skew(vals)                                      # Gives the third moment - Skew

# Fourth moment of a dataset : Kurtosis
sp.kurtosis(vals)                                  # Gives the fourth moment - Kurtosis

# Generate random numbers in the specified range with a step value of 0.001 - Set 4
vals = np.arange(-4, 14, 0.001)                    # Generate random numbers within -3 to + 3 with an increment of 0.001

# Plot the normal distribution curve
# Import required libraries
from scipy.stats import norm                       # Provides pdf function to create probability density function for normal distribution
import matplotlib.pyplot as plt                    # Makes graphs for visualisation

plt.plot(vals, norm.pdf(vals))                     # Display the normal distribution curve

# First moment of a dataset  : Mean
np.mean(vals)                                      # Gives the first moment - Mean

# Second moment of a dataset : Variance
np.var(vals)                                       # Gives the second moment - Variance

# Third moment of a dataset  : Skew
# Import required libraries
import scipy.stats as sp                           # Makes calculations of Skewness and Kurtosis
sp.skew(vals)                                      # Gives the third moment - Skew

# Fourth moment of a dataset : Kurtosis
sp.kurtosis(vals)                                  # Gives the fourth moment - Kurtosis

## End of Practice ##<file_sep>/4. UC - Practice - Python Std Dev and Variance.py
# -*- coding: utf-8 -*-
"""
Udemy Course - Python Basics Std Dev and Var : 28-May-2019
"""
# Standard Deviation and Variance
# Variance - Measures how "spread out" the data is
# Variance - Average of the squared differences from the mean
# Standard Deviation - Square root of the variance
# Variance and Standard Deviation - Helps to identify outliers
# Population Mean : μ = ( Σ Xi ) / N
# Population Variance : σ2
# Sample Mean : M = ( Σ Xi ) / N
# Population and Sample 
# Population Variance uses N, Sample Variance uses N- 1

# Import required libraries
import numpy as np                                 # Makes calculation of Mean, Median, Mode, Standard Deviation, Variance
import matplotlib.pyplot as plt                    # Makes graphs for visualisation

# Generate random income numbers - Set 1
incomes = np.random.normal(100.0, 20.0, 10000)     # Generate random income numbers

# Segment the income data into 50 buckets, and plot it as a histogram
plt.hist(incomes, 50)                              # Segments the data into 50 buckets
plt.show()                                         # Display the histogram    

# Calculate Standard Deviation
incomes.std()                                      # Display the standard deviation

# Calculate Variance 
incomes.var()                                      # Display the variance

# Generate random income numbers - Set 2
incomes = np.random.normal(100.0, 50.0, 10000)     # Generate random income numbers

# Segment the income data into 50 buckets, and plot it as a histogram
plt.hist(incomes, 50)                              # Segments the data into 50 buckets
plt.show()                                         # Display the histogram    

# Calculate Standard Deviation
incomes.std()                                      # Display the standard deviation

# Calculate Variance 
incomes.var()                                      # Display the variance

# Generate random income numbers - Set 3
incomes = np.random.normal(100.0, 75.0, 10000)     # Generate random income numbers

# Segment the income data into 50 buckets, and plot it as a histogram
plt.hist(incomes, 50)                              # Segments the data into 50 buckets
plt.show()                                         # Display the histogram    

# Calculate Standard Deviation
incomes.std()                                      # Display the standard deviation

# Calculate Variance 
incomes.var()                                      # Display the variance

## End of Practice ##<file_sep>/1. UC - Practice - Python Basics.py
# -*- coding: utf-8 -*-
"""
Udemy Course - Python Basics : 26-May-2019
"""
# For Loop example
listOfNumbers = [1, 2, 3, 4, 5, 6]
for number in listOfNumbers:
    print(number)
    if (number % 2 == 0):
        print("is even")
    else:
        print("is odd")
print ("All done.")

# Importing Modules 
# Importing numpy package (Numpy ~ Numeric Python)
import numpy as np

# Generating random numbers : Calling random function and generating the normal distribution
a = np.random.normal(25.0, 5.0, 10)
print (a)

b = np.random.normal(5.0, 25.0, 10)
print (b)

# Working with Lists
# Lists are mutable : Add items to list and rearrange items
# Square brackets [ ] indicate a python list 
# We can add any type of data to a list
x = [1,2,3,4,5,6]
print (len(x))

y = [1,2,3,4,5,6,'a']
print (len(y))

# List - Slicing and Dicing
# Select anything before element number 3
x[:3]

# Select anything before element number 4
x[:4]

# Select anything after element number 3
x[3:]

# Select anything after element number 4
x[4:]

# Select two elements from last element
x[-2:]

# Select all elements from last two elements
x[:-2]

# Add more elements to the existing list in the form of list
x.extend([7,8])
x

# Add more element to the existing list in the form of single character
x.append(9)
x

x.append([10,11])
x

# Adding a list to an existing list : Merges both the lists and creates a new list
y = [12, 13, 14]
listOfLists = [x,y]                 # Merges both the lists
listOfLists                         # Shows both the lists througn the merged list

# Select second element of list y
y[1]

# Select second element of list listOfLists
listOfLists[1]

# Sorting Lists - Ascending Order
z = [3 ,2 , 1]
z.sort()
z

# Sorting Lists - Ascending Order : Another example
zs = [1,3,5,7,9,2,4,6,8,10]
zs.sort()
zs

# Sorting Lists - Descending Order
z = [3 ,2 , 1]
z.sort(reverse=True)
z

# Sorting Lists - Descending Order : Another example
zs = [1,3,5,7,9,2,4,6,8,10]
zs.sort(reverse=True)
zs

# Working with Tuples
# Tuples are immutable : We can not change them, they are what they are
# Round brackets ( ) indicate a python tuple 
x = (1,2,3)                       # Defining a tuple
len(x)                            # Display the length of a tuple

y = (4,5,6)                       # Defining a tuple
y[0]                              # Display first element of a tuple - Count starts from 0
y[1]                              # Display second element of a tuple - Count starts from 0
y[2]                              # Display third element of a tuple - Count starts from 0
y[3]                              # Gives index out of range error

# Creating a list from tuples
listofTuples = [ x , y ]          # Defining a list of tuples
listofTuples                      # Display the list of tuples

# Moving values to a tuple from an input data stream
# Split the input data stream based on a delimiter with the help of split function
(age, income) = "32,12000".split(',')  # Defining an input data stream and split it by split function
print(age)                             # Display the first element
print(income)                          # Display the second element

# Working with Dictionary
# Like a map or hash table in other languages, like a mini database
# Key value mapping datastore
# Round brackets { } indicate a python dictionary
captains = {}                          # Defining a blank dictionary
captains["Enterprise"] = "Kirk"        # Defining dictionary element
captains["Enterprise D"] = "Picard"    # Defining dictionary element
captains["Deep Space Nine"] = "Sisko"  # Defining dictionary element
captains["Voyager"] = "Janeway"        # Defining dictionary element

print(captains["Voyager"])             # Display dictionary element

print(captains.get("Enterprise"))      # Display dictionary element - Another way
print(captains.get("Voyager"))         # Display dictionary element - Another way

print(captains.get("NX-01"))           # Display error message

# Using for loop to display all elements of the dictionary
for ship in captains:                  # For loop
    print(ship + ": " + captains[ship])# Display the dictionary elements
    
## End of Practice ##<file_sep>/8. UC - Practice - Python Matplotlib.py
# -*- coding: utf-8 -*-
"""
Udemy Course - Matplotlib : 30-Aug-2019
"""
# Matplotlib - Used to create graphs and visualize different types of data in different ways
# Matplotlib - Chooses different colors automatically for different plots

# Import required libraries
from scipy.stats import norm                       # Provides pdf function to create probability density function for normal distribution
import numpy as np                                 # Makes calculation of Mean, Median, Mode, Standard Deviation, Variance
import matplotlib.pyplot as plt                    # Makes graphs for visualisation

# Draw a line graph
# Generate random numbers in the specified range with a step value of 0.001 - Set 1
x = np.arange(-3, 3, 0.001)                        # Generate random numbers within -3 to + 3 with an increment of 0.001

# Plot the normal distribution curve
plt.plot(x, norm.pdf(x))                           # Display the normal distribution curve
plt.show()                                         # Display the plot 

# Multiple plots on one graph - Set 1
plt.plot(x, norm.pdf(x))                           # Display the first normal distribution curve
plt.plot(x, norm.pdf(x, 1.0, 0.5))                 # Display the second normal distribution curve
plt.show()                                         # Display the plot

# Multiple plots on one graph - Set 2
plt.plot(x, norm.pdf(x))                           # Display the first normal distribution curve
plt.plot(x, norm.pdf(x, 1.0, 0.5))                 # Display the second normal distribution curve
plt.plot(x, norm.pdf(x, 1.0, 0.25))                # Display the third normal distribution curve
plt.show()                                         # Display the plot

# Saving the plots on a file - Set 1
plt.plot(x, norm.pdf(x))                           # Display the first normal distribution curve
plt.plot(x, norm.pdf(x, 1.0, 0.5))                 # Display the second normal distribution curve
plt.savefig("C:/Abhinav/Study Material/Data Science/Udemy/Practice/firstplot.png",format='png')   # Save the plots on a .png file
plt.savefig("C:/Abhinav/Study Material/Data Science/Udemy/Practice/firstplot.jpg",format='jpg')   # Save the plots on a .jpg file

# Saving the plots on a file - Set 2
plt.plot(x, norm.pdf(x))                           # Display the first normal distribution curve
plt.plot(x, norm.pdf(x, 1.0, 0.5))                 # Display the second normal distribution curve
plt.plot(x, norm.pdf(x, 1.0, 0.25))                # Display the third normal distribution curve
plt.savefig("C:/Abhinav/Study Material/Data Science/Udemy/Practice/secondplot.png",format='png')   # Save the plots on a .png file
plt.savefig("C:/Abhinav/Study Material/Data Science/Udemy/Practice/secondplot.jpg",format='jpg')   # Save the plots on a .jpg file

# Adjust the axes - Set 1
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 5])                             # Setting the limit for x axis 
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])                  # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
plt.plot(x, norm.pdf(x))                           # Display the first normal distribution curve
plt.plot(x, norm.pdf(x, 1.0, 0.5))                 # Display the second normal distribution curve
plt.show()                                         # Display the plot

# Adjust the axes - Set 2
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 8])                             # Setting the limit for x axis - Extends the axis
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])                  # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
plt.plot(x, norm.pdf(x))                           # Display the first normal distribution curve
plt.plot(x, norm.pdf(x, 1.0, 0.5))                 # Display the second normal distribution curve
plt.show()                                         # Display the plot

# Adjust the axes - Set 3
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 8])                             # Setting the limit for x axis - Extends the axis
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 7])         # Setting the parameters for ticks on the x axis - Extends the ticks
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
plt.plot(x, norm.pdf(x))                           # Display the first normal distribution curve
plt.plot(x, norm.pdf(x, 1.0, 0.5))                 # Display the second normal distribution curve
plt.show()                                         # Display the plot

# Adjust the axes - Set 4
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 2])                             # Setting the limit for x axis - Leads to truncation
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2])     # Setting the parameters for ticks on the x axis - Leads to truncation
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
plt.plot(x, norm.pdf(x))                           # Display the first normal distribution curve
plt.plot(x, norm.pdf(x, 1.0, 0.5))                 # Display the second normal distribution curve
plt.show()                                         # Display the plot

# Adjust the axes - Set 5
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 5])                             # Setting the limit for x axis
axes.set_ylim([0, 0.5])                            # Setting the limit for y axis - Leads to truncation
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 ])         # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5])                    # Setting the parameters for ticks on the y axis - Leads to truncation
plt.plot(x, norm.pdf(x))                           # Display the first normal distribution curve
plt.plot(x, norm.pdf(x, 1.0, 0.5))                 # Display the second normal distribution curve
plt.show()                                         # Display the plot

# Adding the grid
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 5])                             # Setting the limit for x axis 
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])                  # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
axes.grid()                                        # Display the gridlines
plt.plot(x, norm.pdf(x))                           # Display the first normal distribution curve
plt.plot(x, norm.pdf(x, 1.0, 0.5))                 # Display the second normal distribution curve
plt.show()                                         # Display the plot

# Change line type and colors of the plot - Set 1
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 5])                             # Setting the limit for x axis 
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])                  # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
axes.grid()                                        # Display the gridlines
plt.plot(x, norm.pdf(x), 'b-')                     # Display the first normal distribution curve with solid line in blue color
plt.plot(x, norm.pdf(x, 1.0, 0.5), 'r:')           # Display the second normal distribution curve with dotted line in red color
plt.show()                                         # Display the plot 

# Change line type and colors of the plot - Set 2
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 5])                             # Setting the limit for x axis 
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])                  # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
axes.grid()                                        # Display the gridlines
plt.plot(x, norm.pdf(x), 'b-')                     # Display the first normal distribution curve with solid line in blue color
plt.plot(x, norm.pdf(x, 1.0, 0.5), 'g--')          # Display the second normal distribution curve with double dotted line in green color
plt.show()                                         # Display the plot 

# Change line type and colors of the plot - Set 3
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 5])                             # Setting the limit for x axis 
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])                  # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
axes.grid()                                        # Display the gridlines
plt.plot(x, norm.pdf(x), 'b-')                     # Display the first normal distribution curve with solid line in blue color
plt.plot(x, norm.pdf(x, 1.0, 0.5), 'g-.')          # Display the second normal distribution curve with slash and dotted line in green color
plt.show()                                         # Display the plot

# Change line type and colors of the plot - Set 4
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 5])                             # Setting the limit for x axis 
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])                  # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
axes.grid()                                        # Display the gridlines
plt.plot(x, norm.pdf(x), 'b--')                    # Display the first normal distribution curve with double dotted line in blue color
plt.plot(x, norm.pdf(x, 1.0, 0.5), 'g-.')          # Display the second normal distribution curve with slash and dotted line in green color
plt.show()                                         # Display the plot

# Labelling axes and Adding Legends - Position : Top Right
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 5])                             # Setting the limit for x axis 
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])                  # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
axes.grid()                                        # Display the gridlines
plt.xlabel('X-Axis')                               # Display the label for X-Axis
plt.ylabel('Y-Axis')                               # Display the label for X-Axis
plt.plot(x, norm.pdf(x), 'b--')                    # Display the first normal distribution curve with double dotted line in blue color
plt.plot(x, norm.pdf(x, 1.0, 0.5), 'g-.')          # Display the second normal distribution curve with slash and dotted line in green color
plt.legend(['Blue','Green'], loc = 0)              # Display the legend for plot type, 0 is for Top Right
plt.show()                                         # Display the plot

# Labelling axes and Adding Legends - Position : Top Left
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 5])                             # Setting the limit for x axis 
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])                  # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
axes.grid()                                        # Display the gridlines
plt.xlabel('X-Axis')                               # Display the label for X-Axis
plt.ylabel('Y-Axis')                               # Display the label for X-Axis
plt.plot(x, norm.pdf(x), 'b--')                    # Display the first normal distribution curve with double dotted line in blue color
plt.plot(x, norm.pdf(x, 1.0, 0.5), 'g-.')          # Display the second normal distribution curve with slash and dotted line in green color
plt.legend(['Blue','Green'], loc = 2)              # Display the legend for plot type, 2 is for Top Left
plt.show()                                         # Display the plot

# Labelling axes and Adding Legends - Position : Bottom Left
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 5])                             # Setting the limit for x axis 
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])                  # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
axes.grid()                                        # Display the gridlines
plt.xlabel('X-Axis')                               # Display the label for X-Axis
plt.ylabel('Y-Axis')                               # Display the label for X-Axis
plt.plot(x, norm.pdf(x), 'b--')                    # Display the first normal distribution curve with double dotted line in blue color
plt.plot(x, norm.pdf(x, 1.0, 0.5), 'g-.')          # Display the second normal distribution curve with slash and dotted line in green color
plt.legend(['Blue','Green'], loc = 3)              # Display the legend for plot type, 3 is for Bottom Left
plt.show()                                         # Display the plot

# Labelling axes and Adding Legends - Position : Bottom Right
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 5])                             # Setting the limit for x axis 
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])                  # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
axes.grid()                                        # Display the gridlines
plt.xlabel('X-Axis')                               # Display the label for X-Axis
plt.ylabel('Y-Axis')                               # Display the label for X-Axis
plt.plot(x, norm.pdf(x), 'b--')                    # Display the first normal distribution curve with double dotted line in blue color
plt.plot(x, norm.pdf(x, 1.0, 0.5), 'g-.')          # Display the second normal distribution curve with slash and dotted line in green color
plt.legend(['Blue','Green'], loc = 4)              # Display the legend for plot type, 4 is for Bottom Right
plt.show()                                         # Display the plot

# Labelling axes and Adding Legends - Position : Middle Right
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 5])                             # Setting the limit for x axis 
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])                  # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
axes.grid()                                        # Display the gridlines
plt.xlabel('X-Axis')                               # Display the label for X-Axis
plt.ylabel('Y-Axis')                               # Display the label for X-Axis
plt.plot(x, norm.pdf(x), 'b--')                    # Display the first normal distribution curve with double dotted line in blue color
plt.plot(x, norm.pdf(x, 1.0, 0.5), 'g-.')          # Display the second normal distribution curve with slash and dotted line in green color
plt.legend(['Blue','Green'], loc = 5)              # Display the legend for plot type, 5 is for Middle Right
plt.show()                                         # Display the plot

# Labelling axes and Adding Legends - Position : Middle Left
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 5])                             # Setting the limit for x axis 
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])                  # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
axes.grid()                                        # Display the gridlines
plt.xlabel('X-Axis')                               # Display the label for X-Axis
plt.ylabel('Y-Axis')                               # Display the label for X-Axis
plt.plot(x, norm.pdf(x), 'b--')                    # Display the first normal distribution curve with double dotted line in blue color
plt.plot(x, norm.pdf(x, 1.0, 0.5), 'g-.')          # Display the second normal distribution curve with slash and dotted line in green color
plt.legend(['Blue','Green'], loc = 6)              # Display the legend for plot type, 6 is for Middle Left
plt.show()                                         # Display the plot

# Labelling axes and Adding Legends - Position : Middle Bottom
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 5])                             # Setting the limit for x axis 
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])                  # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
axes.grid()                                        # Display the gridlines
plt.xlabel('X-Axis')                               # Display the label for X-Axis
plt.ylabel('Y-Axis')                               # Display the label for X-Axis
plt.plot(x, norm.pdf(x), 'b--')                    # Display the first normal distribution curve with double dotted line in blue color
plt.plot(x, norm.pdf(x, 1.0, 0.5), 'g-.')          # Display the second normal distribution curve with slash and dotted line in green color
plt.legend(['Blue','Green'], loc = 8)              # Display the legend for plot type, 8 is for Middle Bottom
plt.show()                                         # Display the plot

# Labelling axes and Adding Legends - Position : Top Middle
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 5])                             # Setting the limit for x axis 
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])                  # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
axes.grid()                                        # Display the gridlines
plt.xlabel('X-Axis')                               # Display the label for X-Axis
plt.ylabel('Y-Axis')                               # Display the label for X-Axis
plt.plot(x, norm.pdf(x), 'b--')                    # Display the first normal distribution curve with double dotted line in blue color
plt.plot(x, norm.pdf(x, 1.0, 0.5), 'g-.')          # Display the second normal distribution curve with slash and dotted line in green color
plt.legend(['Blue','Green'], loc = 9)              # Display the legend for plot type, 9 is for Top Middle
plt.show()                                         # Display the plot

# Labelling axes and Adding Legends - Position : Middle Middle
axes = plt.axes()                                  # Mapping the axes
axes.set_xlim([-5, 5])                             # Setting the limit for x axis 
axes.set_ylim([0, 1.0])                            # Setting the limit for y axis
axes.set_xticks([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])                  # Setting the parameters for ticks on the x axis
axes.set_yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])   # Setting the parameters for ticks on the y axis
axes.grid()                                        # Display the gridlines
plt.xlabel('X-Axis')                               # Display the label for X-Axis
plt.ylabel('Y-Axis')                               # Display the label for X-Axis
plt.plot(x, norm.pdf(x), 'b--')                    # Display the first normal distribution curve with double dotted line in blue color
plt.plot(x, norm.pdf(x, 1.0, 0.5), 'g-.')          # Display the second normal distribution curve with slash and dotted line in green color
plt.legend(['Blue','Green'], loc = 10)             # Display the legend for plot types 10 is for Middle Middle
plt.show()                                         # Display the plot

# XKCD Style - Try Again
plt.xkcd()                                         # Function to put matplotlib in XKCD mode
fig = plt.figure()                                 # Function to plot the figure
ax = fig.add_subplot(1, 1, 1)                      # Adding the subplot to the figure
ax.spines['right'].set_color('none')               # Setting the position and color
ax.spines['top'].set_color('none')                 # Setting the position and color
plt.xticks([])                                     # Setting the parameters for ticks on the x axis
plt.yticks([])                                     # Setting the parameters for ticks on the x axis
ax.set_ylim([-30, 10])                             # Setting the limit for y axis

# Creating the data to be displayed
data = np.ones(100)                                # Creating the data to be displayed
data[70:] -= np.arange(30)                         # Creating the data to be displayed

# Preparing the text to be displayed/annotated
# plt.annotate('This is my\n first\n XKCD Plot', xy(70, 1), arrowprops=dict(arrowstyle='->'), xytext=(-15,10))

plt.plot(data)                                     # Plotting the graph with the data

plt.xlabel('Time')                                 # Display the label for X-Axis
plt.xlabel('My Practice')                          # Display the label for Y-Axis

# Pie Chart- Set 1
plt.rcdefaults()                                   # Return to default as we had set the plotting to XKCD mode in the previous example
values = [12, 55, 4, 32, 14]                       # Setting the percentage values to be shown on the pie chart
colors = ['r', 'g', 'b', 'c', 'm']                 # Setting the color values to be shown on the pie chart
explode = [0, 0, 0.2, 0, 0]                        # Setting the percentage value to explode and come out
labels = ['India', 'United States', 'Russia', 'China', 'Europe']      # Setting the label values to be shown on the pie chart
plt.pie(values, colors = colors, labels = labels, explode = explode)  # Creating the pie chart
plt.title('Pie Chart')                             # Setting the pie chart title to be shown on the pie chart
plt.show()                                         # Display the plot

# Pie Chart- Set 2
plt.rcdefaults()                                   # Return to default as we had set the plotting to XKCD mode in the previous example
values = [12, 55, 4, 32, 14]                       # Setting the percentage values to be shown on the pie chart
colors = ['r', 'g', 'b', 'c', 'm']                 # Setting the color values to be shown on the pie chart
explode = [0.5, 0, 0.2, 0, 0]                      # Setting the percentage value to explode and come out
labels = ['India', 'United States', 'Russia', 'China', 'Europe']      # Setting the label values to be shown on the pie chart
plt.pie(values, colors = colors, labels = labels, explode = explode)  # Creating the pie chart
plt.title('Pie Chart')                             # Setting the pie chart title to be shown on the pie chart
plt.show()                                         # Display the plot

# Bar Chart - Set 1
values = [12, 55, 4, 32, 14]                       # Setting the values to be shown on the bar chart
colors = ['r', 'g', 'b', 'c', 'm']                 # Setting the color values to be shown on the bar chart
plt.bar(range(0, 5), values, color = colors) # Creating the bar chart
plt.show()                                         # Display the plot

# Bar Chart - Set 2
values = [12, 55, 4, 32, 14]                       # Setting the values to be shown on the bar chart
colors = ['r', 'g', 'b', 'c', 'm']                 # Setting the color values to be shown on the bar chart
plt.bar(range(0, 5), values, color = colors)       # Creating the bar chart
plt.title('Bar Chart')                             # Setting the bar chart title to be shown on the bar chart
plt.show()                                         # Display the plot

# Scatter Plot - Set 1
X = np.random.randn(500)                           # Generate random distribution of numbers to be plotted
Y = np.random.randn(500)                           # Generate random distribution of numbers to be plotted
plt.scatter(X,Y)                                   # Creating the scatter plot
plt.show()                                         # Display the plot

# Scatter Plot - Set 2
X = np.random.randn(750)                           # Generate random distribution of numbers to be plotted
Y = np.random.randn(750)                           # Generate random distribution of numbers to be plotted
plt.scatter(X,Y)                                   # Creating the scatter plot
plt.xlabel('X-Axis')                               # Display the label for X-Axis
plt.ylabel('Y-Axis')                               # Display the label for X-Axis
plt.title('Scatter Plot')                          # Setting the scatter plot title to be shown on the scatter plot
plt.show()                                         # Display the plot

# Histogram - Set 1
incomes = np.random.normal(27000, 15000, 10000)    # Generate random numbers with normal distribution; mu = 27000, sigma = 15000
plt.hist(incomes, 50)                              # Segments the data into 50 buckets
plt.xlabel('X-Axis')                               # Display the label for X-Axis
plt.ylabel('Y-Axis')                               # Display the label for X-Axis
plt.title('Histogram')                             # Setting the histogram title to be shown on the histogram
plt.show()                                         # Display the histogram    

# Histogram - Set 2
incomes = np.random.normal(57000, 25000, 50000)    # Generate random numbers with normal distribution; mu = 27000, sigma = 15000
plt.hist(incomes, 50)                              # Segments the data into 50 buckets
plt.xlabel('X-Axis')                               # Display the label for X-Axis
plt.ylabel('Y-Axis')                               # Display the label for X-Axis
plt.title('Histogram')                             # Setting the histogram title to be shown on the histogram
plt.show()                                         # Display the histogram    

# Box and Whisker Plot
# Box and Whisker Plot - Used for visualizing the spread and skewness of the data
# Box represents the interquartile range and contains half of the data
# Middle line represents the Median of the data and the box represents the bounds of the first and third quartile
# Dotted line (Whiskers) indicate the range of the data except for the outliers which are plotted outside the whiskers
# Outliers are 1.5 times more than the interquartile range

# Box and Whisker Plot - Set 1
uniformSkewed = np.random.rand(100) * 100 - 40     # Generate random numbers for box plot
high_outliers = np.random.rand(10) * 50 + 100      # Generate random number outliers for box plot 
low_outliers = np.random.rand(10) * 50 - 100       # Generate random number outliers for box plot  
data = np.concatenate((uniformSkewed, high_outliers, low_outliers))   # Prepare the data to be plotted
plt.boxplot(data)                                  # Creating the box plot
plt.xlabel('X-Axis')                               # Display the label for X-Axis
plt.ylabel('Y-Axis')                               # Display the label for X-Axis
plt.title('Box and Whisker')                       # Setting the Box and Whisker title title to be shown on the plot
plt.show()                                         # Display the histogram

# Box and Whisker Plot - Set 2
uniformSkewed = np.random.rand(200) * 100 - 40     # Generate random numbers for box plot
high_outliers = np.random.rand(20) * 50 + 100      # Generate random number outliers for box plot 
low_outliers = np.random.rand(20) * 50 - 100       # Generate random number outliers for box plot  
data = np.concatenate((uniformSkewed, high_outliers, low_outliers))   # Prepare the data to be plotted
plt.boxplot(data)                                  # Creating the box plot
plt.xlabel('X-Axis')                               # Display the label for X-Axis
plt.ylabel('Y-Axis')                               # Display the label for X-Axis
plt.title('Box and Whisker')                       # Setting the Box and Whisker title title to be shown on the plot
plt.show()                                         # Display the histogram

## End of Practice ##<file_sep>/6. UC - Practice - Python Percentiles.py
# -*- coding: utf-8 -*-
"""
Udemy Course - Percentiles : 30-Aug-2019
"""
# Percentile - The point in a dataset, at which X% of the values are less than that value. Example : Income Distribution
# Percentiles in a Normal Distribution - Q1, Q2, Q3, Q4
# Percentiles in a Normal Distribution - IQR(Inter Quartile Range) is Q3 - Q1
# Percentiles in a Normal Distribution - IQR holds 50% of the data. 25% on the left of Median and 25% on the right of Median
# Percentiles in a Normal Distribution - Total Range : Q1 - 1.5 * IQR to Q3 + 1.5 * IQR

# Percentiles Examples
# Import required libraries
import numpy as np                                 # Makes calculation of Mean, Median, Mode, Standard Deviation, Variance
import matplotlib.pyplot as plt                    # Makes graphs for visualisation

# Generate random numbers with normal distribution - Set 1
vals = np.random.normal(0, 0.5, 10000)             # Generate random numbers with normal distribution; mu = 0, sigma = 0.5

# Segment the income data into 50 buckets, and plot it as a histogram
plt.hist(vals, 50)                                 # Segments the data into 50 buckets
plt.show()                                         # Display the histogram    

# Calculate Percentile Values using numpy functions
# 50th Percentile
np.percentile(vals, 50)                            # Gives value at 50th percentile which is the Median

# Compute the median
np.median(vals)                                    # Display the median of random values

# 90th Percentile
np.percentile(vals, 90)                            # Gives value at 90th percentile

# 20th Percentile
np.percentile(vals, 20)                            # Gives value at 20th percentile

# 75th Percentile
np.percentile(vals, 75)                            # Gives value at 75th percentile which is the Q3

# 25th Percentile
np.percentile(vals, 25)                            # Gives value at 25th percentile which is the Q1

# Generate random numbers with normal distribution - Set 2
vals = np.random.normal(5, 10, 10000)              # Generate random numbers with normal distribution; mu = 5, sigma = 10

# Segment the income data into 50 buckets, and plot it as a histogram
plt.hist(vals, 50)                                 # Segments the data into 50 buckets
plt.show()                                         # Display the histogram    

# Calculate Percentile Values using numpy functions
# 50th Percentile
np.percentile(vals, 50)                            # Gives value at 50th percentile which is the Median

# Compute the median
np.median(vals)                                    # Display the median of random values

# 90th Percentile
np.percentile(vals, 90)                            # Gives value at 90th percentile

# 20th Percentile
np.percentile(vals, 20)                            # Gives value at 20th percentile

# 75th Percentile
np.percentile(vals, 50)                            # Gives value at 75th percentile which is the Q3

# 25th Percentile
np.percentile(vals, 25)                            # Gives value at 25th percentile which is the Q1

# Generate random numbers with normal distribution - Set 3
vals = np.random.normal(-5, 10, 10000)             # Generate random numbers with normal distribution; mu = -5, sigma = 10

# Segment the income data into 50 buckets, and plot it as a histogram
plt.hist(vals, 50)                                 # Segments the data into 50 buckets
plt.show()                                         # Display the histogram    

# Calculate Percentile Values using numpy functions
# 50th Percentile
np.percentile(vals, 50)                            # Gives value at 50th percentile which is the Median

# Compute the median
np.median(vals)                                    # Display the median of random values

# 90th Percentile
np.percentile(vals, 90)                            # Gives value at 90th percentile

# 20th Percentile
np.percentile(vals, 20)                            # Gives value at 20th percentile

# 75th Percentile
np.percentile(vals, 50)                            # Gives value at 75th percentile which is the Q3

# 25th Percentile
np.percentile(vals, 25)                            # Gives value at 25th percentile which is the Q1

# Generate random numbers with normal distribution - Set 4
vals = np.random.normal(-50, 10, 100)              # Generate random numbers with normal distribution; mu = -50, sigma = 10

# Segment the income data into 50 buckets, and plot it as a histogram
plt.hist(vals, 50)                                 # Segments the data into 50 buckets
plt.show()                                         # Display the histogram    

# Calculate Percentile Values using numpy functions
# 50th Percentile
np.percentile(vals, 50)                            # Gives value at 50th percentile which is the Median

# Compute the median
np.median(vals)                                    # Display the median of random values

# 90th Percentile
np.percentile(vals, 90)                            # Gives value at 90th percentile

# 20th Percentile
np.percentile(vals, 20)                            # Gives value at 20th percentile

# 75th Percentile
np.percentile(vals, 50)                            # Gives value at 75th percentile which is the Q3

# 25th Percentile
np.percentile(vals, 25)                            # Gives value at 25th percentile which is the Q1

## End of Practice ##<file_sep>/3. UC - Practice - Python Basic Stats.py
# -*- coding: utf-8 -*-
"""
Udemy Course - Python Basic Stats : 27-May-2019
"""
# Types of Data : Numerical, Categorical, Ordinal
# Numerical : Discrete, Continuous
# Categorical : Yes/No, Male/Female
# Ordinal : Mix of Numerical and Categorical Data
# Mean, Median, and Mode
# Mean   : Average value, Sum of values / Number of samples
# Median : Sort the values and take the value at midpoint. If there are even number of values, take the average of two values in the middle
# Median : Less susceptible to outliers than the mean
# Mode   : Most common value in a dataset with discrete values
# Mean vs. Median

# Create sample income data, centered around 27,000 with a normal distribution and standard deviation of 15,000, with 10,000 data points
# Compute the mean (average) - it should be close to 27,000

# Import required libraries
import numpy as np                                 # Makes calculation of Mean, Median and Mode easy
import matplotlib.pyplot as plt                    # Makes graphs for visualisation
from scipy import stats                            # Used for stats mode function

# Generate random income numbers
# import numpy as np                               # Makes calculation of Mean, Median and Mode easy
incomes = np.random.normal(27000, 15000, 10000)    # Generate random income numbers
len(incomes)                                       # Display count of random income numbers
np.mean(incomes)                                   # Display mean of random income numbers

# Segment the income data into 50 buckets, and plot it as a histogram
# import matplotlib.pyplot as plt                  # Makes graphs for visualisation - Used in beginning of code
plt.hist(incomes, 50)                              # Segments the data into 50 buckets
plt.show()                                         # Display the histogram

# Compute the median
np.median(incomes)                                 # Display the median of random income numbers
# Since we have a normal distribution, Mean and Median and pretty close and near to each other

# Now, we add an outlier
incomes = np.append(incomes, [1000000000])         # Adding outlier to bring skewness in the data 

np.median(incomes)                                 # Display the median of random income numbers with skewness
np.mean(incomes)                                   # Display mean of random income numbers with skewness
# Adding an outlier changes the Mean drastically, where as Median remains almost the same
# Thus, when there are outliers in the data or the data is skewed, Median should be used

# Mode
# Generate sample age data for 500 people
ages = np.random.randint(18, high=90, size=500)    # Generate random age data
ages                                               # Display random age data 

# from scipy import stats                          # Used for stats mode function - Used in the beginning of code
stats.mode(ages)                                   # Display the mode for sample age data

# Exercise: Mean & Median Customer Spend
custspend = np.random.normal(100.0, 20.0, 10000)   # Generate random customer spend numbers
len(custspend)                                     # Display count of random customer spend numbers
np.mean(custspend)                                 # Display mean of random customer spend numbers

# Segment the customer spend data into 50 buckets, and plot it as a histogram
# import matplotlib.pyplot as plt                  # Makes graphs for visualisation - Used in beginning of code
plt.hist(custspend, 50)                              # Segments the data into 50 buckets
plt.show()                                         # Display the histogram

# Compute the median
np.median(custspend)                               # Display the median of random customer spend numbers
# Since we have a normal distribution, Mean and Median and pretty close and near to each other

# Now, we add an outlier
custspend = np.append(custspend, [100000])         # Adding outlier to bring skewness in the data 

np.median(custspend)                               # Display the median of random customer spend numbers with skewness
np.mean(custspend)                                 # Display mean of random customer spend numbers with skewness
# Adding an outlier changes the Mean drastically, where as Median remains almost the same
# Thus, when there are outliers in the data or the data is skewed, Median should be used

## End of Practice ##