У меня есть два объекта фрейма данных pandas с именами «freq» и «freq1000», каждый из которых представляет собой вектор из 5 чисел. Я хочу нарисовать их на одном графике с plt.text на графике, чтобы показать значения y. 'freq' и 'freq1000' состоят из 'pr' и 'pr1000', оба из которых генерируются с помощью чрезвычайно длительных процедур. Чтобы избежать перекрытия plt.text, я перешел по ссылке Как исправить перекрытие аннотаций/текста и установил пакет Adjust_text. Однако это не сработало так, как я хотел, и на полученном графике все еще есть перекрытие фигур на графике, см. рисунок ниже:
Мой связанный импорт и коды включены ниже.
import pandas as pd
import matplotlib.pyplot as plt
from adjustText import adjust_text
freq = pd.DataFrame(np.average(pr, axis=0), index=pr.columns)
freq1000 = pd.DataFrame(np.average(pr1000, axis=0), index=pr1000.columns)
plt.plot(freq, label='n=500')
plt.plot(freq1000, label='n=1000')
plt.legend(loc='best')
plt.xlabel('x')
plt.ylabel('y')
plt.title('curve high vs low,gtype{}'.format(type_))
# plt.close()
texts=[]
for x, y in zip(freq.index, freq.values):
texts.append(plt.text(x, y,'%.3f' % y))
adjust_text(texts, only_move = {'points':'y', 'texts':'xy'}, arrowprops=dict(arrowstyle = "->", color='r', lw=0.5))
texts=[]
for x, y in zip(freq1000.index, freq1000.values):
texts.append(plt.text(x, y,'%.3f' % y))
adjust_text(texts, only_move = {'points':'y', 'texts':'y'}, arrowprops=dict(arrowstyle = "->", color='r', lw=0.5))
Как мне исправить коды, чтобы решить проблему перекрытия? Спасибо!
@джаред, спасибо! Буду редактировать и обновлять мой вопрос.
Это попытка сделать что-то с меньшим дублированием, но с некоторой настройкой, необходимой, если что-то часто меняется. ax.annotate
выполняет большую часть работы, но есть некоторые дополнительные параметры для дополнительной настройки. Основная идея — сместить синие метки влево, а оранжевые — вправо.
Одна из оранжевых меток перекрывает линию. Вы можете принудительно отключить его вручную, если это необходимо для одноразового графика, но я не сделал этого, чтобы сделать ответ более обобщаемым и устойчивым к новым случаям.
from matplotlib import pyplot as plt
pr_x = [-1, -0.5, 0, 0.5, 1]
pr_y = [1, 0.683, 0.067, 0.5, 1]
color_pr = 'tab:blue'
pr1000_x = [-1, -0.5, 0, 0.5, 1]
pr1000_y = [1, 0.983, 0.05, 0.917, 1]
color_pr1000 = 'tab:orange'
f, ax = plt.subplots(figsize=(6, 3))
ax.plot(pr_x, pr_y, color=color_pr, marker='o', label='n=500')
ax.plot(pr1000_x, pr1000_y, color=color_pr1000, marker='o', label='n=1000')
ax.set(xlabel='x', ylabel='y', title='curve high vs low, gtype2')
ax.legend()
for x, y in zip(pr_x, pr_y):
ax.annotate(
f'{y:.3f}', (x, y),
xytext=(-0.6, 0), textcoords='offset fontsize', #shift blue pts left
horizontalalignment='right', verticalalignment='center',
weight='bold', fontsize=8, color=color_pr,
)
for x, y in zip(pr1000_x, pr1000_y):
ax.annotate(
f'{y:.3f}', (x, y),
xytext=(0.6, 0.2), textcoords='offset fontsize', #shift orange pts up, right
horizontalalignment='left', verticalalignment='bottom',
weight='bold', fontsize=8, color=color_pr1000,
# rotation=20
)
ax.set_xlim(ax.get_xlim()[0] * 1.15, ax.get_xlim()[1]) #make x axis wider
ax.spines[['right', 'top']].set_visible(False)
Это альтернативный вариант, который имеет меньший потенциал дублирования. Для каждого графика есть визуальное руководство, но числа разделены на два графика.
Я думаю, что seaborn
FacetGrid
могли бы сделать что-то подобное и с меньшим количеством кода, если бы вы были заинтересованы в дальнейшем изучении такого рода механизмов.
f, axs = plt.subplots(ncols=2, nrows=1, figsize=(7, 3), sharey=True, layout='tight')
pr_data = (pr_x, pr_y, 'n=500')
pr1000_data = (pr1000_x, pr1000_y, 'n=1000')
for ax in axs:
fg_data = pr_data if ax==axs[0] else pr1000_data
bg_data = pr_data if ax==axs[1] else pr1000_data
#Foreground data
x, y, label = fg_data
ax.plot(x, y, 'tab:purple', marker='o', markersize=7, linewidth=3, label=label)
for x_i, y_i in zip(x, y):
ax.annotate(
f'{y_i:.3f}', (x_i, y_i), xytext=(1, 0.6), textcoords='offset fontsize',
horizontalalignment='left', verticalalignment='top',
color='midnightblue', weight='bold', size=8, rotation=0,
)
#Background data
x, y, label = bg_data
ax.plot(x, y, 'black', marker='o', markersize=6,
linewidth=4.5, linestyle='-', alpha=0.12,
label=label, zorder=0)
ax.set_xlabel('x')
ax.spines[['right', 'top']].set_visible(False)
ax.set_title(label, color='rebeccapurple', weight='bold')
# ax.legend()
axs[0].set_ylabel('y')
Большое спасибо за два отличных решения, очень ценю их! Еще несколько дополнительных вопросов: во-первых, в вашем первом решении нет ли опечаток в кодах? Например, на первом графике, который вы показываете, везде 1 вместо pr_y, как это исправить? Кроме того, должно ли «для xy в zip(pr_x, pr_y)» быть «для x,y в zip(pr_x,pr_y)»?
С удовольствием @ExcitedSnail. Вы правы, в коде есть опечатки, которые я сейчас исправил.
Пожалуйста, включите ваш импорт.