ECDF and __call__ method

July 2, 2015

本日は, Quantative Economics/Object Oriented Programming - Excercise が最初テーマ. 初級者にはやや難しいトピックも含まれているので備忘録として記録しておきます.

Excercise 1: __call__ メソッドについて

Excercise 1 はサンプルデータから累積分布関数を構築するクラス ECDF を作る問題. ただし, __call__ メソッドを使って作るというのが条件.

具体的な例として, 下のような使い方ができるようにとのこと.

>>> from random import uniform
>>> samples = [uniform(0, 1) for _ in range(10)]
>>> F = ECDF(samples)
>>> F(0.5)  # Evaluate ecdf at x = 0.5
0.29
>>> F.observations = [uniform(0, 1) for _ in range(1000)]
>>> F(0.5)
0.479

こちら に掲載されている解答を再掲しておきます:

class ECDF(object):

    def __init__(self, observations):
        self.observations = observations

    def __call__(self, x):
        counter = 0.0
        for obs in self.observations:
            if obs <= x:
                counter += 1
        return counter / len(self.observations)

クラスに __call__ メソッドを追加するとインスタンスを関数として扱うことができるようになる というのがポイントで, F(0.5) という書き方が許されるのも F が関数 (callable) と同じ ように振る舞うように作られているからです.

この練習問題では「observations をパラメータとして累積分布関数を定義するクラス」を 作っています. F が 1つの累積分布関数で, クラスはこのプロセスをモデル化しています. 数学的な表現を使えば パラメトライズされた関数族 を作った訳です.

ちなみに, 途中でパラメータを変更できるという機能を犠牲にすれば, 関数定義だけで同じことができます.

def ecdf(observations):
    def _ecdf(x):
        count = sum(1.0 for obs in observations if obs <= x)
        return count / len(observations)
    return _ecdf

使用例は次のような感じになります.

>>> samples = [uniform(0, 1) for _ in range(10)]
>>> f = ecdf(samples)
>>> f(0.5)
0.8
>>> g = ecdf([uniform(0, 1) for _ in range(1000)])
>>> g(0.5)
0.497

Comments