Wiliam
Wiliam

Reputation: 1088

Transition probability matrix

I have the following array:

a=[['A', 'B'],
 ['B', 'B'],
 ['B', 'C'],
 ['C', 'B'],
 ['B', 'A'],
 ['A', 'D'],
 ['D', 'D'],
 ['D', 'A'],
 ['A', 'B'],
 ['B', 'A'],
 ['A', 'D']]

I wish to make a transition probability matrix of this, such that I get:

[[P_AA,P_AB,P_AC,P_AD],
[P_BA,P_BB,P_BC,P_BD],
[P_CA,P_CB,P_CC,P_CD],
[P_DA,P_DB,P_DC,P_DD]]

(Above is for illustration), where P_AA counts how many ["A","A"] are in the array a and so on divided by P_AA+P_AB+P_AC+P_AD . I have started by using the counter

from collections import Counter
Counter(tuple(x) for x in l)

which counts the elements of array correctly as:

Counter({('A', 'B'): 2,
         ('B', 'B'): 1,
         ('B', 'C'): 1,
         ('C', 'B'): 1,
         ('B', 'A'): 2,
         ('A', 'D'): 2,
         ('D', 'D'): 1,
         ('D', 'A'): 1})

So the matrix shall be,

[[0,2/5,0,2/5],[2/4,1/4,1/4,0],[0,1,0,0],[1/2,0,0,1/2]]

Upvotes: 1

Views: 1682

Answers (4)

cheersmate
cheersmate

Reputation: 2656

If the number of elements is small, simply looping over all elements should be no problem:

import numpy as np

a = [['A', 'B'], ['B', 'B'], ['B', 'C'], ['C', 'B'], ['B', 'A'],
        ['A', 'D'], ['D', 'D'], ['D', 'A'] ['A', 'B'], ['B', 'A'], ['A', 'D']]                                                                                                                                      

a = np.asarray(a)                                                                                                                                   
elems = np.unique(a)                                                                                                                                
dim = len(elems)                                                                                                                                    
P = np.zeros((dim, dim))                                                                                                                            

for j, x_in in enumerate(elems):                                                                                                                    
    for k, x_out in enumerate(elems):                                                                                                               
        P[j,k] = (a == [x_in, x_out]).all(axis=1).sum()                                                                                             

    if P[j,:].sum() > 0:                                                                                                                            
        P[j,:] /= P[j,:].sum()

Output:

array([[0.  , 0.5 , 0.  , 0.5 ],
       [0.5 , 0.25, 0.25, 0.  ],
       [0.  , 1.  , 0.  , 0.  ],
       [0.5 , 0.  , 0.  , 0.5 ]])

But you could also use the counter with a pre-allocated transition matrix, map the elements to indices, assign the counts as values, and normalize (last two steps just like I did).

Upvotes: 1

Ma0
Ma0

Reputation: 15204

This is a problem that fits itertools and Counter perfectly. Take a look at the following1:

l = [['A', 'B'],
     ['B', 'B'],
     ['B', 'C'],
     ['C', 'B'],
     ['B', 'A'],
     ['A', 'D'],
     ['D', 'D'],
     ['D', 'A'],
     ['A', 'B'],
     ['B', 'A'],
     ['A', 'D']]

from collections import Counter
from itertools import product, groupby

unique_elements = set(x for y in l for x in y)  # -> {'B', 'C', 'A', 'D'}

appearances = Counter(tuple(x) for x in l)

# generating all possible combinations to get the probabilities
all_combinations = sorted(list(product(unique_elements, unique_elements)))

# calculating and arranging the probabilities
table = []
for i, g in groupby(all_combinations, key=lambda x: x[0]):
    g = list(g)
    local_sum = sum(appearances.get(y, 0) for y in g)
    table.append([appearances.get(x, 0) / local_sum for x in g])

# [[0.0, 0.5, 0.0, 0.5], [0.5, 0.25, 0.25, 0.0], [0.0, 1.0, 0.0, 0.0], [0.5, 0.0, 0.0, 0.5]]

1 I am assuming you have a mistake on the formulation of your question: "...where P_AA counts how many ["A","A"] are in the array a and so on divided by P_AA + P_AB + P_AC + P_AD...". You mean to divide with something else, right?

Upvotes: 0

Lev Zakharov
Lev Zakharov

Reputation: 2428

from collections import Counter

a = [['A', 'B'],
     ['B', 'B'],
     ['B', 'C'],
     ['C', 'B'],
     ['B', 'A'],
     ['A', 'D'],
     ['D', 'D'],
     ['D', 'A'],
     ['A', 'B'],
     ['B', 'A'],
     ['A', 'D']]

counts = Counter(map(tuple, a))

letters = 'ABCD'
p = []    

for letter in letters:
     d = sum(v for k, v in counts.items() if k[0] == letter)
     p.append([counts.get((letter, x), 0) / d  for x in letters])

print(p)

Output:

[[0.0, 0.5, 0.0, 0.5],
 [0.5, 0.25, 0.25, 0.0],
 [0.0, 1.0, 0.0, 0.0],
 [0.5, 0.0, 0.0, 0.5]]

Upvotes: 1

DYZ
DYZ

Reputation: 57033

A pandas-based solution:

import pandas as pd
from collections import Counter
# Create a raw transition matrix
matrix = pd.Series(Counter(map(tuple, a))).unstack().fillna(0)
# Normalize the rows
matrix.divide(matrix.sum(axis=1),axis=0)
#     A     B     C    D
#A  0.0  0.50  0.00  0.5
#B  0.5  0.25  0.25  0.0
#C  0.0  1.00  0.00  0.0
#D  0.5  0.00  0.00  0.5

Upvotes: 5

Related Questions