RCC 2013年度入会 知能情報学科2回生のRND.cppです.
twitterアカウントは@RND_cppです.
@RND_pngもあります.
初歩的なC++と初心者以下のRubyを書いていることが多いですが,
技術系じゃなくてお絵かき系メインの人間なので大目に見てくださるとありがたいです.
はじめに
私は夏休みに,夏期制作として
2人のtwitterアカウントの最近のツイートから共通する語句を抜き出す
ツイート解析プログラムを制作していました.
今回はそれを拡張してRCC会員のツイートを解析し,
RCC全体の特徴というかRCCのトレンドのようなものを
抽出してみたいと思います.
言語はRubyです.
めかぶ(MeCab)
めかぶとは,オープンソースの形態素解析エンジンMeCabのことです.
導入方法についてはこの記事では詳しく述べませんが,
mecabとmecab-ipadicをインストールして使います.
端末から直接叩くこともできます.
形態素解析エンジン?
形態素解析とは Wikipedia先生曰く
対象言語の文法の知識(文法のルールの集まり)や辞書(品詞等の情報付きの単語リスト)を情報源として用い、自然言語で書かれた文を形態素(Morpheme, おおまかにいえば、言語で意味を持つ最小単位)の列に分割し、それぞれの品詞を判別する作業を指す。
(Wikipedia-形態素解析 から引用)
とのこと.
説明するより実行結果を見たほうが早いと思います.
MeCabで形態素解析すると以下のような情報が得られます.
$ mecab ぱちおくん退院おめでとう ぱちおくん 名詞,一般,*,*,*,*,* 退院 名詞,サ変接続,*,*,*,*,退院,タイイン,タイイン おめでとう 感動詞,*,*,*,*,*,おめでとう,オメデトウ,オメデトー EOS
こんな感じのことをしてくれるのが形態素解析エンジンです.
納豆(Natto)
Nattoは,RubyからMecabを使うためのインタフェースの一つです.
RubyからMecabを使うにはmecab-rubyというのでもできるらしいですが,
Nattoのほうが名前が可愛いので今回はNattoを使います.
導入方法や詳細な使い方はこの記事では詳しく述べませんが,
gem install でnattoを入れたら使えます.
NattoでMeCabを扱う方法は,下のコードにちょっとだけ書いておきます.
# coding: utf-8 require 'natto' nat = Natto::MeCab.new nat.parse("ぱちおくん退院おめでとう") do |word| p "surface:" + word.surface.to_s p "feature:" + word.feature.to_s end
実行結果
$ ruby mecabtest.rb "surface:ぱちおくん" "feature:名詞,一般,*,*,*,*,*" "surface:退院" "feature:名詞,サ変接続,*,*,*,*,退院,タイイン,タイイン" "surface:おめでとう" "feature:感動詞,*,*,*,*,*,おめでとう,オメデトウ,オメデトー" "surface:" "feature:BOS/EOS,*,*,*,*,*,*,*,*"
surface で抽出した形態素自身が, feature でその品詞や分析結果がそれぞれ文字列で得られます.
それ以上の説明は面倒なので割愛します.察して.
ツイート分析(TF-IDF)
今回はTF-IDF法を用いてRCC会員のツイートを分析し,
トレンド的なものを抽出できないかと試みます.
TF-IDF?
TF-IDF法は,文書内での単語の重要度を分析するための方法です.(たぶん)
TF(Term Frequency)…文書内でのその単語の出現頻度
と
DF(Document Frequency)…その単語を含む文書の出現頻度
の2つを用いてその単語がその文書内でどの程度重要性があるかを求めます.
要するに,文書中で何回も登場する単語ほど,他の文書であまり登場しない単語ほど,重要である.
といった考え方に基づいた分析方法です.(たぶん)
ある単語の TF = (文書中のその単語の出現回数)/(文書中の全単語数)
ある単語の DF= (その単語を含む文書の数)/(全ての文書数)
ですね.
この時点でわかると思いますが,TF-IDF法では重要度を分析したい文書だけでなく,「その他の関係ないたくさんの文書の集まり」(ここでは,文書集合と呼びます)を分析する必要があります.
IDF(Inverse Document Frequency)は,DFの逆数,すなわちその単語の珍しさ(出現しにくさ)を表しています.
「今日」という単語は色んな文書中に登場するのでIDFは小さくなります.
一方で「ぱちおくん」という単語(?)はRCC界隈でしか登場しませんのでIDFは大きくなります.
実際には
IDF = log(1/DF)
とか
IDF = log(1/DF)+1
とするみたいです.logは文書全体の数による影響を緩和するために使ってるみたい.
(DF≤1より,log(1/DF)の最小値は0になる.0を重みとして使用したくない場合はlog(1/DF)+1をIDFとして使用する.)
TF(単語の出現頻度に基づいた重要度)とIDF(単語の珍しさに基づいた重要度)をかけることでその単語の文書中での重要度を表すのがTF-IDF法です(間違ってたらゴメンナサイ)
ツイートの取得
RubyでTwitterAPIを扱うには,Ruby Twitter Gem という便利なgemがあります.それを使って特定のアカウントのツイートを200件ずつ取得するコードを書きます.
require 'twitter' module TL extend self @@client = Twitter::REST::Client.new do |config| config.consumer_key = "******" config.consumer_secret = "******" config.access_token = "******" config.access_token_secret = "******" end def get_tweet(user,limit)#引数はUserIDとリクエストを送る回数 results=[] max_id=nil for num in 0...limit begin puts "get_tweet from "+user.to_s if max_id @@client.user_timeline(user,{"count"=>200,"max_id"=>max_id}).each do |t| unless t.text.match(/(^RT|@|http)/) results.push(t.text) end max_id=t.id-1 end else @@client.user_timeline(user,{"count"=>200}).each do |t| unless t.text.match(/(^RT|@|http)/)#URLを含むツイート,リツイート,リプライを除外する results.push(t.text) end max_id=t.id-1 end end Kernel.sleep(5.1)#API上限対策.Application-only authentication使えばこんなに待たなくてもいい rescue => e p e.message end end return results end def get_documents_tweet#Botを大量にフォローして大量にフォロバされてるアカウント「Tweet_TFIDF」を作ったのでそれを利用 results=[] @@client.follower_ids("Tweet_TFIDF").each_with_index do |uid,ind| results.concat(get_tweet(uid,5)) #break if ind > 1 デバッグ用 end return results end def get_group_tweets(screen_name_list)#ユーザーのスクリーンネームが入った配列をわたすとツイートをまとめてくれるメソッド #正直トレンドを出したいならtwitterのリストからツイート取得で良かったと思うんだ. results=[] screen_name_list.each do |screen_name| results.concat(get_tweet(@@client.user(screen_name).id,3)) end return results end end
get_documents_tweetメソッドで使用している「Tweet_TFIDF」は私がBOTをフォローしまくったアカウントです.
放置していたらなんかよくわからないBOTに大量にフォローされていたのでせっかくなので利用してやろうと思い
これらBOTのツイートを文書集合(DFを算出する時につかう文書)に含めることにしました.
BOTのツイートは何かしら偏っていたり,RTが多かったりするので本当は良くないと思いますが他の解決策が思いつきませんでした.
follower_idsではなくfriend_idsを使えばフォロワーではなくフォローの一覧で文書集合を作ることができます.まぁ結局BOTしかフォローしてないので大差ないですが.
TFの算出
今回はわかりやすく2文字以上の名詞だけを抽出しようと思います.幾つものツイートがStringで格納された配列から
それぞれの名詞の数を数え,TFを割り出してハッシュに格納します.
ざっと書くと下のコードみたいな感じです.
module TFIDF #TFとかIDFとかするモジュール(名前は適当) extend self @@nat = Natto::MeCab.new def cnt(documents) word_hash = Hash.new terms_count = 0 documents.each do |e| @@nat.parse(e) do |word| if /[^!-@\[-`{-~ 「」]/ =~ word.surface if (word.feature.match(/(固有名詞|名詞,一般)/)) and (word.surface.length>1)#2文字以上の固有名詞と一般名詞のみ抽出 word_hash[word.surface]||=0 word_hash[word.surface]+=1 terms_count+=1 end end end end word_hash.each{|key,value| word_hash[key] = value.to_f / terms_count } return word_hash end end
TF-IDFの算出
まず,DFを算出するもとになる「単語が含まれる文書の数」を数え上げます.
たぶんTFの算出の時に使用した単語だけを数えた方がいいと思いますが,面倒なので登場する名詞全部数えます(オイ).
文書集合から文書を取り出す→登場する全ての名詞をキーとしたハッシュを作成→作成したハッシュをもとに数え上げる.みたいなかんじ.
#module TFIDFの中 def df_counts(documents)#ある単語が含まれる文書の数を数え上げる df = Hash.new#単語とその単語が登場する文書数を記録するHash documents.each do |e| word_hash = Hash.new @@nat.parse(e) do |word| if (word.feature.match("名詞")) word_hash[word.surface]=1 end end word_hash.each_key do |key| df[key]||=0 df[key]+=1 end end return df end
たぶんもっといい実装方法がいくらでもあると思いますがざっくりとこんな感じです.
TF-IDFの算出はこんな感じ.
#module TFIDFの中 def tfidf(tf,df,d_num)#tf=TF,df=単語をキーとし,その単語が登場する文書数を格納したハッシュ,d_num=全体の文書数 word_hash=Hash.new df_cpy = df.dup tf.each do |key,value| if df_cpy[key] df_cpy[key]=1 if df_cpy[key]==0#文書数が0の時(実際に使うときは文書集合にRCCのツイートを含めているので0にはならないと思う) word_hash[key] = value*(Math.log10(d_num.to_f/df_cpy[key])+1) end end return word_hash end
TF*(log(全体の文書数/単語が含まれる文書数)+1)で計算してます.
いよいよRCC会員のツイートを分析
#moduleの定義は省略 rcc_acounts=[ #RCC会員のアカウント名を羅列 ] rcc_tweets = TL.get_group_tweets(rcc_acounts) tf = TFIDF.cnt(rcc_tweets) documents_set = TL.get_documents_tweet documents_set.concat(rcc_tweets) df = TFIDF.df_counts(documents_set) words = TFIDF.tfidf(tf,df,documents_set.length).sort_by{|key,val| -val} words.each do |key,value| puts ""+key+":"+value.to_s end
$ruby TweetRCC.rb > result.txt
RCC(実行結果)
なお実行結果が出るまでに1時間強かかって糞っぽいです.
実行結果(GoogleDrive)
感じ:0.02565952096957314 これ:0.025401341569093946 自分:0.017377724593458783 is:0.014882230050430326 RCC:0.014191585649419892 gt:0.01410490116943747 guidance:0.013834015655746403 あと:0.01296730662739286 Yonkoma:0.012882232799413008 みんな:0.012341011560737471 やつ:0.011888851099403222 RT:0.01105592118317222 情報:0.011050691762001397 大学:0.010826544595789706 先生:0.010669630837960225 英語:0.010494360545439039 kanjava:0.010225054186510847 ツイート:0.009768806229088295 寿司:0.009646966395637462 それ:0.009432659862264176 ゲーム:0.009159097844847287 なん:0.008965365263996477 kc:0.00882769403468479 サークル:0.00848512950900431 先輩:0.008466574880185467 動画:0.00846268280283059 学校:0.008385315303283042 バイト:0.008316203327496668 トマト:0.0079955414152423 PC:0.00790082318653882 コード:0.007863179020334505 久しぶり:0.007708408615447071 画像:0.007646907240659183 ゼミ:0.007387938792006584 お腹:0.007333269302560361 どこ:0.007271379762993175 アニメ:0.007221120047586259 Twitter:0.0069568889627118005 部室:0.006804332851483256 続き:0.006571555413456901 バス:0.00648897669528573 アプリ:0.006404451983717253 技術:0.00631871024871125 yonkoma:0.006245845231912513 課題:0.006232811961256246 あれ:0.00618211173552463 ここ:0.006111920026521414 単位:0.006106196615963269 京都:0.006102122132052065 コマ:0.006004388604940881
「大学」「英語」「サークル」「ゼミ」「課題」「単位」といった語句を見ると,結構普通の大学生があつまるサークルっぽく見えます.
しかし,「kanjava」「コード」「アプリ」「技術」など見ると,やはり情報系だなぁという感じがしますね.
「寿司」が上位に入っていることから,やっぱり情報系と寿司は切っても切れない縁があるような気がします.
「これ」や「あと」,「やつ」「それ」「ここ」など余計なものが入っているのはやはり文書集合をBOTから生成しているからじゃないかなぁとおもいます.
抽出する名詞の条件をもっと細かく指定すれば除外できるかもしれません.
他に面白かった部分を紹介します.
RCCのアイドルぱちおくん
ぱちおくん(patio-kun)は恐るべきコンテンツ力を持ったRCC会員です.
イラスト,DTM,プログラミング,ギター,歌ってみた,小説などその活動範囲は幅広く,彼自身もコラ画像の素材として数多くのコンテンツを生み出し続けています.
ぱちお:0.00457366529676746
ぱちおくんは88位,RCC会員の名前では一番上に登場しています.
阿部:0.004275571474040004
次が,阿部くんで98位でした.
ちなみに
ぱちおくん:0.0031607594517423835
が167位に入っていました.
「ぱちお」を含む語句は全部で21語はいっており,「ぱちお」はどうやらかなり分散した上での88位のようです.
さすがアイドル…
プログラミング言語とか
Java:0.005517810905020213(59番目) Ruby:0.0023725937288058313(255番目) Unity:0.0023128209813912367(272番目) Haxe:0.002148998364519493(303番目) php:0.0014521023680710786(541番目) js:0.0014521023680710786(548番目) Haskell:0.0007697504962871577(1181番目)
JavaFXが263番目に入ってました.C++は多分単語として認識されなかったんだと思います.
Javaが高い位置にいるのは最近kanjavaとかあったからだと思います.
Rubyが高い位置にいるのはRCC会員が制作したWebアプリ,YonkomaがRubyで組まれていて最近ずっと開発していたからだと思います.
Unityはなんか常に人気がありますね.
Haxeとかphpとかたぶんツイートしてる人1人だけなんだけど…いったいどれだけ呟いたんでしょうか.
Haskellはみんなやりたいと言いつつRCC内ではまだ始めてるひとは少ないみたいですね.
エディタ戦争
vim:0.0016714431724040506(417番目) emacs:0.0010021515240133875(862番目) SublimeText:0.0007697504962871577(1275番目)
これに関しては何も言えません.私はgedit派です.
おまけpatioくんとツイート解析
patioくんのアカウントから,延べ8000件のツイートをもとに解析しました.(個人情報を解析する屑)
最初の解析の際に文書集合としてBOTから取得してきた大量のツイートをローカルでデータベースに保存していたので2回目以降は短い時間で解析できました.
感じ:0.038826417651119174 イラスト:0.03059618987582529 あと:0.026607125281923893 タスク:0.025755621226112555 ギター:0.02506022909922245 GUMI:0.021047992813551446 Yonkoma:0.019521779574939727 自分:0.019242426101834686 みんな:0.01906844195759251 アニメ:0.018898610301805677 これ:0.018795238299285384 エピソード:0.018662681548802382 ボカロ:0.018598127640313617 TL:0.017383129435288712 久しぶり:0.01732512655941398 キャラ:0.01580775705733405 yonkoma:0.014900151724284274 プリキュア:0.014618055290496314 マリオ:0.01450407917737668 情報:0.01429799666352229 pixiv:0.014211720079576263 やつ:0.013975311582912705 localhost:0.013549029440572903 シール:0.01320146504271945 続き:0.012936929366987341 ぁぼした:0.012867469313509596 コマ:0.012710628864704256 阿部:0.012665128965641634 あれ:0.012545763622099847 ぁぼごとに:0.01218159864892639 動画:0.011836232518499695 DTM:0.011809244646544223 RCC:0.011491147696944148 ゲーム:0.01140253957444079 師走:0.01129732772951444 最高:0.011260417336265302 ワン:0.011078964250103292 原稿:0.010986742388635813 ペン:0.010908413018890284 英語:0.010893321605758736 タブ:0.010845433097188736 コミ:0.010795810665649988 漫画:0.010686489275846568 Twitter:0.010651769430130353 ハッカソン:0.010095237973844285
ぱちおくんがどんな人間かなんとなくわかるかなぁと思います.
Yonkoma
抽出結果に何度かyonkomaやらYonkomaというのが出てきていますね.
YonkomaはRCC会員数人で制作した,Twitterのリプライでリレー四コマをまわすWebアプリです.
ぱちおくんも制作チームに入っているプロジェクトです.
私もDBとかModelまわりでちょっとだけコード書いてました(ほとんどサボってた).
ぜひ使ってやってください.
おわりに
今回のツイート解析ですがもともとは
2人のtwitterアカウントをから,2人の共通点を見つけて
話題を提供するプログラムを作ろうと思い立って始めたものでした.
今度はあるツイートが誰のツイートに一番近いかを分類するベイズフィルタ的なものを作って
一番話が合いそうな人を探すプログラムでも作ろうと思います.
参考
TF-IDFで文書内の単語の重み付け-http://blog.takuti.me/2014/01/tf-idf/
自然言語処理 (IT Text)…自然言語処理の教科書
tf-idf – Wikipediahttp://ja.wikipedia.org/wiki/Tf-idf
次の記事はXiPHiAさんです。