Edited at

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

More than 1 year has passed since last update.

言語処理100本ノック 2015の挑戦記録です。環境はUbuntu 16.04 LTS + Python 3.5.2 :: Anaconda 4.1.1 (64-bit)です。過去のノックの一覧はこちらからどうぞ。


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


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



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


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



出来上がったコード:


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]))



実行結果:


端末

埼玉県(3)

山形県(3)
山梨県(3)
群馬県(3)
千葉県(2)
岐阜県(2)
愛知県(2)
静岡県(2)
和歌山県(1)
大阪府(1)
愛媛県(1)
高知県(1)


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


test.sh

#!/bin/sh

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



結果の確認:


端末

segavvy@ubuntu:~/ドキュメント/言語処理100本ノック2015/19$ ./test.sh

3 山梨県
3 山形県
3 埼玉県
3 群馬県
2 千葉県
2 静岡県
2 岐阜県
2 愛知県
1 和歌山県
1 大阪府
1 高知県
1 愛媛県

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


内包表記

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


集計処理

都道府県で集計する部分は、itertools.groupby()を利用しました。確認で使っているUNIXのuniq同様、事前にソートしておく必要があるのが注意点ですね。


問題の解釈の誤り

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


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


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


出来上がったコード(勘違い版):


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箇所で使うのでラムダ式ではなく関数にしました。


実行結果(勘違い版):


端末

埼玉県   熊谷  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本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。