In the code for the Markov chain in the previous section, we used a dictionary to parameterize the Markov chain that had the probability values of all the possible state transitions. Another way of representing state transitions is using a transition matrix. The transition matrix, as the name suggests, uses a tabular representation for the transition probabilities. The transition matrix for the example in Figure 1.1 is shown in the following table.
The following table shows the transition matrix for the Markov chain shown in Figure 1.1. The probability values represent the probability of the system going from the state in the row to the states mentioned in the columns:
The transition matrix represents the same information as in the dictionary, but in a more compact way. For this reason, the transition matrix is the standard way of representing Markov chains. Let's modify our MarkovChain class so that it can accept a transition matrix:
import numpy as np
class MarkovChain(object): def __init__(self, transition_matrix, states): """ Initialize the MarkovChain instance.
Parameters ---------- transition_matrix: 2-D array A 2-D array representing the probabilities of change of state in the Markov Chain.
states: 1-D array An array representing the states of the Markov Chain. It needs to be in the same order as transition_matrix. """ self.transition_matrix = np.atleast_2d(transition_matrix) self.states = states self.index_dict = {self.states[index]: index for index in range(len(self.states))} self.state_dict = {index: self.states[index] for index in range(len(self.states))}
def next_state(self, current_state): """ Returns the state of the random variable at the next time instance.
Parameters ---------- current_state: str The current state of the system. """ return np.random.choice( self.states, p=self.transition_matrix[self.index_dict[current_state], :])
def generate_states(self, current_state, no=10): """ Generates the next states of the system.
Parameters ---------- current_state: str The state of the current random variable.
no: int The number of future states to generate. """ future_states = [] for i in range(no): next_state = self.next_state(current_state) future_states.append(next_state) current_state = next_state return future_states
Running this code should also give similar results to what we got in the previous section. Using a transition matrix might not seem like a good idea because it requires us to create extra variables to store the indices. But, in cases when we have hundreds of states, using a transition matrix is much more efficient than using the simple dictionary implementation. In the case of a transition matrix, we can simply use NumPy indexing to get the probability values in the next_state method, whereas we were looping over all the state names in the previous implementation: