フィルター

フィルターを使って、開発速度を上げます。実際に仕事で使ったsort、cut、uniqについて説明します。

sort

テキストファイルをがCSVのようにフォーマットが決まっているものとして高度な並べ替えを実現します。

4列目でソート、4列目が同じ値の場合は3列目でソートする。


% sort -k 4,4 -k 3,3 profile.log

数値としてソートする。


% sort -n -k 4,4 -k 3,3 profile.log

4列目は数値、4列目が同じ値の場合は3列目は文字列としてソートする。


% sort -k 4,4n -k 3,3 profile.log

4列目は数値で逆順にソート、4列目が同じ値の場合は3列目は文字列としてソートする。


% sort -k 4,4nr -k 3,3 profile.log

数値で逆順にソートする。


% sort -r -n -k 4,4 -k 3,3 profile.log

同じ内容の行を出力しない。


% sort -u -r -n -k 4,4 -k 3,3 profile.log

‘:’をデリミタとする。


% sort -t: -u -r -n -k 4,4 -k 3,3 profile.log

ソートキーの先頭にある空白を無視する。


% sort -b -t: -u -r -n -k 4,4 -k 3,3 profile.log

man sortは、くじけずに読めますが、なかなか読む気になれません。

今の潤沢な計算機資源であれば、多くの場合に単純選択ソートでも、入力というか対象が小さい場合は一瞬で処理が完了してしまうので、上記を覚えるより実装した方が気が楽だったりします。RubyにもPythonにもsortが用意されています。

ですが、プロファイラーを使って、結果が出て、そこからさらにソートスクリプトやソートプログラムを作成するのも気が重いです。下記をメモとして取っておくと、仕事が早いと思います。

残念ながらMacではgprofが入っていないので、こちらを使います。
Python プロファイラ

下記のプログラムのプロファイルを取ります。
7層のネットワークを動かす 1


         8665063 function calls (8397913 primitive calls) in 866.715 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
7452/6575    0.090    0.000    0.395    0.000 :1009(_handle_fromlist)
      596    0.002    0.000    0.002    0.000 :103(release)
      374    0.000    0.000    0.000    0.000 :143(__init__)
      374    0.001    0.000    0.006    0.000 :147(__enter__)
      374    0.000    0.000    0.002    0.000 :151(__exit__)
      596    0.003    0.000    0.004    0.000 :157(_get_module_lock)
      372    0.001    0.000    0.001    0.000 :176(cb)
      222    0.000    0.000    0.002    0.000 :194(_lock_unlock_module)
    493/5    0.000    0.000    0.601    0.120 :211(_call_with_frames_removed)
     3582    0.002    0.000    0.002    0.000 :222(_verbose_message)
        6    0.000    0.000    0.000    0.000 :232(_requires_builtin_wrapper)
      351    0.000    0.000    0.000    0.000 :307(__init__)
      351    0.000    0.000    0.000    0.000 :311(__enter__)
      351    0.001    0.000    0.002    0.000 :318(__exit__)
     1402    0.000    0.000    0.000    0.000 :321()
      298    0.001    0.000    0.001    0.000 :35(_new_module)
      368    0.001    0.000    0.001    0.000 :369(__init__)
      643    0.001    0.000    0.005    0.000 :403(cached)
     1147    0.002    0.000    0.002    0.000 :416(parent)
      351    0.000    0.000    0.000    0.000 :424(has_location)
       10    0.000    0.000    0.000    0.000 :433(spec_from_loader)
      351    0.003    0.000    0.010    0.000 :504(_init_module_attrs)
  351/349    0.001    0.000    0.138    0.000 :576(module_from_spec)
      372    0.001    0.000    0.001    0.000 :58(__init__)
        4    0.000    0.000    0.000    0.000 :634(_load_backward_compatible)
    355/5    0.003    0.000    0.603    0.121 :663(_load_unlocked)
      364    0.000    0.000    0.001    0.000 :719(find_spec)
        6    0.000    0.000    0.000    0.000 :740(create_module)
        6    0.000    0.000    0.000    0.000 :748(exec_module)
        6    0.000    0.000    0.000    0.000 :765(is_package)
      596    0.002    0.000    0.002    0.000 :78(acquire)
      358    0.000    0.000    0.001    0.000 :792(find_spec)
     1088    0.000    0.000    0.001    0.000 :855(__enter__)
     1088    0.001    0.000    0.001    0.000 :859(__exit__)
        8    0.000    0.000    0.000    0.000 :873(_find_spec_legacy)
      364    0.004    0.000    0.055    0.000 :882(_find_spec)
        1    0.000    0.000    0.000    0.000 :929(_sanity_check)
    373/4    0.002    0.000    0.605    0.151 :948(_find_and_load_unlocked)
    374/4    0.003    0.000    0.605    0.151 :978(_find_and_load)
        1    0.000    0.000    0.000    0.000 :994(_gcd_import)
       47    0.000    0.000    0.000    0.000 :1029(__init__)
    47/46    0.000    0.000    0.125    0.003 :1040(create_module)
       47    0.000    0.000    0.000    0.000 :1048(exec_module)

上記を1列目でソートします。プロファイルなので、数値でソートして、大きい方が上に来るようにします。


% sort -k 1,1nr profile.log | less

上記を2列目でソートします。


% sort -k 2,2nr profile.log | less

結果を示します。


    47416  184.843    0.004  184.843    0.004 {built-in method numpy.core.multiarray.dot}
     1600  146.655    0.092  148.888    0.093 util.py:71(col2im)
    41044  132.201    0.003  132.201    0.003 {method 'reshape' of 'numpy.ndarray' objects}
     4002   74.030    0.018  238.956    0.060 util.py:39(im2col)
      800   49.750    0.062   49.775    0.062 optimizer.py:110(update)
    66839   45.659    0.001   45.659    0.001 {method 'reduce' of 'numpy.ufunc' objects}
     2001   38.843    0.019  268.617    0.134 layers.py:257(forward)
    37552   37.820    0.001   37.820    0.001 {method 'copy' of 'numpy.ndarray' objects}
     6946   26.139    0.004   26.139    0.004 {built-in method numpy.core.multiarray.zeros}
     4002   21.027    0.005   45.638    0.011 layers.py:27(forward)
     2001   13.084    0.007  166.618    0.083 layers.py:308(forward)
     2001   13.083    0.007  515.776    0.258 simple_convnet.py:75(predict)
     2402   12.849    0.005   12.849    0.005 {method 'argmax' of 'numpy.ndarray' objects}
   207643   12.115    0.000   12.115    0.000 {built-in method numpy.core.multiarray.array}
      800   12.057    0.015  490.175    0.613 simple_convnet.py:144(gradient)
      800    9.392    0.012   59.825    0.075 layers.py:327(backward)
     1600    6.273    0.004    6.273    0.004 layers.py:36(backward)
     2941    5.846    0.002    5.846    0.002 {method 'flatten' of 'numpy.ndarray' objects}
      220    4.057    0.018    4.057    0.018 {imread}
     4002    3.345    0.001   21.819    0.005 layers.py:73(forward)
        3    2.164    0.721    2.164    0.721 {built-in method matplotlib._png.write_png}
     1600    1.265    0.001   17.803    0.011 layers.py:87(backward)
     4147    1.085    0.000    1.085    0.000 {built-in method numpy.core.multiarray.arange}
436684/360892    0.331    0.000    0.567    0.000 artist.py:230(stale)
     9975    0.312    0.000    2.125    0.000 lines.py:290(__init__)
   428120    0.307    0.000    0.413    0.000 __init__.py:930(__getitem__)
   102246    0.304    0.000    0.304    0.000 transforms.py:88(__init__)
   162709    0.244    0.000    0.244    0.000 {method 'items' of 'dict' objects}
    23500    0.227    0.000    0.343    0.000 artist.py:87(__init__)
    52080    0.205    0.000   45.791    0.001 fromnumeric.py:64(_wrapreduction)
84264/2004    0.175    0.000    0.354    0.000 arrayprint.py:694(recurser)
      800    0.172    0.000  847.861    1.060 trainer.py:152(train_step)
     4002    0.161    0.000   25.454    0.006 arraypad.py:993(pad)
     8004    0.155    0.000    0.205    0.000 stride_tricks.py:115(_broadcast_to)
70409/24013    0.147    0.000    0.233    0.000 transforms.py:142(_invalidate_internal)
        2    0.145    0.072   17.377    8.688 trainer.py:65(get_image_and_label)
   348058    0.140    0.000    0.203    0.000 {built-in method builtins.isinstance}
      220    0.128    0.001    0.128    0.001 {cvtColor}
    47/46    0.122    0.003    0.125    0.003 {built-in method _imp.create_dynamic}
     4444    0.120    0.000    0.541    0.000 lines.py:649(recache)
     3323    0.108    0.000    4.504    0.001 axis.py:74(__init__)
     1187    0.106    0.000    0.106    0.000 {method 'set_text' of 'matplotlib.ft2font.FT2Font' objects}
   429000    0.106    0.000    0.106    0.000 {function RcParams.__getitem__ at 0x11529f158}
        1    0.104    0.104  865.854  865.854 trainer.py:197(train)
    10421    0.100    0.000    0.478    0.000 font_manager.py:668(__init__)
        3    0.093    0.031    0.093    0.031 {method 'randn' of 'mtrand.RandomState' objects}
    37864    0.093    0.000    0.107    0.000 transforms.py:169(set_children)
   129112    0.092    0.000    0.102    0.000 {built-in method builtins.getattr}

本の中で出てきたcol2imとim2colと、numpyのdotとreshapeで大半を占めていることがわかります。これで、もう後はsqueezeやるしかなさそう、みたいな判断になります。自分の実装に自信を持って良さそう、という判断にもなります。

上記を3列目でソートします。


% sort -k 3,3nr profile.log | less

ある程度の傾向が見えたら、リダイレクトしておいて、簡単なレポートをまとめておくと、積み重ねていくことができます。何も資料に残していないと、1年後に全部最初からやり直しということになりかねません。

cut

上述のプロファイルの結果から2列目を取り出すには、下記のようにします。


sed -e 's/^ *//g; s/ * / /g' profile.log | cut -d ' ' -f 2

オプション

cutはデフォルトではdelimiterはタブです。

cutのオプションは少ないですが、ここでは2つだけ説明します。

-d delimiterを指定する。
-f 列を指定する。

自分で仕様を決めてcutを利用する

current directoryにプログラム実行の度に下記の様な名前のファイルが生成されるとします。

network_parameters.pkl.1
network_parameters.pkl.2
network_parameters.pkl.3
network_parameters.pkl.4
network_parameters.pkl.5
network_parameters.pkl.6
network_parameters.pkl.7
network_parameters.pkl.8
network_parameters.pkl.9
network_parameters.pkl.10

一番新しいファイルの番号が欲しい場合は下記で取得できます。


% \ls -1 network_parameters.pkl.* | tail -n 1 | cut -d . -f 3

-dでdelimiterを指定します。ここでは.です。-fで何番目の要素を取り出すかを指定します。

cutは、delimiterの連続をそれぞれ分割して認識するために複数のスペースや複数のタブで見やすくなっている場合にsedを使う必要が出てきます。上記のように自分で仕様を決めて(見やすい必要はどこにもない)、cutで切り出すという方法は便利です。しかし、filterそれぞれの仕様を調べるよりも正規表現を使う人の方が多い気がします。

cutでdelimiterに正規表現が使えると幅が広がるのですが。

uniq

行ごとに比較して、連続した行が同一の場合に1行にします。連続した行が同一の場合にその行だけを表示させることもできます。

ソートして、重複した行を消したファイルを取得する場合は、下記となります。


% sort sample_file | uniq > output
カテゴリーUNIX

コメントを残すために、Twitter OAuthを必要としています。ご了承ねがいます。
コメントは、Twitterに影響しません。

Twitter OAuth