Edited at

素人の言語処理100本ノック:61

More than 1 year has passed since last update.

言語処理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で構築したデータベースを用い,特定の(指定された)アーティストの活動場所を取得せよ.



出来上がったコード:


main.py

# 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 非移植日本語訳)です。