2017年11月7日火曜日

都電とバラ

都電荒川線
Ricoh GR

 荒川区内の都電荒川線沿いには多くのバラが植えられていて、沿線沿いの小路は秋のお散歩にはもってこいです。
 春も悪くないのですが、11月上旬の少し肌寒いぐらいの時期の方が個人的には好き。
 汗をかかずに歩けますし、バラの美しさも長持ちしますしね。

都電荒川線
Ricoh GR

2017年11月5日日曜日

Pythonに関する書籍の紹介

ガーデン
α6500 & Sonnar T* E 24mm F1.8 ZA SEL24F18Z

 Python関連で購入したり図書館で借りたりしたりして読んだ本の一覧と感想です。
 随時更新予定。

入門書

実践力を身につける Pythonの教科書

 自分は入門書としてこれを購入。
 わかりやすくPythonの基本がひと通り身につく。

入門 Python 3

 文法面は詳しく、また有用な標準ライブラリなどにも言及がある。上の本を読んだあとでも、役に立つ記述があるので一度読んでおくとよい。自分は図書館で借りた。

Python 3.6.3 Documentation

 Pythonの日本語ドキュメント。書籍ではなくて恐縮だが、読んでおいたほうがよいと思うので掲載。
 上の本を読んだあとにひととおり読んでおくと標準ライブラリにどんなものがあるかなんとなく頭に入るので、無駄な苦労をしなくてすむと思う。  

クローリング・スクレイピング関係

Pythonによるスクレイピング&機械学習 開発テクニック BautifulSoup,scikit-learn,TensorFlowを使ってみよう

 書名のとおり、スクレイピング、機械学習(含むディープラーニング)をひととおり体験できる。スクレイピングについて詳しく知りたい人は、最初から下の本を買うほうがよいかと思う。

Pythonクローリング&スクレイピング -データ収集・解析のための実践開発ガイド-

 基本的なスクレイピング処理から自動での処理まで、相手先への負荷への配慮とか、エラーが出た場合の処理方法なども含めて載っている。
 下の本と両方手に入れたほうがよいが、どちらか一冊と言われたらこっちかな。

Pythonによるクローラー&スクレイピング入門 設計・開発から収集データの解析・運用まで

 こちらも基本的なスクレイピング処理から自動での処理まで記載されている。
 Scrapyについてもっと詳しく記載してほしかったのと、Web上で記載した記事の焼き直しのようなものもありそれに起因する誤植のようなものもあったのが減点要因。

機械学習・ディープラーニング

Pythonによるスクレイピング&機械学習 開発テクニック BeautifulSoup,scikit-learn,TensorFlowを使ってみよう

 再掲。機械学習やディープラーニングがどんなものか体験してみるには良い本。

その他

Effective Python ―Pythonプログラムを改良する59項目

 内容は高度。理解でない部分も多かったが、初心者のうちに目を通しておくと後で苦労が減りそうな感触がある。図書館で借りたが、手元に置いておきたいので後で買う予定。

PythonユーザのためのJupyter[実践]入門

 Jupyter Notebookを使ってのpandasによるデータ集計や、Matplotlib、Bokehによるデータ可視化を解説。Seabornについても解説されているとさらに嬉しかったのだったが、それはないものねだりですかね。。

2017年11月3日金曜日

【Python】簡易ドキュメントデータベース「TinyDB」を使ってみる

屋敷森
Ricoh GR

TinyDBとは

 Python用の簡易型のドキュメントデータベースです。
 サーバーを立てる必要がなく、データはJSON形式で保存されます。
 インストールもpipで簡単にできるので、ちょっとしたデータベースを作りたいときにお手軽に使えます。

 この記事では、自分用のメモを兼ねて簡単に使い方を記載します。
 詳しいドキュメントは「Welcome to TinyDB! — TinyDB 3.6.0 documentation」を御覧ください。

インストール方法

 pipで簡単にインストールできます。

pip install tinydb

使い方

ライブラリのインポート
from tinydb import TinyDB, Query
データベースの作成・オープン

 TinyDBメソッドにファイル名(必要ならパスを入れて)を引数で指定してデータベースを作成・オープンします。既にファイルがあれば開き、なければ新規に作成します。

# データベースの作成・オープン
db = TinyDB("db.json")

 なお、インメモリーで使う場合は、次のように開きます。

from tinydb.storages import MemoryStorage
db = TinyDB(storage=MemoryStorage)
テーブルの作成・指定

 データベースの中にテーブルを作成するとき(あるは既存のテーブルを開くとき)は、table() メソッドの引数で指定します。なお、テーブルを指定しないで使用することも可能です。この場合は「_default」というテーブルが使用されます。

tb_fruit = db.table("fruit")
tb_animal = db.table("animal")

 データベース内にあるテーブルの一覧は tables()メソッドで取得します。テーブルのリストが戻り値となります。

tables = db.tables()
データの追加

 一件ずつデータを追加するときは insert()メソッドに辞書を引数として渡します。
 後述しますが、戻り値としてドキュメントIDが返ります。
 テーブルを省略した形でデータベースから直接 insert()メソッドを使うと「_default」テーブルに追加されます。他のメソッドも同様です。

db.insert({"kind": "ネギ", "count": 1}) #テーブルを指定しない場合「_defaul」テーブルに追加される
tb_fruit.insert({"kind": "リンゴ", "count": 2})
tb_animal.insert({"kind": "サル", "count": 3})

 複数のデータを追加するときは、insert_multiple()メソッドを使います。引数は辞書のリスト、戻り値はドキュメントIDのリストとなります。

new_fruits = [{"kind": "オレンジ", "count": 4}, {"kind": "メロン", "count": 10}]
tb_fruit.insert_multiple(new_fruits)
new_animals = [{"kind": "キリン", "count": 2}, {"kind": "サル", "count": 10}]
tb_animal.insert_multiple(new_animals)
データの一覧出力 

 all()メソッドでテーブルのデータの一覧が出力されます。

# fruitテーブルのデータを一覧出力
tb_fruit.all()

#データベースに使用した場合は _defaultテーブルのデータが出力される
db.all()
データの検索

 検索にはQueryオブジェクトを使います。

query = Query()

# search()は該当するドキュメント(辞書)のリストが出力
tb_animal.search(query.kind == "サル")  # 該当するドキュメントのリストが出力
tb_fruit.search(query.kind == "キウイ")  # 該当するものがない場合は空のリストで出力
tb_fruit.search(query.count > 3) # 比較演算子が使える

# get()は該当する1件のみの出力
# 戻り値はドキュメント
tb_animal.get(query.kind == "サル")

# contains()は該当するドキュメントがあるか否か(戻り値は True / False)
tb_fruit.contains(query.kind == "オレンジ")
tb_fruit.contains(query.kind == "キウイ")

# count()は該当するドキュメントの数
print(tb_animal.count(query.kind == "サル"))
print("\n---------\n")
データの更新

 データの更新にはupdate()メソッドを使います。

# kindがキリンのドキュメントのcount値を1に更新
tb_animal.update({"count": 1}, query.kind == "キリン")

# 検索条件が複数ドキュメントに該当する場合はすべてのドキュメントが更新される
tb_animal.update({"count": 5}, query.kind == "サル")
データの削除

 データの削除にはremove()メソッドを使います。

# countが5未満のドキュメントを削除
tb_fruit.remove(query.count < 5)
ドキュメントID

 テーブル内の各ドキュメントにはそれぞれドキュメントIDが自動で振られます。
 insert()メソッドでデータ(ドキュメント)を挿入した場合の戻り値は挿入したドキュメントのIDとなり、insert_mulipleで挿入した場合の戻り値は挿入したドキュメントのIDのリストとなります。

# 挿入したデータにはドキュメントIDが振られる
id = tb_fruit.insert({"kind": "ブドウ", "count": 2})

# 複数挿入した場合はIDがリストで返る
new_fruits = [{"kind": "パパイヤ", "count": 12}, {"kind": "マンゴー", "count": 15}]
ids = tb_fruit.insert_multiple(new_fruits)
print("パパイヤとマンゴーのdocumentID: {0}".format(ids))

 既にテーブルにあるドキュメントのIDを取得したい場合は、ドキュメントのdoc_idプロパティから取得できます。

id = tb_fruit.get(query.kind == "メロン").doc_id  # documnet.doc_idでIDを取得

 get() は doc_id 引数、 contains()、 update()、 remove() はdoc_ids引数(IDのリスト)でドキュメントを指定できます。

# get()はdoc_id引数、 contains(), update(), remove()でdoc_ids引数で指定できる
tb_fruit.get(doc_id=id)
tb_fruit.update({"kind": "メロン", "count": 10, "size": "large"}, doc_ids=[id])
テーブル内のデータの個数

 Len()関数で引数にテーブルを指定するとテーブル内に含まれるデータの個数が返ります。

# fruitテーブルのデータの個数を出力
len(tb_fruit)

# データベースを指定すると _defaultテーブルおデータの個数が出力される
len(db)
テーブル内のすべてのデータの削除

 purge()メソッドでテーブル内のすべてのデータが削除されます。

tb_fruit.purge()
tb_animal.purge()
db.purge()  # _defaultテーブルのデータのみ適用される

サンプルコード

from pprint import pprint
from tinydb import TinyDB, Query

db_path = "db.json"

# データベースの作成・オープン
db = TinyDB(db_path)

# インメモリーで使う場合
# from tinydb.storages import MemoryStorage
# db = TinyDB(storage=MemoryStorage)


# テーブルの作成・指定(未指定の場合は_defaultというテーブルが使用される)
tb_fruit = db.table("fruit")
tb_animal = db.table("animal")

# データベースにあるテーブルの一覧リスト
tables = db.tables()
print("# データベースにあるテーブルの一覧リスト")
pprint(tables)
print("\n---------\n")

# 一件ずつのデータの追加
db.insert({"kind": "ネギ", "count": 1})
tb_fruit.insert({"kind": "リンゴ", "count": 2})
tb_animal.insert({"kind": "サル", "count": 3})

# 複数のデータの追加
new_fruits = [{"kind": "オレンジ", "count": 4}, {"kind": "メロン", "count": 10}]
tb_fruit.insert_multiple(new_fruits)
new_animals = [{"kind": "キリン", "count": 2}, {"kind": "サル", "count": 10}]
tb_animal.insert_multiple(new_animals)

# データの一覧出力
print("# データの一覧出力")
print("_defaultテーブルのデータ")
pprint(db.all())  # _defaultテーブルのデータのみ適用される
print("fruitテーブルのデータ")
pprint(tb_fruit.all())
print("animalテーブルのデータ")
pprint(tb_animal.all())
print("\n---------\n")

# データの検索
query = Query()
# search()は該当するものがリストで出力
print("# データの検索")
pprint(tb_animal.search(query.kind == "サル"))  # 該当するものがリストで出力
pprint(tb_fruit.search(query.kind == "キウイ"))  # 該当するものがない場合は空のリストで出力
pprint(tb_fruit.search(query.count > 3))
# get()は1件のみの出力
pprint(tb_animal.get(query.kind == "サル"))
# contains()は該当するものがあるか否か
print(tb_fruit.contains(query.kind == "オレンジ"))
print(tb_fruit.contains(query.kind == "キウイ"))
# count()は該当するものの数
print(tb_animal.count(query.kind == "サル"))
print("\n---------\n")

# データの更新
print("# データの更新")
tb_animal.update({"count": 1}, query.kind == "キリン")
tb_animal.update({"count": 5}, query.kind == "サル")  # 複数該当する場合はすべての項目で更新
pprint(tb_animal.all())
print("\n---------\n")

# データの削除
print("# データの削除")
tb_fruit.remove(query.count < 5)
pprint(tb_fruit.all())
print("\n---------\n")

# ドキュメントID
# 挿入したデータにはドキュメントIDが振られる
print("# ドキュメントID")
id = tb_fruit.insert({"kind": "ブドウ", "count": 2})
print("ブドウのdocumentID: {0}".format(id))

# 複数挿入した場合はidがリストで返る
new_fruits = [{"kind": "パパイヤ", "count": 12}, {"kind": "マンゴー", "count": 15}]
ids = tb_fruit.insert_multiple(new_fruits)
print("パパイヤとマンゴーのdocumentID: {0}".format(ids))

# documnet.doc_idでIDを取得
id = tb_fruit.get(query.kind == "メロン").doc_id
print("メロンのdocumentID: {0}".format(id))
print("\n---------\n")

# get()はdoc_id引数、 contains(), update(), remove()でdoc_ids引数で指定できる
print("# ドキュメントIDでのデータの指定")
print("doc_id:{0} は {1}".format(id, tb_fruit.get(doc_id=id)))
tb_fruit.update({"kind": "メロン", "count": 10, "size": "large"}, doc_ids=[id])
print("doc_id:{0} は {1}".format(id, tb_fruit.get(doc_id=id)))
print("\n---------\n")

# データの個数
print("# データの個数")
print("db(_defaultテーブル)のデータの個数 = {0}".format(len(db)))
print("fruitテーブルのデータの個数 = {0}".format(len(tb_fruit)))
print("animalテーブルのデータの個数 = {0}".format(len(tb_animal)))
print("\n---------\n")

# すべてのデータの削除
print("# すべてのデータの削除")
db.purge()  # _defaultテーブルのデータのみ適用される
tb_fruit.purge()
tb_animal.purge()
print("_defaultテーブルのデータ")
pprint(db.all())
print("fruitテーブルのデータ")
pprint(tb_fruit.all())
print("animalテーブルのデータ")
pprint(tb_animal.all())
print("\n---------\n")

2017年10月29日日曜日

オジギソウの花

オジギソウ


 オジギソウを育ててみてびっくりした幾つかのこと。

  • 夜は葉が閉じる。
  • 花が咲く。

 知識として知っていても、実物を育ててみての驚きがいろいろとあります。

2017年10月22日日曜日

【Python】芥川龍之介の『鼻』からワードクラウドを作ってみる

nose

 『Pythonによるクローラー&スクレイピング入門 設計・開発から収集データの解析・運用まで』P283~を参考にして芥川龍之介の『鼻』のテキストから上のようなワードクラウドを作成してみました。
 書籍掲載のものは形態素解析にJanomeを使用していますが、今回はMeCabで行っています。辞書はipadic-neologdを使用。
 また、名詞、副詞、動詞、形容詞のみ抽出し、動詞・形容詞は基礎型に変換しています。


import requests
import MeCab
from bs4 import BeautifulSoup
from wordcloud import WordCloud
import matplotlib.pyplot as plt


def get_wordcloud(text, file_path):
    '''
    :param text, file_path: str
    :return:
    '''

    # ストップワードの設定。出力結果を見ながら手動で設定した。
    stop_words = ["ある", "ない", "いる", "する", "の", "よう", "なる", "それ", "そこ", "これ", "こう", "ため", "そう", "れる", "られる"]

    # 単語に分割し名詞、副詞、動詞、形容詞のみ抽出(動詞・形容詞は基礎型を取得)
    tagger = MeCab.Tagger('-Ochasen')
    pos1 = ("名詞", "副詞")  # 表層形を取得する品詞
    pos2 = ("動詞", "形容詞")  # 基礎形を取得する品詞
    node = tagger.parseToNode(text)
    words = []
    while node:
        features = node.feature.split(",")
        if (features[0] in pos1) and not (node.surface in stop_words):
            words.append(node.surface)
        elif (features[0] in pos2) and not (features[6] in stop_words):
            words.append(features[6])
        node = node.next

    # wordcloudオブジェクトの作成
    font_path = r"C:\WINDOWS\Fonts\SourceHanSerif-SemiBold.otf"
    wordcloud = WordCloud(background_color="white", font_path=font_path, width=1600, height=1066, regexp=r"\w+").generate(" ".join(words))

    # wordcloudファイル出力
    wordcloud.to_file(file_path)

    # wordcloud表示
    plt.imshow(wordcloud, interpolation="bilinear")
    plt.axis("off")
    plt.show()


if __name__ == '__main__':
    # 青空文庫からテキストの取得
    url = "http://www.aozora.gr.jp/cards/000879/files/42_15228.html"  # 芥川龍之介 『鼻』
    r = requests.get(url, timeout=10)
    soup = BeautifulSoup(r.content, "lxml")

    # 本文の抽出
    text_elm = soup.find("div", attrs={"class": "main_text"})

    # ルビの削除
    [e.extract() for e in text_elm.select("rt")]
    text = text_elm.text

    # wordcloudの作成
    file_path = r"D:\nose.png"  # 出力する画像ファイルのパス
    get_wordcloud(text, file_path)

2017年10月21日土曜日

1画面Markdownエディタ「Typora」がなかなか良いです

撮る人
α6500 & SIGMA 35mm F1.4 DG HSM Art

 このところ「Typora」というMarkdownエディタを試用中なのですが、なかなか良い具合なので御紹介。

 Typoraはなかなか珍しい1画面タイプのMarkdownエディタです。Markdown記法のエディタとプレビューを1画面内で切り替えて行います。
 切り替えて、といっても、プレビュー画面内で随時Markdown記法で入力していけるので、ワードプロセッサ感覚で使える感じですね。生のMarkdownを表示させる必要はあまりありません。
 Windows、Mac、Linux の3つのOSで使えます。私が使っているのはWindowsバージョンですが、64bit版と32bit版の両方がありました。

 Markdownといっても様々な記法がありますが、Typoraが採用しているのはGFM(Github Flavored Markdown)です。
 加えて独自機能として目次(TOC)、数式表現、ダイアグラムなどもサポートされています。

 起動した直後の画面は極めてシンプル。
 表示されるのはメニューバー、画面、ステータスバーの3つのみです。
 でも、機能もシンプルというわけではなく、使い勝手もよく考えられています。

 特に気に入ったのは次のとおり。

  • 画面がシンプルで美しい。
  • テーマ機能があり、配色などの切り替えが可能。カスタムもできる。
  • サイドバーを開いてファイルリストを表示できる。おまけにアウトラインも表示できる。
  • キーボードショートカットが充実。
  • テーブルの作成が簡単。

 HTMLとPDFへの変換が本体だけでできるほか、別途、「Pandoc」というツールをインストールすると、docx形式(Microsoft Word)やEpub形式、リッチテキスト形式、OpenOffice形式など入出力できるファイルタイプが大幅に増えます。
 メニューバーの「Help」から「Install and Use Pandoc」を選ぶと出てくるマニュアルのとおりにPandocをダウンロードしてインストールだけなので大した手間ではないです。 

 Typoraのダウンロードは「Typora — a minimal markdown editor, markdown reader.」からできます。
 現在はまだβバージョンのようですが、私が使っていた限りでは特にバグなどは出てきませんでした。

2017年10月13日金曜日

【Python】Windowsでなるべく楽にMeCabをインストールする

花束
α6500 & E 50mm F1.8 OSS SEL50F18

 Pythonで形態素解析をしようと考えたとき、まず最初に選択肢にあがるツールはMeCabだと思います。
 しかしながら、WindowsでMeCabを使うのは、特に64bit版を使うのは意外と準備が面倒。

 というわけで、当初はpipで簡単にインストールできるJanomeを使っていたのですが、小さなデータならともかく大きなデータを扱うには残念ながら処理速度が足りませんでした。
 試しに10MBぐらいのテキストファイルで含まれる名詞の出現数を数える処理をしてみたら(joblibでcorei7論理8コアでの並列処理)、janome(mecab-ipadic-neologd内包版)で3分15秒程度かかりました。GB単位のデータを処理するにはちょっと大変そうです。

 そこで、仕方なく、ネットで検索しながら64bit版MeCab(加えて「mecab-ipadic-NEologd」も)を頑張ってインストールしてみましたので、備忘録として書き残します。先人に感謝。

64bit版MeCabをインストールする

 MeCabの公式サイト(MeCab: Yet Another Part-of-Speech and Morphological Analyzer)では、Windows用の64bit版MeCabは配布されていません。
 知識がある方ならソースコードを元に64bit版を自分で作成できるようですが(知識がなくても先人のものを参考にしながらなら不可能ではなさそうですが)、なるべく楽をしたいので、先人が作成してくださったReleases · ikegami-yukino/mecab · GitHubからMeCabの64bit版をダウンロードしてインストールします。

環境変数を設定する

 環境変数PATHに「\bin」を追加し、環境変数MECABRCに「\etc\mecabrc」を設定します。MECABRCがないとPythonからMeCabを使用する際にエラーが出ます。

Pythonにmecab-pythonを入れる

 一般的に使われる「mecab-python」は、Windowsではそのままpipでインストールとはいきません。ソースをダウンロードしてsetup.pyを書き換えてコンパイラをインストールしてetcの作業が必要になります。
 そこで、先人が作成してくださったWindows用のパッケージであるmecab-python-windows 0.9.9.6 : Python Package Indexを使用させていただいたのですが、前処理をしないとこれもそのままではpipでインストールできませんでした。

  1. Visual C 2015 Build Tools([Download the Visual C Build Tools (standalone C++ compiler, libraries and tools)](http://landinghub.visualstudio.com/visual-cpp-build-tools))のインストール
  2. \sdk」の「C:\Program Files (x86)\MeCab\sdk」へのコピー

の2つの作業が必要になります。この作業をしておくと、コマンドプロンプトから「pip install mecab-python-windows」でインストールできます。

mecab-ipadic-NEologdをインストール

 mecab-ipadic-NEologdは、多数のWeb上の言語資源から得た新語を追加したMeCab用の辞書です。本やマンガのタイトルなど固有名詞を分割させないようにするには必須。
 これをインストールするには「Bash on Ubuntu on Windows」を使います。
 まず、「Bash on Ubuntu on Windowsをインストールしてみよう! - Qiita」などを参考にして、「Bash on Ubuntu on Windows」を導入します。
 次に「mecab-ipadic-neologd/README.ja.md at master · neologd/mecab-ipadic-neologd · GitHub」の「Windows 10 バージョン 1607 以降の場合」の項のとおりに「mecab-ipadic-NEologd」をインストールします。
 インストールした際に、git cloneしたディレクトリ配下に「build\mecab-ipadic-2.7.0-20070801-neologd-日付」というディレクトリができます。この中にmecab-ipadic-NEologdの辞書が入っています。
 最後に「\ipadic」の中身を上記ディレクトリの中身と入れ替えれば完成。
 なお、入れ替えなくても-dオプションで辞書を指定すれば使えますが(例:tagger = MeCab.Tagger(r"-Owakati -d C:\src\mecab-ipadic-neologd\build\mecab-ipadic-2.7.0-20070801-neologd-20171009"))、そのたびに指定するのも面倒なので、私は標準辞書のディレクトリ名をipadic_oldと変更して残した上で入れ替えちゃってます。

おまけ:JanomeとMeCabの速度差

 冒頭で述べた約10MBのテキストファイルに含まれる名詞の出現数を数える処理(joblibでcorei7論理8コアでの並列処理)ですが、Janomeで3分15秒ぐらいかかっていたものが、MeCabで同等の作業をしたら2~3秒で終了!
 スピード差は10倍どころか少なくとも50倍以上はありそうです。

2017年10月1日日曜日

好みの傾向

グラス
α6500 & Sonnar T* E 24mm F1.8 ZA SEL24F18Z

 ここに掲載した写真は何か気に入ったところがあって掲載しているわけで、過去の写真を眺めれば自分がどういうものに心惹かれるのかわかるかなと眺め返して見たのですが、いったいこれのどこが気に入ったのだろうと首をかしげるものもそれなりにありまして、自分を理解するのもなかなか大変だな、と改めて思った次第であります。

2017年9月30日土曜日

9月が終わる

夕方の過ごし方
α6500 & SIGMA 35mm F1.4 DG HSM Art

 9月の終わりは夏の終わり。
 日が短くなったなぁ。

2017年9月27日水曜日

Visual Studio Codeも併用中

撮る人
α6500 & Sonnar T* E 24mm F1.8 ZA SEL24F18Z

 最近、ノートパソコン用の Markdown エディタとして Visual Studio Code がお気に入りです。
 拡張機能などは Atom の方が揃っているように思いますが、何しろ Atom は重いのが弱点。メモリが 4GB しかない私のノートパソコンには、Visual Studio Code の軽さがピッタリです。どうせノートパソコンで Markdown を書くときは複雑なことはしないですし。
 ユーザー設定で改行を Markdown のプレビューの改行に反映されるよう設定さえしておけば、ほぼ問題なし。
 「ほぼ」、というのはひとつだけ弱点がありまして、文書冒頭の全角スペースが Markdown のプレビューに反映されないこと。2行目以降は反映されるのに、どうしてファイルの先頭だけは無視されちゃうのでしょうかね?