このNoteは、株式会社ネオキャリアの分析啓蒙活動の一環として行った EDA Salon のレポートをパブリッシュしたものになります。
こんにちは、株式会社ネオキャリアの松村です。 EDA Salon 第8回 のデータを使ったデータ分析をやってみます。
YouTubeのとレンディングデータということで、「トレンドに入る動画タイトルの特徴」を掘り下げてみたいと思います。
なお、鈴木さんの投稿にて、「視聴数とタイトルのネガポジが関係しているかも」という知見は得られているので、別の観点から見てみることにします。
今回は、「タイトルのテキストマイニング」として「トピックモデル」を使った分析をやってみようと思います。
分析の目論見として、
といったことがあります。
注意: 分析結果そのものよりも、テキストの前処理の説明が多めです。ですので、「YouTubeデータの探索」というよりは「Exploratoryでトピックモデルを実行する方法の解説」みたいになってしまいました。。。
日本語のような単語と単語の間にスペースがない言語でテキスト分析、特に単語に着目した分析をやりたい場合、文章を単語に分割し、それが名詞なのか動詞なのかを判別する作業が必要です。この作業では内部で辞書の参照が行われています。
Exploratory Desktop 5.5から、「ステップの追加→テキストマイニング」の項目に「文章の単語化(日本語)」という項目が追加されました。この作業を 分かち書き と呼びます。この機能を使うことで、日本語の文章を単語に分け、それぞれの文章にその単語が何回現れたかを数えることができます。さらに、ストップワードのオプションを「日本語」にすることで、「て」「に」「を」「は」などの分析に使いそうもない単語を自動で除去することができます。
以下は、「Comedy」カテゴリーに絞って「文章の単語化(日本語)」を実行した例です(カテゴリによるフィルタを行わないと、メモリエラーになりました)。document_idは、文書ID(ここでは1ビデオに該当)、countはその単語が何回現れたかを示しています。
しかしながら、この結果をそのまま分析に使えるかというと、ちょっと不十分に思えます。理由として、
といったことが挙げられます。
そこで、別の方法として、カスタムスクリプトを使う方法を紹介します。Exploratoryはバックグラウンドで プログラミング言語Rが動いているのですが、ExploratoryではRのコマンドを直接使うことができます。そこで、Rで日本語の形態素解析をする際に非常によく使われるRMeCabパッケージと、固有名詞などの判別に強い辞書ツールのneologdを利用して、スクリプトを書いてみました。
MeCab, RMeCabパッケージ、neologdの導入については付録に回しました。
なお、RMeCabをExploratory上から使った例として、安倍首相に関するTwitterデータをテキストマイニングして可視化してみた(Part1) がありますが、今回は以下の理由で上記のスクリプトを使わず、独自でスクリプトを書いてみました。
以下、RMeCab パッケージとneologd がインストールされている前提でのスクリプトになります。
まず、分かち書きのためのスクリプトを作成します。以下のスクリプトを「スクリプト→スクリプト」からmecab_wakatiといった適当な名前をつけて保存します。このとき 名詞|形容詞|動詞
の箇所を変更することで、任意の品詞を抽出することができます。また、 dic =
の箇所は環境によって異なるため注意が必要です(詳細は付録を参照)。
library(rlang)
#' MeCab を用いた分かち書き
@param ... RMeCabC() 関数と同じ
@param pos 品詞の文字列。複数あるときは | で区切る
mecab_wakati <- function(..., pos = "") {
res <- RMeCab::RMeCabC(...) %>%
unlist() %>%
.[stringr::str_detect(names(.), pos) == TRUE] %>%
stringr::str_c(collapse = " ")
if(length(res) == 0) {
res <- NA_character_
}
return(res)
}
#' mecab_wakatiを用いてmutateする
#' @param col カラム名(文字列)
wakati_df <- function(df, col) {
vec <- df %>% dplyr::pull(col)
df_wakati <- df %>%
dplyr::mutate(
wakati = purrr::map_chr(
vec,
~mecab_wakati(.x, mypref = 1,
dic = "~/mecab-ipadic-neologd/build/mecab-ipadic-2.7.0-20070801-neologd-20200130/mecab-user-dict-seed.20200130.csv.dic",
pos = "名詞|形容詞|動詞"))
) %>%
dplyr::select(-col)
return(df_wakati)
}
続いて、トピックモデルのためのスクリプトを作成します。 以下のスクリプトを「スクリプト→スクリプト」からtopic_modelといった適当な名前をつけて保存します。
library(rlang)
library(magrittr)
build_topic_model <- function(data, document, term, value, k) {
doc <- rlang::sym(document)
ter <- rlang::sym(term)
val <- rlang::sym(value)
dtm <- data %>%
tidytext::cast_dtm(document = !!doc, term = !!ter, value = !!val)
lda <- topicmodels::LDA(dtm, k = k, control = list(seed = 1234))
res <- lda %>%
broom::tidy() %>%
tibble::as_tibble()
return(res)
}
長かったですが、準備ができたので実際のテキスト処理&分析に移ります。今回は、日本の「ComedyまたはEnterrainment」のカテゴリーで分析を行ってみます。
まず、分かち書きを実行します。新規ステップのカスタムRコマンドから、以下を実行します。
続いて、新規ステップのカスタムRコマンドから、以下を実行します。
この時点で、テーブルは以下のようになります。
行数は、ビデオ数×単語数(名詞、動詞、形容詞のみ)です。また、neologdを利用したことで「直撃live」「百田尚樹」などの固有名詞を抽出できていることが分かります。
次に、トピックモデルで分析を行うため、各ビデオタイトルにその単語がいくつ含まれているのかを集計します。ここでは普通にステップで「集計」を選択します。
この時点での出力は以下です。
ビデオIDと単語による集計ができましたが、「!!」や「?」、「だ」などのいわゆるゴミが残ってしまっているので、これを以下の順序で取り除きます。
1については、こちらを参考にしました。
2は、ステップのフィルタで以下のように入力をしました。
str_detect(word, "[:punct:]") == FALSE,
str_detect(word, "[A-z0-9]") == FALSE,
str_detect(word, "[Α-ω]") == FALSE,
str_detect(word, "[А-я]") == FALSE,
str_detect(word, "[ヲ-ン]") == FALSE,
str_count(word) > 1
こうすることで、以下の出力が得られました。
割ときれいに分割できたのではと思います。
続いて、トピックモデルによる分析と考察を行います。 新規ステップのカスタムRコマンドから、以下を実行します。 今回は、トピック数4で実行してみます。
各トピックに対して上位10単語を可視化してみると以下のようになりました。
といった感じでしょうか….? 実データでトピックモデルを実行するときは、解釈に困ることが結構多いです。
動画のタイトルがあまり長くない(単語数が少ない)ため、あまりきれいな結果にはならなかった
トピックモデルはトピック数の選び方にいつも迷う。
→今回はスクリプトを書いたけど、Exploratory上でいろいろオプションを選べて実行できるようになると嬉しいです!(トピックモデルの実装を求む!)
→ネットワーク分析(テキストでは、共起ネットワーク)の実装を求む!
MeCabとRMeCabのインストール方法
https://exploratory.io/note/2ac8ae888097/Mecab-RMeCab-0944283373151109
Exploratoryのステップに使うカスタムコマンドの制限として、データフレームを返さなければいけないという制限があります。今回はその制約上、文字列を入力として分かち書きを実行する関数と、dplyr::mutate
を用いてデータフレームの列にする関数を分けました。
また、本当はステップのRコマンド入力時に辞書や原型を返すかなどのオプションを入力できるようにしたかったのですが、時間の都合上、関数の中にそのオプションを入れてしまいました。