Pythonでアプリを作る2

機械学習 Python R

Python の標準モジュールtkinter でアプリを作る第二弾です。
作りたい機能は、打ち込まれたデータを読み取り、それを前処理してモデルに渡し、出力を表示するというものでした。
実務では、打ち込むデータが多いので、先にエクセルに入力してもらってエクセルからデータを取得して計算した方が作業が楽な雰囲気が出ています。
今回は、以下の機能を実装したいと思います。

  • エクセルからデータを読み込む
  • 読み込んだデータを計算する
  • 計算結果を画面に表示する

計算部分は簡単でも複雑でも構造は同じなので、今回の記事中では簡単な計算にしておきます。

使用するデータ

Rに標準で入っているDavisデータを使用します。
個のデータは性別、身長、体重、再測定した身長、体重から構成されています。
以下のようにまとめられています。

身長や体重の測定結果には、明らかに入力ミスと思われるデータもありますが、今回は最終行のデータを最新データと見なして使うので、気にしないことにします。
実際に使う時にはそのようなデータには警告を出すようにしたいものです。

アプリの実装

python のtkinter で実装します。前回の記事のものをベースにしていきます。

エクセルからデータを読み込む

エクセルデータを読み込むのはpandas で以下のように書けます。


import pandas as pd

file_name ="Davis.xlsx"
data = pd.ExcelFile(file_name)
sheet_name = data.sheet_names
sheet1 = data.parse(sheet_name=sheet_name[0])
x = sheet1.tail(1)

これでx に最新データ(最終行データ)が格納されます。ただし、Davis データはアプリと同じディレクトリにDavis.xlsx で保存されているとします。
1
エクセルデータは、シートを成分とするベクトルとして読み込まれます。そして、ベクトルの成分がシートに対応し、Dataframe として読み込まれています。
分ける必要も無いのにシート分けされてるエクセルデータも扱うことが出来ます。

読み込んだデータを計算する

実際には事前に作ってあるモデルから予測値を取得しますが、前処理から書くとコードが長いので、慎重と体重からBMIを計算するとします。
計算式はBMI = 体重(kg)/(身長(m) )^2です。

bmi = int(x["weight"]/((x["height"]/100) **2))

計算結果を画面に表示する

計算ボタンを押すと、エクセルデータからBMIを計算し、BMI, 身長, 体重を表示するアプリを作ります。
tkinter のtreeview を使って、ボタンが押された時に上記3つのデータが出てくるようにします。
その名の通り、tree構造を持ったテキストを表示するライブラリですが、家計簿みたいに表示する事も出来るみたいです。以下を参考にしました。

treeview を使う部分は以下のように書けます。


colname = ["BMI", "身長", "体重"]
tree = ttk.Treeview(self)
tree["columns"] = colname
tree["show"] = "headings"
       
for i,_  in enumerate(colname):
     tree.column(i, width=75)
        tree.heading(i,text=colname[i])

tree_data=(bmi, int(x["height"].values), int(x["weight"].values) )
tree.insert("","end",values= tree_data )
tree.grid()

tree[“columns”] で列が何列あるか指定しています。for文でデータを入れる場所の大きさと、列名を指定しています。最後に、tree.insert で、データを挿入しています。変数の詳細については、公式の説明を見てください。

tkinter.ttk --- Tk のテーマ付きウィジェット — Python 3.10.4 ドキュメント

以上の処理をまとめると、以下になります。


class App(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.tree = None
        self.grid()
        self.create_widgets()
        
        
    def create_widgets(self):       
        #計算ボタン
        self.calc = tk.Button(self)
        self.calc["text"] = "計算"
        self.calc["command"] = self.calc_BMI
        #lambda :self.preprocess(self.master.clipboard_get() )
        self.calc.grid(row=1, columns=1, padx=5, pady=5, columnspan = 2)
        """""
        #キャンセルボタン
        self.quit = tk.Button(self, text="キャンセル")
        self.quit["command"] = self.master.destroy                             
        self.quit.grid(row=2, columns=1, padx=5, pady=5)
        """

        
        
    def calc_BMI(self):
        if not self.tree == None:
            self.tree.destroy()
        
        file_name ="Davis.xlsx"
        data = pd.ExcelFile(file_name)
        sheet_name = data.sheet_names
        sheet1 = data.parse(sheet_name=sheet_name[0])
        colname = ["BMI", "身長", "体重"]
        
        x=  sheet1.tail(1)
        bmi = int(x["weight"]/((x["height"]/100) **2))
        self.tree = ttk.Treeview(self)
        self.tree["columns"] = colname
        self.tree["show"] = "headings"
        
        
        for i,_  in enumerate(colname):
            self.tree.column(i, width=75)
            self.tree.heading(i,text=colname[i])
        
        tree_data=(bmi, int(x["height"].values), int(x["weight"].values) )
        self.tree.insert("","end",values= tree_data )
        self.tree.grid()
    

            
root = tk.Tk()
root.geometry("640x480")
app=App(master=root)
app.mainloop()      

計算ボタンを押すと、calc_BMIが実行されます。calc_BMI でtree_viewが生成され、
実行して、計算ボタンを押すと以下のような画面になります。2

計算結果
計算結果

まとめ

  • エクセルからデータを読みこんだ
  • 計算結果を表示するために、ttk.treeview を使った
  1. 欠損値があったりすると困るので、実際にはここで警告を出すようにする必要があるかもしれません。
  2. 計算ボタンを連打した時に計算結果ウィンドウが無限に出てくるのが嫌なので、calc_BMIが呼び出されるたびに計算結果を消しています。
    もう少し賢いやり方があると思うんですが、思いついてないので教えてほしいです。
タイトルとURLをコピーしました