2025, Oct 02 09:33

VSCode के Jupyter में Plotly की कई ट्रेसेज़ को batch_update से तेज़ी से अपडेट करें

VSCode के Jupyter नोटबुक में Plotly मल्टी‑ट्रेस ऐनिमेशन धीमे हैं? fig.batch_update से सभी ट्रेसेज़ को एक बैच में अपडेट करें और तेज़, स्मूद रेंडर पाएं.

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

धीमापन कैसे दोहराएँ

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

import plotly.graph_objects as go
import numpy as np
import random
perT = 20
t_steps_full = np.arange(1, 101, 2)
jitter = 0.25
decay_const = 60
series_count = 32
series_ids = np.arange(0, series_count, 1)
t_axis = np.arange(1, 51, 2)
y_store = [np.array([np.sin(2*np.pi*tv/perT)*np.exp(-tv/decay_const) + random.random()*jitter for tv in t_axis]) for _ in series_ids]
fig_widget = go.FigureWidget()
for idx, _sid in enumerate(series_ids):
    fig_widget.add_trace(go.Scatter(x=t_axis, y=y_store[idx][0:1], mode='lines+markers', name='lines'))
fig_widget.show()
for step_idx, t_point in enumerate(t_axis):
    for s in range(len(series_ids)):
        fig_widget.data[s].x = t_axis[:step_idx]
        fig_widget.data[s].y = y_store[s][0:step_idx]

ज़्यादा ट्रेसेज़ होने पर ऊपर वाला लूप धीमा पड़ जाता है—हर इटरेशन में कुछ सेकंड लग सकते हैं। ट्रेस की संख्या घटाते ही गति बढ़ जाती है, जो इशारा करता है कि समस्या प्रति‑ट्रेस अपडेट करने के तरीके में ही है।

वास्तव में प्रदर्शन क्यों गिरता है

किसी ट्रेस पर हर एट्रिब्यूट सेट करना, पायथन से फ्रंटएंड तक भेजे गए एक अपडेट में बदल जाता है। जब यही काम आप कई ट्रेसेज़ पर बार‑बार करते हैं, तो ओवरहेड हावी हो जाता है। नोटबुक वातावरण में यह असर और बढ़ जाता है, क्योंकि हर बदलाव Jupyter के comms चैनल से होकर गुजरता है। नतीजा है स्पष्ट देरी, जो ट्रेस की संख्या के साथ बढ़ती जाती है।

तेज़ तरीका: बैच अपडेट्स

हर ट्रेस को अलग‑अलग छेड़ने के बजाय, सभी ट्रेसेज़ का नया डेटा पहले से तैयार करें और उसे एक ही बैच ऑपरेशन में लागू करें। Plotly इसके लिए fig.batch_update() देता है, जो बदलावों को समेटकर एक ही इवेंट के रूप में Plotly.js को भेजता है।

import plotly.graph_objects as go
import numpy as np
import random
import time
perT = 20
t_steps_full = np.arange(1, 101, 2)
jitter = 0.25
decay_const = 60
series_count = 32
series_ids = np.arange(0, series_count, 1)
t_axis = np.arange(1, 51, 2)
y_store = [np.array([np.sin(2*np.pi*tt/perT)*np.exp(-tt/decay_const) + random.random()*jitter for tt in t_axis]) for _ in series_ids]
fig_widget = go.FigureWidget()
for s_idx, _ in enumerate(series_ids):
    fig_widget.add_trace(go.Scatter(x=[], y=[], mode='lines+markers', name=f'Trace {s_idx+1}'))
fig_widget.show()
for j in range(len(t_axis)):
    updates = [{'x': t_axis[:j+1], 'y': y_store[i][:j+1]} for i in range(series_count)]
    with fig_widget.batch_update():
        for k in range(series_count):
            fig_widget.data[k].x = updates[k]['x']
            fig_widget.data[k].y = updates[k]['y']
    time.sleep(0.01)

यह तरीका प्रति‑ट्रेस होने वाली लगातार बात‑चीत से बचाता है और Plotly.js को सभी बदलाव एक साथ लागू करने के लिए कहता है। व्यवहार में, मल्टी‑ट्रेस ऐनिमेशन काफी फुर्तीले हो जाते हैं।

व्यावहारिक अनुभव से जुड़े अवलोकन

यदि आपको सब कुछ एक ही सेल में चलाना है, तो fig.show() की जगह display(fig) का उपयोग करें और उसके बाद लगभग एक सेकंड का छोटा सा इंतज़ार दें—इससे पूरा क्रम अपेक्षित रूप से रेंडर और अपडेट हो पाता है। Plotly का एक और उदाहरण यहाँ है: https://stackoverflow.com/a/78590766/8508004; यह थोड़ा नखरीला हो सकता है—कभी‑कभी आपको इसे दो बार चलाना पड़ सकता है और फिर प्ले बटन दबाना पड़ता है। साथ ही, इस तरह की सेटिंग कुछ पैटर्न्स में IOPub message rate exceeded ट्रिगर कर सकती है; एक वैकल्पिक सिंगल‑सेल उदाहरण यहाँ संदर्भित है: https://stackoverflow.com/a/66923695/8508004। अगर आप Plotly तक सीमित नहीं हैं, तो एनिमेटेड Matplotlib उदाहरणों का यह संग्रह देख सकते हैं: https://github.com/fomightez/animated_matplotlib-binder

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

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

मुख्य निष्कर्ष

सभी ट्रेसेज़ को पहले से खाली डेटा के साथ बना लें, हर फ़्रेम के लिए प्रत्येक सीरीज़ की ऐरे तैयार करें, और उन्हें fig.batch_update() के ज़रिए लागू करें। अगर एक ही सेल में सब चलाना हो, तो फ़िगर के इनिशियलाइज़ होने के लिए display(fig) के साथ छोटा सा डिले दें। और अगर प्रदर्शन अब भी ठीक न लगे, तो याद रखें—ट्रेस कम करने से ओवरहेड काफी घटता है; यही व्यवहार ntraces घटाने पर भी दिखता है।

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