【问题标题】:Building a Transition Matrix using words in Python/Numpy在 Python/Numpy 中使用单词构建转换矩阵
【发布时间】:2017-11-15 00:36:32
【问题描述】:

我正在尝试用这些数据构建一个 3x3 转换矩阵

days=['rain', 'rain', 'rain', 'clouds', 'rain', 'sun', 'clouds', 'clouds', 
  'rain', 'sun', 'rain', 'rain', 'clouds', 'clouds', 'sun', 'sun', 
  'clouds', 'clouds', 'rain', 'clouds', 'sun', 'rain', 'rain', 'sun',
  'sun', 'clouds', 'clouds', 'rain', 'rain', 'sun', 'sun', 'rain', 
  'rain', 'sun', 'clouds', 'clouds', 'sun', 'sun', 'clouds', 'rain', 
  'rain', 'rain', 'rain', 'sun', 'sun', 'sun', 'sun', 'clouds', 'sun', 
  'clouds', 'clouds', 'sun', 'clouds', 'rain', 'sun', 'sun', 'sun', 
  'clouds', 'sun', 'rain', 'sun', 'sun', 'sun', 'sun', 'clouds', 
  'rain', 'clouds', 'clouds', 'sun', 'sun', 'sun', 'sun', 'sun', 'sun', 
  'clouds', 'clouds', 'clouds', 'clouds', 'clouds', 'sun', 'rain', 
  'rain', 'rain', 'clouds', 'sun', 'clouds', 'clouds', 'clouds', 'rain', 
  'clouds', 'rain', 'sun', 'sun', 'clouds', 'sun', 'sun', 'sun', 'sun',
  'sun', 'sun', 'rain']

目前,我使用一些临时字典和一些列表来分别计算每种天气的概率。它不是一个很好的解决方案。有人可以指导我更合理地解决这个问题吗?

self.transitionMatrix=np.zeros((3,3))

#the columns are today
sun_total_count = 0
temp_dict={'sun':0, 'clouds':0, 'rain':0}
total_runs = 0
for (x, y), c in Counter(zip(data, data[1:])).items():
    #if column 0 is sun
    if x is 'sun':
        #find the sum of all the numbers in this column
        sun_total_count +=  c
        total_runs += 1
        if y is 'sun':
            temp_dict['sun'] = c
        if y is 'clouds':
            temp_dict['clouds'] = c
        if y is 'rain':
            temp_dict['rain'] = c

        if total_runs is 3:
            self.transitionMatrix[0][0] = temp_dict['sun']/sun_total_count
            self.transitionMatrix[1][0] = temp_dict['clouds']/sun_total_count
            self.transitionMatrix[2][0] = temp_dict['rain']/sun_total_count

return self.transitionMatrix

对于每种天气,我需要计算第二天的概率

【问题讨论】:

  • 你的解决方案有效吗?
  • @wwii 是的,它有效。但是你可以看到它只计算第一列,现在我必须为第二列和第三列创建两个新的字典。然后为他们检查一大堆 if 语句。它会变得更加混乱:(我想知道是否有更优雅的方法
  • 将 dict 构造代码放入函数中,然后遍历将相关数据传递给该函数的列。

标签: python numpy markov-chains


【解决方案1】:

如果您不介意使用pandas,可以使用单线法提取转换概率:

pd.crosstab(pd.Series(days[1:],name='Tomorrow'),
            pd.Series(days[:-1],name='Today'),normalize=1)

输出:

Today      clouds      rain       sun
Tomorrow                             
clouds    0.40625  0.230769  0.309524
rain      0.28125  0.423077  0.142857
sun       0.31250  0.346154  0.547619

在“雨”列、“太阳”行中找到今天下雨的明天晴天的(前向)概率。如果您想要后向概率(根据今天的天气,昨天的天气可能是什么),请切换前两个参数。

如果您希望将概率存储在行而不是列中,请设置normalize=0,但请注意,如果您直接在此示例中执行此操作,则会获得存储为行的向后概率。如果您想获得与上述相同但转置的结果,您可以 a) 是,转置或 b) 切换前两个参数的顺序并将 normalize 设置为 0。

如果您只想将结果保留为 numpy 二维数组(而不是作为 pandas 数据框),请在最后一个括号后键入 .values

【讨论】:

  • 访问您使用days[1:]days[:-1] 而不仅仅是调用days 的数据时的目的是什么?
  • @Will-i-am,如果你按照你说的那样尝试,你会得到一个三乘三的单位矩阵。访问数据的目的是创建两个系列,其中一个系列在位置 t 包含第 t 天的条目,而另一个包含第 t+1 天的条目。
【解决方案2】:

为此,我喜欢 pandasitertools 的组合。代码块比上面的要长一点,但不要将冗长与速度混为一谈。 (window func 应该非常快;当然 pandas 部分会更慢。)

首先,创建一个“窗口”函数。这是 itertools 食谱中的一个。这会将您带到 转换元组列表(从 state1 到 state2)。

from itertools import islice

def window(seq, n=2):
    """Sliding window width n from seq.  From old itertools recipes."""
    it = iter(seq)
    result = tuple(islice(it, n))
    if len(result) == n:
        yield result
    for elem in it:
        result = result[1:] + (elem,)
        yield result

# list(window(days))
# [('rain', 'rain'),
#  ('rain', 'rain'),
#  ('rain', 'clouds'),
#  ('clouds', 'rain'),
#  ('rain', 'sun'),
# ...

然后使用pandas groupby + value counts 操作得到每个state1到每个state2的转换矩阵:

import pandas as pd

pairs = pd.DataFrame(window(days), columns=['state1', 'state2'])
counts = pairs.groupby('state1')['state2'].value_counts()
probs = (counts / counts.sum()).unstack()

您的结果如下所示:

print(probs)
state2  clouds  rain   sun
state1                    
clouds    0.13  0.09  0.10
rain      0.06  0.11  0.09
sun       0.13  0.06  0.23

【讨论】:

    【解决方案3】:

    这是一个“纯”numpy 解决方案,它创建 3x3 表,其中第零个暗淡(行号)对应于今天,最后一个暗淡(列号)对应于明天。

    从单词到索引的转换是通过在第一个字母之后截断然后使用查找表来完成的。

    用于计数numpy.add.at

    写这篇文章时考虑到了效率。它在一秒钟内完成一百万个单词。

    import numpy as np
    
    report = [
      'rain', 'rain', 'rain', 'clouds', 'rain', 'sun', 'clouds', 'clouds', 
      'rain', 'sun', 'rain', 'rain', 'clouds', 'clouds', 'sun', 'sun', 
      'clouds', 'clouds', 'rain', 'clouds', 'sun', 'rain', 'rain', 'sun',
      'sun', 'clouds', 'clouds', 'rain', 'rain', 'sun', 'sun', 'rain', 
      'rain', 'sun', 'clouds', 'clouds', 'sun', 'sun', 'clouds', 'rain', 
      'rain', 'rain', 'rain', 'sun', 'sun', 'sun', 'sun', 'clouds', 'sun', 
      'clouds', 'clouds', 'sun', 'clouds', 'rain', 'sun', 'sun', 'sun', 
      'clouds', 'sun', 'rain', 'sun', 'sun', 'sun', 'sun', 'clouds', 
      'rain', 'clouds', 'clouds', 'sun', 'sun', 'sun', 'sun', 'sun', 'sun', 
      'clouds', 'clouds', 'clouds', 'clouds', 'clouds', 'sun', 'rain', 
      'rain', 'rain', 'clouds', 'sun', 'clouds', 'clouds', 'clouds', 'rain', 
      'clouds', 'rain', 'sun', 'sun', 'clouds', 'sun', 'sun', 'sun', 'sun',
      'sun', 'sun', 'rain']
    
    # create np array, keep only first letter (by forcing dtype)
    # obviously, this only works because rain, sun, clouds start with different
    # letters
    # cast to int type so we can use for indexing
    ri = np.array(report, dtype='|S1').view(np.uint8)
    # create lookup
    c, r, s = 99, 114, 115 # you can verify this using chr and ord
    lookup = np.empty((s+1,), dtype=int)
    lookup[[c, r, s]] = np.arange(3)
    # translate c, r, s to 0, 1, 2
    rc = lookup[ri]
    # get counts (of pairs (today, tomorrow))
    cnts = np.zeros((3, 3), dtype=int)
    np.add.at(cnts, (rc[:-1], rc[1:]), 1)
    # or as probs
    probs = cnts / cnts.sum()
    # or as condional probs (if today is sun how probable is rain tomorrow etc.)
    cond = cnts / cnts.sum(axis=-1, keepdims=True)
    
    print(cnts)
    print(probs)
    print(cond)
    
    # [13  9 10]
    #  [ 6 11  9]
    #  [13  6 23]]
    # [[ 0.13  0.09  0.1 ]
    #  [ 0.06  0.11  0.09]
    #  [ 0.13  0.06  0.23]]
    # [[ 0.40625     0.28125     0.3125    ]
    #  [ 0.23076923  0.42307692  0.34615385]
    #  [ 0.30952381  0.14285714  0.54761905]]
    

    【讨论】:

    • 这很快。您能否详细说明为什么将原始字符串映射到其首字母的chr
    • 另外:你可以用np.unique(report, return_inverse=True)映射到整数。
    • @BradSolomon 1. 截断使数组构造函数更快。我认为,如果您不强制使用 dtype,那么 numpy 必须进行两次传递,一次只是为了找到最长的字符串,因此它知道 dtype 的大小。 2. 单字母id可以作为索引进入查找表;因为我们可以非常便宜地(在 numpy 中查看转换基本上是免费的)将这些字符解释为不是很大的数字,所以可以通过指针算术来完成查找。作为一个额外的好处,这种查找正是 numpy 花式索引所做的,所以它是一个以 C 速度循环的单行。 HTH
    • @BradSolomon 由于 np.unique 比我们在这里所做的更通用,我希望它会更慢。例如,我认为它不能使用我刚才描述的查找技巧。
    【解决方案4】:
    1. 将报告从天数转换为索引代码。
    2. 遍历数组,获取昨天和今天天气的代码。
    3. 使用这些索引来计算 3x3 矩阵中的组合。

    以下是帮助您入门的编码设置。

    report = [
      'rain', 'rain', 'rain', 'clouds', 'rain', 'sun', 'clouds', 'clouds', 
      'rain', 'sun', 'rain', 'rain', 'clouds', 'clouds', 'sun', 'sun', 
      'clouds', 'clouds', 'rain', 'clouds', 'sun', 'rain', 'rain', 'sun',
      'sun', 'clouds', 'clouds', 'rain', 'rain', 'sun', 'sun', 'rain', 
      'rain', 'sun', 'clouds', 'clouds', 'sun', 'sun', 'clouds', 'rain', 
      'rain', 'rain', 'rain', 'sun', 'sun', 'sun', 'sun', 'clouds', 'sun', 
      'clouds', 'clouds', 'sun', 'clouds', 'rain', 'sun', 'sun', 'sun', 
      'clouds', 'sun', 'rain', 'sun', 'sun', 'sun', 'sun', 'clouds', 
      'rain', 'clouds', 'clouds', 'sun', 'sun', 'sun', 'sun', 'sun', 'sun', 
      'clouds', 'clouds', 'clouds', 'clouds', 'clouds', 'sun', 'rain', 
      'rain', 'rain', 'clouds', 'sun', 'clouds', 'clouds', 'clouds', 'rain', 
      'clouds', 'rain', 'sun', 'sun', 'clouds', 'sun', 'sun', 'sun', 'sun',
      'sun', 'sun', 'rain']
    
    weather_dict = {"sun":0, "clouds":1, "rain": 2}
    weather_code = [weather_dict[day] for day in report]
    print weather_code
    
    for n in range(1, len(weather_code)):
        yesterday_code = weather_code[n-1]
        today_code     = weather_code[n]
    
    # You now have the indicies you need for your 3x3 matrix.
    

    【讨论】:

      【解决方案5】:

      您似乎想要创建一个矩阵,其中包含太阳后下雨或太阳后出现云(或等)的概率矩阵。您可以像这样吐出概率矩阵(不是数学术语):

      def probabilityMatrix():
          tomorrowsProbability=np.zeros((3,3))
          occurancesOfEach = Counter(data)
          myMatrix = Counter(zip(data, data[1:]))
          probabilityMatrix = {key : myMatrix[key] / occurancesOfEach[key[0]] for key in myMatrix}
          return probabilityMatrix
      
      print(probabilityMatrix())
      

      但是,您可能想根据今天的天气吐出每种天气类型的概率:

      def getTomorrowsProbability(weather):
          probMatrix = probabilityMatrix()
          return {key[1] : probMatrix[key]  for key in probMatrix if key[0] == weather}
      
      print(getTomorrowsProbability('sun'))
      

      【讨论】:

        【解决方案6】:

        下面是使用熊猫的另一种选择。转换列表可以替换为“rain”、“clouds”等。

        import pandas as pd
        transitions = ['A', 'B', 'B', 'C', 'B', 'A', 'D', 'D', 'A', 'B', 'A', 'D'] * 2
        df = pd.DataFrame(columns = ['state', 'next_state'])
        for i, val in enumerate(transitions[:-1]): # We don't care about last state
            df_stg = pd.DataFrame(index=[0])
            df_stg['state'], df_stg['next_state'] = transitions[i], transitions[i+1]
            df = pd.concat([df, df_stg], axis = 0)
        cross_tab = pd.crosstab(df['state'], df['next_state'])
        cross_tab.div(cross_tab.sum(axis=1), axis=0)
        

        【讨论】:

        • 由于pd.concat() 行,这非常低效,尤其是当您尝试逐行读取语料库时。 @HerrIvan 的回答要快得多。
        猜你喜欢
        • 1970-01-01
        • 2014-06-20
        • 2016-08-25
        • 1970-01-01
        • 1970-01-01
        • 2020-11-07
        • 2021-06-19
        • 2021-03-24
        • 1970-01-01
        相关资源
        最近更新 更多