LoginSignup
5
7

More than 1 year has passed since last update.

SIRモデル計算プログラムをGUI化する

Last updated at Posted at 2020-02-22

#はじめに
前回の記事に、感染症の拡散を説明するSIRモデルについて説明しました。その時に、SIRモデルを解く数値積分のPythonプログラムも掲載しました。今回の記事には、そのPythonプログラムをGUI化してみます。

前回の記事:感染病の数学予測モデル (SIRモデル):事例紹介(1) https://qiita.com/kotai2003/items/d74583b588841e6427a2

#GUIの説明
自分が作ったプログラムをGUI化する理由は、「他人に使ってもらいたいから」です。将来的には、GUIのPyコードだけではなくて、setup.exeの形で配布したいと思います。

まず、今回に作成したGUIの画面構成です。SIRモデルの感染率(Beta)と除去率(Gamma)を右側の空欄に入力し、その下のDrawボタンを押すと、SIRモデルの計算結果が中央の画面にプロットされます。


\begin{align}

\frac{dS}{dt} &= -\beta SI \\
\frac{dI}{dt} &=  \beta SI -\gamma I \\
\frac{dR}{dt} &=  \gamma I \\

\end{align} 
\begin{align}
S &: 感染可能者 \quad \text{(Susceptible)} \\
I &: 感染者 \quad \text{(Infectious)} \\
R &: 感染後死亡者、もしくは免疫を獲得した者 \quad \text{(Removed)} \\
\beta &: 感染率\quad \text{(The infectious rate)} \quad [1/day] \\
\gamma &:除去率\quad \text{(The Recovery rate)} \quad [1/day] \\
\end{align}

初期の画面に、Typical Condition~と書いてありますが、これは感染率(Beta)と除去率(Gamma)を入力する際に参考にするためです。

スクリーンショット 2020-02-22 20.38.20.png

次の画面が計算結果を示します。SIRモデルの数値積分結果がLine Plotで表示されます。新しく入力した感染率(Beta)と除去率(Gamma)の値が、タイトルに更新されます。

スクリーンショット 2020-02-22 20.38.41.png

GUIの終了は、右下のQuitボタンを押すことで実行されます。

スクリーンショット 2020-02-22 20.40.14.png

#プログラムの説明
GUIのライブラリは、Tkinterを利用しました。
簡単にプログラムの動作を説明します。

(1) TkinterでCanvas Widgetを用意する。 TkinterのEntry Widgetで、感染率(Beta)と除去率(Gamma)を入力するテキストボックスを用意する。
(2) scipyでSIRモデルを計算し、その結果をmatplotlibでプロットする。
(3) matplotlibのFigureを Canvas WidgetでDrawする。

必要なライブラリをインポートします。

import tkinter
import tkinter.messagebox as tkmsg
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk

import numpy as np
from functools import partial
from scipy.integrate import odeint

SIRモデルの数値積分に必要な関数を用意します。


# SIR Differential Equation
def SIR_EQ(v, t, beta, gamma):
    '''
    dS/dt = -beta * S * I
    dI/dt = beta * S * I - gamma * I
    dR/dt = gamma * I

    [v[0], v[1], v[2]]=[S, I, R]

    dv[0]/dt = -beta * v[0] * v[1]
    dv[1]/dt = beta * v[0] * v[1] - gamma * v[1]
    dv[2]/dt = gamma * v[1]

    '''
    return [-beta*v[0]*v[1], beta * v[0] * v[1] - gamma * v[1], gamma * v[1]]

# Solving SIR Equation
def Calc_SIR(var_b, var_r):
    # parameters
    t_max = 14
    dt = 0.01
    #beta_const = 0.0026
    #gamma_const = 0.5

    beta_const = var_b
    gamma_const = var_r

    # initial_state
    S_0 = 762
    I_0 = 1
    R_0 = 0
    ini_state = [S_0, I_0, R_0]  # [S[0], I[0], R[0]]

    # numerical integration
    times = np.arange(1, t_max, dt)
    args = (beta_const, gamma_const)
    # Solver SIR model
    result = odeint(SIR_EQ, ini_state, times, args)
    return times,result

TkinterのボタンのEventの時に実行される関数を用意します。
DrawCanvas関数に、matplotlibのaxesインスタンス(=ax)を用意し、そのインスタンスをプロットする仕組みです。最後のcanvas.draw()で、matplotlibのaxesインスタンスが、canvas widgetに表示されることになります。

def Quit():
    tkmsg.showinfo("Tomomi Research Inc.","Thank you for running this program!")
    root.quit()
    root.destroy()

#Draw Button
def DrawCanvas(canvas, ax):
    value_beta = EditBox_beta.get()
    value_gamma = EditBox_gamma.get()
    if value_beta != '':
        EditBox_beta.delete(0, tkinter.END)
        EditBox_gamma.delete(0, tkinter.END)
        ax.cla()
        beta = float(value_beta)
        gamma = float(value_gamma)
        t_r, res_r = Calc_SIR(beta,gamma)

        ax.plot(t_r, res_r)
        ax.legend(['Susceptible', 'Infectious', 'Removed'])
        ax.set_title('Beta='+str(beta)+', Gamma='+str(gamma) )
        ax.set_xlabel('time(days)')
        ax.set_ylabel('population')
    canvas.draw()

この部分はメインのプログラムです。
Tkinterの各Widgetの設定は、この部分に書いてあります。

if __name__ == '__main__':
    try:
        # GUI generate
        root = tkinter.Tk()
        root.title("SIR model")

        # Graph setting
        fig, ax1 = plt.subplots()
        #fig.gca().set_aspect('equal', adjustable='box')  # グラフ領域の調整 #get current axes

        ax1.set_title('Typical Condition:  beta=0.0026, gamma=0.5, $S_0$=762, $I_0$=1')


        # Generate Canvas
        Canvas = FigureCanvasTkAgg(fig, master=root)
        Canvas.get_tk_widget().grid(row=0, column=0, rowspan=10)

        #Beta
        EditBox_beta = tkinter.Entry(width=5)  # テキストボックスの生成
        EditBox_beta.grid(row=1, column=2)

        GridLabel_beta = tkinter.Label(text="Beta")
        GridLabel_beta.grid(row=1, column=1)

        # Gamma
        EditBox_gamma = tkinter.Entry(width=5)  # テキストボックスの生成
        EditBox_gamma.grid(row=4, column=2)

        GridLabel_gamma = tkinter.Label(text="Gamma")
        GridLabel_gamma.grid(row=4, column=1)

        # ボタンに関する諸々の設定
        ReDrawButton = tkinter.Button(text="Draw", width=15, command=partial(DrawCanvas, Canvas, ax1))  # ボタンの生成
        ReDrawButton.grid(row=5, column=1, columnspan=2)  # 描画位置(テキトー)

        QuitButton = tkinter.Button(text="Quit", width=15, command=Quit)  # ボタンの生成
        QuitButton.grid(row=7, column=1, columnspan=2)  # 描画位置(テキトー)

        DrawCanvas(Canvas, ax1)
        root.mainloop()
    except:
        import traceback

        traceback.print_exc()
    finally:
        input(">>")  # エラー吐き出したときの表示待ち

#まとめ

Python GUIのライブラリには、Tkinterと PyQTが有名だそうですが、Tkiknterが初心者にもプログラムしやすいと聞いたため、Tkinterを選んでみました。未だにもPyQTに興味がありますので、両方使ったことがある方は、コメントを残していただければと思います。

PythonのGUIのメリットは、Cross Platformであることだと思います。プログラムを一つ作成すれば、変更なしにWindows, Mac OS, Linuxに使えることが可能です。現在、WindowsでDeep Learningのプログラムを開発し、Nvidia製のJetson系(Linux)に実装することを考えています。その時、Python GUIが大変役に立つと思います。

#参考資料

  1. MatplotlibにGUI(Tkinter)を組み合わせる話 https://www.shtsno24.tokyo/2018/12/matplotlibguitkinter.html
  2. Matplotlib&Seaborn実装ハンドブック https://amzn.to/2ujQ2CL
  3. 感染病の数学予測モデルの紹介 (SIRモデル)https://qiita.com/kotai2003/items/3078f4095c3e94e5325c
  4. 感染病の数学予測モデル (SIRモデル):事例紹介(1) https://qiita.com/kotai2003/items/d74583b588841e6427a2
  5. Embedding in Tk https://matplotlib.org/gallery/user_interfaces/embedding_in_tk_canvas_sgskip.html
5
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7