#python #matplotlib #annotations #overlap
#python #matplotlib #аннотации #перекрытие
Вопрос:
Я пытаюсь адаптировать представленный здесь код, чтобы он лучше соответствовал тому, что мне нужно. Мне нужно отображать длинные аннотации, поэтому я хотел бы показать их, начиная с «края» основной строки, которая находится дальше по отношению к оси y, добавив немного места, если текст попадает поверх вертикальной линии. Я думаю, что могу объяснить это, сказав, что вместо определения того, где начинается текст, я хотел бы установить, когда он должен заканчиваться. Я думал о том, чтобы просто изменить [‘top’, ‘bottom’] часть кода на [‘bottom’, ‘top’], но это не совсем сработало.
Это воспроизводимый пример кода:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.dates as mdates
from datetime import datetime
names = ['v2.2.4', 'v3.0.3', 'v3.0.2', 'v3.0.1', 'v3.0.0', 'v2.2.3',
'v2.2.2', 'v2.2.1', 'v2.2.0', 'v2.1.2', 'v2.1.1', 'v2.1.0',
'v2.0.2', 'v2.0.1', 'v2.0.0', 'v1.5.3', 'v1.5.2', 'v1.5.1',
'v1.5.0', 'v1.4.3', 'v1.4.2', 'v1.4.1', 'v1.4.0']
dates = ['2019-02-26', '2019-02-26', '2018-11-10', '2018-11-10',
'2018-09-18', '2018-08-10', '2018-03-17', '2018-03-16',
'2018-03-06', '2018-01-18', '2017-12-10', '2017-10-07',
'2017-05-10', '2017-05-02', '2017-01-17', '2016-09-09',
'2016-07-03', '2016-01-10', '2015-10-29', '2015-02-16',
'2014-10-26', '2014-10-18', '2014-08-26']
names = [name "very_very_long_long" for name in names]
# Convert date strings (e.g. 2014-10-18) to datetime
dates = [datetime.strptime(d, "%Y-%m-%d") for d in dates]
# Choose some nice levels
levels = np.tile([-5, 5, -3, 3, -1, 1],
int(np.ceil(len(dates)/6)))[:len(dates)]
# Create figure and plot a stem plot with the date
fig, ax = plt.subplots(figsize=(8.8, 4), constrained_layout=True)
ax.set(title="Matplotlib release dates")
markerline, stemline, baseline = ax.stem(dates, levels,
linefmt="C3-", basefmt="k-",
use_line_collection=True)
plt.setp(markerline, mec="k", mfc="w", zorder=3)
# Shift the markers to the baseline by replacing the y-data by zeros.
markerline.set_ydata(np.zeros(len(dates)))
# annotate lines
vert = np.array(['bottom', 'top'])[(levels > 0).astype(int)]
for d, l, r, va in zip(dates, levels, names, vert):
ax.annotate(r, xy=(d, l), xytext=(-3, np.sign(l)*3),
textcoords="offset points", va=va, ha="right", rotation=90)
# format xaxis with 4 month intervals
# ax.get_xaxis().set_major_locator(mdates.MonthLocator(interval=4))
# ax.get_xaxis().set_major_formatter(mdates.DateFormatter("%b %Y"))
# plt.setp(ax.get_xticklabels(), rotation=30, ha="right")
# remove y axis and spines
ax.get_yaxis().set_visible(False)
for spine in ["left", "top", "right"]:
ax.spines[spine].set_visible(False)
# ax.margins(y=0.1)
plt.show()
Я пытался использовать эту библиотеку, но пока это просто создает новый беспорядок, потому что я, вероятно, неправильно ее использую.
Ответ №1:
У меня нет большого опыта работы с такого рода графами, но я пришел к двум идеям: первая — разместить строку горизонтально и расположить ее в верхней части уровня. Другой способ — радикально переключить оси и сделать его вертикальным графиком.
1) Горизонтальная и центрированная строка
for d, l, r, va in zip(dates, levels, names, vert):
ax.annotate(r, xy=(d, l), xytext=(60, np.sign(l)*15),
textcoords="offset points", va=va, ha="right")
- Вертикальный тип с заменой осей XY
fig, ax = plt.subplots(figsize=(6, 12), constrained_layout=True)
# define bounding box
boxdic={'facecolor':'0.9',
'edgecolor':'0.6',
'boxstyle':'round',
'linewidth':1}
arrowprops = dict(
arrowstyle="->",
connectionstyle="angle,angleA=0,angleB=45,rad=10")
arrowprops1 = dict(
arrowstyle="->",
connectionstyle="angle,angleA=0,angleB=-45,rad=10")
ax = plt.axes(xlim=(-5,5), ylim=(datetime(2020,1,1,0,0), datetime(2014,1,1,0,0)))
ax.set(title="Matplotlib release dates")
ax.scatter([0]*len(dates), dates, marker='o', color='w', ec='k')
ax.spines['left'].set_position('zero')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.axes.xaxis.set_visible(False)
for i, (d, n) in enumerate(zip(dates, names)):
if i % 2 == 0:
ax.annotate(str("{}".format(n)), xy=(0.1, d),
xycoords='data', xytext=(15, 25), textcoords='offset points',
color="k", fontsize=10, bbox=boxdic, arrowprops=arrowprops)
else:
ax.annotate(str("{}".format(n)), xy=(-0.1, d),
xycoords='data', xytext=(-15, 25), textcoords='offset points',
color="k", fontsize=10, bbox=boxdic, ha='right', arrowprops=arrowprops1)
months = mdates.MonthLocator(interval = 6)
months_fmt = mdates.DateFormatter('%Y-%m')
ax.yaxis.set_major_locator(months)
ax.yaxis.set_major_formatter(months_fmt)
plt.show()