テスト

ローカルでの開発環境のセットアップ

インストール手順に従って、ソースからstatsmodelsをビルドするための適切な環境をセットアップしてください。gitリポジトリのルートディレクトリから以下を実行して、venvにstatsmodelsを開発インストールすることを推奨します。

python -m venv .venv
python -m pip install -e ".[develop]"

フラグ -e は編集可能にするためのものです。

このコマンドはCコードをコンパイルし、Python環境のライブラリからstatsmodelsソースコードへのリンクを作成することで、アクティブなPython環境にstatsmodelsを追加します。そのため、純粋なPythonコードへの変更は、再インストールすることなくすぐに利用可能になります。CコードまたはCythonコードへの変更は、これらの変更が利用可能になる前に、python -m pip install -e ".[develop]" を再実行する必要があります。

テスト駆動開発

テスト駆動開発(TDD)パターンに従うよう努めています。メインコードベースに追加されるすべてのモデルまたは統計関数は、可能な限り既存の統計パッケージと比較してテストする必要があります。

pytest入門

多くのパッケージと同様に、statsmodelsはpytestテストシステムnumpy.testingの便利な拡張機能を使用しています。Pytestは、test または Test(クラスのみ)で始まるファイル、ディレクトリ、関数、またはクラス名を検出します。テスト関数は test で始まり、テストクラスは Test で始まる必要があります。これらの関数とクラスは、tests というディレクトリにある、test で始まる名前のファイルに配置する必要があります。

テストの実行

pytest を呼び出すことで、コマンドラインからテストを実行できます。pytestを使用して直接テストを実行するには、上記のように python -m pip install -e ".[develop]" を使用してstatsmodelsをインストールする必要があります。

テストは、さまざまな粒度レベルで実行できます。

  • プロジェクトレベル:すべてのテストを実行します。テストスイート全体を実行するのは時間がかかり、通常はstatsmodelsに大幅な変更を加える場合にのみ必要になります。

pytest statsmodels
  • フォルダレベル:フォルダ以下のすべてのテストを実行します。

pytest statsmodels/regression/tests
  • ファイルレベル:ファイル内のすべてのテストを実行します。

pytest statsmodels/regression/tests/test_regression.py
  • クラスレベル:クラス内のすべてのテストを実行します。

pytest statsmodels/regression/tests/test_regression.py::TestOLS
  • テストレベル:単一のテストを実行します。最初の例では、クラス内のテストを実行します。2番目の例では、スタンドアロンテストを実行します。

pytest statsmodels/regression/tests/test_regression.py::TestOLS::test_missing
pytest statsmodels/regression/tests/test_regression.py::test_ridge

テストの書き方

NumPyは、pytestとNumPy拡張機能を使用した単体テストの良い入門書をこちらで提供しています。詳細については、一読する価値があります。ここでは、私たちが従っているいくつかの規約について説明します。たとえば、1つの関数だけでなく、モデル全体を一度にテストしたいことがよくあります。以下は、test_discrete.pyの簡略版です。この場合、異なるオプションを持ついくつかの異なるモデルをテストする必要があります。テストは次のようになります。

from numpy.testing import assert_almost_equal
import statsmodels.api as sm
from results.results_discrete import Spector

class CheckDiscreteResults(object):
    """
    res2 are the results. res1 are the values from statsmodels
    """

    def test_params(self):
        assert_almost_equal(self.res1.params, self.res2.params, 4)

    decimal_tvalues = 4
    def test_tvalues(self):
        assert_almost_equal(self.res1.params, self.res2.params, self.decimal_tvalues)

    # ... as many more tests as there are common results

class TestProbitNewton(CheckDiscreteResults):
    """
    Tests the Probit model using Newton's method for fitting.
    """

    @classmethod
    def setup_class(cls):
        # set up model
        data = sm.datasets.spector.load()
        data.exog = sm.add_constant(data.exog)
        cls.res1 = sm.Probit(data.endog, data.exog).fit(method='newton', disp=0)

        # set up results
        res2 = Spector.probit
        cls.res2 = res2

        # set up precision
        cls.decimal_tvalues = 3

    def test_model_specifc(self):
        assert_almost_equal(self.res1.foo, self.res2.foo, 4)

主力となるのは、CheckDiscreteResultsクラスです。サブクラスTestProbitNewtonでは、tvaluesの精度レベルをデフォルトとは異なるレベルに設定できることに注意してください。すべてのテストクラスには、@classmethod と呼ばれる setup_class があります。そうでない場合、pytestはすべてのテストメソッドの前にクラスを再インスタンス化します。モデルのフィッティングに時間がかかる場合、これは明らかに望ましくありません。最後に、Pythonファイルを実行している場合にテストを実行できるように、下部にスクリプトがあります。

テスト結果

テスト結果は、上記の例の最後の部分です。多くのテスト、特にモデルのテストでは、テストしたい結果が多数あります。そのため、テストを読みやすくするために、ハードコードされた結果を実際のテストから分離するのが理にかなっています。結果が少数の場合は、結果を分離する必要はありません。多くの場合、他の統計パッケージの結果を使用します。結果を取得した場所と、statsmodelsで得られる結果と異なる可能性のある理由を文書化することが重要です。各テストフォルダには、resultsサブディレクトリがあります。離散モデルのフォルダ構造を考えてみましょう。

tests/
    __init__.py
    test_discrete.py
    results/
        __init__.py
        results_discrete.py
        nbinom_resids.csv

結果をどのように構成するのが最適かは、あなた次第です。離散モデルの例では、特定のデータセットに基づく結果クラスがあり、そのデータセットの異なるモデル結果を読み込むためのメソッドがあることに気付くでしょう。クラス自体に配置するよりも簡単な場合は、結果クラスによってロードされる結果を保持するテキストファイルを含めることもできます。

フル実行の高速化

テストスイート全体を実行すると時間がかかります。幸いなことに、低レベルの変更(たとえば、statsmodels.baseへの変更)を行う場合にのみ、スイート全体を実行する必要があります。必要に応じて、テストスイート全体の実行を高速化するために、2つの方法を使用できます。

  • pytest-xdistパッケージを使用する

python -m pip install pytest-xdist
export MKL_NUM_THREADS=1
export OMP_NUM_THREADS=1
pytest -n auto statsmodels
  • --skip-slow を使用して低速テストをスキップする

pytest --skip-slow statsmodels

より高速な実行のために、これら2つのアプローチを組み合わせることができます。

export MKL_NUM_THREADS=1 && export OMP_NUM_THREADS=1
pytest -n auto --skip-slow statsmodels

test() メソッド

statsmodelsのルートとすべてのサブモジュールは、パッケージ(statsmodels.test())またはモジュール(statsmodels.regression.test())のすべてのテストを実行するために使用できる test() メソッドを公開しています。このメソッドを使用すると、上記のように*編集可能*フラグを使用してインストールされていない場合でも、statsmodelsのインストールコピーからテストを実行できます。このメソッドは、リリースビルドでホイールをテストするために必要であり、開発には**推奨されません**。

このメソッドを使用して、すべてのテストは次のように実行されます。

import statsmodels.api as sm
sm.test()

サブモジュールテストは次のように実行されます。

sm.discrete.test()

test([extra_args, exit])

テストスイートを実行する


最終更新日:2024年10月3日