Search on the blog

2016年4月30日土曜日

デフォルトシェルをzshにしてみた

会社の人がzsh使っているのを見て、bashからzshに変えてみた。
pecoというツールを入れてみたところ、コマンド履歴検索、ディレクトリ検索が圧倒的に楽になった。


やったことをメモしておく。

最新版zshのインストール
デフォルトでzsh入っていたが、新しい方がいいので最新版をインストール。
brew install zsh
echo "/usr/local/bin/zsh" | sudo tee -a /etc/shells 
chsh -s /usr/local/bin/zsh

~/.zshrcの設定
プロンプト、コマンド履歴、lsの色などを設定。
$ cat ~/.zshrc
PROMPT="%F{green}%n$%f "
export LSCOLORS=gxfxcxdxbxegedabagacad
alias ls='ls -G'

export HISTFILE=${HOME}/.zsh_history
export HISTSIZE=1000
export SAVEHIST=100000
setopt hist_ignore_dups
setopt EXTENDED_HISTORY

コマンド履歴検索の強化
pecoを利用した履歴検索の強化。
c-rで検索画面が出てくるようにした。

http://qiita.com/shepabashi/items/f2bc2be37a31df49bca5

ディレクトリ検索の強化
pecoを利用したディレクトリ検索の強化。
c-uで検索画面が出てくるようにした。


gitの補完強化
gitのコマンドやブランチ名の補完ができるようにした。

2016年4月29日金曜日

便利なPythonライブラリ(5)retry

デコレータつけるだけで、リトライしてくれるという便利ライブラリ。

インストール
$ pip install retry

サンプル
失敗するかもしれない不安定な関数に@retryをつけると成功するまで実行してくれる。
from retry import retry
from random import randint

def unreliable_func(x):
    if randint(0, 10) == 0:
        return x*x
    raise Exception("Sorry, I'm busy.")

@retry()
def solve(x):
    return unreliable_func(x)

def main():
    ret = solve(10)
    print (ret)

if __name__ == '__main__':
    main()

もう少し実用的な例。
クローラを書いた時に、エラーが起きたら再トライみたいなことをしたい場合も@retryが使える。以下のサンプルでは、
  • 最大4回トライする(4回実行してすべてエラーだったらエラーとする)
  • トライの間隔は、5秒から始めて、10秒、20秒と2倍ずつ増やしていく
という条件でリトライしている。
from retry import retry
import urllib.request

@retry(tries=4, delay=5, backoff=2)
def crawl_url(url):
    print ("crawling...")
    f = urllib.request.urlopen(url)
    return f.read()

def main():
    ret = crawl_url("http://kenjih.com")
    print (len(ret))

if __name__ == '__main__':
    main()

2016年4月26日火曜日

便利なPythonライブラリ(4)OptionParser

 コマンドライン引数を解析するときはOptionParserが便利。

(2016/4/29追記) zfhrp7さんがコメントにてご指摘くださったように、OptionParserはpending deprecationとなっています。同様の機能を提供するargparseを使うようにしてください。

サンプル
from optparse import OptionParser

def main():
    parser = OptionParser()
    parser.add_option("-n", "--n_jobs", dest="n_jobs",
                      action="store", type="int", default=1,
                      metavar="N", help="run N jobs in parallel")
    parser.add_option("-v", "--verbose", dest="verbose",
                      action="store_true", default=False,
                      help="if true, output verbose messsages")
    (options, args) = parser.parse_args()
    print (options)
    print (args)

if __name__ == '__main__':
    main()

説明

上のサンプルのように、オプションと次の値のセットで指定するコマンド引数も、オプション単体で指定するコマンド引数も、正しく解析することができる。

add_optionの引数の説明を下表にまとめる。
引数 説明
第一引数 コマンドラインから渡されるオプション名 
第二引数 コマンドラインから渡されるオプション名(第一引数とは別の表記)
dest 解析後に、optionsのどのキーに設定するか
action オプションが与えられたとき何をするか
次の引数を設定値として格納する、フラグをtrueにする、フラグをfalseにするなど
type オプションの次の引数が満たすべき型
default オプションが指定されなかったときのデフォルト値
metavar ヘルプの文言中に使うメタ値
help ヘルプに表示する文言

実行結果

実行サンプルをいくつか。

まず、ヘルプの表示。
kenjih$ python main.py -h
Usage: main.py [options]

Options:
  -h, --help        show this help message and exit
  -n N, --n_jobs=N  run N jobs in parallel
  -v, --verbose     if true, output verbose messsages

次にオプションを指定した場合。parserに解析されなかったコマンド引数はargsに格納されることに注意。
kenjih$ python main.py -n 5 -v arg1 arg2
{'n_jobs': 5, 'verbose': True}
['arg1', 'arg2']

最後にオプション指定なしの場合。
kenjih$ python main.py 
{'n_jobs': 1, 'verbose': False}
[]

2016年4月24日日曜日

便利なPythonライブラリ(3)collections.defaultdict

 Pythonのdefaultdictの話。

defaultdictは基本的にはdictと同じだが、キーが存在しない場合のデフォルト値を設定することができる。

例えば、出現要素の回数を数えるプログラムを考える。(collections.Counter使えば一発だが、簡単なサンプルということで敢えてdict/defaultdictで書いてみる)

dictを使った場合は、以下のように書ける。
出現回数をインクリメントする前に、xがdictに格納されているかどうかのチェックを行い格納されていない場合は0をセットする必要がある。
import numpy as np

def main():
    xs = np.random.randint(0, 10, 100)
    counter = dict()
    for x in xs:
        if x not in counter:
            counter[x] = 0
        counter[x] += 1
    print (counter)

if __name__ == '__main__':
    main()

defaultdictを使うと以下のように書ける。
デフォルト値を予め設定しておくことで、キーが存在するかどうかのチェックを行う必要がない。デフォルト値の設定は、デフォルト値を生成する関数をコンストラクタに渡すことで実現する。今回の例では、lambda : 0 という関数を渡せばよいが、intという関数が同値なので後者を使っている。
from collections import defaultdict
import numpy as np

def main():
    xs = np.random.randint(0, 10, 100)
    counter = defaultdict(int)
    for x in xs:
        counter[x] += 1
    print (counter)

if __name__ == '__main__':
    main()

2016年4月23日土曜日

awkの連想配列を使ってユニークなデータ数を数える

サンプルファイル
 以下のようなファイルをサンプルとして用いる。
$ wc -l tmp.log 
 1000000 tmp.log
$ head tmp.log 
F 61
F 18
A 95
V 98
U 23
C 94
F 47
N 42
I 85
Q 28
1列目にアルファベット、2列目に数字があるような何らかのログファイルで、行数は100 万行。
このファイルの2列目のユニークなデータ数を数えたいとする。

sort | uniq
有名なイディオムを使うと、
$ cat tmp.log | awk '{print $2}' | sort | uniq | wc -l
100

だが、これは遅い。
手元の環境だと約12秒ほどかかる。

awkの連想配列
awk内で連想配列を使って値の出現数をカウントして、初めて出た時だけカウントするというやり方でやってみると、
cat tmp.log | awk '!seen[$2]++' | wc -l
100

速い!
手元の環境で0.7秒くらい。

ということで、sort | uniq するより awkの連想配列使った方がいいという話でした。

2016年4月9日土曜日

便利なPythonライブラリ(2)itertools.groupby

 与えられたiterableをキーごとにgroupbyすることができる。
unixのuniqコマンドやC++ STLのuniqueと同様に、iterableは予めsortした状態で渡さなければならない。

 以下の例では、xs[i], ys[i]にi番目の点の座標が格納されているとして、x軸ごとにyの合計を計算している。
from itertools import groupby
from operator import itemgetter

def main():
    xs = [1,2,3,1,2,3,1,2,3]
    ys = [1,2,3,4,5,6,7,8,9]

    points = sorted(zip(xs, ys))
    for k,v in groupby(points, key=itemgetter(0)):
        print (k, sum(y for x,y in v))

if __name__ == '__main__':
    main()

2016年4月2日土曜日

便利なPythonライブラリ(1)Counter

 Counterを使うと、iterableの要素ごとの出現数をカウントすることができる。

サンプル
from collections import Counter
import numpy as np

counter = Counter(np.random.randint(10, size=1000))
print (counter.most_common())

実行結果
$ python counter.py 
[(6, 118), (8, 113), (9, 110), (1, 108), (3, 103), (4, 95), (0, 91), (7, 89), (\
2, 87), (5, 86)]

上のサンプルのようにmost_common()を呼ぶと出現回数の降順に(要素, 出現数)を取得できる。most_common()の引数に整数を渡すと、上位N件だけ取得することもできる。