【问题标题】:Plot an infinite line between two pandas series points在两个熊猫系列点之间绘制一条无限线
【发布时间】:2021-06-07 09:43:39
【问题描述】:

我想在熊猫系列形式的两个点之间绘制一条无限的非结束线。我能够成功地在点之间绘制一条标准线,但是我不希望这条线“结束”,而是应该继续。对此进行扩展,我还想将这条新的无限线的值提取到一个新的数据帧中,这样我就可以看到给定的 x 值具有什么对应的线值。

data = yf.download("AAPL", start="2021-01-01", interval = "1d").drop(columns=['Adj Close'])
data = data[30:].rename(columns={"Open": "open", "High": "high", "Low": "low", "Close": "close", "Volume": "volume"})
local_max = argrelextrema(data['high'].values, np.greater)[0]
local_min = argrelextrema(data['low'].values, np.less)[0]
highs = data.iloc[local_max,:]
lows = data.iloc[local_min,:]

highesttwo = highs["high"].nlargest(2)
lowesttwo = lows["low"].nsmallest(2)

fig = plt.figure(figsize=[10,7])
data['high'].plot(marker='o', markevery=local_max)
data['low'].plot(marker='o', markevery=local_min)
highesttwo.plot()
lowesttwo.plot()
plt.show()

目前我的情节是这样的:

我希望它看起来像这样,并且能够获取相应 x 值的行的值。

【问题讨论】:

    标签: python pandas matplotlib plot geometry


    【解决方案1】:

    这可以通过几个步骤完成,如以下示例所示,其中使用线方程的slope-intercept form 通过逐元素运算(即矢量化)来计算线。

    股票数据的频率基于证券交易所的开市日期。 pandas 不会自动识别此频率,因此.plot 方法会生成一个带有连续日期的图,其中 x 轴包含没有数据的日期。这可以通过设置参数 use_index=False 来避免,这样 x 轴将使用从零开始的整数。

    接下来的挑战是创建格式良好的刻度标签。以下示例尝试通过使用列表推导来选择刻度位置并格式化标签来模仿 pandas 刻度格式。如果日期范围显着延长或缩短,则需要进行调整。

    import numpy as np                      # v 1.19.2
    import pandas as pd                     # v 1.2.3
    import matplotlib.pyplot as plt         # v 3.3.4
    from scipy.signal import argrelextrema  # v 1.6.1
    import yfinance as yf                   # v 0.1.54
    
    # Import data
    data = (yf.download('AAPL', start='2021-01-04', end='2021-03-15', interval='1d')
             .drop(columns=['Adj Close']))
    data = data.rename(columns={'Open': 'open', 'High': 'high', 'Low': 'low',
                                'Close': 'close', 'Volume': 'volume'})
    
    # Extract points and get appropriate x values for the points by using
    # reset_index for highs/lows
    local_max = argrelextrema(data['high'].values, np.greater)[0]
    local_min = argrelextrema(data['low'].values, np.less)[0]
    highs = data.reset_index().iloc[local_max, :]
    lows = data.reset_index().iloc[local_min, :]
    htwo = highs['high'].nlargest(2).sort_index()
    ltwo = lows['low'].nsmallest(2).sort_index()
    
    # Compute slope and y-intercept for each line
    slope_high, intercept_high = np.polyfit(htwo.index, htwo, 1)
    slope_low, intercept_low = np.polyfit(ltwo.index, ltwo, 1)
    
    # Create dataframe for each line by using reindexed htwo and ltwo so that the
    # index extends to the end of the dataset and serves as the x variable then
    # compute y values
    # High
    line_high = htwo.reindex(range(htwo.index[0], len(data))).reset_index()
    line_high.columns = ['x', 'y']
    line_high['y'] = slope_high*line_high['x'] + intercept_high
    # Low
    line_low = ltwo.reindex(range(ltwo.index[0], len(data))).reset_index()
    line_low.columns = ['x', 'y']
    line_low['y'] = slope_low*line_low['x'] + intercept_low
    
    # Plot data using pandas plotting function and add lines with matplotlib function
    fig = plt.figure(figsize=[10,6])
    ax = data['high'].plot(marker='o', markevery=local_max, use_index=False)
    data['low'].plot(marker='o', markevery=local_min, use_index=False)
    ax.plot(line_high['x'], line_high['y'])
    ax.plot(line_low['x'], line_low['y'])
    ax.set_xlim(0, len(data)-1)
    
    # Set major and minor tick locations
    tks_maj = [idx for idx, timestamp in enumerate(data.index)
               if (timestamp.month != data.index[idx-1].month) | (idx == 0)]
    tks_min = range(len(data))
    ax.set_xticks(tks_maj)
    ax.set_xticks(tks_min, minor=True)
    
    # Format major and minor tick labels
    labels_maj = [ts.strftime('\n%b\n%Y') if (data.index[tks_maj[idx]].year
                  != data.index[tks_maj[idx-1]].year) | (idx == 0)
                  else ts.strftime('\n%b') for idx, ts in enumerate(data.index[tks_maj])]
    labels_min = [ts.strftime('%d') if (idx+3)%5 == 0 else ''
                  for idx, ts in enumerate(data.index[tks_min])]
    ax.set_xticklabels(labels_maj)
    ax.set_xticklabels(labels_min, minor=True)
    
    plt.show()
    



    您可以找到更多刻度格式 herehere in Solution 1 的示例。

    Date string format codes

    【讨论】:

    • 这似乎可行,但是在很多情况下结果会如下所示:imgur.com/73HPGhf
    • 那么究竟有什么解决方案呢?我如何不通过 matplotlib 绘图并仍然保留正确的线以及确保包含绘图点的数据框实际上是正确的?
    • 我使用的是代码的精确复制粘贴。我已经把它放在这个 pastebin 里了,我仍然得到波浪线。只需更改第 15 行的 tail(number) 即可测试具有不同数据输入的代码。 pastebin.com/FsjMevxR
    • @sword134 感谢您分享您的代码。我发现我的错误是没有使用 yf 数据测试我的答案,因为我避免额外的包导入以使我的答案中的代码更具可持续性(过去 yf 存在问题)。对此我深表歉意。问题是熊猫试图推断日期时间索引的频率。在您的问题中,样本 data[30:] 恰好足够短,以至于它仅包含 pandas 识别为对应于“工作日频率”的日历日(如我的回答中所使用的那样)。
    • 因此 pandas 以该频率绘制数据,仅绘制日期时间索引中包含的日期并很好地格式化标签。现在,如果您将示例更改为data[29:],您将看到标签看起来不同(matplotlib 样式)并且行更长,因为它现在包含周末。为什么?因为 2 月 15 日是纽约证券交易所的假期,所以该日期不包含在表格中。这会导致 pandas 不再识别数据的频率,因此它使用 matplotlib 默认值来处理 x 轴的日期时间索引。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-20
    • 2018-12-19
    • 2017-09-20
    • 1970-01-01
    • 2021-06-16
    • 2023-02-07
    相关资源
    最近更新 更多