%pylab inline

# Import libraries
from __future__ import absolute_import, division, print_function

# Ignore warnings
import warnings
warnings.filterwarnings('ignore')

import sys
sys.path.append('tools/')

import numpy as np
import pandas as pd

# Graphing Libraries
import matplotlib.pyplot as pyplt
import seaborn as sns
sns.set_style("white")  

# Configure for presentation
np.set_printoptions(threshold=50, linewidth=50)
import matplotlib as mpl
mpl.rc('font', size=16)

from IPython.display import display

"""
Explanation: Data Sampling
The Normal Approximation for Probability Histograms
End of explanation
"""


trips = pd.read_csv('data/trip.csv')
commute = trips.where(trips.Duration < 1800)

display(trips.head())
display(commute.head())

"""
Explanation: To get our feet wet, let's take a look at the city of San Francisco's bike sharing data
End of explanation
"""


half_hour = 30 * 60 # 30 minutes times 60 seconds
bins = np.arange(1, half_hour+1, 60)

pyplt.rcParams['figure.figsize'] = (4, 3)

commute.hist('Duration', bins=bins, normed=True)
pyplt.ylabel('percent per unit');

def bin_frequency(k):
    bins = np.arange(1, half_hour+1, k)
    commute.hist('Duration', bins=bins, normed=True)

bin_frequency(10)
pyplt.ylabel('percent per unit');

weather = pd.read_csv('data/weather.csv')
sf = weather.where(weather.Zip == 94107)

sf = sf[[0, 1, 3]]
sf.columns = [u'PDT', 'High', 'Low']

sf.head()

def axis_tick_frequency(ax, axis, freq):
    """The frequency of the y axis tick marks
        Attributes
        ----------
        ax: matplotlib axis object
        axis: char eithher 'y' or 'x'
        freq: int, the integer value of which the range moves
    """
    
    if axis == 'y':
        start, end = ax.get_ylim()
        ax.yaxis.set_ticks(np.arange(start, end, freq))
    elif axis == 'x':
        start, end = ax.get_xlim()
        ax.xaxis.set_ticks(np.arange(start, end, freq))
    else:
        raise ValueError('{argument} is not a valid axis object'.format(argument=repr(axis)))

ax = sf[[1,2]].plot.hist(color =['coral','cadetblue'], bins=np.arange(30, 101, 5), normed=True, alpha = 0.5)
pyplt.ylabel('percent per unit')
pyplt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
pyplt.grid(True);

"""
Explanation: For this analysis, we are limiting ourselves to bike sharing trips that are leess that 30 minutes, because that is what comes free as part of the bike sharing program for the city of San Francisco.
We want to draw a histogram of the duration to understand exactly how long trips typically take. 
To do that, I am going to start off making bins of the data. For example, restricting the sampling to every minutes.
End of explanation
"""


sf.where(sf.Low >= 45).Low.count() / 365 * 100

"""
Explanation: Question:

What proportion of days had a low temp greater than 45 degrees?

Answer:
End of explanation
"""


sf['diff'] = sf.High - sf.Low

ax = sf['diff'].plot.hist(bins=np.arange(0, 40, 1))
axis_tick_frequency(ax, 'y', 10)
pyplt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.);


"""
Explanation: Question:

Figure out the distribution of the days of the year where temperature swings betweens the highs and the lows.

Answer:
End of explanation
"""


top = pd.read_csv('data/top_movies.csv')

top.head()

"""
Explanation: Deterministic Sampling
End of explanation
"""


interval = 20
sample = arange(0, len(top), interval)

df = pd.DataFrame()
for i in sample:
    df = df.append(top.ix[i, :])

df

"""
Explanation: Let's make a deterministic sample. We will sample every 20th top grossing movie
End of explanation
"""


start = np.random.choice(np.arange(interval))
sample = np.arange(start, len(top), interval)

df = pd.DataFrame()
for i in sample:
    df = df.append(top.ix[i, :])

df

"""
Explanation: Probability Sampling
A population is the set of all elements from whom a sample will be drawn. A probability sample is one for which it is possible to calculate, before the sample is drawn, the chance with which any subset of elements will enter the sample. In a probability sample, all elements need not have the same chance of being chosen.
Now let's do probabilistic sampling of the top movies. When we do a probabilistic sample like this, we call it a "systematic sample."
End of explanation
"""


def sample(num_sample, top):
    """
    Create a random sample from a table
    
    Attributes
    ---------
    num_sample: int
    top: dataframe
    
    Returns a random subset of table index
    """
    df_index = []

    for i in np.arange(0, num_sample, 1):

        # pick randomly from the whole table
        sample_index = np.random.randint(0, len(top))

        # store index
        df_index.append(sample_index)
        
    return df_index

def sample_no_replacement(num_sample, top):
    """
    Create a random sample from a table
    
    Attributes
    ---------
    num_sample: int
    top: dataframe
    
    Returns a random subset of table index
    """
    df_index = []
    lst = np.arange(0, len(top), 1)

    for i in np.arange(0, num_sample, 1):

        # pick randomly from the whole table
        sample_index = np.random.choice(lst)

        lst = np.setdiff1d(lst,[sample_index])
        df_index.append(sample_index)
            
    return df_index



index_ = sample(35, top)
df = top.ix[index_, :]
df.sort_values(by='Title')

index_ = sample_no_replacement(25, top)
df = top.ix[index_, :]
df.sort_values(by='Title')

"""
Explanation: Question:

What is the probability that the Rush Hour 2 ends up in the sample we have systematically chosen?

Answer:

This is depends solely on the the chance that the first movie chosen from the dataset is The Dark Knight Rises and that has a chance of 1 in 20, i.e., 5%. 

Uniform Sample
A uniform sample is a sample drawn at random without replacements
End of explanation
"""


die = pd.DataFrame()
die["Face"] = [1,2,3,4,5,6]

die

"""
Explanation: Dice
End of explanation
"""


coin = pd.DataFrame()
coin["Face"] = [1,2]
coin

"""
Explanation: Coin
End of explanation
"""


index_ = sample(3, die)
df = die.ix[index_, :]
df

index_ = sample(1, coin)
df = coin.ix[index_, :]
df

def dice_hist(n):
    """Construct histogram of n simulated dice rolls
    
    Attributes
    -----------
    n: int 
    """
    
    if n > 0:
        dice_bins = np.arange(0.5, 7, 1) 
        index_ = sample(n, die)
        df = die.ix[index_, :]
        df.plot.hist(bins=dice_bins, normed=True)
        pyplt.ylabel('percent per unit')
        pyplt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.);
    else:
        raise ValueError('n has to be greater than 0')

        
def dice_sum_hist(n):
    """
    Construct histogram of rolling a pair of dice and plot the sum of the faces
    
    Attributes
    -----------
    num_die: int (number of dice)
    n: int 
    """
    
    if n > 0:
        d1 = np.random.randint(1, 6 + 1, n)
        d2 = np.random.randint(1, 6 + 1, n)
        data = d1 + d2

        bins = np.arange(data.min()-0.5, data.max()+1, 1) 
        pyplt.hist(data, bins=bins, normed=True)
        pyplt.ylabel('percent per unit')
        pyplt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.);
    else:
        raise ValueError('n has to be greater than 0')
        

def dice_prod_hist(n):
    """
    Construct histogram of rolling a pair of dice and plotting the product of the faces.
    
    Attributes
    -----------
    num_die: int (number of dice)
    n: int 
    """
    
    if n > 0:
        d1 = np.random.randint(1, 6 + 1, n)
        d2 = np.random.randint(1, 6 + 1, n)
        data = d1 * d2

        bins = np.arange(data.min()-0.5, data.max()+1, 1) 
        pyplt.hist(data, bins=bins, normed=True)
        pyplt.ylabel('percent per unit')
        pyplt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.);
    else:
        raise ValueError('n has to be greater than 0')
        


"""
Explanation: We can simulate the act of rolling dice by just pulling out rows
End of explanation
"""


low, high = coin.Face.min() - 0.5, coin.Face.max() + 1
bins = np.arange(low, high, 1)

# norm the histogram to give us the density scale
coin.plot.hist(bins=bins, normed=True)
pyplt.ylabel('percent per unit')
pyplt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.);


low, high = die.Face.min() - 0.5, die.Face.max() + 1
bins = np.arange(low, high, 1)

# norm the histogram to give us the density scale
die.plot.hist(bins=bins, normed=True)
pyplt.ylabel('percent per unit')
pyplt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.);


dice_hist(20)

dice_hist(10000)

"""
Explanation: Probability Histogram
End of explanation
"""


n = 100
dice_sum_hist(n)

"""
Explanation: Plot the sum of dices
End of explanation
"""


n = 1000
dice_sum_hist(n)

"""
Explanation: Roll two six sided dice 1000 times and sum the results:
End of explanation
"""


n = 100
dice_prod_hist(n)

"""
Explanation: Gaps in the histogram?
The graph below helps us see that the probability histograms can have gaps. The graph below plots the probability histogram of the product of a pair of dice. The smallest number is 1, while the largest number is 36. Because of the distribution of the die face, we can never get a 7, 11 and so on.
End of explanation
"""


box = pd.DataFrame()
box["Content"] = [1,2,3]

low, high = box.Content.min() - 0.5, box.Content.max() + 1
bins = np.arange(low, high, 1)

box.plot.hist(bins=bins, normed=True)
pyplt.ylabel('percent per unit')
pyplt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.);


def sum_draws( n, box ):
    """
    Construct histogram for the sum of n draws from a box with replacement
    
    Attributes
    -----------
    n: int (number of draws)
    box: dataframe (the box model)
    """
    data = numpy.zeros(shape=(n,1))
    if n > 0:
        for i in range(n):
            index_ = np.random.randint(0, len(box), n)
            df = box.ix[index_, :]
            data[i] = df.Content.sum()

        bins = np.arange(data.min()-0.5, data.max()+1, 1) 
        pyplt.hist(data, bins=bins, normed=True)
        pyplt.ylabel('percent per unit')
        pyplt.xlabel('Number on ticket')
        pyplt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.);
    else:
        raise ValueError('n has to be greater than 0')
    
        


sum_draws(100, box)

box = pd.DataFrame()
box["Content"] = [0,1]

pyplt.rcParams['figure.figsize'] = (4, 3)

low, high = box.Content.min() - 0.5, box.Content.max() + 1
bins = np.arange(low, high, 1) 

box.plot.hist(bins=bins, normed=True)
pyplt.ylabel('percent per unit')
pyplt.xlabel('Number on ticket')
pyplt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.);


sum_draws(10000, box)

"""
Explanation: The scope of the Normal Approximation
End of explanation
"""


