如何以绘图方式显示带有范围分隔的图形对象散点图(类型='线')以不显示排除的范围?

问题描述 投票:0回答:1

我目前正在尝试使用plotly 将线图(最终具有多条迹线)绘制为图形对象。它基于时间序列股票市场价格,并且由于我只需要/拥有某些日期的数据,因此我需要根据日期或日期时间值排除 x 轴上的不相关范围。我的数据集可以这样考虑:

d = {'plotPrice': [107, 107.1, 107.2, 106.9, 107, 106.5, 106, 106.2, 106.8, 107], 
     'dateTime': [pd.Timestamp('2023-10-24 08:01:56.668000'), pd.Timestamp('2023-10-24 09:25:58.272'), pd.Timestamp('2023-10-26 11:42:09.199'),
                 pd.Timestamp('2023-10-26 14:54:06.095'),pd.Timestamp('2023-10-26 15:25:50.430'),pd.Timestamp('2023-10-30 09:15:33.546'),
                 pd.Timestamp('2023-10-30 11:34:44.861'),pd.Timestamp('2023-10-30 14:18:27.706'),pd.Timestamp('2023-10-30 15:02:33.771'),
                 pd.Timestamp('2023-10-30 16:30:37.412')]}
df = pd.DataFrame(data=d)

数据基于刻度数据,因此没有以相等大小的间隔组织,例如 1 分钟或类似的间隔,并且由于其他原因,我无法按时间间隔对其进行聚合。这意味着我需要将 xaxis 识别为

type=date
轴才能正确缩放。我当前通过绘图和应用范围中断进行绘图的函数如下所示:

def generate_plotly_line_plot(df, titlestr, ccy,saveBool=False):

    # Figure and Layout
    fig = go.Figure()
    fig.update_layout(
        autosize=False,
        width=1000,
        height=500,
        margin=dict(
            l=50,
            r=50,
            b=50,
            t=50,
            pad=4
        ),
        title=go.layout.Title(text=titlestr),
        showlegend=True,
        hovermode='x unified'
    )

    # Format x and y axis
    dateTime_date = df['dateTime'].apply(lambda x: x.date()).unique()
    df_date_range = pd.date_range(df['dateTime'].min(), df['dateTime'].max(), freq='1D').to_series().reset_index(
        drop=True).apply(lambda x: x.date())
    exclude_dates = [x.strftime('%Y-%m-%d') for x in df_date_range if x not in dateTime_date]

    fig.update_xaxes(
        title_text='Date',
        rangebreaks=[
            dict(values=exclude_dates)
        ],
        rangeslider_visible = True
    )

    # Add Traces
    for plot_col in df.columns[df.columns!='dateTime']:
        fig.add_trace(go.Scatter(x=df['dateTime'],
                                 y=df[plot_col],
                                 mode='lines',
                                 name=plot_col,
                                 line=dict(
                                     width=.75
                                 ))
                      )

    fig.update_yaxes(
        title_text="Stock Price in "+ccy)

    fig.show()

generate_plotly_line_plot(df=df[['plotPrice', 'dateTime']],titlestr='Test', ccy='EUR')

上面的脚本会给我以下情节 根据上面的例子: enter image description here 根据全部数据: enter image description here

现在的问题是,这似乎实现了范围划分,但没有删除 x 轴上的排除区域,导致所需日期之间存在巨大差距。有没有办法让绘图正确地从 x 轴上消除范围间隙间隙?

提前致谢!

我已经尝试过以下操作:

  • dict(bounds=[16.5, 9], pattern="hour")
    dict(bounds=["sat", "mon"])
    的形式使用区间突破,但这对我不起作用,因为我还需要在正常交易周期间排除特定日期。
  • exclude_dates
    重新格式化为字符串,包括小时、分钟、秒和毫秒。 这会导致绘图仅显示第一个日期,而不显示整个时间段。
  • 在每秒/分钟级别上使用
    exclude_dates
    ,即传递更多值作为
    exclude_dates
    。 这导致绘图也仅显示第一天的数据。
python pandas time-series plotly
1个回答
0
投票

尝试使用

rangebreaks
以不同的方式重建时间序列图后,我无法让它工作。

然后我切换回使用

brokenaxes
包,对于更高级的绘图,使用 matplotlib 中的
transform=ax.transAxes
转换。这至少允许我根据日期时间正确缩放 x 轴,同时每天打破 x 轴。请注意,我的数据已经排除了周末和选定的日期。每个脚本看起来都有点像这样:

断斧:

import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from brokenaxes import brokenaxes

def matplotlib_price_plot(df):

    # Init
    fig = plt.figure(figsize=(20, 10))
    x_major_formatter  = mdates.DateFormatter("%Y-%m-%d\n%H:%M")
    
    # Get ax breaks
    date_ranges = []
    for d in df['date'].unique():
        temp = df[df['date']==d]
        date_ranges.append((temp['dateTime'].iloc[0], temp['dateTime'].iloc[-1]))
    date_ranges = tuple(date_ranges)
    
    # Init broken ax
    bax = brokenaxes(xlims=date_ranges, wspace=.03)
    
    # Plot values
    bax.plot(df['dateTime'], df['price'], c='darkorange', label='price', alpha=.5)
    bax.set_ylabel('Price in EUR')
    bax.legend()
    
    # Format Ticks
    for ax in bax.axs:
        ax.grid(which='major', axis='both')
        ax.xaxis.set_tick_params(labelsize=6)
        ax.xaxis.set_major_formatter(x_major_formatter)
    
    return

输出: brokenaxes output

matplotlib

(ax.transAxes)
注意:这里我需要删除/覆盖各个轴以获得所需的结果,因为您实际上将多个单独的子图组合成一个图形。

import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib as mpl

def matplotlib_sector_price_plot(df):
    # Get ax breaks
    date_ranges = []
    for d in df['date'].unique():
        temp = df[df['date']==d]
        date_ranges.append((temp.index[0], temp.index[-1]))
    date_ranges = tuple(date_ranges)

    # Init fig
    fig, axs = plt.subplots(1, len(date_ranges), sharex=False, figsize=(25, 10))

    x_major_formatter  = mdates.DateFormatter("%d-%m\n%H:%M")

    stockTicker = 'CFR SW'
    stockCurrency = 'CHF'
    sectorTicker = 'ESIC GR'

    # Check whether 1-D or more and adjust tick size
    if len(axs) == 1:
        hour_tick_size = 1
    else:
        hour_tick_size = 2

    # Plot values
    for ax, dr in zip(axs, date_ranges):
        temp = df[(df.index>=dr[0])&(df.index<=dr[1])]
        ax.set_xlim(dr)
        kwargs = dict(transform=ax.transAxes, color='k', clip_on=False)
        d = 0.005
        if ax == axs[0] and len(axs) > 1: # First subplot for multi day
            ax.plot(temp['lastPx'], c='mediumblue', label=stockTicker + ' (Last)', linewidth=.75)
            ax.spines['right'].set_visible(False)
            ax.tick_params(labelright='off')
            ax.yaxis.tick_left()
            ax.set_ylabel('Stock Price in ' + stockCurrency)
            ax.yaxis.set_major_formatter(mpl.ticker.StrMethodFormatter('{x:,.2f}'))
            ax.tick_params(axis='x', labelrotation=0)
            ax.yaxis.set_major_locator(mpl.ticker.LinearLocator(5))
            ax.xaxis.set_major_formatter(x_major_formatter)
            ax.xaxis.set_major_locator(mdates.HourLocator(byhour=range(1,25),interval=hour_tick_size))
            ax.grid(which='major', axis='x')

            ax2 = ax.twinx()
            ax2.plot(temp['lastMidPt'], c='gold', label=sectorTicker + ' (Mid)', linewidth=.75)
            ax2.spines['right'].set_visible(False)
            ax2.tick_params(labelright='off')
            ax2.yaxis.set_ticks([])
            ax2.yaxis.set_major_formatter(mpl.ticker.StrMethodFormatter('{x:,.4f}'))

            ax.plot((1 - d, 1 + d), (-d, +d), **kwargs)
            ax.plot((1 - d, 1 + d), (1 - d, 1 + d), **kwargs)
        elif ax == axs[-1] and len(axs) > 1: # Last subplot for multi day
            line1 = ax.plot(temp['lastPx'], c='mediumblue', label=stockTicker + ' (Last)', linewidth=.75)
            ax.spines['left'].set_visible(False)
            ax.yaxis.tick_right()
            ax.yaxis.set_ticks_position('none')
            ax.yaxis.set_ticks([])
            ax.xaxis.set_major_formatter(x_major_formatter)
            ax.xaxis.set_major_locator(mdates.HourLocator(byhour=range(1,25),interval=hour_tick_size))
            ax.tick_params(axis='x', labelrotation=0)
            ax.grid(which='major', axis='both')

            ax2 = ax.twinx()
            line2 = ax2.plot(temp['lastMidPt'], c='gold', label=sectorTicker + ' (Mid)', linewidth=.75)
            ax2.spines['left'].set_visible(False)
            ax2.tick_params(labelleft='off')
            ax2.set_ylabel('ETF Price in EUR')
            ax2.yaxis.tick_right()
            ax2.yaxis.set_major_locator(mpl.ticker.LinearLocator(5))
            ax2.yaxis.set_major_formatter(mpl.ticker.StrMethodFormatter('{x:,.4f}'))

            ax.plot((-d, +d), (1 - d, 1 + d), **kwargs)
            ax.plot((-d, +d), (-d, +d), **kwargs)
        elif len(axs) == 1: # 1-D plot
            line1 = ax.plot(temp['lastPx'], c='mediumblue', label=stockTicker + ' (Last)', linewidth=.75)
            ax.set_ylabel('Stock Price in ' + stockCurrency)
            ax.yaxis.set_major_formatter(mpl.ticker.StrMethodFormatter('{x:,.2f}'))
            ax.tick_params(axis='x', labelrotation=0)
            ax.yaxis.set_major_locator(mpl.ticker.LinearLocator(5))
            ax.xaxis.set_major_formatter(x_major_formatter)
            ax.xaxis.set_major_locator(mdates.HourLocator(byhour=range(1, 25), interval=hour_tick_size))
            ax.grid(True, which='major', axis='both')

            ax2 = ax.twinx()
            line2 = ax2.plot(temp['lastMidPt'], c='gold', label=sectorTicker + ' (Mid)', linewidth=.75)
            ax2.yaxis.set_major_locator(mpl.ticker.LinearLocator(5))
            ax2.yaxis.set_major_formatter(mpl.ticker.StrMethodFormatter('{x:,.4f}'))
            ax2.xaxis.set_major_locator(mdates.HourLocator(byhour=range(1, 25), interval=hour_tick_size))
            ax2.grid(True, which='major', axis='both')
        else: # Middle subplots for multi day
            ax.plot(temp['lastPx'], c='mediumblue', label=stockTicker + ' (Last)', linewidth=.75)
            ax.spines['right'].set_visible(False)
            ax.spines['left'].set_visible(False)
            ax.tick_params(labelright='off')
            ax.yaxis.set_ticks_position('none')
            ax.yaxis.set_ticks([])
            ax.xaxis.set_major_formatter(x_major_formatter)
            ax.tick_params(axis='x', labelrotation=0)
            ax.xaxis.set_major_locator(mdates.HourLocator(byhour=range(1,25),interval=hour_tick_size))
            ax.grid(which='major', axis='both')

            ax2 = ax.twinx()
            ax2.plot(temp['lastMidPt'], c='gold', label=sectorTicker + ' (Mid)', linewidth=.75)
            ax2.spines['right'].set_visible(False)
            ax2.spines['left'].set_visible(False)
            ax2.yaxis.set_ticks_position('none')
            ax2.yaxis.set_ticks([])
            ax2.yaxis.set_major_formatter(mpl.ticker.StrMethodFormatter('{x:,.4f}'))

            ax.plot((1 - d, 1 + d), (-d, +d), **kwargs)
            ax.plot((1 - d, 1 + d), (1 - d, 1 + d), **kwargs)

            ax.plot((-d, +d), (1 - d, 1 + d), **kwargs)
            ax.plot((-d, +d), (-d, +d), **kwargs)

    lines = line1+line2
    labs = [l.get_label() for l in lines]
    axs[-1].legend(lines, labs, loc='upper right')

    fig.tight_layout()

    plt.show()
    return

输出: matplotlib transAxes output

总的来说,结果并不相同,因为绘图是静态的并且缺少绘图功能,但至少从可视化的角度来看是我想要实现的。

© www.soinside.com 2019 - 2024. All rights reserved.