Ruby による Twitter のデータ・マイニング

Twitter API の興味深い側面

Twitter は、リアルタイムの素晴らしいソーシャル・ネットワーキング・ツールというだけではありません。データ・マイニングの対象にも十分適した情報の宝庫です。Twitter のユーザーたちがさまざまなトピックで生成するツイートは、1 日あたり平均して 14 億件にも上ります。この記事ではデータ・マイニングについて紹介するとともに、オブジェクト指向の Ruby 言語を使用してデータ・マイニングの概念を具体的に説明します。

M. Tim Jones, Independent author

M. Tim JonesM. Tim Jones は組み込みソフトウェアのエンジニアであり、『Artificial Intelligence: A Systems Approach』、『GNU/Linux Application Programming』(現在、第 2 版です) や『AI Application Programming』(こちらも現在、第 2 版です)、それに『BSD Sockets Programming from a Multilanguage Perspective』などの著者でもあります。技術的な経歴は静止軌道衛星用のカーネル開発から、組み込みシステム・アーキテクチャーやネットワーク・プロトコル開発まで、広範にわたっています。彼はコロラド州ロングモン在住で、Intel に勤務するプラットフォーム・アーキテクトであり、執筆活動も行っています。



2011年 11月 04日

2008年 10月、私は多くの人々と同じく、好奇心から Twitter アカウントを作り、このサービスをよく理解するために友だちをフォローし、適当な検索を思い付くまま行ってみました。140 文字で通信するというアイデアは人気を呼びそうにないという印象を得ましたが、私は思いがけない出来事によって Twitter の真の価値を理解するようになりました。

2009年 7月の初め、私が使っている Web ホスティング・プロバイダーのサービスが停止したときのことです。手当たり次第に Web を検索したところ、シアトルの Fisher Plaza で起こった火事が原因だと指摘する情報が見つかりました。従来の Web ベースのソースからの情報はリアルタイムではなく、いつサービスが復旧するのかを知る参考にもなりません。その一方、Twitter を検索してみると、この事故を話題にした個人アカウントが見つかりました。なかには、現場で何が起こっているかをリアルタイムで伝えている情報もあります。例えば、ホスティング・サービスが復旧する少し前に、あるツイートでは建物の外部にディーゼル発電機があることを伝えていました。

私はこの出来事をきっかけに、Twitter の真の威力は個人やグループの間でのオープンでリアルタイムの情報交換ができることであると実感したわけです。しかもそこには、ユーザーの行動に関する情報、そして地域レベルと世界レベルでの動向に関する情報が宝の山となって存在しています。この記事では、Ruby 言語と Twitetr の API ラッパーである Twitter gem を使用した単純なスクリプトを背景に、この情報の宝庫を実際に利用する方法を探ります。さらに、他の Web サービスおよびアプリケーションを使用してデータを視覚化する、単純なマッシュアップを作成する方法を紹介します。

Ruby に関する知識

Ruby という素晴らしい言語についての基礎知識がない方は、「参考文献」セクションで Ruby の資料を調べてください。この記事の例では、Ruby の価値と、大量の機能を限られたライン数のソース・コードにコーディングする Ruby の能力を具体的に説明します。

Twitter と API

初期の頃の Web が目的としていたのは人間とマシンとの対話でしたが、現在の Web が目的とするのは、Web サービスを使用して実現するマシン同士の対話です。このようなサービスは、さまざまな Google サービスから LinkedIn、Facebook、そして Twitter に至るまで、人気の高いほとんどの Web サイトを対象に存在しています。これらの Web サービスでは、外部アプリケーションから Web サイトのコンテンツに対してクエリーを実行したり、操作を行ったりするための API を作成しています。

Web サービスはさまざまなスタイルを使って実装されますが、現在最もよく使用されているスタイルは、REST (Representational State Transfer) です。REST の実装の 1 つはお馴染みの HTTP プロトコル上に実装されており、(GETPUTPOSTDELETE といった標準的な HTTP 操作を使用する) RESTful なアーキテクチャーの媒体として HTTP が存在することを可能にしています。Twitter の API は、この媒体の抽象化として開発されているため、Twitter には REST や、HTTP、あるいは XML や JSON などのデータ・フォーマットの情報はなく、代わりに Ruby 言語に巧みに統合されるオブジェクト・ベースのインターフェースがあるというわけです。


Ruby と Twitter のクイック・ツアー

このセクションでは、Ruby で Twitter API を使用する方法を説明します。まずは、必要なリソースを入手しなければなりません。私と同じく Ubuntu Linux を使用している場合には、apt フレームワークを使用することになります。

最新の完全な Ruby ディストリビューション (ダウンロード・サイズは約 13MB) を入手するには、以下の内容をコマンドラインで実行します。

$ sudo apt-get install ruby1.9.1-full

次に、gem ユーティリティーを使って Twitter gem を取得します。

$ sudo gem install twitter

これで、Ruby で Twitter API を使用するために必要なすべてのものが揃いました。次は、Twitter ラッパーをテストします。この例では、IRB (Interactive Ruby Shell) と呼ばれるシェルを使用します。このシェルでは、Ruby コマンドを実行して Ruby 言語をリアルタイムで試すことができます。IRB には多数の機能が備わっていますが、ここでは IRB を単純な実験のために使用します。

リスト 1 に記載する IRM とのセッションは、読みやすくするために、3 つのセクションに分かれています。最初のセクションの行 001 と 002 では、必要なランタイム要素をインポートして、環境を準備しているだけです (require メソッドが指定のライブラリーをロードして実行します)。次の行 003 に、Twitter gem を使用して IBM developerWorks からの最新ツイートを表示する方法を示しています。この行に示されているように、ツイートを表示するには Client::Timeline モジュールの user_timeline メソッドを使用します。この最初の例で実演しているのは、Ruby の「メソッド・チェーン」機能です。user_timeline メソッドは 20 のツイートからなる配列を返します。この配列を first メソッドにつなぐことで、配列から最初のツイートを抽出します (firstArray クラスのメソッドです)。この 1 つのツイートからテキスト・フィールドを抽出し、それを puts メソッドによって出力に書き出します。

次のセクションの行 004 で使用しているユーザー定義の location フィールドは、有用な位置情報も、有用ではない位置情報も入力できるフリー・フォームのフィールドです。この例で User モジュールが取得するユーザー情報は、この location フィールドによって制約されます。

最後のセクションの行 005 以降では、Twitter::Search モジュールを詳しく調べています。この検索モジュールは、Twitter を検索するための極めてリッチなインターフェースを提供します。この例では、最初に検索インスタンスを作成し (行 005)、次の行 006 でその検索内容を指定します。ここで検索対象としているのは、ユーザー「LulzSec」に送信されたツイートのうち「why」という単語が含まれる最新のツイートです。検索結果のリストは、短く編集してあります。検索は、検索インスタンスは定義されたフィルターを保持するという点で固定的です。これらのフィルターをクリアするには、search.clear を実行します。

リスト 1. IRB での Twitter API の実験
$ irb
irb(main):001:0> require "rubygems"
=> true
irb(main):002:0> require "twitter"
=> true

irb(main):003:0> puts Twitter.user_timeline("developerworks").first.text
dW Twitter is saving #IBM over $600K per month: will #Google+ add to that? > 
http://t.co/HiRwir7 #Tech #webdesign #Socialmedia #webapp #app
=> nil

irb(main):004:0> puts Twitter.user("MTimJones").location
Colorado, USA
=> nil

irb(main):005:0> search = Twitter::Search.new
=> #<Twitter::Search:0xb7437e04 @oauth_token_secret=nil, 
    @endpoint="https://api.twitter.com/1/", 
    @user_agent="Twitter Ruby Gem 1.6.0", 
    @oauth_token=nil, @consumer_secret=nil, 
    @search_endpoint="https://search.twitter.com/", 
    @query={:tude=>[], :q=>[]}, @cache=nil, @gateway=nil, @consumer_key=nil, 
    @proxy=nil, @format=:json, @adapter=:net_http<
irb(main):006:0> search.containing("why").to("LulzSec").
result_type("recent").each do |r| puts r.text end
@LulzSec why not stop posting <bleep> and get a full time job! MYSQLi isn't 
hacking you <bleep>.
...
irb(main):007:0>

続いて、Twitter ユーザーのスキーマを調べてみます。それには IRB を使うこともできますが、Twitter ユーザーの詳細を簡潔に示すために、結果のフォーマットを変更することにします。リスト 2 に、ユーザー構造の出力結果を記載します。この構造は Ruby の Hashie::Mash です。Hashie::Mash が役に立つ理由は、この構造ではオブジェクトにハッシュ・キーへのアクセサー (メソッドのようなもの) を持たせることができるためです (つまり、オープン・オブジェクトなのです)。リスト 2 を見るとわかるように、このオブジェクトには (ジオコード情報を伴う) 現在のユーザー・ステータスをはじめ、豊富な情報 (ユーザー固有の情報とレンダリング情報) が含まれます。ツイートにも同じく大量の情報が含まれていて、user_timeline クラスによってこの情報を生成できることは簡単に想像できるはずです。

リスト 2. Twitter ユーザーの分析 (Ruby の観点)
irb(main):007:0> puts Twitter.user("MTimJones")
<#Hashie::Mash 
  contributors_enabled=false 
  created_at="Wed Oct 08 20:40:53 +0000 2008" 
  default_profile=false default_profile_image=false 
  description="Platform Architect and author (Linux, Embedded, Networking, AI)."
  favourites_count=1 
  follow_request_sent=nil 
  followers_count=148 
  following=nil 
  friends_count=96 
  geo_enabled=true 
  id=16655901 id_str="16655901" 
  is_translator=false 
  lang="en" 
  listed_count=10 
  location="Colorado, USA" 
  name="M. Tim Jones" 
  notifications=nil 
  profile_background_color="1A1B1F" 
  profile_background_image_url="..."
  profile_background_image_url_https="..." 
  profile_background_tile=false 
  profile_image_url="http://a0.twimg.com/profile_images/851508584/bio_mtjones_normal.JPG" 
  profile_image_url_https="..." 
  profile_link_color="2FC2EF" 
  profile_sidebar_border_color="181A1E" profile_sidebar_fill_color="252429" 
  profile_text_color="666666" 
  profile_use_background_image=true 
  protected=false 
  screen_name="MTimJones" 
  show_all_inline_media=false 
  status=<#Hashie::Mash 
    contributors=nil coordinates=nil 
    created_at="Sat Jul 02 02:03:24 +0000 2011" 
    favorited=false 
    geo=nil 
    id=86978247602094080 id_str="86978247602094080" 
    in_reply_to_screen_name="AnonymousIRC" 
    in_reply_to_status_id=nil in_reply_to_status_id_str=nil 
    in_reply_to_user_id=225663702 in_reply_to_user_id_str="225663702" 
    place=<#Hashie::Mash 
      attributes=<#Hashie::Mash> 
      bounding_box=<#Hashie::Mash 
        coordinates=[[[-105.178387, 40.12596], 
                      [-105.034397, 40.12596], 
                      [-105.034397, 40.203495], 
                      [-105.178387, 40.203495]]] 
        type="Polygon"
      > 
      country="United States" country_code="US" 
      full_name="Longmont, CO" 
      id="2736a5db074e8201" 
      name="Longmont" place_type="city" 
      url="http://api.twitter.com/1/geo/id/2736a5db074e8201.json"
    > 
    retweet_count=0 
    retweeted=false 
    source="web" 
    text="@AnonymousIRC @anonymouSabu @LulzSec @atopiary @Anonakomis Practical reading 
          for future reference... LULZ \"Prison 101\" http://t.co/sf8jIH9" truncated=false
  >
  statuses_count=79 
  time_zone="Mountain Time (US & Canada)" 
  url="http://www.mtjones.com" 
  utc_offset=-25200 
  verified=false
>
=> nil
irb(main):008:0>

以上で、クイック・ツアーは終わりです。次は、Ruby と Twitter API を使用してデータを収集して視覚化するために使用できる単純なスクリプトを検討していきます。その過程で、実行回数の制限や認証といった Twitter の概念についても説明します。


Twitter のデータ・マイニング

以降のセクションでは、Twitter API を使用して入手可能なデータを収集して表示するスクリプトをいくつか記載します。これらのスクリプトでは簡潔さに重点が置かれていますが、スクリプトを拡張および組み合わせることで、新しい機能を作成することができます。さらに、このセクションで取り上げる Twitter gem API の機能はほんの一部に過ぎません。この API には他にも多数の機能が用意されています。

注意すべき点として、Twitter API では、クライアントが 1 時間あたりに行える呼び出しの数は限られています。つまり、Twitter ではリクエストの実行回数に制限を設けているので (現在、1 時間あたり最大 150 に制限)、この制限を超えて実行するとエラー・メッセージが表示され、新しいリクエストを送信できるようになるまで待たなければなりません。

ユーザー情報

リスト 2 で見たように、Twitter ユーザーごとに大量のユーザー関連の情報が使用できるようになっていますが、この情報にアクセスできるのは、そのユーザーが保護されていない場合のみです。これから、手際よくユーザーのデータを抽出して表示する方法を調べます。

リスト 3 に記載する単純な Ruby スクリプトは、(ユーザー名を基準に) ユーザーの情報を検索し、そのなかから有用な要素を抽出して出力します。必要に応じて Ruby の to_s メソッドを使用して値をストリングに変換します。重要な点は、ます始めにユーザーが保護されていないことを確認することです。ユーザーが保護されている場合、そのユーザーのデータにはアクセスすることができません。

リスト 3. Twitter ユーザーのデータを抽出する単純なスクリプト (user.rb)
#!/usr/bin/env ruby
require "rubygems"
require "twitter"

screen_name = String.new ARGV[0]

a_user = Twitter.user(screen_name)

if a_user.protected != true

  puts "Username   : " + a_user.screen_name.to_s
  puts "Name       : " + a_user.name
  puts "Id         : " + a_user.id_str
  puts "Location   : " + a_user.location
  puts "User since : " + a_user.created_at.to_s
  puts "Bio        : " + a_user.description.to_s
  puts "Followers  : " + a_user.followers_count.to_s
  puts "Friends    : " + a_user.friends_count.to_s
  puts "Listed Cnt : " + a_user.listed_count.to_s
  puts "Tweet Cnt  : " + a_user.statuses_count.to_s
  puts "Geocoded   : " + a_user.geo_enabled.to_s
  puts "Language   : " + a_user.lang
  if (a_user.url != nil)
    puts "URL        : " + a_user.url.to_s
  end
  if (a_user.time_zone != nil)
    puts "Time Zone  : " + a_user.time_zone
  end
  puts "Verified   : " + a_user.verified.to_s
  puts

  tweet = Twitter.user_timeline(screen_name).first

  puts "Tweet time : " + tweet.created_at
  puts "Tweet ID   : " + tweet.id.to_s
  puts "Tweet text : " + tweet.text

end

このスクリプトを呼び出すには、これが実行可能なスクリプトであることを確認した上で (chmod +x user.rb)、ユーザーを指定して呼び出します。リスト 4 は、ユーザーとして「developerworks」を指定した場合の結果です。この結果には、ユーザーの情報と現在のステータス (最新のツイートの情報) が示されています。Twitter でのフォロワーの定義は、そのユーザーをフォローしている別のユーザーですが、そのユーザーがフォローする相手のユーザーについては、友だちと呼ぶことに注意してください。

リスト 4. user.rb による出力例
$ ./user.rb developerworks
Username   : developerworks
Name       : developerworks
Id         : 16362921
Location   : 
User since : Fri Sep 19 13:10:39 +0000 2008
Bio        : IBM's premier Web site for Java, Android, Linux, Open Source, PHP, Social, 
Cloud Computing, Google, jQuery, and Web developer educational resources
Followers  : 48439
Friends    : 46299
Listed Cnt : 3801
Tweet Cnt  : 9831
Geocoded   : false
Language   : en
URL        : http://bit.ly/EQ7te
Time Zone  : Pacific Time (US & Canada)
Verified   : false

Tweet time : Sun Jul 17 01:04:46 +0000 2011
Tweet ID   : 92399309022167040
Tweet text : dW Twitter is saving #IBM over $600K per month: will #Google+ add to that? > 
http://t.co/HiRwir7 #Tech #webdesign #Socialmedia #webapp #app

友だちの人気

今度は友だち (フォローしている相手) のことを調べてみましょう。友だちのデータを収集して、友だちにどれだけの人気があるかを調べます。それには、友だちを収集して、フォロワー数の順でソートします。そのためのスクリプトは、リスト 5 に記載するとおりの単純なものです。

このスクリプトでは、(ユーザー名を基準に) 分析したいユーザーを把握してから、ユーザー・ハッシュを作成します。Ruby のハッシュ (または、連想配列) とは、(単純な数値インデックスの代わりに) キーを定義してデータを格納できるデータ構造のことです。ハッシュを作成すると、そのハッシュには Twitter のユーザー名でインデックスが付けられ、ユーザーのフォロワー数が値として関連付けられます。このプロセスが友だちごとに単純に繰り返されて、それぞれのフォロワー数がハッシュに格納されます。そして最後にハッシュを (フォロワー数の降順で) ソートして、出力として書き出します。

リスト 5. 友だちの人気を調べるスクリプト (friends.rb)
#!/usr/bin/env ruby
require "rubygems"
require "twitter"
require 'google_chart'

name = String.new ARGV[0]

user = Hash.new

# Iterate friends, hash their followers
friends = Twitter.friend_ids(name)

friends.ids.each do |fid|

  f = Twitter.user(fid)

  # Only iterate if we can see their followers
  if (f.protected.to_s != "true")
    user[f.screen_name.to_s] = f.followers_count
  end

end

user.sort_by {|k,v| -v}.each { |user, count| puts "#{user}, #{count}" }

リスト 6 は、リスト 5 の friends スクリプトによる出力例です。この出力は、スペースを節約するために一部省略されていますが、ご覧のとおり、私が直接フォローしている Twitter ユーザーのなかで人気の高いのは ReadWriteWeb (RWW) と Playstation です。

リスト 6. リスト 5 の friends スクリプトによる画面出力
$ ./friends.rb MTimJones
RWW, 1096862
PlayStation, 1026634
HarvardBiz, 541139
tedtalks, 526886
lifehacker, 146162
wandfc, 121683
AnonymousIRC, 117896
iTunesPodcasts, 82581
adultswim, 76188
forrester, 72945
googleresearch, 66318
Gartner_inc, 57468
developerworks, 48518

フォロワーの位置

リスト 2 に示されているように、Twitter はさまざまな位置情報を提供します。location フィールドはユーザーが定義するフリー・フォームのフィールドで、オプションでジオコーディング・データを定義することができます。その一方、ユーザー定義のタイムゾーンも、フォロワーの実際の位置を知る手掛かりとなります。

これから紹介する例では、Twitter フォロワーのタイムゾーン・データを抽出するマッシュアップを作成し、このデータを、Google Chart を使用してグラフで表示します。Google Chart は、Web 上で各種のグラフ作成を可能にする興味深いプロジェクトです。グラフのタイプとデータを HTTP リクエストとして定義すると、そのレスポンスとして直接ブラウザーにグラフがレンダリングされます。Google Chart を使用するための Ruby gem をインストールするには、以下の内容をコマンドラインで実行します。

$ gem install gchartrb

リスト 7 に、タイムゾーン・データを抽出し、続いて Google Chart リクエストを作成するスクリプトを記載します。これまでのスクリプトとは異なり、このスクリプトを使用するには最初に Twitter で認証されなければなりません。そのためには、アプリケーションを Twitter に登録し、キーとトークンのセットを入手する必要があります。これらのトークンをリスト 7 のスクリプトに適用すれば、データを抽出できるようになります。この簡単な登録プロセスについての詳細は、「参考文献」を参照してください。

このスクリプトは同じようなパターンに従って、あるユーザーのユーザー名を受け入れ、そのユーザーのフォロワーを繰り返し処理します。現行のフォロワーのタイムゾーンが抽出されて、それが tweetlocation ハッシュに格納されます。最初にこのキーがハッシュに含まれているかどうかをテストし、含まれている場合には、キーのカウンターをインクリメントすることに注意してください。また、抽出されたタイムゾーンごとの合計数を記録し、後でパーセンテージを算出できるようにします。

スクリプトの最後の部分では、Google 円グラフの URL を作成します。まず、新規 PieChart を作成し、オプション (サイズ、タイトル、3D にするかどうか) を指定します。次に、タイムゾーン・ハッシュを繰り返し処理して、グラフのデータとして各タイムゾーンのストリング (& 記号を削除) と、そのタイムゾーンが全体に占めるパーセンテージを出力します。

リスト 7. Twitter フォロワーのタイムゾーンを示す円グラフを作成するスクリプト (followers-location.rb)
#!/usr/bin/env ruby
require "rubygems"
require "twitter"
require 'google_chart'

screen_name = String.new ARGV[0]

tweetlocation = Hash.new
timezones = 0.0

# Authenticate
Twitter.configure do |config|
  config.consumer_key = '<consumer_key>'
  config.consumer_secret = '<consumer_secret>'
  config.oauth_token = '<oath_token>'
  config.oauth_token_secret = '<oath_token_secret>'
end

cursor = "-1"

# Loop through all pages
while cursor != 0 do

  # Iterate followers, hash their location
  followers = Twitter.follower_ids(screen_name, :cursor=>cursor)

  followers.ids.each do |fid|

    f = Twitter.user(fid)

    loc = f.time_zone.to_s

    if (loc.length > 0)

      if tweetlocation.has_key?(loc)
        tweetlocation[loc] = tweetlocation[loc] + 1
      else
        tweetlocation[loc] = 1
      end

      timezones = timezones + 1.0

    end

  end

  cursor = followers.next_cursor

end

# Create a pie chart
GoogleChart::PieChart.new('650x350', "Time Zones", false ) do |pc|

  tweetlocation.each do |loc,count|
    pc.data loc.to_s.delete("&"), (count/timezones*100).round
  end

  puts pc.to_url

end

リスト 7 のスクリプトを実行するには、スクリプトに Twitter ユーザー名を指定し、それによって生成された URL をブラウザーにコピー・アンド・ペーストします。リスト 8 にこのプロセスと、結果として生成された URL を記載します。

リスト 8. followers-location スクリプトの呼び出し (この結果は実際には 1 行です)
$ ./followers-location.rb MTimJones
http://chart.apis.google.com/chart?chl=Seoul|Santiago|Paris|Mountain+Time+(US++Canada)|
Madrid|Central+Time+(US++Canada)|Warsaw|Kolkata|London|Pacific+Time+(US++Canada)|
New+Delhi|Pretoria|Quito|Dublin|Moscow|Istanbul|Taipei|Casablanca|Hawaii|Mumbai|
International+Date+Line+West|Tokyo|Ulaan+Bataar|Vienna|Osaka|Alaska|Chennai|Bern|
Brasilia|Eastern+Time+(US++Canada)|Rome|Perth|La+Paz
&chs=650x350&chtt=Time+Zones&chd=s:KDDyKcKDOcKDKDDDDDKDDKDDDDOKK9DDD&cht=p
$

リスト 8 の URL をブラウザーに貼り付けると、図 1 に示す結果が得られます。

図 1. Twitter フォロワーの位置を示す円グラフ
タイムゾーンで表されたフォロワーの国を示す円グラフ

Twitter ユーザーの行動

Twitter の大量のデータをマイニングすることで、ユーザーの行動に関するいくつかの要素もわかります。そのデータ・マイニングの単純な例として、Twitter ユーザーがツイートする時刻の分析、そしてツイートするために使用するアプリケーションの分析の 2 つが挙げられます。以降に記載する 2 つの単純なスクリプトによって、これらの情報を抽出してグラフ化することができます。

リスト 9 に記載するスクリプトは、特定のユーザーによるツイートを繰り返し処理した後 (user_timeline メソッドを使用します)、ツイートごとに、そのツイートが行われた曜日を抽出します。ここでも単純なハッシュを使用して曜日ごとの数を累積し、前のタイムゾーンの例と同じように Google Chart を使用して棒グラフを生成します。ハッシュのデフォルト値を設定することで、ハッシュが未定義の場合の戻り値を指定していることにも注意してください。

リスト 9. ツイートが行われた曜日を示す棒グラフを作成するスクリプト (tweet-days.rb)
#!/usr/bin/env ruby
require "rubygems"
require "twitter"
require "google_chart"

screen_name = String.new ARGV[0]

dayhash = Hash.new

# Initialize to avoid a nil error with GoogleCharts (undefined is zero)
dayhash.default = 0

timeline = Twitter.user_timeline(screen_name, :count => 200 )
timeline.each do |t|

  tweetday = t.created_at.to_s[0..2]

  if dayhash.has_key?(tweetday)
    dayhash[tweetday] = dayhash[tweetday] + 1
  else
    dayhash[tweetday] = 1
  end

end

GoogleChart::BarChart.new('300x200', screen_name, :vertical, false) do |bc|
  bc.data "Sunday", [dayhash["Sun"]], '00000f'
  bc.data "Monday", [dayhash["Mon"]], '0000ff'
  bc.data "Tuesday", [dayhash["Tue"]], '00ff00'
  bc.data "Wednesday", [dayhash["Wed"]], '00ffff'
  bc.data "Thursday", [dayhash["Thu"]], 'ff0000'
  bc.data "Friday", [dayhash["Fri"]], 'ff00ff'
  bc.data "Saturday", [dayhash["Sat"]], 'ffff00'
  puts bc.to_url
end

developerWorks アカウントに対してリスト 9 の tweet-days スクリプトを実行した結果は、図 2 のとおりです。この図に示されているように、developerWorks アカウントの場合、ツイートが最も盛んに行われる傾向がある曜日は水曜日で、最も少ないのは日曜日となっています。

図 2. 曜日ごとのツイート数を相対的に示す棒グラフ
曜日ごとのツイート数を相対的に示す棒グラフ

次に記載するスクリプトは、特定のユーザーがどのサービスからツイートしたのかを判断するものです。ツイートするにはいくつかの方法がありますが、このスクリプトはそのすべてについてコーディングしているわけではありません。リスト 10 に示されているように、同様のパターンで特定のユーザーのユーザー・タイムラインを抽出してから、ツイートする際に使用したサービスの名前をハッシュに格納します。このハッシュは、後で Google Chart で単純な円グラフを作成してデータを視覚化するときに使用します。

リスト 10. あるユーザーがツイートする際に使用したサービスを示す円グラフを作成するスクリプト (tweet-source.rb)
#!/usr/bin/env ruby
require "rubygems"
require "twitter"
require 'google_chart'

screen_name = String.new ARGV[0]

tweetsource = Hash.new

timeline = Twitter.user_timeline(screen_name, :count => 200 )
timeline.each do |t|

  if (t.source.rindex('blackberry')) then
    src = 'Blackberry'
  elsif (t.source.rindex('snaptu')) then
    src = 'Snaptu'
  elsif (t.source.rindex('tweetmeme')) then
    src = 'Tweetmeme'
  elsif (t.source.rindex('android')) then
    src = 'Android'
  elsif (t.source.rindex('LinkedIn')) then
    src = 'LinkedIn'
  elsif (t.source.rindex('twitterfeed')) then
    src = 'Twitterfeed'
  elsif (t.source.rindex('twitter.com')) then
    src = 'Twitter.com'
  else
    src = t.source
  end

  if tweetsource.has_key?(src)
    tweetsource[src] = tweetsource[src] + 1
  else
    tweetsource[src] = 1
  end

end

GoogleChart::PieChart.new('320x200', "Tweet Source", false) do |pc|

  tweetsource.each do|source,count|
    pc.data source.to_s, count
  end

  puts "\nPie Chart"
  puts pc.to_url

end

図 3 に示すのは、ある Twitter ユーザーがツイートする際に使用したサービスの割合を示す興味深い結果をグラフにしたものです。最もよく使用されているのは従来の Twitter Web サイトであり、次いで、携帯電話のアプリケーションがよく使用されています。

図 3. ある Twitter ユーザーがツイートする際に使用したサービスの割合を示す円グラフ
Twitter.com や Web、LinkedIn などの、ツイートする際に使用したサービスの割合を示す円グラフ

フォロワーのグラフ

Twitter は、(多数のノードとエッジで構成される) 1 つのグラフを形作る壮大なユーザー・ネットワークです。これまで紹介したスクリプトで見てきたように、連絡先を繰り返し処理することは簡単です。そしてさらに、連絡先の連絡先を繰り返し処理することも簡単です。こうしたレベルであっても、このような繰り返し処理によって大規模なグラフの基礎が形成されます。

グラフを描画するためのツールとして私が選んだのは、GraphViz というグラフ描画ソフトウェアです。Ubuntu では、以下の内容をコマンドラインで実行することで、簡単にこのツールをインストールすることができます。

$ sudo apt-get install graphviz

リスト 11 に記載するスクリプトは、ユーザーのフォロワーを繰り返し処理し、さらにフォロワーのフォロワーを繰り返し処理します。このパターンの中でこれまでのスクリプトと本質的に異なるところは、GraphViz の dot フォーマット・ファイルを作成する部分のみです。GraphViz は単純なスクリプト・フォーマットを使用してグラフを定義するので、これらのグラフを Twitter ユーザーの列挙の一部として出力します。以下のとおり、単にノードの関係を指定するだけで、グラフを定義することができます。

リスト 11. Twitter フォロワーのグラフを描画するスクリプト (followers-graph.rb)
#!/usr/bin/env ruby
require "rubygems"
require "twitter"
require 'google_chart'

screen_name = String.new ARGV[0]

tweetlocation = Hash.new

# Authenticate
Twitter.configure do |config|
  config.consumer_key = '<consumer_key>'
  config.consumer_secret = '<consumer_secret>'
  config.oauth_token = '<oath_token>'
  config.oauth_token_secret = '<oath_token_secret>'
end

my_file = File.new("graph.dot", "w")

my_file.puts "graph followers {"
my_file.puts "  node [ fontname=Arial, fontsize=6, penwidth=4 ];"

# Get the first page of followers
followers = Twitter.follower_ids(screen_name, :cursor=> -1 )

# Iterate the followers returned in the Array (max 10).
followers.ids[0..[5,followers.ids.length].min].each do |fid|

  f = Twitter.user(fid)

  # Only iterate if we can see their followers
  if (f.protected.to_s != "true")

    my_file.puts "  \"" + screen_name + "\" -- \"" + f.screen_name.to_s + "\""

    # Get the first page of their followers
    followers2 = Twitter.follower_ids(f.screen_name, :cursor => -1 )

    # Iterate the followers returned in the Array (max 10).
    followers2.ids[0..[5,followers2.ids.length].min].each do |fid2|

      f2 = Twitter.user(fid2)

      my_file.puts "    \"" + f.screen_name.to_s + "\" -- \"" +
                    f2.screen_name.to_s + "\""

    end

  end

end

my_file.puts "}"

あるユーザーに対してリスト 11 のスクリプトを実行すると dot ファイルが作成されるので、GraphViz を使用して、その dot ファイルからグラフの画像を生成します。最初に Ruby スクリプトを呼び出してグラフ・データを収集し (graph.dot として保存)、それから GraphViz を使用してグラフの画像を生成します (ここで使用している circo は、円形レイアウトを指定します)。この画像を生成するプロセスは、以下のように定義されます。

$ ./followers-graph.rb MTimJones
$ circo graph.dot -Tpng -o graph.png

図 4 に、生成されたグラフの画像を示します。Twitter グラフは大規模になりがちなので、ここでは列挙するユーザーとそのフォロワーの数を最小限にして (リスト 11 では min 関数を使用することで)、グラフを小さくしていることに注意してください。

図 4. Twitter フォロワー・グラフの例 (ほんの一部)
ネットワーク図のように、接続されたハブとしてフォロワーを示すフォロワー・グラフ

位置情報

ジオロケーションを利用できる場合、Twitter はユーザーとユーザーのツイートに関するジオロケーション・データを収集します。このデータを構成する緯度と経度の情報は、ユーザーの正確な位置、あるいはツイートが送信された正確な位置を突き止めるために使用することができます。この情報は検索にも組み込めるので、定義した位置または自分の位置を基準にして、場所または人を特定することもできます。

(プライバシー上の理由により) すべてのユーザーまたはツイートについてジオロケーションを利用できるというわけではありませんが、この情報によって Twitter エクスペリエンス全体に興味深い側面がもたらされます。そこで、ジオロケーション・データの視覚化を行うスクリプトと、ジオロケーション・データを使って検索を行うスクリプトを紹介します。

最初のスクリプト (リスト 12 を参照) でユーザーから取得するデータは、緯度と経度です (リスト 2 に示されていた境界ボックスを思い出してください)。この境界ボックスは、ユーザーに対して描画する領域を定義する多角形ですが、このデータを単純化して 1 つの点を使用します。このデータにより、単純な HTML ファイル内に単純な JavaScript 関数を生成します。この JavaScript コードが Google マップとインターフェースを取り、(Twitter ユーザーから抽出した緯度と経度のデータを基に) 該当する位置の航空写真による地図を表示します。

リスト 12. ユーザーの地図を作成する Ruby スクリプト (where-am-i.rb)
#!/usr/bin/env ruby
require "rubygems"
require "twitter"
require 'google_chart'

Twitter.configure do |config|
  config.consumer_key = '<consumer_key>'
  config.consumer_secret = '<consumer_secret>'
  config.oauth_token = '<oauth_token>'
  config.oauth_token_secret = '<oauth_token_secret>'
end

screen_name = String.new ARGV[0]

a_user = Twitter.user(screen_name)

if a_user.geo_enabled == true

  long = a_user.status.place.bounding_box.coordinates[0][0][0];
  lat  = a_user.status.place.bounding_box.coordinates[0][0][1];

  my_file = File.new("test.html", "w")

  my_file.puts "<!DOCTYPE html>"
  my_file.puts "<html><head>"
  my_file.puts "<meta name=\"viewport\" content=\"initial-scale=1.0, "
  my_file.puts "user-scalable=no\" />"
  my_file.puts "<style type=\"text/css\">"
  my_file.puts "html { height: 100% }"
  my_file.puts "body { height: 100%; margin: 0px; padding: 0px }"
  my_file.puts "#map_canvas { height: 100% }"
  my_file.puts "<style>"
  my_file.puts "<script type=\"text/javascript\""
  my_file.puts "src=\"http://maps.google.com/maps/api/js?sensor=false\">"
  my_file.puts "<script>"
  my_file.puts "<script type=\"text/javascript\">"
  my_file.puts "function initialize() {"
  my_file.puts "var latlng = new google.maps.LatLng(" + lat.to_s + ", " + long.to_s + ");"
  my_file.puts "var myOptions = {"
  my_file.puts "zoom: 12,"
  my_file.puts "center: latlng,"
  my_file.puts "mapTypeId: google.maps.MapTypeId.HYBRID"
  my_file.puts "};"
  my_file.puts "var map = new google.maps.Map(document.getElementById(\"map_canvas\"),"
  my_file.puts "myOptions);"
  my_file.puts "}"
  my_file.puts "<script>"
  my_file.puts "<head>"
  my_file.puts "<body onload=\"initialize()\">"
  my_file.puts "<div id=\"map_canvas\" style=\"width:100%; height:100%\"<>/div>"
  my_file.puts "<body>"
  my_file.puts "<html>"

else

  puts "no geolocation data available."

end

リスト 12 のスクリプトは、以下のように簡単に実行することができます。

$ ./where-am-i.rb MTimJones

実行した結果として生成された HTML ファイルは以下のようにしてブラウザーでレンダリングされます。

$ firefox test.html

上記のスクリプトは、位置情報を利用できなければ失敗しますが、正常に実行された場合には HTML ファイルが生成されます。ブラウザーはそのファイルを読み取ることで、地図をレンダリングすることができます。図 5 に、ブラウザーでレンダリングされた地図の画像を示します。ここに示されているのは、米国コロラド州北部のFront Range の一部です。

図 5. リスト 12 のスクリプトによってレンダリングされた画像の例
特殊なマーカーやタグのない、航空写真による選択地域の Google マップ

ジオロケーションを使って Twitter を検索し、特定の位置に関連する Twitter ユーザーとツイートを特定することもできます。Twitter Search API では、ジオコーディング情報を使用して検索結果を絞り込めるようになっています。リスト 13 に記載するのは、ユーザーの緯度と経度のデータを抽出し、そのデータを使って、該当する位置の半径 5 マイル以内で送信されたツイートを取得する例です。

リスト 13. 緯度と経度のデータを利用することによる、近くで送信されたツイートの検索 (tweets-local.rb)
#!/usr/bin/env ruby
require "rubygems"
require "twitter"

Twitter.configure do |config|
  config.consumer_key = '<consumer_key>'
  config.consumer_secret = '<consumer_secret>'
  config.oauth_token = '<oauth_token>'
  config.oauth_token_secret = '<oauth_token_secret>'
end

screen_name = String.new ARGV[0]

a_user = Twitter.user(screen_name)

if a_user.geo_enabled == true

  long = a_user.status.place.bounding_box.coordinates[0][0][0]
  lat  = a_user.status.place.bounding_box.coordinates[0][0][1]

  Array tweets = Twitter::Search.new.geocode(lat, long, "5mi").fetch

  tweets.each do |t|

    puts t.from_user + " | " + t.text

  end

end

リスト 13 のスクリプトを実行した結果をリスト 14 に記載します。このリストには、指定された範囲内で頻繁にツイートしているユーザーのものだけが示されています。

リスト 14. 私がいる位置の半径 5 マイル以内で送信されたツイートの表示
$ ./tweets-local.rb MTimJones
Breesesummer | @DaltonOls did he answer u
LongmontRadMon | 60 CPM, 0.4872 uSv/h, 0.6368 uSv/h, 2 time(s) over natural radiation
graelston | on every street there is a memory; a time and place we can never be again.
Breesesummer | #I'minafight with @DaltonOls to see who will marry @TheCodySimpson I will 
marry him!!! :/
_JennieJune_ | ok I'm done, goodnight everyone!
Breesesummer | @DaltonOls same
_JennieJune_ | @sylquejr sleep well!
Breesesummer | @DaltonOls ok let's see what he says
LongmontRadMon | 90 CPM, 0.7308 uSv/h, 0.7864 uSv/h, 2 time(s) over natural radiation
Breesesummer | @TheCodySimpson would u marry me or @DaltonOls
natcapsolutions | RT hlovins: The scientific rebuttal to the silly Forbes release this 
morning: Misdiagnosis of Surface Temperatu... http://bit.ly/nRpLJl
$

さらに詳しく調べてください

この記事では、Ruby 言語を使用して Twitter からデータを抽出する数々の単純なスクリプトを紹介しました。記事では、基本的な概念を説明する単純なスクリプトを作成して提示することに重点を置きましたが、データを抽出する以外にも Twitter API ではさまざまなことが可能です。この Twitter API を使用して、例えば友だちのネットワークを探索し、自分にとって興味深い、最も人気のある Twitter ユーザーを探し出すこともできます。興味深い領域としては、ツイート自体のマイニングも挙げられます。ジオロケーション・データを使用してツイートを検索すれば、位置をベースに行動や出来事 (インフルエンザの大流行など) を理解することができます。この記事では表面をかじっただけに過ぎませんが、皆さん独自のマッシュアップを作って、この記事にコメントをお寄せください。Ruby と Twitter gem を使用すれば、それぞれのデータ・マイニングの必要を満たす有益なマッシアップやダッシュボードを簡単に開発することができます。

参考文献

学ぶために

  • Ruby 言語の公式 Web サイトは、Ruby のニュース、情報、リリース、資料、そして Ruby 言語のコミュニティー・サポートをまとめて調べられる情報源です。Web フレームワーク (Ruby on Rails など) で Ruby がますます使われるようになっていることから、最新のセキュリティー上の弱点とそれに対するソリューションも学べるようになっています。
  • Github social coding サイトに、Twitter gem の公式ソースが用意されています。このサイトで、Ruby Twitter gem のソース、資料、メーリング・リストにアクセスできます。
  • Twitter APIの特定の要素を使用するには、Twitter アプリケーションを登録する必要があります。登録は無料です。登録した後は、この API のさらに有用な要素にアクセスできるようになります。
  • Google Maps JavaScript API チュートリアルでは、Google Maps を使用して、ユーザー提供のジオロケーション・データで各種の地図をレンダリングする方法を説明しています。この記事で使用した JavaScript は、このチュートリアルに記載されている「Hello World」の例に基づいています。
  • developerWorks Open source ゾーンには、オープソースのツールおよびオープンソース技術の使用方法に関する情報が豊富に揃っています。
  • Twitter での developerWorks: developerWorks をフォローしてください。この記事の著者については、M. Tim Jones でフォローできます。
  • developerWorks オンデマンド・デモ: 初心者向けの製品のインストールおよびセットアップから熟練開発者向けの高度な機能に至るまで、さまざまに揃ったデモを見て、学んでください。

製品や技術を入手するために

  • John Nunemaker によって開発された Twitter Ruby gem は、Ruby 言語に巧みに統合する、Twitter サービスへの価値あるインターフェースとなります。
  • Google Chart API は、さまざまなスタイルとオプションを使って複雑でリッチなグラフを作成できる、便利なサービスです。このサービスの API を使って URL を指定すると、Google サイトでグラフがレンダリングされます。
  • Google Chart API Ruby ラッパーは、Google Chart API に対する Ruby インターフェースとなり、Ruby で有効なグラフを作成できるようにします。
  • ご自分に最適な方法で IBM 製品を評価してください。評価の方法としては、製品の試用版をダウンロードすることも、オンラインで製品を試してみることも、クラウド環境で製品を使用することもできます。また、SOA Sandbox では、数時間でサービス指向アーキテクチャーの実装方法を効率的に学ぶことができます。

議論するために

  • developerWorks コミュニティー: ここでは他の developerWorks ユーザーとのつながりを持てる他、開発者が主導するブログ、フォーラム、グループ、ウィキを調べることができます。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source
ArticleID=768322
ArticleTitle=Ruby による Twitter のデータ・マイニング
publish-date=11042011