#Imports
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
import scipy.stats as ss
In this part we will look at how to generate and plot analytic distributions.
The Python code below generates and plots the PDF and CDF of a normal (Gaussian) distribution whose parameters are mu and sigma.
x = np.linspace(-5, 5, 5000)
mu = 0
sigma = 1
y_pdf = ss.norm.pdf(x, mu, sigma) # the normal pdf
y_cdf = ss.norm.cdf(x, mu, sigma) # the normal cdf
plt.plot(x, y_pdf, label='pdf')
plt.plot(x, y_cdf, label='cdf')
plt.legend();
Next we write code to:
xs = np.linspace(0, 5, 5000)
lambdas=[0.5, 1, 2]
for lamb in lambdas:
y_pdf = [lamb*np.e**(-lamb*x) for x in xs]
plt.plot(x, y_pdf, label= 'lambda= ' + str(lamb))
plt.legend()
for lamb in lambdas:
y_cdf = [1-np.e**(-lamb*x) for x in xs]
plt.plot(x, y_cdf, label= 'lambda= ' + str(lamb))
plt.legend()
The question we are trying to answer in this part is: How well can we model empirical distributions with analytic distributions?
Let's start by asking the question Are the Belmont Stakes results normally distributed?
For context: Since 1926, the Belmont Stakes is a 1.5 mile-long race of 3-year old thoroughbred horses. Secretariat ran the fastest Belmont Stakes in history in 1973. While that was the fastest year, 1970 was the slowest because of unusually wet and sloppy conditions. These two outliers have been removed from the data set, which has been obtained by scraping the Belmont Wikipedia page. (The file belmont.csv is available on Canvas, if you want to learn more about the race's results.)
The code below:
np.random.normal().import numpy as np
import matplotlib.pyplot as plt
belmont_no_outliers = np.array([148.51, 146.65, 148.52, 150.7, 150.42, 150.88, 151.57,
147.54, 149.65, 148.74, 147.86, 148.75, 147.5, 148.26,
149.71, 146.56, 151.19, 147.88, 149.16, 148.82, 148.96,
152.02, 146.82, 149.97, 146.13, 148.1, 147.2, 146.,
146.4, 148.2, 149.8, 147., 147.2, 147.8, 148.2,
149., 149.8, 148.6, 146.8, 149.6, 149., 148.2,
149.2, 148., 150.4, 148.8, 147.2, 148.8, 149.6,
148.4, 148.4, 150.2, 148.8, 149.2, 149.2, 148.4,
150.2, 146.6, 149.8, 149., 150.8, 148.6, 150.2,
149., 148.6, 150.2, 148.2, 149.4, 150.8, 150.2,
152.2, 148.2, 149.2, 151., 149.6, 149.6, 149.4,
148.6, 150., 150.6, 149.2, 152.6, 152.8, 149.6,
151.6, 152.8, 153.2, 152.4, 152.2])
def ecdf(data):
"""Compute ECDF for a one-dimensional array of measurements."""
# Number of data points: n
n = len(data)
# x-data for the ECDF: x
x = np.sort(data)
# y-data for the ECDF: y
y = np.arange(1, n + 1) / n
return x, y
# Seed random number generator
np.random.seed(42)
# Compute mean and standard deviation: mu, sigma
mu = np.mean(belmont_no_outliers)
sigma = np.std(belmont_no_outliers)
# Sample out of a normal distribution with this mu and sigma: samples
samples = np.random.normal(mu, sigma, 10000)
# Get the CDF of the samples and of the data
x_theor, y_theor = ecdf(samples)
x, y = ecdf(belmont_no_outliers)
# Plot the CDFs and show the plot
_ = plt.plot(x_theor, y_theor)
_ = plt.plot(x, y, marker='.', linestyle='none')
plt.margins(0.02)
_ = plt.xlabel('Belmont winning time (sec.)')
_ = plt.ylabel('CDF')
plt.show()
The empirical distribution of the data looks normally distributed.
Other plots that could be used to represent this data are a histogram, or a normal probability plot, which provides a linear representation of normally distributed data.
Let's try to answer the question: What are the chances of a horse matching or beating Secretariat's record?
Assuming that the Belmont winners' times are Normally distributed (with the 1970 and 1973 years removed), we will attempt to answer the question: What is the probability that the winner of a given Belmont Stakes will run it as fast or faster than Secretariat?
To this end, we will:
np.random.normal() function.mu and standard deviation sigma from the belmont_no_outliers array. sec_winners=[]
np.random.seed(50)
# Compute mean and standard deviation: mu, sigma
mu = np.mean(belmont_no_outliers)
sigma = np.std(belmont_no_outliers)
# Sample out of a normal distribution with this mu and sigma: samples
samples = np.random.normal(mu, sigma, 1000000)
for sample in samples:
if sample<=144:
sec_winners.append(sample)
sec_winners=np.array(sec_winners)
print("Probability of beating or tying Secretariat's record: ", len(sec_winners)/len(samples))
Let's investigate whether the speed of light measurements by Michelson are normally distributed.
We will follow a similar sequence of steps as above, namely:
mu and standard deviation sigma from the michelson_speed_of_light array. np.random.normal().x_theor, y_theor and x, y, respectively.For more on Michelson: https://en.wikipedia.org/wiki/Albert_A._Michelson
michelson=pd.read_csv("michelson_speed_of_light.csv")
michelson_speed_of_light=np.array(michelson['velocity of light in air (km/s)'])
# Seed random number generator
np.random.seed(0)
# Compute mean and standard deviation: mu, sigma
mu = np.mean(michelson_speed_of_light)
sigma = np.std(michelson_speed_of_light)
# Sample out of a normal distribution with this mu and sigma: samples
samples = np.random.normal(mu, sigma, 10000)
# Get the CDF of the samples and of the data
x_theor, y_theor = ecdf(samples)
x, y = ecdf(michelson_speed_of_light)
# Plot the CDFs and show the plot
_ = plt.plot(x_theor, y_theor)
_ = plt.plot(x, y, marker='.', linestyle='none')
plt.margins(0.02)
_ = plt.xlabel('Velocity of light in air (km/s)')
_ = plt.ylabel('CDF')
plt.show()
Next, let's turn our attention to baby births.
In the real world, exponential distributions come up when we look at a series of events and measure the times between events, called interarrival times. If the events are equally likely to occur at any time, the distribution of interarrival times tends to look like an exponential distribution.
We will use the dataset from babies_brisbane.csv containing information about the time of birth for 44 babies born in a hospital in Brisbane, Australia, on December 18, 1997, as reported in the local paper.
We will write code to:
lam, since lambda is a reserved word in Python).np.random.exponential().x_theor, y_theor and x, y, respectively.babies_brisbane=pd.read_csv("babies_brisbane.csv")
diffs=babies_brisbane['minutes'].diff()
diffs_mean=np.mean(diffs)
lam=1/diffs_mean
# Seed random number generator
np.random.seed(25)
# Sample out of a normal distribution with this mu and sigma: samples
samples = np.random.exponential(diffs_mean, 10000)
# Get the CDF of the samples and of the data
x_theor, y_theor = ecdf(samples)
x, y = ecdf(diffs)
# Plot the CDFs and show the plot
_ = plt.plot(x_theor, y_theor)
_ = plt.plot(x, y, marker='.', linestyle='none')
plt.margins(0.02)
_ = plt.xlabel('Time between births (min)')
_ = plt.ylabel('CDF')
plt.show()
y_c=1-y
y_theor_c=1-y_theor
_=plt.yscale('log')
_ = plt.plot(x_theor, y_theor_c)
_ = plt.plot(x, y_c, marker='.', linestyle='none')
_ = plt.xlabel('Time between births (min)')
_ = plt.ylabel('CCDF')
plt.show()
Let's revisit the dataset of salaries from the data science stack notebook and use it to measure skewness.
salaries = pd.read_csv('salaries.csv')
salaries.describe()
We write Python code to:
scipy.stats.skewsalary_act=np.array(salaries['earn'])
salary_mean=np.mean(salary_act)
salary_median=np.median(salary_act)
print("Mean of salaries: \n", salary_mean)
print("Median of salaries: ", salary_median)
moment_1=sum(salary_act)/len(salary_act)
print("First raw moment: \n", moment_1)
print("Salary mean: ", salary_mean)
moment_2=ss.moment(salary_act, moment=2)
salary_var=np.std(salary_act)**2
print("Second central moment: \n", moment_2)
print("Salary variance: ", salary_var)
salary_skew=ss.skew(salary_act)
print("Salary skewness: ", salary_skew)
Next we write code to repeat the steps above, this time for male and female employees separately.
male_salaries=[]
female_salaries=[]
for i in range(0, len(salaries)):
if salaries['sex'][i]=='male':
male_salaries.append(salaries['earn'][i])
else:
female_salaries.append(salaries['earn'][i])
male_salaries=np.array(male_salaries)
female_salaries=np.array(female_salaries)
print("Average salary (male): \n", np.mean(male_salaries))
print("Median salary (male): \n", np.median(male_salaries))
print("Average salary (female): \n", np.mean(female_salaries))
print("Median salary (female):", np.median(female_salaries))
moment_1_m=sum(male_salaries)/len(male_salaries)
moment_1_f=sum(female_salaries)/len(female_salaries)
print("First raw moment (male): \n", moment_1_m)
print("Salary mean (male): \n", np.mean(male_salaries))
print("First raw moment (female): \n", moment_1_f)
print("Salary mean (female): ", np.mean(female_salaries))
moment_2_m=ss.moment(male_salaries, moment=2)
moment_2_f=ss.moment(female_salaries, moment=2)
print("Second central moment (male): \n", moment_2_m)
print("Salary variance (male): \n", np.std(male_salaries)**2)
print("Second central moment (female): \n", moment_2_f)
print("Salary variance (female): ", np.std(female_salaries)**2)
salary_skew_m=ss.skew(male_salaries)
salary_skew_f=ss.skew(female_salaries)
print("Salary skew (male): \n", salary_skew_m)
print("Salary skew (female): \n", salary_skew_f)