ついったーで自分が追加されてるListから、未フォローのアカウントをレコメンドするスクリプトを書いた。
ついったーにリスト機能が追加されましたね。いろいろ賛否両論ありますし、クライアントがまだ対応してないんですが、おもしろい機能だと思います。
というのは、リスト機能は人力によるユーザのタグ付けという側面を持っているからです。
その人がどんな人なのかは、追加されているリストを見ればなんとなくわかるという。
リストのデータから特徴抽出してクラスタリングすれば、より精度の高いユーザのクラスタ分析が可能になるでしょう。
(そのためにはクローリングしてリストのデータを収集する必要がありますが・・・。)
前置きはともかく、表題のとおり自分が追加されてるListから、未フォローのアカウントをレコメンドするスクリプトを書きました。
コード
レコメンドのアルゴリズムはすごく単純で、追加されているリストのフォロワー数を元に標準偏差を算出して、(フォロワー数/標準偏差)をウェイトとしてポイントを算出しています。
多くのリストに追加されているアカウントは、自分と近いクラスタにいるはずだし、フォロワー数が多いリストは信頼の置けるリストである、という根拠に基づいています。
gistに貼っておきます。ダウンロー丼してコンパイルして実行すると、こんな感じで未フォローのアカウントとポイントが出力されます。
yasuc18 : 26.0070 ykhroki : 26.0070 pk0612 : 26.0070 yuroyoro : 19.5053 oja7 : 16.2544 fukunishi : 16.2544 FGtatsuro : 16.2544 ...
実行するにはこうですよ。
$ scalac TwitterListRecommender.scala $ scala TwitterListRecommender <your_id> <password>
で、コードです。簡単ですね。
import java.net.{Authenticator, PasswordAuthentication} import scala.Math._ import scala.xml._ import scala.io.Source object TwitterListRecommender{ val url ="http://twitter.com/%s/lists/memberships.xml" // Usage : scala TwitterListRecommender <your_id> <your_password> def main(args:Array[String]) = { val Array( userid, passwd ) = args // Basic認証 Authenticator.setDefault( new Authenticator { override def getPasswordAuthentication = new PasswordAuthentication( userid, passwd.toCharArray) } ) // 登録されているListを取得する val source = Source.fromURL( url.format( userid) ) val xml = XML.loadString( source.getLines.mkString ) val lists = xml \\ "list" // Listの購読者数から標準偏差を算出 val sd = { val cnts = lists.map{ l => (l \ "subscriber_count" text ).toDouble + 1} val total = (0.0 /: cnts ){ _ + _} val ave = total / lists.size cnts.map{ i => pow( i - ave , 2) }.foldLeft( 0.0 ){ _ + _ } / cnts.size } // followしているidを取得 val ids = { val src = Source.fromURL( "http://twitter.com/friends/ids/%s.xml".format( userid )) val idxml = XML.loadString( src.getLines.mkString ) (idxml \\ "id").map( _ text ) } // Listから集計 ( Map.empty[String, Double] /: lists ){ (m,l) => // (購読者数 + 1)/標準偏差 * 100 がweight val weight = (( l \ "subscriber_count" text ).toDouble + 1) / sd * 100 // Listのmemberを取得する val src = Source.fromURL( "http://twitter.com%s/members.xml".format( l \ "uri" text )) val lxml = XML.loadString( src.getLines.mkString ) // すでにfollowしているIDはのぞく ( lxml \\ "user").filter{ user => ids.exists( _ == (user \ "id" text) ) == false }.foldLeft( m ){ (mm , user) => { val name = user \ "screen_name" text ; mm + ( name -> ( mm.getOrElse( name ,0.0 ) + weight )) } } }.toList.sort{ case( (_, v1), (_, v2) )=> v1 > v2 }.foreach{ case ( name, v ) => println( "%-30s : %.4f ".format( name, v)) } } }