LoginSignup
2
1

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-01-16

言語処理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等を用いてもよい.

63. オブジェクトを値に格納したKVS

KVSを用い,アーティスト名(name)からタグと被タグ数(タグ付けされた回数)のリストを検索するためのデータベースを構築せよ.さらに,ここで構築したデータベースを用い,アーティスト名からタグと被タグ数を検索せよ.

出来上がったコード:

main.py
# coding: utf-8
import gzip
import json
import re
import leveldb

fname = 'artist.json.gz'
fname_db = 'test_db'

# keyをnameとidに分解するための正規表現
pattern = re.compile(r'''
    ^
    (.*)    # name
    \t      # 区切り
    (\d+)   # id
    $
    ''', re.VERBOSE + re.DOTALL)

# LevelDBオープン、ない時だけ作成
try:
    db = leveldb.LevelDB(fname_db, error_if_exists=True)

    # gzファイル読み込み、パース
    with gzip.open(fname, 'rt') as data_file:
        for line in data_file:
            data_json = json.loads(line)

            # name+idとtagsをDBへ追加
            key = data_json['name'] + '\t' + str(data_json['id'])
            value = data_json.get('tags')       # tagsはないことがあるのでチェック
            if value is None:
                value = []
            db.Put(key.encode(), json.dumps(value).encode())

    # 確認のため登録件数を表示
    print('{}件登録しました。'.format(len(list(db.RangeIter(include_value=False)))))

except:
    db = leveldb.LevelDB(fname_db)
    print('既存の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

    # タグ情報取得
    tags = json.loads(value.decode())
    print('{}(id:{})のタグ情報:'.format(name, id))
    if len(tags) > 0:
        for tag in tags:
            print('\t{}({})'.format(tag['value'], tag['count']))
    else:
        print('\tタグはありません')
    hit = True

if not hit:
    print('{}は登録されていません'.format(clue))

実行結果:

例になっているOasisはこんな感じでした。

実行結果
921337件登録しました。
アーティスト名を入力してください--> Oasis
Oasis(id:20660)のタグ情報:
    rock(1)
    britpop(3)
    british(4)
    uk(1)
    britannique(1)
    rock and indie(1)
    england(1)
    manchester(1)
Oasis(id:286198)のタグ情報:
    タグはありません
Oasis(id:377879)のタグ情報:
    morning glory(1)
    oasis(1)

なお、DBの登録は手元の環境だと1分弱待たされるので、DBのファイルがある場合は登録処理をスキップするようにしました。スキップした場合は、その旨のメッセージを表示します。

実行結果
既存のDBを使います。
アーティスト名を入力してください--> T‐SQUARE
T‐SQUARE(id:9707)のタグ情報:
    fusion(1)

アーティストが登録されていない場合は、次のようになります。

実行結果
既存のDBを使います。
アーティスト名を入力してください--> segavvy
segavvyは登録されていません

オブジェクトの格納

この問題では、タグと被タグ数をvalueに格納する必要があります。今回はjson形式の文字列にして格納してみました。

json形式からの変換は問題20以降ちょくちょく出てきましたが、json形式へ変換するのは今回初めてかもしれません。これまでjson形式の読み込みで使ってきたjson.loads()の逆に相当するのがjson.dumps()です。今回はこれを使ってタグと被タグ数の情報をvalueに格納しました。

jsonの処理についての詳細はjson — JSON エンコーダおよびデコーダなどを参照してください。

 
64本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。


実行結果には、100本ノックで用いるコーパス・データで配布されているデータの一部が含まれます。この第7章で用いているデータのライセンスはクリエイティブ・コモンズ 表示 - 非営利 - 継承 3.0 非移植日本語訳)です。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1