言語処理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]#
ここらへんまでは以前解いたことあるからなんとか