2025, Oct 15 20:31
Matplotlib में कई legends को सही तरह से जोड़ना और हटाना
Matplotlib में कई legends को भरोसेमंद तरीके से बनाना-हटाना सीखें: ax.legend का असर, add_artist कब जोड़ें, और remove पर डुप्लीकेट समस्या से बचें. टिप्स भी
Matplotlib में कई legends का प्रबंधन जितना साधारण लगता है, उतना नहीं है। अक्सर ऐसा होता है कि आप एक ही axes पर दो legends बनाते हैं, और बाद में उन्हें हटाने पर व्यवहार असंगत दिखता है: एक legend गायब हो जाता है, दूसरा हटने से इंकार करता है, या कोई सामान्य क्लीनअप लूप अपवाद फेंक देता है। वजह remove में नहीं, बल्कि इस बात में है कि legends कैसे बनाए जाते हैं और axes से कैसे जोड़े जाते हैं।
समस्या को पुन: उत्पन्न करना
नीचे दिया गया उदाहरण दो legends के साथ एक plot बनाता है और अंत में उनमें से एक को दो बार रजिस्टर कर देता है — यही वजह है कि हटाना भरोसेमंद नहीं रहता।
import matplotlib.pyplot as plt
canvas, axes = plt.subplots()
# नमूना डेटा
xs = [1, 2, 3, 4, 5]
vals_a = [1, 4, 9, 16, 25]
vals_b = [25, 16, 9, 4, 1]
axes.plot(xs, vals_a, label='y = x^2')
axes.plot(xs, vals_b, label='y = 25 - x^2')
# handles/labels लेने के लिए प्रारंभिक legend
axes.legend()
hnds, lbls = axes.get_legend_handles_labels()
# अस्थायी legend हटाएँ
axes.get_legend().remove()
# एकत्र किए गए handles/labels से दो legends बनाएँ
split_idx = len(hnds) // 2
legend_left = axes.legend(hnds[:split_idx], lbls[:split_idx], loc="upper left")
legend_right = axes.legend(hnds[split_idx:], lbls[split_idx:], loc="lower right")
# दोनों को स्पष्ट रूप से संलग्न करें
axes.add_artist(legend_left)
axes.add_artist(legend_right)
वास्तव में क्या हो रहा है
axes.legend क्या करता है, यह समझना इस अजीब व्यवहार को स्पष्ट कर देता है। जब आप ax.legend कॉल करते हैं, तो यह एक legend बनाता है, उसे axes से जोड़ देता है, और पहले से मौजूद किसी भी legend को हटा देता है। ऊपर दिए उदाहरण में दूसरी कॉल के बाद केवल दूसरा legend axes से जुड़ा रहता है; पहला अपने-आप हट चुका होता है। बाद में जब आप दोनों legend ऑब्जेक्ट्स पर add_artist चलाते हैं, तो पहला legend एक बार जुड़ता है, जबकि दूसरा axes पर दो बार आ जाता है। यही डुप्लीकेशन कारण है कि दूसरे legend पर एक remove() कभी-कभी बेअसर लगता है, और क्यों बच्चों पर साधारण क्लीनअप लूप चलते समय वही ऑब्जेक्ट दो बार हटाने की कोशिश में ValueError फेंक सकता है।
ax.legendकॉल करने से legend बनता है, axes में जुड़ता है, और इस प्रक्रिया में पहले से मौजूद legend को बदल देता है। अगर बाद में आपadd_artistसे legends को मैन्युअली जोड़ते हैं, तो ध्यान रखें कि हर legend केवल एक बार जोड़ा जाए; वरना वही legend कई बार मौजूद हो सकता है।
समाधान
कुंजी बेहद सरल है: दूसरे legend को दोबारा न जोड़ें। वह तो दूसरी axes.legend कॉल के जरिए पहले ही संलग्न हो चुका है। जब हर legend बिल्कुल एक बार मौजूद होगा, तब दोनों पर remove() साफ़-सुथरे ढंग से काम करेगा।
import matplotlib.pyplot as plt
canvas, axes = plt.subplots()
xs = [1, 2, 3, 4, 5]
vals_a = [1, 4, 9, 16, 25]
vals_b = [25, 16, 9, 4, 1]
axes.plot(xs, vals_a, label='y = x^2')
axes.plot(xs, vals_b, label='y = 25 - x^2')
# handles/labels एकत्र करने के लिए अस्थायी legend बनाएँ
axes.legend()
hnds, lbls = axes.get_legend_handles_labels()
axes.get_legend().remove()
# दो legends बनाएँ
split_idx = len(hnds) // 2
legend_ul = axes.legend(hnds[:split_idx], lbls[:split_idx], loc="upper left")
legend_lr = axes.legend(hnds[split_idx:], lbls[split_idx:], loc="lower right")
# केवल पहले को स्पष्ट रूप से संलग्न करें; दूसरा legend() द्वारा पहले ही जुड़ चुका है
axes.add_artist(legend_ul)
# बाद में: दोनों legends को भरोसेमंद तरीके से हटाएँ
legend_ul.remove()
legend_lr.remove()
यह क्यों मायने रखता है
Matplotlib में legend प्रबंधन stateful होता है। हर axes.legend कॉल मौजूदा legend को बदल कर axes को संशोधित कर देती है। इसी व्यवहार को मैन्युअल add_artist कॉल्स के साथ मिलाने पर डुप्लीकेट legend ऑब्जेक्ट्स आसानी से बन सकते हैं। ये डुप्लीकेट दिखने में पकड़ में न आएं, लेकिन क्लीनअप के दौरान आंशिक हटाने या अपवादों का कारण बन सकते हैं—उदाहरण के लिए, जब आप axes.get_children() पर इटरेट करते हुए उसी legend ऑब्जेक्ट को एक से अधिक बार हटाने की कोशिश करते हैं।
मुख्य बातें
ध्यान रखें कि axes.legend legend को बनाता भी है और जोड़ता भी है, साथ ही पहले वाले को हटाता है। अगर आपको कई legends चाहिए, तो सिर्फ उन्हीं को मैन्युअली जोड़ें जो पहले से axes पर मौजूद नहीं हैं। जब हर legend केवल एक बार मौजूद होगा, तो remove() अपेक्षित रूप से काम करेगा। अगर आप क्लीनअप को ऑटोमेट करते हैं, तो यह सुनिश्चित करें कि एक ही legend ऑब्जेक्ट को बार-बार हटाने की कोशिश न हो; जिन legend इंस्टैंसेज़ को आपने जोड़ा है, उनका रिकॉर्ड रखना इस समस्या से बचाता है।
यह लेख StackOverflow के एक प्रश्न (लेखक: BernhardWebstudio) और RuthC के उत्तर पर आधारित है।