A transition matrix stores the probabilities of moving between a finite set of states during one period. In Markov analysis, it turns a current distribution—such as customers across competing providers—into a forecast for the next period. This use assumes the next state depends on the current state, not the earlier path, and that transition probabilities remain constant over time. section 11.1 section 11.0
Write the matrix as P. Its entry Pᵢⱼ is the conditional probability of moving from current state i to next state j. Put starting states in rows and destination states in columns; Pᵢᵢ is the chance of staying put. Because the possible destinations are mutually exclusive and exhaustive, each row must sum to 1. A valid probability in the matrix lies between 0 and 1. section 11.2.2 section 11.3
>>> import numpy as np >>> pi_0 = np.array([0.40, 0.35, 0.20, 0.05]) >>> P = np.array([ >>> [0.85, 0.06, 0.07, 0.02], >>> [0.08, 0.80, 0.10, 0.02], >>> [0.10, 0.10, 0.75, 0.05], >>> [0.12, 0.10, 0.08, 0.70] >>> ])
The NumPy listing defines a four-provider transition matrix alongside the starting market-share vector. Read its first row as an 85% chance of retaining a customer and respective 6%, 7%, and 2% chances of switching to the other providers. section 11.2.2
Represent the current distribution as a row vector π₀, then calculate the next distribution with π₁ = π₀P. Each destination’s result adds the contributions from every starting state. Reapply the same matrix for later periods: π₂ = π₁P = π₀P². In the provider example, [0.40, 0.35, 0.20, 0.05] becomes [0.394, 0.329, 0.217, 0.060] after one period. These are forecast shares, not individual customers’ certain destinations. section 11.2.2 section 11.5
Check longer-term behavior
For an engine with states running and not running, use P = [[0.90, 0.10], [0.20, 0.80]]. Starting at [1, 0], repeated NumPy dot products yield [0.90, 0.10] after one week and [0.83, 0.17] after two. By about period 10, the running probability is near two-thirds. An equilibrium distribution remains unchanged when multiplied by P; inspect successive vectors to see whether they stabilize rather than assuming they will. section 11.3 section 11.3.1
>>> pi_0 = np.array([1, 0]) #1
>>> P = np.array([
>>> [0.90, 0.10],
>>> [0.20, 0.80]
>>> ]) #2
>>> n_periods = 20 #3
>>> state_probabilities = [pi_0] #4
>>> for _ in range(n_periods):
>>> pi_next = np.dot(state_probabilities[-1], P)
>>> state_probabilities.append(pi_next) #5
>>> for i, pi in enumerate(state_probabilities):
>>> print(f'Period {i} = {pi}') #6
Period 0 = [1 0]
Period 1 = [0.9 0.1]
Period 2 = [0.83 0.17]
Period 3 = [0.781 0.219]
Period 4 = [0.7467 0.2533]
Period 5 = [0.72269 0.27731]
Period 6 = [0.705883 0.294117]
Period 7 = [0.6941181 0.3058819]
Period 8 = [0.68588267 0.31411733]
Period 9 = [0.68011787 0.31988213]
Period 10 = [0.67608251 0.32391749]
Period 11 = [0.67325776 0.32674224]
Period 12 = [0.67128043 0.32871957]
Period 13 = [0.6698963 0.3301037]
Period 14 = [0.66892741 0.33107259]
Period 15 = [0.66824919 0.33175081]
Period 16 = [0.66777443 0.33222557]
Period 17 = [0.6674421 0.3325579]
Period 18 = [0.66720947 0.33279053]
Period 19 = [0.66704663 0.33295337]
Period 20 = [0.66693264 0.33306736]
The loop stores each engine-state vector and prints 20 successive forecasts, making stabilization visible without random sampling. section 11.3.1
A terminal outcome changes the matrix’s structure. An absorbing state has probability 1 of remaining in itself and 0 of leaving; other rows can still lead into it. For delinquent accounts, separating absorbing from non-absorbing states permits calculation of eventual absorption probabilities using submatrices and a fundamental matrix. This is a different question from forecasting only the next period. section 11.4 section 11.4.3
>>> P = np.array([
>>> [1, 0, 0, 0],
>>> [0, 1, 0, 0],
>>> [0.80, 0, 0.10, 0.10],
>>> [0.30, 0.10, 0.30, 0.30]
>>> ]) #1
>>> I = np.array([
>>> [1, 0],
>>> [0, 1]
>>> ]) #2
>>> A = np.array([
>>> [0.80, 0],
>>> [0.30, 0.10]
>>> ]) #3
>>> B = np.array([
>>> [0.10, 0.10],
>>> [0.30, 0.30]
>>> ]) #4
>>> I_minus_B = I – B #5
>>> F = np.linalg.inv(I_minus_B) #6
>>> FA = np.dot(F, A) #7
>>> print('The final matrix FA is:')
>>> print(FA) #8
The final matrix FA is:
[[0.98333333 0.01666667]
[0.85 0.15 ]]
The NumPy listing partitions a delinquency transition matrix and computes F = (I − B)⁻¹, then FA. Its rows give eventual probabilities of reaching each absorbing state from the two non-absorbing states. section 11.4.3

