├── .gitignore ├── Chapter01 ├── MarkovChain.py └── MarkovChainMatrix.py ├── Chapter02 ├── GaussianHMM.py └── MultinomialHMM.py ├── Chapter03 ├── backward.py ├── fibonacci.py ├── fibonacci_cache_dict.py ├── fibonacci_cache_list.py ├── forward.py └── viterbi.py ├── Chapter04 ├── coin_mle.py ├── gaussian_mle.py ├── hmmlearn_gaussian.py └── weather.py ├── Chapter06 ├── analyse_data.py ├── get_data.py └── parse_data.py ├── Chapter07 ├── MFCTagger.py ├── hmm_tagger.py └── part_of_speech.py ├── Chapter09 └── mdp.py ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | */data/* 3 | -------------------------------------------------------------------------------- /Chapter01/MarkovChain.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | class MarkovChain(object): 4 | def __init__(self, transition_prob): 5 | """ 6 | Initialize the MarkovChain instance. 7 | 8 | Parameters 9 | ---------- 10 | transition_prob: dict 11 | A dict object representing the transition probabilities in 12 | Markov Chain. Should be of the form: {'state1': {'state1': 13 | 0.1, 'state2': 0.4}, 'state2': {...}} 14 | """ 15 | self.transition_prob = transition_prob 16 | self.states = list(transition_prob.keys()) 17 | 18 | def next_state(self, current_state): 19 | """ 20 | Returns the state of the random variable at the next time 21 | instance. 22 | 23 | Parameters 24 | ---------- 25 | current_state: str 26 | The current state of the system. 27 | """ 28 | return np.random.choice( 29 | self.states, p=[self.transition_prob[current_state][next_state] 30 | for next_state in self.states]) 31 | 32 | def generate_states(self, current_state, no=10): 33 | """ 34 | Generates the next states of the system. 35 | 36 | Parameters 37 | ---------- 38 | current_state: str 39 | The state of the current random variable. 40 | 41 | no: int 42 | The number of future states to generate. 43 | """ 44 | future_states = [] 45 | for i in range(no): 46 | next_state = self.next_state(current_state) 47 | future_states.append(next_state) 48 | current_state = next_state 49 | return future_states 50 | 51 | transition_prob = {'Sunny': {'Sunny': 0.8, 'Rainy': 0.19, 52 | 'Snowy': 0.01}, 53 | 'Rainy': {'Sunny': 0.2, 'Rainy': 0.7, 54 | 'Snowy': 0.1}, 55 | 'Snowy': {'Sunny': 0.1, 'Rainy': 0.2, 56 | 'Snowy': 0.7}} 57 | 58 | weather_chain = MarkovChain(transition_prob=transition_prob) 59 | weather_chain.next_state(current_state='Sunny') 60 | weather_chain.next_state(current_state='Snowy') 61 | weather_chain.generate_states(current_state='Snowy', no=10) 62 | -------------------------------------------------------------------------------- /Chapter01/MarkovChainMatrix.py: -------------------------------------------------------------------------------- 1 | from math import gcd 2 | from itertools import combinations 3 | from functools import reduce 4 | 5 | import numpy as np 6 | 7 | 8 | class MarkovChain(object): 9 | def __init__(self, transition_matrix, states): 10 | """ 11 | Initialize the MarkovChain instance. 12 | 13 | Parameters 14 | ---------- 15 | transition_matrix: 2-D array 16 | A 2-D array representing the probabilities of change of 17 | state in the Markov Chain. 18 | 19 | states: 1-D array 20 | An array representing the states of the Markov Chain. It 21 | needs to be in the same order as transition_matrix. 22 | """ 23 | self.transition_matrix = np.atleast_2d(transition_matrix) 24 | self.states = states 25 | self.index_dict = {self.states[index]: index for index in 26 | range(len(self.states))} 27 | self.state_dict = {index: self.states[index] for index in 28 | range(len(self.states))} 29 | 30 | 31 | def next_state(self, current_state): 32 | """ 33 | Returns the state of the random variable at the next time 34 | instance. 35 | 36 | Parameters 37 | ---------- 38 | current_state: str 39 | The current state of the system. 40 | """ 41 | return np.random.choice( 42 | self.states, 43 | p=self.transition_matrix[self.index_dict[current_state], :]) 44 | 45 | 46 | def generate_states(self, current_state, no=10): 47 | """ 48 | Generates the next states of the system. 49 | 50 | Parameters 51 | ---------- 52 | current_state: str 53 | The state of the current random variable. 54 | 55 | no: int 56 | The number of future states to generate. 57 | """ 58 | future_states = [] 59 | for i in range(no): 60 | next_state = self.next_state(current_state) 61 | future_states.append(next_state) 62 | current_state = next_state 63 | return future_states 64 | 65 | 66 | def is_accessible(self, i_state, f_state, check_up_to_depth=1000): 67 | """ 68 | Check if state f_state is accessible from i_state. 69 | 70 | Parameters 71 | ---------- 72 | i_state: str 73 | The state from which the accessibility needs to be checked. 74 | 75 | f_state: str 76 | The state to which accessibility needs to be checked. 77 | """ 78 | counter = 0 79 | reachable_states = [self.index_dict[i_state]] 80 | for state in reachable_states: 81 | if counter == check_up_to_depth: 82 | break 83 | if state == self.index_dict[f_state]: 84 | return True 85 | else: 86 | reachable_states.extend(np.nonzero(self.transition_matrix[state, :])[0]) 87 | counter = counter + 1 88 | return False 89 | 90 | 91 | def is_irreducible(self): 92 | """ 93 | Check if the Markov Chain is irreducible. 94 | """ 95 | for (i, j) in combinations(self.states, 2): 96 | if not self.is_accessible(i, j): 97 | return False 98 | return True 99 | 100 | 101 | def get_period(self, state, max_number_stps = 50, max_number_trls = 100): 102 | """ 103 | Returns the period of the state in the Markov Chain. 104 | 105 | Parameters 106 | ---------- 107 | state: str 108 | The state for which the period needs to be computed. 109 | """ 110 | initial_state = state 111 | max_number_steps = max_number_stps 112 | max_number_trials = max_number_trls 113 | periodic_lengths = [] 114 | a= [] 115 | 116 | for i in range(1, max_number_steps+1): 117 | for j in range(max_number_trials): 118 | last_states_chain = self.generate_states(current_state=initial_state, no=i)[-1] 119 | if last_states_chain == initial_state: 120 | periodic_lengths.append(i) 121 | break 122 | 123 | if len(periodic_lengths) >0: 124 | a = reduce(gcd, periodic_lengths) 125 | return a 126 | 127 | 128 | def is_aperiodic(self): 129 | """ 130 | Checks if the Markov Chain is aperiodic. 131 | """ 132 | periods = [self.get_period(state) for state in self.states] 133 | for period in periods: 134 | if period != 1: 135 | return False 136 | return True 137 | 138 | 139 | def is_transient(self, state): 140 | """ 141 | Checks if a state is transient or not. 142 | 143 | Parameters 144 | ---------- 145 | state: str 146 | The state for which the transient property needs to be checked. 147 | """ 148 | if np.all(self.transition_matrix[~self.index_dict[state], self.index_dict[state]] == 0): 149 | return True 150 | else: 151 | return False 152 | 153 | def is_absorbing(self, state): 154 | """ 155 | Checks if the given state is absorbing. 156 | 157 | Parameters 158 | ---------- 159 | state: str 160 | The state for which we need to check whether it's absorbing 161 | or not. 162 | """ 163 | state_index = self.index_dict[state] 164 | if self.transition_matrix[state_index, state_index] == 1: 165 | return True 166 | else: 167 | return False 168 | -------------------------------------------------------------------------------- /Chapter02/GaussianHMM.py: -------------------------------------------------------------------------------- 1 | from hmmlearn.hmm import GaussianHMM 2 | 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | 6 | startprob = np.array([0.6, 0.3, 0.1, 0.0]) 7 | 8 | # The transition matrix, note that there are no transitions possible 9 | # between component 1 and 3 10 | transmat = np.array([[0.7, 0.2, 0.0, 0.1], 11 | [0.3, 0.5, 0.2, 0.0], 12 | [0.0, 0.3, 0.5, 0.2], 13 | [0.2, 0.0, 0.2, 0.6]]) 14 | 15 | # The means of each component 16 | means = np.array([[0.0, 0.0], 17 | [0.0, 11.0], 18 | [9.0, 10.0], 19 | [11.0, -1.0]]) 20 | 21 | # The covariance of each component 22 | covars = .5 * np.tile(np.identity(2), (4, 1, 1)) 23 | 24 | # Build an HMM instance and set parameters 25 | model = hmm.GaussianHMM(n_components=4, covariance_type="full") 26 | 27 | # Instead of fitting it from the data, we directly set the estimated 28 | # parameters, the means and covariance of the components 29 | model.startprob_ = startprob 30 | 31 | model.transmat_ = transmat 32 | model.means_ = means 33 | model.covars_ = covars 34 | X, state_sequence = model.sample(n_samples=100) 35 | plt.plot(X[:, 0], X[:, 1], ".-", label="observations", ms=6, 36 | mfc="orange", alpha=0.7) 37 | for i, m in enumerate(means): 38 | plt.text(m[0], m[1], 'Component %i' % (i + 1), 39 | size=12, horizontalalignment='center', 40 | bbox=dict(alpha=.7, facecolor='w')) 41 | plt.legend(loc='best') 42 | plt.show() 43 | -------------------------------------------------------------------------------- /Chapter02/MultinomialHMM.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | class MultinomialHMM: 5 | def __init__(self, num_states, observation_states, prior_probabilities, 6 | transition_matrix, emission_probabilities): 7 | """ 8 | Initialize Hidden Markov Model 9 | Parameters 10 | ----------- 11 | num_states: int 12 | Number of states of latent variable 13 | observation_states: 1-D array 14 | An array representing the set of all observations 15 | prior_probabilities: 1-D array 16 | An array representing the prior probabilities of all the states 17 | of latent variable 18 | transition_matrix: 2-D array 19 | A matrix representing the transition probabilities of change of 20 | state of latent variable 21 | emission_probabilities: 2-D array 22 | A matrix representing the probability of a given observation 23 | given the state of the latent variable 24 | """ 25 | # As latent variables form a Markov chain, we can use 26 | # use the previous defined MarkovChain class to create it 27 | self.latent_variable_markov_chain = MarkovChain( 28 | transition_matrix=transition_matrix, 29 | states=['z{index}'.format(index=index) for index in 30 | range(num_states)], 31 | ) 32 | self.observation_states = observation_states 33 | self.prior_probabilities = np.atleast_1d(prior_probabilities) 34 | self.transition_matrix = np.atleast_2d(transition_matrix) 35 | self.emission_probabilities = np.atleast_2d(emission_probabilities) 36 | 37 | def observation_from_state(self, state): 38 | """ 39 | Generate observation for a given state in accordance with 40 | the emission probabilities 41 | 42 | Parameters 43 | ---------- 44 | state: int 45 | Index of the current state 46 | """ 47 | state_index = self.latent_variable_markov_chain.index_dict[state] 48 | return np.random.choice(self.observation_states, 49 | p=self.emission_probabilities[state_index, :]) 50 | 51 | def generate_samples(self, no=10): 52 | """ 53 | Generate samples from the hidden Markov model 54 | 55 | Parameters 56 | ---------- 57 | no: int 58 | Number of samples to be drawn 59 | 60 | Returns 61 | ------- 62 | observations: 1-D array 63 | An array of sequence of observations 64 | state_sequence: 1-D array 65 | An array of sequence of states 66 | """ 67 | observations = [] 68 | state_sequence = [] 69 | initial_state = np.random.choice( 70 | self.latent_variable_markov_chain.states, 71 | p=self.prior_probabilities) 72 | state_sequence.append(initial_state) 73 | observations.append(self.observation_from_state(initial_state)) 74 | current_state = initial_state 75 | for i in range(2, no): 76 | next_state = self.latent_variable_markov_chain.next_state(current_state) 77 | state_sequence.append(next_state) 78 | observations.append(self.observation_from_state(next_state)) 79 | current_state = next_state 80 | return observations, state_sequence 81 | -------------------------------------------------------------------------------- /Chapter03/backward.py: -------------------------------------------------------------------------------- 1 | def backward(obs, transition, emission, init): 2 | """ 3 | Runs backward algorithm on the HMM. 4 | 5 | Parameters 6 | ---------- 7 | obs: 1D list, array-like 8 | The list of observed states. 9 | 10 | transition: 2D array-like 11 | The transition probability of the HMM. 12 | size = {n_states x n_states} 13 | 14 | emission: 1D array-like 15 | The emission probabiltiy of the HMM. 16 | size = {n_states} 17 | 18 | init: 1D array-like 19 | The initial probability of HMM. 20 | size = {n_states} 21 | 22 | Returns 23 | ------- 24 | float: Probability value for the obs to occur. 25 | """ 26 | n_states = transition.shape[0] 27 | bkw = [{} for t in range(len(obs))] 28 | T = len(obs) 29 | 30 | for y in range(n_states): 31 | bkw[T-1][y] = 1 32 | for t in reversed(range(T-1)): 33 | for y in range(n_states): 34 | bkw[t][y] = sum((bkw[t+1][y1] * transition[y][y1] * emission[obs[t+1]]) for y1 in 35 | range(n_states)) 36 | prob = sum((init[y] * emission[obs[0]] * bkw[0][y]) for y in range(n_states)) 37 | return prob 38 | -------------------------------------------------------------------------------- /Chapter03/fibonacci.py: -------------------------------------------------------------------------------- 1 | def fibonacci(n): 2 | """ 3 | Returns the n-th number in the Fibonacci sequence. 4 | 5 | Parameters 6 | ---------- 7 | n: int 8 | The n-th number in the Fibonacci sequence. 9 | """ 10 | if n <= 1: 11 | return n 12 | else: 13 | return fibonacci(n-1) + fibonacci(n-2) 14 | -------------------------------------------------------------------------------- /Chapter03/fibonacci_cache_dict.py: -------------------------------------------------------------------------------- 1 | cache = {0: 0, 1: 1} # Initialize the first two values. 2 | def fibonacci(n): 3 | """ 4 | Returns the n-th number in the Fibonacci sequence. 5 | 6 | Parameters 7 | ---------- 8 | n: int 9 | The n-th number in the Fibonacci sequence. 10 | """ 11 | try: 12 | return cache[n] 13 | except KeyError: 14 | fib = fibonacci(n-1) + fibonacci(n-2) 15 | cache[n] = fib 16 | return fib 17 | -------------------------------------------------------------------------------- /Chapter03/fibonacci_cache_list.py: -------------------------------------------------------------------------------- 1 | cache = [0, 1] # Initialize with the first two terms of Fibonacci series. 2 | def fibonacci(n): 3 | """ 4 | Returns the n-th number in the Fibonacci sequence. 5 | 6 | Parameters 7 | ---------- 8 | n: int 9 | The n-th number in the Fibonacci sequence. 10 | """ 11 | for i in range(2, n): 12 | cache.append(cache[i-1] + cache[i-2]) 13 | return cache[-1] 14 | -------------------------------------------------------------------------------- /Chapter03/forward.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | transition_matrix = 4 | np.array([[0.33, 0.33, 0, 0, 0, 0.33, 0, 0, 0, 0, 0, 0, 0], 5 | [0.33, 0.33, 0.33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 6 | [ 0, 0.25, 0.25, 0.25, 0, 0, 0.25, 0, 0, 0, 0, 0, 0], 7 | [ 0, 0, 0.33, 0.33, 0.33, 0, 0, 0, 0, 0, 0, 0, 0], 8 | [ 0, 0, 0, 0.33, 0.33, 0, 0, 0.33, 0, 0, 0, 0, 0], 9 | [0.33, 0, 0, 0, 0, 0.33, 0, 0, 0.33, 0, 0, 0, 0], 10 | [ 0, 0, 0.33, 0, 0, 0, 0.33, 0, 0, 0, 0.33, 0, 0], 11 | [ 0, 0, 0, 0, 0.33, 0, 0, 0.33, 0, 0, 0, 0, 0.33], 12 | [ 0, 0, 0, 0, 0, 0.33, 0, 0, 0.33, 0.33, 0, 0, 0], 13 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0.33, 0.33, 0.33, 0, 0], 14 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.33, 0.33, 0.33, 0], 15 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.33, 0.33, 0.33], 16 | [ 0, 0, 0, 0, 0, 0, 0, 0.33, 0, 0, 0, 0.33, 0.33]]) 17 | 18 | emission = np.array([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0]) 19 | 20 | init_prob = np.array([0.077, 0.077, 0.077, 0.077, 0.077, 0.077, 0.077, 21 | 0.077, 0.077, 0.077, 0.077, 0.077, 0.077]) 22 | 23 | def forward(obs, transition, emission, init): 24 | """ 25 | Runs forward algorithm on the HMM. 26 | 27 | Parameters 28 | ---------- 29 | obs: 1D list, array-like 30 | The list of observed states. 31 | 32 | transition: 2D array-like 33 | The transition probability of the HMM. 34 | size = {n_states x n_states} 35 | 36 | emission: 1D array-like 37 | The emission probabiltiy of the HMM. 38 | size = {n_states} 39 | 40 | init: 1D array-like 41 | The initial probability of HMM. 42 | size = {n_states} 43 | 44 | Returns 45 | ------- 46 | float: Probability value for the obs to occur. 47 | """ 48 | n_states = transition.shape[0] 49 | fwd = [{}] 50 | 51 | for i in range(n_states): 52 | fwd[0][y] = init[i] * emission[obs[0]] 53 | for t in range(1, len(obs)): 54 | fwd.append({}) 55 | for i in range(n_states): 56 | fwd[t][i] = sum((fwd[t-1][y0] * transition[y0][i] * emission[obs[t]]) for y0 in 57 | range(n_states)) 58 | prob = sum((fwd[len(obs) - 1][s]) for s in range(n_states)) 59 | return prob 60 | -------------------------------------------------------------------------------- /Chapter03/viterbi.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def viterbi(obs, transition, emission, init=None): 4 | """ 5 | Return the MAP estimate of state trajectory of Hidden Markov Model. 6 | 7 | Parameters 8 | ---------- 9 | y : array (T,) 10 | Observation state sequence. int dtype. 11 | 12 | transition : array (K, K) 13 | State transition matrix. See HiddenMarkovModel.state_transition for 14 | details. 15 | 16 | emission : array (K,) 17 | Emission matrix. See HiddenMarkovModel.emission for details. 18 | 19 | init: optional, (K,) 20 | Initial state probabilities: Pi[i] is the probability x[0] == i. If 21 | None, uniform initial distribution is assumed (Pi[:] == 1/K). 22 | 23 | Returns 24 | ------- 25 | x : array (T,) 26 | Maximum a posteriori probability estimate of hidden state trajectory, 27 | conditioned on observation sequence y under the model parameters. 28 | 29 | T1: array (K, T) 30 | the probability of the most likely path so far 31 | 32 | T2: array (K, T) 33 | the x_j-1 of the most likely path so far 34 | """ 35 | # Cardinality of the state space 36 | K = transition.shape[0] 37 | 38 | emission = np.repeat(emission[np.newaxis, :], K, axis=0) 39 | 40 | # Initialize the priors with default (uniform dist) if not given by caller 41 | init = init if init is not None else np.full(K, 1 / K) 42 | T = len(obs) 43 | T1 = np.empty((K, T), 'd') 44 | T2 = np.empty((K, T), 'B') 45 | 46 | # Initilaize the tracking tables from first observation 47 | T1[:, 0] = init * emission[:, obs[0]] 48 | T2[:, 0] = 0 49 | 50 | # Iterate throught the observations updating the tracking tables 51 | for i in range(1, T): 52 | T1[:, i] = np.max(T1[:, i - 1] * transition.T * emission[np.newaxis, :, obs[i]].T, 1) 53 | T2[:, i] = np.argmax(T1[:, i - 1] * transition.T, 1) 54 | 55 | # Build the output, optimal model trajectory 56 | x = np.empty(T, 'B') 57 | x[-1] = np.argmax(T1[:, T - 1]) 58 | for i in reversed(range(1, T)): 59 | x[i - 1] = T2[x[i], i] 60 | 61 | return x, T1, T2 62 | -------------------------------------------------------------------------------- /Chapter04/coin_mle.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def coin_mle(data): 5 | """ 6 | Returns the learned probability of getting a heads using MLE. 7 | 8 | Parameters 9 | ---------- 10 | data: list, array-like 11 | The list of observations. 1 for heads and 0 for tails. 12 | 13 | Returns 14 | ------- 15 | theta: The learned probability of getting a heads. 16 | """ 17 | data = np.array(data) 18 | n_heads = np.sum(data) 19 | return n_heads / data.size 20 | -------------------------------------------------------------------------------- /Chapter04/gaussian_mle.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def gaussian_mle(data): 5 | """ 6 | Returns the learned parameters of the Normal Distribution using MLE. 7 | 8 | Parameters 9 | ---------- 10 | data: list, array-like 11 | The list of observed variables. 12 | 13 | Returns 14 | ------- 15 | \mu: The learned mean of the Normal Distribution. 16 | \sigma: The learned standard deviation of the Normal Distribution. 17 | """ 18 | data = np.array(data) 19 | mu = np.mean(data) 20 | variance = np.sqrt(np.mean((data - mu)**2)) 21 | return mu, variance 22 | -------------------------------------------------------------------------------- /Chapter04/hmmlearn_gaussian.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | 4 | import datetime 5 | import numpy as np 6 | import pandas as pd 7 | from matplotlib import cm, pyplot as plt 8 | from matplotlib.dates import YearLocator, MonthLocator 9 | 10 | # try: 11 | # from matplotlib.finance import quotes_historical_yahoo_ochl 12 | # except ImportError: 13 | # # For Matplotlib prior to 1.5. 14 | # from matplotlib.finance import ( 15 | # quotes_historical_yahoo as quotes_historical_yahoo_ochl 16 | # ) 17 | 18 | from hmmlearn.hmm import GaussianHMM 19 | 20 | 21 | print(__doc__) 22 | 23 | quotes = pd.read_csv('data/yahoofinance-INTC-19950101-20040412.csv', 24 | index_col=0, 25 | parse_dates=True, 26 | infer_datetime_format=True) 27 | # Unpack quotes 28 | dates = np.array(quotes.index, dtype=int) 29 | close_v = np.array(quotes.Close) 30 | volume = np.array(quotes.Volume)[1:] 31 | 32 | # Take diff of close value. Note that this makes 33 | # ``len(diff) = len(close_t) - 1``, therefore, other quantities also 34 | # need to be shifted by 1. 35 | diff = np.diff(close_v) 36 | dates = dates[1:] 37 | close_v = close_v[1:] 38 | 39 | # Pack diff and volume for training. 40 | X = np.column_stack([diff, volume]) 41 | 42 | # Make an HMM instance and execute fit 43 | model = GaussianHMM(n_components=4, covariance_type="diag", 44 | n_iter=1000).fit(X) 45 | 46 | # Predict the optimal sequence of internal hidden state 47 | hidden_states = model.predict(X) 48 | 49 | print("Transition matrix") 50 | print(model.transmat_) 51 | print() 52 | 53 | print("Means and vars of each hidden state") 54 | for i in range(model.n_components): 55 | print("{0}th hidden state".format(i)) 56 | print("mean = ", model.means_[i]) 57 | print("var = ", np.diag(model.covars_[i])) 58 | print() 59 | -------------------------------------------------------------------------------- /Chapter04/weather.py: -------------------------------------------------------------------------------- 1 | def weather_fit(data): 2 | """ 3 | Learn the transition and emission probabilities from the given data 4 | for the weather model. 5 | 6 | Parameters 7 | ---------- 8 | data: 2-D list (array-like) 9 | Each data point should be a tuple of size 2 with the first element 10 | representing the state of Weather and the second element representing 11 | whether it rained or not. 12 | 13 | Sunny = 0, Cloudy = 1, Windy = 2 14 | Rain = 0, No Rain = 1 15 | 16 | Returns 17 | ------- 18 | transition probability: 2-D array 19 | The conditional distribution respresenting the transition probability 20 | of the model. 21 | 22 | emission probability: 2-D array 23 | The conditional distribution respresenting the emission probability 24 | of the model. 25 | """ 26 | data = np.array(data) 27 | transition_counts = np.zeros((3, 3)) 28 | emission_counts = np.zeros((3, 2)) 29 | for index, datapoint in enumerate(data): 30 | if index != len(data)-1: 31 | transition_counts[data[index][0], data[index+1][0]] += 1 32 | emission_counts[data[index][0], data[index][1]] += 1 33 | transition_prob = transition_counts / np.sum(transition_counts, axis=0) 34 | emission_prob = (emission_counts.T / np.sum(emission_counts.T, 35 | axis=0)).T 36 | return transition_prob, emission_prob 37 | -------------------------------------------------------------------------------- /Chapter06/analyse_data.py: -------------------------------------------------------------------------------- 1 | """ 2 | Usage: analyse_data.py --company= 3 | """ 4 | import warnings 5 | import logging 6 | import itertools 7 | import pandas as pd 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | from hmmlearn.hmm import GaussianHMM 11 | from sklearn.model_selection import train_test_split 12 | from tqdm import tqdm 13 | from docopt import docopt 14 | 15 | args = docopt(doc=__doc__, argv=None, help=True, 16 | version=None, options_first=False) 17 | 18 | # Supress warning in hmmlearn 19 | warnings.filterwarnings("ignore") 20 | # Change plot style to ggplot (for better and more aesthetic visualisation) 21 | plt.style.use('ggplot') 22 | 23 | 24 | class StockPredictor(object): 25 | def __init__(self, company, test_size=0.33, 26 | n_hidden_states=4, n_latency_days=10, 27 | n_steps_frac_change=50, n_steps_frac_high=10, 28 | n_steps_frac_low=10): 29 | self._init_logger() 30 | 31 | self.company = company 32 | self.n_latency_days = n_latency_days 33 | 34 | self.hmm = GaussianHMM(n_components=n_hidden_states) 35 | 36 | self._split_train_test_data(test_size) 37 | 38 | self._compute_all_possible_outcomes( 39 | n_steps_frac_change, n_steps_frac_high, n_steps_frac_low) 40 | 41 | def _init_logger(self): 42 | self._logger = logging.getLogger(__name__) 43 | handler = logging.StreamHandler() 44 | formatter = logging.Formatter( 45 | '%(asctime)s %(name)-12s %(levelname)-8s %(message)s') 46 | handler.setFormatter(formatter) 47 | self._logger.addHandler(handler) 48 | self._logger.setLevel(logging.DEBUG) 49 | 50 | def _split_train_test_data(self, test_size): 51 | data = pd.read_csv( 52 | 'data/company_data/{company}.csv'.format(company=self.company)) 53 | _train_data, test_data = train_test_split( 54 | data, test_size=test_size, shuffle=False) 55 | 56 | self._train_data = _train_data 57 | self._test_data = test_data 58 | 59 | @staticmethod 60 | def _extract_features(data): 61 | open_price = np.array(data['open']) 62 | close_price = np.array(data['close']) 63 | high_price = np.array(data['high']) 64 | low_price = np.array(data['low']) 65 | 66 | # Compute the fraction change in close, high and low prices 67 | # which would be used a feature 68 | frac_change = (close_price - open_price) / open_price 69 | frac_high = (high_price - open_price) / open_price 70 | frac_low = (open_price - low_price) / open_price 71 | 72 | return np.column_stack((frac_change, frac_high, frac_low)) 73 | 74 | def fit(self): 75 | self._logger.info('>>> Extracting Features') 76 | feature_vector = StockPredictor._extract_features(self._train_data) 77 | self._logger.info('Features extraction Completed <<<') 78 | 79 | self.hmm.fit(feature_vector) 80 | 81 | def _compute_all_possible_outcomes(self, n_steps_frac_change, 82 | n_steps_frac_high, n_steps_frac_low): 83 | frac_change_range = np.linspace(-0.1, 0.1, n_steps_frac_change) 84 | frac_high_range = np.linspace(0, 0.1, n_steps_frac_high) 85 | frac_low_range = np.linspace(0, 0.1, n_steps_frac_low) 86 | 87 | self._possible_outcomes = np.array(list(itertools.product( 88 | frac_change_range, frac_high_range, frac_low_range))) 89 | 90 | def _get_most_probable_outcome(self, day_index): 91 | previous_data_start_index = max(0, day_index - self.n_latency_days) 92 | previous_data_end_index = max(0, day_index - 1) 93 | previous_data = self._test_data.iloc[previous_data_end_index: previous_data_start_index] 94 | previous_data_features = StockPredictor._extract_features( 95 | previous_data) 96 | 97 | outcome_score = [] 98 | for possible_outcome in self._possible_outcomes: 99 | total_data = np.row_stack( 100 | (previous_data_features, possible_outcome)) 101 | outcome_score.append(self.hmm.score(total_data)) 102 | most_probable_outcome = self._possible_outcomes[np.argmax( 103 | outcome_score)] 104 | 105 | return most_probable_outcome 106 | 107 | def predict_close_price(self, day_index): 108 | open_price = self._test_data.iloc[day_index]['open'] 109 | predicted_frac_change, _, _ = self._get_most_probable_outcome( 110 | day_index) 111 | return open_price * (1 + predicted_frac_change) 112 | 113 | def predict_close_prices_for_days(self, days, with_plot=False): 114 | predicted_close_prices = [] 115 | for day_index in tqdm(range(days)): 116 | predicted_close_prices.append(self.predict_close_price(day_index)) 117 | 118 | if with_plot: 119 | test_data = self._test_data[0: days] 120 | days = np.array(test_data['date'], dtype="datetime64[ms]") 121 | actual_close_prices = test_data['close'] 122 | 123 | fig = plt.figure() 124 | 125 | axes = fig.add_subplot(111) 126 | axes.plot(days, actual_close_prices, 'bo-', label="actual") 127 | axes.plot(days, predicted_close_prices, 'r+-', label="predicted") 128 | axes.set_title('{company}'.format(company=self.company)) 129 | 130 | fig.autofmt_xdate() 131 | 132 | plt.legend() 133 | plt.show() 134 | 135 | return predicted_close_prices 136 | 137 | 138 | stock_predictor = StockPredictor(company=args['--company']) 139 | stock_predictor.fit() 140 | stock_predictor.predict_close_prices_for_days(500, with_plot=True) 141 | -------------------------------------------------------------------------------- /Chapter06/get_data.py: -------------------------------------------------------------------------------- 1 | """ 2 | Usage: get_data.py --year= 3 | """ 4 | import requests 5 | import os 6 | from docopt import docopt 7 | 8 | args = docopt(doc=__doc__, argv=None, help=True, 9 | version=None, options_first=False) 10 | 11 | year = args['--year'] 12 | 13 | # Create directory if not present 14 | year_directory_name = 'data/{year}'.format(year=year) 15 | if not os.path.exists(year_directory_name): 16 | os.makedirs(year_directory_name) 17 | 18 | # Fetching file list for the corresponding year 19 | year_data_files = requests.get( 20 | 'http://data.pystock.com/{year}/index.txt'.format(year=year)).text.strip().split('\n') 21 | 22 | for data_file_name in year_data_files: 23 | file_location = '{year_directory_name}/{data_file_name}'.format(year_directory_name=year_directory_name, 24 | data_file_name=data_file_name) 25 | 26 | with open(file_location, 'wb+') as data_file: 27 | print('>>> Downloading \t {file_location}'.format( 28 | file_location=file_location)) 29 | data_file_content = requests.get('http://data.pystock.com/{year}/{data_file_name}'.format( 30 | year=year, data_file_name=data_file_name) 31 | ).content 32 | print('<<< Download Completed \t {file_location}'.format( 33 | file_location=file_location)) 34 | data_file.write(data_file_content) 35 | -------------------------------------------------------------------------------- /Chapter06/parse_data.py: -------------------------------------------------------------------------------- 1 | """ 2 | Usage: parse_data.py --company= 3 | """ 4 | import os 5 | import tarfile 6 | import pandas as pd 7 | from pandas import errors as pd_errors 8 | from functools import reduce 9 | from docopt import docopt 10 | 11 | args = docopt(doc=__doc__, argv=None, help=True, 12 | version=None, options_first=False) 13 | 14 | years = [2015, 2016, 2017] 15 | company = args['--company'] 16 | 17 | # Getting the data files list 18 | data_files_list = [] 19 | for year in years: 20 | year_directory = 'data/{year}'.format(year=year) 21 | for file in os.listdir(year_directory): 22 | data_files_list.append( 23 | '{year_directory}/{file}'.format(year_directory=year_directory, file=file)) 24 | 25 | 26 | def parse_data(file_name, company_symbol): 27 | """ 28 | Returns data for the corresponding company 29 | 30 | :param file_name: name of the tar file 31 | :param company_symbol: company symbol 32 | :type file_name: str 33 | :type company_symbol: str 34 | :return: dataframe for the corresponding company data 35 | :rtype: pd.DataFrame 36 | """ 37 | tar = tarfile.open(file_name) 38 | try: 39 | price_report = pd.read_csv(tar.extractfile('prices.csv')) 40 | company_price_data = price_report[price_report['symbol'] 41 | == company_symbol] 42 | return company_price_data 43 | except (KeyError, pd_errors.EmptyDataError): 44 | return pd.DataFrame() 45 | 46 | 47 | # Getting the complete data for a given company 48 | company_data = reduce(lambda df, file_name: df.append(parse_data(file_name, company)), 49 | data_files_list, 50 | pd.DataFrame()) 51 | company_data = company_data.sort_values(by=['date']) 52 | 53 | # Create folder for company data if does not exists 54 | if not os.path.exists('data/company_data'): 55 | os.makedirs('data/company_data') 56 | 57 | # Write data to a CSV file 58 | company_data.to_csv('data/company_data/{company}.csv'.format(company=company), 59 | columns=['date', 'open', 'high', 'low', 60 | 'close', 'volume', 'adj_close'], 61 | index=False) 62 | -------------------------------------------------------------------------------- /Chapter07/MFCTagger.py: -------------------------------------------------------------------------------- 1 | def pair_counts(tags, words): 2 | d = defaultdict(lambda: defaultdict(int)) 3 | for tag, word in zip(tags, words): 4 | d[tag][word] += 1 5 | return d 6 | 7 | tags = [tag for i, (word, tag) in enumerate(data.training_set.stream())] 8 | words = [word for i, (word, tag) in enumerate(data.training_set.stream())] 9 | 10 | FakeState = namedtuple('FakeState', 'name') 11 | 12 | 13 | class MFCTagger: 14 | missing = FakeState(name = '') 15 | 16 | def __init__(self, table): 17 | self.table = defaultdict(lambda: MFCTagger.missing) 18 | self.table.update({word: FakeState(name=tag) for word, tag in 19 | table.items()}) 20 | 21 | def viterbi(self, seq): 22 | """This method simplifies predictions by matching the Pomegranate 23 | viterbi() interface""" 24 | return 0., list(enumerate([""] + [self.table[w] for w in 25 | seq] + [""])) 26 | 27 | 28 | tags = [tag for i, (word, tag) in enumerate(data.training_set.stream())] 29 | words = [word for i, (word, tag) in enumerate(data.training_set.stream())] 30 | 31 | word_counts = pair_counts(words, tags) 32 | mfc_table = dict((word, max(tags.keys(), key=lambda key: tags[key])) for 33 | word, tags in word_counts.items()) 34 | 35 | mfc_model = MFCTagger(mfc_table) 36 | 37 | 38 | def replace_unknown(sequence): 39 | return [w if w in data.training_set.vocab else 'nan' for w in sequence] 40 | 41 | 42 | def simplify_decoding(X, model): 43 | _, state_path = model.viterbi(replace_unknown(X)) 44 | return [state[1].name for state in state_path[1:-1]] 45 | 46 | 47 | def accuracy(X, Y, model): 48 | correct = total_predictions = 0 49 | for observations, actual_tags in zip(X, Y): 50 | # The model.viterbi call in simplify_decoding will return None if the HMM 51 | # raises an error (for example, if a test sentence contains a word that 52 | # is out of vocabulary for the training set). Any exception counts the 53 | # full sentence as an error (which makes this a conservative estimate). 54 | try: 55 | most_likely_tags = simplify_decoding(observations, model) 56 | correct += sum(p == t for p, t in zip(most_likely_tags, 57 | actual_tags)) 58 | except: 59 | pass 60 | total_predictions += len(observations) 61 | return correct / total_predictions 62 | -------------------------------------------------------------------------------- /Chapter07/hmm_tagger.py: -------------------------------------------------------------------------------- 1 | def unigram_counts(sequences): 2 | return Counter(sequences) 3 | 4 | 5 | tags = [tag for i, (word, tag) in enumerate(data.training_set.stream())] 6 | tag_unigrams = unigram_counts(tags) 7 | 8 | def bigram_counts(sequences): 9 | d = Counter(sequences) 10 | return d 11 | 12 | tags = [tag for i, (word, tag) in enumerate(data.stream())] 13 | o = [(tags[i],tags[i+1]) for i in range(0,len(tags)-2,2)] 14 | tag_bigrams = bigram_counts(o) 15 | 16 | 17 | def starting_counts(sequences): 18 | d = Counter(sequences) 19 | return d 20 | 21 | tags = [tag for i, (word, tag) in enumerate(data.stream())] 22 | starts_tag = [i[0] for i in data.Y] 23 | tag_starts = starting_counts(starts_tag) 24 | 25 | 26 | def ending_counts(sequences): 27 | d = Counter(sequences) 28 | return d 29 | 30 | end_tag = [i[len(i)-1] for i in data.Y] 31 | tag_ends = ending_counts(end_tag) 32 | 33 | 34 | basic_model = HiddenMarkovModel(name="base-hmm-tagger") 35 | 36 | tags = [tag for i, (word, tag) in enumerate(data.stream())] 37 | words = [word for i, (word, tag) in enumerate(data.stream())] 38 | 39 | tags_count=unigram_counts(tags) 40 | tag_words_count=pair_counts(tags,words) 41 | 42 | starting_tag_list=[i[0] for i in data.Y] 43 | ending_tag_list=[i[-1] for i in data.Y] 44 | starting_tag_count=starting_counts(starting_tag_list)#the number of times a tag occured at the start 45 | ending_tag_count=ending_counts(ending_tag_list) #the number of times a tag occured at the end 46 | 47 | to_pass_states = [] 48 | for tag, words_dict in tag_words_count.items(): 49 | total = float(sum(words_dict.values())) 50 | distribution = {word: count/total for word, count in 51 | words_dict.items()} 52 | tag_emissions = DiscreteDistribution(distribution) 53 | tag_state = State(tag_emissions, name=tag) 54 | to_pass_states.append(tag_state) 55 | 56 | basic_model.add_states() 57 | start_prob={} 58 | 59 | for tag in tags: 60 | start_prob[tag]=starting_tag_count[tag]/tags_count[tag] 61 | 62 | for tag_state in to_pass_states : 63 | basic_model.add_transition(basic_model.start,tag_state,start_prob[next_tag_state.name]) 64 | 65 | end_prob={} 66 | 67 | for tag in tags: 68 | end_prob[tag]=ending_tag_count[tag]/tags_count[tag] 69 | 70 | for tag_state in to_pass_states : 71 | basic_model.add_transition(tag_state,basic_model.end,end_prob[tag_state.name]) 72 | 73 | transition_prob_pair={} 74 | for key in tag_bigrams.keys(): 75 | transition_prob_pair[key]=tag_bigrams.get(key)/tags_count[key[0]] 76 | for tag_state in to_pass_states : 77 | for next_tag_state in to_pass_states : 78 | basic_model.add_transition(tag_state,next_tag_state,transition_prob_pair[( 79 | tag_state.name,next_tag_state.name)]) 80 | basic_model.bake() 81 | -------------------------------------------------------------------------------- /Chapter07/part_of_speech.py: -------------------------------------------------------------------------------- 1 | # Imports 2 | import random 3 | from itertools import chain 4 | from collections import Counter, defaultdict 5 | 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | from pomegranate import State, HiddenMarkovModel, DiscreteDistribution 9 | 10 | Sentence = namedtuple("Sentence", "words tags") 11 | 12 | 13 | def read_data(filename): 14 | """ 15 | Function to read tagged sentence data. 16 | 17 | Parameters 18 | ---------- 19 | filename: str 20 | The path to the file from where to read the data. 21 | """ 22 | with open(filename, 'r') as f: 23 | sentence_lines = [l.split("\n") for l in f.read().split("\n\n")] 24 | return OrderedDict(((s[0], Sentence(*zip(*[l.strip().split("\t") for l 25 | in s[1:]]))) for s in sentence_lines if s[0]) 26 | 27 | 28 | def read_tags(filename): 29 | """ 30 | Function to read a list of word tag classes. 31 | 32 | Parameters 33 | ---------- 34 | filename: str 35 | The path to the file from where to read the tags. 36 | """ 37 | with open(filename, 'r') as f: 38 | tags = f.read().split("\n") 39 | return frozenset(tags) 40 | 41 | 42 | class Subset(namedtuple("BaseSet", "sentences keys vocab X tagset Y N 43 | stream")): 44 | """ 45 | Class to handle a subset of the whole data. This is required when we 46 | split the data into training and test sets. 47 | """ 48 | def __new__(cls, sentences, keys): 49 | word_sequences = tuple([sentences[k].words for k in keys]) 50 | tag_sequences = tuple([sentences[k].tags for k in keys]) 51 | wordset = frozenset(chain(*word_sequences)) 52 | tagset = frozenset(chain(*tag_sequences)) 53 | N = sum(1 for _ in chain(*(sentences[k].words for k in keys))) 54 | stream = tuple(zip(chain(*word_sequences), chain(*tag_sequences))) 55 | return super().__new__(cls, {k: sentences[k] for k in keys}, keys, 56 | wordset, word_sequences, tagset, tag_sequences, N, 57 | stream.__iter__) 58 | 59 | def __len__(self): 60 | return len(self.sentences) 61 | 62 | def __iter__(self): 63 | return iter(self.sentences.items()) 64 | 65 | 66 | class Dataset(namedtuple("_Dataset", "sentences keys vocab X tagset Y" + 67 | "training_set testing_set N stream")): 68 | """ 69 | Class to represent the data in structured form for easy processing. 70 | """ 71 | def __new__(cls, tagfile, datafile, train_test_split=0.8, seed=112890): 72 | tagset = read_tags(tagfile) 73 | sentences = read_data(datafile) 74 | keys = tuple(sentences.keys()) 75 | wordset = frozenset(chain(*[s.words for s in sentences.values()])) 76 | word_sequences = tuple([sentences[k].words for k in keys]) 77 | tag_sequences = tuple([sentences[k].tags for k in keys]) 78 | N = sum(1 for _ in chain(*(s.words for s in sentences.values()))) 79 | 80 | # split data into train/test sets 81 | _keys = list(keys) 82 | if seed is not None: 83 | random.seed(seed) 84 | random.shuffle(_keys) 85 | split = int(train_test_split * len(_keys)) 86 | training_data = Subset(sentences, _keys[:split]) 87 | testing_data = Subset(sentences, _keys[split:]) 88 | stream = tuple(zip(chain(*word_sequences), chain(*tag_sequences))) 89 | return super().__new__(cls, dict(sentences), keys, wordset, 90 | word_sequences, tagset, tag_sequences, 91 | training_data, testing_data, N, stream.__iter__) 92 | 93 | def __len__(self): 94 | return len(self.sentences) 95 | 96 | def __iter__(self): 97 | return iter(self.sentences.items()) 98 | -------------------------------------------------------------------------------- /Chapter09/mdp.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import random 3 | 4 | 5 | class MDP(object): 6 | """ 7 | Defines an Markov Decision Process containing: 8 | 9 | - States, s 10 | - Actions, a 11 | - Rewards, r(s,a) 12 | - Transition Matrix, t(s,a,_s) 13 | 14 | Includes a set of abstract methods for extended class will 15 | need to implement. 16 | 17 | """ 18 | 19 | def __init__(self, states=None, actions=None, rewards=None, transitions=None, 20 | discount=.99, tau=.01, epsilon=.01): 21 | """ 22 | Parameters: 23 | ----------- 24 | states: 1-D array 25 | The states of the environment 26 | 27 | actions: 1-D array 28 | The possible actions by the agent. 29 | 30 | rewards: 2-D array 31 | The rewards corresponding to each action at each state of the environment. 32 | 33 | transitions: 2-D array 34 | The transition probabilities between the states of the environment. 35 | 36 | discount: float 37 | The discount rate for the reward. 38 | """ 39 | self.s = np.array(states) 40 | self.a = np.array(actions) 41 | self.r = np.array(rewards) 42 | self.t = np.array(transitions) 43 | 44 | self.discount = discount 45 | self.tau = tau 46 | self.epsilon = epsilon 47 | 48 | # Value iteration will update this 49 | self.values = None 50 | self.policy = None 51 | 52 | def getTransitionStatesAndProbs(self, state, action): 53 | """ 54 | Returns the list of transition probabilities 55 | """ 56 | return self.t[state][action][:] 57 | 58 | def getReward(self, state): 59 | """ 60 | Gets reward for transition from state->action->nextState. 61 | """ 62 | return self.r[state] 63 | 64 | 65 | def takeAction(self, state, action): 66 | """ 67 | Take an action in an MDP, return the next state 68 | 69 | Chooses according to probability distribution of state transitions, 70 | contingent on actions. 71 | """ 72 | return np.random.choice(self.s, p=self.getTransitionStatesAndProbs(state, action)) 73 | 74 | 75 | def valueIteration(self): 76 | """ 77 | Performs value iteration to populate the values of all states in 78 | the MDP. 79 | 80 | """ 81 | 82 | # Initialize V_0 to zero 83 | self.values = np.zeros(len(self.s)) 84 | self.policy = np.zeros([len(self.s), len(self.a)]) 85 | 86 | policy_switch = 0 87 | 88 | # Loop until convergence 89 | while True: 90 | 91 | # To be used for convergence check 92 | oldValues = np.copy(self.values) 93 | 94 | for i in range(len(self.s)-1): 95 | 96 | self.values[i] = self.r[i] + np.max(self.discount * \ 97 | np.dot(self.t[i][:][:], self.values)) 98 | 99 | # Check Convergence 100 | if np.max(np.abs(self.values - oldValues)) <= self.epsilon: 101 | break 102 | 103 | 104 | 105 | def extractPolicy(self): 106 | """ 107 | Extract policy from values after value iteration runs. 108 | """ 109 | 110 | self.policy = np.zeros([len(self.s),len(self.a)]) 111 | 112 | for i in range(len(self.s)-1): 113 | 114 | state_policy = np.zeros(len(self.a)) 115 | 116 | state_policy = self.r[i] + self.discount* \ 117 | np.dot(self.t[i][:][:], self.values) 118 | 119 | # Softmax the policy 120 | state_policy -= np.max(state_policy) 121 | state_policy = np.exp(state_policy / float(self.tau)) 122 | state_policy /= state_policy.sum() 123 | 124 | self.policy[i] = state_policy 125 | 126 | 127 | 128 | def simulate(self, state): 129 | 130 | """ 131 | Runs the solver for the MDP, conducts value iteration, extracts policy, 132 | then runs simulation of problem. 133 | 134 | NOTE: Be sure to run value iteration (solve values for states) and to 135 | extract some policy (fill in policy vector) before running simulation 136 | """ 137 | 138 | # Run simulation using policy until terminal condition met 139 | 140 | while not self.isTerminal(state): 141 | 142 | # Determine which policy to use (non-deterministic) 143 | policy = self.policy[np.where(self.s == state)[0][0]] 144 | p_policy = self.policy[np.where(self.s == state)[0][0]] / \ 145 | self.policy[np.where(self.s == state)[0][0]].sum() 146 | 147 | # Get the parameters to perform one move 148 | stateIndex = np.where(self.s == state)[0][0] 149 | policyChoice = np.random.choice(policy, p=p_policy) 150 | actionIndex = np.random.choice(np.array(np.where(self.policy[state][:] == policyChoice)).ravel()) 151 | 152 | # Take an action, move to next state 153 | nextState = self.takeAction(stateIndex, actionIndex) 154 | 155 | print "In state: {}, taking action: {}, moving to state: {}".format( 156 | state, self.a[actionIndex], nextState) 157 | 158 | # End game if terminal state reached 159 | state = int(nextState) 160 | if self.isTerminal(state): 161 | 162 | # print "Terminal state: {} has been reached. Simulation over.".format(state) 163 | return state 164 | 165 | 166 | class BettingGame(MDP): 167 | 168 | """ 169 | Defines the Betting Game: 170 | 171 | Problem: A gambler has the chance to make bets on the outcome of 172 | a fair coin flip. If the coin is heads, the gambler wins as many 173 | dollars back as was staked on that particular flip - otherwise 174 | the money is lost. The game is won if the gambler obtains $100, 175 | and is lost if the gambler runs out of money (has 0$). This gambler 176 | did some research on MDPs and has decided to enlist them to assist 177 | in determination of how much money should be bet on each turn. Your 178 | task is to build that MDP! 179 | 180 | Params: 181 | 182 | pHead: Probability of coin flip landing on heads 183 | - Use .5 for fair coin, else choose a bias [0,1] 184 | 185 | """ 186 | 187 | def __init__(self, pHeads=.5, discount=.99, epsilon=.1, tau=.0001): 188 | 189 | MDP.__init__(self,discount=discount,tau=tau,epsilon=epsilon) 190 | self.pHeads = pHeads 191 | self.setBettingGame(pHeads) 192 | self.valueIteration() 193 | self.extractPolicy() 194 | 195 | # Edge case fix: Policy for $1 196 | self.policy[1][:] = 0 197 | self.policy[1][1] = 1.0 198 | 199 | def isTerminal(self, state): 200 | """ 201 | Checks if MDP is in terminal state. 202 | """ 203 | return True if state is 100 or state is 0 else False 204 | 205 | def setBettingGame(self, pHeads=.5): 206 | 207 | """ 208 | Initializes the MDP to the starting conditions for 209 | the betting game. 210 | 211 | Params: 212 | pHeads = Probability that coin lands on head 213 | - .5 for fair coin, otherwise choose bias 214 | 215 | """ 216 | 217 | # This is how much we're starting with 218 | self.pHeads = pHeads 219 | 220 | # Initialize all possible states 221 | self.s = np.arange(102) 222 | 223 | # Initialize possible actions 224 | self.a = np.arange(101) 225 | 226 | # Initialize rewards 227 | self.r = np.zeros(101) 228 | self.r[0] = -5 229 | self.r[100] = 10 230 | 231 | # Initialize transition matrix 232 | temp = np.zeros([len(self.s),len(self.a),len(self.s)]) 233 | 234 | # List comprehension using tHelper to determine probabilities for each index 235 | self.t = [self.tHelper(i[0], i[1], i[2], self.pHeads) for i,x in np.ndenumerate(temp)] 236 | self.t = np.reshape(self.t, np.shape(temp)) 237 | 238 | for x in range(len(self.a)): 239 | 240 | # Remembr to add -1 to value it, and policy extract 241 | # Send the end game states to the death state! 242 | self.t[100][x] = np.zeros(len(self.s)) 243 | self.t[100][x][101] = 1.0 244 | self.t[0][x] = np.zeros(len(self.s)) 245 | self.t[0][x][101] = 1.0 246 | 247 | 248 | def tHelper(self, x, y, z , pHeads): 249 | 250 | """ 251 | Helper function to be used in a list comprehension to quickly 252 | generate the transition matrix. Encodes the necessary conditions 253 | to compute the necessary probabilities. 254 | 255 | Params: 256 | x,y,z indices 257 | pHeads = probability coin lands on heads 258 | 259 | """ 260 | 261 | # If you bet no money, you will always have original amount 262 | if x + y is z and y is 0: 263 | return 1.0 264 | 265 | # If you bet more money than you have, no chance of any outcome 266 | elif y > x and x is not z: 267 | return 0 268 | 269 | # If you bet more money than you have, returns same state with 1.0 prob. 270 | elif y > x and x is z: 271 | return 1.0 272 | 273 | # Chance you lose 274 | elif x - y is z: 275 | return 1.0 - pHeads 276 | 277 | # Chance you win 278 | elif x + y is z: 279 | return pHeads 280 | 281 | # Edge Case: Chance you win, and winnings go over 100 282 | elif x + y > z and z is 100: 283 | return pHeads 284 | 285 | 286 | else: 287 | return 0 288 | 289 | return 0 290 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Hands-On Markov Models with Python 5 | 6 | Hands-On Markov Models with Python 7 | 8 | This is the code repository for [Hands-On Markov Models with Python](https://www.packtpub.com/big-data-and-business-intelligence/hands-markov-models-python?utm_source=github&utm_medium=repository&utm_campaign=9781788625449), published by Packt. 9 | 10 | **Implement probabilistic models for learning complex data sequences using the Python ecosystem** 11 | 12 | ## What is this book about? 13 | 14 | Hidden Markov Model (HMM) is a statistical model based on the Markov chain concept. Hands-On Markov Models with Python helps you get to grips with HMMs and different inference algorithms by working on real-world problems. The hands-on examples explored in the book help you simplify the process flow in machine learning by using Markov model concepts, thereby making it accessible to everyone. 15 | 16 | This book covers the following exciting features: 17 | * Explore a balance of both theoretical and practical aspects of HMM 18 | * Implement HMMs using different datasets in Python using different packages 19 | * Understand multiple inference algorithms and how to select the right algorithm to resolve your problems 20 | * Develop a Bayesian approach to inference in HMMs 21 | * Implement HMMs in finance, natural language processing (NLP), and image processing 22 | 23 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1788625447) today! 24 | 25 | https://www.packtpub.com/ 27 | 28 | 29 | ## Instructions and Navigations 30 | All of the code is organized into folders. For example, Chapter02. 31 | 32 | The code will look like the following: 33 | ``` 34 | from hmmlearn.hmm import GaussianHMM 35 | import numpy as np 36 | import matplotlib.pyplot as plt 37 | 38 | ``` 39 | 40 | **Following is what you need for this book:** 41 | 42 | Hands-On Markov Models with Python is for you if you are a data analyst, data scientist, or machine learning developer and want to enhance your machine learning knowledge and skills. This book will also help you build your own hidden Markov models by applying them to any sequence of data. 43 | Basic knowledge of machine learning and the Python programming language is expected to get the most out of the book 44 | 45 | With the following software and hardware list you can run all code files present in the book (Chapter 1-9). 46 | 47 | ### Software and Hardware List 48 | 49 | | Chapter | Software required | OS required | 50 | | -------- | ------------------------------------| -----------------------------------| 51 | | 1 | Python 3.5, numpy 1.15.1 | Linux, Windows or MacOS | 52 | | 2 | Python 3.5, numpy 1.15.1, hmmlearn 0.2.0, matplotlib 2.2.3 |Linux, Windows or MacOS | 53 | | 3 | Python 3.5, numpy 1.15.1 | Linux, Windows or MacOS | 54 | | 4 | Python 3.5, numpy 1.15.1, hmmlearn 0.2.0 |Linux, Windows or MacOS | 55 | |6 |Python 3.5, numpy 1.15.1, pandas 0.23.4, hmmlearn 0.2.0, matplotlib 2.2.3, scikit-learn 0.19.2, tqdm 4.26, docopt 0.6.2, requests 2.19.1 | Linux, Windows or MacOS | 56 | | 7 | Python 3.5, numpy 1.15.1, matplotlib 2.2.3, pomegranate 0.10.0 |Linux, Windows or MacOS | 57 | | 9 | Python 3.5, numpy 1.15.1 |Linux, Windows or MacOS | 58 | 59 | 60 | 61 | We also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](https://www.packtpub.com/sites/default/files/downloads/9781788625449_ColorImages.pdf). 62 | 63 | 64 | ### Related products 65 | * Machine Learning Algorithms - Second Edition [[Packt]](https://www.packtpub.com/big-data-and-business-intelligence/machine-learning-algorithms-second-edition?utm_source=github&utm_medium=repository&utm_campaign=9781789347999) [[Amazon]](https://www.amazon.com/dp/1789347998) 66 | 67 | * Building Machine Learning Systems with Python - Third Edition [[Packt]](https://www.packtpub.com/big-data-and-business-intelligence/building-machine-learning-systems-python-third-edition?utm_source=github&utm_medium=repository&utm_campaign=9781788623223) [[Amazon]](https://www.amazon.com/dp/1788623223) 68 | 69 | ## Get to Know the Authors 70 | **Ankur Ankan** is a BTech graduate from IIT (BHU), Varanasi. He is currently working in 71 | the field of data science. He is an open source enthusiast and his major work includes 72 | starting pgmpy with four other members. In his free time, he likes to participate in Kaggle 73 | competitions. 74 | 75 | **Abinash Panda** has been a data scientist for more than 4 years. He has worked at multiple 76 | early-stage start-ups and helped them build their data analytics pipelines. He loves to 77 | munge, plot, and analyze data. He has been a speaker at Python conferences. These days, 78 | he is busy co-founding a start-up. He has contributed to books on probabilistic graphical 79 | models by Packt Publishing. 80 | 81 | 82 | ## Other book by the authors 83 | * [Mastering Probabilistic Graphical Models Using Python](https://www.packtpub.com/big-data-and-business-intelligence/mastering-probabilistic-graphical-models-using-python?utm_source=github&utm_medium=repository&utm_campaign=9781784394684) 84 | 85 | ### Suggestions and Feedback 86 | [Click here](https://docs.google.com/forms/d/e/1FAIpQLSdy7dATC6QmEL81FIUuymZ0Wy9vH1jHkvpY57OiMeKGqib_Ow/viewform) if you have any feedback or suggestions. 87 | ### Download a free PDF 88 | 89 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
90 |

https://packt.link/free-ebook/9781788625449

--------------------------------------------------------------------------------