言語処理100本ノック 2015の挑戦記録です。環境はUbuntu 16.04 LTS + Python 3.5.2 :: Anaconda 4.1.1 (64-bit)です。過去のノックの一覧はこちらからどうぞ。
第7章: データベース
artist.json.gzは,オープンな音楽データベースMusicBrainzの中で,アーティストに関するものをJSON形式に変換し,gzip形式で圧縮したファイルである.このファイルには,1アーティストに関する情報が1行にJSON形式で格納されている.JSON形式の概要は以下の通りである.
フィールド 型 内容 例 id ユニーク識別子 整数 20660 gid グローバル識別子 文字列 "ecf9f3a3-35e9-4c58-acaa-e707fba45060" name アーティスト名 文字列 "Oasis" sort_name アーティスト名(辞書順整列用) 文字列 "Oasis" area 活動場所 文字列 "United Kingdom" aliases 別名 辞書オブジェクトのリスト aliases[].name 別名 文字列 "オアシス" aliases[].sort_name 別名(整列用) 文字列 "オアシス" begin 活動開始日 辞書 begin.year 活動開始年 整数 1991 begin.month 活動開始月 整数 begin.date 活動開始日 整数 end 活動終了日 辞書 end.year 活動終了年 整数 2009 end.month 活動終了月 整数 8 end.date 活動終了日 整数 28 tags タグ 辞書オブジェクトのリスト tags[].count タグ付けされた回数 整数 1 tags[].value タグ内容 文字列 "rock" rating レーティング 辞書オブジェクト rating.count レーティングの投票数 整数 13 rating.value レーティングの値(平均値) 整数 86 artist.json.gzのデータをKey-Value-Store (KVS) およびドキュメント志向型データベースに格納・検索することを考える.KVSとしては,LevelDB,Redis,KyotoCabinet等を用いよ.ドキュメント志向型データベースとして,MongoDBを採用したが,CouchDBやRethinkDB等を用いてもよい.
###61. KVSの検索
60で構築したデータベースを用い,特定の(指定された)アーティストの活動場所を取得せよ.
####出来上がったコード:
# coding: utf-8
import re
import leveldb
fname_db = 'test_db'
# keyをnameとidに分解するための正規表現
pattern = re.compile(r'''
^
(.*) # name
\t # 区切り
(\d+) # id
$
''', re.VERBOSE + re.DOTALL)
# LevelDBオープン
db = leveldb.LevelDB(fname_db)
# 条件入力
clue = input('アーティスト名を入力してください--> ')
hit = False
# アーティスト名+'\t'で検索
for key, value in db.RangeIter(key_from=(clue + '\t').encode()):
# keyをnameとidに戻す
match = pattern.match(key.decode())
name = match.group(1)
id = match.group(2)
# 異なるアーティストになったら終了
if name != clue:
break
# 活動場所のチェック、表示
area = value.decode()
if area != '':
print('{}(id:{})の活動場所:{}'.format(name, id, area))
else:
print('{}(id:{})の活動場所は登録されていません'.format(name, id))
hit = True
if not hit:
print('{}は登録されていません'.format(clue))
####実行結果:
アーティスト名を入力してください--> Oasis
Oasis(id:20660)の活動場所:United Kingdom
Oasis(id:286198)の活動場所:United States
Oasis(id:377879)の活動場所:United Kingdom
例になっているOasisは3件該当しました。
アーティスト名を入力してください--> SMAP
SMAP(id:265728)の活動場所:Japan
このデータベースでは、SMAPはまだ日本で活動中です。
アーティストとして登録されていても、活動場所の情報がない場合があります。
アーティスト名を入力してください--> 比嘉真優子
比嘉真優子(id:1075206)の活動場所は登録されていません
比嘉真優子さんは、活動場所が登録されていないアーティストの一覧を眺めていて、たまたま漢字で目立ったので例に使わせていただきました。沖縄出身の方のようです。
なお、アーティストそのものが登録されていない場合は、次のようになります。
アーティスト名を入力してください--> segavvy
segavvyは登録されていません
###LevelDBの検索
keyで探すだけであれば、LevelDB.Get()
でkeyを指定すればvalueが返ってきて終わりなのですが、前問で作ったデータベースは、アーティスト名の重複に対応するため、keyをアーティスト名+'\t'+ユニーク識別子にしています。ユニーク識別子は事前には分からないので、イテレータを取得してチェックする形にしました。
イテレータの取得はLevelDB.RangeIter()
で行いますが、普通に全件取得してチェックしていたらKVSを使うメリットがなくなってしまうため、LevelDBのkeyが常にソートされていることを利用しています。
LevelDB.RangeIter()
はkey_fromでイテレータの開始条件を指定できるので、探したいアーティスト名+'\t'を指定してそれ以降だけをチェックすれば、ユニーク識別子がなんであっても該当アーティストのvalueが直接取得できます。
なお、key_toで終了条件も指定できるのですが、LevelDBのソートロジックが明確には分からず指定方法がうまく思いつかなかったので、今回は指定していません。その代わりに、アーティスト名が目的のものではなくなったらbreakするようにしました。
###表記ゆれ、Unicodeコードポイント
余談ですが、私が好きなフュージョンバンドのT-SQUAREがなぜか登録されていません。
アーティスト名を入力してください--> T-SQUARE
T-SQUAREは登録されていません
不思議に思いちょっとデータを覗いたところ、-
(ハイフン)がいつもの文字(Unicodeコードポイント:45)ではなく別の文字(同:8208)になっていました。その文字で入力すればヒットします。
アーティスト名を入力してください--> T‐SQUARE
T‐SQUARE(id:9707)の活動場所:Japan
こういった表記のゆれは検索漏れの原因になってしまうので、検索の仕組みを作ろうとした場合、なかなか頭の痛いテーマではないかと思います。これまでの問題でも、形態素解析して原型にして扱うとかステミングするとか、いろいろな手法を学んできましたが、他にも大小文字や全半角、異体字や新旧漢字、漢字なのか開く(=ひらがなにする)のか、フルスペルなのか略すのかとか、いろいろありそうです。
ちなみにUnicodeコードポイントは、Pythonではord()
で調べられます。
Unicodeコードポイントって何?という方は、ギークを目指してさんの文字コードの考え方から理解するUnicodeとUTF-8の違いの解説が分かりやすいかと思います。
62本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。
実行結果には、100本ノックで用いるコーパス・データで配布されているデータの一部が含まれます。この第7章で用いているデータのライセンスはクリエイティブ・コモンズ 表示 - 非営利 - 継承 3.0 非移植(日本語訳)です。