言語処理100本ノック 2015の「第2章: UNIXコマンドの基礎」の記録です。
第2章はCSVファイルの操作関連です。1年以上前にやった内容の復習です。やった当時は「UNIXコマンド使わないでPythonでいいじゃん」くらいに思っていましたが、大きいサイズのファイルを扱うときはUNIXコマンドの方がだいたい速いんですよね。UNIXコマンドは覚えておいて損がないです。
今回、Python部分についてはPandas
パッケージを多く使っています。CSVのような行列系データの扱いにはほんとに便利です。
環境
種類 | バージョン | 内容 |
---|---|---|
OS | Ubuntu18.04.01 LTS | 仮想で動かしています |
pyenv | 1.2.15 | 複数Python環境を使うことがあるのでpyenv使っています |
Python | 3.6.9 | pyenv上でpython3.6.9を使っています 3.7や3.8系を使っていないことに深い理由はありません パッケージはvenvを使って管理しています |
第2章: UNIXコマンドの基礎
学習内容
研究やデータ分析において便利なUNIXツールを体験します.これらの再実装を通じて,プログラミング能力を高めつつ,既存のツールのエコシステムを体感します.
head, tail, cut, paste, split, sort, uniq, sed, tr, expand
ノック内容
hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.
10. 行数のカウント
行数をカウントせよ.確認にはwcコマンドを用いよ.
010.行数のカウント.ipynb
Pythonだとreadlines
で一気に読むのが一番速いはず(たいして調べていません)。
print(len(open('./hightemp.txt').readlines()))
24
wcはWord Countの略でしょう。-l
オプションで改行コードをカウントします。大きなファイルはテキストエディタで開くだけでも時間がかかるので重宝します。
wc hightemp.txt -l
24 hightemp.txt
11.タブをスペースに置換
タブ1文字につきスペース1文字に置換せよ.確認にはsedコマンド,trコマンド,もしくはexpandコマンドを用いよ.
011.タブをスペースに置換.ipynb
replace
関数を使って置換します。改行しないと結果が見にくいのでpprint
使っています。
from pprint import pprint
with open('./hightemp.txt') as f:
pprint([line.replace('\t', ' ')for line in f])
['高知県 江川崎 41 2013-08-12\n',
'埼玉県 熊谷 40.9 2007-08-16\n',
'岐阜県 多治見 40.9 2007-08-16\n',
中略
'山梨県 大月 39.9 1990-07-19\n',
'山形県 鶴岡 39.9 1978-08-03\n',
'愛知県 名古屋 39.9 1942-08-02\n']
sed
は文字列置換、行の削除ができます。このコマンドを実行では、ターミナルに結果を出すだけなのでファイル中身が更新されるわけではありません。「【 sed 】 文字列の置換,行の削除を行う」を参考にしました。
sed 's/\t/ /g' ./hightemp.txt
高知県 江川崎 41 2013-08-12
埼玉県 熊谷 40.9 2007-08-16
岐阜県 多治見 40.9 2007-08-16
中略
山梨県 大月 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コマンドを用いよ.
012.1列目をcol1.txtに,2列目をcol2.txtに保存.ipynb
Pandas
を使いました。パラメータusecols
で読み込む列を1と2列目に限定しています。便利ですね。
import pandas as pd
df = pd.read_table('./hightemp.txt', header=None, usecols=[0, 1])
df[0].to_csv('012.col1.txt',index=False, header=False)
df[1].to_csv('012.col2.txt',index=False, header=False)
cut
で中身を確かめます。「【 cut 】コマンド――行から固定長またはフィールド単位で切り出す」を参考にしました。
cut -f 1 ./hightemp.txt
cut -f 2 ./hightemp.txt
高知県
埼玉県
岐阜県
中略
山梨県
山形県
愛知県
江川崎
熊谷
多治見
山形
中略
大月
鶴岡
名古屋
13.col1.txtとcol2.txtをマージ
12で作ったcol1.txtとcol2.txtを結合し,元のファイルの1列目と2列目をタブ区切りで並べたテキストファイルを作成せよ.確認にはpasteコマンドを用いよ.
013.col1.txtとcol2.txtをマージ.ipynb
pandas
で2つのファイルを読んでつなげています。
import pandas as pd
result = pd.read_csv('012.col1.txt', header=None)
result[1] = pd.read_csv('012.col2.txt', header=None)
result.to_csv('013.col1_2.txt', index=False, header=None, sep='\t')
「pasteコマンドについて詳しくまとめました 【Linuxコマンド集】」を参考にしました。出力結果は省略します。
paste 012.col1.txt 012.col2.txt
14.先頭からN行を出力
自然数Nをコマンドライン引数などの手段で受け取り,入力のうち先頭のN行だけを表示せよ.確認にはheadコマンドを用いよ.
014.先頭からN行を出力.ipynb
input
関数で引数を受け取っています。
from pprint import pprint
n = int(input('N Lines--> '))
with open('hightemp.txt') as f:
for i, line in enumerate(f):
if i < n:
pprint(line)
else:
break
'高知県\t江川崎\t41\t2013-08-12\n'
'埼玉県\t熊谷\t40.9\t2007-08-16\n'
'岐阜県\t多治見\t40.9\t2007-08-16\n'
「ファイルの先頭から表示するheadコマンドの詳細まとめ【Linuxコマンド集】」を参考にしました。
head hightemp.txt -n 3
高知県 江川崎 41 2013-08-12
埼玉県 熊谷 40.9 2007-08-16
岐阜県 多治見 40.9 2007-08-16
15.末尾のN行を出力
自然数Nをコマンドライン引数などの手段で受け取り,入力のうち末尾のN行だけを表示せよ.確認にはtailコマンドを用いよ.
015.末尾のN行を出力.ipynb
これは結構迷いました。ファイルが大きい場合に全件読み込むのが嫌だったのでLinuxのhead
で全件を確認した後にlinecache
パッケージを使おうかとも思いましたが、だったらtail
でいいじゃん状態です。結局readlines
使いました。
from pprint import pprint
n = int(input('N Lines--> '))
with open('hightemp.txt') as f:
pprint(f.readlines()[-n:])
['山梨県\t大月\t39.9\t1990-07-19\n',
'山形県\t鶴岡\t39.9\t1978-08-03\n',
'愛知県\t名古屋\t39.9\t1942-08-02\n']
tail hightemp.txt -n 3
山梨県 大月 39.9 1990-07-19
山形県 鶴岡 39.9 1978-08-03
愛知県 名古屋 39.9 1942-08-02
16.ファイルをN分割する
自然数Nをコマンドライン引数などの手段で受け取り,入力のファイルを行単位でN分割せよ.同様の処理をsplitコマンドで実現せよ.
016.ファイルをN分割する.ipynb
math
パッケージのceil
関数を使って商を切り上げています。
ファイル書き出しはwritelines
関数で一括追加しています。
import math
n = int(input('N spilits--> '))
with open('./hightemp.txt') as f:
lines = f.readlines()
unit = math.ceil(len(lines) / n)
for i in range(0, n):
with open('016.hightemp{}.txt'.format(i), 'w') as out_file:
out_file.writelines(lines[i*unit:(i+1)*unit])
「【 split 】コマンド――ファイルを分割する」を参考にしました。
split -n 3 -d hightemp.txt 016.hightemp-u
17.1列目の文字列の異なり
1列目の文字列の種類(異なる文字列の集合)を求めよ.確認にはsort, uniqコマンドを用いよ.
017.1列目の文字列の異なり.ipynb
pandas
のunique
関数使いました。pandas
は、この手の処理は非常に楽にできます。
import pandas as pd
df = pd.read_table('hightemp.txt', header=None, usecols=[0])
print(df[0].unique())
['高知県' '埼玉県' '岐阜県' '山形県' '山梨県' '和歌山県' '静岡県' '群馬県' '愛知県' '千葉県' '愛媛県' '大阪府']
「sortコマンドについて詳しくまとめました 【Linuxコマンド集】」を参考にしました。
cut --fields=1 hightemp.txt | sort | uniq > result.txt
千葉県
和歌山県
埼玉県
大阪府
山形県
山梨県
岐阜県
愛媛県
愛知県
群馬県
静岡県
高知県
18.各行を3コラム目の数値の降順にソート
各行を3コラム目の数値の逆順で整列せよ(注意: 各行の内容は変更せずに並び替えよ).確認にはsortコマンドを用いよ(この問題はコマンドで実行した時の結果と合わなくてもよい).
018.各行を3コラム目の数値の降順にソート.ipynb
pandas
のsort_values
関数使いました。
import pandas as pd
df = pd.read_table('hightemp.txt', header=None, usecols=[0])
print(df.sort_values(2, ascending=False))
0 1 2 3
0 高知県 江川崎 41.0 2013-08-12
2 岐阜県 多治見 40.9 2007-08-16
1 埼玉県 熊谷 40.9 2007-08-16
中略
21 山梨県 大月 39.9 1990-07-19
22 山形県 鶴岡 39.9 1978-08-03
23 愛知県 名古屋 39.9 1942-08-02
sort hightemp.txt -k 3 -n -r
高知県 江川崎 41 2013-08-12
岐阜県 多治見 40.9 2007-08-16
埼玉県 熊谷 40.9 2007-08-16
中略
大阪府 豊中 39.9 1994-08-08
埼玉県 鳩山 39.9 1997-07-05
千葉県 茂原 39.9 2013-08-11
19.各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる
各行の1列目の文字列の出現頻度を求め,その高い順に並べて表示せよ.確認にはcut, uniq, sortコマンドを用いよ.
019.各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる.ipynb
pandas
のvalue_counts
関数使いました。
import pandas as pd
df = pd.read_table('hightemp.txt', header=None, usecols=[0])
print(df[0].value_counts(ascending=False))
埼玉県 3
山梨県 3
山形県 3
中略
愛媛県 1
高知県 1
大阪府 1
cut -f 1 hightemp.txt | sort | uniq -c | sort -r
3 群馬県
3 山梨県
3 山形県
中略
1 愛媛県
1 大阪府
1 和歌山県