कई खुले आंकड़ों के बारे में चेतावनी


166

एक स्क्रिप्ट में जहां मैं कई आंकड़े बनाता हूं fix, ax = plt.subplots(...), मुझे चेतावनी मिलती है रनटाइमवेयरिंग: 20 से अधिक आंकड़े खोले गए हैं। Pyplot इंटरफ़ेस ( matplotlib.pyplot.figure) के माध्यम से बनाए गए आंकड़े स्पष्ट रूप से बंद होने तक बनाए रखे जाते हैं और बहुत अधिक मेमोरी का उपभोग कर सकते हैं।

हालाँकि, मुझे समझ में नहीं आ रहा है कि मुझे यह चेतावनी क्यों मिल रही है, क्योंकि फिगर को सेव करने के बाद fig.savefig(...), मैं इसे डिलीट कर देता हूँ fig.clear(); del fig। मेरे कोड में कोई बिंदु नहीं है, मेरे पास एक समय में एक से अधिक आंकड़े खुले हैं। फिर भी, मुझे कई खुले आंकड़ों के बारे में चेतावनी मिली है। इसका क्या मतलब है / मैं चेतावनी पाने से कैसे बच सकता हूं?


9
यदि आप इसमें से बहुत कुछ कर रहे हैं, और कुछ भी अंतःक्रियात्मक रूप से प्रदर्शित नहीं कर रहे हैं, तो आप pltपूरी तरह से दरकिनार कर सकते हैं। Eg stackoverflow.com/a/16337909/325565 (मेरे स्वयं के उत्तरों में से एक को प्लग करने के लिए नहीं, लेकिन यह वही है जिसे मैं सबसे जल्दी पा सकता हूं ...)
जो किंग्सटन

1
@JoeKington धन्यवाद, यह एक बेहतर समाधान है
hi 19

जो किंगटन का जवाब मुख्य उत्तर सूची में होना चाहिए। यह काम करता है और plt.close () के कार्यक्रम को धीमा करता है जो डॉन किर्बी ने उल्लेख किया है।
नेटलीएल

जवाबों:


199

एक नया आंकड़ा बनाने के बजाय अपने आंकड़ा ऑब्जेक्ट का उपयोग करें .clfया .claपर । से @DavidZwicker

मान लें कि आप आयात किया है pyplotके रूप में

import matplotlib.pyplot as plt

plt.cla()एक धुरी को साफ करता है , अर्थात वर्तमान आकृति में वर्तमान में सक्रिय अक्ष। यह अन्य अक्षों को अछूता छोड़ देता है।

plt.clf()अपने सभी अक्षों के साथ पूरे वर्तमान आंकड़े को साफ करता है , लेकिन खुली हुई खिड़की को छोड़ देता है, जैसे कि यह अन्य भूखंडों के लिए पुन: उपयोग किया जा सकता है।

plt.close()एक विंडो बंद करता है , जो वर्तमान विंडो होगी, यदि अन्यथा निर्दिष्ट नहीं है। plt.close('all')सभी खुले आंकड़े बंद कर देंगे।

del figकाम नहीं करने का कारण यह है कि pyplotराज्य-मशीन आसपास के आंकड़े का संदर्भ रखती है (जैसा कि यह जानना चाहिए कि यह 'वर्तमान आंकड़ा' क्या है)। इसका मतलब है कि भले ही आप अपने रेफरी को फिगर में हटा दें , लेकिन कम से कम एक लाइव रेफरी है, इसलिए यह कभी भी कचरा एकत्र नहीं होगा।

चूंकि मैं इस उत्तर के लिए यहां सामूहिक ज्ञान पर मतदान कर रहा हूं, @JoeKington टिप्पणियों में उल्लेख करता है जो पाइलैबplt.close(fig) स्टेट मशीन ( plt._pylab_helpers.Gcf ) से एक विशिष्ट आंकड़ा उदाहरण को हटा देगा और इसे कचरा एकत्र करने की अनुमति देगा।


1
Mhh। नहीं है clfके लिए figureवर्ग है, लेकिन नहीं closedel figवास्तव में आंकड़ा बंद और हटाना क्यों नहीं है ?
andreas-h

2
@ andreas-h मेरा अनुमान है: अपने स्वयं के हैंडलर के साथ खिड़की प्रबंधक की तरह कुछ जटिल के लिए, दायरे से बाहर कुछ डालने की तुलना में अधिक सफाई की आवश्यकता हो सकती है। आपका अधिकार जो closeआंकड़ा ऑब्जेक्ट पर काम नहीं करेगा, उसे plt.close()इसके बजाय कॉल करें fig.clf()
आदी

5
@ andreas-h - मूल रूप से, कारण यह del figहै कि काम नहीं करता है कि यह एक __del__विधि (जो मूल रूप से कॉल plt.close(fig)करेगा) देने से इस विशेष मामले में परिपत्र संदर्भ पैदा होगा, और figएक __del__विधि होने से कचरा एकत्र नहीं होने के लिए अन्य चीजों का कारण होगा । (या यह मेरी अस्पष्ट याद है, वैसे भी।) किसी भी दर पर, यह निश्चित रूप से थोड़ा परेशान है, लेकिन आपको plt.close(fig)इसके बजाय कॉल करना चाहिए del fig। एक साइड नोट पर, matplotlib वास्तव में इस के लिए एक संदर्भ प्रबंधक का उपयोग कर सकता है ...
जो किंगटन

6
@Hooked - इसे थोड़ा और स्पष्ट करने के लिए, आप अपने प्रश्न को यह उल्लेख करने के लिए संपादित कर सकते हैं कि plt.close(fig)पाइलैब स्टेट मशीन ( plt._pylab_helpers.Gcf) से एक विशिष्ट आंकड़ा उदाहरण को हटा देगा और इसे एकत्र किए जाने वाले कचरे की अनुमति देगा।
जो किंगटन

2
@JoeKington pltथोड़ा गड़बड़ है और इस बात का विचार है कि इसका एक गुच्छा फिर से कैसे किया जाए। संदर्भ प्रबंधक पेचीदा है .... देखें github.com/matplotlib/matplotlib/pull/2736 , github.com/matplotlib/matplotlib/pull/2624
tacaswell

32

हुक किए गए उत्तर पर विस्तार करने के लिए यहां थोड़ा और विस्तार दिया गया है । जब मैंने पहली बार उस उत्तर को पढ़ा, तो मैंने clf() एक नया आंकड़ा बनाने के बजाय कॉल करने के निर्देश को याद किया । clf()अपने दम पर मदद नहीं करता है यदि आप तब जाकर एक और आंकड़ा बनाते हैं।

यहां एक तुच्छ उदाहरण दिया गया है जो चेतावनी का कारण बनता है:

from matplotlib import pyplot as plt, patches
import os


def main():
    path = 'figures'
    for i in range(21):
        _fig, ax = plt.subplots()
        x = range(3*i)
        y = [n*n for n in x]
        ax.add_patch(patches.Rectangle(xy=(i, 1), width=i, height=10))
        plt.step(x, y, linewidth=2, where='mid')
        figname = 'fig_{}.png'.format(i)
        dest = os.path.join(path, figname)
        plt.savefig(dest)  # write image to file
        plt.clf()
    print('Done.')

main()

चेतावनी से बचने के लिए, मुझे कॉल को subplots()लूप के बाहर खींचना होगा । आयतों को देखने के लिए, मुझे स्विच clf()करने की आवश्यकता है cla()। वह धुरी को हटाए बिना ही धुरी को साफ करता है।

from matplotlib import pyplot as plt, patches
import os


def main():
    path = 'figures'
    _fig, ax = plt.subplots()
    for i in range(21):
        x = range(3*i)
        y = [n*n for n in x]
        ax.add_patch(patches.Rectangle(xy=(i, 1), width=i, height=10))
        plt.step(x, y, linewidth=2, where='mid')
        figname = 'fig_{}.png'.format(i)
        dest = os.path.join(path, figname)
        plt.savefig(dest)  # write image to file
        plt.cla()
    print('Done.')

main()

यदि आप बैचों में भूखंडों का निर्माण कर रहे हैं, तो आपको दोनों cla()और का उपयोग करना पड़ सकता है close()। मैं एक समस्या में भाग गया, जहां एक बैच शिकायत किए बिना 20 से अधिक प्लॉट रख सकता था, लेकिन यह 20 बैचों के बाद शिकायत करेगा। मैंने तय किया कि cla()प्रत्येक साजिश के close()बाद , और प्रत्येक बैच के बाद का उपयोग करके ।

from matplotlib import pyplot as plt, patches
import os


def main():
    for i in range(21):
        print('Batch {}'.format(i))
        make_plots('figures')
    print('Done.')


def make_plots(path):
    fig, ax = plt.subplots()
    for i in range(21):
        x = range(3 * i)
        y = [n * n for n in x]
        ax.add_patch(patches.Rectangle(xy=(i, 1), width=i, height=10))
        plt.step(x, y, linewidth=2, where='mid')
        figname = 'fig_{}.png'.format(i)
        dest = os.path.join(path, figname)
        plt.savefig(dest)  # write image to file
        plt.cla()
    plt.close(fig)


main()

मैंने यह देखने के लिए प्रदर्शन को मापा कि क्या यह एक बैच के भीतर आकृति का पुन: उपयोग करने के लायक है, और यह छोटा सा नमूना कार्यक्रम 41s से 49s (20% धीमा) तक धीमा हो गया, जब मुझे बस close()हर साजिश के बाद बुलाया गया था।


यह एक बेहतरीन जवाब है। स्वीकृत उत्तर वास्तव में हाथ में वास्तविक समस्या को संबोधित नहीं करता है, जो कि मेमोरी खपत है।
काइल

24

यदि आप जानबूझकर कई भूखंडों को स्मृति में रखना चाहते हैं, लेकिन इसके बारे में चेतावनी नहीं देना चाहते हैं, तो आप आंकड़े बनाने से पहले अपने विकल्पों को अपडेट कर सकते हैं।

import matplotlib.pyplot as plt
plt.rcParams.update({'figure.max_open_warning': 0})

यह मेमोरी को प्रबंधित करने के तरीके के बारे में कुछ भी बदले बिना चेतावनी को उत्सर्जित होने से रोकेगा।


ज्यूपिटर के माहौल में, क्या मेमोरी एलोकेशन तब तक बनी रहती है, जब तक कि प्लॉट दिखा रहा सेल मौजूद रहता है?
Matanster

2
@matanster, मुझे लगता है कि यह अपना सवाल है के रूप में पोस्ट करेंगे। मैंने जवाब देना शुरू कर दिया, तब मुझे एहसास हुआ कि मैं ईमानदारी से जवाब देने के लिए गुठली के जुपिटर के प्रबंधन के बारे में पर्याप्त नहीं जानता।
बजे

@matanster जब तक कर्नेल उपयोगकर्ता द्वारा स्पष्ट रूप से बंद नहीं किया जाता है, तब तक उनके लिए आवंटित सभी चर और मेमोरी मौजूद हैं। यह कोशिकाओं से जुड़ा नहीं है। नए जुपिटर हब में, सिस्टम गुठली को बंद कर सकता है (इसे कॉन्फ़िगर किया जा सकता है)।
महानवयन

0

निम्नलिखित स्निपेट ने मेरे लिए समस्या हल कर दी:


class FigureWrapper(object):
    '''Frees underlying figure when it goes out of scope. 
    '''

    def __init__(self, figure):
        self._figure = figure

    def __del__(self):
        plt.close(self._figure)
        print("Figure removed")


# .....
    f, ax = plt.subplots(1, figsize=(20, 20))
    _wrapped_figure = FigureWrapper(f)

    ax.plot(...
    plt.savefig(...
# .....

जब _wrapped_figureस्कोप निकल जाता है तो रनटाइम हमारी __del__()विधि को plt.close()अंदर से बुलाता है। यह तब भी होता है जब अपवाद _wrapped_figureनिर्माणकर्ता के बाद आग हो जाती है ।


0

यह भी उपयोगी है यदि आप केवल अस्थायी रूप से चेतावनी को दबाना चाहते हैं:

    import matplotlib.pyplot as plt
       
    with plt.rc_context(rc={'figure.max_open_warning': 0}):
        lots_of_plots()
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.