著作一覧 |
先日、オライリーの瀧澤さんにいただいて、電車の中で読んでいたのだが、これはおもしろい。おもしろい理由は簡単で、おれが全然知らないことがたくさん出ていて、かつ、読めば理屈がわかるからだ(というか、知らないはずは無い気もするのだが、知らないのだった)。
集合知プログラミング(Toby Segaran)表紙には「スマートなWeb 2.0アプリケーションを構築するために」とか書いてあるので、SNSでも作る本かといえば、そうではなく、でもないかな、単純に言ってしまえば、傾向分析というか、傾向を数値化するための方法を実装ベースで紹介する本ということになる。説明がやたらとうまいので、身に付くかどうかはともかく、妙に理解した気分になる。
たとえば、人と、その人が観た映画の5点満点の星取り表がある。そこから、自分の嗜好に近い人が高い評価をつけてかつ自分が観ていない映画のお勧めリストを取る方法とかだ。
で、実際に試してみたくなるのだが、わざわざPythonの処理系を入れるほど(著者はPythonが好きらしく、なぜPythonでサンプルを書くのか理由を書いていたりするのだが、結局、この人がPythonに慣れているのだな、ということはわかった)、Rubyと違うようにも見えないし(リストコンプリヘンション――綴り調べないとわからない――はちょっと違うけど)、最初の20ページ分くらい(デリシャスの前の映画のところ)のをRubyに直したソースがあるので公開してみたりして。下のpreで囲ってあるのは、本文の対話型で実行したのと同じ個所をirbで実行した例。
P.8-9
recommendations.pyの代わりに定数Criticsにハッシュを設定したrecommendations.rb(Recommendationsモジュール)を利用する。
irb(main):001:0> require 'recommendations' => true irb(main):002:0> include Recommendations => Object irb(main):003:0> Critics['Lisa Rose']['Lady in the Water'] => 2.5 irb(main):004:0> Critics['Toby']['Snakes on a plane']=4.5 => 4.5 irb(main):005:0> Critics['Toby'] => {"Snakes on a Plane"=>4.5, "Snakes on a plane"=>4.5, "Superman Returns"=>4.0, "You, Me and Dupree"=>1.0}
P.10
powの代わりに**を利用する。
irb(main):012:0> Math.sqrt((5-4)**2+(4-1)**2) => 3.16227766016838 irb(main):014:0> 1/(1+Math.sqrt((5-4)**2 + (4-1)**2)) => 0.240253073352042
P.11
sim_distanceはRecommendationsのモジュールメソッドとして定義
irb(main):001:0> require 'recommendations' => true irb(main):002:0> Recommendations.sim_distance(Recommendations::Critics, 'Lisa Ro se', 'Gene Seymour') => 0.148148148148148 irb(main):003:0>
P.13-14
sim_pearsonはRecommendationsのモジュールメソッドとして定義
irb(main):001:0> require 'recommendations' => true irb(main):002:0> Recommendations.sim_pearson(Recommendations::Critics, 'Lisa Ros e', 'Gene Seymour') => 0.39605901719067
P15
topMatchesは、top_matchesという名前でRecommendationsのモジュールメソッドとして定義
irb(main):001:0> require 'recommendations' => true irb(main):002:0> Recommendations.top_matches(Recommendations::Critics, 'Toby', 3 ) => [[0.99124070716193, "Lisa Rose"], [0.924473451641905, "Mick LaSalle"], [0.893 405147441565, "Claudia Puig"]]
P.17
getRecommendationsは、get_recommendationsという名前でRecommendationsのモジュールメソッドとして定義
irb(main):001:0> require 'recommendations' => true irb(main):002:0> Recommendations.get_recommendations(Recommendations::Critics, ' Toby') => [[3.3477895267131, "The Night Listener"], [2.83254991826416, "Lady in the Wat er"], [2.53098070376556, "Just My Luck"]] irb(main):003:0> Recommendations.get_recommendations(Recommendations::Critics, ' Toby', Recommendations.method(:sim_distance)) => [[3.50024784014159, "The Night Listener"], [2.75612429399594, "Lady in the Wa ter"], [2.46198848607437, "Just My Luck"]]
P.19
transformPrefsは、transform_prefsという名前でRecommendationsのモジュールメソッドとして定義
irb(main):001:0> require 'recommendations' => true irb(main):002:0> movies = Recommendations.transform_prefs(Recommendations::Criti cs) => {"Snakes on a Plane"=>{"Michael Phillips"=>3.0, "Jack Mattews"=>4.0, "Claudia (snip) Seymour"=>3.5, "Lisa Rose"=>2.5, "Toby"=>1.0}} irb(main):003:0> Recommendations.top_matches(movies, 'Superman Returns') => [[0.657951694959769, "You, Me and Dupree"], [0.487950036474269, "Lady in the Water"], [0.111803398874989, "Snakes on a Plane"], [-0.179847194799054, "The Nig ht Listener"], [-0.422890031611031, "Just My Luck"]] irb(main):004:0> Recommendations.get_recommendations(movies, 'Just My Luck') => [[4.0, "Michael Phillips"], [3.0, "Jack Mattews"]]追記:はてぶのおかげで、翻訳が出る前にma2さんがいろいろ試したエントリーが上がってきて、参考になった。というか、既に先人が通ったところだったり(まあ、この場合はEnumerableをいろいろ使うという目的があるので、通っていようがいまいがどうでも良いのだけど)。『Programming Collective Intelligence』。で、それよりこっちが素晴らしい。『"Collective Intelligence"のサンプルを,はてなに対応させてみた』。参考になります(現時点では試してないので、今もはてなで使えるかは別問題だけど)。
hash = {} hash.default = {} ['a', 'b', 'c'].each do |i| ['0', '1', '2'].each do |j| hash[j][i] = 0 end endこれ、こうなるのだな。
irb(main):001:0> hash = {} => {} irb(main):002:0> hash.default = {} => {} irb(main):003:0> ['a', 'b', 'c'].each do |i| irb(main):004:1* ['0', '1', '2'].each do |j| irb(main):005:2* hash[j][i] = 0 irb(main):006:2> end irb(main):007:1> end => ["a", "b", "c"] irb(main):008:0> hash => {}
でも、hash[j][i]の直後にp hash[j]するとちゃんと入っているので、hash[j]はできているように見えるんだけど。なぜ、こうなるのかわからない。
たぶん、存在しないキーを指定されると、default値を返すのであって、キーに対応するエントリーを作るわけではない、ということなのだろう。
追記:というか、Hashの落とし穴に、default属性については出ていないけど、「Hash.new(val) 」のvalについて、単に返しているだけだと書いてあった。
scores = prefs.keys.inject([]) do |r, other| if person == other r else r << [similarity.call(prefs, person, other), other] end endこれ、最初、
scores = prefs.keys.inject([]) do |r, other| r << [similarity.call(prefs, person, other), other] if person != other end
と書いていたのであった。elseの場合はnilになって、それがrに入るからさあ大変。しかし、その前の同じような処理では常に条件が真になる例しか動かさないから、戸惑ったのだった。というわけで、必ず1度は偽になるループならばれるから良いのだが、たまたま真になるパターンしか試さなかったりするとまずいね。
追記:というか、倒置しなきゃならないわけじゃないんだから、(person != other) ? r << [similarity.call(prefs, person, other), other] : r
と書けばよいのか。?:式の値は最終評価値なんだから、このほうが良いな。
ジェズイットを見習え |
hash = Hash.new {|h, k| h[k] = {}}ですかねえ
>リストコンプリヘンション<br>これ、本文中の表記がこれだったんですか?<br>英語表記は List comprehension ですが、リスト内包表記という訳語が<br>それなりに定着していると思うんですが。<br><br>http://en.wikipedia.org/wiki/List_comprehension<br><br>http://www.google.co.jp/search?q=%E3%83%AA%E3%82%B9%E3%83%88%E5%86%85%E5%8C%85%E8%A1%A8%E8%A8%98&ie=UTF-8
本文中には別に書いてないですね。Pythonお勧めの理由には「リスト内包」がある、とは書いてありますけど。
ようは、僕が、頭の中にリストコンプリヘンションを音で持っているので、それを書いただけです。
咳さん、それいいですね。>hash = Hash.new {|h, k| h[k] = {}}<br>それを利用するように修正しました。
ちなみに、hash.default の方が育ってますね。<br>>> hash.default<br>=> {"a"=>0, "b"=>0, "c"=>0}
確かにそうでした。>育つ<br>落とし穴解説(追記でリンク張った)にはfreezeしておけ、と書いてありますね(この場合は、それ以前の問題でしたが)。
#inject をこの手のイディオムに用いるときは、#<< (や条件式)の戻り値に r を期待(あるいは強要)するより、行を改めて r を付記(あるいは式を改めて ;r )する習慣をつけるほうがベターではないかな…と思います。
うーん、そうかなぁ。1つの式で収まるのならば1つの式で書いたほうが良くありませんかね、といっても1つの式の気分的な範囲が問題かな。2項演算子が2個以内、代入演算子が2個以内、比較演算子が2個以内くらい。<br>最後にrと書いておくのがベターっていうのは、僕には"……\0"と書いておくのがベターって言っているように聞こえるけど。
と、はまったとか書いた野郎が言っても説得力ないし。安全なイディオムとしては、その通りですね。