schedule2018-07-21

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

はじめに

言語処理100本ノック 2015

Pythonを勉強するため、東京工業大学の岡崎教授が出題されている言語処理100本ノック 2015を解いていきます。

より深く理解するため、別解や利用したライブラリの解説もまとめていきます。

環境

Python3.6

OS : mac
2章からUNIXコマンドの問題ですが、macもターミナルから利用できるようです。

問題

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

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

解答:Python

points

  • 1列目の配列を取り出す
  • 空白文字などは削除しておく
  • それぞれの個数を求める
  • 多い順にソートする

program

import collections


def read_file(file_name):
    with open(file_name, 'r') as file:
        return file.read()

lines = read_file('hightemp.txt').split('')

# 空行の削除
lines = list(filter(lambda line: line != '', lines))

# 行をタブで分割
lines = list(map(lambda line: line.split('\t'), lines))

# 1列目の取り出し
cal1 = list(map(lambda line: line[0].strip(), lines))

# 要素と数を多い順に取り出す。
counted_cal1 = collections.Counter(cal1).most_common()
print(counted_cal1)
# => [('埼玉県', 3), ('山形県', 3), ('山梨県', 3), ('群馬県', 3), ('岐阜県', 2), ('静岡県', 2), ('愛知県', 2), ('千葉県', 2), ('高知県', 1), ('和歌山県', 1), ('愛媛県', 1), ('大阪府', 1)]

# タブ区切りで行を結合
counted_cal1 = list(map(lambda x: f'{x[1]}\t{x[0]}', counted_cal1))

print("".join(counted_cal1))

出力

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

解説

入力を2次元配列にするまでは、前回までの問題と同じです。

# 1列目の取り出し
cal1 = list(map(lambda line: line[0].strip(), lines))

この行で、1列目を取り出しつつ、空白文字も除いておきます。

import collections

# 要素と数を多い順に取り出す。
counted_cal1 = collections.Counter(cal1).most_common()
print(counted_cal1)
# => [('埼玉県', 3), ('山形県', 3), ('山梨県', 3), ('群馬県', 3), ('岐阜県', 2), ('静岡県', 2), ('愛知県', 2), ('千葉県', 2), ('高知県', 1), ('和歌山県', 1), ('愛媛県', 1), ('大阪府', 1)]

カウントの方法を探していたら、collections.Counter()に行き着いてしまいました。

これは、要素を辞書のキーとして保存し、そのカウントを辞書の値として保存します。
しかも、most_common()の関数で数の多い順に並び替えてもくれます。

つまり、ほぼ答えです。。。

1行で書けてしまいました。

#  要素の数を数える
counted_cal1 = [(x, cal1.count(x)) for x in set(cal1)]
# 指定列で逆順ソート
counted_cal1 = sorted(counted_cal1, key=lambda x: x[1], reverse=True)

一応こんな感じで、set()で集合を出して、連想配列の中で数を数えて。。。と考えていましたが、collectionsを使った方がスマートですね。

# タブ区切りで行を結合
counted_cal1 = list(map(lambda x: f'{x[1]}\t{x[0]}', counted_cal1))

print("".join(counted_cal1))

最後に、書式を整えて出力します。

解答:UNIXコマンド

考え中。。。

前の問題:18. 各行を3コラム目の数値の降順にソート

次の問題:

続いての記事

Python3で言語処理100本ノックまとめ

前の問題:18. 各行を3コラム目の数値の降順にソート

次の問題:20. JSONデータの読み込み