自然言語処理100本ノック 第2章 UNIXコマンドの基礎(後半)


第2章の後半の問題を解いた記録。
UNIXコマンドでの実行結果も併記。

15. 末尾のN行を出力

自然数Nをコマンドライン引数などの手段で受け取り,入力のうち末尾のN行だけを表示せよ.確認にはtailコマンドを用いよ.

# -*- coding: utf-8 -*-
__author__ = 'todoroki'

import sys

if len(sys.argv) == 3:
    N = int(sys.argv[1])
    f = open(sys.argv[2])
    lines = f.readlines()[::-1][0:N][::-1]
    for i in xrange(N):
        print lines[i].strip()
    f.close()
else:
    print "please input \'N\' and \'FILENAME\'"

# (python problem15.py 5 hightemp.txt)
#=> 埼玉県     鳩山      39.9    1997-07-05
#=> 大阪府     豊中      39.9    1994-08-08
#=> 山梨県     大月      39.9    1990-07-19
#=> 山形県     鶴岡      39.9    1978-08-03
#=> 愛知県     名古屋     39.9    1942-08-02

行ごとにファイルを読み込んだ配列を反転させ、N行分を取得。
それを再び反転させて順番に出力することで実現。
(可読性を無視してこの処理を1行で書いた)

tail -n 5 hightemp.txt

#=> (出力は上と同じ)

16. ファイルをN分割する

自然数Nをコマンドライン引数などの手段で受け取り,入力のファイルを行単位でN分割せよ.同様の処理をsplitコマンドで実現せよ.

# -*- coding: utf-8 -*-
__author__ = 'todoroki'

import sys

if len(sys.argv) == 3:
    N = int(sys.argv[1])
    f = open(sys.argv[2])
    rows = f.readlines()
    n, mod = divmod(len(rows), N)
    if mod != 0:
        n += 1
    idx = 0
    for i in xrange(N):
        filename = "split_%s.txt" % (i + 1)
        g = open(filename, "w")
        for j in xrange(n):
            try:
                g.write(rows[idx + j])
            except:
                break
        idx += n
        g.close()
    f.close()

else:
    print "please input \'N\' and \'FILENAME\'"

# python problem16.py 5 hightemp.txt
#=> (split_1.txt〜split_5.txtに出力)

元ファイルをN分割するために、分割後のファイル行数nを計算。
あとは必要分の行数を各ファイルに出力。

split -l 5 hightemp.txt out.

#=> (out.aa, out.ab, ..., out.aeのファイルに出力)

pythonスクリプトとコマンドによる引数の意味に違いがあるんだけど、これで正解なのだろうか...

17. 1列目の文字列の異なり

1列目の文字列の種類(異なる文字列の集合)を求めよ.確認にはsort, uniqコマンドを用いよ.

# -*- coding: utf-8 -*-
__author__ = 'todoroki'

import sys

if len(sys.argv) == 2:
    f = open(sys.argv[1])
    str_set = set()
    for line in f.readlines():
        str_set.add(line.split()[0])
    f.close()
    for s in str_set:
        print s
else:
    print "please input \'FILENAME\'"

# (python problem17.py hightemp.txt)
#=> 愛知県
#=> 山形県
#=> 岐阜県
#=> 千葉県
#=> 埼玉県
#=> 高知県
#=> 群馬県
#=> 山梨県
#=> 和歌山県
#=> 愛媛県
#=> 大阪府
#=> 静岡県
cat col1.txt | sort | uniq

#=> (出力は上と同じ)

18. 各行を3コラム目の数値の降順にソート

各行を3コラム目の数値の逆順で整列せよ(注意: 各行の内容は変更せずに並び替えよ).確認にはsortコマンドを用いよ(この問題はコマンドで実行した時の結果と合わなくてもよい).

# -*- coding: utf-8 -*-
__author__ = 'todoroki'

import sys

if len(sys.argv) == 2:
    f = open(sys.argv[1])
    lines = f.readlines()
    sorted_lines = sorted(lines, key=(lambda x:float(x.split()[2])), reverse=True)
    g = open("sorted_hightemp.txt", "w")
    for line in sorted_lines:
        g.write(line.strip() + "\n")
    g.close()
    f.close()
else:
    print "please input \'FILENAME\'"

# (python problem18.py hightemp.txt)
#=> 高知県   江川崎   41  2013-08-12
#=> 埼玉県   熊谷  40.9    2007-08-16
#=> 岐阜県   多治見   40.9    2007-08-16
#=> 山形県   山形  40.8    1933-07-25
#=> 山梨県   甲府  40.7    2013-08-10
#=> 和歌山県    かつらぎ    40.6    1994-08-08
#=> 静岡県   天竜  40.6    1994-08-04
#=> 山梨県   勝沼  40.5    2013-08-10
#=> 埼玉県   越谷  40.4    2007-08-16
#=> 群馬県   館林  40.3    2007-08-16
#=> 群馬県   上里見   40.3    1998-07-04
#=> 愛知県   愛西  40.3    1994-08-05
#=> 千葉県   牛久  40.2    2004-07-20
#=> 静岡県   佐久間   40.2    2001-07-24
#=> 愛媛県   宇和島   40.2    1927-07-22
#=> 山形県   酒田  40.1    1978-08-03
#=> 岐阜県   美濃  40  2007-08-16
#=> 群馬県   前橋  40  2001-07-24
#=> 千葉県   茂原  39.9    2013-08-11
#=> 埼玉県   鳩山  39.9    1997-07-05
#=> 大阪府   豊中  39.9    1994-08-08
#=> 山梨県   大月  39.9    1990-07-19
#=> 山形県   鶴岡  39.9    1978-08-03
#=> 愛知県   名古屋   39.9    1942-08-02

問題のタイトルに降順ってあるからプログラム書いていたんだけど、元ファイルも3コラム目の降順になっている...
問題の主旨を読み違えてるのかな??
もし元ファイルの逆順なら reverse=False とすればいいんだけど

sort -r -k 3 hightemp.txt

#=> (出力は省略)

昇順で並び替えなら -r オプションをなくせばいい

19. 各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる

各行の1列目の文字列の出現頻度を求め,その高い順に並べて表示せよ.確認にはcut, uniq, sortコマンドを用いよ.

# -*- coding: utf-8 -*-
__author__ = 'todoroki'

import sys

if len(sys.argv) == 2:
    f = open(sys.argv[1])
    lines = f.readlines()
    count = {}
    for line in lines:
        l = line.split()[0]
        if count.has_key(l):
            count[l] += 1
        else:
            count[l] = 1
    for k, v in sorted(count.items(), key=(lambda x:x[1]), reverse=True):
        print k
else:
    print "please input \'FILENAME\'"

# (python problem19.py hightemp.txt)
#=> 山形県
#=> 埼玉県
#=> 群馬県
#=> 山梨県
#=> 愛知県
#=> 岐阜県
#=> 千葉県
#=> 静岡県
#=> 高知県
#=> 和歌山県
#=> 愛媛県
#=> 大阪府

1コラム目の値をkey、出現回数をvalueとするdictを作成し、それを元に出現回数が多い順に出力。

cut -f 1 hightemp.txt | sort  | uniq -c | sort -n -r | less

#=> (出力は省略)