元日の頻出名詞とトレンドワード

元日の頻出名詞

元日の頻出名詞を見てみました。さすがにいつもとは違います。

トレンドワードを抽出したい

ともあれ、これらの名詞の頻度は絶対値なので、「RT」とか「こと」とか常に上位に入っている名詞がやっぱり上位にあります。
それよりもトレンドワード的なものが見られたほうが面白そうです。

ある名詞が今日になったらいきなり出現頻度が増えたなんてときに、その名詞を知りたいものです。どうしたらそれがわかるかを考えてみました。もちろんソーシャルデータの扱いにおいて、既存の手法は腐るほどあると思います。でも、こういうのは自分で考えて試してみるっていうのが面白い(ような気がする)わけで。

ある日の名詞Aの頻度が、それ以前の頻度に比べて明らかに多いとき、その多さの比率順に表示する、という仕様にします。「それ以前の頻度に比べて明らかに多い」をどう定義するかですが、2倍なら明らかじゃないけれど3倍なら明らかかとか、10倍なら明らかだけれど9倍は明らかじゃないのかとか、閾値については悩ましいのですが、目的に応じて充分に明らかという値を考えれば良いと思います。
僕は「ある日」の11日前から2日前までの名詞毎に平均頻度を計算して、「ある日」の頻度が平均頻度の25倍以上多ければトレンディ!ってことにしました。25なんて値に意味はないので、必要に応じて変更すれば良いです。なお、「ある日」の1日前の頻度は無視しています。これは名詞の出現数は連続的に増えることを考えると、1日前の頻度については無視したほうが明確な結果が得られるからです。

頻度が1の名詞は省いて扱う

元となる頻度と名詞のデータは、data/count_nouns20120101 というような日ごとのファイルに

頻度\t名詞

というフォーマットで、頻度の多い順に記述されています。

2012年1月1日のデータを見ると、名詞(と判定されたもの)は122,090種類で、その日のファイルは122,090行になっています。そのうち、頻度が1の名詞は70,541種類です。つまり、過半数の種類の名詞はその日の(収集できた)全ツイートで1度しか出現しません。これらをきちんと扱うのもバカらしく、適当に省略して扱うことにします。

ざっとコードを書いてみました。慣れないRubyですが、それでも慣れているC++で書くより早いかもしれません。いつものように汚いコードです。

#!/usr//bin/ruby

require 'date'

def loadDayNouns2(fn, noun_freq)
  #p fn
  if infile = open(fn)
    while line = infile.gets
      freq_noun = line.split("\t")
      break if (freq_noun[0].to_i == 1) #freq==1は省く
      noun_freq[freq_noun[1].chomp] = freq_noun[0].to_i
    end
    infile.close
  end
end

def dayStr(day)
  day.to_s.gsub("-", "")
end

def loadDayNouns1(day, hash)
  day_str = dayStr(day)
  filename = "data/count_nouns" + day_str
  loadDayNouns2(filename, hash)
end

def aveFreq(noun, noun_freq_array)
  freq_sum = 0.0
  noun_freq_array.each {|noun_freq|
    freq_sum += noun_freq[noun].to_i #to_i is required?
  }
  ave = freq_sum / noun_freq_array.size
  return ave < 1.0 ? 1.0 : ave
end

daybasis = ARGV[0] ? Date.strptime(ARGV[0], "%Y%m%d") : (Date::today - 1)

noun_freq_array = Array::new
d = 11
while d >= 2
  noun_freq_array.push(Hash::new)
  loadDayNouns1(daybasis - d, noun_freq_array.last)
  d -= 1
end

daybasis_nouns = Hash::new
loadDayNouns1(daybasis, daybasis_nouns)

trend_nouns = Hash::new

GRAD_THRESHOLD = 25.0
daybasis_nouns.each_pair {|noun, freq|
  if freq > GRAD_THRESHOLD
    ave = aveFreq(noun, noun_freq_array);
    trend_nouns[noun] = freq/ave if ave * GRAD_THRESHOLD <= freq
  end
}

trend_nouns.sort_by{|noun, grad| -grad}.each do |noun, grad|
  puts sprintf("%s\t%8.1f", noun, grad)
end

「freq_sum += noun_freq[noun].to_i」という行があるのですが、ここでto_iが無いとエラーでした。なぜここにto_iが必要なのかわからないのですが、付けてあります。

trend1.rbというファイル名にしました。引数なしで実行すると昨日のトレンドワードを抽出します。「20120101」のような引数を付けて実行すると、その日(この場合は2012年1月1日)のトレンドワードを抽出します。

元日のトレンドワード

ってことで実行してみました。そんなに速くないコンピュータですが7〜8秒でした。名詞と頻度の関係をすべてメモリに読み込んで数万件のハッシュを何個も作ってそれを扱っています。そこそこ時間がかかるかなと思いましたが、予想外に速かったです。でも、せめてあと5倍くらい速くなればwebインタフェースから呼び出せるようにしてもいいんですけどねえ。

$ ./trend1.rb 20120101
争事	   556.0
出頭	   458.2
[ 	   369.0
 ]	   283.0
鳥島	   275.0
(&#9685;	   191.0
オウム真理教	   174.0
謹賀	   138.4
大儲け	   129.0
・:*:・。♪☆	   126.0
&#9829;	   121.9
蓼	   110.0
大損	   109.0
賭け事	   108.0
学業	   106.3
初夢	   105.1
大凶	    99.1
&#10039;(	    92.0
大吉	    87.9
小吉	    87.4
Gackt	    83.3
朝生	    81.8
&#8245;)	    75.6
あけ	    74.7
HIRO	    73.6
ゃんあけおめ	    66.0
大河内	    65.0
ベイベー	    63.0
格付け	    61.9
アケオメ	    61.0
近海	    60.7
年初	    60.3
里谷	    59.0
本年	    58.5
屠蘇	    57.0
^)/&#10024;	    56.0
YEAR	    55.1
川崎大師	    54.5
上戸	    54.4
吹豪	    53.0
書初め	    53.0
ハス	    52.0
末吉	    51.7
苦汁	    51.0
ヱヴァンゲリヲン	    50.6
袢袢	    50.0
メヌ	    49.0
(´&#9763;	    49.0
辛酸	    48.0
ピカル	    47.0
イタミン	    47.0
エイトレンジャー	    46.0
名塚	    46.0
CDTV	    45.0
&#9556;&#9559;&#9556;&#9559;	    44.0
16197	    44.0
運勢	    43.6
中通り	    43.3
343343	    43.0
幸先	    42.5
初詣	    41.3
初日の出	    41.1
アンダルシア	    41.1
120101	    41.0
マジカルバナナ	    41.0
ベイベ	    40.8
ガクト	    40.8
梅宮	    40.0
貞治	    40.0
Year	    39.2
猪苗代湖	    39.2
年頭	    39.0
QE	    39.0
ガックン	    39.0
宙ぶらりん	    38.0
雑煮	    37.6
富澤	    37.0
オウム	    36.6
口々	    36.0
白也	    36.0
ペケポン	    36.0
心眼	    36.0
願望	    35.4
TIGERandBUNNY	    35.0
あっけ	    35.0
111231	    35.0
久喜	    34.4
震度	    34.4
ーベイ	    34.0
孝之	    33.8
フットンダ	    33.3
田崎	    33.3
ジャニーズカウントダウン	    33.1
年男	    33.1
コトヨロ	    33.0
&#9673;∀&#9673;	    33.0
 &#9684;	    33.0
ペッタン	    32.2
GACKT	    32.1
栄作	    32.0
凶	    31.8
お神酒	    31.0
平田	    30.0
アケオメ	    30.0
護国	    30.0
芹沢	    30.0
昨年	    29.4
ニューイヤー	    29.3
籤	    29.1
センタ	    29.0
慶び	    29.0
Yell	    29.0
年女	    28.9
堤下	    28.0
佳織	    28.0
ご来光	    28.0
year	    27.5
ラーガン	    27.1
CDTV	    27.0
フレキス	    27.0
HappyNewYear	    26.0
アビリティレベル	    26.0
ドロンパ	    26.0
由紀子	    26.0
待人	    26.0
詣で	    26.0
5088	    26.0
明夫	    25.8
賀正	    25.7
1102	    25.5

はてなダイアリーEUCでハンドリングされているため、正しく表示できていないものがあります。

本当は除去すべき記号やらワケのわからん数字もありますが、やはり単純な頻度ランキングよりも面白いです。「争事」なんていうことばがあったので、なんのこっちゃと思ったのですが、おみくじ用語なのですね。他にもおみくじ用語が多いようです。そして、たとえば「5088」なんていう数は、非公式リツイートの多かった「バルス=秒間2万5088ツイート」という文から抽出されたものだったりします。