ブックリスト演算(足し算編)


本棚演算が面白かったのでいじってみました。
とりあえず一番やりたい,任意のISBNから関連する本を出す奴から。

http://myrmecoleon.sytes.net/book/hondanaenzan.php?isbn=4044292019


いまのところAjaxとかは組み込んでないし,速度をみるためにキャッシュさせてないので非常に重いです。*1あくまでテスト版ですね。やってることは本棚演算の「類似本の収集」とほぼ同じ。


作り方


まず下準備として,本棚.orgのユーザー毎のISBNリストを用意する。
今回は本棚演算のダウンロードパッケージに入っている,2005年10月時点の本棚.orgのデータを使用。RubyのMarshalとかいうので入ってたので,扱いやすいようにCSVに整形するのに苦労した(Ruby入れてあれば一発なんだろうに;)。
元々のファイルは ユーザー名["isbn","isbn","isbn"... みたいな感じで配列になっていたけれど,今回はRDBで使おうと思ってたので,

user isbn *2

のかたちに正規化。CSV形式に整形して,サーバで動かしてるMySQLでも同型のテーブルつくって登録。
で,どれくらいあるかなー,とみたら20万レコード超えてるっ!?
すごいなー,1年以上前の時点で下手な図書館より冊数あるよおいw


レコード数みてちょっと不安になったので,試しに用意しておいたSQLクエリをかけてみる。

SELECT isbn, COUNT(a.user) AS user_num FROM db AS a INNER JOIN (SELECT user FROM db WHERE isbn = '{isbn}') AS b ON (a.user = b.user) GROUP BY isbn HAVING isbn <> '{isbn}' ORDER BY user_num DESC;*3

いくつか思いついた本を入れてみると,だいたい1秒くらいで結果が帰る。ちょっと遅いが,まあ許容範囲。ISBNのリクエストを拾って上のSQLを実行するようなスクリプトを作って実験し,動作することを確認。
あとは見栄えのいいように,Amazonの画像が出るようにしたりと小細工。このへんはいろいろ作ってて慣れてきたので最近はすぐ。少し実験して,一度にたくさんの本が見たいってのと,あんまりたくさん表示するとAmazonに拾いにいくのに時間がかかっちゃうのとでジレンマ。とりあえず現状で妥協。調整して,ちょっと反応が欲しかったので公開してみる。

やってること。


根幹は上にもあげたSQL文一本です。

SELECT isbn, COUNT(a.user) AS user_num FROM db AS a INNER JOIN (SELECT user FROM db WHERE isbn = '{isbn}') AS b ON (a.user = b.user) GROUP BY isbn HAVING isbn <> '{isbn}' ORDER BY user_num DESC;


自分もSQLは素人なので詳しくは解説できないけれど,簡単にいえば

(SELECT user FROM db WHERE isbn = '{isbn}')

の部分でその本をもってるユーザーのリストを取得して,

FROM db AS a INNER JOIN (SELECT user FROM db WHERE isbn = '{isbn}') AS b ON (a.user = b.user)

でその本のもってるユーザーの本棚だけを抽出。

GROUP BY isbn HAVING isbn <> '{isbn}'

はその本棚をISBN(つまりタイトル)毎にまとめてって指示。後ろのhavingでは元の本と同じ本は出力しなくていいよ,って指示してる。

SELECT isbn, COUNT(a.user) AS user_num

COUNT(a.user) は,一つの本あたりのユーザー数,つまり指定したユーザーの中でその本をもってる人の人数。これとISBNを最終的に出力するように設定。で最後に,

ORDER BY user_num DESC

で持ってる人が多い順に本を並べさせる。
結果として出てくるのは「ある本をもってる人たちが共通してもってる本の,持ってる人数ランキング」。本棚演算の「類似本の収集」でやってるのとほぼ同じ。


もし,類似本棚検索をやりたい場合は,

SELECT user, COUNT(isbn) AS similar FROM db GROUP BY user HAVING isbn IN '{isbnのリスト}' ORDER BY similar

とかやるといい(指定した本のリストに含まれる本を各人がどれだけもってるかのリストができる)。
単純に共通してもってる本を数えるだけならこれでOK。もっとも,たくさんの本を登録している人は当然類似しやすいので,それを避けて本当によく似た本棚だけを探すなら,共通して持ってる本の冊数をその人の持ってる本の総数で割るとかするべきかも。でもこれだと数冊しか登録してない人の類似度が上がりすぎるジレンマ。このへんは適度な計算式を設定するのが吉。たとえば共通冊数の二乗を総冊数で割るとか如何。


引き算の場合は,

SELECT isbn FROM {引かれる方のISBNリスト} WHERE isbn NOT IN '{引く方のisbnのリスト}'

とかでOKなはず。まあこれはわざわざRDB使わんでも配列の処理でやった方が早いかもしれないけど。
このへんのSQL文もあとで使うので,そのときまた検証してみる予定。


ちなみに繰り返しますが,SQL素人なので「MySQLならこっちのが早いよー」「その式間違ってるよー」ってことはありそうな予感。もしあれば遠慮なく教えてください(ぁ

今後の展望


とりあえずちゃんと使えるものにするにはちょい重いので,一回処理したらキャッシュつくって一定時間はそっち読ませるとかにするつもり。あと最新のデータを取得する機構は欲しい。最新のデータをまとめてもらえたらもっと嬉しいのですがw
あといまはテーブルでは本棚.orgのユーザー名をそのままキーにしちゃってるけど,ユーザー管理のテーブルを別につくって,もっと短いキー(数字連番等)で管理したらもうちょい軽くなるかも。やたら長い名前の人とかいてIndexが大きすぎ。あんまり変わらないかもしれないが。


サービス面では,やはり同じ著者の本とかが非常に目立つので,著者名検索の結果と引算してそれぞれ表示,とかがいいかもしれない。前回書いたクラスタリングっぽい感じ。
あと,本棚.org以外のサービスへの対応も考える。まあそれほど手間はない印象。いまのところ考えてるのはほかに,ブクログはてブ(コレクション),AmazonリストマニアAmazonのおすすめあたり。それと,もしかしたらレビュー系のデータも,レビュアー管理のされてるとこなら似たような処理できるかもとちょっと考え中。Amazonとかmixiとか。


まあとりあえずいま一番欲しいのはマシンパワーですかね(マテ
数年前のPCの流用じゃそろそろキツいかなあ。


おまけ(作ってみて雑感)


類似本検索といいながら,ハルヒで検索してハルヒシリーズの『〜動揺』より上にキノやブギーポップやマリ見てが入るところが興味深い。これと西尾維新秋山瑞人乙一あたりが仲の良いグループ。同時に自分も好きなグループですが。特に,あまり話題にもなってない印象の秋山瑞人の『猫の地球儀』がライトSFまわりで非常によく見かけるのが面白い。いやあれは名作だと思いますけどね。ハルヒはあまり持ってる人が少ない。まあ2005年末だとアニメハルヒ以前だからこんなもんか。
つーかこれ,ラノベ探しにはかなり強いかもしれないな。好きな本で検索して,読んでない本があったら確かに買いかもと思わせる。

*1:ハルヒとかは意外ともってる人が少なかったのでそんなに重くないですが。一番所持数が多かったのはC言語の本でした。

*2:ユーザー*ISBNの形式。あるユーザーが100冊本を登録してたら100冊似たようなレコードが並ぶ。

*3:{isbn}は指定したISBNのこと。あとdbってのは嘘名前です。