मुझे धुरी निर्देशांक (इस मामले में केवल टिक और टिकलबेल) सहित एक साजिश की सीमा को खोजने की आवश्यकता है (जैसा कि matplotlib transformations tutorial में परिभाषित किया गया है)।धुरी निर्देशांक में एक matplotlib साजिश (टिकलबेल सहित) की सीमा को ढूँढना
इस पर पृष्ठभूमि यह है कि मैं बड़ी संख्या में चार्ट के लिए स्वचालित रूप से थंबनेल प्लॉट (में) बना रहा हूं, केवल तभी जब मैं थंबनेल की स्थिति बना सकता हूं ताकि यह मूल साजिश में डेटा अस्पष्ट न हो।
यह मेरा वर्तमान दृष्टिकोण है:
- उम्मीदवार आयतों के एक नंबर बनाने के लिए परीक्षण करने के लिए, मूल कथानक के ऊपरी-दाएं से शुरू होकर काम कर छोड़ दिया है, तो मूल कथानक और इस कदम के निचले दाएं बाएं।
- प्रत्येक उम्मीदवार आयत के लिए:
- this SO question से कोड का उपयोग करना (अक्ष में निर्देशांक) डेटा निर्देशांक में है, जो एक्स-डेटा आयत को कवर किया जाएगा की टुकड़ा खोजने के लिए छोड़ दिया और रेक्ट के दाहिने हाथ की ओर परिवर्तित।
- आयताकार कवर के डेटा के टुकड़े के लिए न्यूनतम/अधिकतम वाई-मान खोजें।
- डेटा निर्देशांक में आयत के ऊपर और नीचे खोजें।
- उपरोक्त का उपयोग करके, यह निर्धारित करें कि आयताकार किसी भी डेटा के साथ ओवरलैप करता है या नहीं। यदि नहीं, तो मौजूदा आयत में थंबनेल प्लॉट खींचें, अन्यथा जारी रखें।
इस दृष्टिकोण के साथ समस्या यह है कि अक्ष निर्देशांक आप (1,1)
को (कुल्हाड़ियों के नीचे बाईं ओर) (0,0)
से धुरी की हद तक देना (शीर्ष सही) और टिक और ticklabels (शामिल नहीं है थंबनेल प्लॉट में शीर्षक, अक्ष लेबल, किंवदंतियों या अन्य कलाकार नहीं हैं)।
सभी चार्ट एक ही फ़ॉन्ट आकार उपयोग करें, लेकिन चार्ट, अलग-अलग लंबाई (जैसे 1.5
या 1.2345 * 10^6
) की ticklabels हैं, हालांकि इन से पहले इनसेट तैयार की है जाना जाता है। फ़ॉन्ट आकार/अंक से धुरी निर्देशांक में कनवर्ट करने का कोई तरीका है? वैकल्पिक रूप से, शायद उपर्युक्त (बाध्यकारी बक्से?) से बेहतर दृष्टिकोण हो सकता है।
निम्नलिखित कोड उपरोक्त एल्गोरिथ्म लागू करता है:
import math
from matplotlib import pyplot, rcParams
rcParams['xtick.direction'] = 'out'
rcParams['ytick.direction'] = 'out'
INSET_DEFAULT_WIDTH = 0.35
INSET_DEFAULT_HEIGHT = 0.25
INSET_PADDING = 0.05
INSET_TICK_FONTSIZE = 8
def axis_data_transform(axis, xin, yin, inverse=False):
"""Translate between axis and data coordinates.
If 'inverse' is True, data coordinates are translated to axis coordinates,
otherwise the transformation is reversed.
Code by Covich, from: https://stackoverflow.com/questions/29107800/
"""
xlim, ylim = axis.get_xlim(), axis.get_ylim()
xdelta, ydelta = xlim[1] - xlim[0], ylim[1] - ylim[0]
if not inverse:
xout, yout = xlim[0] + xin * xdelta, ylim[0] + yin * ydelta
else:
xdelta2, ydelta2 = xin - xlim[0], yin - ylim[0]
xout, yout = xdelta2/xdelta, ydelta2/ydelta
return xout, yout
def add_inset_to_axis(fig, axis, rect):
left, bottom, width, height = rect
def transform(coord):
return fig.transFigure.inverted().transform(
axis.transAxes.transform(coord))
fig_left, fig_bottom = transform((left, bottom))
fig_width, fig_height = transform([width, height]) - transform([0, 0])
return fig.add_axes([fig_left, fig_bottom, fig_width, fig_height])
def collide_rect((left, bottom, width, height), fig, axis, data):
# Find the values on the x-axis of left and right edges of the rect.
x_left_float, _ = axis_data_transform(axis, left, 0, inverse=False)
x_right_float, _ = axis_data_transform(axis, left + width, 0, inverse=False)
x_left = int(math.floor(x_left_float))
x_right = int(math.ceil(x_right_float))
# Find the highest and lowest y-value in that segment of data.
minimum_y = min(data[int(x_left):int(x_right)])
maximum_y = max(data[int(x_left):int(x_right)])
# Convert the bottom and top of the rect to data coordinates.
_, inset_top = axis_data_transform(axis, 0, bottom + height, inverse=False)
_, inset_bottom = axis_data_transform(axis, 0, bottom, inverse=False)
# Detect collision.
if ((bottom > 0.5 and maximum_y > inset_bottom) or # inset at top of chart
(bottom < 0.5 and minimum_y < inset_top)): # inset at bottom
return True
return False
if __name__ == '__main__':
x_data, y_data = range(0, 100), [-1.0] * 50 + [1.0] * 50 # Square wave.
y_min, y_max = min(y_data), max(y_data)
fig = pyplot.figure()
axis = fig.add_subplot(111)
axis.set_ylim(y_min - 0.1, y_max + 0.1)
axis.plot(x_data, y_data)
# Find a rectangle that does not collide with data. Start top-right
# and work left, then try bottom-right and work left.
inset_collides = False
left_offsets = [x/10.0 for x in xrange(6)] * 2
bottom_values = (([1.0 - INSET_DEFAULT_HEIGHT - INSET_PADDING] * (len(left_offsets)/2))
+ ([INSET_PADDING * 2] * (len(left_offsets)/2)))
for left_offset, bottom in zip(left_offsets, bottom_values):
# rect: (left, bottom, width, height)
rect = (1.0 - INSET_DEFAULT_WIDTH - left_offset - INSET_PADDING,
bottom, INSET_DEFAULT_WIDTH, INSET_DEFAULT_HEIGHT)
inset_collides = collide_rect(rect, fig, axis, y_data)
print 'TRYING:', rect, 'RESULT:', inset_collides
if not inset_collides:
break
if not inset_collides:
inset = add_inset_to_axis(fig, axis, rect)
inset.set_ylim(axis.get_ylim())
inset.set_yticks([y_min, y_min + ((y_max - y_min)/2.0), y_max])
inset.xaxis.set_tick_params(labelsize=INSET_TICK_FONTSIZE)
inset.yaxis.set_tick_params(labelsize=INSET_TICK_FONTSIZE)
inset_xlimit = (0, int(len(y_data)/100.0 * 2.5)) # First 2.5% of data.
inset.set_xlim(inset_xlimit[0], inset_xlimit[1], auto=False)
inset.plot(x_data[inset_xlimit[0]:inset_xlimit[1] + 1],
y_data[inset_xlimit[0]:inset_xlimit[1] + 1])
fig.savefig('so_example.png')
और इस के उत्पादन में है:
TRYING: (0.6, 0.7, 0.35, 0.25) RESULT: True
TRYING: (0.5, 0.7, 0.35, 0.25) RESULT: True
TRYING: (0.4, 0.7, 0.35, 0.25) RESULT: True
TRYING: (0.30000000000000004, 0.7, 0.35, 0.25) RESULT: True
TRYING: (0.2, 0.7, 0.35, 0.25) RESULT: True
TRYING: (0.10000000000000002, 0.7, 0.35, 0.25) RESULT: False
इस बात के लिए बहुत धन्यवाद:
अंत में, यहाँ मेरी परिवर्तन और परिवर्धन के साथ अपना पूरा कोड है। जब मैंने इसे हमारे मूल कोड पर पोर्ट किया (जिस पर यहां उदाहरण दिया गया है) 'शो()' ठीक काम करने लग रहा था। मुझे संदेह है क्योंकि हम एक पीडीएफ रेंडरर का उपयोग कर रहे हैं। संयोग से, मुझे लगता है कि लाइन 'inset_bbox = inset.bbox.inverse_transformed (axis.transAxes) 'अनावश्यक हो सकती है, क्योंकि' inset_bbox' कभी नहीं पढ़ा जाता है। – snim2
आप सही हैं, वह रेखा पिछले पुनरावृत्ति से बचे हुए थे और अब मैंने इसे स्पष्टता के लिए हटा दिया है। यदि आप लगभग 'रेंडरर' सामान के आसपास खोज करते हैं, तो यह आपके बैकएंड के आधार पर हिट या मिस लगता है, मैक ओएस विशेष रूप से समस्याग्रस्त होने के साथ। कोई भी 'tighterer = fig.canvas.get_renderer()' का उपयोग करने का प्रयास कर सकता है, 'tight_layout' आयात करने या [इस प्रश्न] के उत्तर (http://stackoverflow.com/questions/22667224/matplotlib-get-text-bounding -Box स्वतंत्र के- बैकएंड)। वैसे भी, अगर यह पहले से ही काम करता है, तो यह बहुत अच्छा है। मैं खुशी से मदद कर सकता है! –