自动放置注释气泡
Auto place annotation bubble
我有以下代码片段:
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
,这使得“气泡”变得凌乱,因为它们在某些位置重叠,难以阅读。有什么办法可以“自动放置”,使它们不重叠。例如一些“气泡”在情节线的上方和下方,它们不重叠。
根据我的说法,自动放置是这样的,您可以放大缩小并自动调整数据,并且 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()
以上代码的工作:
因为节假日数据用的少,所以标注的重叠度会因为标注的重叠度小而显得效果不佳,但是回答的要点是这个问题可以改进根据索引稍微改变注释的位置。
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)
我有以下代码片段:
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
,这使得“气泡”变得凌乱,因为它们在某些位置重叠,难以阅读。有什么办法可以“自动放置”,使它们不重叠。例如一些“气泡”在情节线的上方和下方,它们不重叠。
根据我的说法,自动放置是这样的,您可以放大缩小并自动调整数据,并且 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()
以上代码的工作:
因为节假日数据用的少,所以标注的重叠度会因为标注的重叠度小而显得效果不佳,但是回答的要点是这个问题可以改进根据索引稍微改变注释的位置。
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)