生物屋さんのためのゼロからのプログラミング

―忘れないための覚書 (たま~に更新)―

PythonでExcelのデータを読み込み、名前をつけて保存する。

Pythonの練習第2弾。今回は、かなり昔の記事エクセルからデータを読み込み、Peakを検出しエクセルに出力する - 生物屋さんのためのゼロからのプログラミングPythonを使って書いてみた。ただし、今回はエクセルのデータの読み込みと書き出しのみを行った。

ソースコードは下記。

import tkinter
import tkinter.filedialog
import openpyxl
import numpy as np

#エクセルの読み込みと書き込み、そして保存する関数
def openFile(event):
    fileType = [("excel", "*.xlsx")]
    iDir = "/Users/Desktop/"
    filePath = tkinter.filedialog.askopenfilename(filetypes = fileType, initialdir = iDir)
    wb = openpyxl.load_workbook(filePath)
    sheet = wb.active

    #入れ物の作成
    u = np.zeros((sheet.max_column, sheet.max_row))

    #入れ物に数値を入れる
    for i in range(1,sheet.max_column+1):
        for j in range(1, sheet.max_row+1):
            u[i-1][j-1] = sheet.cell(row=j, column=i).value

    #データを入れるためのシートを作る
    wb.create_sheet(index=2, title="Copy Data")
    sheet2 = wb["Copy Data"]
    # シートにデータを貼り付ける
    for i in range(1, sheet.max_column+1):
        for j in range(1, sheet.max_row+1):
            sheet2.cell(row=j, column=i).value = u[i-1][j-1]

    #新しいExcelファイルを名前をつけて保存する
    closePath = tkinter.filedialog.asksaveasfilename()
    wb.save(closePath+".xlsx")

#GUIの作成
root = tkinter.Tk()
root.title("Excel Test")
root.geometry("100x100")
#イベントを起こすためのボタンの作成
button1 = tkinter.Button(text = "Open", width = 50)
button1.bind("<Button-1>", openFile)
button1.pack()
#実行
root.mainloop()

このプログラムを実行すると、オープンダイアログが立ち上がるので、目的のExcelファイルを選択。今回は下記のような単純な数字が並んだものにした。f:id:Aki-Miya:20180606125334p:plain
そして、すぐに保存用のダイアログが立ち上がるので、適当に名前をつけて保存すると、f:id:Aki-Miya:20180606125441p:plainのように、新しいシートにデータを書き込める。



幾つかのコードを見ていく。
まず、Javaでエクセルを取り扱うには、

poi-3.17.jar
poi.examples-3.17.jar
poi.excelant-3.17.jar
poi-ooxml-3.17.jar
poi-ooxml-schemas-3.17.jar
poi-scratchpad-3.17.jar
xmlbeans-2.6.0.jar

上記のjarファイルをEclipseに入れる必要があった(詳細はEclipseでImageJのPlugin作成 -マウスでクリックした場所にOval ROIを描き、Excelファイルにデータを書き出す。- - 生物屋さんのためのゼロからのプログラミング参照)。


一方、Pythonの場合は、まず、ターミナルで、

sudo pip3 install openpyxl

と入力すると、"openpyxl"がインストールできる。
ただし、この時、インストールされた"openpyxl"は「”ライブラリ”⇨”Frameworks”⇨”Python.framework”⇨”Versions”⇨”3.6”⇨”lib”⇨”Python3.6”⇨”site-packages”」に入っているので(僕のMacでは)、”site-packages”の中から出して、一つ上の”Python3.6”に移す必要がある。

そして、

import openpyxl

を書くだけで、PythonExcelが扱えるようになる。
(ただし、Mac Excel 2011とopenpyxlの相性が悪いのか、Mac Excel 2011で作成したExcelファイルは開けなかったが、Mac Excel 2016で作成したExcelファイルは扱えた。理由は不明)


Excelファイルを開く場合、多くのサイトでは、カレントディレクトリに開きたいExcelファイルを移して、Excelファイル名をコードに手入力する方法を紹介している。
しかし、これでは使い勝手が非常に悪いので、オープンダイアログを使って開きたいExcelファイルを選択できるようにした。ソースコードは下記。

    fileType = [("excel", "*.xlsx")]
    iDir = "/Users/Desktop/"
    filePath = tkinter.filedialog.askopenfilename(filetypes = fileType, initialdir = iDir)
    wb = openpyxl.load_workbook(filePath)

実質一行で完結するので、Javaに比べると非常に楽である。


次に、なんらかの数値処理を行うために、Excelのデータを配列に取り込むためのコードが下記。

    #入れ物の作成
    u = np.zeros((sheet.max_column, sheet.max_row))

    #入れ物に数値を入れる
    for i in range(1,sheet.max_column+1):
        for j in range(1, sheet.max_row+1):
            u[i-1][j-1] = sheet.cell(row=j, column=i).value

配列には"numpy"を使用した。"numpy"のインストールには、"openpyxl"と同様に、ターミナルで

 sudo pip3 install numpy

と入力することでインストールでき、"openpyxl"と同様に”Python3.6”に移せば使えるようになる。
まだnumpyの使い方がよく分からなかったため、下記の部分で”作りたい大きさ”の空の入れ物を作った。

    #入れ物の作成
    u = np.zeros((sheet.max_column, sheet.max_row))

Excelのデータを配列に入れる時の注意点として、数の数え方の違いがある。

    #入れ物に数値を入れる
    for i in range(1,sheet.max_column+1):
        for j in range(1, sheet.max_row+1):
            u[i-1][j-1] = sheet.cell(row=j, column=i).value

上記の部分でExcelのデータを配列に入れているのだが、エクセル上では”1”から数え始めるのに対し、配列は”0”から始まるため微調整が必要となる。


Excelにデータを貼る場合も、Pythonは非常に楽であった。コードは下記。

    # シートにデータを貼り付ける
    for i in range(1, sheet.max_column+1):
        for j in range(1, sheet.max_row+1):
            sheet2.cell(row=j, column=i).value = u[i-1][j-1]

Javaの場合は、記入するための"cell"を最初に作る必要があり、これを怠ったり、記入するデータサイズと異なっているとエラーが生じていた。なので、Excel操作に関しては圧倒的にPythonが楽そうである。


最後に、保存ダイアログを使ったExcelファイルの保存方法は下記。

    #新しいExcelファイルを名前をつけて保存する
    closePath = tkinter.filedialog.asksaveasfilename()
    wb.save(closePath+".xlsx")

オープンダイアログと同様、一行で終わるので非常に楽である。



上記のコードをグローバル変数を使った物に少しmodifyしたので、追記。

import tkinter
import tkinter.filedialog
import openpyxl
import numpy as np

#エクセルファイルを開く
def openFile(event):
    global sheet
    global wb
    fileType = [("excel", "*.xlsx")]
    iDir = "/Users/Desktop/"
    filePath = tkinter.filedialog.askopenfilename(filetypes = fileType, initialdir = iDir)
    wb = openpyxl.load_workbook(filePath)
    sheet = wb.active
    getData()

#ExcelファイルのDataを取得
def getData ():
    global u
    global sheet2

    u = np.zeros((sheet.max_column, sheet.max_row))

    for i in range(1, sheet.max_column+1):
        for j in range(1, sheet.max_row+1):
            u[i-1][j-1] = sheet.cell(row = j, column = i).value

#Excelファイルを名前をつけて保存する
def saveFile(event):
    wb.create_sheet(index=1, title="Copy")
    sheet2 = wb["Copy"]
    for i in range(1, sheet.max_column + 1):
        for j in range(1, sheet.max_row + 1):
            sheet2.cell(row=j, column=i).value = (u[i - 1][j - 1]) * 2

    closePath = tkinter.filedialog.asksaveasfilename()
    wb.save(closePath+".xlsx")

#GUIの作成およびボタンの設置
root = tkinter.Tk()
root.title("Test")
root.geometry("100x100")
openButton = tkinter.Button(text = "Open", width = 25)
saveButton = tkinter.Button (text = "Save", width = 25)
openButton.bind("<Button-1>", openFile)
saveButton.bind("<Button-1>", saveFile)
openButton.pack()
saveButton.pack()
root.mainloop()

関数間で変数を共有できるように、global文を使った。
具体的には、下記の部分などである。

    global u
    global sheet2

こうすることで、関数をまたいで変数を使える。
このコードを実行すると、f:id:Aki-Miya:20180606154157p:plainのように2つのボタンがついたGUIが立ち上がり、OpenボタンでExcelファイルを呼び出し、Saveボタンで名前を付けて保存できるようにした。