言語処理100本ノック2020 (10~19)

# はじめに
本記事は言語処理100本ノックの解説です。
100本のノックを全てこなした記録をQiitaに残します。
使用言語はPythonです。

https://nlp100.github.io/2020/ja/

今回は第2章:UNIXコマンド(10~19)までの解答例をご紹介します。
>第2章: UNIXコマンド
>popular-names.txtは，アメリカで生まれた赤ちゃんの「名前」「性別」「人数」「年」をタブ区切り形式で格納したファイルである．以下の処理を行うプログラムを作成し，popular-names.txtを入力ファイルとして実行せよ．さらに，同様の処理をUNIXコマンドでも実行し，プログラムの実行結果を確認せよ．

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

**Python**
```python:コード
with open("[PATH]/popular-names.txt", "r") as f:
    lines = f.readlines()
    count = len(lines)
    print(count)
```
```text:出力結果
2780
```
**UNIXコマンド**
```text:コマンド
$ wc -l "[PATH]/popular-names.txt"
```
```text:出力結果
2780
```

**コメント**
wc→word countですね。この章はPythonとコマンドのどちら掲載していきます。
# 11. タブをスペースに置換
>タブ1文字につきスペース1文字に置換せよ．確認にはsedコマンド，trコマンド，もしくはexpandコマンドを用いよ．

**Python**

```python:コード
with open("[PATH]/popular-names.txt", "r") as f:
    for line in f:
        print(line.replace('\t', ' '))
```
```text:出力結果
Mary F 7065 1880
Anna F 2604 1880
Emma F 2003 1880
Elizabeth F 1939 1880
Minnie F 1746 1880
Margaret F 1578 1880
~~~~~~~~~~省略~~~~~~~~~~~
```
**UNIXコマンド**
```text:コマンド
$ cat '[PATH]/popular-names.txt' | sed 's/\t/ /'
$ cat '[PATH]/popular-names.txt' | tr '\t' ' '
$ cat '[PATH]/popular-names.txt' | expand
```
```text:出力結果
Mary F 7065 1880
Anna F 2604 1880
Emma F 2003 1880
Elizabeth F 1939 1880
Minnie F 1746 1880
Margaret F 1578 1880
~~~~~~~~~~省略~~~~~~~~~~~
```
**コメント**
UNIXコマンドは3つ作成。catは初見「なんで猫？」ってなりません？

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

**Python**

```python:コード
basepath = "[PATH]/"
with open("{}popular-names.txt".format(basepath), "r") as f, open("{}col1.txt".format(basepath), "w") as fc1, open("{}col2.txt".format(basepath), "w") as fc2:
    lines = f.readlines()
    count = len(lines)
    for i, line in enumerate(lines):
      name, sex, _, _ = line.split(' ')
      if count == i:
        fc1.write(name)
        fc2.write(sex)
      else:
        fc1.write(name + '\n')
        fc2.write(sex + '\n')
```
```text:出力結果(col1.txt)
Mary
Anna
Emma
Elizabeth
Minnie
~~~~~~~~~~省略~~~~~~~~~~~
```
```text:出力結果(col2.txt)
F
F
F
F
F
~~~~~~~~~~省略~~~~~~~~~~~
```
**UNIXコマンド**
```text:コマンド
#1列目
$ cut -f 1 -d " " '[PATH]/popular-names.txt'
#2列目
$ cut -f 2 -d " " '[PATH]/popular-names.txt'
```
```text:出力結果
#1列目
Mary
Anna
Emma
Elizabeth
Minnie
~~~~~~~~~~省略~~~~~~~~~~~

#2列目
F
F
F
F
F
~~~~~~~~~~省略~~~~~~~~~~~
```
**コメント**
テキスト出力するときに最終行だけ改行コード外す方法、ちょっと考えがち。
# 13. col1.txtとcol2.txtをマージ
>12で作ったcol1.txtとcol2.txtを結合し，元のファイルの1列目と2列目をタブ区切りで並べたテキストファイルを作成せよ．確認にはpasteコマンドを用いよ．

**Python**

```python:コード
basepath = "[PATH]/"
with open("{}col1.txt".format(basepath), "r") as fc1, open("{}col2.txt".format(basepath), "r") as fc2, open("{}col1col2.txt".format(basepath), "w") as f:
    i = 0
    linesfc1 = fc1.readlines()
    linesfc2 = fc2.readlines()
    count = len(lines)
    for name, sex in zip(linesfc1, linesfc2):
        name = name.replace("\n", "")
        sex = sex.replace("\n", "")
        i += 1
        if count == i:
            f.write(name + "\t" + sex)
        else:
            f.write(name + "\t" + sex + "\n")
```
```text:出力結果(col1col2.txt)
Mary	F
Anna	F
Emma	F
Elizabeth	F
Minnie	F
~~~~~~~~~~省略~~~~~~~~~~~
```
**UNIXコマンド**
```text:コマンド
$ paste '[PATH]/col1.txt' '[PATH]/col2.txt'
```
```text:出力結果
Mary	F
Anna	F
Emma	F
Elizabeth	F
Minnie	F
~~~~~~~~~~省略~~~~~~~~~~~
```
**コメント**
pasteコマンドはデフォルトでタブ区切りにしてくれるからオプション無しでオッケー。

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

**Python**

```python:コード
basepath = "[PATH]/"
with open("{}popular-names.txt".format(basepath), "r") as f:
    val = int(input())
    lines = f.readlines()
    for i in range(val):
        lines[i] = lines[i].replace('\n', '')
        print(lines[i])
```
```text:出力結果(N=5)
Mary F 7065 1880
Anna F 2604 1880
Emma F 2003 1880
Elizabeth F 1939 1880
Minnie F 1746 1880
```
**UNIXコマンド**
```text:コマンド
$ head -n 5 "[PATH]/popular-names.txt"
```
```text:出力結果
Mary F 7065 1880
Anna F 2604 1880
Emma F 2003 1880
Elizabeth F 1939 1880
Minnie F 1746 1880
```
**コメント**
Pythonのinput()ってあまり使わないですよね。

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

**Python**

```python:コード
basepath = "[PATH]/"
with open("{}popular-names.txt".format(basepath), "r") as f:
    val = int(input())
    lines = f.readlines()
    count = len(lines) - 1
    for i in reversed(range(val)):
        result = lines[count - i].replace('\n', '')
        print(result)
```
```text:出力結果
Benjamin M 13381 2018
Elijah M 12886 2018
Lucas M 12585 2018
Mason M 12435 2018
Logan M 12352 2018
```
**UNIXコマンド**
```text:コマンド
$ tail -n 5 "[PATH]/popular-names.txt"
```
```text:出力結果
Benjamin M 13381 2018
Elijah M 12886 2018
Lucas M 12585 2018
Mason M 12435 2018
Logan M 12352 2018
```
**コメント**
今度は後ろから取ってくる問題。リストの番号指定を工夫するところ。

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

**Python**

```python:コード
import numpy as np
basepath = "[PATH]/"
with open("{}popular-names.txt".format(basepath), "r") as f:
    val = int(input())
    lines = f.readlines()
    count = len(lines)
    num_list = range(count)
    div_list = np.array_split(num_list, val)
    for i, div in enumerate(div_list, 1):
        f = open('{}{}.txt'.format(basepath, str(i).zfill(3)), 'w')
        for j in div:
            f.write(lines[j])
        f.close()
```
```text:出力結果(N=3)
# 001.txt
Mary F 7065 1880
Anna F 2604 1880
Emma F 2003 1880
~~~省略~~~
# 002.txt
Virginia F 16162 1926
Mildred F 13551 1926
Frances F 13355 1926
~~~省略~~~
# 003.txt
John M 43181 1972
Robert M 43037 1972
Jason M 37446 1972
```
**UNIXコマンド**
```text:コマンド
$ split -n 3 -d --additional-suffix=.txt "[PATH]/popular-names.txt" "[出力先のPATH]"
```
```text:出力結果
# 00.txt
Mary F 7065 1880
Anna F 2604 1880
Emma F 2003 1880
~~~省略~~~
# 01.txt
Donald M 23840 1927
Joseph M 23506 1927
Edward M 19113 1927
~~~省略~~~
# 02.txt
Matthew M 24546 1973
Jennifer F 63114 1974
Amy F 29565 1974
~~~省略~~~
```
**コメント**
分割したファイルの名前は結局何が良いのか。。
# 17. １列目の文字列の異なり
>1列目の文字列の種類（異なる文字列の集合）を求めよ．確認にはcut, sort, uniqコマンドを用いよ．

**Python**

```python:コード
basepath = "[PATH]/"
with open("{}popular-names.txt".format(basepath), "r") as f:
    lines = f.readlines()
    name = sorted(set(map(lambda x: x.split(' ')[0], lines)))
print(name)
```
```text:出力結果
['Abigail', 'Aiden', 'Alexander', 'Alexis', 'Alice', ~~省略
```
**UNIXコマンド**
```text:コマンド
$ cut -d ' ' -f 1 "[PATH]/popular-names.txt"|sort|uniq
```
```text:出力結果
Abigail
Aiden
Alexander
Alexis
Alice
~~~省略~~~
```
**コメント**
めっちゃ入れ子構造になった。ここにきてlambdaが初登場。lambda関数を理解できたらPython中級者ですかね？

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

**Python**

```python:コード
basepath = "[PATH]/"
with open("{}popular-names.txt".format(basepath), "r") as f:
  lines = f.readlines()
  lines = map(lambda x: x.replace("\n",""), lines)
  temp = list(map(lambda x: x.split(' '), lines))
  pnum = sorted(temp, key=lambda x: int(x[2]), reverse=True)
print(pnum)
```
```text:出力結果
[['Linda', 'F', '99689', '1947'], ['Linda', 'F', '96211', '1948'], ['James', 'M', '94757', '1947'], ~~省略
```
**UNIXコマンド**
```text:コマンド
$ sort -n -r -k 3,3 -t " " "[PATH]/popular-names.txt"
```
```text:出力結果
Linda F 99689 1947
Linda F 96211 1948
James M 94757 1947
~~~省略~~~
```
**コメント**
UNIXコマンドのsortにたくさんオプションがついてる。

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

**Python**

```python:コード
import collections
basepath = "[PATH]/"
with open("{}popular-names.txt".format(basepath), "r") as f:
  lines = f.readlines()
  temp = list(map(lambda x: x.split(' '), lines))
  c = collections.Counter(map(lambda x: x[0], temp))
  c2 = c.most_common()
print(c2)
```
```text:出力結果
[('James', 118), ('William', 111), ('John', 108), ('Robert', 108), ('Mary', 92),~~省略
```
**UNIXコマンド**
```text:コマンド
$ cut -d ' ' -f 1 "[PATH]/popular-names.txt"|sort|uniq -c|sort -r -n
```
```text:出力結果
118 James
111 William
108 Robert
108 John
92 Mary
~~~省略~~~
```
**コメント**
collectionsモジュールを使用。Pythonは便利なモジュールが揃っていていいですよね～～

# 他章の解答例

https://qiita.com/Dukapan100knock/items/d418f2aa37b2b3d16428