2011-02-13 39 views
54

Làm thế nào tôi có thể loại bỏ một dòng (hoặc dòng) của một trục matplotlib theo cách sao cho nó thực sự bị thu gom rác thải và giải phóng bộ nhớ? Mã dưới đây dường như xoá dòng, nhưng không bao giờ giải phóng bộ nhớ (thậm chí với các cuộc gọi rõ ràng để GC.Collect())Làm thế nào để loại bỏ các đường trong một âm mưu Matplotlib

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. 

Vậy là có một cách để chỉ cần xóa một dòng từ một trục và nhận được bộ nhớ trở lại? This potential solution cũng không hoạt động.

Trả lời

40

Tôi cho thấy rằng sự kết hợp của lines.pop(0)l.remove()del l thực hiện thủ thuật.

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) 

Tôi đã kiểm tra bộ dữ liệu lớn và phát hành bộ nhớ cũng được xác nhận trên màn hình hệ thống.

Tất nhiên cách đơn giản hơn (khi không rắc rối-chụp) sẽ được bật nó từ danh sách và gọi remove trên đối tượng dòng mà không cần tạo một tài liệu tham khảo cứng với nó:

lines.pop(0).remove() 
+0

Tôi đã chạy mã của bạn và nhận được: [8:37 pm] @flattop: ~/Desktop/sandbox> python delete_lines.py Tôi đang sử dụng phiên bản matplotlib 0.99.1.1 trong ubuntu 10.04 –

+1

@David Morton Tôi vừa hạ cấp xuống 0.99.1 và bây giờ tôi đã tái tạo sự cố của bạn. Tôi đoán tôi chỉ có thể khuyên bạn nên nâng cấp lên 1.0.1.Đã có * nhiều * số sửa lỗi từ 0.99.x – Paul

+0

Cảm ơn! Tối đánh giá rất cao sự giúp đỡ của bạn. –

2

(sử dụng cùng một ví dụ như anh chàng ở trên)

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() 
9

Tôi đã thử nhiều câu trả lời khác nhau trong các diễn đàn khác nhau. Tôi đoán nó phụ thuộc vào máy bạn đang phát triển. Nhưng tôi đã sử dụng tuyên bố

ax.lines = [] 

và hoạt động hoàn hảo. Tôi không sử dụng cla() vì nó xóa tất cả các định nghĩa mà tôi đã thực hiện đối với lô đất

Ví dụ:

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

nhưng tôi đã thử xóa nhiều lần. Cũng sử dụng thư viện weakref để kiểm tra tham chiếu đến dòng đó trong khi tôi đang xóa nhưng không có gì làm việc cho tôi.

Hy vọng điều này làm việc cho người khác = D

+0

Vấn đề ở đây có thể là vấn đề tham khảo khi họ không nên tham gia. Tôi sẽ đặt cược rằng OP đã sử dụng IPython để kiểm tra mọi thứ. Xem câu trả lời của tôi. – Vorticity

46

Đây là một lời giải thích rất dài mà tôi gõ lên cho một đồng nghiệp của tôi. Tôi nghĩ nó cũng hữu ích ở đây. Hãy kiên nhẫn, mặc dù. Tôi nhận được vấn đề thực sự mà bạn đang có để kết thúc. Cũng giống như một lời trêu ghẹo, đó là vấn đề có thêm tham chiếu đến các đối tượng Line2D của bạn treo xung quanh.

CẢNH BÁO: Một lưu ý khác trước khi chúng tôi đi sâu vào. Nếu bạn đang sử dụng IPython để kiểm tra điều này, IPython giữ tham chiếu của riêng nó và không phải tất cả chúng đều yếu. Vì vậy, kiểm tra bộ sưu tập rác trong IPython không hoạt động. Nó chỉ gây nhầm lẫn vấn đề.

OK, ở đây chúng tôi đi. Mỗi đối tượng matplotlib (Figure, Axes, v.v.) cung cấp quyền truy cập cho các nghệ sĩ trẻ thông qua các thuộc tính khác nhau. Ví dụ sau đây nhận được khá dài, nhưng nên được chiếu sáng.

Chúng tôi bắt đầu bằng cách tạo đối tượng Figure, sau đó thêm đối tượng Axes vào hình đó. Lưu ý rằng axfig.axes[0] là cùng một đối tượng (giống như 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 

này cũng mở rộng đến dòng trong một trục đối tượng:

>>> #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) 

Nếu bạn đã gọi plt.show() sử dụng những gì đã được thực hiện ở trên, bạn sẽ thấy một con số có chứa một tập hợp các trục và một dòng duy nhất :

A figure containing a set of axes and a single line

Bây giờ, trong khi chúng ta đã thấy rằng nội dung của linesax.lines là giống nhau, nó là rất quan trọng cần lưu ý rằng đối tượng được tham chiếu bởi biến lines là không giống như các đối tượng tôn kính bởi ax.lines như có thể thấy bằng cách như sau:

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

Như một hệ quả, loại bỏ một phần tử từ lines không làm gì với cốt truyện hiện tại, nhưng việc xóa một phần tử khỏi ax.lines sẽ xóa dòng đó khỏi ô hiện tại. Vì vậy:

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

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

Vì vậy, nếu bạn đã chạy dòng thứ hai của mã này, bạn sẽ loại bỏ các đối tượng Line2D chứa trong ax.lines[0] từ cốt truyện hiện tại và nó sẽ được biến mất. Lưu ý rằng điều này cũng có thể được thực hiện thông qua ax.lines.remove() ý nghĩa mà bạn có thể tiết kiệm một trường hợp Line2D trong một biến, sau đó vượt qua nó để ax.lines.remove() xóa dòng đó, như vậy:

>>> #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>] 

A figure containing a set of axes and two lines

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

A figure containing a set of axes and only the second line

Tất cả các công việc trên cho fig.axes cũng như hoạt động đối với ax.lines

Bây giờ, vấn đề thực sự ở đây. Nếu chúng ta lưu trữ các tài liệu tham khảo chứa trong ax.lines[0] vào một đối tượng weakref.ref, sau đó cố gắng để xóa nó, chúng ta sẽ nhận thấy rằng nó không được thu gom rác thải:

>>> #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> 

Tham chiếu vẫn còn sống! Tại sao? Điều này là do vẫn có một tham chiếu khác đến đối tượng Line2D mà tham chiếu trong số wr trỏ đến. Hãy nhớ cách lines không có cùng một ID là ax.lines nhưng chứa cùng một yếu tố? Vâng, đó là vấn đề.

>>> #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> 

Vì vậy, tinh thần của câu chuyện là, dọn sạch sau khi chính bạn. Nếu bạn mong đợi một cái gì đó để được thu thập rác nhưng nó không phải là, bạn có khả năng để lại một tài liệu tham khảo treo ra một nơi nào đó.

+2

Chính xác những gì tôi cần. Tôi đang vẽ hàng nghìn bản đồ, mỗi bản đồ có một âm mưu phân tán trên đầu bản đồ thế giới. Họ đã dành 3 giây mỗi lần! Bằng cách tái sử dụng con số với bản đồ đã được rút ra và popping bộ sưu tập kết quả từ ax.collections tôi đã nhận nó xuống đến 1/3 của một giây. Cảm ơn! – GaryBishop

+0

Vui vì tôi có thể giúp! – Vorticity

+3

Tôi nghĩ rằng điều này không còn cần thiết trong các phiên bản hiện tại của mpl. Các nghệ sĩ có một 'loại bỏ()' chức năng mà sẽ làm sạch chúng ra khỏi mặt mpl của sự vật, và sau đó bạn chỉ cần theo dõi các tài liệu tham khảo của bạn. – tacaswell

Các vấn đề liên quan