कैसे एक Matplotlib साजिश में लाइनों को हटाने के लिए


84

मैं एक matplotlib कुल्हाड़ियों की एक पंक्ति (या लाइनों) को इस तरह से कैसे हटा सकता हूं क्योंकि यह वास्तव में कचरा एकत्र करता है और स्मृति को वापस करता है? नीचे दिया गया कोड लाइन को हटाने के लिए प्रकट होता है, लेकिन कभी भी मेमोरी जारी नहीं करता है (यहां तक ​​कि स्पष्ट कॉल के साथ gc.collect())

from matplotlib import pyplot
import numpy
a = numpy.arange(int(1e7))
# large so you can easily see the memory footprint on the system monitor.
fig = pyplot.Figure()
ax  = pyplot.add_subplot(1, 1, 1)
lines = ax.plot(a) # this uses up an additional 230 Mb of memory.
# can I get the memory back?
l = lines[0]
l.remove()
del l
del lines
# not releasing memory
ax.cla() # this does release the memory, but also wipes out all other lines.

तो क्या एक कुल्हाड़ी से सिर्फ एक लाइन को हटाने और मेमोरी वापस पाने का एक तरीका है? यह संभावित समाधान भी काम नहीं करता है।

जवाबों:


70

मैं दिखा रहा हूँ कि एक संयोजन lines.pop(0) l.remove()और del lचाल है।

from matplotlib import pyplot
import numpy, weakref
a = numpy.arange(int(1e3))
fig = pyplot.Figure()
ax  = fig.add_subplot(1, 1, 1)
lines = ax.plot(a)

l = lines.pop(0)
wl = weakref.ref(l)  # create a weak reference to see if references still exist
#                      to this object
print wl  # not dead
l.remove()
print wl  # not dead
del l
print wl  # dead  (remove either of the steps above and this is still live)

मैंने आपके बड़े डेटासेट की जाँच की और मेमोरी के रिलीज़ की पुष्टि सिस्टम मॉनीटर पर भी की गई।

बेशक सरल तरीका (जब मुसीबत-शूटिंग नहीं) इसे सूची से पॉप करना होगा और इसके लिए removeएक कठिन संदर्भ बनाए बिना लाइन ऑब्जेक्ट पर कॉल करना होगा:

lines.pop(0).remove()

मैंने आपका कोड चलाया और मुझे मिला: [8:37 pm] @flattop: ~ / Desktop / sandbox> python delete_lines.py <weakref at 0x8dd348c; 0x8dd43ec पर <Line2D '> 0x8dd348c पर कमजोर; 0x8dd43ec पर <Line2D '> 0x8dd348c पर कमजोर; 0x8dd43ec पर 'लाइन 2 डी'> मैं ubuntu 10.04 में डेविडप्लॉटिब संस्करण 0.99.1.1 का उपयोग कर रहा हूं
डेविड मॉर्टन

1
@ डेविड मोर्टन मैं बस 0.99.1 पर डाउनग्रेड हो गया और अब मैं आपकी समस्या को पुन: पेश करता हूं। मुझे लगता है कि मैं केवल 1.0.1 को अपग्रेड करने की सिफारिश कर सकता हूं। 0.99.x
पॉल

1
यहाँ समस्या की संभावना है कि जब वे नहीं होने चाहिए, तब चारों ओर लटके संदर्भ। मैं शर्त लगा सकता हूं कि ओपी इप्थॉन का इस्तेमाल चीजों को परखने के लिए कर रहा था। मेरा जवाब देखिए।
वर्टिसिटी

67

यह एक बहुत लंबी व्याख्या है जो मैंने अपने एक सहकर्मी के लिए टाइप की थी। मुझे लगता है कि यह यहां भी मददगार होगा। हालांकि, धैर्य रखें। मैं असली मुद्दे पर आता हूं कि आप अंत की ओर हैं। एक टीज़र के रूप में, यह आपकी Line2Dवस्तुओं के चारों ओर अतिरिक्त संदर्भ होने का एक मुद्दा है ।

चेतावनी: इससे पहले कि हम गोता लगाते हैं एक अन्य नोट। यदि आप इसे बाहर परीक्षण करने के लिए आईपीथॉन का उपयोग कर रहे हैं, तो आईपीथॉन अपने स्वयं के संदर्भ रखता है और उनमें से सभी कमजोर नहीं हैं। तो, IPython में कचरा संग्रह का परीक्षण काम नहीं करता है। यह सिर्फ मामलों को भ्रमित करता है।

ठीक है, यहाँ हम चलते हैं। प्रत्येक matplotlibऑब्जेक्ट ( Figureऔर Axes, आदि) विभिन्न विशेषताओं के माध्यम से अपने बाल कलाकारों तक पहुंच प्रदान करता है। निम्नलिखित उदाहरण काफी लंबा हो रहा है, लेकिन रोशन होना चाहिए।

हम एक Figureऑब्जेक्ट बनाकर शुरू करते हैं , फिर Axesउस आकृति में एक ऑब्जेक्ट जोड़ते हैं । ध्यान दें कि axऔर fig.axes[0]समान वस्तु (समान id()) हैं।

>>> #Create a figure
>>> fig = plt.figure()
>>> fig.axes
[]

>>> #Add an axes object
>>> ax = fig.add_subplot(1,1,1)

>>> #The object in ax is the same as the object in fig.axes[0], which is 
>>> #   a list of axes objects attached to fig 
>>> print ax
Axes(0.125,0.1;0.775x0.8)
>>> print fig.axes[0]
Axes(0.125,0.1;0.775x0.8)  #Same as "print ax"
>>> id(ax), id(fig.axes[0])
(212603664, 212603664) #Same ids => same objects

यह भी एक कुल्हाड़ी वस्तु में लाइनों तक फैली हुई है:

>>> #Add a line to ax
>>> lines = ax.plot(np.arange(1000))

>>> #Lines and ax.lines contain the same line2D instances 
>>> print lines
[<matplotlib.lines.Line2D object at 0xce84bd0>]
>>> print ax.lines
[<matplotlib.lines.Line2D object at 0xce84bd0>]

>>> print lines[0]
Line2D(_line0)
>>> print ax.lines[0]
Line2D(_line0)

>>> #Same ID => same object
>>> id(lines[0]), id(ax.lines[0])
(216550352, 216550352)

यदि आपको plt.show()ऊपर बताई गई कॉल का उपयोग करना है, तो आपको एक आकृति का एक अक्ष और एक रेखा दिखाई देगी:

एक आकृति जिसमें अक्षों का एक समूह और एक एकल रेखा होती है

अब, जबकि हमने देखा है कि सामग्री linesऔर ax.linesसमान है, यह ध्यान रखना बहुत महत्वपूर्ण है कि linesचर द्वारा संदर्भित वस्तु वैसी नहीं है, जैसा ax.linesकि निम्न द्वारा देखी जा रही वस्तु द्वारा देखी जा सकती है:

>>> id(lines), id(ax.lines)
(212754584, 211335288)

परिणामस्वरूप, एक तत्व को हटाने से linesवर्तमान भूखंड के लिए कुछ भी नहीं होता है, लेकिन एक तत्व को हटाने से ax.linesउस रेखा को वर्तमान भूखंड से हटा दिया जाता है। इसलिए:

>>> #THIS DOES NOTHING:
>>> lines.pop(0)

>>> #THIS REMOVES THE FIRST LINE:
>>> ax.lines.pop(0)

इसलिए, यदि आप कोड की दूसरी पंक्ति को चलाने के लिए थे, तो आप वर्तमान भूखंड से Line2Dनिहित वस्तु को हटा देंगे ax.lines[0]और यह चला जाएगा। ध्यान दें कि यह ax.lines.remove()अर्थ के माध्यम से भी किया जा सकता है कि आप Line2Dएक चर में एक उदाहरण को बचा सकते हैं , फिर इसे ax.lines.remove()उस पंक्ति को हटाने के लिए पास कर सकते हैं , जैसे:

>>> #Create a new line
>>> lines.append(ax.plot(np.arange(1000)/2.0))
>>> ax.lines
[<matplotlib.lines.Line2D object at 0xce84bd0>,  <matplotlib.lines.Line2D object at 0xce84dx3>]

एक आकृति जिसमें कुल्हाड़ियों और दो लाइनों का एक सेट होता है

>>> #Remove that new line
>>> ax.lines.remove(lines[0])
>>> ax.lines
[<matplotlib.lines.Line2D object at 0xce84dx3>]

एक आकृति जिसमें कुल्हाड़ियों का एक सेट होता है और केवल दूसरी पंक्ति होती है

उपरोक्त सभी काम करता है fig.axesऔर साथ ही इसके लिए काम करता हैax.lines

अब, असली समस्या यहाँ। यदि हम ax.lines[0]किसी weakref.refऑब्जेक्ट में निहित संदर्भ को संग्रहीत करते हैं , तो इसे हटाने का प्रयास करें, हम देखेंगे कि यह कचरा एकत्र नहीं करता है:

>>> #Create weak reference to Line2D object
>>> from weakref import ref
>>> wr = ref(ax.lines[0])
>>> print wr
<weakref at 0xb758af8; to 'Line2D' at 0xb757fd0>
>>> print wr()
<matplotlib.lines.Line2D at 0xb757fd0>

>>> #Delete the line from the axes
>>> ax.lines.remove(wr())
>>> ax.lines
[]

>>> #Test weakref again
>>> print wr
<weakref at 0xb758af8; to 'Line2D' at 0xb757fd0>
>>> print wr()
<matplotlib.lines.Line2D at 0xb757fd0>

संदर्भ अभी भी जीवित है! क्यों? इसका कारण यह है Line2Dकि वस्तु के संदर्भ में एक और संदर्भ अभी भी wrहै। याद रखें कि कैसे linesसमान ID नहीं थी, ax.linesलेकिन समान तत्व सम्‍मिलित थे? खैर, यह समस्या है।

>>> #Print out lines
>>> print lines
[<matplotlib.lines.Line2D object at 0xce84bd0>,  <matplotlib.lines.Line2D object at 0xce84dx3>]

To fix this problem, we simply need to delete `lines`, empty it, or let it go out of scope.

>>> #Reinitialize lines to empty list
>>> lines = []
>>> print lines
[]
>>> print wr
<weakref at 0xb758af8; dead>

तो, कहानी का नैतिक है, अपने आप को साफ करना। यदि आप उम्मीद करते हैं कि कुछ कचरा एकत्र किया जाएगा, लेकिन ऐसा नहीं है, तो आप कहीं बाहर एक संदर्भ छोड़ रहे हैं।


2
वास्तव में मुझे जो चाहिए था। मैं हजारों मानचित्रों की साजिश रच रहा हूं, जिनमें से प्रत्येक एक विश्व मानचित्र प्रक्षेपण के शीर्ष पर तितर बितर साजिश है। वे प्रत्येक 3 सेकंड ले रहे थे! पहले से ही खींचे गए मानचित्र के साथ आकृति का पुन: उपयोग करके और अक्षतरण से परिणामी संग्रह को पॉपअप करके मैंने इसे एक सेकंड के 1/3 पर ले लिया। धन्यवाद!
गैरीबिशप

4
मुझे लगता है कि यह mpl के वर्तमान संस्करणों में आवश्यक नहीं है। कलाकार के पास एक remove()फ़ंक्शन होता है जो उन्हें चीजों के mpl पक्ष से साफ करेगा, और फिर आपको केवल अपने संदर्भों का ट्रैक रखने की आवश्यकता है।
ताकसवेल

2
हुह, किसी भी विचार क्या matplotlib के संस्करण में यह एक ही परिवर्तन?
वर्टिसिटी

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

14

मैंने अलग-अलग मंचों में बहुत सारे अलग-अलग जवाबों की कोशिश की है। मुझे लगता है कि यह आपके विकास की मशीन पर निर्भर करता है। लेकिन मैंने कहा कि बयान का इस्तेमाल किया गया था

ax.lines = []

और पूरी तरह से काम करता है। मैं इसका उपयोग नहीं करता हूं cla()क्योंकि यह उन सभी परिभाषाओं को हटा देता है जो मैंने साजिश के लिए की हैं

पूर्व।

pylab.setp(_self.ax.get_yticklabels(), fontsize=8)

लेकिन मैंने कई बार लाइनें हटाने की कोशिश की है। जब मैं हटा रहा था, तब भी उस लाइन के संदर्भ की जांच करने के लिए कमजोर लाइब्रेरी का उपयोग कर रहा था, लेकिन मेरे लिए कुछ भी काम नहीं किया।

आशा है कि यह किसी और के लिए काम करता है = डी


यहाँ समस्या की संभावना है कि जब वे नहीं होने चाहिए, तब चारों ओर लटके संदर्भ। मैं शर्त लगाऊंगा कि ओपी इप्थॉन का इस्तेमाल चीजों को परखने के लिए कर रहा था। मेरा जवाब देखिए।
वर्टिसिटी

5

(ऊपर के आदमी के समान उदाहरण का उपयोग करके)

from matplotlib import pyplot
import numpy
a = numpy.arange(int(1e3))
fig = pyplot.Figure()
ax  = fig.add_subplot(1, 1, 1)
lines = ax.plot(a)

for i, line in enumerate(ax.lines):
    ax.lines.pop(i)
    line.remove()

1

उम्मीद है कि यह दूसरों की मदद कर सकता है: उपरोक्त उदाहरण उपयोग करते हैं ax.lines। अधिक हालिया mpl (3.3.1) के साथ है ax.get_lines()। यह कॉलिंग की आवश्यकता को दरकिनार कर देता हैax.lines=[]

for line in ax.get_lines(): # ax.lines:
    line.remove()
# ax.lines=[] # needed to complete removal when using ax.lines
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.