言語処理100本ノック with Python【第2章】

No 日付 学んだこと
10 4/9 with open()、wcコマンド、encoding="utf-8_sig"
11 4/9 -
12 4/9 -
13 4/9 -
14 4/10 -
15 4/10 readlines
16 4/11 -
17 4/12 set()
18 4/12 sort()
19 4/12 pprint、setdefault()

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

研究やデータ分析において便利なUNIXツールを体験します.これらの再実装を通じて,プログラミング能力を高めつつ,既存のツールのエコシステムを体感します.

10. 行数のカウント

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

素人の言語処理100本ノック:10 - Qiita
Man page of WC

参考にしているQiita記事を見てmanコマンドを打ってみたがPowershellでは出なかった。家のMacが恋しい。。。
まぁぐぐればいいんだけど。

解法1

fname = 'hightemp.txt'
count = 0

with open(fname, encoding="utf-8_sig") as hightemp:
    for line in hightemp:
        count += 1

print(count)
# 24

リストとかでfor回してるとenumerateとかでループカウント(インデックス)を取得できるけど…。
すごく単純だし1行でかっこよく書きたいけどなさげ。。
pythonの内包表記について - Qiita

解法2

後から追加した解法

fname = 'hightemp.txt'
with open(fname, encoding="utf-8_sig") as hightemp:
    lines = hightemp.readlines()

print(len(lines))
open
open(filename, mode) 

open()はfileオブジェクトを返す。

mode
r 読み込み専用
w 書き込み専用(同名の既存のファイルがあれば消去される)
a ファイルを追記用に開く。書き込まれた内容は自動的にファイルの終端に追加される。
r+ 読み書き両用

mode 引数は省略可能で、デフォルトは 'r'。
withブロックを用いることでclose()を書かなくても済む。こっちが推奨されている。
7. 入力と出力 — Python 3.6.5 ドキュメント


エンコード

以下のようなエラーが出るので、エンコード(バイナリデータをテキストに変換)してあげなくちゃいけない。

UnicodeDecodeError: 'cp932' codec can't decode byte 0x8c in position 8: illegal multibyte sequence

WindowsでCP932(Shift-JIS)エンコード以外のファイルを開くのに苦労した話 - Qiita

UNIXコマンド版

wc --line hightemp.txt

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

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

素人の言語処理100本ノック:11 - Qiita

解法

fname = 'hightemp.txt'

with open(fname, encoding="utf-8_sig") as hightemp:
    for line in hightemp:
        print(line.replace('\t', ' '), end='')

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

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

素人の言語処理100本ノック:12 - Qiita

解法

fname = 'hightemp.txt'

with open(fname, encoding="utf-8_sig") as hightemp, \
     open('col1.txt', mode='w', encoding="utf-8_sig") as col1, \
     open('col2.txt', mode='w', encoding="utf-8_sig") as col2:
    for line in hightemp:
        cols = line.split('\t')
        col1.write(cols[0] + '\n')
        col2.write(cols[1] + '\n')

Jupyter上で開くと一番上に赤ぽちあるのなんでだろ。
最初エンコードするの忘れてたらメモ帳では開けたけど、Jupyterでは開けなかった。
f:id:saneeeatsu:20180409165128p:plain



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

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

素人の言語処理100本ノック:13 - Qiita
Pythonにおける「空白文字」についてまとめてくださってる。ありがたひ。

解法

with open('col1.txt', encoding="utf-8_sig") as col1, \
     open('col2.txt', encoding="utf-8_sig") as col2, \
     open('merge.txt', mode='w', encoding="utf-8_sig") as output:

    for col1, col2 in zip(col1, col2):
        output.write(col1.rstrip() + '\t' + col2.rstrip() + '\n')






100本ノック…疲れてきた!!!





14. 先頭からN行を出力

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

素人の言語処理100本ノック:14 - Qiita

解法

fname = 'hightemp.txt'

n = int(input('>>> N : '))

with open(fname, encoding="utf-8_sig") as hightemp:
    for i, line in enumerate(hightemp):
        if i >= n:
            break
        print(line.rstrip())

Jupyteで標準入力初めて使った。便利~。



あれ...??
これUNIXコマンドを学ぶ章なのに全然UNIXコマンド書いてないのでは。
まぁ、また今度書こう(書かない)。



15. 末尾のN行を出力

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

素人の言語処理100本ノック:15 - Qiita

fname = 'hightemp.txt'

n = int(input('>>> N : '))

with open(fname, encoding="utf-8_sig") as hightemp:
    lines = hightemp.readlines() # 全データ
    
    for line in lines[-n::1]:
        print(line.rstrip())

最初は、最後までの行数を持ってきて~とか考えていたけど、全データもってこれるよねそうですよね。
readlinesを調べているときにあった面白い記事。
ファイルを読み込む時のreadlines関数について - SELECT * FROM life;



このファイル見て思ったけど、地球温暖化っていう割りに歴代最高気温は最近のものが多いわけではないのね。
1930、1940年代のものがあるのは意外。



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

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

素人の言語処理100本ノック:16 - Qiita

解法

import math

fname = 'hightemp.txt'
n = int(input('>>> N : '))

with open(fname, encoding="utf-8_sig") as hightemp:
    lines = hightemp.readlines()

count = len(lines)
unit = math.ceil(count / n)  # 1ファイル当たりの行数

for i, offset in enumerate(range(0, count, unit), 1):
    with open('output_{:02d}.txt'.format(i), mode='w', encoding="utf-8_sig") as output:
        for line in lines[offset:offset + unit]:
            output.write(line)

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

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

素人の言語処理100本ノック:17 - Qiita

解法

fname = 'hightemp.txt'

with open(fname, encoding="utf-8_sig") as hightemp:
    column = [line.split('\t')[0] for line in hightemp]

print(set(column)) # 重複の削除

# {'和歌山県', '埼玉県', '山梨県', '静岡県', '山形県', '大阪府', '愛知県', '千葉県', '岐阜県', '愛媛県', '高知県', '群馬県'}

最初fnameの下でcolumnを初期化してたけど、なくても大丈夫だった。

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

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

素人の言語処理100本ノック:18 - Qiita
つまり、気温で降順ソートすればいい。

解法

fname = 'hightemp.txt'

# lines = open(fname).readlines()
with open(fname, encoding="utf-8_sig") as hightemp:
    lines = hightemp.readlines()

lines.sort(key=lambda line: float(line.split('\t')[2]), reverse=True)

for line in lines:
    print(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

最初for文で書いたけど参考先のQiitaの記事ではlambdaで書かれていてそっちのが断然きれいだったので消したorz
参考サイトでは、コメントアウトにしてるopenのやり方でcloseしてないけどいいのかな?

float
sort

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

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

素人の言語処理100本ノック:19 - Qiita

解法

from pprint import pprint

fname = 'hightemp.txt'
count = {}

with open(fname, encoding="utf-8_sig") as hightemp:
    prefectures = [row.split('\t')[0] for row in hightemp]

for prefecture in prefectures:
    count.setdefault(prefecture, 0)
    count[prefecture] += 1

count = sorted(count.items(), key=lambda x: x[1], reverse=True)

pprint(count)

# [('埼玉県', 3),
#  ('山形県', 3),
#  ('山梨県', 3),
#  ('群馬県', 3),
#  ('岐阜県', 2),
#  ('静岡県', 2),
#  ('愛知県', 2),
#  ('千葉県', 2),
#  ('高知県', 1),
#  ('和歌山県', 1),
#  ('愛媛県', 1),
#  ('大阪府', 1)]
pprint

pretty printerの略で、データ構造によって綺麗なフォーマットで出力してくれる。
pprint – データ構造を見やすい形で出力する - Python Module of the Week

setdefault

辞書.setdefault(キー, 値)
辞書にキーが存在しないときにのみキーと値のペアを新規追加する
if文しなくていい!クール!便利!

辞書について色々まとめられていたので貼っておく。
Pythonの辞書 初心者向けガイド - Qiita