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


[言語処理100本ノック 2015](http://www.cl.ecei.tohoku.ac.jp/nlp100/)の挑戦記録です。環境はUbuntu 16.04 LTS ＋ Python 3.5.2 \:\: Anaconda 4.1.1 (64-bit)です。過去のノックの一覧は[こちら](http://qiita.com/segavvy/items/fb50ba8097d59475f760)からどうぞ。

## 第2章: UNIXコマンドの基礎
>hightemp.txtは，日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである．以下の処理を行うプログラムを作成し，hightemp.txtを入力ファイルとして実行せよ．さらに，同様の処理をUNIXコマンドでも実行し，プログラムの実行結果を確認せよ．

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

####出来上がったコード：

```python:main.py
# coding: utf-8
from itertools import groupby
fname = 'hightemp.txt'

# 都道府県名の読み込み
lines = open(fname).readlines()
kens = [line.split('\t')[0] for line in lines]

# 都道府県で集計し、(都道府県, 出現頻度)のリスト作成
kens.sort()    # goupbyはソート済みが前提
result = [(ken, len(list(group))) for ken, group in groupby(kens)]

# 出現頻度でソート
result.sort(key=lambda ken: ken[1], reverse=True)

# 結果出力
for ken in result:
	print('{ken}({count})'.format(ken=ken[0], count=ken[1]))
```

####実行結果：

```console:端末
埼玉県(3)
山形県(3)
山梨県(3)
群馬県(3)
千葉県(2)
岐阜県(2)
愛知県(2)
静岡県(2)
和歌山県(1)
大阪府(1)
愛媛県(1)
高知県(1)
```

####UNIXコマンド確認用のシェルスクリプト:

```bash:test.sh
#!/bin/sh

# 1カラム目でソートし、重複除去して件数付きで出力、その結果をソート
cut --fields=1 hightemp.txt | sort | uniq --count | sort --reverse
```

####結果の確認：

```console:端末
segavvy@ubuntu:~/ドキュメント/言語処理100本ノック2015/19$ ./test.sh
      3 山梨県
      3 山形県
      3 埼玉県
      3 群馬県
      2 千葉県
      2 静岡県
      2 岐阜県
      2 愛知県
      1 和歌山県
      1 大阪府
      1 高知県
      1 愛媛県
```

前問同様、出現頻度が同じところの並びは変わってしまうので、書式を合わせて`diff`するところまではがんばらず、目視で確認しました。

###内包表記
今回初めて内包表記を使ってみました。リストを作成する部分は、なんとか読み書きできるようになってきました。

###集計処理
都道府県で集計する部分は、[itertools.groupby()]
(http://docs.python.jp/3/library/itertools.html#itertools.groupby)を利用しました。確認で使っているUNIXの`uniq`同様、事前にソートしておく必要があるのが注意点ですね。

###問題の解釈の誤り
実は最初、問題の意味を取り違えていて、

> 各行の1列目の文字列の出現頻度を求め，その高い順に【各行を】並べて表示せよ．

だと思っていました。せっかくなので、そのコードと結果も載せておきます。

####出来上がったコード（勘違い版）：

```python:main2.py
# coding: utf-8
from itertools import groupby
fname = 'hightemp.txt'


def get_ken(target):
	'''1行分のデータから都道府県部分を切り出す

	引数：
	target -- 1行分のデータ
	戻り値：
	都道府県の文字列
	'''
	return target.split('\t')[0]

# 読み込み
lines = open(fname).readlines()

# 都道府県で集計
lines.sort(key=get_ken)    # goupbyはソート済みが前提
groups = groupby(lines, key=get_ken)

# 集計結果を(都道府県, 出現頻度, 該当行のリスト)のリストに変換
result = []
for ken, group in groups:
	lines = list(group)
	result.append((ken, len(lines), lines))

# 出現頻度でソート
result.sort(key=lambda group: group[1], reverse=True)

# 結果表示
for group in result:
	for line in group[2]:
		print(line, end='')
```

`get_ken()`は1カラム目の都道府県を取り出す関数で、`sort()`と`groupby()`の2箇所で使うのでラムダ式ではなく関数にしました。

####実行結果（勘違い版）：

```console:端末
埼玉県	熊谷	40.9	2007-08-16
埼玉県	越谷	40.4	2007-08-16
埼玉県	鳩山	39.9	1997-07-05
山形県	山形	40.8	1933-07-25
山形県	酒田	40.1	1978-08-03
山形県	鶴岡	39.9	1978-08-03
山梨県	甲府	40.7	2013-08-10
山梨県	勝沼	40.5	2013-08-10
山梨県	大月	39.9	1990-07-19
群馬県	館林	40.3	2007-08-16
群馬県	上里見	40.3	1998-07-04
群馬県	前橋	40	2001-07-24
千葉県	牛久	40.2	2004-07-20
千葉県	茂原	39.9	2013-08-11
岐阜県	多治見	40.9	2007-08-16
岐阜県	美濃	40	2007-08-16
愛知県	愛西	40.3	1994-08-05
愛知県	名古屋	39.9	1942-08-02
静岡県	天竜	40.6	1994-08-04
静岡県	佐久間	40.2	2001-07-24
和歌山県	かつらぎ	40.6	1994-08-08
大阪府	豊中	39.9	1994-08-08
愛媛県	宇和島	40.2	1927-07-22
高知県	江川崎	41	2013-08-12
```

　
20本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。