- Xonshのグラフインライン描画 -
Xonshでグラフは端末状にインライン描画する方法は、以下の2種類が容易されています。
xontrib.mplhooks.display_figure_with_iterm2(fig)
xontrib.mplhooks.show()
以下がソースコード です。
xontrib.mplhooks — xonsh 0.5.12.dev97 documentation
xonsh/mplhooks.py at master · xonsh/xonsh · GitHub
Python なのですぐ読めます。
display_figure_with_iterm2は、 iterm2_toolsを使ってiTerm2上にグラフを描画するようになっています。
showは、iterm2_toolsがimportできればdisplay_figure_with_iterm2で描画、iterm2_toolsが存在しない場合はplt.gcf()で現状プロット図を取得してきてRGB値をcolor stringにして描画する形になっています。
iTerm2でインライン描画
私はiTerm2利用者ですので以下でiterm2_toolsを導入しています。
pip install iterm2_tools
GitHub - asmeurer/iterm2-tools: iTerm2 tools for Python
以下エラーが出るのでXonshコンソールを再起動しておきます。
NameError: name ' display_image_bytes ' is not defined
再起動後、Xonshコンソール上で以下を淡々と入力していきます。
もしくは適当にxshスクリプト にしてもOKです。
import matplotlib.pyplot as plt
import xontrib.mplhooks
import numpy as np
x = np.linspace(-3 , 3 , 20 )
y = x ** 2
fig = plt.figure(figsize=(6 , 4 ))
ax1 = fig.add_subplot(1 , 1 , 1 )
ax1.plot(x, y)
xontrib.mplhooks.display_figure_with_iterm2(fig)
xonsh上でもインライン描画できました
Terminal.app上のxonshでインライン描画
試しにMac にデフォルトで付随するTerminal.appでもXonsh上でインライン描画をしてみます。
上記 iTerm2の時と同じGraphを作って以下。
xontrib.mplhooks.show()
ちょっとアレですがまあまあ…
iTerm2の描画は良いですね
- vm _statコマンドでメモリ可視化 -
Mac でメモリ状況を出力するコマンドではfreeやtop、vm _statといった選択肢があります。
今回は適当にvm _statを選択します。
Xonshであれば以下コマンドどちらも結果を得ることができます
vm_stat
$( vm_stat )
vm _statコマンドの結果をparseしてdictにする関数を作って実行してみます。
def vms ():
vm_dic = {}
for i,x in enumerate ($(vm_stat).split(" \n " )):
row = x.split(":" )
if i>1 and len (row)==2 :
vm_dic[row[0 ]] = int (row[1 ].strip().replace("." ,"" ))*4096 /(1024.0 ** 3 )
return vm_dic
以下のようにGB単位でメモリ状況が出力されます。
$ vms()
{ ' "Translation faults" ' : 2588.0195083618164,
' Anonymous pages ' : 7.266414642333984,
' Compressions ' : 54.13356018066406,
' Decompressions ' : 33.81982421875,
' File-backed pages ' : 2.6624984741210938,
' Pageins ' : 32.48748779296875,
' Pageouts ' : 0.26355743408203125,
' Pages active ' : 4.984306335449219,
' Pages copy-on-write ' : 82.98124694824219,
' Pages inactive ' : 4.944099426269531,
' Pages occupied by compressor ' : 3.5574417114257812,
' Pages purgeable ' : 0.14667129516601562,
' Pages purged ' : 1.3369598388671875,
' Pages reactivated ' : 22.909423828125,
' Pages speculative ' : 0.000507354736328125,
' Pages stored in compressor ' : 8.809425354003906,
' Pages throttled ' : 0.0,
' Pages wired down ' : 2.446277618408203,
' Pages zero filled ' : 1392.7644996643066,
' Swapins ' : 16.50379180908203,
' Swapouts ' : 17.286388397216797}
せっかくですのでwatchコマンドのように、メモリ状況をリアルタイムで表示し続けてみます。
import json
while True :
vm_dic = vms()
$[clear]
print (json.dumps(vm_dic, indent=4 ))
watch vm _statでよくない!?みたいな何かが出来上がりました。
vms functionをprintするスクリプト にして、watchもできます。
watchコマンドはデフォルトでsh -cにコマンドを飛ばすだけなので、--exec, -xで指定できるよう環境設定するか、以下のようにxonshを定期的に作るという酷いやつで対応できます。
watch " xonsh sample.xsh "
watch vm _statでよくない!?みたいな何かです。
- vm _statをMatplotlibでリアルタイム描画 -
上記 2項目で作った関数を利用して、Matplotlibでvm _statの結果をリアルタイム描画してみます。
wired, active, inactive, speculative辺りがusedメモリに関連する項目ですのでそちらを可視化してみます。
(occupied by compressorも必要かな?)
スクリプト が適当です。
import time
import xontrib.mplhooks
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('ggplot' )
def init_data (fig, id , title):
x = np.zeros(100 )
y = np.zeros(100 )
subplot = fig.add_subplot(2 ,2 ,id )
subplot.set_title(title)
subplot.set_ylabel('GB' )
subplot.set_xlabel('time' )
li, = subplot.plot(x, y)
return x, y, subplot, li
def set_data (x,y,vm_dic,key):
x = np.append(x, time.time())
x = np.delete(x, 0 )
y = np.append(y, vm_dic[key])
y = np.delete(y, 0 )
return x,y
def update_fig (x, y, subplot, li):
li.set_xdata(x)
li.set_ydata(y)
subplot.set_ylim(min (y), max (y))
subplot.set_xlim(min (x), max (x))
def watch_vms_windows ():
fig = plt.figure()
x,y,subplot,li = init_data(fig, 1 , "Pages active" )
x2,y2,subplot2,li2 = init_data(fig, 2 , "Pages inactive" )
x3,y3,subplot3,li3 = init_data(fig, 3 , "Pages speculative" )
x4,y4,subplot4,li4 = init_data(fig, 4 , "Pages wired down" )
count = 0
while True :
vm_dic = vms()
x,y = set_data(x, y, vm_dic, "Pages active" )
x2,y2 = set_data(x2, y2, vm_dic, "Pages inactive" )
x3,y3 = set_data(x3, y3, vm_dic, "Pages speculative" )
x4,y4 = set_data(x4, y4, vm_dic, "Pages wired down" )
if count > 100 :
update_fig(x, y, subplot, li)
update_fig(x2, y2, subplot2, li2)
update_fig(x3, y3, subplot3, li3)
update_fig(x4, y4, subplot4, li4)
plt.pause(.01 )
else :
count += 1
print (count, end=" \r " )
以下参考にしています。
Arduino で測定したデータを Matplotlib でリアルタイムプロット | org-技術
100ループ回してGraph描画に十分なデータを集めたらGraph描画を開始するスクリプト です。
QuickTime Playerを利用して画面の動画を撮っているのでactiveがガンガン上昇しinactiveが下降、メモリ利用と廃棄が頻繁に行われている事がわかります。
$[clear] して display_figure_with_iterm2(fig) でインライン描画するスクリプト にする事も可能で、試しましたがdisplay_figure_with_iterm2では実行時描画になるため、思ったより綺麗に表示されませんでした。
- MatplotlibのAnimationモジュールを使ってリアルタイム描画を実現する -
Matplotlibにはpltで作られた配列をアニメーションにして表示するモジュールが存在する。
全てのGraphを事前に配列に取っておいて描画するArtistAnimation、動的に生成していくFuncAnimationの2つである。
animation module — Matplotlib 2.1.0 documentation
Animationはgifやmp4を生成するだけなので、これらを前述したitermplotに流してiTerm2にインライン描画する。
itermplotのカッケー背景はAnimationには対応してないので以下だけ設定しておく。
export MPLBACKEND="module://itermplot"
export ITERMPLOT_FRAMES=100 適当に書いたコード
import time
import xontrib.mplhooks
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
plt.style.use('ggplot' )
class VMS :
def __init__ (self, fig):
self.fig = fig
self.x, self.y, self.subplot, self.li = self.init_data(1 , "Pages active" )
self.x2, self.y2, self.subplot2, self.li2 = self.init_data(2 , "Pages inactive" )
self.x3, self.y3, self.subplot3, self.li3 = self.init_data(3 , "Pages speculative" )
self.x4, self.y4, self.subplot4, self.li4 = self.init_data(4 , "Pages wired down" )
self.c = 0
for i in range (100 ):
self.plotsub(i)
def init_data (self, id , title):
x = np.zeros(100 )
y = np.zeros(100 )
subplot = self.fig.add_subplot(2 ,2 ,id )
subplot.set_title(title)
subplot.set_ylabel('GB' )
li, = subplot.plot(x, y)
return x, y, subplot, li
def vms (self):
vm_dic = {}
for i,x in enumerate ($(vm_stat).split(" \n " )):
row = x.split(":" )
if i>1 and len (row)==2 :
vm_dic[row[0 ]] = int (row[1 ].strip().replace("." ,"" ))*4096 /(1024.0 ** 3 )
return vm_dic
def count (self):
self.c += 1
def plotsub (self, frame):
vm_dic = self.vms()
self.count()
self.x = np.append(self.x, self.c)
self.x = np.delete(self.x, 0 )
self.y = np.append(self.y, vm_dic["Pages active" ])
self.y = np.delete(self.y, 0 )
self.li.set_xdata(self.x)
self.li.set_ydata(self.y)
self.subplot.set_ylim(min (self.y), max (self.y))
self.subplot.set_xlim(min (self.x), max (self.x))
self.subplot.set_xticks(np.arange(min (self.x), max (self.x), 20 ))
self.x2 = np.append(self.x2, self.c)
self.x2 = np.delete(self.x2, 0 )
self.y2 = np.append(self.y2, vm_dic["Pages inactive" ])
self.y2 = np.delete(self.y2, 0 )
self.li2.set_xdata(self.x2)
self.li2.set_ydata(self.y2)
self.subplot2.set_ylim(min (self.y2), max (self.y2))
self.subplot2.set_xlim(min (self.x2), max (self.x2))
self.subplot2.set_xticks( np.arange(min (self.x2), max (self.x2), 20 ) )
self.x3 = np.append(self.x3, self.c)
self.x3 = np.delete(self.x3, 0 )
self.y3 = np.append(self.y3, vm_dic["Pages speculative" ])
self.y3 = np.delete(self.y3, 0 )
self.li3.set_xdata(self.x3)
self.li3.set_ydata(self.y3)
self.subplot3.set_ylim(min (self.y3), max (self.y3))
self.subplot3.set_xlim(min (self.x3), max (self.x3))
self.subplot3.set_xticks( np.arange(min (self.x3), max (self.x3), 20 ) )
self.x4 = np.append(self.x4, self.c)
self.x4 = np.delete(self.x4, 0 )
self.y4 = np.append(self.y4, vm_dic["Pages wired down" ])
self.y4 = np.delete(self.y4, 0 )
self.li4.set_xdata(self.x4)
self.li4.set_ydata(self.y4)
self.subplot4.set_ylim(min (self.y4), max (self.y4))
self.subplot4.set_xlim(min (self.x4), max (self.x4))
self.subplot4.set_xticks( np.arange(min (self.x4), max (self.x4), 20 ) )
fig = plt.figure(figsize=(8 ,8 ))
vms = VMS(fig)
animation.FuncAnimation(vms.fig, vms.plotsub, blit=True )
plt.show()
plt.show()がitermplotによって継承されてればインラインで見れる
これは動的に見れている訳ではなく、事前に100フレーム分をgif画像にしてbyteIOでiTerm2に送り込んでいるだけなのでリアルタイムの本質を見失っている状態である。
iTerm2に表示できるGIFのフレーム数を上手くコントロール できないとか課題は多いがまあできたという事で…
- おわりに -
「これ watch vm _stat で良くね!?」
「リアルタイムインライン描画とは」
「Python スクリプト なら os.system や commands.getoutput でコマンド実行できるよね!?」
「これXonshじゃなくても良くない!?」
等の耳が痛い声が聞こえて来ます。
どんどんおかしい方向に進んだ気がしますが、xonshの繁栄のための知見として納めておきます。
おわり。