2014-09-16 23 views
6

Tôi đang tạo một số ô đường viền với contour được dán nhãn qua clabel. Vấn đề là các nhãn đường viền có xu hướng trùng lặp với các trục: enter image description herematplotlib nhãn lô đường viền chồng chéo các trục

(một số nhãn khác lộn xộn, bỏ qua điều đó). Đối với cốt truyện bên trái, 10^-3 và 10 là vấn đề. Ở bên phải, 10^3 là vấn đề duy nhất. Đây là mã mà tạo ra một trong số họ:

fig = plt.figure(figsize=(6,3)) 
ax = fig.add_subplot(121) 
ax.set_xscale('log') 
ax.set_yscale('log') 
ax.set_xlabel(r'$T_e$ (eV)', fontsize=10) 
ax.set_ylabel(r'$n_e$ (1/cm$^3$)', fontsize=10) 
ax.set_xlim(0.1, 1e4) 
ax.set_ylim(1e16, 1e28) 
CS = ax.contour(X, Y, Z, V, colors='k') 
ax.clabel(CS, inline=True, inline_spacing=3, rightside_up=True, colors='k', fontsize=8, fmt=fmt) 

Có cách nào để có được clabel phải cư xử tốt hơn về vị trí của nó?

Trả lời

3

Xem xét rằng the examples in the documentation bị cùng một căn bệnh cho thấy rằng sẽ không khó để giải quyết vấn đề này. It would seem mà bạn phải sống với những người tự động, sử dụng manual vị trí, hoặc có được bàn tay của bạn bẩn.

Như một sự thỏa hiệp, tôi sẽ thử một trong hai điều. Cả hai bắt đầu cho phép matplotlib đề xuất vị trí nhãn cho bạn, sau đó xử lý các vị trí quá gần một trục.

Trường hợp đơn giản hơn, đó cũng là an toàn hơn, là chỉ cần thoát khỏi những clabel s mà gần gũi với một đường viền, làm đầy những đường đồng mức:

# based on matplotlib.pyplot.clabel example: 
import matplotlib 
import numpy as np 
import matplotlib.cm as cm 
import matplotlib.mlab as mlab 
import matplotlib.pyplot as plt 

delta = 0.025 
x = np.arange(-3.0, 3.0, delta) 
y = np.arange(-2.0, 2.0, delta) 
X, Y = np.meshgrid(x, y) 
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) 
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) 
# difference of Gaussians 
Z = 10.0 * (Z2 - Z1) 


plt.figure() 
CS = plt.contour(X, Y, Z) 
CLS = plt.clabel(CS, inline=1, fontsize=10) 

# now CLS is a list of the labels, we have to find offending ones 
thresh = 0.05 # ratio in x/y range in border to discard 

# get limits if they're automatic 
xmin,xmax,ymin,ymax = plt.axis() 
Dx = xmax-xmin 
Dy = ymax-ymin 

# check which labels are near a border 
keep_labels = [] 
for label in CLS: 
    lx,ly = label.get_position() 
    if xmin+thresh*Dx<lx<xmax-thresh*Dx and ymin+thresh*Dy<ly<ymax-thresh*Dy: 
     # inlier, redraw it later 
     keep_labels.append((lx,ly)) 

# delete the original lines, redraw manually the labels we want to keep 
# this will leave unlabelled full contour lines instead of overlapping labels 

for cline in CS.collections: 
    cline.remove() 
for label in CLS: 
    label.remove() 

CS = plt.contour(X, Y, Z) 
CLS = plt.clabel(CS, inline=1, fontsize=10, manual=keep_labels) 

Nhược điểm là một số nhãn rõ ràng sẽ mất tích, và tất nhiên ngưỡng 5% cần điều chỉnh thủ công cho ứng dụng cụ thể của bạn. Kết quả trên so với bản gốc (xem phía trên):

beforeafter

Các giải pháp khác tôi đã đề cập sẽ được lấy nhãn vi phạm, nhìn vào Path s của CS.collections dữ liệu tương ứng của họ, và cố gắng để tìm một điểm gần với bên trong hình. Vì nó không tầm thường khi ghép nối dữ liệu collections với các nhãn (vì mỗi đường bao mức đường bao với nhiều đoạn đường tương ứng với một phần tử đơn lẻ là CS.collections), nó có thể không đáng để thử. Đặc biệt là bạn có thể phải đối mặt với các dòng cấp độ quá ngắn đến mức không thể đặt một nhãn lên chúng, và bạn cũng phải ước tính kích thước của mỗi nhãn.


Xem xét trường hợp của bạn là đường đồng mức khá đơn giản, bạn cũng có thể thử xem từng đường đồng mức và tìm điểm gần với trung tâm của hình.

Vì vậy, đây là một tái thiết dữ liệu của bạn đặt ra cho mục đích trình diễn:

# guesstimated dummy data 
X,Y = np.meshgrid(np.logspace(-3,7,200),np.logspace(13,31,200)) 
Z = X/Y*10**21 
Vrange = range(-3,5) 
V = [10**k for k in Vrange] 
fmt = {lev: '$10^{%d}$'%k for (k,lev) in zip(Vrange,V)} 


fig = plt.figure(figsize=(3,3)) 
ax = fig.add_subplot(111) 
ax.set_xscale('log') 
ax.set_yscale('log') 
ax.set_xlabel(r'$T_e$ (eV)', fontsize=10) 
ax.set_ylabel(r'$n_e$ (1/cm$^3$)', fontsize=10) 
ax.set_xlim(0.1, 1e4) 
ax.set_ylim(1e16, 1e28) 

CS = ax.contour(X, Y, Z, V, colors='k') 
ax.clabel(CS, inline=True, inline_spacing=3, rightside_up=True, colors='k', fontsize=8, fmt=fmt) 

Bằng cách sử dụng rõ ràng về điều đó cả hai trục của bạn là logarit, ý tưởng chính là để thay thế các cuộc gọi cuối cùng ở trên để clabel với :

# get limits if they're automatic 
xmin,xmax,ymin,ymax = plt.axis() 
# work with logarithms for loglog scale 
# middle of the figure: 
logmid = (np.log10(xmin)+np.log10(xmax))/2, (np.log10(ymin)+np.log10(ymax))/2 

label_pos = [] 
for line in CS.collections: 
    for path in line.get_paths(): 
     logvert = np.log10(path.vertices) 

     # find closest point 
     logdist = np.linalg.norm(logvert-logmid, ord=2, axis=1) 
     min_ind = np.argmin(logdist) 
     label_pos.append(10**logvert[min_ind,:]) 

# draw labels, hope for the best 
ax.clabel(CS, inline=True, inline_spacing=3, rightside_up=True, colors='k', fontsize=8, fmt=fmt, manual=label_pos) 

quả (phải) so với bản gốc (trái):

before 2after 2

Tôi đã không thực hiện nhiều nỗ lực để làm cho chú thích trục đẹp, vì vậy vui lòng bỏ qua các chi tiết này.Bạn có thể thấy rằng các nhãn thực sự được thu thập ở gần giữa hình. Tùy thuộc vào ứng dụng của bạn, điều này có thể hoặc có thể không phải là những gì bạn muốn.

Là một lưu ý cuối cùng, lý do nhãn là không đặt dọc theo đường chéo của các trục là mở rộng quy mô là khác nhau dọc theo XY trục. Điều này có thể khiến một số nhãn vẫn tiếp xúc với các trục. Giải pháp dễ sử dụng nhất là xem xét đường [xmin,ymax] - [xmax,ymin] (lôgarít) và để tìm giao điểm của đường này với mỗi path s. Bạn phải rất đầu tư vào điều này nếu điều này là giá trị nó: bạn cũng có thể đặt nhãn của bạn hoàn toàn bằng tay.

+0

Cảm ơn vì điều này. Sử dụng nó để ngăn chặn các cạnh cắt xén xấu xí trong dịch vụ bản đồ lát gạch (ví dụ: đường viền áp suất không khí). –

Các vấn đề liên quan