ここでは、以下の常識をチェックします:
( ) 内はおおまかな目安時間です。
foo,3 baa,1 john,5 foo,1出力例:
john,5 foo,4 baa,1
Id | Name | Score |
---|---|---|
1 | Alice | 100 |
2 | Bob | 50 |
3 | Carol | 75 |
a001.txt
, a002.txt
, ... という名前のついた
複数のファイルがあるとする。これらの名前を一括して
a001.html
, a002.html
, ... に変更するような
シェルスクリプトを書け。 (5分)
files.txt
中に一行ずつ書かれている場合は
スクリプトをどう変更すべきか。(5分)
特殊な記号 ({, }, $, *, ;) を 引数に渡す場合は '〜' で囲む。
-t
」というファイル中から
*
という文字列を検索する grep
コマンドを書け。
a="foo" echo "$a" b="$a $a" c='$a $a' d='$a '"$a"
「こんなコマンドを実行したはずなんだけど、なんだっけ」
$ history | grep なんか
注意: historyファイルはときどき消されることがある。
function _prompt_cmd { local s=$? echo "`date '+%Y-%m-%d %H:%M:%S'` $HOSTNAME:$$ $PWD ($s) " \ "`history 1`" >> $MYHISTFILE } PROMPT_COMMAND=_prompt_cmd
コマンドラインの記録は、そのまま実験ノートにもなる。 あとで実験手順を再現したいときに参考になる。
$ ls -l $ ls -l | wc $ ls -l | sort $ ls -l | sort -k4 $ ls -l | sort -k4 -r $ ls -l | sort -k4 -r -n $ ls -l | awk '{print $5;}' $ ls -l | awk '{a+=$5;}' $ ls -l | awk 'BEGIN{a=0;} {a+=$5;} END{print(a);}' $ ls -l | awk '/euske/ {print $4;}' | uniq $ ls -l | awk '/euske/ {print $4;}' | uniq -c $ ls -l | awk '/euske/ {print $4;}' | uniq -c | wc
$ find ~ $ find ~ -type f $ find ~ -type f | grep test $ find ~ -type f | grep -i test $ find ~ -type f | grep -i test | wc $ find ~ -type f -name '*test*' $ find ~ -type d -ctime -3 $ find ~ -type d -mtime +3 $ find ~ -type d -mtime -3
shスクリプトはこの行から始める。
#!/bin/sh または #!/bin/bash
$ chmod 755 foo.sh
echo "$0" a=$1 shift b=$1 c="$@"
`コマンド` または $(コマンド)
if 式; then ...; else ...; fi
if 式; then ... else ... fi
for 変数 in 式; do ... done
while read 変数; do ... done
case 式 in パターン1) ... ;; パターン2) ... ;; *) ... ;; esac
$ cat files.txt a.txt b.txt c.txt $ cat files.txt | xargs echo $ cat files.txt | xargs cat $ find -type d | xargs ls
UNIXプログラムのお約束:
$ cmd1 input.txt | cmd2 | cmd3 > output.txt
$ cmd1 input.txt
$ cmd1 input.txt > temp.txt $ cmd2 < temp.txt | cmd3 > output.txt
$ cmd1 input.txt | tee temp.txt | cmd2 | cmd3 > output.txt
$ コマンド [オプション] < 入力ファイル > 出力ファイル
$ コマンド [オプション] 入力ファイル
ファイル名が与えられない場合は標準入力を使用する。$ コマンド [オプション] 入力ファイル1 入力ファイル2 ...
こうしておくと
のようにできる。$ find ... | xargs コマンド [オプション]
また、実験パラメータの変更はコードをじかに変更するのではなく、 コマンドラインオプションとして処理すること。
$ exp1 -a1 -p2 -k0 input.txt > output_a1_p2_k0.txt $ exp1 -a2 -p3 -k5 input.txt > output_a2_p3_k5.txt ...
ログは、たとえ人間が読む場合でも、 なるべく機械的に処理できるようにしておくこと。 (grep/awk 等でのおおまかな統計が簡単にとれる。)
print("kokodayo", x, y)
print(f"DEBUG: data read complete: x={x}, y={y}.")
#!/bin/sh exec </dev/null exec >log exec 2>&1 renice +20 -p $$ echo "*** START `date` ***" /usr/bin/time -v 実際のコマンド echo "*** END `date` ***"
Pythonスクリプトは慣例によりこの行から始める。
#!/usr/bin/env python
だいたい以下のようなパターンで書くと、 上に示した「お約束」に沿ったコマンドになる。
import sys import fileinput def doit(args): for line in fileinput.input(args): print(line) return def main(argv): import getopt def usage(): print(f'usage: {argv[0]} [-d] [-o output] [file ...]') return 100 try: (opts, args) = getopt.getopt(argv[1:], 'do:') except getopt.GetoptError: return usage() debug = 0 output = None for (k, v) in opts: if k == '-d': debug += 1 elif k == '-o': output = v return doit(args) if __name__ == '__main__': sys.exit(main(sys.argv))
sys.argv
でコマンドライン引数を取得。
getopt.getopt
でオプションを取得。
usage()
でヘルプを表示。
main()
関数はコマンドラインの解析のみをおこない
実際の処理は doit()
関数に正式なパラメータを渡してやらせる。
(これにより、他モジュールから利用することが可能)
$ python test.py input.txt または $ ./test.py input.txt または $ cat input.txt | test.py または $ cat files.txt | xargs test.py
debug
により doit()
内の
なんらかの挙動が変わるようにせよ。
-p
オプションを追加せよ:
$ test.py -p 4 input.txt
たいていの研究では、複数の対象を異なる条件で実験する。 このような実験条件・実験対象はよく記録し忘れるため、 実験プロセス全体をシェルスクリプトにし、さらにそれを git で管理するのがおすすめ。 (スクリプトとその履歴が実験ノートになる)
基本戦略は、シェルのワイルドカード (*) で ある条件をもったファイルだけを簡単に指定できるようにすることである。
src_exp1_ver2.csv
result-v4-20190803-temp.txt
out.3cddd4d137ad4f794a8ccf0763b4d5a6450934b5
jikken
, jiken2
, ..., kekka3
jikken_1
, jikken_2
, ..., jikken_3
jikken_2.input
jikken_2.output
jikken_2.graph
jikken_2.graph.svg
f1
, f2
, ..., f443
f0001
, f0002
, ..., f0443
基本的にUNIXのファイル名は逐次探索である。 したがって、あまり1個のディレクトリに沢山のファイルを置くと遅くなる (せいぜい1000個程度)。
それからパス名が長くなりすぎると見にくいし、入力も大変。
input/
, output/
)
s201909121012/
, ...)
data_seg01_p3_q4/
, ...)
data_201909121012_seg01_p3_input
, ...)
実験結果は、たいていの場合あとで解析可能な形式で記録しておく必要がある。 実験に時間がかかる場合・実験が複数のステージに分かれている場合などは、 その中間的な状態を記録しておく必要がある。
重要: できるだけ既存のツール・ライブラリで処理できるようにする。
おすすめしない。 もしやるとしたら、parseが簡単にできるようにすること。
# コメント +キー1 バリュー1 +キー2 バリュー2 (空行がレコード区切り)
rec = {} for line in fp: line = line.strip() if line.startswith('#'): continue if line.startswith('+'): (k,_,v) = line.partition(' ') rec[k] = v elif not line: yield rec rec = {}
超おすすめしない。
簡単なデータだけならいいかも (たとえば 32ビット列の羅列ひたすら1G個とか)。
「SQLite は fopen()
に対抗するために作られた」
複雑な構造 × 膨大な数があるときに使う方法。
ProtocolBuffer, HDF, MongoDB, ...
導入に手間がかかりすぎて、個人でやる実験には向かない。
import csv with open('output.csv', 'w') as fp: writer = csv.writer(fp) writer.writerow(['a', 'b', 'ccc'])
import csv with open('input.csv') as fp: for row in csv.reader(fp): print(row)
import json with open('output.json', 'w') as fp: data = {'a':123, b:['x','y']} fp.write(json.dumps(data))
import json with open('input.json') as fp: for line in fp: data = json.loads(line)
C から SQLite を使う場合は SQLite C/C++ Interface を参照。
import sqlite3 db = sqlite3.connect('data.db') cur = db.cursor() cur.executescript(''' CREATE TABLE Student ( Id INTEGER PRIMARY KEY, Name TEXT, Score INTEGER); ''') for (name,id,score) in scores: cur.execute('INSERT INTO Student VALUES (?, ?, ?);', (id, name, score))
import sqlite3 db = sqlite3.connect('data.db') cur = db.cursor() for row in cur.execute('SELECT Name,Id FROM Student;'): (name,id) = row
SVG (Scalable Vector Graphics) 形式とは、テキスト形式の一種で、 図形を文字によって記述する。
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='300' height='200'> <rect x='10' y='10' width='200' height='100' stroke='#000000' fill='#ff0000' /> <circle cx='200' cy='100' r='50' stroke='#000000' fill='#0000ff' /> </svg>
作成したファイル first.svg
は、ブラウザで開くことができる。
すると、以下のような図が表示される:
上の SVG は、以下のような情報を表している:
#000000
(黒) で、内部の塗りは #ff0000
(赤)。
#000000
(黒) で、内部の塗りは #0000ff
(青)。
単位はすべてピクセルである。また、色は #RRGGBB
のように
赤 (R)、緑 (G)、青 (B) の各原色が
16進数 00
(0) 〜 ff
(255) で表されている。
つまり、黒は #000000
であり、白は #ffffff
となる。
座標のような数値は '〜'
または "〜"
で囲む。
first.svg
を実際に入力し画面に描画せよ。
#…
) にはどのような値を指定すればよいか?
SVG の基本構造は以下のようになっている。
まず <svg>
〜 </svg>
で
囲まれる文字列があり、その中に描画コマンドが並んでいる。
<svg>
のような文字列を タグ (tag) という。
最初の <svg>
タグでは、図形全体の幅と高さをピクセル単位で指定する。
「xmlns='http://www.w3.org/2000/svg' version='1.1'
」の部分は固定である。
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='幅' height='高さ'>
...描画コマンド...
</svg>
rect
)
<rect x='10' y='10' width='100' height='80' fill='none' stroke='#000000' stroke-width='2' /> <rect x='80' y='60' width='50' height='40' fill='#ffcc00' stroke='#0000ff' stroke-width='4' />
x
、y
に左上の座標を指定する。
width
、height
に幅と高さを指定する。
fill
には塗る色を指定する。(塗らない場合は none
)
stroke
には線の色を指定する。(線がない場合は none
)
stroke-width
には線の幅を指定する。(省略した場合は 1)
<rect ...
の最後に必ず />
をつけること。
line
)
<line x1='10' y1='10' x2='100' y2='80' stroke='#000000' stroke-width='2' />
x1
、y1
に始点の座標を指定する。
x2
、y2
に終点の座標を指定する。
stroke
, stroke-width
は同じ。
polygon
)
<polygon points='10,90 50,10 90,90' fill='#00ff00' stroke='#000000' stroke-width='2' />
points
に点の座標列を指定する。x,y x,y ...
のように 2個ずつ区切って記述する。
(カンマなしで x y x y ...
と書いてもよい)
fill
、stroke
、stroke-width
は同じ。
circle
, ellipse
)
<circle cx='50' cy='50' r='40' fill='none' stroke='#000000' stroke-width='2' /> <ellipse cx='200' cy='50' rx='80' ry='40' fill='#ff00ff' stroke='#000000' stroke-width='2' />
cx
、cy
に中心座標を指定する。
r
(円の場合) または rx
、ry
(楕円の場合) に半径を指定する。
fill
、stroke
、stroke-width
は同じ。
text
)
<rect x='10' y='10' width='200' height='80' fill='none' stroke='#000000' /> <text x='10' y='40' text-anchor='start'>左寄せ</text> <text x='110' y='60' fill='red' text-anchor='middle'>中央寄せ</text> <text x='210' y='80' fill='white' stroke='#000000' text-anchor='end'>右寄せ</text>
x
、y
に起点の座標を指定する。
text-anchor
に起点の方法
(start
、middle
、end
のいずれか) を指定する。left
、center
、right
ではない。
<text>
タグはやや特殊で、
表示したい文字列を <text>
〜 </text>
で囲む。
なお、複数行にわたる文字は一度に書けない。
fill
で指定する。
stroke
は文字の縁取り色として使われるので、
通常は none
にしておく。
すべての描画コマンドにいちいち stroke
や fill
を
書くのは面倒くさいので、このような場合は <g>
タグによるグループ化を使う。
<g>
〜 </g>
で囲んだ部分には、
同じ色・線幅が適用される。
<g fill='none' stroke='#0000ff' stroke-width='2'> <rect x='10' y='10' width='50' height='30' /> <line x1='35' y1='25' x2='100' y2='50' /> <circle cx='100' cy='50' r='30' /> </g>
$ cat foo.gv digraph G { Nfoo [label="foo"]; Nbar [label="bar"]; Nbaz [label="baz"]; Nfoo -> Nbar; Nbar -> Nbaz; Nfoo -> Nbaz; } $ dot -Tsvg foo.gv > foo.svg (GV → SVGに変換) $ rsvg-convert -f pdf -o foo.pdf foo.svg (SVG → PDFに変換)