※2016/2/21時点での自分の最適解。
勉強中ゆえまだまだ未解決事項が残ってはいるものの、先ずはある程度のことまでできれば良しとしています。
テキストマイニングの為のツールは沢山あります。
商用ツールでも幾つかあるでしょうし、大学で導入されているSPSSにも入っていると思います。
個人が無料で出来るものとしては、
TTM: TinyTextMiner β versionやKH Coder Index Pageが有名でしょう。
私もKHCoder愛用者です。
広告・マーケティング業界でテキストマイニングは、
・アンケートのフリーアンサー
・商品レビューサイトのレビューの分析
・Twitter等、SNSの呟きの分析
などの需要がありますが、多くの分析は、単語の頻度や、共起語の頻度を出しているのが現状だと思います。
既存のツールでも既に充分応えられてはいますが、最近試してみて便利だな、と思ったのは、トピックモデルを使ったRのLDA(LDA:Latent Dirichlet Allocatoion)パッケージだったので、それを使う為に必要な手順をメモしておきたいと思います。
0)前提
・Twitterのデータを何らかのサービスを使ってDLし、CSVファイルにしている。
・R(とRStudio)を入れている
の2点は終えた所からの準備とします。
1)mecabを入れて、IPAの辞書を変更する
MeCab: Yet Another Part-of-Speech and Morphological Analyzer
ダウンロードし、文字コードを環境に合わせて選んで解凍します。
辞書はデフォルトで入っているIPAを使います。*1
次に、mecabを使うためにPATHを通します。
mecabは MeCab\bin の中に入っているので、右クリック⇒プロパティで位置情報をコピペし、
マイコンピュータを右クリック⇒システムの詳細から下段のPATHを編集して、一番端っこまで移動して、「;」と先ほどのディレクトリ情報を貼り付けて終了です。*2
更に、この時注意が必要なのがIPAの辞書では未知語が「名詞、サ変接続」と設定されているために辞書を更新する必要がある点です。Twitterの呟きには記号が大変多いので、これをしておかないと後で困ります。
マイコンピュータで MeCab> dic > ipadic まで移動し、「unk.def」というファイルをメモ帳等で開き、SYMBOLの所を以下のように変えます
SYMBOL,1283,1283,17585,名詞,サ変接続,*,*,*,*,*
↓
SYMBOL,1283,1283,17585,記号,一般,*,*,*,*,*
コマンドプロンプトで MeCab> dic > ipadic まで移動し
mecab-dict-index
と辞書を改めて憶えさせます。
2)RにRMeCab等を入れる
RMeCabのバージョンを間違えないように選択します
Rで
install.packages("RMeCab", repos = "http://rmecab.jp/R")
とするのが便利です。
3)LDAを入れる
RStudioを使っていれば普通にCRANからダウンロードできます
4)データの読み込み加工
準備が整ったので分析を始めます。
データの読込は、read.tableよりread.csvの方が文字コードの闇に巻き込まれないので便利です。
今回は、Twitterから「雪まつり」「雪像」という言葉が含まれたツイートをデータに使いました。
summaryを使ってデータの状態を確認すると便利です。
このときデータフレームの列名が日本語の場合は英語にしておきます。
colnames(tw) <- c("Media","Date","Acount","url","honbun")
元
メディア 日付 タイトル URL
1 twitter 2016/2/4 0:00 DB_item http://twitter.com/DB_item/status/694898215582318594
本文
1 超巨大な「ドラゴンボール」や「進撃の巨人」が待ってるぞ! 『さっぽろ雪まつり』は2016年2月5日開催だッ!! - ロケットニュース24 https://t.co/3xVdWbLtND
後
Media Date Acount url
1 twitter 2016/2/4 0:00 DB_item http://twitter.com/DB_item/status/694898215582318594
honbun
1 超巨大な「ドラゴンボール」や「進撃の巨人」が待ってるぞ! 『さっぽろ雪まつり』は2016年2月5日開催だッ!! - ロケットニュース24 https://t.co/3xVdWbLtNDっぽろ雪まつり』は2016年2月5日開催だッ!! - ロケットニュース24 https://t.co/3xVdWbLtND
5)RTやURLを削除する
ツイートデータにはだいたいリンクのURLだったり、公式RTのデータであれば、「RT @hogehoge」といった情報が一緒に入っている筈です。これらは、どんな話題がつぶやかれたのか?には、不要なので削除します。
tw$honbun <- gsub("^RT\\s@[0-9a-zA-Z\\._]*:\\s+","",tw$honbun)
RTで始まり、空白があり、@マークがあって英数字があってコロンで終わる部分を置換で削除してしまいます。
同様にURLも削除します。
tw$honbun <- gsub("https?://t.co/[0-9a-zA-Z\\._]*","",tw$honbun)
こちらのページを参考にさせて頂きました!
6)一度CSVファイルに書き出してまた読み込む
ここが現時点で謎のママなのですが、何故かそのままだと文字化けしてしまい、、、
一度CSVファイルに書き出してからまた読みなおしたデータフレームだと上手く行きました。
write.csv(tw,"tw_honbun_2.csv",quote=F)
tw2 <- read.csv("tw_honbun_2.csv",header=T)
colnames(tw2) <- c("ID","Media","Date","Acount","url","honbun")
7)docDFを使う
RMeCabと言えば、
で、自分もこの本に触発されてRを知って勉強し始めたものでした。
しかし、この本には載っていない関数を使うのが便利です。
データフレームに対して、どの列にどの単語が出ているのか?のマトリックスを出します。
tw_docdf <- docDF(tw2,column="honbun",type=1)
このファイルの最初の2列を6行目まで見てみましょう
## TERM POS1
## 1 ! 記号
## 2 !! 記号
## 3 !!! 記号
## 4 !!!!! 記号
## 5 !!!!!】... 記号
## 6 !!!\\( 記号
このように記号が沢山でてきます。mecabの辞書を変更していないと、ここで「記号」として貰えません。
ツイートの内容を理解するには、名詞・形容詞・動詞 辺りに情報を絞ると捉えやすくなります。
search_word <- c("名詞","形容詞","動詞")
tw_docdf_2 <- subset(tw_docdf,tw_docdf$POS1 %in% search_word)
頭の方の行は数字ばかりになるので、、、後ろから6行を6列めまで出すと、こんな感じになります
TERM POS1 POS2 Row1 Row2 Row3
9827 艸 名詞 一般 0 0 0
9828 蜥蜴 名詞 一般 0 0 0
9829 蠢く 動詞 自立 0 0 0
9830 迸る 動詞 自立 0 0 0
9831 鍼灸 名詞 一般 0 0 0
9832 頷く 動詞 自立 0 0 0
この辺りはこちらのサイトを参考にさせて頂きました
Blog — RMeCabによる形態素解析とLDAによるトピック抽出
8)LDA用のデータを準備
移行は、ほぼほぼこの2つのサイトから
RでLDAの一例
http://rstudio-pubs-static.s3.amazonaws.com/16714_5b89bc4687994ff3b43c6e715ddadda1.html#R_LDA___
事前に格納する為のオブジェクトが必要なのが通常のRとは違います。
先ず、リストのオブジェクトを作り、その中に頻度表のデータを入れます。
4列目から入れているのは、頻度データが4列目から入っているためです
また、それとは別に、単語のリストもオブジェクトを作ります。
doc <- list()
for (i in c(4:ncol(tw_docdf_2))) {
d <- tw_docdf_2[,i]
doci-3 <- rbind(as.integer*4[d>0]-1),as.integer(d[d>0]))
}
vcab <- tw_docdf_2[,1]
9)LDAを実行
LDAパッケージのギブズサンプリングを行います。*5
この「n」に入れる数字で、トピックスの数何単語の組み合わせのセットにするかを決められます。
n <- 10
ldaret <- lda.collapsed.gibbs.sampler(doc,n,vcab,1000,0.1,0.001)
summary(ldaret)
トピックスを10程度、見つけてくれました。
Length Class Mode
assignments 50 -none- list
topics 6430 -none- numeric
topic_sums 10 -none- numeric
document_sums 500 -none- numeric
<NA> 0 -none- NULL
<NA> 0 -none- NULL
<NA> 0 -none- NULL
<NA> 0 -none- NULL
<NA> 0 -none- NULL
<NA> 0 -none- NULL
10)可視化の準備1
この辺、まだ理解が追いついていませんが、各トピックスから10ワードを抜き出す設定にしてみます
k_words <- 10
top.words <- top.topic.words(ldaret$topics,k_words,by.score=TRUE)
例えば、トップワードの一列目を抜き出してみます
(top.words[,1])
こんな感じです ↓
[1] "新幹線" "凄い" "プロジェクションマッピング"
[4] "まつる" "すぎる" "POP"
[7] "防弾" "団" "少年"
[10] "FESTIVAL"
10個位抜き出すと、「新幹線」の「凄い」「プロジェクションマッピング」があった、という内容と、残りはちょっとわかりませんが、そんな話題があった、ということがわかります。
11)可視化の準備2
次に、グラフにするときに、何ツイートを抜き出すか決めます。
例えば、こちらでは、「20」に設定しました。
N <- 20
topic.proportions <- t(ldaret$document_sums)/colSums(ldaret$document_sums)
topic.proportions <- topic.proportions[1:N,]
topic.proportions[is.na(topic.proportions)] <- 1/n
こうすることで、抽出された10のトピックスが、20個のツイートのうち、どれ位の割合でどう分布しているのか?を見ることができます。
12)可視化の準備3
ggplotで可視化するためにreshape2パッケージでmeltします。
topic.proportions.df <- melt(cbind(data.frame(topic.proportions),document=factor(1:N)),variable.name="topic", id.vars = "document")
こんなデータ構造です。document
document topic
1 1 新幹線.凄い.プロジェクションマッピング.まつる.すぎる.POP.防弾.団.少年.FESTIVAL
2 2 新幹線.凄い.プロジェクションマッピング.まつる.すぎる.POP.防弾.団.少年.FESTIVAL
3 3 新幹線.凄い.プロジェクションマッピング.まつる.すぎる.POP.防弾.団.少年.FESTIVAL
4 4 新幹線.凄い.プロジェクションマッピング.まつる.すぎる.POP.防弾.団.少年.FESTIVAL
5 5 新幹線.凄い.プロジェクションマッピング.まつる.すぎる.POP.防弾.団.少年.FESTIVAL
6 6 新幹線.凄い.プロジェクションマッピング.まつる.すぎる.POP.防弾.団.少年.FESTIVAL
value
1 0.3809524
2 0.8333333
3 0.0000000
4 0.0000000
5 0.0000000
6 0.0000000
topicの一つめが、docimen1にどれ位含まれているか?を意味しているようです。
docmentになっているのは、元々ツイート1つ1つに対して使うというよりも、複数の書類(文書)の中に、共通したトピックスがどれ位みられるか?を調べるものなので、今回のように140文字のツイートに対して使うのは、やや不適切な使い方なのかもしれませんね。
12)ggplot2で可視化
後はいつもと同じですが、ツイート(document)毎に色分けをしてグラフ化。
他のサイトではfacet_wrapをしてマトリックスにしていますが、今回は20ツイートも選んでいるので入りきらなくなってしまいますのでパス
g <- ggplot(topic.proportions.df,aes(x=topic,y=value,fill=document))
g <- g + theme_gray(base_family="Japan1GothicBBB")
g <- g + geom_bar(stat = "identity",position = "dodge",)
#g <- g + facet_wrap(~document,ncol=N)
g <- g + coord_flip()
plot(g)
また、PDF等で保存する時に文字化けを防ぐために、この段階で、フォントを指定しておくのがggplotにおけるポイントです。
これだけでも何となくわかると思いますが、
「ラブライブ」だとか「三太郎」(auのやつですかね?)だとか、「行くよ!」と言うようなツイートも結構あったのだということが見て取れます。
13)具体的なツイートを検索する
10単語ではわかるところ、わからないところがあるので、元々のファイルをテキストエディタなりExcelなりで開いて、キーワードを検索してみます。
例えば、「オーストラリア」「すぎる」というツイートは、
雪まつり来たんだが、オーストラリアの人が作った雪像が怖すぎて... pic.twitter.com/ZLU3F8Ex9p
— ハルフト (@rabbit_love8715) 2016, 2月 6
のツイートだとわかります。
結構シェアされていますね。
【迫力満点】「第67回さっぽろ雪まつり」開幕! 進撃の巨人が襲来https://t.co/GIYHitv1dC
— ライブドアニュース (@livedoornews) 2016, 2月 5
「ドラゴンボール超」の悟空&ベジータや、五郎丸風の雪像も展示。夜にはプロジェクションマッピングも上映されます! pic.twitter.com/U0jCVoYiaB
のニュース記事のシェアだとわかります。
「熱気」は、なんだろう?と思うと
第67回さっぽろ雪まつりK-POP FESTIVAL 2016に来てくれたA.R.M.Yの皆さん!楽しかったですか〜!?札幌の雪が溶けるほど皆さんの熱気が熱くてとても楽しかったです!A.R.M.Yなまらめんこい〜💕 またね〜👋🏻👋🏻 pic.twitter.com/SE6whIVHN6
— 防弾少年団 JAPAN OFFICIAL (@BTS_jp_official) 2016, 2月 6
ということのようです。
キャラクターやアイドルというコアなファンが居るコンテンツはTwitterではシェアされやすいので、公式RTを含むようにすると多くヒットする傾向にあります。
その他、「行く」「寒い」などは、一つの呟きのシェアではありませんが、幾つか検索すると、「やっぱ寒いー」だったり「行ったよー」「行きたいー」などのツイートがありました。
14)補講1 公式RTの除外
公式RTを外して分析する場合は、grepで公式RTを含む行名を取得し、
その行を外したデータを作り直します。
rt_list <- grep("^RT\\s@[0-9a-zA-Z\\._]*:\\s+",tw[,1])
head(rt_list)
tw <-tw[-(rt_list),]
15)補講2 そもそも論
Twitterは、ゴミツイートが多く今回は少なかったものの変なbotやらステマアカウントがあるのでもっと事前に除外しないといけないことが多いです。
また、もっと抽出量が少ないうちは、目視で人間の目で見て分析するのが、一番発見が多いものです。
--------------------------
参考書
本来はちゃんとアルゴリズムを勉強してから使わないと、きっと落とし穴にハマるのですが、今のところ上手く検出できているようなので取り急ぎは満足していますが、いずれは勉強したいところ。。。
この2冊を推薦しているサイトが多いので、これから頑張ります。
トピックモデルによる統計的潜在意味解析 (自然言語処理シリーズ)
- 作者: 佐藤一誠,奥村学
- 出版社/メーカー: コロナ社
- 発売日: 2015/03/13
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (5件) を見る