2015/02/24

Python Tips:文章中の単語を集計したい

Python で英語文章中の単語を集計する方法をご紹介します。

大まかな流れは次のとおりになるでしょうか。

  1. 文章を単語に分けて
  2. 集計して
  3. 表示する

collectons の Counter というクラスを使うときれいに書くことができます。

# ライブラリの読み込み
import re
from collections import Counter

# 00 テキストの取得
target_text = """
Python is a remarkably powerful dynamic programming language that is used in a wide variety of application domains. Python is often compared to Tcl, Perl, Ruby, Scheme or Java. Some of its key distinguishing features include:

very clear, readable syntax
strong introspection capabilities
intuitive object orientation
natural expression of procedural code
full modularity, supporting hierarchical packages
exception-based error handling
very high level dynamic data types
extensive standard libraries and third party modules for virtually every task
extensions and modules easily written in C, C++ (or Java for Jython, or .NET languages for IronPython)
embeddable within applications as a scripting interface
"""

# 01 文章を単語に分ける
# 複数の区切り文字を指定するため re.split を使う
words = re.split(r'\s|\,|\.|\(|\)', target_text.lower())

# 02 集計する
counter = Counter(words)

# 03 表示する
# Counter#most_common を使って出現頻度の降順に csv 形式で表示する
for word, count in counter.most_common():
    if len(word) > 0:
        print("%s,%d" % (word, count))
# => csv 形式の単語出現回数


以上です。少し変更して標準入力を受け付けるようにすると、ちょっとした英単語カウンタとして使えます。


参考

Python: Split string with multiple delimiters - Stack Overflow

2015/02/13

Python Tips:標準入力を 1 行ずつ処理したい

Python で標準入力から入力を得て 1 行ずつ処理する方法をご紹介します。

ちょっとしたコマンドラインツールを作るときに 1 行ずつ処理できれば便利かと思います。

やり方はいくつか考えられるかと思いますが、ひとつカンタンな方法は fileinput ライブラリを使う方法です。

# ライブラリを読み込み
import fileinput

# 入力の各行に対して処理を行う
for line in fileinput.input():
# some process...
pass

たとえば、単語の数を行ごとに数えてみたいなら次のようにすれば OK です。

# word_count.py
import fileinput

for line in fileinput.input():
    print(len(line.split()))

次の内容のファイル sample.txt があったとすると

Freude, schöner Götterfunken
Tochter aus Elysium,
Wir betreten feuertrunken,
Himmlische, dein Heiligtum!
Deine Zauber binden wieder
Was die Mode streng geteilt;
Alle Menschen werden Brüder,
Wo dein sanfter Flügel weilt.


以下のコマンドで行ごとの単語数を返してくれます。

$ cat sample.txt | python word_count.py
3
3
3
3
4
5
4
5

上記のスクリプトは次のような形で使うこともできます。

$ python word_count.py sample.txt

こちらの場合も返ってくる結果は同じです。

こうなるのは、 fileinput.input() は引数が与えられればそれらを cat したような形でまとめて順番に 1 行する処理するようになっているからです。

fileinput にはこの他にもコマンドライン引数にファイルを渡したい場合に便利な関数がもろもろ用意されています。興味があれば公式ドキュメントの方も見てみてください。


参考
fileinput - Python 公式ドキュメント

2015/02/10

Python Tips:複数のリストに対してループを回したい

Pythonで複数のリストに対してループを同時に回す方法をご紹介します。

最もシンプルなのは zip 関数を使う方法です。

さっそく例を見てみます。まず最初は同じ長さのふたつのリストの場合。
li1 = [1, 2, 3]
li2 = [11, 12, 13]
zip(li1, li2)
# => [(1, 11), (2, 12), (3, 13)]
戻り値は各要素をペアにしたタプルのリストとなります。

リストの長さが異なる場合は短い方のリストに合わせられます。
li1 = [1, 2, 3, 4, 5]
li2 = [11, 12, 13]
zip(li1, li2)
# => [(1, 11), (2, 12), (3, 13)]

みっつ以上のリストの場合もふたつの場合と同じようにまとめてくれます。
li1 = [1, 2, 3]
li2 = [11, 12, 13]
li3 = ['a', 'b', 'c']
zip(li1, li2, li3)
# => [(1, 11, 'a'), (2, 12, 'b'), (3, 13, 'c')]

逆にリストがひとつの場合も例外をあげることなく動作してくれます。
li1 = [1, 2, 3]
zip(li1)
# => [(1,), (2,), (3,)]


生成される合成リストの長さを最短のリストではなく最長のリストに合わせたい場合は itertools.zip_longest を使うのが便利です。
import itertools

li1 = [1, 2, 3, 4, 5]
li2 = [11, 12, 13]
print([x for x in itertools.zip_longest(li1, li2)])
# => [(1, 11), (2, 12), (3, 13), (None, 14), (None, 15)]


以上です。

ちなみに組み込みの zip と同じような処理を自分で書くとすると次のような形になるかと思います。
def myzip(*lists):
    """組み込みの zip と同じ処理をする関数 myzip """
    i = 0
    combined = []
    while True:
        try:
            # 各リストの同じインデックスの要素をタプルにまとめて追加
            # インデックスがどれかひとつの要素の長さを越えれば break
            e = []
            for li in lists:
                e.append(li[i])
            combined.append(tuple(e))
            i += 1
        except IndexError:
            break
    return combined

またまたちなみに、Python3では zip 関数はリストではなくイテレータを返す形に変更されています。


参考
iterator - how can I iterate through two lists in parallel in Python? - Stack Overflow


2015/02/03

Python Tips:IPython の起動時に特定の処理を走らせたい

IPython で起動時に特定の処理を走らせる方法をご紹介します。

IPython で起動時の処理を設定するには「プロファイル」というものを利用する形となります( IPython 1.1 の場合)。

ちなみに、 IPython の 1.1 ではプロファイルを利用する形となっていますが、以前のバージョンでは .ipythonrc を使うなどその他の方法が採用されていたようです。

以下、具体的なやり方を見ていきます。対象となる IPython のバージョンは 1.1 です。


01 プロファイルの作成

まずは IPython の「プロファイル」というものを作成します。
$ ipython profile create some_profile_name
some_profile_name のところを省略すれば、ホームディレクトリ以下に .ipython/profile_default ディレクトリが作られ、その中にデフォルトの設定ファイルが作成されます。今回はそこを省略したものとして以下進めていきます。

.ipython/profile_default の中をのぞくと ipython_config.py というファイルが入っています。これが IPython 起動時の設定を書き込むためのものです。


02 設定ファイルの編集

ipython_config.py の中にはプロファイルを作成時にすでにひな形が書き込まれているので、その中に適宜設定を追加します。

たとえば、特定のライブラリを読み込みたいなら、 ipython_config.py の中の次の一文を探します。
c = get_config()

これを以下の内容に変更します。
c = get_config()

c.InteractiveShellApp.exec_lines = [
    'import numpy',
    'import scipy'
]
こうすることで、 numpy と scipy が IPython の起動時に読み込まれる形となります。

特定の読み込みパスを追加しておきたい場合は次のようにします。
c.InteractiveShellApp.exec_lines = [
    'import sys',
    'sys.path.append(some_path_you_want_to_add)'
]

IPython を立ち上げたらほぼ毎回使うような使用頻度が高いライブラリやパスなんかはここで読み込んでおくとようにすると便利です。

以上です。


ライブラリの読み込み以外にもさまざまな設定ができるようになっているので、詳しくは公式ドキュメントをご参照ください。


参考
Introduction to IPython configuration#profiles — IPython公式ドキュメント