Складання неблокуючим способом за допомогою Matplotlib


138

Я грав у Numpy та matplotlib останні кілька днів. У мене виникають проблеми при спробі зробити графік matplotlib функцією без блокування виконання. Я знаю, що тут уже багато тем на ТАК, які задають подібні запитання, і я дуже гугл, але мені не вдалося зробити цю роботу.

Я спробував використовувати show (block = False), як деякі люди пропонують, але все, що я отримую, це заморожене вікно. Якщо я просто дзвоню show (), результат будується належним чином, але виконання блокується до закриття вікна. З інших тем, які я читав, я підозрюю, що працює чи ні (show = block = False) залежить від бекенда. Це правильно? Мій задній кінець - Qt4Agg. Ви можете подивитися на мій код і сказати мені, якщо ви бачите щось не так? Ось мій код. Дякуємо за будь-яку допомогу.

from math import *
from matplotlib import pyplot as plt
print plt.get_backend()



def main():
    x = range(-50, 51, 1)
    for pow in range(1,5):   # plot x^1, x^2, ..., x^4

        y = [Xi**pow for Xi in x]
        print y

        plt.plot(x, y)
        plt.draw()
        #plt.show()             #this plots correctly, but blocks execution.
        plt.show(block=False)   #this creates an empty frozen window.
        _ = raw_input("Press [enter] to continue.")


if __name__ == '__main__':
    main()

PS. Я забув сказати, що хотів би оновлювати наявне вікно кожного разу, коли щось будувати, а не створювати нове.


1
ви спробували інтерактивний режим matplotlib з plt.ion()раніше plt.show()? Тоді це не може блокувати, оскільки кожен сюжет вкладається в дочірню нитку.
Анзель

@Anzel я просто спробував це, але, здається, це не має ніякого значення.
opetroch

3
Як ти працюєш зі своїм сценарієм? Якщо я запускаю ваш приклад код із терміналу / командного рядка, він, здається, спрацює нормально, але, думаю, у мене були проблеми в минулому, коли намагалися робити такі дії з IPython QtConsole або IDE.
Маріус

1
@Marius Aha !! Ти правий. Дійсно, я запускаю його з консолі свого IDE (PyCharm). Запускаючи його з підказки cmd, plt.show (блок = хибне), працює чудово! Я буду запитувати занадто багато, якщо я запитаю вас, чи знайшли ви якусь ідею / рішення для цього? Дуже дякую!
opetroch

Я не дуже знаю пробачення. Я не дуже розумію подробиці того, як matplotlib взаємодіє з консоллю, тому я, як правило, просто переходжу до запуску з командного рядка, якщо мені потрібно це робити matplotlib.
Маріус

Відповіді:


167

Я довго шукав рішення, і знайшов цю відповідь .

Схоже, для того, щоб отримати те, що ви (і я) хочете, вам потрібна комбінація plt.ion(), plt.show()(не з block=False) і, головне, plt.pause(.001)(або в будь-який час ви хочете). Пауза необхідна , тому що GUI події відбуваються в той час як основний код спить, в тому числі малюнка. Цілком можливо, що це реалізується шляхом вибору часу зі сплячої нитки, тому, можливо, IDE з цим возиться - я не знаю.

Ось реалізація, яка працює для мене на python 3.5:

import numpy as np
from matplotlib import pyplot as plt

def main():
    plt.axis([-50,50,0,10000])
    plt.ion()
    plt.show()

    x = np.arange(-50, 51)
    for pow in range(1,5):   # plot x^1, x^2, ..., x^4
        y = [Xi**pow for Xi in x]
        plt.plot(x, y)
        plt.draw()
        plt.pause(0.001)
        input("Press [enter] to continue.")

if __name__ == '__main__':
    main()

3
Ваша відповідь мені дуже допомогла у вирішенні подібного питання, яке у мене було. Раніше я plt.drawслідував за цим, plt.show(block = False)але потім він перестав працювати: Фігура не реагувала, закривши її, впав iPython. Моє рішення було видалити кожен екземпляр plt.draw()і замінити його plt.pause(0.001). Замість того, щоб слідувати за тим, plt.show(block = False)як plt.drawбуло раніше, йому передували plt.ion()і plt.show(). Зараз у мене є, MatplotlibDeprecationWarningале це дозволило побудувати свої цифри, тому я задоволений цим рішенням.
blue_chip

3
Зауважте, що в python 2.7 потрібно використовувати raw_inputне input. Дивіться тут
Кріс

Дійсно корисне рішення, коли реактивний "анімаційний" підхід неможливий! Хтось знає, як позбутися попередження про депресію?
Фредерік Фортьє

Скажіть, будь ласка, хтось мені, чому я отримую командний рядок freezen, коли я намагаюся додати plt.ion перед plt.show?
Габріель Аугусто

@GabrielAugusto Я не впевнений, що це може спричинити, і я не впевнений, що ти маєш на увазі. Я тільки тестував цей приклад в Python 3.6, і він все ще працює. Якщо ви використовували ту саму схему, і вона замерзає, можливо, у вашій установці щось не так. Спершу слід перевірити, чи нормальне планування спрацьовує спочатку. Якщо ви спробували щось інше, з цим у коментарях не багато чого. У будь-якому випадку ви можете розглянути окреме запитання.
krs013

23

Простий фокус, який працює для мене, полягає в наступному:

  1. Використовуйте блок = хибний аргумент всередині show: plt.show (block = False)
  2. Використовуйте інший plt.show () в кінці сценарію .py.

Приклад :

import matplotlib.pyplot as plt

plt.imshow(add_something)
plt.xlabel("x")
plt.ylabel("y")

plt.show(block=False)

#more code here (e.g. do calculations and use print to see them on the screen

plt.show()

Примітка : plt.show()останній рядок мого сценарію.


7
Це створює (для мене в Linux, Anaconda, Python 2.7, за замовчуванням) порожнє вікно, яке залишається порожнім до самого кінця виконання, коли воно остаточно заповнюється. Не корисно для оновлення сюжету в розпал виконання. :-(
sh37211

@ sh37211 Не впевнений, яка ваша мета. У деяких випадках, якщо ви намагаєтеся побудувати щось, але після команди plot, у вас є інші команди, то це корисно, оскільки дозволяє побудувати та виконати інші команди. Дивіться цю публікацію, щоб дізнатися більше про це: stackoverflow.com/questions/458209/… . Якщо ви хочете оновити сюжет, то це має бути іншим способом.
seralouk

17

Ви можете уникнути блокування виконання, записавши графік в масив, а потім відобразивши масив в іншому потоці. Ось приклад генерування та відображення сюжетів одночасно за допомогою pf.screen з піформформул 0.2.8 :

import pyformulas as pf
import matplotlib.pyplot as plt
import numpy as np
import time

fig = plt.figure()

canvas = np.zeros((480,640))
screen = pf.screen(canvas, 'Sinusoid')

start = time.time()
while True:
    now = time.time() - start

    x = np.linspace(now-2, now, 100)
    y = np.sin(2*np.pi*x) + np.sin(3*np.pi*x)
    plt.xlim(now-2,now+1)
    plt.ylim(-3,3)
    plt.plot(x, y, c='black')

    # If we haven't already shown or saved the plot, then we need to draw the figure first...
    fig.canvas.draw()

    image = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')
    image = image.reshape(fig.canvas.get_width_height()[::-1] + (3,))

    screen.update(image)

#screen.close()

Результат:

Синус анімації

Відмова: Я підтримую піформули.

Довідка: Matplotlib: збереження ділянки до numpy масиву


9

Багато цих відповідей надзвичайно завищені, і з того, що я можу знайти, відповідь не все так складно зрозуміти.

Ви можете використовувати, plt.ion()якщо хочете, але я знайшов використання plt.draw()настільки ж ефективний

Для мого конкретного проекту , я креслення зображення, але ви можете використовувати plot()або scatter()чи що - то замість того figimage(), це не має значення.

plt.figimage(image_to_show)
plt.draw()
plt.pause(0.001)

Або

fig = plt.figure()
...
fig.figimage(image_to_show)
fig.canvas.draw()
plt.pause(0.001)

Якщо ви використовуєте фактичну фігуру.
Я використовував @ krs013 і відповіді @Default Picture, щоб зрозуміти це.
Сподіваємось, це позбавить когось від запуску кожної окремої фігури в окрему нитку або від необхідності читати ці романи, просто щоб зрозуміти це


3

Прямий графік

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
# plt.axis([x[0], x[-1], -1, 1])      # disable autoscaling
for point in x:
    plt.plot(point, np.sin(2 * point), '.', color='b')
    plt.draw()
    plt.pause(0.01)
# plt.clf()                           # clear the current figure

якщо кількість даних завелика, ви можете знизити швидкість оновлення за допомогою простого лічильника

cnt += 1
if (cnt == 10):       # update plot each 10 points
    plt.draw()
    plt.pause(0.01)
    cnt = 0

Проведення сюжету після виходу з програми

Це була моя реальна проблема, на яку не вдалося знайти задовільної відповіді. Я хотів, щоб сюжет не закрився після закінчення сценарію (як MATLAB),

Якщо ви подумаєте про це, після закінчення сценарію програма припиняється, і немає логічного способу провести сюжет таким чином, тому є два варіанти

  1. заблокувати вихід сценарію (це plt.show (), а не те, що я хочу)
  2. запустити сюжет на окрему нитку (занадто складна)

це було не задовільно для мене, тому я знайшов інше рішення поза коробкою

SaveToFile та Переглянути у зовнішньому переглядачі

Для цього збереження та перегляд має бути швидким, і глядач не повинен блокувати файл, а оновити вміст автоматично

Вибір формату для збереження

векторні формати - це і малі, і швидкі

  • SVG хороший, але не може знайти для нього хорошого глядача, окрім веб-браузера, який за замовчуванням потребує оновлення вручну
  • PDF може підтримувати векторні формати, і є легкі глядачі, які підтримують оновлення в реальному часі

Швидкий легкий переглядач з оновленням в реальному часі

Для PDF існує кілька хороших варіантів

  • У Windows я використовую SumatraPDF, який є вільним, швидким і легким (для мого випадку використовується лише 1,8 Мб оперативної пам’яті)

  • У Linux існує кілька варіантів, таких як Evince (GNOME) та Ocular (KDE)

Приклад коду та результати

Зразок коду для виведення графіку у файл

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(2 * x)
plt.plot(x, y)
plt.savefig("fig.pdf")

після першого запуску відкрийте вихідний файл в одному з перерахованих вище глядачів і насолоджуйтесь.

Ось скріншот VSCode поряд з SumatraPDF, також процес досить швидкий, щоб отримати напівживучу швидкість оновлення (я можу отримати близько 10 Гц в моїй установці просто використовувати time.sleep()між інтервалами) pyPlot, Неблокуючий


2

Відповідь Іггі була для мене найпростішою, але я отримав таку помилку, коли робив наступну subplotкоманду, якої не було, коли я тільки робив show:

MatplotlibDeprecationWarning: Додавання осей за допомогою тих же аргументів, що і попередні осі, в даний час повторно використовує попередній екземпляр. У майбутній версії новий екземпляр завжди буде створюватися та повертатися. Тим часом це попередження можна придушити, а подальше поводження забезпечити, передавши унікальний ярлик кожному екземпляру осей.

Щоб уникнути цієї помилки, вона допомагає закрити (або очистити ) сюжет після того, як користувач натисне введення.

Ось код, який працював для мене:

def plt_show():
    '''Text-blocking version of plt.show()
    Use this instead of plt.show()'''
    plt.draw()
    plt.pause(0.001)
    input("Press enter to continue...")
    plt.close()

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.