在 Python 中生成马尔可夫转移矩阵

Posted

技术标签:

【中文标题】在 Python 中生成马尔可夫转移矩阵【英文标题】:Generating Markov transition matrix in Python 【发布时间】:2018-03-21 07:10:35 【问题描述】:

假设我有一系列 4 种可能的马尔可夫状态(A、B、C、D):

X = [A, B, B, C, B, A, D, D, A, B, A, D, ....]

如何使用 Python 生成马尔可夫变换矩阵?矩阵必须是 4 x 4,显示从每个状态移动到其他 3 个状态的概率。 我一直在网上查看许多示例,但在所有示例中,都给出了矩阵,而不是根据数据计算得出的。 我也研究了hmmlearn,但我没有读到如何让它吐出转换矩阵。有没有可以用于此目的的库?

这是我在 Python 中尝试做的确切事情的 R 代码: https://stats.stackexchange.com/questions/26722/calculate-transition-matrix-markov-in-r

【问题讨论】:

在原始 Python 中,您需要使用列表列表。这种事情在numpypandas 中更自然地完成。如果您想使用其中一种工具,也许您可​​以添加适当的标签。无论如何,您的问题的输入是什么?有限的状态列表? “从每个状态移动到其他 3 个状态的概率”不应该是“到 4 个状态中的任何一个”,因为一个状态可以持续一个或多个时间步长吗? 【参考方案1】:

这可能会给你一些想法:

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

def rank(c):
    return ord(c) - ord('A')

T = [rank(c) for c in transitions]

#create matrix of zeros

M = [[0]*4 for _ in range(4)]

for (i,j) in zip(T,T[1:]):
    M[i][j] += 1

#now convert to probabilities:
for row in M:
    n = sum(row)
    if n > 0:
        row[:] = [f/sum(row) for f in row]

#print M:

for row in M:
    print(row)

输出:

[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]

On Edit这是一个实现上述想法的函数:

#the following code takes a list such as
#[1,1,2,6,8,5,5,7,8,8,1,1,4,5,5,0,0,0,1,1,4,4,5,1,3,3,4,5,4,1,1]
#with states labeled as successive integers starting with 0
#and returns a transition matrix, M,
#where M[i][j] is the probability of transitioning from i to j

def transition_matrix(transitions):
    n = 1+ max(transitions) #number of states

    M = [[0]*n for _ in range(n)]

    for (i,j) in zip(transitions,transitions[1:]):
        M[i][j] += 1

    #now convert to probabilities:
    for row in M:
        s = sum(row)
        if s > 0:
            row[:] = [f/s for f in row]
    return M

#test:

t = [1,1,2,6,8,5,5,7,8,8,1,1,4,5,5,0,0,0,1,1,4,4,5,1,3,3,4,5,4,1,1]
m = transition_matrix(t)
for row in m: print(' '.join('0:.2f'.format(x) for x in row))

输出:

0.67 0.33 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00 0.50 0.12 0.12 0.25 0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 1.00 0.00 0.00
0.00 0.00 0.00 0.50 0.50 0.00 0.00 0.00 0.00
0.00 0.20 0.00 0.00 0.20 0.60 0.00 0.00 0.00
0.17 0.17 0.00 0.00 0.17 0.33 0.00 0.17 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 1.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 1.00
0.00 0.33 0.00 0.00 0.00 0.33 0.00 0.00 0.33

【讨论】:

谢谢,约翰。唯一的问题是您的代码产生的转换矩阵不完全是马尔可夫转换矩阵。您的代码将行单元格除以 n=11。无论如何,您是否可以方便地修复代码以在我添加到问题的 “更新” 部分的屏幕截图中给出表格? @st19297 我刚刚用特定行的n 替换了全局n(使条目成为条件概率)。由于我不喜欢除以 0,所以上面的代码保持一排零不变。当没有观察到从给定状态的转换时,不可能估计从给定状态的转换概率。【参考方案2】:

如果您想在 pandas 中完成所有操作,这里有一种适用于非数字数据的方法:

import pandas as pd
transitions = ['A', 'B', 'B', 'C', 'B', 'A', 'D', 'D', 'A', 'B', 'A', 'D']

df = pd.DataFrame(transitions)

# create a new column with data shifted one space
df['shift'] = df[0].shift(-1)

# add a count column (for group by function)
df['count'] = 1

# groupby and then unstack, fill the zeros
trans_mat = df.groupby([0, 'shift']).count().unstack().fillna(0)

# normalise by occurences and save values to get transition matrix
trans_mat = trans_mat.div(trans_mat.sum(axis=1), axis=0).values

它比纯 python 方法慢,但可能值得因为灵活性和避免创建自己的函数。

【讨论】:

你救了我的命,这个解决方案很完美:【参考方案3】:

以下代码提供了关于马尔可夫转移矩阵阶数1的另一种解决方案。您的数据可以是整数列表、字符串列表或字符串。消极的想法是这个解决方案 - 很可能 - 需要时间和内存。

    创建 1 阶马尔可夫转移矩阵(二元组) 生成 1000 个整数,以便将马尔可夫转移矩阵训练为数据集。 训练马尔可夫转移矩阵

直到这里我们有了问题的解决方案。下面的代码试图解决一个额外的问题。具体来说就是根据训练好的马尔科夫任务生成数据。

    将马尔可夫转移矩阵的概率转换为累积(算术编码) 生成 30 个数据
import pandas as pd

def transition_matrix_order1(data):
    alphabet = []
    for element in data:
        if element not in alphabet:
            alphabet.append(element)
    alphabet.sort()
    
    previous = data[0]
    matrix = pd.DataFrame(0.0, index=alphabet, columns=alphabet)
    
    for i in data[1:]:
        matrix[i][previous]    += 1.0
        previous = i
    
    total = matrix.sum()
    for element in alphabet:
        matrix[element] = matrix.div(total[element])[element]
    
    return matrix, alphabet



#create data using random integers========
import random
data = [random.randint(1,5) for i in range(1000)] #You can also put list of strings or a string as input data



#create markov transition matrix order 1 (bigram)
markov_matrix, alphabet = transition_matrix_order1(data)



#=the following code uses the probabilities in order to create new data.=



#transform probabilities of markov transition matrix to cumulative
for column in alphabet:
    for pos, index in enumerate(alphabet[1:]):
        markov_matrix[column][index] += markov_matrix[column][alphabet[pos]]




#generating 30 data
generated_data = []
feed = random.choice(alphabet)
generated_data.append(feed)
for i in range(30):
    random_value = random.uniform(0, 1)
    for i in alphabet:
        if markov_matrix[feed][i] >= random_value:
            generated_data.append(i)
            feed = i
            break



print(generated_data)

【讨论】:

欢迎来到 ***。虽然此代码可能会回答问题,但提供有关 如何 和/或 为什么 解决问题的附加上下文将提高​​答案的长期价值。 你是对的!谢谢你的重播!我编辑了我的帖子并添加了一些解释。如果你愿意,我可以删除额外的上下文。

以上是关于在 Python 中生成马尔可夫转移矩阵的主要内容,如果未能解决你的问题,请参考以下文章

隐马尔可夫模型

如何处理马尔可夫链的转移矩阵的负稳态概率?

r 在R中生成带有马尔可夫链的文本

应用随机过程02:马尔可夫链及其概率分布

用 R 中的大马尔可夫转移矩阵计算从 s1:s400 到 sn 的概率需要永远

在 Swift 中使用马尔可夫链生成文本