yamakiy’s tech blog

技術系の記事をメインにやります。

言語処理100本ノック(第2章)

前回の続き。

第2章: UNIXコマンドの基礎

hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.

10. 行数のカウント

行数をカウントせよ.確認にはwcコマンドを用いよ.

In [1]: f = open("hightemp.txt", "r")

In [2]: sum(1 for i in f.readlines())
Out[2]: 24
[root@localhost tmp]# wc -l hightemp.txt
24 hightemp.txt

11. タブをスペースに置換

タブ1文字につきスペース1文字に置換せよ.確認にはsedコマンド,trコマンド,もしくはexpandコマンドを用いよ.

In [1]: f = open("hightemp.txt", "r")

In [2]: new_hightemp = [line.replace("\t", " ") for line in f.readlines()]

In [3]: f.close()

In [4]: new_f = open("new_hightemp.txt", "w")

In [5]: new_f.writelines(new_hightemp)

In [6]: new_f.close()

diff <(コマンド) <(コマンド)
でコマンドの出力結果をdiffに適用できるの便利

[root@localhost tmp]# diff new_hightemp.txt <(sed 's/\t/ /g' hightemp.txt)
[root@localhost tmp]#

12. 1列目をcol1.txtに,2列目をcol2.txtに保存

各行の1列目だけを抜き出したものをcol1.txtに,2列目だけを抜き出したものをcol2.txtとしてファイルに保存せよ.確認にはcutコマンドを用いよ.

In [1]: f = open("hightemp.txt", "r")

In [2]: hightemp = f.readlines()

In [3]: col1_list =[line.split("\t")[0] for line in hightemp]

In [4]: col2_list =[line.split("\t")[1] for line in hightemp]

In [5]: f.close()

In [6]: with open("col1.txt", "w") as f:
   ...:     f.writelines("\n".join(col1_list)+"\n")

In [7]: with open("col2.txt", "w") as f:
   ...:     f.writelines("\n".join(col2_list)+"\n")
[root@localhost tmp]# diff col2.txt <(cut hightemp.txt -f 2)
[root@localhost tmp]# diff col1.txt <(cut hightemp.txt -f 1)
[root@localhost tmp]#

13. col1.txtとcol2.txtをマージ

12で作ったcol1.txtとcol2.txtを結合し,元のファイルの1列目と2列目をタブ区切りで並べたテキストファイルを作成せよ.確認にはpasteコマンドを用いよ.

In [1]: with open("col1.txt", "r") as f:
   ...:     col1 = f.readlines()

In [2]: with open("col2.txt", "r") as f:
   ...:     col2 = f.readlines()

In [3]: with open("col1_2.txt", "w") as f:
   ...:     col1_2_list = [f"{col[0]}\t{col[1]}".replace("\n", "") for col in zip(col1, col2)]
   ...:     f.write("\n".join(col1_2_list)+"\n")
[root@localhost tmp]# diff col1_2.txt <(paste col1.txt col2.txt)
[root@localhost tmp]#

14. 先頭からN行を出力

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

# head_hightemp.py
import sys

def hightemp_head(n):
    with open("hightemp.txt", "r") as f:
        head = [line for line in f.readlines()[:n]]
        print("".join(head), end="")

if __name__=="__main__":
    n = int(sys.argv[1])
    hightemp_head(n)
[root@localhost tmp]# python3.6 head_hightemp.py 2
高知県  江川崎  41      2013-08-12
埼玉県  熊谷    40.9    2007-08-16
[root@localhost tmp]# diff <(head -3 hightemp.txt) <(python3.6 head_hightemp.py 3)
[root@localhost tmp]#

15. 末尾のN行を出力

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

# tail_hightemp.py
import sys

def hightemp_tail(n):
    with open("hightemp.txt", "r") as f:
        head = [line for line in f.readlines()[::-1][:n][::-1]]
        print("".join(head), end="")

if __name__=="__main__":
    n = int(sys.argv[1])
    hightemp_tail(n)
[root@localhost tmp]# python3.6 tail_hightemp.py 2
山形県  鶴岡    39.9    1978-08-03
愛知県  名古屋  39.9    1942-08-02
[root@localhost tmp]# diff <(tail -3 hightemp.txt) <(python3.6 tail_hightemp.py 3)
[root@localhost tmp]#

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

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

# split_hightemp.py
import sys

def hightemp_split(n):
    with open("hightemp.txt", "r") as f:
        hightemp_list = f.readlines()
        max_line_per_file = int(len(hightemp_list) / n) + 1

        line_num = 0
        file_number = 1
        for line in hightemp_list:
            line_num += 1
            if line_num > n:
                line_num = 1
                file_number += 1
            with open(f"files/higtemp_{file_number}.txt", "a") as f:
                f.write(line)

if __name__=="__main__":
    n = int(sys.argv[1])
    hightemp_split(n)

差分は問題なし

[root@localhost tmp]# split -d -l 5 hightemp.txt files/
[root@localhost tmp]# ls files/
00  01  02  03  04
[root@localhost tmp]# python3.6 split_hightemp.py 5
[root@localhost tmp]# ls files/
00  01  02  03  04  higtemp_1.txt  higtemp_2.txt  higtemp_3.txt  higtemp_4.txt  higtemp_5.txt
[root@localhost tmp]# diff files/00 files/higtemp_1.txt
[root@localhost tmp]# diff files/01 files/higtemp_2.txt
[root@localhost tmp]# diff files/02 files/higtemp_3.txt
[root@localhost tmp]# diff files/03 files/higtemp_4.txt
[root@localhost tmp]# diff files/04 files/higtemp_5.txt
[root@localhost tmp]

中身もよさそう

[root@localhost tmp]# cat files/00
高知県  江川崎  41      2013-08-12
埼玉県  熊谷    40.9    2007-08-16
岐阜県  多治見  40.9    2007-08-16
山形県  山形    40.8    1933-07-25
山梨県  甲府    40.7    2013-08-10
[root@localhost tmp]# cat files/04
大阪府  豊中    39.9    1994-08-08
山梨県  大月    39.9    1990-07-19
山形県  鶴岡    39.9    1978-08-03
愛知県  名古屋  39.9    1942-08-02

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

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

def hightemp_arr_spot_strtype():
    with open("hightemp.txt", "r") as f:
        col1_types = set(line.split()[0] for line in f)
        print("\n".join(col1_types))

if __name__=="__main__":
    hightemp_arr_spot_strtype()
[root@localhost tmp]# diff <(cut -f 1 hightemp.txt| sort |uniq) <(python arr_spot_strtype_hightemp.py | sort)
[root@localhost tmp]#

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

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

# sort_hightemp.py
def hightemp_sort():
    with open("hightemp.txt", "r") as f:
        sort_hightemp = sorted([line.split() for line in f.readlines()], key=lambda x: x[2], reverse=True)
        sort_hightemp = ["\t".join(line) for line in sort_hightemp]
        print("\n".join(sort_hightemp)+"\n")

if __name__=="__main__":
    hightemp_sort()
[root@localhost tmp]# diff <(sort -r -n -k 3 hightemp.txt) <(python sort_hightemp.py)
10d9
< 群馬県        上里見  40.3    1998-07-04
11a11
> 群馬県        上里見  40.3    1998-07-04
17d16
< 群馬県        前橋    40      2001-07-24
19c18
< 大阪府        豊中    39.9    1994-08-08
---
> 群馬県        前橋    40      2001-07-24
20a20,21
> 埼玉県        鳩山    39.9    1997-07-05
> 大阪府        豊中    39.9    1994-08-08
23d23
< 埼玉県        鳩山    39.9    1997-07-05
24a25
>
[root@localhost tmp]# python sort_hightemp.py
高知県  江川崎  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

ソートはできてるけど、ソート数値に同値があるため、diffで差分がでてます

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

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

ソート方法どうしようかって結構悩んだ結果ごり押し
辞書をsortのkeyに利用しようとしたけど、上手くいかなかった

def hightemp_sort2():
    with open("hightemp.txt", "r") as f:
        hightemp = [line.split() for line in f.readlines()]
        appear_cnt_dict = get_appear_cnt_dict(hightemp)

        # 出現回数を配列に加える
        hightemp = [[str(appear_cnt_dict[line[0]])]+line for line in hightemp]
        # 出現回数と都道府県名でソート
        sort_hightemp = sorted(hightemp, key=lambda x: (x[0], x[1]), reverse=True)
        sort_hightemp = ["\t".join(line[1:]) for line in sort_hightemp]
        print("\n".join(sort_hightemp))

# 出現回数を取得
def get_appear_cnt_dict(hightemp):
    appear_cnt_dict = {}
    for line in hightemp:
        if line[0] in appear_cnt_dict:
            appear_cnt_dict[line[0]] += 1
        else:
            appear_cnt_dict[line[0]] = 1
    return appear_cnt_dict

if __name__=="__main__":
    hightemp_sort2()

目視だとよさそう

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

そのままだと第1keyの順番がずれるから、互いにsortしてdiffとって確認

[root@localhost tmp]# sort hightemp.txt | cut -f 1 | uniq -c | sort -r
      3 山梨県
      3 山形県
      3 埼玉県
      3 群馬県
      2 千葉県
      2 静岡県
      2 岐阜県
      2 愛知県
      1 和歌山県
      1 大阪府
      1 高知県
      1 愛媛県
[root@localhost tmp]# diff <(sort hightemp.txt | cut -f 1 | uniq -c | sort -r) <(python3.6 sort2_hightemp.py | cut -f 1 | uniq -c | sort -r)
[root@localhost tmp]#

ここらへんまでは以前解いたことあるからなんとか