クイックノート

ちょっとした発見・アイデアから知識の発掘を

【R】ブログ記事の特徴を主成分分析により可視化

特定のテーマに絞らないブログを書いていると、
ブログの強みが見えずらくなります。
それは、ブログを読んでくれる方ももちろんですが、
書いている本人ですら、自分のブログの強みが分からなくてなりがちです。

強みを認識することで、
この先さらにその強みを生かすのか、
それとも、他の部分に注力したいのかという、
今後の方針を決めることができます。

個人で書きためたブログなら、
その人の強みが記事に滲み出ているはずで、
テーマを絞らなくとも、
自然とブログ自体に何かしらの特徴が現れていることでしょう。

ここでは、このブログを題材として、
ブログの特徴を抽出・可視化してみたいと思います。

大まかな考え方

それぞれの記事では、
特定のテーマについて掘り下げて書いているはずで、
記事毎に使われる単語のセットが変わってくるはずです。

そのため、記事の単語によって、
その記事が扱うテーマの特徴が表現されていると考えられます。

似たようなテーマを扱った記事は同じような単語が使われ、
異なるテーマを暑かった記事は
単語の出現パターンも違ったものになるでしょう。

つまり、テーマを単語のまとまりとして考えるのです。

そして、記事の単語の出現頻度から、
それぞれの記事がどのテーマに分類されるかを見れば、
ブログのテーマが見えてくるはずです。

準備

ブログ記事を単語に分解するために、
MeCabという形態素解析エンジンを用います。
そのため、MeCabをインストールしておく必要があります。

R では、MeCabのラッパーであるRMeCabライブラリと、
可視化ツールとしてplotlyを用います。

library(RMeCab)
library(plotly)

ブログ記事の形態素解析

まずは、ブログ記事の本文を取得して、
本文を単語に分解して、出現頻度を数えます。

下の関数は、ブログ記事のURL一覧を受け取って、
それぞれのブログの本文を取得した後、 単語毎に分解して、文字の出現頻度をカウントします。

feat_page = function(urls){
  
  texts = sapply(urls,function(url){
    page = read_html(url)
    text = html_nodes(page,xpath="//div[@class='entry-content']//p") %>% html_text()
    paste(text,collapse = "")    
  })
  tmp = t(as.data.frame(RMeCab::docMatrixDF(texts,weight = "tf")))
  rownames(tmp) = sub(paste(url,"entry/",sep=""),"",urls)
  return(tmp)
}

記事のURL一覧はAPIスクレイピングを用いて取得しますが、
ここでは省略します。

URLの一覧を先ほどの関数に与えたら、
記事と単語の出現頻度の表が返ってきます。

urls = c("https://〜〜〜", "https://〜〜〜")
x = feat_page(urls)

主成分分析により単語の組み合わせを抽出

単語の出現頻度をカウントすることで、
記事を数値のデータとして表すことができました。

ところが、一つの記事を単語の数分の数値で表現することになるので、
このブログの場合、6000個の数字で記事の特徴を表現することになります。

数字が二つであれば、そのまま2次元の散布図にして、
記事同士の位置関係をプロットできますが、
6000個の数字となると難しそうです。

はじめに、単語を組み合わせてテーマにすると言ったように、
いくつかの単語の組み合わせを一まとまりと見れば、
テーマAに沿った軸とテーマBに沿った軸でプロットすることで、
全体の外観ができそうです。

そこで、主成分分析を用います。

主成分分析とは

主成分分析とは、多数の変数が存在する場合に、
データの散らばり方をできる限り少数の新たな変数で表現する方法です。

この新たな変数とは元の変数を組み合わせたものとなっており、
今のデータの場合でいえば、単語を組み合わせたテーマに当たります。

ブログの中では、記事毎に特徴がバラついていますが、
どのテーマ軸に沿って記事が特徴の異なる記事があるのか、
つまり、ブログの中で特徴的な記事が現れるのは、
どのテーマなのかを抽出することができます。

主成分分析を実行する

Rでは非常に簡単に主成分分析を実行することができます。

必要なのは、次の一行だけです。

pca = prcomp(x)

主成分分析によって、
どのように単語を組み合わせてテーマとするか、
テーマの軸に沿って記事の特徴を再計算するとどうなるか、
が導かれます。

下の図のPC1,PC2は主成分分析で新しく作られた変数で、
テーマを表しています。
図の中の数値は、テーマの中での単語「!」の組み合わせ方を表しています。
PC1,PC2,PC3,...の順にデータがばらつくテーマを表しており、
より上位のテーマほど、特徴に差が出やすいテーマとなります。

f:id:u874072e:20190823165620p:plain
単語の組み合わせとしてのテーマ

下の図では、テーマに沿った記事の特徴を表しています。

f:id:u874072e:20190823165411p:plain
テーマを軸にした記事の特徴

結果の可視化

数値として結果は出てきましたが、
このままだと分かりづらいので、
可視化しましょう。

主成分分析では、二つの主成分を軸にとってプロットする
バイプロットと呼ばれるプロット方法が一般的です。

関数biplotを使えば可視化できるのですが、
少々見づらいので、plotlyを使って可視化することにします。

plotly_biplot = function(pca,x_pc=1,y_pc=2){
  M = max(pca$x[,c(x_pc,y_pc)])
  m = max(pca$rotation[,c(x_pc,y_pc)])
  top_id1 = order(abs(pca$rotation[,x_pc]),decreasing = T) %>% head
  top_id2 = order(abs(pca$rotation[,y_pc]),decreasing = T) %>% head
  p_id1 = order(pca$rotation[,x_pc]) %>% tail()
  n_id1 = order(pca$rotation[,x_pc]) %>% head()
  p_id2 = order(pca$rotation[,y_pc]) %>% tail()
  n_id2 = order(pca$rotation[,y_pc]) %>% head()
  word_ids = c(p_id1,p_id2,n_id1,n_id2)
  p = plot_ly() %>% 
    add_trace(x=pca$x[,x_pc],y=pca$x[,y_pc],text=rownames(x),hoverinfo="text",mode="markers") %>%
    add_trace(x=pca$rotation[word_ids,x_pc]*M/m,y=pca$rotation[word_ids,y_pc]*M/m,
              text=colnames(x)[word_ids],hoverinfo="text",mode="text",
              textfont = list(color = '#FF0000', size = 12))
  p
}

上の関数はx_pc,y_pcで指定した二つの軸に沿って、
主成分分析の結果をプロットします。
軸を構成する単語も見るために、
主要な単語もプロットするようにしています。

単語のカウントを用いているため、
記事ごとの文字数で大きくバラついているようで、
一つ目の主成分はイマイチ意味のない軸となっていました。

そこで、2つ目と、3つ目に大きい主成分を、
ブログのテーマ軸としてプロットして見ます。

plotly_biplot(pca,2,3)

f:id:u874072e:20190823160123p:plain
分析結果

2軸でプロットしましたが、
グラフを見ると、3つの主なテーマがあるように見えます。

テーマは、主要な単語の組み合わせを見れば何となくわかってきますね。

  • 右下: 暗号などのセキュリティ系テーマ
  • 左下:確率などを扱う統計よりのテーマ
  • 上: 一時保護を扱ったテーマ

これを踏まえると、上下の軸は「社会系と理数系」でテーマを分ける軸で、
左右の軸は「技術系と理論系」のテーマを分ける軸と言えそうです。

まとめ

ブログ記事の単語の出現頻度から、
ブログの中で、どのようなテーマを扱っているかを分類してみました。

主成分分析を使って、
単語の組み合わせからなるテーマ軸を抽出して、
2つの大テーマ軸でざっくりとブログの記事を分類するだけで、
面白いくらい綺麗にテーマを分けることができました。

プライバシーポリシー