Wordファイル内であれば文字の揺らぎ(”ドライバー”と”ドライバ”等)があればアラートがでますが、複数のExcel、Wordファイルの記載をまとめてチェックする方法が無かったので作ってみました。同じディレクトリにあるExcel、Wordファイルを読み込んで一斉に文字の揺らぎのチェックをかけます。複数のファイルからなる申請書とかを作りたいときに役立てばよいなと思ってます。(といいつつ、janomeを使いたかったというスケベ心がチラホラ)
#1.使用モジュール一覧。
from janome.tokenizer import Tokenizer
from janome.analyzer import Analyzer
from janome.tokenfilter import *
import sys
from collections import Counter
import pandas as pd
import docx2txt
import numpy as np
import os
import openpyxl
import openpyxl as px
import xlwt
import re
#2.注意事項のお知らせと条件設定
同じディレクトリにあるファイルを読み取る作業の前にファイル名の文字制限に関して注意事項が出てくるようにしました。今回、結果は”まとめ”という名前のExcelシートとして吐き出すようにしています。チェックする対象の個々ファイルの調査結果はまとめファイルの各シートにひとつずつ吐き出されるようにしているのですが、Excelファイルのシート名って文字制限がるのでエラー回避です。
また、行政に申請する書類とかフォーマットをダウンロードし、そこに記載していくようになっていると思います。フォーマットに最初から記載されている文字はノイズになるので取り除けるようにフォーマットファイルがあれば頭に”FMT_”として同じディレクトリに入れておいてもらえれば考慮するようにしました。
print("ファイル名は半角で31文字以下でお願いします。(エクセルの場合、「book名」と「シート名」合計で30文字以下)。")
print("カラのフォーマットがある場合はファイル名に「FMT_」をつけて保存してください。")
print("「FMT_」中にもある単語はカウントしません。")
print("ファイルを保存したら、すべてのファイルを閉じた後、エンターキーを押してください。")
input()
”出てくる単語上位何位まで考慮に入れるか”と”類似度を算出するか”を聞いてくるようにしました。
また、類似文字の類似度算出までがこのプログラムの最終目標ですが、作ってみたらすんごい時間がかかることが判明したのでif文でやるならyを入力するようにしました。時間がかかってイライラしたら中止するショートカットも表示されるようにしています。
最後にディレクトリ内のファイル名を取得しています。
###上位n位まで書き出し
while True:
Num = input("頻出単語順代何位まで書き出しますか?数値を入力してください。 例)200 : ")
if Num.isdecimal():break
###########################################################################
###while文で意思確認
while True:
choice = input("単語の類似度検索まで実施する場合は「y」、実施しない場合は「n」を入力 : ")
if choice in ["y","n"] :break
print("実行中 中止したい場合は「Ctrl」+「c」を押してください。")
path = os.path.abspath(".")
files = os.listdir(path)
name_lists = []
dataframe = {}
#3.Wordファイルの読み込み
同じディレクトリにある".docx"ファイルの読み込み準備です。Wordファイルをテキストにしてそのファイルの中から名詞だけを抜き出しています。複合名詞は複合名詞のかたちで抜き出すようにしていますが、そこは以下のURLを参考にさせていただきました。
(というかこの部分が肝なんですが…オリジナリティーが無くお恥ずかしい限りです)
参考:URL:http://eneprog.blogspot.com/2018/07/janome-analayzerpython.html
wordfiles = [x for x in files if x[-5:] == '.docx']
for wordfile in wordfiles:
###word fileからtxt fileに変更
data = docx2txt.process(wordfile)
name = wordfile.replace(".docx", "")
###名詞の抜出し
token_filters = [CompoundNounFilter(),
POSKeepFilter(["名詞"]),
POSStopFilter(["名詞,代名詞","名詞,非自立","名詞,数"]),
TokenCountFilter()]
a = Analyzer(token_filters=token_filters)
tokens=a.analyze(data)
counts=pd.DataFrame()
for count in a.analyze(data):
counts = counts.append([list(count)],ignore_index = True)
if counts.empty:
pass
else:
name_lists.append(name)
counts.columns = ["単語",name + "回数"]
counts = counts.sort_values(by = name + "回数", ascending = False).reset_index(drop = True)
counts2 = counts.set_index("単語")
counts2[:int(Num)]
dataframe[name] = pd.DataFrame(counts2)
#4.Excelファイルの読み込み
同じようにディレクトリにある".xlsx"ファイルの読み込み準備です。Excelファイル内のシートも個別に読み込むようにしているのでif文の中にif文が入る入れ子になってます。
excelfiles = [x for x in files if x[-5:] == '.xlsx']
for fnameR in excelfiles:
book = openpyxl.load_workbook(fnameR)
bname = fnameR.replace(".xlsx", "")
sheets = book.sheetnames
for sname in sheets:
###excel fileからtxt fileに変更
df = pd.read_excel(fnameR, sheet_name = sname, header = None)
name = bname + "_" + sname
df = df.replace( '\n', '', regex=True)
data = df.to_csv(sep = " ",index = False)
###名詞の抜出し
token_filters = [CompoundNounFilter(),
POSKeepFilter(["名詞"]),
POSStopFilter(["名詞,代名詞","名詞,非自立","名詞,数"]),
TokenCountFilter()]
a = Analyzer(token_filters=token_filters)
tokens = a.analyze(data)
counts = pd.DataFrame()
for count in a.analyze(data):
counts = counts.append([list(count)],ignore_index = True)
if counts.empty:
pass
else:
name_lists.append(name)
counts.columns = ["単語", name + "回数"]
counts = counts.sort_values(by=name + "回数", ascending = False).reset_index(drop = True)
counts2 = counts.set_index("単語")
counts2[:int(Num)]
dataframe[name] = pd.DataFrame(counts2)
#5.結果の書き出し
まとめフォルダを作成。フォーマットファイルがある場合はフォーマットファイルの単語頻出数を減算するようにしています。
if not os.path.exists("まとめ"):
os.mkdir("まとめ")
###########################################################################
###個々の結果書き出し
with pd.ExcelWriter("まとめ/まとめ.xlsx") as writer:
for A in name_lists:
dataframe[A].to_excel(writer, sheet_name=A)
###フォーマットデータを引き算
format_lists = [x for x in name_lists if x[:4] == 'FMT_']
if len(format_lists) == 0:
df_needs = name_lists
else:
for format_list in format_lists:
post_list = format_list.replace("FMT_","")
df1 = dataframe[format_list]
df2 = dataframe[post_list]
dataF = pd.concat([df1,df2], axis = 1, sort = False)
dataF = dataF.fillna(0)
dataF = dataF.assign( subtra = dataF[post_list + "回数"] - dataF[format_list + "回数"])
dataF = dataF[dataF.subtra != 0]
dataF = dataF.rename(columns = {"subtra":("Δ" + post_list + "回数")})
del dataF[post_list + "回数"]
del dataF[format_list + "回数"]
dataframe[post_list] = pd.DataFrame(dataF)
###########################################################################
df_needs = list(set(name_lists) - set(format_lists))
###########################################################################
df_need_total = pd.DataFrame()
###換算後の個々の結果書き出し
with pd.ExcelWriter("まとめ/まとめ(雛型除算後).xlsx") as writer:
for B in df_needs:
dataframe[B].to_excel(writer, sheet_name = B)
for df_need in df_needs:
df_need_total = pd.concat([dataframe[df_need],df_need_total], axis = 1, sort = False)
df_need_total.to_csv('まとめ/一覧.csv', encoding = 'utf_8_sig')
lst = df_need_total.index.values
#6.類似度の算出
総当たりで類似度を数値化します。正直沢山のExcel、Wordファイルを読み込んでしまうとすごい時間がかかるので要検討です。以下のURLを参考にさせていただきました。
参考URL:https://blog.mudatobunka.org/entry/2016/05/08/154934
###類似度検索の確認。choiceでyを入力したときのみ類似度を算出する
import difflib
if choice == "y":
##リスト内包表記で strs の中の文字列から重複なしの組み合わせを作る
df_finaldata = pd.DataFrame(index = [], columns = ["名詞","名詞2","類似度"])
for (str1, str2) in [
(str1, str2)
for str1 in lst
for str2 in lst
if str1 < str2
]:
s = difflib.SequenceMatcher(None, str1, str2).ratio()
series = pd.Series([str1, str2, s] ,index = df_finaldata.columns)
df_finaldata = df_finaldata.append(series, ignore_index = True)
df_finaldata = df_finaldata.set_index("類似度")
df_finaldata = df_finaldata.drop(index = [0])
df_finaldata = df_finaldata.sort_values('類似度', ascending = False)
df_finaldata.to_csv('まとめ/類似度結果.csv', encoding = 'utf_8_sig')
###########################################################################
###choiceでnのときはpassでif文から抜ける。y,n以外の文字の時はWhile文でループする。
else:pass
二回目の寄稿になります。だいぶ前に作ったスクリプトを今回寄稿しながら編集したのでもしかしたら動かないってこともあるかもしれません。