【问题标题】:How to plot events on time on using matplotlib如何在使用 matplotlib 时按时绘制事件
【发布时间】:2012-01-07 19:32:20
【问题描述】:

我有 3 个列表,每个列表都包含数字,代表一个时间。时间代表事件的发生。例如,在这个A 中,我为每个事件A 的发生都有一个编号。我想在图表上表示这些数据。以下两种方式之一:

1)

aabaaabbccacac

2)

a-> xx xxx    x x
b->   x   xx  
c->         xx x x

【问题讨论】:

    标签: python time plot matplotlib


    【解决方案1】:

    你可以使用plt.hlines:

    import matplotlib.pyplot as plt
    import random
    import numpy as np
    import string
    
    def generate_data(N = 20):
        data = [random.randrange(3) for x in range(N)]
        A = [i for i, x in enumerate(data) if x == 0]
        B = [i for i, x in enumerate(data) if x == 1]
        C = [i for i, x in enumerate(data) if x == 2]
        return A,B,C
    
    def to_xy(*events):
        x, y = [], []
        for i,event in enumerate(events):
            y.extend([i]*len(event))
            x.extend(event)
        x, y = np.array(x), np.array(y)
        return x,y
    
    def event_string(x,y):
        labels = np.array(list(string.uppercase))        
        seq = labels[y[np.argsort(x)]]
        return seq.tostring()
    
    def plot_events(x,y):
        labels = np.array(list(string.uppercase))    
        plt.hlines(y, x, x+1, lw = 2, color = 'red')
        plt.ylim(max(y)+0.5, min(y)-0.5)
        plt.yticks(range(y.max()+1), labels)
        plt.show()
    
    A,B,C = generate_data(20)
    x,y = to_xy(A,B,C)
    print(event_string(x,y))
    plot_events(x,y)
    

    产量

    BBACBCACCABACCBCABCC
    

    【讨论】:

      【解决方案2】:

      作为对前面答案的扩展,您可以使用plt.hbar

      import matplotlib.pyplot as plt
      import numpy as np
      import string
      
      x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13])
      y = np.array([0, 0, 1, 0, 0, 0, 1, 1, 2, 2, 0, 2, 0, 2])
      
      labels = np.array(list(string.uppercase))    
      plt.barh(y, [1]*len(x), left=x, color = 'red', edgecolor = 'red', align='center', height=1)
      plt.ylim(max(y)+0.5, min(y)-0.5)
      plt.yticks(np.arange(y.max()+1), labels)
      plt.show()
      

      或者,你可以尝试这样的事情:

      import matplotlib.pyplot as plt
      import numpy as np
      
      data = [[1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0],
              [0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0], 
              [0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 3, 0, 3]]
      
      fig = plt.figure()
      ax = fig.add_subplot(111)
      ax.axes.get_yaxis().set_visible(False)
      ax.set_aspect(1)
      
      def avg(a, b):
          return (a + b) / 2.0
      
      for y, row in enumerate(data):
          for x, col in enumerate(row):
              x1 = [x, x+1]
              y1 = np.array([y, y])
              y2 = y1+1
              if col == 1:
                  plt.fill_between(x1, y1, y2=y2, color='red')
                  plt.text(avg(x1[0], x1[1]), avg(y1[0], y2[0]), "A", 
                                              horizontalalignment='center',
                                              verticalalignment='center')
              if col == 2:
                  plt.fill_between(x1, y1, y2=y2, color='orange')
                  plt.text(avg(x1[0], x1[0]+1), avg(y1[0], y2[0]), "B", 
                                              horizontalalignment='center',
                                              verticalalignment='center')
              if col == 3:
                  plt.fill_between(x1, y1, y2=y2, color='yellow')
                  plt.text(avg(x1[0], x1[0]+1), avg(y1[0], y2[0]), "C", 
                                              horizontalalignment='center',
                                              verticalalignment='center')
      
      plt.ylim(3, 0)
      plt.show()
      

      如果您希望所有插槽位于同一行,只需进行一些更改,如下所示:

      import matplotlib.pyplot as plt
      import numpy as np
      
      data = [[1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0],
              [0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0], 
              [0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 3, 0, 3]]
      
      fig = plt.figure()
      ax = fig.add_subplot(111)
      ax.axes.get_yaxis().set_visible(False)
      ax.set_aspect(1)
      
      def avg(a, b):
          return (a + b) / 2.0
      
      for y, row in enumerate(data):
          for x, col in enumerate(row):
              x1 = [x, x+1]
              y1 = [0, 0]
              y2 = [1, 1]
              if col == 1:
                  plt.fill_between(x1, y1, y2=y2, color='red')
                  plt.text(avg(x1[0], x1[1]), avg(y1[0], y2[0]), "A", 
                                              horizontalalignment='center',
                                              verticalalignment='center')
              if col == 2:
                  plt.fill_between(x1, y1, y2=y2, color='orange')
                  plt.text(avg(x1[0], x1[0]+1), avg(y1[0], y2[0]), "B", 
                                              horizontalalignment='center',
                                              verticalalignment='center')
              if col == 3:
                  plt.fill_between(x1, y1, y2=y2, color='yellow')
                  plt.text(avg(x1[0], x1[0]+1), avg(y1[0], y2[0]), "C", 
                                              horizontalalignment='center',
                                              verticalalignment='center')
      
      plt.ylim(1, 0)
      plt.show()
      

      第二个和第三个选项代码更多,但它们产生的结果要好得多。

      【讨论】:

      • 最后一个图表类型是否有名称(例如堆积条形图、面积线图等)?
      【解决方案3】:

      以@amillerrhodes 的最后一张图为基础并对其进行简化(同时删除文本部分):

      import matplotlib.pyplot as plt
      import numpy as np
      
      # run-length encoding, instead of a list of lists with a bunch of zeros
      data = [(2, 1), (1, 2), (3, 1), (2, 2), (2, 3), (1, 1), (1, 3), (1, 1), (1, 3)]
      
      fig = plt.figure()
      ax = fig.add_subplot(111)
      ax.axes.get_yaxis().set_visible(False)
      ax.set_aspect(1)
      
      for i, (num, cat) in enumerate(data):
      
          if i > 0:
              x_start += data[i-1][0] # get previous end position
          else:
              x_start = i             # start from 0
      
          x1 = [x_start, x_start+num]
      
          y1 = [0, 0]
          y2 = [1, 1]
      
          if cat == 1:
              plt.fill_between(x1, y1, y2=y2, color='red')
          if cat == 2:
              plt.fill_between(x1, y1, y2=y2, color='orange')
          if cat == 3:
              plt.fill_between(x1, y1, y2=y2, color='yellow')
      
      plt.ylim(1, 0)
      plt.show()
      

      【讨论】:

        【解决方案4】:

        这是您可以开始的一种方法:

        from matplotlib import pyplot as plt
        
        A = [23,45,56,78,32,11]
        B = [44,56,78,98]
        C = [23,46,67,79]
        
        x = []
        y = []
        for idx, lst in enumerate((A, B, C)):
            for time in lst:
                x.append(time)
                y.append(idx) 
        
        plt.ylim((-3,5))
        plt.yticks([0, 1, 2], ['A', 'B', 'C'])
        plt.scatter(x,y, color='r', s=70)
        plt.show()
        

        【讨论】:

          【解决方案5】:

          您可能需要考虑在 Edward Tufte 的The Visual Display of Quantitative Information 的封面上使用的火车时刻表显示。这对于显示不同时间的事件变化率很有用(参见第 31 页,第 2 版的解释),但这仅适用于您的事件发生在不规则时间。

          无论哪种方式,其他答案都为您的第二个请求提供了很好的选择。您可能只想绘制线条,使用 pyplot(或轴)plot(x) 命令。您可以更改其他答案中显示的标签,以便它们是代表您的事件的文本。最后为了模拟火车时刻表图中显示的效果,您可以使用 pyplot grid 方法(或 axes.xaxis.grid)设置网格。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-12-20
            • 1970-01-01
            • 2015-06-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-12-07
            相关资源
            最近更新 更多