cảnh báo về quá nhiều số liệu mở


166

Trong một kịch bản nơi tôi tạo nhiều hình fix, ax = plt.subplots(...), tôi nhận được cảnh báo RuntimeWarning: Hơn 20 hình đã được mở. Các hình được tạo thông qua giao diện pyplot ( matplotlib.pyplot.figure) được giữ lại cho đến khi đóng rõ ràng và có thể tiêu thụ quá nhiều bộ nhớ.

Tuy nhiên, tôi không hiểu tại sao tôi nhận được cảnh báo này, vì sau khi lưu hình với fig.savefig(...), tôi xóa nó đi fig.clear(); del fig. Không có điểm nào trong mã của tôi, tôi có nhiều hơn một con số mở tại một thời điểm. Tuy nhiên, tôi nhận được cảnh báo về quá nhiều số liệu mở. Điều đó có nghĩa là gì / làm thế nào tôi có thể tránh nhận được cảnh báo?


9
Nếu bạn đang làm nhiều việc này và không hiển thị bất cứ điều gì tương tác, bạn có thể tốt hơn hết là bỏ qua plthoàn toàn. Ví dụ stackoverflow.com/a/16337909/325565 (Không để cắm một trong những câu trả lời của riêng tôi, nhưng đó là một trong tôi có thể tìm nhanh ...)
Joe Kington

1
@JoeKington cảm ơn bạn đây là một giải pháp tốt hơn
hihell

Câu trả lời của Joe Kington nên có trong danh sách câu trả lời chính. Nó hoạt động và cũng giải quyết vấn đề với plt.close () làm chậm chương trình mà Don Kirby đã đề cập.
NatalieL

Câu trả lời:


198

Sử dụng .clfhoặc .clatrên đối tượng hình của bạn thay vì tạo một hình mới . Từ @DavidZwicker

Giả sử bạn đã nhập pyplotnhư

import matplotlib.pyplot as plt

plt.cla()xóa một trục , tức là trục hiện đang hoạt động trong hình hiện tại. Nó để lại các trục khác không bị ảnh hưởng.

plt.clf()xóa toàn bộ hình hiện tại bằng tất cả các trục của nó, nhưng để cửa sổ mở, để nó có thể được sử dụng lại cho các ô khác.

plt.close()đóng một cửa sổ , sẽ là cửa sổ hiện tại, nếu không được chỉ định khác. plt.close('all')sẽ đóng tất cả các số liệu mở.

Lý do del figkhông hoạt động là máy pyplottrạng thái giữ một tham chiếu đến hình xung quanh (vì nó phải nếu nó sẽ biết "con số hiện tại" là gì). Điều này có nghĩa là ngay cả khi bạn xóa ref của mình vào hình, vẫn có ít nhất một ref trực tiếp, do đó nó sẽ không bao giờ được thu gom rác.

Vì tôi đang thăm dò ý kiến ​​tập thể ở đây cho câu trả lời này, @JoeKington đề cập trong các bình luận plt.close(fig)sẽ loại bỏ một trường hợp con số cụ thể khỏi máy trạng thái pylab ( plt._pylab_helpers.Gcf ) và cho phép nó được thu gom rác.


1
Ừm. Có clfcho figurelớp, nhưng không close. Tại sao không del figthực sự đóng và xóa hình?
andreas-h

2
@ andreas-h Tôi đoán: đối với một thứ phức tạp như trình quản lý cửa sổ với trình xử lý riêng của nó, có thể cần dọn dẹp nhiều hơn là đặt một cái gì đó ngoài phạm vi. Quyền của bạn closesẽ không hoạt động trên đối tượng hình, hãy gọi nó như thế plt.close(), thay vì fig.clf().
Móc vào

5
@ andreas-h - Về cơ bản, lý do del figkhông hoạt động là việc cung cấp cho nó một __del__phương thức (về cơ bản sẽ gọi plt.close(fig)) sẽ gây ra các tham chiếu vòng tròn trong trường hợp cụ thể này và figviệc có một __del__phương thức sẽ khiến những thứ khác không được thu gom rác . (Hoặc đó là hồi ức mơ hồ của tôi, dù sao đi nữa.) Dù sao đi nữa, chắc chắn sẽ có một chút khó chịu, nhưng bạn nên gọi plt.close(fig)thay vì del fig. Bên cạnh đó, matplotlib thực sự có thể sử dụng trình quản lý bối cảnh cho việc này ...
Joe Kington

6
@ Đã xem - Để làm cho nó rõ ràng hơn một chút, bạn có thể chỉnh sửa câu hỏi của mình để đề cập đến việc plt.close(fig)sẽ xóa một thể hiện cụ thể khỏi máy trạng thái pylab ( plt._pylab_helpers.Gcf) và cho phép nó được thu gom rác.
Joe Kington

2
@JoeKington pltlà một mớ hỗn độn và có những suy nghĩ về cách làm lại một loạt nó. Trình quản lý bối cảnh rất hấp dẫn .... Xem github.com/matplotlib/matplotlib/pull/2736 , github.com/matplotlib/matplotlib/pull/2624
tacaswell 19/214

32

Dưới đây là một chút chi tiết để mở rộng câu trả lời của Hooked . Khi tôi lần đầu tiên đọc câu trả lời đó, tôi đã bỏ lỡ hướng dẫn để gọi clf() thay vì tạo một hình mới . clf()tự nó không giúp đỡ nếu bạn sau đó đi và tạo một hình khác.

Đây là một ví dụ tầm thường gây ra cảnh báo:

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

Để tránh cảnh báo, tôi phải kéo cuộc gọi ra subplots()bên ngoài vòng lặp. Để tiếp tục nhìn thấy các hình chữ nhật, tôi cần chuyển clf()sang cla(). Điều đó xóa trục mà không loại bỏ trục chính nó.

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

Nếu bạn đang tạo các lô theo lô, bạn có thể phải sử dụng cả hai cla()close(). Tôi gặp phải một vấn đề trong đó một lô có thể có hơn 20 lô mà không phàn nàn, nhưng nó sẽ khiếu nại sau 20 lô. Tôi đã sửa nó bằng cách sử dụng cla()sau mỗi lô và close()sau mỗi đợt.

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

Tôi đã đo hiệu suất để xem liệu có đáng sử dụng lại con số trong một đợt hay không và chương trình mẫu nhỏ này đã chậm lại từ 41 đến 49 giây (chậm hơn 20%) khi tôi chỉ gọi close()sau mỗi âm mưu.


Đây là một câu trả lời tuyệt vời. Câu trả lời được chấp nhận không thực sự giải quyết vấn đề thực tế, đó là tiêu thụ bộ nhớ.
Kyle

24

Nếu bạn có ý định giữ nhiều lô trong bộ nhớ, nhưng không muốn được cảnh báo về nó, bạn có thể cập nhật các tùy chọn của mình trước khi tạo số liệu.

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

Điều này sẽ ngăn cảnh báo được phát ra mà không thay đổi bất cứ điều gì về cách quản lý bộ nhớ.


trong môi trường Jupyter, việc cấp phát bộ nhớ có tồn tại miễn là ô hiển thị cốt truyện tồn tại không?
matanster

2
@matanster, tôi sẽ đăng nó như câu hỏi của riêng mình. Tôi bắt đầu trả lời, sau đó nhận ra rằng tôi thực sự không biết đủ về cách quản lý hạt nhân của jupyter để trả lời trung thực.
mightypile

@matanster Tất cả các biến và bộ nhớ được phân bổ cho chúng tồn tại cho đến khi kernel bị tắt rõ ràng bởi người dùng. Nó không được liên kết với các tế bào. Trong Jupyter Hub mới hơn, hệ thống có thể tắt hạt nhân (nó có thể được cấu hình).
greatvovan

0

Đoạn mã sau đã giải quyết vấn đề cho tôi:


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

Khi _wrapped_figuređi ra khỏi phạm vi thời gian chạy gọi __del__()phương thức của chúng ta với plt.close()bên trong. Nó xảy ra ngay cả khi ngoại lệ cháy sau khi _wrapped_figurexây dựng.


0

Điều này cũng hữu ích nếu bạn chỉ muốn tạm thời chặn cảnh báo:

    import matplotlib.pyplot as plt
       
    with plt.rc_context(rc={'figure.max_open_warning': 0}):
        lots_of_plots()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.