2025, Oct 17 12:31

Jupyter में स्टैक्ड x–y चार्ट: min/max सीमा से सीधे बार/आयत ड्रॉ करें

Jupyter और matplotlib में स्टैक्ड x–y विज़ुअल बनाने का सरल तरीका: min_x, max_x, min_y, max_y सीमाओं से सीधे बार/आयत रेंडर करें। साफ़ भरा चार्ट, कम कोड।

Jupyter में भरी हुई, स्टैक्ड x–y विज़ुअलाइज़ेशन बनाना अक्सर उलझ सकता है, अगर आप लाइन प्लॉट्स से शुरुआत करके परतों के बीच शेड भरने की कोशिश करें। जब श्रेणियों को “PVR Group” फ़ील्ड के आधार पर स्टैक करना हो और वास्तविक ज्योमेट्री प्रत्येक आयत के min/max सीमाओं से निर्धारित हो, तो रास्ता सरल है: आयतों को सीधे ड्रॉ करें। नीचे एक संक्षिप्त मार्गदर्शिका है, जो लाइन-केंद्रित दृष्टिकोण से शुरू होकर बार-आधारित समाधान तक पहुँचती है और अपेक्षित भरा हुआ नतीजा देती है।

समस्या सेटअप

डेटा में हर श्रेणी के लिए आयत की स्पष्ट सीमा-मान मौजूद हैं: क्षैतिज सीमा के लिए min_x और max_x, तथा ऊर्ध्वाधर सीमा के लिए min_y और max_y। शुरुआती सोच यह थी कि इन मानों को आउटलाइन पाथ में बदला जाए, प्रत्येक समूह के लिए लाइनों के रूप में प्लॉट किया जाए, और फिर श्रेणी के अनुसार क्रमिक आउटलाइन के बीच का क्षेत्र भरा जाए।

import pandas as pd
import matplotlib.pyplot as plt
# उदाहरण: समूह बनाना और प्रति-श्रेणी आउटलाइन श्रृंखला तैयार करना
frame_xy = df_xy_columns.groupby('PVR Group')
xs_map = {}
ys_map = {}
for grp_name, grp_df in frame_xy:
    xs = grp_df[["min_x", "min_x", "max_x", "max_x"]].values.flatten()
    xs_map[grp_name] = pd.Series(xs, name=f"x_vals_{grp_name}")
    ys = grp_df[["min_y", "max_y", "max_y", "min_y"]].values.flatten()
    ys_map[grp_name] = pd.Series(ys, name=f"y_vals_{grp_name}")
plt.xlabel("Cumulative " + x_hdr)
plt.ylabel(y_hdr)
plt.title(y_hdr + " Bookshelf Chart by " + agg_tag)
plt.grid(False)
for key in xs_map:
    plt.plot(xs_map[key], ys_map[key], linestyle='-')

यह तरीका श्रेणी-वार आउटलाइन तो बना देता है, लेकिन मुख्य जरूरत अधूरी रह जाती है: स्टैक्ड परतों के बीच भरा हुआ क्षेत्र। कमी इस बात की है कि शेडिंग करते समय पिछली परत का भरोसेमंद संदर्भ कैसे लें, वो भी बिना मैनुअल नाम देने के।

असल में हो क्या रहा है

हर पंक्ति पहले से ही एक आयत का वर्णन करती है। बहुभुजों को लाइनों के रूप में फिर से गढ़ना और फिर उनसे भराव क्षेत्र निकालना फालतू जटिलता जोड़ता है। जब मनचाहा आउटपुट श्रेणी-आधारित स्टैक्ड, भरा हुआ चार्ट हो, तो बार सीधे डेटा मॉडल पर मैप होते हैं: चौड़ाई = max_x − min_x, ऊँचाई = max_y − min_y, और निचली सीमा = min_y। x पोज़िशन बार का केंद्र होता है, यानी min_x + (max_x − min_x)/2।

स्रोत में एक और बारीकी है: डुप्लिकेट कॉलम। जब फ्रेम में min_x और min_x2, या max_y और max_y2 जैसी जोड़ियाँ हों, तो डुप्लिकेट हटाने से प्लॉटिंग सुगम हो जाती है।

समाधान: आयतें सीधे रेंडर करें

बार्स पर स्विच करने से आउटलाइन-और-फिल की जटिलता हट जाती है और एक ही कॉल सही ज्योमेट्री खींच देता है। श्रेणियाँ समूह के आधार पर अलग बनी रहती हैं, और रेंडरिंग अपने आप भरी हुई होती है।

import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
# यदि डुप्लिकेट मौजूद हों तो केवल अद्वितीय कॉलम रखें
trimmed_df = df.T.drop_duplicates().T
fig_obj, ax_obj = plt.subplots(figsize=(15, 3))
palette = {"1_Low": "blue", "2_Med": "green", "3_High": "red"}
for cat_name, chunk in trimmed_df.groupby("PVR Group"):
    ax_obj.bar(
        (chunk['min_x'] + (chunk['max_x'] - chunk['min_x']) / 2),
        chunk['max_y'] - chunk['min_y'],
        width=(chunk['max_x'] - chunk['min_x']),
        bottom=chunk['min_y'],
        label=cat_name,
        alpha=.5,
        color=palette[cat_name],
        edgecolor=matplotlib.colors.colorConverter.to_rgba(palette[cat_name], alpha=.7)
    )
ax_obj.legend()
ax_obj.set_facecolor('#EBEBEB')
ax_obj.grid(color='white', linewidth=.5, alpha=.5)
for side in list(ax_obj.spines):
    ax_obj.spines[side].set_visible(False)
plt.xlim(xmin=0.0)
plt.show()

मैपिंग बिल्कुल सीधी है: x-अक्ष पर बार का केंद्र min_x के साथ आधी चौड़ाई जोड़कर मिलता है; ऊँचाई ऊर्ध्वाधर अंतर है; बेसलाइन min_y है। नतीजे में “PVR Group” के हिसाब से भरा हुआ, स्टैक्ड चार्ट मिलता है, और इंटर-लाइन शेडिंग से बिल्कुल वास्ता नहीं पड़ता। रंग चुनना चाहें तो स्पष्ट रूप से निर्धारित करें, या डिफ़ॉल्ट पर छोड़ दें—दोनों तरीके लक्ष्य के अनुरूप हैं।

यह क्यों मायने रखता है

जब ज्योमेट्री सीमाओं के रूप में एन्कोड हो, तो आयत जैसे प्रिमिटिव के साथ काम करना आउटलाइन-से-फिल रूपांतरण की ओवरहेड और पेचीदगी से बचाता है। यह किसी भी संख्या की श्रेणियों के साथ स्वाभाविक रूप से स्केल होता है और आधारभूत डेटा के प्रति सुसंगत रहता है। बार रेंडरिंग पर टिके रहने से स्टैकिंग और फिल अपने आप मिल जाते हैं, और समूहबद्धता स्पष्ट रहती है।

मुख्य बातें

यदि आपके DataFrame में पहले से min/max सीमाएँ हैं, तो लाइन पाथ से फिल रिवर्स-इंजीनियर करने के बजाय सीधे इन्हीं प्रिमिटिव से चार्ट बनाइए। श्रेणी फ़ील्ड पर groupby कीजिए, और सीमाओं से ही center, width, height, और bottom निकालकर बार से रेंडर कीजिए। अगर फ्रेम में डुप्लिकेट कॉलम हों, तो प्लॉटिंग से पहले उन्हें हटा दें ताकि पाइपलाइन साफ़ रहे। सख्त विज़ुअल नियंत्रण चाहिए तो रंग स्पष्ट रूप से सेट करें, वरना डिफ़ॉल्ट पर छोड़कर आगे चाहें तो उपयोगकर्ताओं को रंग चुनने दें। इससे कोड छोटा रहेगा, मंशा स्पष्ट रहेगी, और विज़ुअलाइज़ेशन डेटा के प्रति सटीक रहेगा।

यह लेख StackOverflow के एक प्रश्न (लेखक: rfulks) और strawdog के उत्तर पर आधारित है।