ノートブック内にGUIを置く: ipywidgets

ノートブック内にGUI部品(ウィジェット)を置く機能がいくつか開発されている。

簡易な利用法のひとつとして interact を紹介し、そのあとEvent Handlerの利用例を示す。

ロジスティックマップの描画

マップの定義と描画部分を関数として作成 マップのパラメータ$a$と繰り返し回数を引数として与える。

In [8]:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

def logisticFunc(xin,a): # definition of the Logistic map
    return a*xin*(1-xin)

def plotLogisticMap(a=3.2, iteration=20):
    '''
    Plotting package of 1-d maps
    args: parameter of map, number of iteration
    '''
    xn = 0.1   # initial value of the variable
    
    fig = plt.figure()
    axes = fig.add_subplot(1,1,1) # Alternatively you can simply use plt.plot()
    # draw y=x and f(x)
    x = np.arange(0,1.0,0.01)
    axes.plot(x, a*x*(1-x), 'r')
    axes.plot(x, x, 'k')
    axes.set_xlabel("$x_n$")
    axes.set_ylabel("$x_{n+1}$")
    axes.set_title("a=" + str(a))
    # show map iterations
    xnext = logisticFunc(xn,a) 
    axes.plot([xn,xn],[0,xnext])
    for i in range(iteration):
        axes.plot([xn,xnext],[xnext,xnext],"b")
        xn = xnext
        xnext = logisticFunc(xn,a)
        axes.plot([xn,xn], [xn,xnext],"b")

関数にパラメータ$a$と繰り返し回数を引数として与えれば描画される

In [9]:
plotLogisticMap(3.8, 30)

interactの利用

  • ipywidgetsのinteract関数をインポート
  • interract関数の第1引数に実行する関数名、2つめ以降に実行する関数の引数の最大値、最小値、規定値を与える。
In [10]:
from ipywidgets import interact, fixed
interact(plotLogisticMap, a=(0.0,4.0,0.02), iteration=(10,100,5))

Lorenz方程式に適用した例

$$ \begin{eqnarray} \frac{dx}{dt} &= \sigma (y-x) \\ \frac{dy}{dt} &= -xz + rx - y\\ \frac{dz}{dt} &=xy -bz \end{eqnarray} $$

ライブラリのインポート

まずは、必要なライブラリをインポート

In [11]:
import numpy as np
from scipy import integrate
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

描画部分を関数に

パラメータを引数にする。

(注)interactiveの利用では、各widgetの状態が変化するごとに、関数が実行される。下のcalc_and_plotでは、引数でrunがTrueになったときのみ実行するように設定した。

In [12]:
def lorentz_deriv(x, t0, sigma=10., beta=8./3., rho=28.0):
    """Compute the time-derivative of a Lorenz system."""
    return (sigma * (x[1] - x[0]), x[0] * (rho - x[2]) - x[1], x[0] * x[1] - beta * x[2])

def calc_and_plot(sigma, beta, rho, max_time=20, run=True) :
    
    if not run:
        return "not run"
    
    x0 = [1., 1., 1.]  # starting vector
    t = np.linspace(0, max_time, max_time*100) # time step is fixed
    x_t = integrate.odeint(lorentz_deriv, x0, t, args=(sigma, beta, rho))
    fig =plt.figure(figsize=(12,5))

    ax1 =fig.add_subplot(1,2,1)   # 1 in (1 row, 2 columns)
    ax1.plot(t,x_t[:,0], label = "$x$")
    ax1.plot(t,x_t[:,1], label = "$y$")
    ax1.plot(t,x_t[:,2], label = "$z$")
    ax1.legend(loc=2)
    
    ax2= fig.add_subplot(1,2,2,projection='3d')  # Axes3D is required
    ax2.plot(x_t[:,0], x_t[:,1], x_t[:,2])
    plt.title('$\sigma =' + str(sigma) + '$, $\\beta =' + str(beta) + '$, $\\rho =' + str(rho) + '$')
    plt.show()
    # return fig

interact, interactiveの利用例

スライダによるパラメータの設定だけだと、そのどれかが変更されるごとに計算するので、コンピュータの能力によっては極端に反応が悪くなる。 そのため、runのチェックボックスにチェックが入っているときのみ計算を実行するようにした。 下の例のようにwidget名を省略するとスライダになる。詳しくは、http://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Basics.html

interactは即時的に実行される。 interactiveは、たとえば w = interactive(引数列)のようにオブジェクトを作って、dipsplay(w)のようにそれを表示する。この程度のものではどちらでもよいと思う。

In [5]:
import numpy as np
from ipywidgets import interact, interactive, Checkbox
from IPython.display import clear_output, display, HTML

interact(calc_and_plot, sigma=(0.0,20.0), beta=(0, 8.0), rho=(0.0, 40.0),max_time=(10,50,1), run=Checkbox(value=False))
#display(w)

interact_manualの利用

A simpler method is to use "interact_manual" as mensioned Sec.1. If using "interact_manual", chackbox (the last argument of calc_and_plot) is not necessary.

In [16]:
from ipywidgets import interact_manual
interact_manual(calc_and_plot, sigma=(0.0,20.0), beta=(0, 8.0), rho=(0.0, 40.0),max_time=(10,50,1))
Out[16]:
<function __main__.calc_and_plot>

ボタンクリックによるスタート(Envent Handler)

interact_manualを使えば、特に必要はないかもしえないが、スタートボタンを押したときに計算を始めるようにするための一つの工夫を紹介する。

(下のコードのなかのスライダなどのwidgetの引数指定は、役立つノウハウではあると思う。interact_manualを使った上の例に組み込んでみてもらいたい。)

ボタンにイベントハンドラ(on_click)をつけて、クリック時に計算を開始するようにしてみる。 計算に時間がかかるときはこの方法が便利 (Javaなどのウィジェットにリスナーを定義するような感じ。)

(以前に描いたプロットが消去できない。計算、描画部分をクラスとして記述しておいて、描画部分のインスタンスを保持しながら実行すればよいかもしれないが試していない。)

In [13]:
import ipywidgets as widgets
from IPython.display import display
from ipykernel.pylab.backend_inline import flush_figures

present_fig = None

set_sigma = widgets.FloatSlider(value= 10, min=0, max=20.0, step=0.1,
                                 description="sigma", readout=True,
                                 readout_format='i', layout=widgets.Layout(width='50%'))
set_beta = widgets.FloatSlider(value= 3.0, min=0, max=8.0, step=0.1,
                                 description='beta', readout=True,
                                 readout_format='i', layout=widgets.Layout(width='50%'))
set_rho = widgets.FloatSlider(value= 10.0, min=0, max=40.0, step=0.1,
                                 description='rho', readout=True,
                                 readout_format='i', layout=widgets.Layout(width='50%'))
set_max_time = widgets.FloatSlider(value= 20.0, min=10, max=50.0, step=1.0,
                                 description='max_time', readout=True,
                                 readout_format='i', layout=widgets.Layout(width='50%'))
startBtn =widgets.Button(description='Run', disabled=False,icon='check')

def run_by_button(dummy):
    # global present_fig
    #if present_fig is not None: # This doesn't work (?)
    #    present_fig.clf()
    flush_figures() # doesn't work
    calc_and_plot(set_sigma.value, set_beta.value, set_rho.value, set_max_time.value)

startBtn.on_click(run_by_button)

display(set_sigma)
display(set_beta)
display(set_rho)
display(set_max_time)
display(startBtn)

デコレータとして用いる方法

次のように、関数を呼ぶときの引数をスライダの現在値を渡すデコレータをつける方法もある。 (@で始まるのは、デコレータと呼ばれ、その下で定義される関数の上に被せるものという意味。)

引用元: http://qiita.com/tanemaki/items/e965cd9c25acbeed860c

In [8]:
%matplotlib inline
from __future__ import print_function
from IPython.html.widgets import interact, interactive, fixed
from IPython.html import widgets
import numpy as np
import matplotlib.pyplot as plt

@interact(amp=(0.1, 4.0, 0.1), omega=(0.1, 4.0, 0.1), phase=(-np.pi, np.pi, 0.1), 
          fn = {'sin': np.sin, 'cos': np.cos, 'tan': np.tan})
def h(amp=1.0, omega=1.0, phase=0.0, fn=np.sin):
    domain=[-np.pi, np.pi]
    x = np.linspace(domain[0], domain[1], 100)
    y  = amp * fn(omega * x + phase)
    plt.plot(x, y)
    plt.plot(-phase/omega, 0, 'or')
    plt.xlim(domain)
    plt.ylim([-4, 4])

h()
plt.show()

一般的なGUI

本格的なGUIを作成するには、ノートブックとは独立に独自のウィンドウ上に作成するライブラリを利用する.

次は、複数の非線形振動子が結合した系の振る舞いを調べる。 該当ページへ

In [ ]: