Tôi đã viết giải pháp nhanh, kiểm tra từng vị trí chú thích đối với các hộp giới hạn mặc định cho tất cả các chú thích khác. Nếu có va chạm, nó sẽ thay đổi vị trí của nó sang vị trí xung đột có sẵn miễn phí tiếp theo. Nó cũng đặt trong mũi tên đẹp.
Đối với một ví dụ khá cực đoan, nó sẽ sản xuất này (không ai trong số những con số trùng nhau):
Thay vì điều này:
Đây là mã:
import numpy as np
import matplotlib.pyplot as plt
from numpy.random import *
def get_text_positions(x_data, y_data, txt_width, txt_height):
a = zip(y_data, x_data)
text_positions = y_data.copy()
for index, (y, x) in enumerate(a):
local_text_positions = [i for i in a if i[0] > (y - txt_height)
and (abs(i[1] - x) < txt_width * 2) and i != (y,x)]
if local_text_positions:
sorted_ltp = sorted(local_text_positions)
if abs(sorted_ltp[0][0] - y) < txt_height: #True == collision
differ = np.diff(sorted_ltp, axis=0)
a[index] = (sorted_ltp[-1][0] + txt_height, a[index][1])
text_positions[index] = sorted_ltp[-1][0] + txt_height
for k, (j, m) in enumerate(differ):
#j is the vertical distance between words
if j > txt_height * 2: #if True then room to fit a word in
a[index] = (sorted_ltp[k][0] + txt_height, a[index][1])
text_positions[index] = sorted_ltp[k][0] + txt_height
break
return text_positions
def text_plotter(x_data, y_data, text_positions, axis,txt_width,txt_height):
for x,y,t in zip(x_data, y_data, text_positions):
axis.text(x - txt_width, 1.01*t, '%d'%int(y),rotation=0, color='blue')
if y != t:
axis.arrow(x, t,0,y-t, color='red',alpha=0.3, width=txt_width*0.1,
head_width=txt_width, head_length=txt_height*0.5,
zorder=0,length_includes_head=True)
Dưới đây là mã sản xuất các ô này, cho biết cách sử dụng:
#random test data:
x_data = random_sample(100)
y_data = random_integers(10,50,(100))
#GOOD PLOT:
fig2 = plt.figure()
ax2 = fig2.add_subplot(111)
ax2.bar(x_data, y_data,width=0.00001)
#set the bbox for the text. Increase txt_width for wider text.
txt_height = 0.04*(plt.ylim()[1] - plt.ylim()[0])
txt_width = 0.02*(plt.xlim()[1] - plt.xlim()[0])
#Get the corrected text positions, then write the text.
text_positions = get_text_positions(x_data, y_data, txt_width, txt_height)
text_plotter(x_data, y_data, text_positions, ax2, txt_width, txt_height)
plt.ylim(0,max(text_positions)+2*txt_height)
plt.xlim(-0.1,1.1)
#BAD PLOT:
fig = plt.figure()
ax = fig.add_subplot(111)
ax.bar(x_data, y_data, width=0.0001)
#write the text:
for x,y in zip(x_data, y_data):
ax.text(x - txt_width, 1.01*y, '%d'%int(y),rotation=0)
plt.ylim(0,max(text_positions)+2*txt_height)
plt.xlim(-0.1,1.1)
plt.show()
khá tốt đẹp. Có cách nào để khái quát hóa điều này lên grafics không phải thanh không? Tôi đang cố gắng chú thích một scatterplot, và tự nhiên nó sẽ là tốt đẹp nếu khoảng cách của các mũi tên đã được giảm thiểu, quá. Cũng có thể giảm thiểu số lượng mũi tên đi qua các con số không? – tarrasch
@tarrasch - Nguyên tắc này sẽ hoạt động tốt cho bất kỳ loại âm mưu nào. Hy vọng rằng tôi sẽ có thời gian để gõ mã vào hình dạng hấp dẫn hơn trong vài ngày tới (nó cần phải được khái quát hóa, như tôi đã đề cập). Khoảng cách của các mũi tên có thể được giảm một chút (thay đổi '2 * L' thành' L'), nhưng các loại mũi tên phải đi qua các con số đôi khi (nó sẽ bắt đầu nhận được rất nhiều phức tạp hơn để tránh điều đó), tuy nhiên nếu bạn thay đổi cài đặt mũi tên 'alpha' thành' alpha = 0.3' và văn bản 'màu' thành màu xanh dương, ô sẽ bắt đầu trông đẹp hơn. – fraxel
đẹp! Tôi sẽ thử nó chiều nay :) – tarrasch