2018年08月29日

言語処理100本ノックでPython入門 #68 - MongoDBでソート


今日は、言語処理100本ノック 2015の第7章・問題68に挑戦です。


■ 問題
68. ソート
"dance"というタグを付与されたアーティストの中でレーティングの投票数が多いアーティスト・トップ10を求めよ.

■ Pythonのコード
import json
import pymongo

def findDance():
    client = pymongo.MongoClient('localhost', 27017)
    db = client['MusicBrainzDb']
    co = db['artists']
    return co.find({'tags.value': 'dance'}).sort('rating.count', pymongo.DESCENDING)

def main():
    for d in findDance()[0:10]:
        print(f'{} - {}'.format(d['name'], d['rating']['count']))
        #print(f'{d['name']} - {d['rating']['count']}')

if __name__ == '__main__':
    main()

■ ちょと説明

まずは、検索ですが、以下のようにオブジェクトを引数に渡して検索することができます。

co.find({'tags.value': 'dance'})
面白いですね。
そして、以下のように、ドットで繋げてsortすることができます。
co.find({'tags.value': 'dance'}).sort('rating.count', pymongo.DESCENDING)

pymongo.DESCENDINGを指定しているので、降順にソートしています。

取得したオブジェクトの各値にアクセスするには、辞書型なので、d['name']のように書けば、値を取得できます。階層構造になっている場合は、

d['rating']['count'])

のようにアクセスします。

これを使って、結果を以下のコードで画面に表示しています。

print('{} - {}'.format(d['name'], d['rating']['count']))

■ 結果
Madonna - 26
Bjork - 23
The Prodigy - 23
Rihanna - 15
Britney Spears - 13
Maroon 5 - 11
Adam Lambert - 7
Fatboy Slim - 7
Basement Jaxx - 6
Cornershop - 5
  

Posted by gushwell at 22:00Comments(0)

2018年08月26日

言語処理100本ノックでPython入門 #67 - MongoDBで複数ドキュメントの取得


今日は、言語処理100本ノック 2015の第7章・問題67を解きます。

■ 問題
67. 複数のドキュメントの取得
特定の(指定した)別名を持つアーティストを検索せよ.

■ Pythonのコード

import json
import pymongo
from bson.objectid import ObjectId

def ObjectIdToStr(o):
    if isinstance(o, ObjectId):
        return str(o)
    raise TypeError(repr(o) + " is not sported")

def findAlias(alias):
    client = pymongo.MongoClient('localhost', 27017)
    db = client['MusicBrainzDb']
    co = db['artists']
    return co.find({'aliases.name': alias})

def printArtist(artist):
    print('')
    print(json.dumps(artist, indent=4, ensure_ascii=False, sort_keys=True, default=ObjectIdToStr))

def main():
    alias = input('=>')
    for d in findAlias(alias):
        printArtist(d)

if __name__ == '__main__':
    main()

■ ちょと説明

ireturn co.find({'aliases.name': alias})
で検索結果を返しています。このメソッドは、条件に合うデータが複数あれば、複数のアーティストのオブジェクトが返ります。 

これをprintArtist関数で表示しています。MongoDBから取得したデータはjsonなので、json.dumpsで整形させて表示させています。


■ 実行結果

実行例では、取得したデータは一つだけですが、入力した別名と一致するアーティストが複数いれば、複数のアーティストのデータが表示されます。
=>サザン

{
    "_id": "5a653c60323575566c9d9167",
    "aliases": [
        {
            "name": "Southern All Stars",
            "sort_name": "Southern All Stars"
        },
        {
            "name": "SAS .",
            "sort_name": "SAS ."
        },
        {
            "name": "サザン",
            "sort_name": "サザン"
        }
    ],
    "area": "Japan",
    "begin": {
        "year": 1975
    },
    "ended": true,
    "gid": "24d9e513-c215-4901-87ee-b66aa769ab9d",
    "id": 9549,
    "name": "サザンオールスターズ",
    "rating": {
        "count": 2,
        "value": 70
    },
    "sort_name": "Southern All Stars",
    "tags": [
        {
            "count": 1,
            "value": "japanese"
        },
        {
            "count": 1,
            "value": "asian"
        },
        {
            "count": 1,
            "value": "fixme label mess"
        }
    ],
    "type": "Group"
}
  
Posted by gushwell at 22:00Comments(0)

2018年08月22日

言語処理100本ノックでPython入門 #66 - MongoDBで検索件数の取得


今日は、言語処理100本ノック 2015の第7章・問題66を解きます。

■ 問題
66. 検索件数の取得
MongoDBのインタラクティブシェルを用いて,活動場所が「Japan」となっているアーティスト数を求めよ.

■ Pythonのコード
import pymongo

def countJapan():
    client = pymongo.MongoClient('localhost', 27017)
    db = client['MusicBrainzDb']
    co = db['artists']
    count = co.find({'area': 'Japan'}).count()
    print(count)

def main():
    countJapan()

if __name__ == '__main__':
    main()

■ ほんの少し説明

今回は簡単でした。単に、findメソッドと、countメソッド使うだけでした。
count = co.find({'area': 'Japan'}).count()

のように、ドットで連結できる。ちょとC#のLINQっぽい。


■ 実行結果
22821
  
Posted by gushwell at 21:30Comments(0)

2018年08月19日

言語処理100本ノックでPython入門 #65 - MongoDBの検索


今日は、言語処理100本ノック 2015の第7章・問題65を解きます。


■ 問題
65. MongoDBの検索
MongoDBのインタラクティブシェルを用いて,"Queen"というアーティストに関する情報を取得せよ.さらに,これと同様の処理を行うプログラムを実装せよ.
「MongoDBのインタラクティブシェルを用いて」という部分は省略、後半のプログラムを作成するほうだけ考えます。

■ Pythonのコード
import json
import pymongo
from bson.objectid import ObjectId

def ObjectIdToStr(o):
    if isinstance(o, ObjectId):
        return str(o)
    raise TypeError(repr(o) + " is not suported")

def find(name):
    client = pymongo.MongoClient('localhost', 27017)
    db = client['MusicBrainzDb']
    co = db['artists']
    return co.find({'name': name})

def printArtist(artist):
    print('')
    print(json.dumps(artist, indent=4, ensure_ascii=False, sort_keys=True, default=ObjectIdToStr))

def main():
    for d in find('Queen'):
        printArtist(d)

if __name__ == '__main__':
    main()

■ 簡単に説明


find メソッドを使うと、該当するデータを(複数ある場合はすべて)取得できます。この結果をforで回しています。

取得したデータはJSON形式です。これをdumpメソッドで文字列にして表示しています。この時、
json.dumps(d, indent=4, ensure_ascii=False, sort_keys=True, default=ObjectIdToStr)
と、パラメータでensure_ascii=False としないと、以下のように
    "aliases": [
        {
            "name": "\u5973\u738b",
            "sort_name": "\u5973\u738b"
        }
    ],
漢字部分が、"\u5973"といった形式でエンコードされてしまいます。

それと、ObjectId()は、そのままではdumpsできないので、default= で変換ルーチンを登録しています。 これをやらないと、以下の例外が発生してしまいます。
TypeError: Object of type 'ObjectId' is not JSON serializable

ちなみに、ObjectIdToStr関数の中で使っている repr()組み込み関数は、オブジェクトの印字可能な表現を含む文字列を返します。

■ 結果
{
    "_id": "5b73dc59b520d9c8477aafe2",
    "aliases": [
        {
            "name": "Queen",
            "sort_name": "Queen"
        }
    ],
    "area": "Japan",
    "ended": true,
    "gender": "Female",
    "gid": "420ca290-76c5-41af-999e-564d7c71f1a7",
    "id": 701492,
    "name": "Queen",
    "sort_name": "Queen",
    "tags": [
        {
            "count": 1,
            "value": "kamen rider w"
        },
        {
            "count": 1,
            "value": "related-akb48"
        }
    ],
    "type": "Character"
}

{
    "_id": "5b73dcd0b520d9c8477b768e",
    "aliases": [
        {
            "name": "女王",
            "sort_name": "女王"
        }
    ],
    "area": "United Kingdom",
    "begin": {
        "date": 27,
        "month": 6,
        "year": 1970
    },
    "ended": true,
    "gid": "0383dadf-2a4e-4d10-a46a-e9e041da8eb3",
    "id": 192,
    "name": "Queen",
    "rating": {
        "count": 24,
        "value": 92
    },
    "sort_name": "Queen",
    "tags": [
        {
            "count": 2,
            "value": "hard rock"
        },
        {
            "count": 1,
            "value": "70s"
        },
        {
            "count": 1,
            "value": "queen family"
        },
        {
            "count": 1,
            "value": "90s"
        },
        {
            "count": 1,
            "value": "80s"
        },
        {
            "count": 1,
            "value": "glam rock"
        },
        {
            "count": 4,
            "value": "british"
        },
        {
            "count": 1,
            "value": "english"
        },
        {
            "count": 2,
            "value": "uk"
        },
        {
            "count": 1,
            "value": "pop/rock"
        },
        {
            "count": 1,
            "value": "pop-rock"
        },
        {
            "count": 1,
            "value": "britannique"
        },
        {
            "count": 1,
            "value": "classic pop and rock"
        },
        {
            "count": 1,
            "value": "queen"
        },
        {
            "count": 1,
            "value": "united kingdom"
        },
        {
            "count": 1,
            "value": "langham 1 studio bbc"
        },
        {
            "count": 1,
            "value": "kind of magic"
        },
        {
            "count": 1,
            "value": "band"
        },
        {
            "count": 6,
            "value": "rock"
        },
        {
            "count": 1,
            "value": "platinum"
        }
    ],
    "type": "Group"
}

{
    "_id": "5b73de48b520d9c8477d30e6",
    "ended": true,
    "gid": "5eecaf18-02ec-47af-a4f2-7831db373419",
    "id": 992994,
    "name": "Queen",
    "sort_name": "Queen"
}
  
Posted by gushwell at 21:30Comments(0)

2018年08月15日

言語処理100本ノックでPython入門 #64(後編) - MongoDBの構築


言語処理100本ノック 2015の第7章・問題64です。

前回はMoongoDBをインストールしたので、今日は、実際の問題を解いてみます。

■ 問題

64. MongoDBの構築
アーティスト情報(artist.json.gz)をデータベースに登録せよ.さらに,次のフィールドでインデックスを作成せよ: name, aliases.name, tags.value, rating.value



■ Pythonのコード
import json
import pymongo

def enumdata():
    with open('artist.json', 'r', encoding='utf8') as fin:
        for line in fin:
            jsd = json.loads(line)
            yield jsd

def register():
    client = pymongo.MongoClient('localhost', 27017)
    db = client['MusicBrainzDb']
    artists = db['artists']
    for data in enumdata():
        artists.insert(data)
    artists.create_index([('name', pymongo.ASCENDING)])
    artists.create_index([('aliases.name', pymongo.ASCENDING)])
    artists.create_index([('tags.value', pymongo.ASCENDING)])
    artists.create_index([('rating.value', pymongo.ASCENDING)])


def main():
    register()

if __name__ == '__main__':
    main()

■ 簡単に説明


artist.jsonからJSONデータを一つずつ取り出して、insertメソッドでDBに登録しています。
全てのデータが登録終了したら、create_indexでインデックスを作成しています。

create_indexを先にやるとどうなるのかな? これは試していません。 件数が多いので、終わるまで結構時間がかかります。

■ データが登録されたかを確認

では、データが登録されたのか確認します。

MongoDB Compass ComunityというGUIの管理ツールがあるので、それをインストールします。

インストールのページで、バージョンを選択する場所があるので、そこで、Comunity Editionを選びます。
最初これがわからず、Comunityではないパッケージをダウンロードしてしまいました。

インストールはすぐに終わります。

インストールが終わったら、MongoDB Compass Community を起動します。
接続の画面ではデフォルトのままでDBに接続します。

MusicBrainzDbができているのが確認できます。
左のペインでMusicBrainzDbを選択すると、登録されている内容が見られます。
indexesタブを選ぶと、インデクスが作成されていることがわかります。

  
Posted by gushwell at 21:00Comments(0)

2018年08月08日

言語処理100本ノックでPython入門 #64(前編) - MacにMongoDBをインストール


今日は、言語処理100本ノック 2015の第7章・問題64です。

■ 問題

64. MongoDBの構築
アーティスト情報(artist.json.gz)をデータベースに登録せよ.さらに,次のフィールドでインデックスを作成せよ: name, aliases.name, tags.value, rating.value


■ MongoDBのインストール

まずは、MongoDBをMacにインストールします。
$ brew install mongodb
このコマンドを投入したら、合わせていろんなものがアップデートされました。

ターミナルの表示を眺めてみると
==> Installing mongodb dependency: sqlite
とか
==> Installing mongodb dependency: python@2
とか。

Python@2って、Python2をインストールしたということなんだと思うんだけど、 MongoDBってPython2に依存してるってことなのかな?
よくわかりません。

とりあえず、最後に
==> mongodb
To have launchd start mongodb now and restart at login:
  brew services start mongodb
Or, if you don't want/need a background service you can just run:
  mongod --config /usr/local/etc/mongod.conf

と表示されたので、正常にインストールできたのだと思います。

続けて、
$ mongo --version
でバージョンを見てみます。
MongoDB shell version v4.0.0
git version: 3b07af3d4f471ae89e8186d33bbb1d5259597d51
allocator: system
modules: none
build environment:
    distarch: x86_64
    target_arch: x86_64

■ Pymongoのインストール


続けて、MongoDBをPythonから操作する pymongoをインストールします。
$ conda install pymongo

以下のようなメッセージが出てきたので、yを押して、更新します。
The following packages will be UPDATED:

    conda:   4.5.8-py36_0         --> 4.5.9-py36_0
    openssl: 1.0.2o-h26aff7b_0    --> 1.0.2o-h1de35cc_1

Proceed ([y]/n)? y

まあ、これは、pymongoとは直接の関係はないですね。

■ MongoDBの起動

以下のコマンドでMongoDBを起動します。
mongod --config /usr/local/etc/mongod.conf

■ MongoDBの終了
$ mongo
で、MongoDBのCLIを起動します。

続けて、
> use admin

で管理者権限に移動します。

そして、以下のコマンドでMongoDBを終了させます。
> db.shutdownServer()

exit コマンドでCLIを終了します。
> exit


macOSでMongoDBを使うを参考にさせていただきました。
っていうか、ほとんどそのままです(^^;;

今日はここまで、実際の問題は後日チャレンジします。
  
Posted by gushwell at 22:15Comments(0)

2018年08月05日

言語処理100本ノックでPython入門 #63 - KVSにオブジェクトを格納


今日は、言語処理100本ノック 2015の第7章・問題63を解きます。


■ 問題
63. オブジェクトを値に格納したKVS
KVSを用い,アーティスト名(name)からタグと被タグ数(タグ付けされた回数)のリストを検索するためのデータベースを構築せよ.さらに,ここで構築したデータベースを用い,アーティスト名からタグと被タグ数を検索せよ.

■ どうやって解くか

KVS(Redis)のValueには、オブジェクトも格納できるから、前回とほとんど変わらないコードでいけるんじゃないかな、と思ったのですが、ダメでした。

登録するまではよかったのですが、取得したデータはなぜかシリアライズされたバイト列でした。 このシリアライズされたバイト列をPythonのオブジェクトに戻す方法がわかりません。

しかたないので、jsonにシリアライズしたものを格納して、取り出すときにjsonからデシリアライズすることにしました。

pickleというものを使う方法もあるみたいだけど、そうなると、Python依存のDBになってしまうので今回は却下。もしかしたら、バイト列がpickleオブジェクトだったりするのかも。

db=1 と指定することで、前回までと別のDBにしてみました。

■ 登録するプログラム

import json
import redis

def enumKv():
    with open('artist.json', 'r', encoding='utf8') as fin:
        for line in fin:
            jsd = json.loads(line)
            if 'tags' in jsd:
                yield jsd['name'], json.dumps(jsd['tags'])

def register():
    r = redis.StrictRedis(host='localhost', port=6379, db=1)
    r.flushdb()
    for k, v in enumKv():
        r.set(k, v)
    r.save()

def main():
    register()

if __name__ == '__main__':
    main()

■ アーティスト名からデータを取得するプログラム
import json
import redis

def printTag(r, name):
    v = r.get(name)
    if v != None:
        print('*' + name)
        tags = json.loads(v.decode())
        for tag in tags:
            print("{}\t{}".format(tag['value'], tag['count']))

def main():
    r = redis.StrictRedis(host='localhost', port=6379, db=1)
    printTag(r, 'George Winston')
    printTag(r, 'Mariah Carey')
    printTag(r, '松任谷由実')

if __name__ == '__main__':
    main()

■ 結果

*George Winston
new age 1
folk    1
*Mariah Carey
whistle register        1
rnb     1
pop     1
american        1
pop and chart   1
girl    1
*松任谷由実
fixme label mess        1
likedis auto    1
  
Posted by gushwell at 21:30Comments(0)

2018年08月01日

言語処理100本ノックでPython入門 #62 - KVSの反復処理


今日は、言語処理100本ノック 2015の第7章・問題62を解きます。


■ 問題
62. KVS内の反復処理
60で構築したデータベースを用い,活動場所が「Japan」となっているアーティスト数を求めよ.

■ Pythonのコード
import redis

def countjapan():
    r = redis.StrictRedis(host='localhost', port=6379, db=0)
    keys = r.scan_iter()
    count = 0
    for key in keys:
        v = r.get(key)
        if v == b'Japan':
            count = count + 1
    print(count)

def main():
    countjapan()

if __name__ == '__main__':
    main()

■ ちょっと説明

ものすごく力業っぽいコードだけど、Redisが良く分かっていないので、 この方法しか思いつかなかったです。 

この問題のお題が「反復処理」ってあるから、これでいいのだと思うけど... 遅いです。まあ、このコードじゃ仕方ないよね。 

なお、取り出した値は、バイト列なので、b'Japan'で比較しました。


■ Redisサーバーの停止

  それと、Redisサーバーを停止する正しい方法がわからず悩みました。
$ redis-cli
で、Redisのコマンドラインを起動して、そこで、
> shutdown save
とタイプすれば、停止することができました。

このコマンドラインを終了するには、
> exit
とします。  
Posted by gushwell at 21:30Comments(0)