GUIツールで文書内にどんな人名や地名が盛り込まれているのを一発確認

__固有表現認識器*spaCy*__と、__GUIツールの生成環境*Tkinter*__を__Python3__から呼び出すことで、次のUIを持つGUIツールを作ってみました。

##１.作ったもの

####（ 起動コマンド ）

__実装コードは、このページの第２節に掲載しています。__

```bash:Termkinal
$ python3 tkinter_ner.py 
```

![スクリーンショット 2020-11-20 22.05.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/862466/14fe71fd-2af2-4f37-6c4a-f1e1358d7fda.png)


####（手順１） テキストファイルの相対パスと、調べたい固有表現のラベル名を入力する
__・（固有表現）「人名」を指定__

![スクリーンショット 2020-11-20 22.06.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/862466/e8436017-4918-8d9d-0aab-eba3a82bd1bc.png)


####（手順２） 中央にあるボタン「解析結果を出力する」を押す。

![スクリーンショット 2020-11-20 22.08.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/862466/2ebd8bf3-9154-5f5b-544e-94010e87453f.png)

####（その後）手順１と手順２を、好きなだけ繰り返す。
__・（固有表現）「組織名」を指定__

![スクリーンショット 2020-11-20 22.12.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/862466/9eb22abd-0d2d-ae16-2a37-f52028bf0d48.png)

__・（固有表現）「地名」を指定__

![スクリーンショット 2020-11-20 22.13.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/862466/bf6c4911-322f-505e-3b8a-2ab88ed4fccb.png)

__・（固有表現）「イベント名」を指定__

![スクリーンショット 2020-11-20 22.14.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/862466/c5a382ed-2036-a35a-7dd0-c0174ebda7e2.png)

__・（固有表現）「日付」を指定__

![スクリーンショット 2020-11-20 22.15.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/862466/df4acedd-ce99-c2b2-62e1-262a71d5bfbf.png)


#####（サンプル・テキスト）
・[NHK NewsWeb（配信日）2020年11月20日 19時08分「レムデシビル WHO 入院患者への投与勧められないとの指針公表」](https://www3.nhk.or.jp/news/html/20201120/k10012721961000.html?utm_int=detail_contents_news-related_003)

![スクリーンショット 2020-11-20 21.56.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/862466/346d16d7-bbe1-77cd-93e1-818ebc85e441.png)

---

##２. 実装スクリプト

#####（ 参考にしたウェブページ ）
・[ミナピピンの研究室「【Python/tkinter】GUIツールでファイル選択処理を実装する」](https://tkstock.site/2020/01/11/python-tkinter-gui-%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab%e9%81%b8%e6%8a%9e-%e3%82%a2%e3%83%83%e3%83%97%e3%83%ad%e3%83%bc%e3%83%89/)

```Python3:tkinter_ner.py
# Tkinterのライブラリを取り込む
import tkinter, spacy, collections, os
from tkinter import *
from tkinter import ttk
from tkinter import filedialog
from tkinter import messagebox
from spacy.matcher import Matcher

# ファイルの参照処理
def click_refer_button():
    fTyp = [("","*")]
    iDir = os.path.abspath(os.path.dirname(__file__))
    filepath = filedialog.askopenfilename(filetypes = fTyp, initialdir = iDir)
    file_path.set(filepath)

# 固有表現抽出処理
def extract_words_by_entity_label(text, named_entity_label):
    named_entity_label_dict = {"jinmei" : "PERSON", "soshikimei" : "ORG", "chimei" : "LOC", "eventmei" : "EVENT", "hiduke" : "DATE"}
    if named_entity_label in named_entity_label_dict.keys():
        label = named_entity_label_dict[named_entity_label]
    else:
        pass
    
    nlp = spacy.load('ja_ginza')
    if label in ["PERSON", "NORP", "FAC", "ORG", "GPE", "LOC", "PRODUCT", "EVENT", "WORK_OF_ART", "LAW", "LANGUAGE", "DATE", "TIME", "PERCENT", "MONEY", "QUANTITY", "ORDINAL", "CARDINAL"]:
        text = text.replace("\n", "")
        doc = nlp(text)
        words_list = [ent.text for ent in doc.ents if ent.label_ == label]
    else:
        words_list = []
    return words_list
    
        
# 出力処理
def click_export_button():
    # 入力されたテキストファイルの相対パスを取得
    path = file_path.get()
    # 入力された固有表現の種別名のテキストを取得
    named_entity_label = str(named_entity_label_name_entry.get())
    if path[-4:] == '.txt':
        f = open(path, encoding="utf-8")
        text_data = f.read()
        label_word_list = extract_words_by_entity_label(text_data, named_entity_label)
        # 指定された固有表現に該当する単語を取得した結果（単語リスト）を、{単語文字列 ： 出現回数}の辞書に変換する
        count_per_word = collections.Counter(label_word_list)
        # 出現回数の多い順番に並べる
        freq_per_word_dict = dict(count_per_word.most_common())
        #output_list =  ["{k}  : {v}".format(k=key, v=value) for (key, value) in freq_per_word_dict.items()]
        # 単語数を取得する
        unique_word_num = len(freq_per_word_dict)
        if unique_word_num > 0:
            message = "{num}件の{label}が見つかりました。\n\n出現回数順に並べた結果は以下です。\n\n".format(num=unique_word_num, label=named_entity_label)
            word_list = list(freq_per_word_dict.keys())
            word_freq_list = list(freq_per_word_dict.values())
            for i in  range(unique_word_num):
                tmp = "{rank}番目の単語 : {word}\n出現回数 : {count}\n\n===================\n".format(rank=i+1, word=word_list[i], count=word_freq_list[i])
                message += tmp
        else:
            message = "{num}件の{label}が見つかりました。\n\n".format(num=unique_word_num, label=named_entity_label)     
        textBox.insert(END, message)
    else:
        textBox.insert(END, '\nファイルがテキストファイルではありません')

if __name__ == '__main__':
    # ウィンドウを作成
    root = tkinter.Tk()
    root.title("固有表現計数カウンター") # アプリの名前
    root.geometry("730x500") # アプリの画面サイズ

    # Frame1の作成
    frame1 = ttk.Frame(root, padding=10)
    frame1.grid()

    # 「ファイル」ラベルの作成
    s = StringVar()
    s.set('ファイル名：')
    
    # 「ラベル名」ラベルの作成
    t = StringVar()
    t.set('抽出する語句の種類：')
    label1 = ttk.Label(frame1, textvariable=s)
    label1.grid(row=0, column=0)

    label2 = ttk.Label(frame1, textvariable=t)
    label2.grid(row=1, column=0)
    
    u = StringVar()
    u.set('抽出する語句の種類は、以下のいずれか１つを入力して下さい。\njinmei", "soshikimei", "chimei", "eventmei", "hiduke"のいずれか１つを入力してください。')
    label3 = ttk.Label(frame1, textvariable=u)
    label3.grid(row=2, column=1)
    # 参照ファイルのパスを表示するテキストボックスの作成
    file_path = StringVar()
    filepath_entry = ttk.Entry(frame1, textvariable=file_path, width=50)
    filepath_entry.grid(row=0, column=1)
    # 抽出する固有表現の種別名を入力するテキストボックスの作成
    named_entity_label_name = StringVar()
    named_entity_label_name_entry = ttk.Entry(frame1, textvariable=named_entity_label_name, width=50)
    named_entity_label_name_entry.grid(row=1, column=1)

    # Frame2の作成
    frame2 = ttk.Frame(root, padding=10)
    frame2.grid()
    
    # テキスト出力ボタンの作成
    export_button = ttk.Button(frame2, text='解析結果を出力する', command=click_export_button, width=20)
    export_button.grid(row=0, column=0)

    # テキスト出力ボックスの作成
    textboxname = StringVar()
    textboxname.set('\n\n出力内容 ')
    label3 = ttk.Label(frame2, textvariable=textboxname)
    label3.grid(row=1, column=0)
    textBox = Text(frame2, width=100)
    textBox.grid(row=2, column=0)

    # ウィンドウを動かす
    root.mainloop()
```

###（ 今後やりたいこと ）

1. ファイルを画面内に__ドラッグ・ドロップして指定__できるようにしたい。
2. 拡張子が".txt"のファイルだけでなく、__".pdf"や".pptx"のファイルを扱えるように__したい。PDFファイルやPowerPointファイルから、テキスト部分のみを切り出してテキストファイルに吐き出すPythonのモジュールを使うか、UNIXのコマンドをPythonの*os.system()*か*subprocess*メソッドで用いることで実現できそう。
3. __カウントできる固有表現のラベル名を、ラジオボタンを並べて表示__して、押したラジオボタンのラベル名の固有表現を、カウントするUXにしたい。
4. 画面のテキスト入力欄に、日本語を打ち込めるようにしたい。
5. Pythonの環境構築がされていないPCで、スタンドアロンで動く*exe*ファイルや*MacOSアプリ*を作りたい。*Electron*や*Pyinstaller*は、Pythonモジュールのパスの設定がしんどそう。でも頑張りたい。