programing tip

Matplotlib를 사용하여 비 차단 방식으로 플로팅

itbloger 2020. 8. 18. 07:26
반응형

Matplotlib를 사용하여 비 차단 방식으로 플로팅


나는 지난 며칠 동안 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()

추신. 새 창을 만드는 대신 무언가를 플롯 할 때마다 기존 창을 업데이트하고 싶다고 말하는 것을 잊었습니다.


나는 해결책을 찾기 위해 오랜 시간을 보냈고이 대답을 찾았습니다 .

당신과 내가 원하는 것을 얻으려면 plt.ion(), plt.show()(사용되지 않음 blocking=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()


나를 위해 작동하는 간단한 트릭은 다음과 같습니다.

  1. show : plt.show (block = False)에서 block = False 인수를 사용하십시오.
  2. .py 스크립트 끝에 다른 plt.show () 사용하십시오 .

:

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()내 스크립트의 마지막 줄입니다.


플롯을 배열에 쓴 다음 다른 스레드에 배열을 표시하여 실행 차단을 방지 할 수 있습니다. 다음은 pyformulas 0.2.8의 pf.screen을 사용하여 동시에 플롯을 생성하고 표시하는 예입니다 .

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

결과:

사인 애니메이션

면책 조항 : 저는 pyformulas의 관리자입니다.

참조 : Matplotlib : 플롯을 numpy 배열에 저장


이 답변 중 많은 부분이 과장되어 있으며 제가 찾을 수있는 바에 따르면 그 답은 이해하기 어렵지 않습니다.

You can use plt.ion() if you want, but I found using plt.draw() just as effective

For my specific project I'm plotting images, but you can use plot() or scatter() or whatever instead of figimage(), it doesn't matter.

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

Or

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

If you're using an actual figure.
I used @krs013, and @Default Picture's answers to figure this out
Hopefully this saves someone from having launch every single figure on a separate thread, or from having to read these novels just to figure this out


Live Plotting

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

if the amount of data is too much you can lower the update rate with a simple counter

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

Holding Plot after Program Exit

This was my actual problem that couldn't find satisfactory answer for, I wanted plotting that didn't close after the script was finished (like MATLAB),

If you think about it, after the script is finished, the program is terminated and there is no logical way to hold the plot this way, so there are two options

  1. block the script from exiting (that's plt.show() and not what I want)
  2. run the plot on a separate thread (too complicated)

this wasn't satisfactory for me so I found another solution outside of the box

SaveToFile and View in external viewer

For this the saving and viewing should be both fast and the viewer shouldn't lock the file and should update the content automatically

Selecting Format for Saving

vector based formats are both small and fast

  • SVG is good but coudn't find good viewer for it except the web browser which by default needs manual refresh
  • PDF can support vector formats and there are lightweight viewers which support live updating

Fast Lightweight Viewer with Live Update

For PDF there are several good options

  • On Windows I use SumatraPDF which is free, fast and light (only uses 1.8MB RAM for my case)

  • On Linux there are several options such as Evince (GNOME) and Ocular (KDE)

Sample Code & Results

Sample code for outputing plot to a file

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

after first run, open the output file in one of the viewers mentioned above and enjoy.

Here is a screenshot of VSCode alongside SumatraPDF, also the process is fast enough to get semi-live update rate (I can get near 10Hz on my setup just use time.sleep() between intervals) pyPlot, 비 차단


Iggy's answer was the easiest for me to follow, but I got the following error when doing a subsequent subplot command that was not there when I was just doing show:

MatplotlibDeprecationWarning: Adding an axes using the same arguments as a previous axes currently reuses the earlier instance. In a future version, a new instance will always be created and returned. Meanwhile, this warning can be suppressed, and the future behavior ensured, by passing a unique label to each axes instance.

In order to avoid this error, it helps to close (or clear) the plot after the user hits enter.

Here's the code that worked for me:

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

drawnow Python 패키지를 사용하면 차단되지 않는 방식으로 실시간으로 플롯을 업데이트 할 수 있습니다.
또한 웹캠 및 OpenCV와 함께 작동하여 각 프레임에 대한 측정 값을 표시합니다. 원본 게시물을
참조하십시오 .

참고 URL : https://stackoverflow.com/questions/28269157/plotting-in-a-non-blocking-way-with-matplotlib

반응형