【问题标题】:How to plot multi-color line if x-axis is date time index of pandas如果x轴是熊猫的日期时间索引,如何绘制多色线
【发布时间】:2017-11-22 09:38:54
【问题描述】:

我正在尝试使用熊猫系列绘制一条多色线。我知道matplotlib.collections.LineCollection 会大幅提升效率。 但是 LineCollection 要求线段必须是浮动的。我想使用 pandas 的数据时间索引作为 x 轴。

points = np.array((np.array[df_index.astype('float'), values]).T.reshape(-1,1,2))
segments = np.concatenate([points[:-1],points[1:]], axis=1)
lc = LineCollection(segments)
fig = plt.figure()
plt.gca().add_collection(lc)
plt.show()

但是图片不能让我满意。 有什么解决办法吗?

【问题讨论】:

    标签: python pandas matplotlib


    【解决方案1】:

    要生成多色线,您需要先将日期转换为数字,因为 matplotlib 在内部仅适用于数值。

    对于转换 matplotlib 提供了matplotlib.dates.date2num。这可以理解日期时间对象,因此您首先需要使用 series.index.to_pydatetime() 将时间序列转换为日期时间,然后应用 date2num。

    s = pd.Series(y, index=dates)
    inxval = mdates.date2num(s.index.to_pydatetime())
    

    然后您可以像往常一样使用数字点,例如绘制为 Polygon 或 LineCollection[1,2]。

    完整的例子:

    import pandas as pd
    import matplotlib.pyplot as plt
    import matplotlib.dates as mdates
    import numpy as np
    from matplotlib.collections import LineCollection
    
    dates = pd.date_range("2017-01-01", "2017-06-20", freq="7D" )
    y = np.cumsum(np.random.normal(size=len(dates)))
    
    s = pd.Series(y, index=dates)
    
    fig, ax = plt.subplots()
    
    #convert dates to numbers first
    inxval = mdates.date2num(s.index.to_pydatetime())
    points = np.array([inxval, s.values]).T.reshape(-1,1,2)
    segments = np.concatenate([points[:-1],points[1:]], axis=1)
    
    lc = LineCollection(segments, cmap="plasma", linewidth=3)
    # set color to date values
    lc.set_array(inxval)
    # note that you could also set the colors according to y values
    # lc.set_array(s.values)
    # add collection to axes
    ax.add_collection(lc)
    
    
    ax.xaxis.set_major_locator(mdates.MonthLocator())
    ax.xaxis.set_minor_locator(mdates.DayLocator())
    monthFmt = mdates.DateFormatter("%b")
    ax.xaxis.set_major_formatter(monthFmt)
    ax.autoscale_view()
    plt.show()
    


    由于人们似乎对抽象这个概念有问题,这里有一段与上面相同的代码,没有使用 pandas 并具有独立的颜色数组:

    import matplotlib.pyplot as plt
    import matplotlib.dates as mdates
    import numpy as np; np.random.seed(42)
    from matplotlib.collections import LineCollection
    
    dates = np.arange("2017-01-01", "2017-06-20", dtype="datetime64[D]" )
    y = np.cumsum(np.random.normal(size=len(dates)))
    c = np.cumsum(np.random.normal(size=len(dates)))
    
    
    fig, ax = plt.subplots()
    
    #convert dates to numbers first
    inxval = mdates.date2num(dates)
    points = np.array([inxval, y]).T.reshape(-1,1,2)
    segments = np.concatenate([points[:-1],points[1:]], axis=1)
    
    lc = LineCollection(segments, cmap="plasma", linewidth=3)
    # set color to date values
    lc.set_array(c)
    ax.add_collection(lc)
    
    ax.xaxis_date()
    ax.autoscale_view()
    plt.show()
    

    【讨论】:

    • ax.plot(s.index, s.values, alpha=0.0) 有必要吗?因为我发现这需要太多时间。
    • 不,没有必要。我把它放在那里的原因是让情节自动缩放。您也可以使用 xlim 和 ylim 手动设置限制。
    • 我在使用这种方法时遇到了另一个问题。因为我在绘制财务数据,所以 datetime 索引是不连续的,比如 2017/6/21 11:30:00 在 2017/6/21 13:30:00 旁边。但在情节上,时间轴是连续的。所以剧情上有很多跳跃。有什么好的解决方案吗?再次感谢您的好意!
    • 解决方案是什么?期望的结果是什么?
    • 目前存在冗余时间坐标,如2017/6/21 11:31:00,未包含在原始数据中。我想消除多余的时间坐标。这就是期望的结果。
    【解决方案2】:

    ImportanceOfBeingErnest 是一个非常好的答案,为我节省了很多时间。我想分享一下我如何使用上述答案根据来自 pandas DataFrame 的信号更改颜色。

    import matplotlib.dates as mdates
    # import matplotlib.pyplot as plt
    # import numpy as np
    # import pandas as pd
    from matplotlib.collections import LineCollection
    from matplotlib.colors import ListedColormap, BoundaryNorm
    

    制作测试数据框

    equity = pd.DataFrame(index=pd.date_range('20150701', periods=150))
    equity['price'] = np.random.uniform(low=15500, high=18500, size=(150,))
    equity['signal'] = 0
    equity.signal[15:45] = 1
    equity.signal[60:90] = -1
    equity.signal[105:135] = 1
    
    # Create a colormap for crimson, limegreen and gray and a norm to color
    # signal = -1 crimson, signal = 1 limegreen, and signal = 0 lightgray
    cmap = ListedColormap(['crimson', 'lightgray', 'limegreen'])
    norm = BoundaryNorm([-1.5, -0.5, 0.5, 1.5], cmap.N)
    
    # Convert dates to numbers
    inxval = mdates.date2num(equity.index.to_pydatetime())
    
    # Create a set of line segments so that we can color them individually
    # This creates the points as a N x 1 x 2 array so that we can stack points
    # together easily to get the segments. The segments array for line collection
    # needs to be numlines x points per line x 2 (x and y)
    points = np.array([inxval, equity.price.values]).T.reshape(-1,1,2)
    segments = np.concatenate([points[:-1],points[1:]], axis=1)
    
    # Create the line collection object, setting the colormapping parameters.
    # Have to set the actual values used for colormapping separately.
    lc = LineCollection(segments, cmap=cmap, norm=norm, linewidth=2)
    
    # Set color using signal values
    lc.set_array(equity.signal.values)
    
    fig, ax = plt.subplots()
    fig.autofmt_xdate()
    
    # Add collection to axes
    ax.add_collection(lc)
    
    plt.xlim(equity.index.min(), equity.index.max())
    plt.ylim(equity.price.min(), equity.price.max())
    plt.tight_layout()
    
    # plt.savefig('test_mline.png', dpi=150)
    plt.show()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-11
      • 1970-01-01
      • 2016-07-29
      • 2023-01-13
      相关资源
      最近更新 更多