言語処理100本ノック:第2章 UNIXコマンドの基礎(pandas使用)

[言語処理100本ノック 2015](http://www.cl.ecei.tohoku.ac.jp/nlp100/)の[「第2章: UNIXコマンドの基礎」](http://www.cl.ecei.tohoku.ac.jp/nlp100/#ch2)の記録です。
第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を使っています<br>3.7や3.8系を使っていないことに深い理由はありません<br>パッケージはvenvを使って管理しています|

# [第2章: UNIXコマンドの基礎](http://www.cl.ecei.tohoku.ac.jp/nlp100/#ch2)
## 学習内容
> 研究やデータ分析において便利なUNIXツールを体験します．これらの再実装を通じて，プログラミング能力を高めつつ，既存のツールのエコシステムを体感します．
>> head, tail, cut, paste, split, sort, uniq, sed, tr, expand

## ノック内容
> [hightemp.txt](http://www.cl.ecei.tohoku.ac.jp/nlp100/data/hightemp.txt)は，日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである．以下の処理を行うプログラムを作成し，hightemp.txtを入力ファイルとして実行せよ．さらに，同様の処理をUNIXコマンドでも実行し，プログラムの実行結果を確認せよ．

## [10. 行数のカウント](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec10)
> 行数をカウントせよ．確認にはwcコマンドを用いよ．

### [010.行数のカウント.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/02.UNIX%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AE%E5%9F%BA%E7%A4%8E/010.%E8%A1%8C%E6%95%B0%E3%81%AE%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88.ipynb)
Pythonだと`readlines`で一気に読むのが一番速いはず(たいして調べていません)。

```python:Python部分
print(len(open('./hightemp.txt').readlines()))
```

```bash:ターミナル出力結果
24
```

wcはWord Countの略でしょう。`-l`オプションで改行コードをカウントします。**大きなファイルはテキストエディタで開くだけでも時間がかかるので重宝します**。

```bash:Bash部分
wc hightemp.txt -l
```

```bash:ターミナル出力結果
24 hightemp.txt
```

## [11.タブをスペースに置換](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec11)
> タブ1文字につきスペース1文字に置換せよ．確認にはsedコマンド，trコマンド，もしくはexpandコマンドを用いよ．

### [011.タブをスペースに置換.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/02.UNIX%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AE%E5%9F%BA%E7%A4%8E/011.%E3%82%BF%E3%83%96%E3%82%92%E3%82%B9%E3%83%9A%E3%83%BC%E3%82%B9%E3%81%AB%E7%BD%AE%E6%8F%9B.ipynb)
`replace`関数を使って置換します。改行しないと結果が見にくいので`pprint`使っています。

```python:Python部分
from pprint import pprint

with open('./hightemp.txt') as f:
    pprint([line.replace('\t', ' ')for line in f])
```

```bash:ターミナル出力結果
['高知県 江川崎 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 】 文字列の置換，行の削除を行う」](http://tech.nikkeibp.co.jp/it/article/COLUMN/20060227/230879/)を参考にしました。

```bash:Bash部分
sed 's/\t/ /g' ./hightemp.txt
```

```bash:ターミナル出力結果
高知県 江川崎 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に保存](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec12)
> 各行の1列目だけを抜き出したものをcol1.txtに，2列目だけを抜き出したものをcol2.txtとしてファイルに保存せよ．確認にはcutコマンドを用いよ．

### [012.1列目をcol1.txtに，2列目をcol2.txtに保存.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/02.UNIX%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AE%E5%9F%BA%E7%A4%8E/012.1%E5%88%97%E7%9B%AE%E3%82%92col1.txt%E3%81%AB%EF%BC%8C2%E5%88%97%E7%9B%AE%E3%82%92col2.txt%E3%81%AB%E4%BF%9D%E5%AD%98.ipynb)
`Pandas`を使いました。パラメータ`usecols`で読み込む列を1と2列目に限定しています。便利ですね。

```python:Python部分
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 】コマンド――行から固定長またはフィールド単位で切り出す」](http://www.atmarkit.co.jp/ait/articles/1610/31/news026.html)を参考にしました。

```bash:Bash部分
cut -f 1 ./hightemp.txt
cut -f 2 ./hightemp.txt
```

```bash:ターミナル出力結果(1列目)
高知県
埼玉県
岐阜県

中略

山梨県
山形県
愛知県
```

```bash:ターミナル出力結果(2列目)
江川崎
熊谷
多治見
山形

中略

大月
鶴岡
名古屋
```


## [13.col1.txtとcol2.txtをマージ](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec13)
> 12で作ったcol1.txtとcol2.txtを結合し，元のファイルの1列目と2列目をタブ区切りで並べたテキストファイルを作成せよ．確認にはpasteコマンドを用いよ．

### [013.col1.txtとcol2.txtをマージ.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/02.UNIX%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AE%E5%9F%BA%E7%A4%8E/013.col1.txt%E3%81%A8col2.txt%E3%82%92%E3%83%9E%E3%83%BC%E3%82%B8.ipynb)
`pandas`で2つのファイルを読んでつなげています。

```python:Python部分
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コマンド集】」](https://eng-entrance.com/linux-command-paste)を参考にしました。出力結果は省略します。

```bash:Bash部分
paste 012.col1.txt 012.col2.txt
```

## [14.先頭からN行を出力](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec14)
> 自然数Nをコマンドライン引数などの手段で受け取り，入力のうち先頭のN行だけを表示せよ．確認にはheadコマンドを用いよ．

### [014.先頭からN行を出力.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/02.UNIX%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AE%E5%9F%BA%E7%A4%8E/014.%E5%85%88%E9%A0%AD%E3%81%8B%E3%82%89N%E8%A1%8C%E3%82%92%E5%87%BA%E5%8A%9B.ipynb)
`input`関数で引数を受け取っています。

```python:Python部分
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
```

```bash:ターミナル出力結果
'高知県\t江川崎\t41\t2013-08-12\n'
'埼玉県\t熊谷\t40.9\t2007-08-16\n'
'岐阜県\t多治見\t40.9\t2007-08-16\n'
```

[「ファイルの先頭から表示するheadコマンドの詳細まとめ【Linuxコマンド集】」](https://eng-entrance.com/linux-command-head)を参考にしました。

```bash:Bash部分
head hightemp.txt -n 3
```

```bash:ターミナル出力結果
高知県	江川崎	41	2013-08-12
埼玉県	熊谷	40.9	2007-08-16
岐阜県	多治見	40.9	2007-08-16
```

## [15.末尾のN行を出力](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec15)
> 自然数Nをコマンドライン引数などの手段で受け取り，入力のうち末尾のN行だけを表示せよ．確認にはtailコマンドを用いよ．

### [015.末尾のN行を出力.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/02.UNIX%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AE%E5%9F%BA%E7%A4%8E/015.%E6%9C%AB%E5%B0%BE%E3%81%AEN%E8%A1%8C%E3%82%92%E5%87%BA%E5%8A%9B.ipynb)
これは結構迷いました。ファイルが大きい場合に全件読み込むのが嫌だったのでLinuxの`head`で全件を確認した後に[`linecache`パッケージ](https://docs.python.org/ja/3/library/linecache.html)を使おうかとも思いましたが、だったら`tail`でいいじゃん状態です。結局`readlines`使いました。

```python:Python部分
from pprint import pprint

n = int(input('N Lines--> '))

with open('hightemp.txt') as f:
    pprint(f.readlines()[-n:])
```

```bash:ターミナル出力結果
['山梨県\t大月\t39.9\t1990-07-19\n',
 '山形県\t鶴岡\t39.9\t1978-08-03\n',
 '愛知県\t名古屋\t39.9\t1942-08-02\n']
```

```bash:Bash部分
tail hightemp.txt -n 3
```

```bash:ターミナル出力結果
山梨県	大月	39.9	1990-07-19
山形県	鶴岡	39.9	1978-08-03
愛知県	名古屋	39.9	1942-08-02
```

## [16.ファイルをN分割する](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec16)
> 自然数Nをコマンドライン引数などの手段で受け取り，入力のファイルを行単位でN分割せよ．同様の処理をsplitコマンドで実現せよ．

### [016.ファイルをN分割する.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/02.UNIX%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AE%E5%9F%BA%E7%A4%8E/016.%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92N%E5%88%86%E5%89%B2%E3%81%99%E3%82%8B.ipynb)
`math`パッケージの`ceil`関数を使って商を切り上げています。
ファイル書き出しは`writelines`関数で一括追加しています。

```python:Python部分
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 】コマンド――ファイルを分割する」](http://www.atmarkit.co.jp/ait/articles/1711/24/news016.html)を参考にしました。

```bash:Bash部分
split -n 3 -d hightemp.txt 016.hightemp-u
```

## [17.１列目の文字列の異なり](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec17)
> 1列目の文字列の種類（異なる文字列の集合）を求めよ．確認にはsort, uniqコマンドを用いよ．

### [017.１列目の文字列の異なり.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/02.UNIX%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AE%E5%9F%BA%E7%A4%8E/017.%EF%BC%91%E5%88%97%E7%9B%AE%E3%81%AE%E6%96%87%E5%AD%97%E5%88%97%E3%81%AE%E7%95%B0%E3%81%AA%E3%82%8A.ipynb)
`pandas`の`unique`関数使いました。`pandas`は、この手の処理は非常に楽にできます。

```python:Python部分
import pandas as pd

df = pd.read_table('hightemp.txt', header=None, usecols=[0])

print(df[0].unique())
```

```bash:ターミナル出力結果
['高知県' '埼玉県' '岐阜県' '山形県' '山梨県' '和歌山県' '静岡県' '群馬県' '愛知県' '千葉県' '愛媛県' '大阪府']
```

[「sortコマンドについて詳しくまとめました 【Linuxコマンド集】」](https://eng-entrance.com/linux-command-sort)を参考にしました。

```bash:Bash部分
cut --fields=1 hightemp.txt | sort | uniq > result.txt
```

```bash:ターミナル出力結果
千葉県
和歌山県
埼玉県
大阪府
山形県
山梨県
岐阜県
愛媛県
愛知県
群馬県
静岡県
高知県
```

## [18.各行を3コラム目の数値の降順にソート](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec18)
> 各行を3コラム目の数値の逆順で整列せよ（注意: 各行の内容は変更せずに並び替えよ）．確認にはsortコマンドを用いよ（この問題はコマンドで実行した時の結果と合わなくてもよい）．

### [018.各行を3コラム目の数値の降順にソート.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/02.UNIX%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AE%E5%9F%BA%E7%A4%8E/018.%E5%90%84%E8%A1%8C%E3%82%923%E3%82%B3%E3%83%A9%E3%83%A0%E7%9B%AE%E3%81%AE%E6%95%B0%E5%80%A4%E3%81%AE%E9%99%8D%E9%A0%86%E3%81%AB%E3%82%BD%E3%83%BC%E3%83%88.ipynb)
`pandas`の`sort_values`関数使いました。

```python:Python部分
import pandas as pd

df = pd.read_table('hightemp.txt', header=None, usecols=[0])

print(df.sort_values(2, ascending=False))
```

```bash:ターミナル出力結果
       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
```

```bash:Bash部分
sort hightemp.txt -k 3 -n -r
```

```bash:ターミナル出力結果
高知県	江川崎	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コラム目の文字列の出現頻度を求め，出現頻度の高い順に並べる](http://www.cl.ecei.tohoku.ac.jp/nlp100/#sec19)
> 各行の1列目の文字列の出現頻度を求め，その高い順に並べて表示せよ．確認にはcut, uniq, sortコマンドを用いよ．

### [019.各行の1コラム目の文字列の出現頻度を求め，出現頻度の高い順に並べる.ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/02.UNIX%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AE%E5%9F%BA%E7%A4%8E/019.%E5%90%84%E8%A1%8C%E3%81%AE1%E3%82%B3%E3%83%A9%E3%83%A0%E7%9B%AE%E3%81%AE%E6%96%87%E5%AD%97%E5%88%97%E3%81%AE%E5%87%BA%E7%8F%BE%E9%A0%BB%E5%BA%A6%E3%82%92%E6%B1%82%E3%82%81%EF%BC%8C%E5%87%BA%E7%8F%BE%E9%A0%BB%E5%BA%A6%E3%81%AE%E9%AB%98%E3%81%84%E9%A0%86%E3%81%AB%E4%B8%A6%E3%81%B9%E3%82%8B.ipynb)
`pandas`の`value_counts`関数使いました。

```python:Python部分
import pandas as pd

df = pd.read_table('hightemp.txt', header=None, usecols=[0])

print(df[0].value_counts(ascending=False))
```

```bash:ターミナル出力結果
埼玉県     3
山梨県     3
山形県     3

中略

愛媛県     1
高知県     1
大阪府     1
```

```bash:Bash部分
cut -f 1 hightemp.txt | sort | uniq -c | sort -r
```

```bash:ターミナル出力結果
      3 群馬県
      3 山梨県
      3 山形県

中略

      1 愛媛県
      1 大阪府
      1 和歌山県
```