Juliaによるテキストデータの前処理をまとめました. TextAnalysis.jlを試しに使用. まだ使いこなせていないので, 機会があればまたまとめ直したいです.
とりあえず以下のツールを用います.
using TextAnalysis
using WordTokenizers
import TinySegmenter
TextAnalysisにはいくつかの構造体が定義されています. そのうちの1つがファイルドキュメント. ただのファイルですが, 様々なフィールドが定義されています.
#create file dicument
fd = FileDocument("../../data/testfile.txt")
A FileDocument
* Language: Languages.English()
* Title: ../../data/testfile.txt
* Author: Unknown Author
* Timestamp: Unknown Time
* Snippet: Hello World! This is a test file. O Romeo, Romeo,
他にもStringDocument
というものもあります. これは文字列です.
#create string document
sd = StringDocument("O Romeo, Romeo, wherefore art thou Romeo?")
A StringDocument{String}
* Language: Languages.English()
* Title: Untitled Document
* Author: Unknown Author
* Timestamp: Unknown Time
* Snippet: O Romeo, Romeo, wherefore art thou Romeo?
これらのドキュメントからテキストを取り出すには次のようにします.
#text of the document
sd.text
"O Romeo, Romeo, wherefore art thou Romeo?"
Author
フィールドも書き換えてみます.
#change the author name
sd.metadata.author = "W.Shakespeare"
sd
A StringDocument{String}
* Language: Languages.English()
* Title: Untitled Document
* Author: W.Shakespeare
* Timestamp: Unknown Time
* Snippet: O Romeo, Romeo, wherefore art thou Romeo?
いくつかの前処理を試します.
prepare!
を用いると, 文字列中の様々な要素を除去できます. 例えばstrip_hrml_tags
を指定すれば, HTMLのタグを除去できます.
sd = StringDocument("<a href='Romeo and Juliet'>O Romeo, Romeo, wherefore art thou Romeo?</a">)
sd.text |> println
prepare!(sd,strip_html_tags) #remove the HTML tags
sd.text |> println
<a href='Romeo and Juliet'>O Romeo, Romeo, wherefore art thou Romeo?</a>
O Romeo, Romeo, wherefore art thou Romeo?
自然言語処理において処理の対象外となるストップワードを除去してみます. これもprepare!
で簡単にできます.
sd = StringDocument("One for all, all for one.")
sd.text |> println
prepare!(sd,strip_stopwords) #remove the stop words
sd.text |> println
One for all, all for one.
One , .
処理内容によっては削りすぎかもしれませんね...
同じ単語でも大文字や小文字が混じっていると厄介です. 小文字に揃えるには以下のようにします.
#unify the font
sd = StringDocument("O Romeo, Romeo, wherefore art thou Romeo?")
remove_case!(sd)
sd.text |> println
romeo, romeo, wherefore art thou romeo?
また, 単数形や複数形などの揺れも厄介です. 単数形に統一するにはstem!
を用います.
#stem
sd = StringDocument("musical 'cats'")
stem!(sd)
sd.text |> println
music ' cat ' is a stori of a cat .
musical
がmusic
になるのはちょっと... story
も変な感じになってしまいました.
次は単語分割です. tokens
を使えば一発です.
#tokenize
sd = StringDocument("O Romeo, Romeo, wherefore art thou Romeo?")
tokens(sd)
10-element Vector{String}:
"O"
"Romeo"
","
"Romeo"
","
"wherefore"
"art"
"thou"
"Romeo"
"?"
日本語だとうまくいきません.
#default tokenize
sd = StringDocument("ああロミオどうしてあなたはロミオなの")
tokens(sd)
1-element Vector{String}:
"ああロミオどうしてあなたはロミオなの"
こんな時は, tokenizerを変えてやります. tokenizerはいくつか定義されています. 日本語に関しては, TinySegmenter
を使うとうまくいくようです.
#change the tokenizer
set_tokenizer(TinySegmenter.tokenize)
sd = StringDocument("ああロミオどうしてあなたはロミオなの")
tokens(sd)
10-element Vector{SubString{String}}:
"ああ"
"ロミオ"
"どう"
"して"
"あ"
"なた"
"は"
"ロミオ"
"な"
"の"
デフォルトの設定に戻しておきます.
#change the tokenizer to the default one
set_tokenizer(toktok_tokenize)
ドキュメントをいくつか寄せ集めてコーパスを作成できます. 前処理も先ほど同様できます. prepare!
に複数処理を命じる時は, パイプライン処理を施します.
#create corpus
crps = Corpus([
StringDocument("<a href='Romeo and Juliet'>To be or not to be. </a>"),
StringDocument("<a href='日本の思想'>To do or not to do. </a>")
])
prepare!(crps,strip_html_tags|strip_non_letters) #preprocessing
remove_case!(crps) #unify to lower case
update_lexicon!(crps) #update lexicon
これを踏まえて, テキストをベクトル, または行列で表現します.
count encodingでは, テキスト中に現れた回数を各単語に関して記録します. 横が単語, 縦がドキュメントの行列を作成します. 横の単語の並びは, terms
フィールドから確認できます.
#represent document as a matrix
M = DocumentTermMatrix(crps)
M.terms |> print #words
dtm(M) |> print #frequency
["be", "do", "not", "or", "to"]
2 ⋅ 1 1 2
⋅ 2 1 1 2
TF-IFDは, tf_idf
関数を使えばOKです.
#represent document as a matrix
M = DocumentTermMatrix(crps)
M.terms |> print #words
tf_idf(M) #TF-IDF
["be", "do", "not", "or", "to"]
2×5 SparseArrays.SparseMatrixCSC{Float64, Int64} with 8 stored entries:
0.231049 ⋅ 0.0 0.0 0.0
⋅ 0.231049 0.0 0.0 0.0