2025, Oct 03 19:31

Matplotlib में थ्रेशोल्ड वाले द्वि-रंग कंटूर और सही BoundaryNorm

Matplotlib में थ्रेशोल्ड आधारित द्वि-रंग कंटूर में कलरबार के सफेद गैप व अतिरिक्त टिक क्यों आते हैं, और BoundaryNorm की सही बिन सीमाएँ सेट कर इसे कैसे ठीक करें—उदाहरण सहित.

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

समस्या को दोहराना

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

from matplotlib import colors as mpl_colors, cm as mpl_cm
import numpy as nps
import matplotlib.pyplot as pl
import matplotlib.tri as mtri
nps.random.seed(19680801)
pt_count = 200
grid_x = 100
grid_y = 200
x_vals = nps.random.uniform(-2, 2, pt_count)
y_vals = nps.random.uniform(-2, 2, pt_count)
z_vals = x_vals * nps.exp(-x_vals**2 - y_vals**2)
x_lin = nps.linspace(-2.1, 2.1, grid_x)
y_lin = nps.linspace(-2.1, 2.1, grid_y)
tri_obj = mtri.Triangulation(x_vals, y_vals)
lin_interp = mtri.LinearTriInterpolator(tri_obj, z_vals)
Xg, Yg = nps.meshgrid(x_lin, y_lin)
z_grid = lin_interp(Xg, Yg)
fig_obj = pl.figure()
axis = fig_obj.subplots()
palette = ['blue', 'red']
cutoff = nps.mean(z_vals)
normer = mpl_colors.BoundaryNorm([cutoff - 1e-3, cutoff], ncolors=2, clip=True)
cmap_obj = mpl_colors.LinearSegmentedColormap.from_list('name', palette, N=2)
axis.contour(x_lin, y_lin, z_grid, norm=normer, cmap=cmap_obj)
scalar = mpl_cm.ScalarMappable(norm=normer, cmap=cmap_obj)
bar = fig_obj.colorbar(scalar, ax=axis)
bar.set_ticks([nps.min(z_grid), cutoff, nps.max(z_grid)])
bar.set_ticklabels([nps.min(z_grid), cutoff, nps.max(z_grid)])

असल में गड़बड़ी क्या है

BoundaryNorm आपके डेटा-रेंज को स्पष्ट किनारों से परिभाषित बिन्स में बाँटता है। अगर आपको ठीक दो रंग चाहिए, तो दो बिन्स चाहिए, यानी तीन सीमांत मान। केवल थ्रेशोल्ड के आस-पास के मान देने से बिनिंग गलत हो जाती है। नतीजा: कलरबार थ्रेशोल्ड पर सुस्पष्ट विभाजन नहीं दिखाता, अनचाहे खाली हिस्से और भटका हुआ एक टिक दिख सकता है।

समाधान

बिन के किनारे पूरे डेटा-रेंज को कवर करें, और थ्रेशोल्ड को बीच में रखें। इससे दो बिन बनेंगे: [सबसे न्यून मान, थ्रेशोल्ड] और [थ्रेशोल्ड, सबसे उच्च मान]।

from matplotlib import colors as mpl_colors, cm as mpl_cm
import numpy as nps
import matplotlib.pyplot as pl
import matplotlib.tri as mtri
nps.random.seed(19680801)
pt_count = 200
grid_x = 100
grid_y = 200
x_vals = nps.random.uniform(-2, 2, pt_count)
y_vals = nps.random.uniform(-2, 2, pt_count)
z_vals = x_vals * nps.exp(-x_vals**2 - y_vals**2)
x_lin = nps.linspace(-2.1, 2.1, grid_x)
y_lin = nps.linspace(-2.1, 2.1, grid_y)
tri_obj = mtri.Triangulation(x_vals, y_vals)
lin_interp = mtri.LinearTriInterpolator(tri_obj, z_vals)
Xg, Yg = nps.meshgrid(x_lin, y_lin)
z_grid = lin_interp(Xg, Yg)
fig_obj = pl.figure()
axis = fig_obj.subplots()
palette = ['blue', 'red']
cutoff = nps.mean(z_vals)
normer = mpl_colors.BoundaryNorm([nps.min(z_vals), cutoff, nps.max(z_vals)], ncolors=2, clip=True)
cmap_obj = mpl_colors.LinearSegmentedColormap.from_list('name', palette, N=2)
axis.contour(x_lin, y_lin, z_grid, norm=normer, cmap=cmap_obj)
scalar = mpl_cm.ScalarMappable(norm=normer, cmap=cmap_obj)
bar = fig_obj.colorbar(scalar, ax=axis)
bar.set_ticks([nps.min(z_grid), cutoff, nps.max(z_grid)])
bar.set_ticklabels([nps.min(z_grid), cutoff, nps.max(z_grid)])

इससे दो बिन की सीमाएँ [सबसे न्यून मान, औसत मान] और [औसत मान, सबसे उच्च मान] को कवर करती हैं, जिससे कलरमैप और कलरबार इच्छित द्विआधारी थ्रेशोल्ड-लॉजिक के साथ मेल में रहते हैं।

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

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

निष्कर्ष

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

यह लेख StackOverflow पर दिए गए प्रश्न (लेखक: Auguste Eclancher) और Matt Pitkin के उत्तर पर आधारित है।