VB.NETでも形態素解析っぽいことをしてExcel方眼紙から漢字を抽出したい
はじめに
皆さんは漢字は得意ですか? 弊社はクッソ苦手です。
パソコンに頼り切っちゃってるのでまぁ当たり前っちゃー当たり前だし、普段パソコンで文章を書くなら別に困りもしないのですが、打ち合わせの時に書記でホワイトボードに書くときに漢字が書けなくて困るんですよね。
そこで思ったのですが、似たような打ち合わせなら似たような漢字が出てくるのでは? つまり議事録から漢字を引っこ抜けば似たような打ち合わせに適用できるのでは?
そして議事録といえばExcel。表計算からDB設計からポスター作製までをこなすオールラウンドアプリケーション。
つまり
すればその打ち合わせで使われる漢字の傾向が掴めるって寸法です。
この時点で自分でも思っています。
素直に漢字を勉強しろよ
Excelから文字列の取り出し
とりあえずExcelからデータを抜かないといけません。
ここではEPPlusを使ってデータを取り出します。
ライブラリのインストールはNuGetからサクッとインストールしてください。
今回は特にエクセルの見た目なんてものには興味がなく、ただ文字を引っこ抜ければいいので
- ディレクトリ直下のエクセルファイルをひたすら列挙し
- すべてのシートのすべてのセルから文字列を取り出す
という感じでやってます。
Imports OfficeOpenXml Imports System.IO Public Class ExcelContentExtractor Private Sub New() ' Nothing to do. End Sub Public Shared Function ExtractContent(targetDirectory As String) As IEnumerable(Of String) Dim contents = New List(Of String) For Each it In Directory.GetFiles(targetDirectory, "*.xlsx") Using exc = New ExcelPackage(New FileInfo(it)) For Each sheet In exc.Workbook.Worksheets For Each cell In sheet.Cells() Dim text = TryCast(cell.Value, String) If Not text Is Nothing Then contents.Add(text) End If Next Next End Using Next Return contents End Function End Class
形態素解析
形態素解析はMeCabの.NETの移植版であるNMeCabを使用しています。
インストールとか使い方は完全にフィーリングでやったので解説出来ないです。ごめんね
Imports NMeCab Public Class SentenceSplitter Private Sub New() ' Nothing to do. End Sub Public Shared Function Split(sentence As IEnumerable(Of String)) As IEnumerable(Of String) Dim param = New MeCabParam() With {.DicDir = ".\ipadic"} Dim words = New List(Of String) Using mecab = MeCabTagger.Create(param) For Each it In sentence Dim node = mecab.ParseToNode(it) While Not node Is Nothing If node.Feature Is Nothing Then node = node.Next Continue While End If words.Add(node.Surface) node = node.Next End While Next End Using return words End Function End Class
このあたりを試行錯誤してる時も常に頭の片隅にありました。
素直に漢字を勉強しろよ
漢字のフィルタリング
次に漢字を含む単語をフィルタリングします。
ヤフーでググるとAscW
でUnicodeスカラ値を取り出しIf
で判断するというとっても原始的素朴でステキな方法が紹介されていました。*1
.NETの正規表現は名前付きブロックをサポートしているのでありがたくそれを使いましょう。
特に下調べしてないんで確証は無いんですがまぁCJK統合漢字を含んでれば大丈夫でしょうという感じで行きました。 好みで拡張Aもあわせてどうぞ。
Imports System.Text.RegularExpressions Public Class HanFilter Private Sub New() ' Nothing to do. End Sub Public Shared Function HasHan(word As String) As Boolean Return Regex.IsMatch(word, ".*[\p{IsCJKUnifiedIdeographs}]+.*") End Function End Class
集計
あとはLINQで一発なのでもういいや。
Module Program Sub Main(args as String()) If args.Length <> 1 Then Console.WriteLine("AppName <path of directory that contains the Excel file>") Environment.Exit(0) End If Dim contents = ExcelContentExtractor.ExtractContent(args(0)) Dim words = SentenceSplitter.Split(contents) Dim result = words. Where(AddressOf HanFilter.HasHan). GroupBy(Function(it) it, Function(it) 1). Select(Function(it) New With {.Count = it.Sum(), .Word = it.Key}). OrderByDescending(Function(it) it.Count) for each it in result Console.WriteLine("「{0}」: {1}", it.Word, it.Count) Next End Sub End Module
「漢字」: 12 「文字」: 5 「素」: 4 「形態」: 4 「解析」: 4 「打ち合わせ」: 4 「似」: 3 「議事」: 3 「録」: 3 「列」: 3 「取り出し」: 3 「弊社」: 2 「当たり前」: 2 「書く」: 2 「時」: 2 「思っ」: 2 「引っこ抜け」: 2 「抜き出し」: 2 「集計」: 2 「素直」: 2 「勉強」: 2 「特に」: 2 「感じ」: 2 「正規」: 2 「表現」: 2 「方眼」: 1 「紙」: 1 「抽出」: 1 「好き」: 1 「変数」: 1 「個」: 1 「越え」: 1 「普通」: 1 「皆さん」: 1 「得意」: 1 「苦手」: 1 「頼り」: 1 「切っ」: 1 「普段」: 1 「文章」: 1 「別に」: 1 「困り」: 1 「書記」: 1 「書け」: 1 「困る」: 1 「出」: 1 「適用」: 1 「表」: 1 「計算」: 1 「設計」: 1 「作製」: 1 「行い」: 1 「各」: 1 「要素」: 1 「使わ」: 1 「傾向」: 1 「掴める」: 1 「寸法」: 1 「時点」: 1 「自分」: 1 「抜か」: 1 「使っ」: 1 「今回」: 1 「見た目」: 1 「興味」: 1 「直下」: 1 「列挙」: 1 「取り出す」: 1 「移植」: 1 「版」: 1 「使用」: 1 「使い方」: 1 「完全」: 1 「解説」: 1 「出来」: 1 「試行」: 1 「錯誤」: 1 「常に」: 1 「頭」: 1 「片隅」: 1 「次に」: 1 「含む」: 1 「単語」: 1 「値」: 1 「判断」: 1 「原始」: 1 「的」: 1 「素朴」: 1 「方法」: 1 「紹介」: 1 「名前」: 1 「付き」: 1 「使い」: 1 「下調べ」: 1 「確証」: 1 「無い」: 1 「統合」: 1 「含ん」: 1 「大丈夫」: 1 「行き」: 1 「好み」: 1 「拡張」: 1 「一」: 1 「発」: 1
おわりに
素直に漢字を勉強しろよ
*1:しょうもないバグを作りこみそうだし、テストも面倒そう