言語処理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コマンドを用いよ.
####出来上がったコード:
# 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コマンド確認用のシェルスクリプト:
#!/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()]
(http://docs.python.jp/3/library/itertools.html#itertools.groupby)を利用しました。確認で使っているUNIXの`uniq`同様、事前にソートしておく必要があるのが注意点ですね。
###問題の解釈の誤り
実は最初、問題の意味を取り違えていて、
各行の1列目の文字列の出現頻度を求め,その高い順に【各行を】並べて表示せよ.
だと思っていました。せっかくなので、そのコードと結果も載せておきます。
####出来上がったコード(勘違い版):
# 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本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。