【问题标题】:Auto place annotation bubble自动放置注释气泡
【发布时间】:2020-10-25 07:24:09
【问题描述】:

我有以下代码片段:

data.plot(y='Close', ax = ax)
newdates = exceptthursday.loc[start:end]
for anotate in (newdates.index + BDay()).strftime('%Y-%m-%d'):
    ax.annotate('holliday', xy=(anotate, data['Close'].loc[anotate]),  xycoords='data',
                xytext=(-30, 40), textcoords='offset points',
                size=13, ha='center', va="baseline",
                bbox=dict(boxstyle="round", alpha=0.1),
                arrowprops=dict(arrowstyle="wedge,tail_width=0.5", alpha=0.1)); 

这会产生一个如下所示的图:

正如您所看到的,我已经明确提到了xytext,这使得“气泡”变得混乱,因为它们在某些位置重叠,难以阅读。有什么办法可以“自动放置”以使它们不重叠。比如一些“气泡”在情节线的上方和下方,它们不重叠。

【问题讨论】:

  • 您是否检查过这些问题中提出的解决方案? 1, 2, 3
  • 您可以发布示例数据框吗?
  • @bigbounty 我使用来自web.DataReader('fb', 'yahoo')的数据这是来自from pandas_datareader import data as web的数据

标签: python python-3.x pandas matplotlib annotations


【解决方案1】:

根据我的自动放置是这样的,您可以放大缩小并自动调整数据和 UI,因为即使您将气泡放置在绘图线的上方和下方,您也无法避免重叠,因为有很多数据点不容忽视。我使用了 plotly 库,因为 matplotlib 有限制。我选择了英国假期。您可以相应地更改它。

import plotly.graph_objects as go
import plotly.express as px
from pandas_datareader import data as web
import holidays

data = web.DataReader('fb', 'yahoo')
uk_holidays = holidays.UnitedKingdom()
data["is_holiday"] = [True if i in uk_holidays else False for i in data.index]
data["Date"] = data.index
data.reset_index(drop=True, inplace=True)

fig = px.line(data, x='Date', y='Close')

fig.update_xaxes(
    rangeslider_visible=True,
    rangeselector=dict(
        buttons=list([
            dict(count=1, label="1m", step="month", stepmode="backward"),
            dict(count=6, label="6m", step="month", stepmode="backward"),
            dict(count=1, label="YTD", step="year", stepmode="todate"),
            dict(count=1, label="1y", step="year", stepmode="backward"),
            dict(step="all")
        ])
    )
)


for close, date in data[data["is_holiday"] == True][["Close","Date"]].itertuples(index=False):
    fig.add_annotation(
        x=date.date(),
        y=close,
        xref="x",
        yref="y",
        text="Holiday",
        showarrow=True,
        font=dict(
            family="Courier New, monospace",
            size=16,
            color="#ffffff"
            ),
        align="center",
        arrowhead=2,
        arrowsize=1,
        arrowwidth=2,
        arrowcolor="#636363",
        ax=20,
        ay=-30,
        bordercolor="#c7c7c7",
        borderwidth=2,
        borderpad=4,
        bgcolor="#ff7f0e",
        opacity=0.8
        )
fig.update_layout(title_text='Trend Analysis with Holiday', title_x=0.5,showlegend=False)
fig.show()

上述代码的工作原理:

【讨论】:

    【解决方案2】:

    由于使用了少量的假期数据,注释中的重叠程度似乎不太有效,因为注释中的重叠量很小,但答案的要点是可以通过以下方式稍微改善问题根据索引改变注释的位置。

    import matplotlib.pyplot as plt
    import matplotlib.dates as mdates
    from pandas_datareader import data as web
    from pandas.tseries.holiday import USFederalHolidayCalendar as calendar
    
    data = web.DataReader('fb', 'yahoo')
    cal = calendar()
    holidays = cal.holidays(start=data.index.min(), end=data.index.max())
    data['Holiday'] = data.index.isin(holidays)
    holiday = data[data['Holiday'] == True]
    
    fig = plt.figure(figsize=(16,6))
    ax = fig.add_subplot(111)
    
    ax.plot(data.index, data.Close)
    
    for i,x,y in zip(range(len(holiday)),holiday.index, holiday.Close):
        if i % 2 == 0: 
            ax.annotate('holliday', xy=(x,y),  xycoords='data',
                        xytext=(-30, 40), textcoords='offset points',
                        size=13, ha='center', va="baseline",
                        bbox=dict(boxstyle="round", alpha=0.1),
                        arrowprops=dict(arrowstyle="wedge,tail_width=0.5", alpha=0.1))
        else:
            ax.annotate('holliday', xy=(x,y),  xycoords='data',
                    xytext=(30, -40), textcoords='offset points',
                    size=13, ha='center', va="baseline",
                    bbox=dict(boxstyle="round", alpha=0.1),
                    arrowprops=dict(arrowstyle="wedge,tail_width=0.5", alpha=0.1))
    
    
    ax.xaxis.set_major_locator(mdates.MonthLocator(bymonth=None, interval=3, tz=None))
    ax.xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m-%d"))
    ax.tick_params(axis='x', labelrotation=45)
    

    【讨论】:

    • 能否请您告知如何将 x 轴更改为日期。我也收到TypeError: float() argument must be a string or a number, not 'Timestamp' 我该如何解决这个错误?
    • 修改了x轴显示日期,替换了代码和图表图片。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多