From 1199af3e997b654aa0818990c539d47b86c7093f Mon Sep 17 00:00:00 2001 From: Aleksandr Michuda Date: Thu, 6 Jun 2024 19:27:55 -0400 Subject: [PATCH] first commit of score test --- poetry.lock | 570 +++++++++++++++++++- pyproject.toml | 4 +- tests/sim_sample.dta | Bin 0 -> 79435 bytes tests/test_mle.ipynb | 1178 ++++++++++++++++++++++++++++++++++++++++++ tests/test_score.do | 163 ++++++ 5 files changed, 1911 insertions(+), 4 deletions(-) create mode 100644 tests/sim_sample.dta create mode 100644 tests/test_mle.ipynb create mode 100644 tests/test_score.do diff --git a/poetry.lock b/poetry.lock index 37b1adf..f7dcf55 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,33 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand. + +[[package]] +name = "appnope" +version = "0.1.4" +description = "Disable App Nap on macOS >= 10.9" +optional = false +python-versions = ">=3.6" +files = [ + {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, + {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, +] + +[[package]] +name = "asttokens" +version = "2.4.1" +description = "Annotate AST trees with source code positions" +optional = false +python-versions = "*" +files = [ + {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, + {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, +] + +[package.dependencies] +six = ">=1.12.0" + +[package.extras] +astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] +test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] [[package]] name = "astunparse" @@ -15,6 +44,17 @@ files = [ six = ">=1.6.1,<2.0" wheel = ">=0.23.0,<1.0" +[[package]] +name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +optional = false +python-versions = "*" +files = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] + [[package]] name = "build" version = "1.0.3" @@ -274,6 +314,23 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "comm" +version = "0.2.2" +description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." +optional = false +python-versions = ">=3.8" +files = [ + {file = "comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"}, + {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"}, +] + +[package.dependencies] +traitlets = ">=4" + +[package.extras] +test = ["pytest"] + [[package]] name = "crashtest" version = "0.4.1" @@ -339,6 +396,48 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] +[[package]] +name = "debugpy" +version = "1.8.1" +description = "An implementation of the Debug Adapter Protocol for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "debugpy-1.8.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3bda0f1e943d386cc7a0e71bfa59f4137909e2ed947fb3946c506e113000f741"}, + {file = "debugpy-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda73bf69ea479c8577a0448f8c707691152e6c4de7f0c4dec5a4bc11dee516e"}, + {file = "debugpy-1.8.1-cp310-cp310-win32.whl", hash = "sha256:3a79c6f62adef994b2dbe9fc2cc9cc3864a23575b6e387339ab739873bea53d0"}, + {file = "debugpy-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:7eb7bd2b56ea3bedb009616d9e2f64aab8fc7000d481faec3cd26c98a964bcdd"}, + {file = "debugpy-1.8.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:016a9fcfc2c6b57f939673c874310d8581d51a0fe0858e7fac4e240c5eb743cb"}, + {file = "debugpy-1.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd97ed11a4c7f6d042d320ce03d83b20c3fb40da892f994bc041bbc415d7a099"}, + {file = "debugpy-1.8.1-cp311-cp311-win32.whl", hash = "sha256:0de56aba8249c28a300bdb0672a9b94785074eb82eb672db66c8144fff673146"}, + {file = "debugpy-1.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:1a9fe0829c2b854757b4fd0a338d93bc17249a3bf69ecf765c61d4c522bb92a8"}, + {file = "debugpy-1.8.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ebb70ba1a6524d19fa7bb122f44b74170c447d5746a503e36adc244a20ac539"}, + {file = "debugpy-1.8.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2e658a9630f27534e63922ebf655a6ab60c370f4d2fc5c02a5b19baf4410ace"}, + {file = "debugpy-1.8.1-cp312-cp312-win32.whl", hash = "sha256:caad2846e21188797a1f17fc09c31b84c7c3c23baf2516fed5b40b378515bbf0"}, + {file = "debugpy-1.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:edcc9f58ec0fd121a25bc950d4578df47428d72e1a0d66c07403b04eb93bcf98"}, + {file = "debugpy-1.8.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7a3afa222f6fd3d9dfecd52729bc2e12c93e22a7491405a0ecbf9e1d32d45b39"}, + {file = "debugpy-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d915a18f0597ef685e88bb35e5d7ab968964b7befefe1aaea1eb5b2640b586c7"}, + {file = "debugpy-1.8.1-cp38-cp38-win32.whl", hash = "sha256:92116039b5500633cc8d44ecc187abe2dfa9b90f7a82bbf81d079fcdd506bae9"}, + {file = "debugpy-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:e38beb7992b5afd9d5244e96ad5fa9135e94993b0c551ceebf3fe1a5d9beb234"}, + {file = "debugpy-1.8.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:bfb20cb57486c8e4793d41996652e5a6a885b4d9175dd369045dad59eaacea42"}, + {file = "debugpy-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd3fdd3f67a7e576dd869c184c5dd71d9aaa36ded271939da352880c012e703"}, + {file = "debugpy-1.8.1-cp39-cp39-win32.whl", hash = "sha256:58911e8521ca0c785ac7a0539f1e77e0ce2df753f786188f382229278b4cdf23"}, + {file = "debugpy-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:6df9aa9599eb05ca179fb0b810282255202a66835c6efb1d112d21ecb830ddd3"}, + {file = "debugpy-1.8.1-py2.py3-none-any.whl", hash = "sha256:28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242"}, + {file = "debugpy-1.8.1.zip", hash = "sha256:f696d6be15be87aef621917585f9bb94b1dc9e8aced570db1b8a6fc14e8f9b42"}, +] + +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +optional = false +python-versions = ">=3.5" +files = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] + [[package]] name = "distlib" version = "0.3.7" @@ -452,6 +551,20 @@ files = [ [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "executing" +version = "2.0.1" +description = "Get the currently executing AST node of a frame, and other information" +optional = false +python-versions = ">=3.5" +files = [ + {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, + {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, +] + +[package.extras] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] + [[package]] name = "fastjsonschema" version = "2.18.1" @@ -583,6 +696,78 @@ files = [ {file = "installer-0.7.0.tar.gz", hash = "sha256:a26d3e3116289bb08216e0d0f7d925fcef0b0194eedfa0c944bcaaa106c4b631"}, ] +[[package]] +name = "ipykernel" +version = "6.29.4" +description = "IPython Kernel for Jupyter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ipykernel-6.29.4-py3-none-any.whl", hash = "sha256:1181e653d95c6808039c509ef8e67c4126b3b3af7781496c7cbfb5ed938a27da"}, + {file = "ipykernel-6.29.4.tar.gz", hash = "sha256:3d44070060f9475ac2092b760123fadf105d2e2493c24848b6691a7c4f42af5c"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "platform_system == \"Darwin\""} +comm = ">=0.1.1" +debugpy = ">=1.6.5" +ipython = ">=7.23.1" +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +matplotlib-inline = ">=0.1" +nest-asyncio = "*" +packaging = "*" +psutil = "*" +pyzmq = ">=24" +tornado = ">=6.1" +traitlets = ">=5.4.0" + +[package.extras] +cov = ["coverage[toml]", "curio", "matplotlib", "pytest-cov", "trio"] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"] +pyqt5 = ["pyqt5"] +pyside6 = ["pyside6"] +test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.23.5)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "ipython" +version = "8.12.3" +description = "IPython: Productive Interactive Computing" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ipython-8.12.3-py3-none-any.whl", hash = "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c"}, + {file = "ipython-8.12.3.tar.gz", hash = "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "sys_platform == \"darwin\""} +backcall = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +pickleshare = "*" +prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0" +pygments = ">=2.4.0" +stack-data = "*" +traitlets = ">=5" +typing-extensions = {version = "*", markers = "python_version < \"3.10\""} + +[package.extras] +all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.21)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +black = ["black"] +doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] +test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] + [[package]] name = "jaraco-classes" version = "3.3.0" @@ -601,6 +786,25 @@ more-itertools = "*" docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +[[package]] +name = "jedi" +version = "0.19.1" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=3.6" +files = [ + {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, + {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, +] + +[package.dependencies] +parso = ">=0.8.3,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] + [[package]] name = "jeepney" version = "0.8.0" @@ -633,6 +837,60 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "joblib" +version = "1.4.2" +description = "Lightweight pipelining with Python functions" +optional = false +python-versions = ">=3.8" +files = [ + {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"}, + {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"}, +] + +[[package]] +name = "jupyter-client" +version = "8.6.2" +description = "Jupyter protocol implementation and client libraries" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_client-8.6.2-py3-none-any.whl", hash = "sha256:50cbc5c66fd1b8f65ecb66bc490ab73217993632809b6e505687de18e9dea39f"}, + {file = "jupyter_client-8.6.2.tar.gz", hash = "sha256:2bda14d55ee5ba58552a8c53ae43d215ad9868853489213f37da060ced54d8df"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +python-dateutil = ">=2.8.2" +pyzmq = ">=23.0" +tornado = ">=6.2" +traitlets = ">=5.3" + +[package.extras] +docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] + +[[package]] +name = "jupyter-core" +version = "5.7.2" +description = "Jupyter core package. A base package on which Jupyter projects rely." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, + {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, +] + +[package.dependencies] +platformdirs = ">=2.5" +pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} +traitlets = ">=5.3" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] +test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout"] + [[package]] name = "keyring" version = "24.2.0" @@ -777,6 +1035,20 @@ files = [ {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, ] +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +description = "Inline Matplotlib backend for Jupyter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, + {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, +] + +[package.dependencies] +traitlets = "*" + [[package]] name = "mergedeep" version = "1.3.4" @@ -996,6 +1268,17 @@ files = [ {file = "msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87"}, ] +[[package]] +name = "nest-asyncio" +version = "1.6.0" +description = "Patch asyncio to allow nested event loops" +optional = false +python-versions = ">=3.5" +files = [ + {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, + {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, +] + [[package]] name = "numba" version = "0.58.1" @@ -1146,6 +1429,21 @@ sql-other = ["SQLAlchemy (>=1.4.16)"] test = ["hypothesis (>=6.34.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"] xml = ["lxml (>=4.6.3)"] +[[package]] +name = "parso" +version = "0.8.4" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, + {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, +] + +[package.extras] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["docopt", "pytest"] + [[package]] name = "pathspec" version = "0.11.2" @@ -1189,6 +1487,17 @@ files = [ [package.dependencies] ptyprocess = ">=0.5" +[[package]] +name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +optional = false +python-versions = "*" +files = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] + [[package]] name = "pkginfo" version = "1.9.6" @@ -1315,6 +1624,48 @@ files = [ poetry = ">=1.6.0,<2.0.0" poetry-core = ">=1.7.0,<2.0.0" +[[package]] +name = "prompt-toolkit" +version = "3.0.43" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, + {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "psutil" +version = "5.9.8" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, + {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, + {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, + {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, + {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, + {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, + {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, + {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, + {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, + {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + [[package]] name = "ptyprocess" version = "0.7.0" @@ -1326,6 +1677,20 @@ files = [ {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] +[[package]] +name = "pure-eval" +version = "0.2.2" +description = "Safely evaluate AST nodes without side effects" +optional = false +python-versions = "*" +files = [ + {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, + {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, +] + +[package.extras] +tests = ["pytest"] + [[package]] name = "pycparser" version = "2.21" @@ -1447,6 +1812,29 @@ files = [ {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, ] +[[package]] +name = "pywin32" +version = "306" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, + {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, + {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, + {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, +] + [[package]] name = "pywin32-ctypes" version = "0.2.2" @@ -1532,6 +1920,106 @@ files = [ [package.dependencies] pyyaml = "*" +[[package]] +name = "pyzmq" +version = "26.0.3" +description = "Python bindings for 0MQ" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyzmq-26.0.3-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:44dd6fc3034f1eaa72ece33588867df9e006a7303725a12d64c3dff92330f625"}, + {file = "pyzmq-26.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:acb704195a71ac5ea5ecf2811c9ee19ecdc62b91878528302dd0be1b9451cc90"}, + {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dbb9c997932473a27afa93954bb77a9f9b786b4ccf718d903f35da3232317de"}, + {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bcb34f869d431799c3ee7d516554797f7760cb2198ecaa89c3f176f72d062be"}, + {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ece17ec5f20d7d9b442e5174ae9f020365d01ba7c112205a4d59cf19dc38ee"}, + {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ba6e5e6588e49139a0979d03a7deb9c734bde647b9a8808f26acf9c547cab1bf"}, + {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3bf8b000a4e2967e6dfdd8656cd0757d18c7e5ce3d16339e550bd462f4857e59"}, + {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2136f64fbb86451dbbf70223635a468272dd20075f988a102bf8a3f194a411dc"}, + {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e8918973fbd34e7814f59143c5f600ecd38b8038161239fd1a3d33d5817a38b8"}, + {file = "pyzmq-26.0.3-cp310-cp310-win32.whl", hash = "sha256:0aaf982e68a7ac284377d051c742610220fd06d330dcd4c4dbb4cdd77c22a537"}, + {file = "pyzmq-26.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:f1a9b7d00fdf60b4039f4455afd031fe85ee8305b019334b72dcf73c567edc47"}, + {file = "pyzmq-26.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:80b12f25d805a919d53efc0a5ad7c0c0326f13b4eae981a5d7b7cc343318ebb7"}, + {file = "pyzmq-26.0.3-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:a72a84570f84c374b4c287183debc776dc319d3e8ce6b6a0041ce2e400de3f32"}, + {file = "pyzmq-26.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ca684ee649b55fd8f378127ac8462fb6c85f251c2fb027eb3c887e8ee347bcd"}, + {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e222562dc0f38571c8b1ffdae9d7adb866363134299264a1958d077800b193b7"}, + {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f17cde1db0754c35a91ac00b22b25c11da6eec5746431d6e5092f0cd31a3fea9"}, + {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b7c0c0b3244bb2275abe255d4a30c050d541c6cb18b870975553f1fb6f37527"}, + {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ac97a21de3712afe6a6c071abfad40a6224fd14fa6ff0ff8d0c6e6cd4e2f807a"}, + {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:88b88282e55fa39dd556d7fc04160bcf39dea015f78e0cecec8ff4f06c1fc2b5"}, + {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:72b67f966b57dbd18dcc7efbc1c7fc9f5f983e572db1877081f075004614fcdd"}, + {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4b6cecbbf3b7380f3b61de3a7b93cb721125dc125c854c14ddc91225ba52f83"}, + {file = "pyzmq-26.0.3-cp311-cp311-win32.whl", hash = "sha256:eed56b6a39216d31ff8cd2f1d048b5bf1700e4b32a01b14379c3b6dde9ce3aa3"}, + {file = "pyzmq-26.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:3191d312c73e3cfd0f0afdf51df8405aafeb0bad71e7ed8f68b24b63c4f36500"}, + {file = "pyzmq-26.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:b6907da3017ef55139cf0e417c5123a84c7332520e73a6902ff1f79046cd3b94"}, + {file = "pyzmq-26.0.3-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:068ca17214038ae986d68f4a7021f97e187ed278ab6dccb79f837d765a54d753"}, + {file = "pyzmq-26.0.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7821d44fe07335bea256b9f1f41474a642ca55fa671dfd9f00af8d68a920c2d4"}, + {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eeb438a26d87c123bb318e5f2b3d86a36060b01f22fbdffd8cf247d52f7c9a2b"}, + {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69ea9d6d9baa25a4dc9cef5e2b77b8537827b122214f210dd925132e34ae9b12"}, + {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7daa3e1369355766dea11f1d8ef829905c3b9da886ea3152788dc25ee6079e02"}, + {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6ca7a9a06b52d0e38ccf6bca1aeff7be178917893f3883f37b75589d42c4ac20"}, + {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1b7d0e124948daa4d9686d421ef5087c0516bc6179fdcf8828b8444f8e461a77"}, + {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e746524418b70f38550f2190eeee834db8850088c834d4c8406fbb9bc1ae10b2"}, + {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6b3146f9ae6af82c47a5282ac8803523d381b3b21caeae0327ed2f7ecb718798"}, + {file = "pyzmq-26.0.3-cp312-cp312-win32.whl", hash = "sha256:2b291d1230845871c00c8462c50565a9cd6026fe1228e77ca934470bb7d70ea0"}, + {file = "pyzmq-26.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:926838a535c2c1ea21c903f909a9a54e675c2126728c21381a94ddf37c3cbddf"}, + {file = "pyzmq-26.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:5bf6c237f8c681dfb91b17f8435b2735951f0d1fad10cc5dfd96db110243370b"}, + {file = "pyzmq-26.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c0991f5a96a8e620f7691e61178cd8f457b49e17b7d9cfa2067e2a0a89fc1d5"}, + {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dbf012d8fcb9f2cf0643b65df3b355fdd74fc0035d70bb5c845e9e30a3a4654b"}, + {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:01fbfbeb8249a68d257f601deb50c70c929dc2dfe683b754659569e502fbd3aa"}, + {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c8eb19abe87029c18f226d42b8a2c9efdd139d08f8bf6e085dd9075446db450"}, + {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5344b896e79800af86ad643408ca9aa303a017f6ebff8cee5a3163c1e9aec987"}, + {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:204e0f176fd1d067671157d049466869b3ae1fc51e354708b0dc41cf94e23a3a"}, + {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a42db008d58530efa3b881eeee4991146de0b790e095f7ae43ba5cc612decbc5"}, + {file = "pyzmq-26.0.3-cp37-cp37m-win32.whl", hash = "sha256:8d7a498671ca87e32b54cb47c82a92b40130a26c5197d392720a1bce1b3c77cf"}, + {file = "pyzmq-26.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:3b4032a96410bdc760061b14ed6a33613ffb7f702181ba999df5d16fb96ba16a"}, + {file = "pyzmq-26.0.3-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2cc4e280098c1b192c42a849de8de2c8e0f3a84086a76ec5b07bfee29bda7d18"}, + {file = "pyzmq-26.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5bde86a2ed3ce587fa2b207424ce15b9a83a9fa14422dcc1c5356a13aed3df9d"}, + {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:34106f68e20e6ff253c9f596ea50397dbd8699828d55e8fa18bd4323d8d966e6"}, + {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ebbbd0e728af5db9b04e56389e2299a57ea8b9dd15c9759153ee2455b32be6ad"}, + {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6b1d1c631e5940cac5a0b22c5379c86e8df6a4ec277c7a856b714021ab6cfad"}, + {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e891ce81edd463b3b4c3b885c5603c00141151dd9c6936d98a680c8c72fe5c67"}, + {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9b273ecfbc590a1b98f014ae41e5cf723932f3b53ba9367cfb676f838038b32c"}, + {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b32bff85fb02a75ea0b68f21e2412255b5731f3f389ed9aecc13a6752f58ac97"}, + {file = "pyzmq-26.0.3-cp38-cp38-win32.whl", hash = "sha256:f6c21c00478a7bea93caaaef9e7629145d4153b15a8653e8bb4609d4bc70dbfc"}, + {file = "pyzmq-26.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:3401613148d93ef0fd9aabdbddb212de3db7a4475367f49f590c837355343972"}, + {file = "pyzmq-26.0.3-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:2ed8357f4c6e0daa4f3baf31832df8a33334e0fe5b020a61bc8b345a3db7a606"}, + {file = "pyzmq-26.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c1c8f2a2ca45292084c75bb6d3a25545cff0ed931ed228d3a1810ae3758f975f"}, + {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b63731993cdddcc8e087c64e9cf003f909262b359110070183d7f3025d1c56b5"}, + {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b3cd31f859b662ac5d7f4226ec7d8bd60384fa037fc02aee6ff0b53ba29a3ba8"}, + {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:115f8359402fa527cf47708d6f8a0f8234f0e9ca0cab7c18c9c189c194dbf620"}, + {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:715bdf952b9533ba13dfcf1f431a8f49e63cecc31d91d007bc1deb914f47d0e4"}, + {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e1258c639e00bf5e8a522fec6c3eaa3e30cf1c23a2f21a586be7e04d50c9acab"}, + {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:15c59e780be8f30a60816a9adab900c12a58d79c1ac742b4a8df044ab2a6d920"}, + {file = "pyzmq-26.0.3-cp39-cp39-win32.whl", hash = "sha256:d0cdde3c78d8ab5b46595054e5def32a755fc028685add5ddc7403e9f6de9879"}, + {file = "pyzmq-26.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:ce828058d482ef860746bf532822842e0ff484e27f540ef5c813d516dd8896d2"}, + {file = "pyzmq-26.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:788f15721c64109cf720791714dc14afd0f449d63f3a5487724f024345067381"}, + {file = "pyzmq-26.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c18645ef6294d99b256806e34653e86236eb266278c8ec8112622b61db255de"}, + {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e6bc96ebe49604df3ec2c6389cc3876cabe475e6bfc84ced1bf4e630662cb35"}, + {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:971e8990c5cc4ddcff26e149398fc7b0f6a042306e82500f5e8db3b10ce69f84"}, + {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8416c23161abd94cc7da80c734ad7c9f5dbebdadfdaa77dad78244457448223"}, + {file = "pyzmq-26.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:082a2988364b60bb5de809373098361cf1dbb239623e39e46cb18bc035ed9c0c"}, + {file = "pyzmq-26.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d57dfbf9737763b3a60d26e6800e02e04284926329aee8fb01049635e957fe81"}, + {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:77a85dca4c2430ac04dc2a2185c2deb3858a34fe7f403d0a946fa56970cf60a1"}, + {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4c82a6d952a1d555bf4be42b6532927d2a5686dd3c3e280e5f63225ab47ac1f5"}, + {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4496b1282c70c442809fc1b151977c3d967bfb33e4e17cedbf226d97de18f709"}, + {file = "pyzmq-26.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:e4946d6bdb7ba972dfda282f9127e5756d4f299028b1566d1245fa0d438847e6"}, + {file = "pyzmq-26.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:03c0ae165e700364b266876d712acb1ac02693acd920afa67da2ebb91a0b3c09"}, + {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3e3070e680f79887d60feeda051a58d0ac36622e1759f305a41059eff62c6da7"}, + {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6ca08b840fe95d1c2bd9ab92dac5685f949fc6f9ae820ec16193e5ddf603c3b2"}, + {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e76654e9dbfb835b3518f9938e565c7806976c07b37c33526b574cc1a1050480"}, + {file = "pyzmq-26.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:871587bdadd1075b112e697173e946a07d722459d20716ceb3d1bd6c64bd08ce"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d0a2d1bd63a4ad79483049b26514e70fa618ce6115220da9efdff63688808b17"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0270b49b6847f0d106d64b5086e9ad5dc8a902413b5dbbb15d12b60f9c1747a4"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:703c60b9910488d3d0954ca585c34f541e506a091a41930e663a098d3b794c67"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74423631b6be371edfbf7eabb02ab995c2563fee60a80a30829176842e71722a"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4adfbb5451196842a88fda3612e2c0414134874bffb1c2ce83ab4242ec9e027d"}, + {file = "pyzmq-26.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3516119f4f9b8671083a70b6afaa0a070f5683e431ab3dc26e9215620d7ca1ad"}, + {file = "pyzmq-26.0.3.tar.gz", hash = "sha256:dba7d9f2e047dfa2bca3b01f4f84aa5246725203d6284e3790f2ca15fba6b40a"}, +] + +[package.dependencies] +cffi = {version = "*", markers = "implementation_name == \"pypy\""} + [[package]] name = "rapidfuzz" version = "3.5.2" @@ -1744,6 +2232,25 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +[[package]] +name = "stack-data" +version = "0.6.3" +description = "Extract data from python stack frames and tracebacks for informative displays" +optional = false +python-versions = "*" +files = [ + {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, + {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, +] + +[package.dependencies] +asttokens = ">=2.1.0" +executing = ">=1.2.0" +pure-eval = "*" + +[package.extras] +tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] + [[package]] name = "statsmodels" version = "0.14.0" @@ -1833,6 +2340,41 @@ files = [ {file = "tomlkit-0.12.2.tar.gz", hash = "sha256:df32fab589a81f0d7dc525a4267b6d7a64ee99619cbd1eeb0fae32c1dd426977"}, ] +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "traitlets" +version = "5.14.3" +description = "Traitlets Python configuration system" +optional = false +python-versions = ">=3.8" +files = [ + {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, + {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] + [[package]] name = "trove-classifiers" version = "2023.10.18" @@ -1844,6 +2386,17 @@ files = [ {file = "trove_classifiers-2023.10.18-py3-none-any.whl", hash = "sha256:20a3da8e3cb65587cc9f5d5b837bf74edeb480bba9bd8cd4f03ab056d6b06c4c"}, ] +[[package]] +name = "typing-extensions" +version = "4.11.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, +] + [[package]] name = "tzdata" version = "2023.3" @@ -1931,6 +2484,17 @@ files = [ [package.extras] watchmedo = ["PyYAML (>=3.10)"] +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + [[package]] name = "wheel" version = "0.41.3" @@ -2046,5 +2610,5 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" -python-versions = ">=3.8,<4.0" -content-hash = "c1bdd7ba1dbf4d249e6aa0d336ebb5a70c8e35ff81235a94cc57055cb45bf884" +python-versions = ">=3.8,<3.12" +content-hash = "15bc27046ea2dae655b38615735a15b44dd8703685e9dd8974130b149a0e18eb" diff --git a/pyproject.toml b/pyproject.toml index dfec8b6..05ad9d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ repository="https://github.com/s3alfisc/wildboottest" [tool.poetry.dependencies] -python=">=3.8,<4.0" +python=">=3.8,<3.12" numba=">=0.57" numpy=">=1.18" pandas=">=1.4" @@ -30,6 +30,7 @@ pytest="^7.2.0" statsmodels=">=0.13" tabulate = "^0.9.0" poetry = "^1.4.2" +joblib = "^1.4.2" [tool.poetry.dev-dependencies] pytest = "^7.2.0" @@ -48,6 +49,7 @@ pymdown-extensions = ">=10.0" mkdocstrings-python-legacy = "^0.2.3" mkdocstrings = {version = "^0.19.0", extras = ["python"], optional = true } pymdown-extensions = ">=10.0" +ipykernel = "^6.29.4" [build-system] diff --git a/tests/sim_sample.dta b/tests/sim_sample.dta new file mode 100644 index 0000000000000000000000000000000000000000..7cf9a8e7a05e801bf3a8d955233859406ff0fca1 GIT binary patch literal 79435 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Μ?UG1?I)61>uuC6j*!20wV@p2jqI%-) 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 real scalar N\n", + "> real matrix Scent, Scentsum, OPGcent\n", + "> \n", + "> N=rows(S)\n", + "> Scent=S - J(N,1,1)*mean(S)\n", + "> Scentsum=L'*Scent\n", + "> OPGcent=Scentsum'*Scentsum\n", + "> return(OPGcent)\n", + "> }\n", + "\n", + ": end\n", + "-------------------------------------------------------------------------------\n", + "\n", + ". \n", + ". \n", + ". \n", + ". /* cap prog drop sim_probit\n", + "> prog def sim_probit, rclass\n", + "> syntax [, c(integer 5) m(integer 50) f(integer 0) r(integer 199)] */\n", + ". *Notes on syntax: \n", + ". *c - # of clusters\n", + ". *m - # of obs per cluster\n", + ". *f - controls whether to use mixture regressor\n", + ". *r - # of bootstrap reps per simulation\n", + ". \n", + ". local c 10\n", + "\n", + ". local m 100\n", + "\n", + ". local f 0\n", + "\n", + ". local r 1000\n", + "\n", + ". \n", + ". **Generate data**\n", + ". drop _all\n", + "\n", + ". set obs `c' //generate c clusters\n", + "Number of observations (_N) was 0, now 10.\n", + "\n", + ". \n", + ". gen x=invnorm(uniform()) //covariate\n", + "\n", + ". gen mix=uniform()>.1 //contamination probability\n", + "\n", + ". \n", + ". if `f'==1{\n", + ". gen D=(mix*invnorm(uniform()) + (1-mix)*(2+3*invnorm(uniform())))/4 /\n", + "> /regressor of interest\n", + ". }\n", + "\n", + ". else{\n", + ". gen D=(invnorm(uniform()))/4 //regressor of interest\n", + ". }\n", + "\n", + ". \n", + ". gen v=invnorm(uniform())\n", + "\n", + ". \n", + ". \n", + ". gen c=_n //generate cluster id\n", + "\n", + ". \n", + ". expand `m' //make m obs per cluster\n", + "(990 observations created)\n", + "\n", + ". \n", + ". replace x=(x+invnorm(uniform()))/4 //add some within cluster variation in x\n", + "(1,000 real changes made)\n", + "\n", + ". \n", + ". gen y=x+D+v/sqrt(2)+invnorm(uniform())/sqrt(2) //form outcome\n", + "\n", + ". replace y=y>0\n", + "(1,000 real changes made)\n", + "\n", + ". sort c\n", + "\n", + ". \n", + ". **Restricted**\n", + ". constraint 1 D=1\n", + "\n", + ". glm y x D, constraint(1) fam(bin) link(probit)\n", + "\n", + "Iteration 0: Log likelihood = -685.70942 \n", + "Iteration 1: Log likelihood = -685.44941 \n", + "Iteration 2: Log likelihood = -685.4494 \n", + "\n", + "Generalized linear models Number of obs = 1,000\n", + "Optimization : ML Residual df = 998\n", + " Scale parameter = 1\n", + "Deviance = 1370.898802 (1/df) Deviance = 1.373646\n", + "Pearson = 1097.020519 (1/df) Pearson = 1.099219\n", + "\n", + "Variance function: V(u) = u*(1-u) [Bernoulli]\n", + "Link function : g(u) = invnorm(u) [Probit]\n", + "\n", + " AIC = 1.374899\n", + "Log likelihood = -685.4494008 BIC = -5523.041\n", + "\n", + " ( 1) [y]D = 1\n", + "------------------------------------------------------------------------------\n", + " | OIM\n", + " y | Coefficient std. err. z P>|z| [95% conf. interval]\n", + "-------------+----------------------------------------------------------------\n", + " x | .9846371 .1474927 6.68 0.000 .6955568 1.273717\n", + " D | 1 (constrained)\n", + " _cons | .1066109 .040653 2.62 0.009 .0269325 .1862893\n", + "------------------------------------------------------------------------------\n", + "\n", + ". predict pr\n", + "(option mu assumed; predicted mean y)\n", + "\n", + ". predict xbr, xb\n", + "\n", + ". gen phir=normalden(xbr)\n", + "\n", + ". gen er=(y-pr)*phir/(pr*(1-pr)) //quasi-residual\n", + "\n", + ". gen wr=phir^2/(pr*(1-pr)) //weights for hessian\n", + "\n", + ". gen one=1\n", + "\n", + ". \n", + ". mat accum Hr=one x D [iw=wr], nocons //form hessian */\n", + "(obs=598.5860322)\n", + "\n", + ". \n", + ". \n", + ". **Unrestricted**\n", + ". probit y x D, cluster(c)\n", + "\n", + "Iteration 0: Log pseudolikelihood = -693.12918 \n", + "Iteration 1: Log pseudolikelihood = -665.3805 \n", + "Iteration 2: Log pseudolikelihood = -665.33497 \n", + "Iteration 3: Log pseudolikelihood = -665.33497 \n", + "\n", + "Probit regression Number of obs = 1,000\n", + " Wald chi2(2) = 7.41\n", + " Prob > chi2 = 0.0247\n", + "Log pseudolikelihood = -665.33497 Pseudo R2 = 0.0401\n", + "\n", + " (Std. err. adjusted for 10 clusters in c)\n", + "------------------------------------------------------------------------------\n", + " | Robust\n", + " y | Coefficient std. err. z P>|z| [95% conf. interval]\n", + "-------------+----------------------------------------------------------------\n", + " x | 1.057877 .3958503 2.67 0.008 .2820252 1.83373\n", + " D | .0942995 1.041973 0.09 0.928 -1.947931 2.13653\n", + " _cons | .0357662 .3079585 0.12 0.908 -.5678212 .6393537\n", + "------------------------------------------------------------------------------\n", + "\n", + ". global b=_b[D]\n", + "\n", + ". local stdof=sqrt((`m'*`c'-3)/(`m'*`c'-1)) //inverse stata DOF correction\n", + "\n", + ". global W=((_b[D]-1)/(_se[D]*`stdof'))^2 //save analytical Wald\n", + "\n", + ". \n", + ". predict pu\n", + "(option pr assumed; Pr(y))\n", + "\n", + ". predict xbu, xb\n", + "\n", + ". gen phiu=normalden(xbu)\n", + "\n", + ". gen eu=(y-pu)*phiu/(pu*(1-pu)) //quasi-residual\n", + "\n", + ". gen wu=phiu^2/(pu*(1-pu)) //weights for hessian\n", + "\n", + ". \n", + ". local dfk=(`c'/(`c'-1))\n", + "\n", + ". mata: L=I(`c')#J(`m',1,1) //matrix for summing across clusters\n", + "\n", + ". mata: X=st_data(.,(\"one\", \"x\", \"D\"))\n", + "\n", + ". mata: C=(0\\0\\1) //constraint matrix -- third coefficient is restricte\n", + "> d\n", + "\n", + ". \n", + ". mata: Hr=st_matrix(\"Hr\")\n", + "\n", + ". mata: Hrinv=invsym(Hr)\n", + "\n", + ". mata: Ar=C'*Hrinv //form A_n\n", + "\n", + ". \n", + ". \n", + ". **Prepare influence function wald\n", + ". mat accum Hu=one x D [iw=wu], nocons //form hessian\n", + "(obs=615.9930729)\n", + "\n", + ". mata: Hu=st_matrix(\"Hu\")\n", + "\n", + ". mata: Huinv=invsym(Hu)\n", + "\n", + ". mata: Au=C'*Huinv\n", + "\n", + ". mata: eu=st_data(.,\"eu\")\n", + "\n", + ". mata: OPGucent=clustOPG(X:*eu,L)\n", + "\n", + ". mata: Vinvu=invsym(Au*OPGucent*`dfk'*Au') //compute inverse of variance of in\n", + "> fluence function\n", + "\n", + ". \n", + ". \n", + ". save \"sim_sample.dta\", replace\n", + "file sim_sample.dta saved\n", + "\n", + ". \n", + ". mata: S = X:*eu\n", + "\n", + ". \n", + ". **Bootstrap**\n", + ". gen u=.\n", + "(1,000 missing values generated)\n", + "\n", + ". gen ystar=.\n", + "(1,000 missing values generated)\n", + "\n", + ". gen ernew=.\n", + "(1,000 missing values generated)\n", + "\n", + ". gen eunew=.\n", + "(1,000 missing values generated)\n", + "\n", + ". gen pos=.\n", + "(1,000 missing values generated)\n", + "\n", + ". \n", + ". \n", + ". mat Ts=J(1,6,.) //store bootstrap statistics\n", + "\n", + ". \n", + ". by c: replace u=uniform()\n", + "(1,000 real changes made)\n", + "\n", + ". by c: replace pos=u[1]<.5 //cluster level rademacher indicator\n", + "(1000 real changes made)\n", + "\n", + ". gen radem = 2*pos - 1\n", + "\n", + ". \n", + ". qui replace ernew=(2*pos-1)*er //weighted residual\n", + "\n", + ". qui replace eunew=(2*pos-1)*eu //weighted residual\n", + "\n", + ". \n", + ". *Score bootstrap LM -- Rademacher\n", + ". mata: er=st_data(.,\"ernew\")\n", + "\n", + ". mata: Sr=colsum(X:*er)\n", + "\n", + ". mata: OPGrcent=clustOPG(X:*er,L)\n", + "\n", + ". mata: lm=Sr*Ar'*invsym(Ar*(OPGrcent)*`dfk'*Ar')*Ar*Sr'\n", + "\n", + ". mata: st_numscalar(\"lm\",lm)\n", + "\n", + ". mat Ts[1,1]=lm //save bootstrap LM \n", + "\n", + ". \n", + ". \n", + ". *Score Wald -- Rademacher\n", + ". mata: eu=st_data(.,\"eunew\")\n", + "\n", + ". mata: Su=colsum(X:*eu)\n", + "\n", + ". mata: OPGucent=clustOPG(X:*eu,L)\n", + "\n", + ". mata: wald=Su*Au'*invsym(Au*OPGucent*`dfk'*Au')*Au*Su'\n", + "\n", + ". mata: st_numscalar(\"wald\",wald)\n", + "\n", + ". mat Ts[1,2]=wald //save bootstrap Wald\n", + "\n", + ". \n", + ". *Score bootstrap Wald combining restricted and unrestricted scores -- Rademac\n", + "> her\n", + ". mata: wald_r=Sr*Ar'*invsym(Au*OPGucent*`dfk'*Au')*Ar*Sr'\n", + "\n", + ". mata: st_numscalar(\"wald_r\",wald)\n", + "\n", + ". mat Ts[1,3]=wald //save bootstrap Wald\n", + "\n", + ". \n", + ". \n", + ". \n" + ] + } + ], + "source": [ + "%%stata\n", + "\n", + "****************************************************\n", + "* Code for Table 2 of Kline and Santos (2011) *\n", + "* This file computes Analytical Wald and LM tests *\n", + "* along with Score bootstrapped tests. \t *\n", + "* Some other results not reported in the paper *\n", + "* are also included.\t\t\t\t *\t\n", + "****************************************************\n", + "\n", + "version 11.1\n", + "set seed 12345\n", + "\n", + "**mata subroutine to compute recentered clustered outer products**\n", + "**inputs a matrix S of scores and a cluster summing matrix L**\n", + "**outputs clustered outer product matrix**\n", + "**note this program assumes a balanced design**\n", + "**and proper sort order**\n", + "cap mata: mata drop clustOPG()\n", + "mata: \n", + "real matrix clustOPG(real matrix S,real matrix L){\n", + "real scalar N\n", + "real matrix Scent, Scentsum, OPGcent\n", + "\n", + "N=rows(S)\n", + "Scent=S - J(N,1,1)*mean(S)\n", + "Scentsum=L'*Scent\n", + "OPGcent=Scentsum'*Scentsum\n", + "return(OPGcent)\n", + "}\n", + "end\n", + "\n", + "\n", + "\n", + "/* cap prog drop sim_probit\n", + "prog def sim_probit, rclass\n", + "syntax [, c(integer 5) m(integer 50) f(integer 0) r(integer 199)] */\n", + "*Notes on syntax: \n", + "*c - # of clusters\n", + "*m - # of obs per cluster\n", + "*f - controls whether to use mixture regressor\n", + "*r - # of bootstrap reps per simulation\n", + "\n", + "local c 10\n", + "local m 100\n", + "local f 0\n", + "local r 1000\n", + "\n", + "**Generate data**\n", + "drop _all\n", + "set obs `c' //generate c clusters\n", + "\n", + "gen x=invnorm(uniform()) //covariate\n", + "gen mix=uniform()>.1 //contamination probability\n", + "\n", + "if `f'==1{\n", + "\tgen D=(mix*invnorm(uniform()) + (1-mix)*(2+3*invnorm(uniform())))/4 //regressor of interest\n", + "}\n", + "else{\n", + "\tgen D=(invnorm(uniform()))/4 //regressor of interest\n", + "}\n", + "\n", + "gen v=invnorm(uniform())\n", + "\n", + "\n", + "gen c=_n\t//generate cluster id\n", + "\n", + "expand `m'\t//make m obs per cluster\n", + "\n", + "replace x=(x+invnorm(uniform()))/4 //add some within cluster variation in x\n", + "\n", + "gen y=x+D+v/sqrt(2)+invnorm(uniform())/sqrt(2) //form outcome\n", + "replace y=y>0\n", + "sort c\n", + "\n", + "**Restricted**\n", + "constraint 1 D=1\n", + "glm y x D, constraint(1) fam(bin) link(probit)\n", + "predict pr\n", + "predict xbr, xb\n", + "gen phir=normalden(xbr)\n", + "gen er=(y-pr)*phir/(pr*(1-pr)) //quasi-residual\n", + "gen wr=phir^2/(pr*(1-pr)) //weights for hessian\n", + "gen one=1\n", + "\n", + "mat accum Hr=one x D [iw=wr], nocons //form hessian */\n", + "\n", + "\n", + "**Unrestricted**\n", + "probit y x D, cluster(c)\n", + "global b=_b[D]\n", + "local stdof=sqrt((`m'*`c'-3)/(`m'*`c'-1)) //inverse stata DOF correction\n", + "global W=((_b[D]-1)/(_se[D]*`stdof'))^2 //save analytical Wald\n", + "\n", + "predict pu\n", + "predict xbu, xb\n", + "gen phiu=normalden(xbu)\n", + "gen eu=(y-pu)*phiu/(pu*(1-pu)) //quasi-residual\n", + "gen wu=phiu^2/(pu*(1-pu)) //weights for hessian\n", + "\n", + "local dfk=(`c'/(`c'-1))\n", + "mata: L=I(`c')#J(`m',1,1) //matrix for summing across clusters\n", + "mata: X=st_data(.,(\"one\", \"x\", \"D\"))\n", + "mata: C=(0\\0\\1)\t\t//constraint matrix -- third coefficient is restricted\n", + "\n", + "mata: Hr=st_matrix(\"Hr\")\n", + "mata: Hrinv=invsym(Hr)\n", + "mata: Ar=C'*Hrinv\t\t//form A_n\n", + "\n", + "\n", + "**Prepare influence function wald\n", + "mat accum Hu=one x D [iw=wu], nocons //form hessian\n", + "mata: Hu=st_matrix(\"Hu\")\n", + "mata: Huinv=invsym(Hu)\n", + "mata: Au=C'*Huinv\n", + "mata: eu=st_data(.,\"eu\")\n", + "mata: OPGucent=clustOPG(X:*eu,L)\n", + "mata: Vinvu=invsym(Au*OPGucent*`dfk'*Au') //compute inverse of variance of influence function\n", + "\n", + "\n", + "save \"sim_sample.dta\", replace\n", + "\n", + "mata: S = X:*eu\n", + "\n", + "**Bootstrap**\n", + "gen u=.\n", + "gen ystar=.\n", + "gen ernew=.\n", + "gen eunew=.\n", + "gen pos=.\n", + "\n", + "\n", + "mat Ts=J(1,6,.) //store bootstrap statistics\n", + "\n", + "by c: replace u=uniform()\n", + "by c: replace pos=u[1]<.5 //cluster level rademacher indicator\n", + "gen radem = 2*pos - 1\n", + "\n", + "qui replace ernew=(2*pos-1)*er //weighted residual\n", + "qui replace eunew=(2*pos-1)*eu //weighted residual\n", + "\n", + "*Score bootstrap LM -- Rademacher\n", + "mata: er=st_data(.,\"ernew\")\n", + "mata: Sr=colsum(X:*er)\n", + "mata: OPGrcent=clustOPG(X:*er,L)\n", + "mata: lm=Sr*Ar'*invsym(Ar*(OPGrcent)*`dfk'*Ar')*Ar*Sr'\n", + "mata: st_numscalar(\"lm\",lm)\n", + "mat Ts[1,1]=lm\t //save bootstrap LM \n", + "\n", + "\n", + "*Score Wald -- Rademacher\n", + "mata: eu=st_data(.,\"eunew\")\n", + "mata: Su=colsum(X:*eu)\n", + "mata: OPGucent=clustOPG(X:*eu,L)\n", + "mata: wald=Su*Au'*invsym(Au*OPGucent*`dfk'*Au')*Au*Su'\n", + "mata: st_numscalar(\"wald\",wald)\n", + "mat Ts[1,2]=wald\t //save bootstrap Wald\n", + "\n", + "*Score bootstrap Wald combining restricted and unrestricted scores -- Rademacher\n", + "mata: wald_r=Sr*Ar'*invsym(Au*OPGucent*`dfk'*Au')*Ar*Sr'\n", + "mata: st_numscalar(\"wald_r\",wald)\n", + "mat Ts[1,3]=wald\t //save bootstrap Wald\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "N = 1000\n", + "rng = np.random.default_rng(seed=1)\n", + "num_X = 2\n", + "\n", + "\n", + "# generates fake probit data for testing (Not used in favor of data generated from paper)\n", + "def probit_data(N, num_X):\n", + " # generate X and error\n", + " epsilon = rng.normal(size=N)\n", + " X = sm.add_constant(rng.normal(size=(N, num_X)))\n", + " beta = np.arange(1,num_X+2)\n", + " \n", + " true_y = X @ beta + epsilon\n", + " \n", + " y_star = np.where(true_y >0, 1,0)\n", + " \n", + " return y_star, true_y, X\n", + "\n", + "y_star, true_y, X = probit_data(1000, 3)\n", + "\n", + "clusters = rng.choice(range(100), size=1000)\n", + "\n", + "df = stata.pdataframe_from_data()\n", + "\n", + "p = Probit.from_formula(\"y ~ x + D\", data=df)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Optimization terminated successfully.\n", + " Current function value: 0.665335\n", + " Iterations 4\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Probit Regression Results
Dep. Variable: y No. Observations: 1000
Model: Probit Df Residuals: 997
Method: MLE Df Model: 2
Date: Thu, 06 Jun 2024 Pseudo R-squ.: 0.04010
Time: 19:23:11 Log-Likelihood: -665.33
converged: True LL-Null: -693.13
Covariance Type: cluster LLR p-value: 8.494e-13
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err z P>|z| [0.025 0.975]
Intercept 0.0358 0.308 0.116 0.908 -0.568 0.640
x 1.0579 0.396 2.670 0.008 0.281 1.835
D 0.0943 1.043 0.090 0.928 -1.950 2.139
" + ], + "text/latex": [ + "\\begin{center}\n", + "\\begin{tabular}{lclc}\n", + "\\toprule\n", + "\\textbf{Dep. Variable:} & y & \\textbf{ No. Observations: } & 1000 \\\\\n", + "\\textbf{Model:} & Probit & \\textbf{ Df Residuals: } & 997 \\\\\n", + "\\textbf{Method:} & MLE & \\textbf{ Df Model: } & 2 \\\\\n", + "\\textbf{Date:} & Thu, 06 Jun 2024 & \\textbf{ Pseudo R-squ.: } & 0.04010 \\\\\n", + "\\textbf{Time:} & 19:23:11 & \\textbf{ Log-Likelihood: } & -665.33 \\\\\n", + "\\textbf{converged:} & True & \\textbf{ LL-Null: } & -693.13 \\\\\n", + "\\textbf{Covariance Type:} & cluster & \\textbf{ LLR p-value: } & 8.494e-13 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lcccccc}\n", + " & \\textbf{coef} & \\textbf{std err} & \\textbf{z} & \\textbf{P$> |$z$|$} & \\textbf{[0.025} & \\textbf{0.975]} \\\\\n", + "\\midrule\n", + "\\textbf{Intercept} & 0.0358 & 0.308 & 0.116 & 0.908 & -0.568 & 0.640 \\\\\n", + "\\textbf{x} & 1.0579 & 0.396 & 2.670 & 0.008 & 0.281 & 1.835 \\\\\n", + "\\textbf{D} & 0.0943 & 1.043 & 0.090 & 0.928 & -1.950 & 2.139 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "%\\caption{Probit Regression Results}\n", + "\\end{center}" + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " Probit Regression Results \n", + "==============================================================================\n", + "Dep. Variable: y No. Observations: 1000\n", + "Model: Probit Df Residuals: 997\n", + "Method: MLE Df Model: 2\n", + "Date: Thu, 06 Jun 2024 Pseudo R-squ.: 0.04010\n", + "Time: 19:23:11 Log-Likelihood: -665.33\n", + "converged: True LL-Null: -693.13\n", + "Covariance Type: cluster LLR p-value: 8.494e-13\n", + "==============================================================================\n", + " coef std err z P>|z| [0.025 0.975]\n", + "------------------------------------------------------------------------------\n", + "Intercept 0.0358 0.308 0.116 0.908 -0.568 0.640\n", + "x 1.0579 0.396 2.670 0.008 0.281 1.835\n", + "D 0.0943 1.043 0.090 0.928 -1.950 2.139\n", + "==============================================================================\n", + "\"\"\"" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "p.fit(cov_type='cluster', cov_kwds={'groups' : df['c']}).summary()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "L = np.array(sfi.Mata.get(\"L\"))\n", + "S = np.array(sfi.Mata.get(\"S\"))\n", + "OPGucent = np.array(sfi.Mata.get(\"OPGucent\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Optimization terminated successfully.\n", + " Current function value: 0.665335\n", + " Iterations 4\n" + ] + }, + { + "data": { + "text/plain": [ + "Intercept 0.856059\n", + "x 7.202267\n", + "D 0.662808\n", + "dtype: float64" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "p.fit().tvalues" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Union, TypeVar\n", + "import numpy.typing as npt\n", + "from statsmodels.base.model import Model\n", + "\n", + "ArrayLike = Union[npt.ArrayLike, pd.DataFrame, pd.Series]\n", + "\n", + "class IncorrectModelException(Exception):\n", + " pass\n", + "\n", + "class ScoreWildBootstrap:\n", + " \n", + " def __init__(self,\n", + " mod: Model, \n", + " clusters=Union[None, ArrayLike]) -> None:\n", + " \"\"\"Score wild bootstrap as in Kline and Santos (2012). Perturbs the score of using wild bootstrap weights and calculates wald statistics.\n", + " Optionally with restricted score or unrestricted scores.\n", + "\n", + " Args:\n", + " mod (Model): A statsmodels model that has a `score_obs` and `hessian` method.\n", + " clusters (ArrayLike|None, optional): A dataframe or array of shape Nx1 that gives the cluster membership of each of the N observations. Defaults to Union[None, ArrayLike].\n", + " \"\"\" \n", + " \n", + " if not hasattr(mod, \"score_obs\"):\n", + " raise IncorrectModelException(f\"{mod} does not implement `score_obs`.\")\n", + " if not hasattr(mod, \"hessian\"):\n", + " raise IncorrectModelException(\"{mod} does not implement `hessian`.\")\n", + " \n", + " self.mod = mod\n", + " \n", + " self.fitted_mod = self.mod.fit(cov_type='cluster', cov_kwds = {'groups' : clusters})\n", + " \n", + " self.beta_hat = self.fitted_mod.params\n", + " \n", + " if clusters is not None:\n", + " self.cluster_ids, self.cluster_obs = np.unique(clusters, return_counts=True)\n", + " self.clusters = clusters\n", + " self.num_clusters = len(self.cluster_ids)\n", + " else:\n", + " self.clusters = np.ones(self.mod.endog.shape[0])\n", + " self.cluster_ids = np.array([1])\n", + " self.num_clusters = 1\n", + " \n", + " def get_analytical_wald(self) -> pd.Series:\n", + " \"\"\"Calculates cluster-robust analytical wald test for each parameter.\n", + " \"\"\" \n", + " return self.fitted_mod.tvalues**2\n", + "\n", + " def wald_test(self, W: ArrayLike, restricted_residuals: bool =False, \n", + " R: ArrayLike =None, r: Union[None, int, float]=None) -> Union[ArrayLike, float]:\n", + " \"\"\"Calculates Wald statistic, given perturbation weights, scores and hessians from the model.\n", + "\n", + " Args:\n", + " W (ArrayLike): Weight vector used to perturb observation score\n", + " restricted_residuals (bool, optional): Whether to construct the test with the restricted score. Defaults to False.\n", + " \\begin{aligned}\n", + " & T_{n, 1}^{\\star s} \\equiv\\left(R H_n^{-1} S_n^{\\star}\\left(\\hat{\\beta}_u\\right)\\right)^{\\prime}\\left(R H_n^{-1} \\sum_n^{\\star s}\\left(\\hat{\\beta}_u\\right) H_n^{-1} R^{\\prime}\\right)^{-1}\\left(R H_n^{-1} S_n^{\\star}\\left(\\hat{\\beta}_u\\right)\\right) \\\\\n", + " & T_{n, 2}^{\\star s} \\equiv\\left(R H_n^{-1} S_n^{\\star}\\left(\\hat{\\beta}_r\\right)\\right)^{\\prime}\\left(R H_n^{-1} \\sum_n^{\\star s}\\left(\\hat{\\beta}_u\\right) H_n^{-1} R^{\\prime}\\right)^{-1}\\left(R H_n^{-1} S_n^{\\star}\\left(\\hat{\\beta}_r\\right)\\right)\n", + " \\end{aligned}\n", + " See Equations 50 and 51 from Kline and Santos (2012).\n", + " R (ArrayLike, optional): Restriction matrix for test. Defaults to None.\n", + " r (Union[None, int, float], optional): test restriction. Defaults to None.\n", + "\n", + " Returns:\n", + " Union[ArrayLike, float]: A Wald Statistic based on the perturbed weights.\n", + " \"\"\" \n", + " \n", + " \n", + " hessian_inv = -np.linalg.inv(self.mod.hessian(self.beta_hat))\n", + " \n", + " if restricted_residuals: # if we use the restricted score in the test\n", + " if r is None:\n", + " raise ValueError(\"Must specify coefficient constraint to restrict residuals\")\n", + " score_beta = self.beta_hat.copy()\n", + " score_beta[np.where(R==1)[0]] = 1 \n", + " else:\n", + " score_beta = self.beta_hat\n", + " \n", + " # make array of weights divided by cluster membership\n", + " W_Jn = np.select([self.clusters == c for c in self.cluster_ids], W.flatten())[:,np.newaxis]\n", + " # divide weights by observations per clusters \n", + " perturbed_score_obs = self.mod.score_obs(self.beta_hat) * W_Jn\n", + " \n", + " # multiply by cluster membership\n", + " L = pd.get_dummies(self.clusters).astype(int).values\n", + " \n", + " perturbed_score_obs_mean = (((L/(L.sum(axis=0)-1)).T @ perturbed_score_obs)).mean(axis=0)\n", + " \n", + " demeaned_perturbed_score_obs = perturbed_score_obs - perturbed_score_obs_mean \n", + " \n", + " demeaned_perturbed_score_obs_L = L.T @ demeaned_perturbed_score_obs\n", + " \n", + " perturbed_score = (self.mod.score_obs(score_beta) * W_Jn).sum(axis=0)\n", + " \n", + " # equation 50 from the paper\n", + " bread = R @ hessian_inv @ perturbed_score\n", + " \n", + " opg_centered = demeaned_perturbed_score_obs_L.T @ demeaned_perturbed_score_obs_L\n", + " \n", + " meat = R @ hessian_inv @ opg_centered @ hessian_inv @ R.T\n", + " \n", + " if isinstance(bread, np.ndarray):\n", + " meat_inv = np.linalg.inv(meat)\n", + " return bread.T @ meat_inv @ bread\n", + " else:\n", + " return (bread)**2/meat \n", + " \n", + " def bootstrap(self, R: ArrayLike, \n", + " W: Union[ArrayLike,None]=None, \n", + " r: Union[int, float, None]=None, \n", + " n_jobs: int = -1, \n", + " rng: Union[np.random.Generator, None] = None, \n", + " seed: Union[int, None] = None, \n", + " bootstrap_num: int =100, \n", + " restricted_residuals: bool=False, \n", + " full_enumeration: bool=False) -> ArrayLike:\n", + " \"\"\"Bootstraps Wald Statistics by generating weights and continuously perturbing the score\n", + "\n", + " Args:\n", + " R (ArrayLike, optional): Restriction Matrix. Defaults to None.\n", + " r (Union[int, float], optional): restriction. Defaults to None.\n", + " W (ArrayLike, optional): Weights vector. If not specified, Rademacher weights will be generated. Defaults to None.\n", + " n_jobs (int, optional): Number of jobs to dispath for parallel processing. Defaults to -1, or all cores.\n", + " rng (Union[np.random.Generator, None], optional): random generator that can be used for reproducibility. Defaults to None.\n", + " seed (Union[int, None], optional): seed for random generator. Defaults to None.\n", + " bootstrap_num (int, optional): number of bootstraps to perform. Defaults to 100.\n", + " restricted_residuals (bool, optional): Whether to use restricted score in constructing Wald. Defaults to False.\n", + " full_enumeration (bool, optional): Whether to generate the full set of random weight possibilities. Defaults to False.\n", + "\n", + " Returns:\n", + " ArrayLike: A `bootstrap_num` x 1 matrix.\n", + " \"\"\" \n", + " \n", + " def bootstrapper(restricted_residuals, R, r, full_enumeration, rng, W):\n", + " if rng is None:\n", + " rng=np.random.RandomState(seed)\n", + "\n", + " if W is None:\n", + " W = draw_weights('rademacher',\n", + " full_enumeration=full_enumeration,\n", + " N_G_bootcluster=self.num_clusters,\n", + " boot_iter=1,\n", + " rng=rng)[0]\n", + " \n", + " return self.wald_test(W = W, restricted_residuals=restricted_residuals, R=R, r=r)\n", + " \n", + " walds = Parallel(n_jobs=n_jobs, backend='loky')(delayed(bootstrapper)(restricted_residuals=restricted_residuals, \n", + " R=R, r=r, full_enumeration=full_enumeration, rng=rng, W=W) for b in range(bootstrap_num))\n", + " \n", + " return np.array(walds)\n", + " \n", + " \n", + " def get_wald_boot(self, n_jobs: int =-1, \n", + " bootstrap_num: int =100, \n", + " restricted_residuals: bool=False, \n", + " full_enumeration: bool=False, \n", + " rng: Union[np.random.Generator, None] =None, \n", + " seed: Union[int, None]=None,\n", + " W= Union[ArrayLike,None]) -> ArrayLike:\n", + " \"\"\"Generates Wald Statistics for each parameter separately.\n", + "\n", + " Args:\n", + " n_jobs (int): Number of jobs to dispath for parallel processing. Defaults to -1, or all cores.\n", + " bootstrap_num (int, optional): number of bootstraps to perform. Defaults to 100.\n", + " restricted_residuals (bool, optional): Whether to use restricted score in constructing Wald. Defaults to False.\n", + " full_enumeration (bool, optional): Whether to generate the full set of random weight possibilities. Defaults to False.\n", + " rng (Union[np.random.Generator, None], optional): random generator that can be used for reproducibility. Defaults to None.\n", + " seed (Union[int, None], optional): seed for random generator. Defaults to None.\n", + " W ( Union[ArrayLike,None], optional): Weights vector. If not specified, Rademacher weights will be generated. Defaults to None.\n", + "\n", + " Returns:\n", + " ArrayLike: A `bootstrap_num` x k matrix where k is the number of parameters.\n", + " \"\"\" \n", + " \n", + " # generate restriction vectors for each variable for beta=0\n", + " R = np.identity(self.beta_hat.shape[0])\n", + " \n", + " wald_boot = np.zeros(shape=(bootstrap_num, self.beta_hat.shape[0]))\n", + " \n", + " for i, R_vec in enumerate(R):\n", + " wald_boot[:, i] = self.bootstrap(R = R_vec, r = 0, n_jobs=n_jobs,\n", + " rng=rng, seed=seed, bootstrap_num=bootstrap_num,\n", + " restricted_residuals=restricted_residuals,\n", + " full_enumeration=full_enumeration,\n", + " W=None)\n", + " \n", + " return wald_boot\n", + " \n", + " \n", + " def get_pvalue(self, pval_type: str='two-tailed', **kwargs) -> ArrayLike:\n", + " \"\"\"Generates p-values by comparing analytical wald statistics with boostrapped, perturbed Wald statistics\n", + "\n", + " Args:\n", + " pval_type (str, optional): The type of p-value to generate. Defaults to 'two-tailed'.\n", + " kwargs can are used for specifying other options for `get_wald_boot`.\n", + "\n", + " Returns:\n", + " ArrayLike: An array of p-values for each parameter\n", + " \"\"\" \n", + " \n", + " wald_boot = self.get_wald_boot(**kwargs)\n", + " \n", + " analytical_wald = self.get_analytical_wald()\n", + " \n", + " if pval_type == \"two-tailed\":\n", + " pvalue = np.where(wald_boot > analytical_wald.values, 1, 0).mean(axis=0)\n", + " elif pval_type == \"equal-tailed\":\n", + " pl = np.where(analytical_wald.values < wald_boot, 1,0).mean(axis=0)\n", + " ph = np.where(analytical_wald.values > wald_boot, 1,0).mean(axis=0)\n", + " pvalue = 2 * min(pl, ph)\n", + " elif pval_type == \">\":\n", + " pvalue = np.where(analytical_wald.values < wald_boot,1,0).mean(axis=0)\n", + " else:\n", + " pvalue = np.where(analytical_wald.values > wald_boot,1,0).mean(axis=0)\n", + " \n", + " return pvalue\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Optimization terminated successfully.\n", + " Current function value: 0.665335\n", + " Iterations 4\n" + ] + }, + { + "data": { + "text/plain": [ + "array([0.909, 0.028, 0.931])" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sb = ScoreWildBootstrap(p, clusters=df['c'])\n", + "\n", + "ts = sb.get_pvalue(n_jobs=1, bootstrap_num=1000)\n", + "\n", + "ts" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "def score_obs(self, params):\n", + " X= self.exog\n", + " y = self.endog\n", + " return (X.T*(y - X @ params)).T\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "sm.OLS.score_obs = score_obs" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "# linear_mod = sm.OLS(true_y, np.column_stack([X, rng.normal(size=1000)]))\n", + "\n", + "linear_mod = sm.OLS.from_formula(\"y ~ x +D\", data=df)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "| param | statistic | p-value |\n", + "|:----------|------------:|----------:|\n", + "| Intercept | 32.953 | 0.000 |\n", + "| x | 7.820 | 0.000 |\n", + "| D | 0.632 | 0.526 |\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/lordflaron/Documents/wildboottest/.venv/lib/python3.9/site-packages/tabulate/__init__.py:107: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison\n", + " (len(row) >= 1 and row[0] == SEPARATING_LINE)\n", + "/Users/lordflaron/Documents/wildboottest/.venv/lib/python3.9/site-packages/tabulate/__init__.py:108: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison\n", + " or (len(row) >= 2 and row[1] == SEPARATING_LINE)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
statisticp-value
param
Intercept[32.95279531027586]0.000
x[7.820060460734793]0.000
D[0.6320615764783465]0.526
\n", + "
" + ], + "text/plain": [ + " statistic p-value\n", + "param \n", + "Intercept [32.95279531027586] 0.000\n", + "x [7.820060460734793] 0.000\n", + "D [0.6320615764783465] 0.526" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "wildboottest(linear_mod, B=1000, weights_type='rademacher',\n", + " impose_null=True, bootstrap_type='11')" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0. , 0.001, 0.868])" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sb = ScoreWildBootstrap(linear_mod, clusters=df['c'])\n", + "\n", + "ts = sb.get_pvalue(n_jobs=1, bootstrap_num=1000)\n", + "\n", + "ts" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tests/test_score.do b/tests/test_score.do new file mode 100644 index 0000000..aec619f --- /dev/null +++ b/tests/test_score.do @@ -0,0 +1,163 @@ + +**************************************************** +* Code for Table 2 of Kline and Santos (2011) * +* This file computes Analytical Wald and LM tests * +* along with Score bootstrapped tests. * +* Some other results not reported in the paper * +* are also included. * +**************************************************** + +version 11.1 +set seed 12345 + +**mata subroutine to compute recentered clustered outer products** +**inputs a matrix S of scores and a cluster summing matrix L** +**outputs clustered outer product matrix** +**note this program assumes a balanced design** +**and proper sort order** +cap mata: mata drop clustOPG() +mata: +real matrix clustOPG(real matrix S,real matrix L){ +real scalar N +real matrix Scent, Scentsum, OPGcent + +N=rows(S) +Scent=S - J(N,1,1)*mean(S) +Scentsum=L'*Scent +OPGcent=Scentsum'*Scentsum +return(OPGcent) +} +end + + + +/* cap prog drop sim_probit +prog def sim_probit, rclass +syntax [, c(integer 5) m(integer 50) f(integer 0) r(integer 199)] */ +*Notes on syntax: +*c - # of clusters +*m - # of obs per cluster +*f - controls whether to use mixture regressor +*r - # of bootstrap reps per simulation + +local c 10 +local m 100 +local f 0 +local r 1000 + +**Generate data** +drop _all +set obs `c' //generate c clusters + +gen x=invnorm(uniform()) //covariate +gen mix=uniform()>.1 //contamination probability + +if `f'==1{ + gen D=(mix*invnorm(uniform()) + (1-mix)*(2+3*invnorm(uniform())))/4 //regressor of interest +} +else{ + gen D=(invnorm(uniform()))/4 //regressor of interest +} + +gen v=invnorm(uniform()) + + +gen c=_n //generate cluster id + +expand `m' //make m obs per cluster + +replace x=(x+invnorm(uniform()))/4 //add some within cluster variation in x + +gen y=x+D+v/sqrt(2)+invnorm(uniform())/sqrt(2) //form outcome +replace y=y>0 +sort c + +**Restricted** +constraint 1 D=1 +glm y x D, constraint(1) fam(bin) link(probit) +predict pr +predict xbr, xb +gen phir=normalden(xbr) +gen er=(y-pr)*phir/(pr*(1-pr)) //quasi-residual +gen wr=phir^2/(pr*(1-pr)) //weights for hessian +gen one=1 + +mat accum Hr=one x D [iw=wr], nocons //form hessian */ + + +**Unrestricted** +probit y x D, cluster(c) +global b=_b[D] +local stdof=sqrt((`m'*`c'-3)/(`m'*`c'-1)) //inverse stata DOF correction +global W=((_b[D]-1)/(_se[D]*`stdof'))^2 //save analytical Wald + +predict pu +predict xbu, xb +gen phiu=normalden(xbu) +gen eu=(y-pu)*phiu/(pu*(1-pu)) //quasi-residual +gen wu=phiu^2/(pu*(1-pu)) //weights for hessian + +global dfk=(`c'/(`c'-1)) +mata: L=I(`c')#J(`m',1,1) //matrix for summing across clusters +mata: X=st_data(.,("one", "x", "D")) +mata: C=(0\0\1) //constraint matrix -- third coefficient is restricted + +mata: Hr=st_matrix("Hr") +mata: Hrinv=invsym(Hr) +mata: Ar=C'*Hrinv //form A_n + + +**Prepare influence function wald +mat accum Hu=one x D [iw=wu], nocons //form hessian +mata: Hu=st_matrix("Hu") +mata: Huinv=invsym(Hu) +mata: Au=C'*Huinv +mata: eu=st_data(.,"eu") +mata: OPGucent=clustOPG(X:*eu,L) +mata: Vinvu=invsym(Au*OPGucent*$dfk*Au') //compute inverse of variance of influence function + + +save "sim_sample.dta", replace + +mata: S = X:*eu + +**Bootstrap** +gen u=. +gen ystar=. +gen ernew=. +gen eunew=. +gen pos=. + + +mat Ts=J(1,6,.) //store bootstrap statistics + +by c: replace u=uniform() +by c: replace pos=u[1]<.5 //cluster level rademacher indicator +gen radem = 2*pos - 1 + +qui replace ernew=(2*pos-1)*er //weighted residual +qui replace eunew=(2*pos-1)*eu //weighted residual + +*Score bootstrap LM -- Rademacher +mata: er=st_data(.,"ernew") +mata: Sr=colsum(X:*er) +mata: OPGrcent=clustOPG(X:*er,L) +mata: lm=Sr*Ar'*invsym(Ar*(OPGrcent)*$dfk*Ar')*Ar*Sr' +mata: st_numscalar("lm",lm) +mat Ts[1,1]=lm //save bootstrap LM + + +*Score Wald -- Rademacher +mata: eu=st_data(.,"eunew") +mata: Su=colsum(X:*eu) +mata: OPGucent=clustOPG(X:*eu,L) +mata: wald=Su*Au'*invsym(Au*OPGucent*$dfk*Au')*Au*Su' +mata: st_numscalar("wald",wald) +mat Ts[1,2]=wald //save bootstrap Wald + +*Score bootstrap Wald combining restricted and unrestricted scores -- Rademacher +mata: wald_r=Sr*Ar'*invsym(Au*OPGucent*$dfk*Au')*Ar*Sr' +mata: st_numscalar("wald_r",wald) +mat Ts[1,3]=wald //save bootstrap Wald + +