言語処理100本ノック 第2章 in Python

  • 14
    いいね
  • 4
    コメント
この記事は最終更新日から1年以上が経過しています。

言語処理100本ノックの第2章:UNIXコマンドの基礎です。ほとんどPythonの練習なので、より良い書き方ありましたらコメントでシェアしてもらえると嬉しいです…。環境はUbuntu14.04, Python3です。
問題文、unixコマンド、pythonコード、実行結果の順で貼っています。

また、今回取り扱う課題における入力ファイルはhightemp.txtです。

hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.

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

10. 行数のカウント

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

command
wc -l hightemp.txt
10.py
with open("hightemp.txt","r") as f:
    print(len(f.readlines()))
24

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

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

command
sed 's/\t/ /g' hightemp.txt 
11.py
with open('hightemp.txt',"r") as f
    print((f.read()).replace("\t"," "))
高知県 江川崎 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

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

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

commannd
cut -f 1 hightemp.txt > col1.txt
cut -f 2 hightemp.txt > col2.txt
12.py
with open("hightemp.txt") as f1, open("col1.txt","w") as f2, open("col1.txt","w") as f3:
    cols=list(zip(*[row.split("\t") for row in f1]))
    f2.write("\n".join(cols[0]))
    f3.write("\n".join(cols[1]))
col1.txt
高知県
埼玉県
岐阜県
 ・
 ・
 ・
山梨県
山形県
愛知県
col2.txt
江川崎
熊谷
多治見
 ・
 ・
 ・
大月
鶴岡
名古屋

まず、line.split("\t")[0]がどうにも気持ち悪いです :(

そして、2次元のリストをほとんど触ったことがなく、列成分を抜き出す方法がいまいちわかりません。行単位のリストが連結されているだけなので、やはりfor文で回さなきゃダメでしょうか…。

イマイチわからない
>>> text=[line.split("\t") for line in open("hightemp.txt","r")]

>>> print(text[0])
['高知県', '江川崎', '41', '2013-08-12\n']

>>> print(text[0][0])
高知県

>>> print(text[:][0])
['高知県', '江川崎', '41', '2013-08-12\n']

>>> print(text[][0])
  File "<stdin>", line 1
    print(text[][0])
               ^
SyntaxError: invalid syntax

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

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

command
paste col1.txt col2.txt 
13.py
with open("col1.txt") as f1, open("col2.txt") as f2, open("output.txt","w") as f3:
    f3.write("".join([c1.strip("\n")+"\t"+c2 for c1,c2 in zip(f1,f2)]))
output.txt
高知県   江川崎
埼玉県   熊谷
岐阜県   多治見
山形県   山形
山梨県   甲府
和歌山県    かつらぎ
静岡県   天竜
山梨県   勝沼
埼玉県   越谷
群馬県   館林
群馬県   上里見
愛知県   愛西
千葉県   牛久
静岡県   佐久間
愛媛県   宇和島
山形県   酒田
岐阜県   美濃
群馬県   前橋
千葉県   茂原
埼玉県   鳩山
大阪府   豊中
山梨県   大月
山形県   鶴岡
愛知県   名古屋

14. 先頭からN行を出力

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

command
head -n 5 hightemp.txt
14.py
import sys
assert len(sys.argv)==2, "[usage]: python 14.先頭からN行を出力.py [N]"
with open("hightemp.txt") as f:
    print("".join(f.readlines()[:int(sys.argv[1])]), end="")
(N=5)
高知県   江川崎   41  2013-08-12
埼玉県   熊谷  40.9    2007-08-16
岐阜県   多治見   40.9    2007-08-16
山形県   山形  40.8    1933-07-25
山梨県   甲府  40.7    2013-08-10

15. 末尾のN行を出力

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

command
tail -n 5 hightemp.txt
15.py
import sys
assert len(sys.argv)==2, "[usage]: python 14.先頭からN行を出力.py [N]"
with open("hightemp.txt") as f:
    print("".join(f.readlines()[-int(sys.argv[1]):]), end="")
(N=5)
埼玉県   鳩山  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

14,15をやっていて、ファイルの中身をすべて読み込むのではなく、表示箇所だけ読み込みたいな、と感じました。が、

14.先頭からN行を出力.py
f=open("hightemp.txt")
for i in range(N):
    print(f.readline(),end="")

のように上からN行はできるのですが、下からN行がどうにもわからないので断念しました。

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

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

command
split -l 5 hightemp.txt
16.py
import sys,math
assert len(sys.argv)==2, "[usage]: python 16.ファイルをN分割する.py [N]"

text=[line for line in open("hightemp.txt","r")]
unit=math.ceil(len(text)/int(sys.argv[1]))

for i in range((int(sys.argv[1]))):
    with open("file"+str(i+1)+".txt","w") as f:
        f.write("".join(text[i*unit:(i+1)*unit]))
file1.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

(・・・中略・・・)

file5.txt
大阪府   豊中  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コマンドを用いよ.

command
cut -f 1 hightemp.txt | sort | uniq
17.py
with open("hightemp.txt") as f:
    print(set([line.split("\t")[0] for line in f]))
{'埼玉県', '和歌山県', '山形県', '群馬県', '山梨県', '高知県', '岐阜県', '愛知県', '静岡県', '愛媛県', '大阪府', '千葉県'}

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

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

command
 sort -r -n -k 3,3 hightemp.txt
18.py
data=[line.split("\t") for line in open("hightemp.txt")]
for line in sorted(data, key=lambda x: float(x[2]), reverse=True):
    print ("\t".join(line), end="")
高知県   江川崎   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

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

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

command
cut -f 1 hightemp.txt | sort | uniq -c | sort -r     
19.py
col1=[line.split("\t")[0] for line in open("hightemp.txt")]
count={word:col1.count(word) for word in col1}
for word,freq in sorted(count.items(), key=lambda x: x[1], reverse=True):
    print(freq, word, sep="\t")

3   埼玉県
3   群馬県
3   山形県
3   山梨県
2   愛知県
2   岐阜県
2   静岡県
2   千葉県
1   高知県
1   愛媛県
1   大阪府
1   和歌山県

まとめ

言語処理100本ノック第2章でした。
表示するのか、ファイルに保存するのか、ただ結果を得るだけなのか、いろいろバラバラでややこしかったです。
もっとスマートに書ける箇所はたくさんあると思うので、意見もらえるととても嬉しいです。
どんな些細なことでも大丈夫です :P



言語処理100本ノック 第1章 in Python