pybhatlib 0.3.2__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/.gitignore +9 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/CHANGELOG.md +24 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/PKG-INFO +38 -18
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/README.md +35 -15
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t04e_mnp_mixture.py +25 -20
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t04h_bhatlib_table1.py +1 -0
- pybhatlib-0.4.0/examples/tutorials/python_scripts/t09a_lr.py +238 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t04e_mnp_mixture.ipynb +25 -20
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t04h_bhatlib_table1.ipynb +1 -0
- pybhatlib-0.4.0/examples/tutorials/t09a_lr.ipynb +320 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/pyproject.toml +1 -1
- pybhatlib-0.4.0/src/pybhatlib/__init__.py +100 -0
- pybhatlib-0.4.0/src/pybhatlib/_version.py +1 -0
- pybhatlib-0.4.0/src/pybhatlib/gradmvn/_pdfrectn.py +616 -0
- pybhatlib-0.4.0/src/pybhatlib/io/_data_loader.py +158 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/matgradient/__init__.py +13 -1
- pybhatlib-0.4.0/src/pybhatlib/matgradient/_corr_chol.py +202 -0
- pybhatlib-0.4.0/src/pybhatlib/matgradient/_radial.py +425 -0
- pybhatlib-0.4.0/src/pybhatlib/mixed/__init__.py +81 -0
- pybhatlib-0.4.0/src/pybhatlib/mixed/_copula.py +500 -0
- pybhatlib-0.4.0/src/pybhatlib/mixed/_draws.py +281 -0
- pybhatlib-0.4.0/src/pybhatlib/mixed/_engine.py +595 -0
- pybhatlib-0.4.0/src/pybhatlib/mixed/_kernel.py +182 -0
- pybhatlib-0.4.0/src/pybhatlib/mixed/_predict.py +594 -0
- pybhatlib-0.4.0/src/pybhatlib/mixed/_rc_pipeline.py +612 -0
- pybhatlib-0.4.0/src/pybhatlib/mixed/_reparam.py +886 -0
- pybhatlib-0.4.0/src/pybhatlib/mixed/_spec.py +566 -0
- pybhatlib-0.4.0/src/pybhatlib/models/lr/__init__.py +17 -0
- pybhatlib-0.4.0/src/pybhatlib/models/lr/_lr_ate.py +234 -0
- pybhatlib-0.4.0/src/pybhatlib/models/lr/_lr_control.py +47 -0
- pybhatlib-0.4.0/src/pybhatlib/models/lr/_lr_forecast.py +29 -0
- pybhatlib-0.4.0/src/pybhatlib/models/lr/_lr_loglik.py +101 -0
- pybhatlib-0.4.0/src/pybhatlib/models/lr/_lr_model.py +250 -0
- pybhatlib-0.4.0/src/pybhatlib/models/lr/_lr_results.py +256 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mdcev/_mdcev_forecast.py +3 -2
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mdcev/_mdcev_loglik.py +150 -20
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mdcev/_mdcev_model.py +2 -1
- pybhatlib-0.4.0/src/pybhatlib/models/mdcev_mixed/__init__.py +48 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mdcev_mixed/_mdcev_mixed_ate.py +225 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mdcev_mixed/_mdcev_mixed_control.py +139 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mdcev_mixed/_mdcev_mixed_forecast.py +514 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mdcev_mixed/_mdcev_mixed_kernel.py +522 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mdcev_mixed/_mdcev_mixed_model.py +750 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mdcev_mixed/_mdcev_mixed_results.py +275 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mixmnl/__init__.py +32 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mixmnl/_mixmnl_ate.py +219 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mixmnl/_mixmnl_control.py +125 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mixmnl/_mixmnl_forecast.py +135 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mixmnl/_mixmnl_kernel.py +149 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mixmnl/_mixmnl_model.py +702 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mixmnl/_mixmnl_results.py +268 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mnp/_mnp_grad_analytic.py +78 -21
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mnp/_mnp_loglik.py +91 -81
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mnp/_mnp_model.py +123 -89
- pybhatlib-0.4.0/src/pybhatlib/models/mnpkercp/__init__.py +41 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mnpkercp/_mnpkercp_ate.py +287 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mnpkercp/_mnpkercp_control.py +139 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mnpkercp/_mnpkercp_diff.py +120 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mnpkercp/_mnpkercp_forecast.py +336 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mnpkercp/_mnpkercp_kernel.py +566 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mnpkercp/_mnpkercp_model.py +700 -0
- pybhatlib-0.4.0/src/pybhatlib/models/mnpkercp/_mnpkercp_results.py +272 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/morp/_morp_grad_analytic.py +5 -4
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/morp/_morp_loglik.py +4 -3
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/morp/_morp_report.py +3 -2
- pybhatlib-0.4.0/src/pybhatlib/models/morp_flex/__init__.py +35 -0
- pybhatlib-0.4.0/src/pybhatlib/models/morp_flex/_morp_flex_ate.py +345 -0
- pybhatlib-0.4.0/src/pybhatlib/models/morp_flex/_morp_flex_control.py +146 -0
- pybhatlib-0.4.0/src/pybhatlib/models/morp_flex/_morp_flex_forecast.py +263 -0
- pybhatlib-0.4.0/src/pybhatlib/models/morp_flex/_morp_flex_kernel.py +864 -0
- pybhatlib-0.4.0/src/pybhatlib/models/morp_flex/_morp_flex_model.py +614 -0
- pybhatlib-0.4.0/src/pybhatlib/models/morp_flex/_morp_flex_results.py +280 -0
- pybhatlib-0.4.0/src/pybhatlib/utils/__init__.py +41 -0
- pybhatlib-0.4.0/src/pybhatlib/utils/_logistic.py +239 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/utils/_qmc.py +18 -4
- pybhatlib-0.4.0/src/pybhatlib/utils/_quadrature.py +55 -0
- pybhatlib-0.4.0/src/pybhatlib/utils/_safe_reparam.py +154 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/vecup/__init__.py +20 -0
- pybhatlib-0.4.0/src/pybhatlib/vecup/_panel.py +236 -0
- pybhatlib-0.4.0/src/pybhatlib/vecup/_yj.py +519 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mdcev/scag_converged/ass.csv +10 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mdcev/scag_converged/b.csv +27 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mdcev/scag_converged/draws_r1.csv +1500 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mdcev/scag_converged/draws_r10.csv +1500 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mdcev/scag_converged/draws_r2.csv +1500 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mdcev/scag_converged/draws_r3.csv +1500 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mdcev/scag_converged/draws_r4.csv +1500 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mdcev/scag_converged/draws_r5.csv +1500 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mdcev/scag_converged/draws_r6.csv +1500 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mdcev/scag_converged/draws_r7.csv +1500 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mdcev/scag_converged/draws_r8.csv +1500 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mdcev/scag_converged/draws_r9.csv +1500 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mdcev/scag_converged/ll_obs.csv +1500 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/ass.csv +15 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/b.csv +12 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/draws_r1.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/draws_r10.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/draws_r11.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/draws_r12.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/draws_r13.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/draws_r14.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/draws_r15.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/draws_r2.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/draws_r3.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/draws_r4.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/draws_r5.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/draws_r6.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/draws_r7.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/draws_r8.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/draws_r9.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/grad_obs.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/ll_obs.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnl/travelmode_converged/meta.json +46 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnp/travelmode_copula/ass.csv +5 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnp/travelmode_copula/b.csv +18 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnp/travelmode_copula/draws_r1.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnp/travelmode_copula/draws_r2.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnp/travelmode_copula/draws_r3.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnp/travelmode_copula/draws_r4.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnp/travelmode_copula/draws_r5.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnp/travelmode_copula/ll_obs.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnp/travelmode_nocopula/ass.csv +5 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnp/travelmode_nocopula/b.csv +18 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnp/travelmode_nocopula/draws_r1.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnp/travelmode_nocopula/draws_r2.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnp/travelmode_nocopula/draws_r3.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnp/travelmode_nocopula/draws_r4.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnp/travelmode_nocopula/draws_r5.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/mnp/travelmode_nocopula/ll_obs.csv +1125 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/morp/panelcommute_converged/ass.csv +10 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/morp/panelcommute_converged/b.csv +21 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/morp/panelcommute_converged/draws_r1.csv +2323 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/morp/panelcommute_converged/draws_r10.csv +2323 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/morp/panelcommute_converged/draws_r2.csv +2323 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/morp/panelcommute_converged/draws_r3.csv +2323 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/morp/panelcommute_converged/draws_r4.csv +2323 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/morp/panelcommute_converged/draws_r5.csv +2323 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/morp/panelcommute_converged/draws_r6.csv +2323 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/morp/panelcommute_converged/draws_r7.csv +2323 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/morp/panelcommute_converged/draws_r8.csv +2323 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/morp/panelcommute_converged/draws_r9.csv +2323 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/morp/panelcommute_converged/ll_obs.csv +2323 -0
- pybhatlib-0.4.0/tests/fixtures/mixed/morp/panelcommute_converged/meta.json +25 -0
- pybhatlib-0.4.0/tests/test_mixed/__init__.py +0 -0
- pybhatlib-0.4.0/tests/test_mixed/test_copula.py +301 -0
- pybhatlib-0.4.0/tests/test_mixed/test_corr_chol.py +173 -0
- pybhatlib-0.4.0/tests/test_mixed/test_draws.py +182 -0
- pybhatlib-0.4.0/tests/test_mixed/test_engine_master.py +162 -0
- pybhatlib-0.4.0/tests/test_mixed/test_logistic.py +72 -0
- pybhatlib-0.4.0/tests/test_mixed/test_logitmod.py +108 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mdcev_mixed_ate.py +456 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mdcev_mixed_collapse.py +172 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mdcev_mixed_gauss_parity.py +158 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mdcev_mixed_kernel.py +206 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mdcev_mixed_master.py +161 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mixed_predict.py +215 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mixmnl_ate.py +383 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mixmnl_collapse.py +161 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mixmnl_facade.py +330 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mixmnl_gauss_parity.py +112 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mixmnl_kernel.py +136 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mnp_diff.py +120 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mnp_spec.py +250 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mnpkercp_ate.py +443 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mnpkercp_corr_grad.py +134 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mnpkercp_gauss_parity.py +197 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mnpkercp_kernel.py +167 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mnpkercp_master.py +132 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mnpkercp_master_corr.py +132 -0
- pybhatlib-0.4.0/tests/test_mixed/test_mnpkercp_master_sparse_corr.py +131 -0
- pybhatlib-0.4.0/tests/test_mixed/test_morp_flex_ate.py +387 -0
- pybhatlib-0.4.0/tests/test_mixed/test_morp_flex_gauss_parity.py +107 -0
- pybhatlib-0.4.0/tests/test_mixed/test_morp_flex_kernel.py +207 -0
- pybhatlib-0.4.0/tests/test_mixed/test_morp_flex_master.py +112 -0
- pybhatlib-0.4.0/tests/test_mixed/test_morp_spec.py +320 -0
- pybhatlib-0.4.0/tests/test_mixed/test_panel.py +174 -0
- pybhatlib-0.4.0/tests/test_mixed/test_pdfrectn.py +144 -0
- pybhatlib-0.4.0/tests/test_mixed/test_rc_pipeline.py +230 -0
- pybhatlib-0.4.0/tests/test_mixed/test_spec.py +398 -0
- pybhatlib-0.4.0/tests/test_mixed/test_yj.py +210 -0
- pybhatlib-0.4.0/tests/test_models/test_lr.py +152 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_mdcev.py +15 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_mnp_grad_mixture.py +38 -44
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_mnp_mixture_ranvars.py +74 -128
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_mnp_table2_parity.py +7 -29
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_mnp_unpar_se.py +10 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_model_facade.py +45 -9
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_morp.py +8 -0
- pybhatlib-0.4.0/tests/test_reparam_extreme_inputs.py +84 -0
- pybhatlib-0.3.2/src/pybhatlib/__init__.py +0 -54
- pybhatlib-0.3.2/src/pybhatlib/_version.py +0 -1
- pybhatlib-0.3.2/src/pybhatlib/io/_data_loader.py +0 -44
- pybhatlib-0.3.2/src/pybhatlib/matgradient/_radial.py +0 -181
- pybhatlib-0.3.2/src/pybhatlib/utils/__init__.py +0 -6
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/CONTRIBUTING.md +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/LICENSE +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/data/TRAVELMODE.csv +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/data/TRAVELMODE_synthetic.csv +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/data/WorkshopData_ToursimExp.csv +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/data/Workshop_SCAG_Est.csv +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/data/generate_travelmode.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/data/modeData.csv +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/mnp_age45.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/mnp_ate_analysis.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/mnp_flexible_cov.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/mnp_iid.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/mnp_mixture.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/mnp_random_coefficients.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/morp_example.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/_convert_to_ipynb.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t00_quickstart.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t01a_vectorization.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t01b_ldlt.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t01c_truncated_mvn.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t02a_gradcovcor.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t02b_spherical.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t02c_chain_rules.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t03a_mvncd_methods.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t03b_mvncd_gradients.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t03c_mvncd_rect.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t03d_univariate_cdfs.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t03e_bhat2018_table1.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t04a_mnp_iid.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t04b_mnp_flexible_cov.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t04c_mnp_heteronly.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t04d_mnp_random_coefficients.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t04f_mnp_control_options.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t04g_mnp_forecasting.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t04i_bhat2018_table2.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t05b_morp_ate_predict.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t06a_backend_switching.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t06b_custom_specs.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t06c_gradient_verification.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t07a_mdcev_trad.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t07b_mdcev_lin.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t08a_mnl.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t00_quickstart.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t01a_vectorization.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t01b_ldlt.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t01c_truncated_mvn.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t02a_gradcovcor.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t02b_spherical.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t02c_chain_rules.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t03a_mvncd_methods.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t03b_mvncd_gradients.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t03c_mvncd_rect.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t03d_univariate_cdfs.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t03e_bhat2018_table1.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t04a_mnp_iid.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t04b_mnp_flexible_cov.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t04c_mnp_heteronly.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t04d_mnp_random_coefficients.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t04f_mnp_control_options.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t04g_mnp_forecasting.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t04i_bhat2018_table2.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t05b_morp_ate_predict.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t06a_backend_switching.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t06b_custom_specs.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t06c_gradient_verification.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t07a_mdcev_trad.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t07b_mdcev_lin.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/t08a_mnl.ipynb +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/backend/__init__.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/backend/_array_api.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/backend/_numpy_backend.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/backend/_torch_backend.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/gradmvn/__init__.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/gradmvn/_bivariate_trunc.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/gradmvn/_cond_trunc_grads.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/gradmvn/_logit_dist.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/gradmvn/_mvncd.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/gradmvn/_mvncd_grad.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/gradmvn/_mvncd_grad_analytic.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/gradmvn/_mvncd_grad_ovus.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/gradmvn/_mvncd_ssj.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/gradmvn/_mvncd_torch.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/gradmvn/_ordering.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/gradmvn/_other_dists.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/gradmvn/_partial_cdf.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/gradmvn/_trunc_grads.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/gradmvn/_truncated.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/gradmvn/_univariate.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/io/__init__.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/io/_spec_parser.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/matgradient/_chain_rules.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/matgradient/_gomegxomegax.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/matgradient/_gradcovcor.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/matgradient/_mat_grad_helpers.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/matgradient/_spherical.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/__init__.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/_ate_common.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/_base.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/_results_common.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mdcev/__init__.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mdcev/_mdcev_ate.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mdcev/_mdcev_control.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mdcev/_mdcev_results.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mnl/__init__.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mnl/_mnl_ate.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mnl/_mnl_control.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mnl/_mnl_forecast.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mnl/_mnl_loglik.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mnl/_mnl_model.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mnl/_mnl_results.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mnp/__init__.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mnp/_mnp_ate.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mnp/_mnp_control.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mnp/_mnp_forecast.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mnp/_mnp_grad_gpu.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/mnp/_mnp_results.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/morp/__init__.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/morp/_morp_ate.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/morp/_morp_control.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/morp/_morp_forecast.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/morp/_morp_model.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/models/morp/_morp_results.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/optim/__init__.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/optim/_convergence.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/optim/_scipy_optim.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/optim/_torch_optim.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/utils/_seeds.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/utils/_validation.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/vecup/_ldlt.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/vecup/_mask.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/vecup/_nondiag.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/vecup/_truncnorm.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/src/pybhatlib/vecup/_vec_ops.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/__init__.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/conftest.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/fixtures/bhatlib_table2_targets.json +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_api_harmonization.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_backend/__init__.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_backend/test_array_api.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_gradmvn/__init__.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_gradmvn/test_bivariate_trunc.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_gradmvn/test_cond_trunc_grads.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_gradmvn/test_mvncd.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_gradmvn/test_mvncd_batch.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_gradmvn/test_mvncd_grad_analytic.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_gradmvn/test_mvncd_grad_ovus.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_gradmvn/test_mvncd_methods.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_gradmvn/test_mvncd_rect.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_gradmvn/test_mvncd_rect_inf_bounds.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_gradmvn/test_ordering.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_gradmvn/test_other_dists.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_gradmvn/test_trunc_grads.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_integration/__init__.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_matgradient/__init__.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_matgradient/test_gradcovcor.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_matgradient/test_mat_grad_helpers.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_matgradient/test_radial.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_matgradient/test_spherical.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/__init__.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_ate_common.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_ate_from_params.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_mdcev_scenarios.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_mnl.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_mnl_ate_from_params.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_mnp_active_mask_verbose.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_mnp_ate_scenarios.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_mnp_control.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_mnp_gauss_parity.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_mnp_grad.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_mnp_perobs_scores.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_mnp_se_options.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_morp_analytic_gradient.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_morp_gauss_parity.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_morp_per_outcome_spec.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_morp_scenarios.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_models/test_results_harmonization.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_vecup/__init__.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_vecup/test_ldlt.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_vecup/test_ldlt_rank2.py +0 -0
- {pybhatlib-0.3.2 → pybhatlib-0.4.0}/tests/test_vecup/test_vec_ops.py +0 -0
|
@@ -4,6 +4,30 @@ All notable changes to pybhatlib are documented here. The format is based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
5
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.4.0] - 2026-09-16
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **Linear regression (LR)** — `LRModel` / `LRControl` / `LRResults`: one
|
|
11
|
+
continuous outcome solved in closed form (SVD least squares) with classical,
|
|
12
|
+
White-robust, and BHHH standard errors; the residual standard deviation
|
|
13
|
+
`sigma` is reported as the trailing parameter (MDCEV convention);
|
|
14
|
+
`lr_predict`, `lr_ate` / `lr_ate_from_params` on the shared `scenarios=`
|
|
15
|
+
API; tutorial `t09a_lr`.
|
|
16
|
+
- **Mixed (simulation-based) model family** — a shared maximum-simulated-
|
|
17
|
+
likelihood engine (Halton draws, panel broadcast, normal / log-normal /
|
|
18
|
+
Yeo-Johnson coefficient transforms, analytic scores through the
|
|
19
|
+
reparameterisation Jacobians), plus four models built on it:
|
|
20
|
+
`MixMNLModel` (mixed multinomial logit), `MNPKerCPModel` (mixed MNP
|
|
21
|
+
coupling random coefficients to the kernel error through a copula),
|
|
22
|
+
`MORPFlexModel` (mixed MORP with a flexible Yeo-Johnson
|
|
23
|
+
kernel and rectangle MVNCD), and `MDCEVMixedModel` (mixed MDCEV with a
|
|
24
|
+
vectorized per-draw kernel). These are new and still under active
|
|
25
|
+
refinement; the closed-form models above are the stable API.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- **MNP mixture-of-normals** — coefficients that are not random now stay shared
|
|
29
|
+
across segments instead of being re-estimated per segment.
|
|
30
|
+
|
|
7
31
|
## [0.3.2] - 2026-07-22
|
|
8
32
|
|
|
9
33
|
First version available on PyPI. Identical in code to 0.3.1; released under a new
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: pybhatlib
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Matrix-based econometric inference in Python: analytic MVNCD evaluation and discrete-choice models
|
|
5
5
|
Project-URL: Homepage, https://github.com/UMN-Choi-Lab/pybhatlib
|
|
6
6
|
Project-URL: Repository, https://github.com/UMN-Choi-Lab/pybhatlib
|
|
7
7
|
Project-URL: Documentation, https://github.com/UMN-Choi-Lab/pybhatlib#readme
|
|
@@ -40,18 +40,28 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
|
|
41
41
|
# pybhatlib
|
|
42
42
|
|
|
43
|
+
[](https://pypi.org/project/pybhatlib/)
|
|
43
44
|
[](https://pypi.org/project/pybhatlib/)
|
|
45
|
+
[](https://github.com/UMN-Choi-Lab/pybhatlib/actions/workflows/tests.yml)
|
|
44
46
|
[](https://github.com/UMN-Choi-Lab/pybhatlib/blob/main/LICENSE)
|
|
45
47
|
|
|
46
|
-
Python
|
|
47
|
-
|
|
48
|
+
Open-source Python library for matrix-based econometric inference: multivariate
|
|
49
|
+
normal CDF evaluation with analytic gradients, and maximum-likelihood estimation of
|
|
50
|
+
discrete-choice models (MNP, MORP, MDCEV, MNL).
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
The numerical core follows the GAUSS **BHATLIB** library by Chandra Bhat and
|
|
53
|
+
colleagues (UT Austin / Aptech Systems) and implements Bhat's (2018) analytic
|
|
54
|
+
MVNCD approximation.
|
|
52
55
|
|
|
53
56
|
## Installation
|
|
54
57
|
|
|
58
|
+
```bash
|
|
59
|
+
pip install pybhatlib # core (NumPy + SciPy + Numba)
|
|
60
|
+
pip install "pybhatlib[torch]" # add PyTorch backend (optional GPU)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
From source (for development, or to run the bundled examples and tutorials):
|
|
64
|
+
|
|
55
65
|
```bash
|
|
56
66
|
git clone https://github.com/UMN-Choi-Lab/pybhatlib.git
|
|
57
67
|
cd pybhatlib
|
|
@@ -117,8 +127,15 @@ A runnable end-to-end example (with `morp_ate`, `morp_predict`,
|
|
|
117
127
|
- **Multivariate Ordered Response Probit (MORP)** — multiple ordinal outcomes
|
|
118
128
|
with shared covariance; per-outcome `spec` mapping
|
|
119
129
|
- **Multiple Discrete-Continuous Extreme Value (MDCEV)** — traditional
|
|
120
|
-
(Bhat 2008) and linear
|
|
121
|
-
|
|
130
|
+
(Bhat 2008) and linear outside-good utility specifications, selected via
|
|
131
|
+
`MDCEVControl.utility`
|
|
132
|
+
- **Multinomial Logit (MNL)**
|
|
133
|
+
- **Linear regression (LR)** — single continuous outcome, closed-form OLS with
|
|
134
|
+
classical / White-robust / BHHH standard errors
|
|
135
|
+
- **Mixed / simulation-based models** — `MixMNLModel`, `MNPKerCPModel`,
|
|
136
|
+
`MORPFlexModel`, `MDCEVMixedModel` on a shared maximum-simulated-likelihood
|
|
137
|
+
engine (Halton draws, panel data, normal / log-normal / Yeo-Johnson
|
|
138
|
+
coefficient transforms). New and under active refinement.
|
|
122
139
|
|
|
123
140
|
**Numerical core**
|
|
124
141
|
- `vecup` — vecdup, matdupfull, LDLT decomposition, truncated MVN moments
|
|
@@ -151,8 +168,8 @@ A runnable end-to-end example (with `morp_ate`, `morp_predict`,
|
|
|
151
168
|
|
|
152
169
|
## Verification
|
|
153
170
|
|
|
154
|
-
pybhatlib reproduces Table 1
|
|
155
|
-
TRAVELMODE dataset (3 modes — DA, SR, TR; 1125 observations):
|
|
171
|
+
pybhatlib reproduces Table 1 of the BHATLIB GAUSS reference implementation using
|
|
172
|
+
the TRAVELMODE dataset (3 modes — DA, SR, TR; 1125 observations):
|
|
156
173
|
|
|
157
174
|
| Model | Specification | Target LL | Achieved LL | Status |
|
|
158
175
|
|-------|--------------|-----------|-------------|--------|
|
|
@@ -162,10 +179,10 @@ TRAVELMODE dataset (3 modes — DA, SR, TR; 1125 observations):
|
|
|
162
179
|
| (c) | + Random coeff. OVTT | -635.871 | -635.871 | exact match |
|
|
163
180
|
| (d) | 2-segment mixture | -634.975 | -632.912 | close (multi-modal) |
|
|
164
181
|
|
|
165
|
-
Models (a)–(c) reproduce the
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
182
|
+
Models (a)–(c) reproduce the GAUSS estimates and BHHH standard errors to ≤0.001
|
|
183
|
+
on every parameter (verified end-to-end against GAUSS 26.1.1 + MaxLik 5.0.9).
|
|
184
|
+
Model (d) is multi-modal — the Python optimum is a slightly better local mode
|
|
185
|
+
than the GAUSS one.
|
|
169
186
|
|
|
170
187
|
For MORP, `iid=False` now uses GAUSS BHATLIB's unit-variance identification by
|
|
171
188
|
default (`MORPControl.fix_scales=True`): the latent-utility variances are fixed
|
|
@@ -193,6 +210,8 @@ matching `.py` script under `python_scripts/`):
|
|
|
193
210
|
| MNP | `t04a_mnp_iid` … `t04g_mnp_forecasting`, `t04h_bhatlib_table1`, `t04i_bhat2018_table2` |
|
|
194
211
|
| MORP | `t05b_morp_ate_predict` |
|
|
195
212
|
| MDCEV | `t07a_mdcev_trad`, `t07b_mdcev_lin` |
|
|
213
|
+
| MNL | `t08a_mnl` |
|
|
214
|
+
| LR | `t09a_lr` |
|
|
196
215
|
| Backends & verification | `t06a_backend_switching`, `t06b_custom_specs`, `t06c_gradient_verification` |
|
|
197
216
|
|
|
198
217
|
## Contributing
|
|
@@ -211,8 +230,9 @@ pytest tests/ -m torch # PyTorch backend tests only
|
|
|
211
230
|
1. Bhat, C. R. (2018). New Matrix-Based Methods for the Analytic Evaluation of the
|
|
212
231
|
Multivariate Cumulative Normal Distribution Function. *Transportation Research
|
|
213
232
|
Part B*, 109: 238–256.
|
|
214
|
-
2. Bhat, C. R
|
|
215
|
-
|
|
233
|
+
2. Bhat, C. R. (2008). The Multiple Discrete-Continuous Extreme Value (MDCEV)
|
|
234
|
+
Model: Role of Utility Function Parameters, Identification Considerations, and
|
|
235
|
+
Model Extensions. *Transportation Research Part B*, 42(3): 274–303.
|
|
216
236
|
|
|
217
237
|
## License
|
|
218
238
|
|
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
# pybhatlib
|
|
2
2
|
|
|
3
|
+
[](https://pypi.org/project/pybhatlib/)
|
|
3
4
|
[](https://pypi.org/project/pybhatlib/)
|
|
5
|
+
[](https://github.com/UMN-Choi-Lab/pybhatlib/actions/workflows/tests.yml)
|
|
4
6
|
[](https://github.com/UMN-Choi-Lab/pybhatlib/blob/main/LICENSE)
|
|
5
7
|
|
|
6
|
-
Python
|
|
7
|
-
|
|
8
|
+
Open-source Python library for matrix-based econometric inference: multivariate
|
|
9
|
+
normal CDF evaluation with analytic gradients, and maximum-likelihood estimation of
|
|
10
|
+
discrete-choice models (MNP, MORP, MDCEV, MNL).
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
The numerical core follows the GAUSS **BHATLIB** library by Chandra Bhat and
|
|
13
|
+
colleagues (UT Austin / Aptech Systems) and implements Bhat's (2018) analytic
|
|
14
|
+
MVNCD approximation.
|
|
12
15
|
|
|
13
16
|
## Installation
|
|
14
17
|
|
|
18
|
+
```bash
|
|
19
|
+
pip install pybhatlib # core (NumPy + SciPy + Numba)
|
|
20
|
+
pip install "pybhatlib[torch]" # add PyTorch backend (optional GPU)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
From source (for development, or to run the bundled examples and tutorials):
|
|
24
|
+
|
|
15
25
|
```bash
|
|
16
26
|
git clone https://github.com/UMN-Choi-Lab/pybhatlib.git
|
|
17
27
|
cd pybhatlib
|
|
@@ -77,8 +87,15 @@ A runnable end-to-end example (with `morp_ate`, `morp_predict`,
|
|
|
77
87
|
- **Multivariate Ordered Response Probit (MORP)** — multiple ordinal outcomes
|
|
78
88
|
with shared covariance; per-outcome `spec` mapping
|
|
79
89
|
- **Multiple Discrete-Continuous Extreme Value (MDCEV)** — traditional
|
|
80
|
-
(Bhat 2008) and linear
|
|
81
|
-
|
|
90
|
+
(Bhat 2008) and linear outside-good utility specifications, selected via
|
|
91
|
+
`MDCEVControl.utility`
|
|
92
|
+
- **Multinomial Logit (MNL)**
|
|
93
|
+
- **Linear regression (LR)** — single continuous outcome, closed-form OLS with
|
|
94
|
+
classical / White-robust / BHHH standard errors
|
|
95
|
+
- **Mixed / simulation-based models** — `MixMNLModel`, `MNPKerCPModel`,
|
|
96
|
+
`MORPFlexModel`, `MDCEVMixedModel` on a shared maximum-simulated-likelihood
|
|
97
|
+
engine (Halton draws, panel data, normal / log-normal / Yeo-Johnson
|
|
98
|
+
coefficient transforms). New and under active refinement.
|
|
82
99
|
|
|
83
100
|
**Numerical core**
|
|
84
101
|
- `vecup` — vecdup, matdupfull, LDLT decomposition, truncated MVN moments
|
|
@@ -111,8 +128,8 @@ A runnable end-to-end example (with `morp_ate`, `morp_predict`,
|
|
|
111
128
|
|
|
112
129
|
## Verification
|
|
113
130
|
|
|
114
|
-
pybhatlib reproduces Table 1
|
|
115
|
-
TRAVELMODE dataset (3 modes — DA, SR, TR; 1125 observations):
|
|
131
|
+
pybhatlib reproduces Table 1 of the BHATLIB GAUSS reference implementation using
|
|
132
|
+
the TRAVELMODE dataset (3 modes — DA, SR, TR; 1125 observations):
|
|
116
133
|
|
|
117
134
|
| Model | Specification | Target LL | Achieved LL | Status |
|
|
118
135
|
|-------|--------------|-----------|-------------|--------|
|
|
@@ -122,10 +139,10 @@ TRAVELMODE dataset (3 modes — DA, SR, TR; 1125 observations):
|
|
|
122
139
|
| (c) | + Random coeff. OVTT | -635.871 | -635.871 | exact match |
|
|
123
140
|
| (d) | 2-segment mixture | -634.975 | -632.912 | close (multi-modal) |
|
|
124
141
|
|
|
125
|
-
Models (a)–(c) reproduce the
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
142
|
+
Models (a)–(c) reproduce the GAUSS estimates and BHHH standard errors to ≤0.001
|
|
143
|
+
on every parameter (verified end-to-end against GAUSS 26.1.1 + MaxLik 5.0.9).
|
|
144
|
+
Model (d) is multi-modal — the Python optimum is a slightly better local mode
|
|
145
|
+
than the GAUSS one.
|
|
129
146
|
|
|
130
147
|
For MORP, `iid=False` now uses GAUSS BHATLIB's unit-variance identification by
|
|
131
148
|
default (`MORPControl.fix_scales=True`): the latent-utility variances are fixed
|
|
@@ -153,6 +170,8 @@ matching `.py` script under `python_scripts/`):
|
|
|
153
170
|
| MNP | `t04a_mnp_iid` … `t04g_mnp_forecasting`, `t04h_bhatlib_table1`, `t04i_bhat2018_table2` |
|
|
154
171
|
| MORP | `t05b_morp_ate_predict` |
|
|
155
172
|
| MDCEV | `t07a_mdcev_trad`, `t07b_mdcev_lin` |
|
|
173
|
+
| MNL | `t08a_mnl` |
|
|
174
|
+
| LR | `t09a_lr` |
|
|
156
175
|
| Backends & verification | `t06a_backend_switching`, `t06b_custom_specs`, `t06c_gradient_verification` |
|
|
157
176
|
|
|
158
177
|
## Contributing
|
|
@@ -171,8 +190,9 @@ pytest tests/ -m torch # PyTorch backend tests only
|
|
|
171
190
|
1. Bhat, C. R. (2018). New Matrix-Based Methods for the Analytic Evaluation of the
|
|
172
191
|
Multivariate Cumulative Normal Distribution Function. *Transportation Research
|
|
173
192
|
Part B*, 109: 238–256.
|
|
174
|
-
2. Bhat, C. R
|
|
175
|
-
|
|
193
|
+
2. Bhat, C. R. (2008). The Multiple Discrete-Continuous Extreme Value (MDCEV)
|
|
194
|
+
Model: Role of Utility Function Parameters, Identification Considerations, and
|
|
195
|
+
Model Extensions. *Transportation Research Part B*, 42(3): 274–303.
|
|
176
196
|
|
|
177
197
|
## License
|
|
178
198
|
|
|
@@ -80,7 +80,8 @@ print("""
|
|
|
80
80
|
Example (S=2, n_beta=7, flexible covariance with 1 free param):
|
|
81
81
|
2 * 7 + 1 + 1 = 16 parameters
|
|
82
82
|
|
|
83
|
-
In pybhatlib: set nseg=2 in MNPControl
|
|
83
|
+
In pybhatlib: set nseg=2 in MNPControl and name the coefficient(s) that
|
|
84
|
+
vary by segment in ranvars=[...]; every other coefficient is shared.
|
|
84
85
|
""")
|
|
85
86
|
|
|
86
87
|
# ============================================================
|
|
@@ -110,6 +111,7 @@ model = MNPModel(
|
|
|
110
111
|
alternatives=alternatives,
|
|
111
112
|
spec=spec,
|
|
112
113
|
control=MNPControl(iid=False, nseg=2, maxiter=200, verbose=1, seed=42),
|
|
114
|
+
ranvars=["OVTT"], # OVTT varies by segment; all other betas are shared
|
|
113
115
|
)
|
|
114
116
|
results = model.fit()
|
|
115
117
|
t_elapsed = time.perf_counter() - t0
|
|
@@ -149,15 +151,13 @@ print("""
|
|
|
149
151
|
"MNP Table2 d") fits a 2-segment mixture WITH a random coefficient
|
|
150
152
|
on OVTT (ranvars=["OVTT"], nrand=1), reaching LL = -634.975.
|
|
151
153
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
have
|
|
158
|
-
|
|
159
|
-
exact paper model, add ranvars=["OVTT"] to the MNPModel(...) call
|
|
160
|
-
(see Step 4).
|
|
154
|
+
That is exactly the model fitted above: only OVTT gets a
|
|
155
|
+
segment-specific coefficient (and its own variance) in each segment,
|
|
156
|
+
every other coefficient is shared across the two segments, and the
|
|
157
|
+
kernel covariance is common. On the supplied TRAVELMODE data
|
|
158
|
+
pybhatlib reaches LL = -634.94, matching the paper to 0.04. Mixture
|
|
159
|
+
likelihoods have several local optima, so a different seed may stop
|
|
160
|
+
at a slightly different mode with a similar LL.
|
|
161
161
|
""")
|
|
162
162
|
|
|
163
163
|
# Print segment probabilities if available
|
|
@@ -180,10 +180,12 @@ print("""
|
|
|
180
180
|
two segments' coefficient vectors side by side and weight them by the
|
|
181
181
|
estimated mixing probabilities pi_s.
|
|
182
182
|
|
|
183
|
-
In this 7-variable specification
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
183
|
+
In this 7-variable specification only OVTT was allowed to vary by
|
|
184
|
+
segment (ranvars=["OVTT"]), so segment 1 owns the first 7 entries of
|
|
185
|
+
b_original (CON_SR..COST) and segment 2 differs from it only in the
|
|
186
|
+
OVTT_s2 entry near the end; the other six coefficients are shared and
|
|
187
|
+
identical in both columns below. Reading OVTT across the two groups
|
|
188
|
+
tells us how they differ in out-of-vehicle time sensitivity.
|
|
187
189
|
""")
|
|
188
190
|
|
|
189
191
|
# Pair up segment-1 and segment-2 betas for the 7 utility variables.
|
|
@@ -196,7 +198,7 @@ print(f" {'Variable':<12s}{'Seg1 beta':>12s}{'Seg2 beta':>12s}"
|
|
|
196
198
|
print(" " + "-" * 49)
|
|
197
199
|
for nm in beta_names:
|
|
198
200
|
b1 = name_to_val.get(nm, float("nan"))
|
|
199
|
-
b2 = name_to_val.get(nm + "_s2",
|
|
201
|
+
b2 = name_to_val.get(nm + "_s2", b1) # shared coefficient -> same value
|
|
200
202
|
mixed = pi[0] * b1 + pi[1] * b2
|
|
201
203
|
print(f" {nm:<12s}{b1:>12.4f}{b2:>12.4f}{mixed:>13.4f}")
|
|
202
204
|
|
|
@@ -247,12 +249,15 @@ print("""
|
|
|
247
249
|
|
|
248
250
|
for seed in [42, 123, 456, 789]:
|
|
249
251
|
ctrl = MNPControl(iid=False, nseg=2, seed=seed, maxiter=200)
|
|
250
|
-
res = MNPModel(..., control=ctrl).fit()
|
|
252
|
+
res = MNPModel(..., control=ctrl, ranvars=["OVTT"]).fit()
|
|
251
253
|
print(seed, res.loglik * res.n_obs)
|
|
252
254
|
|
|
253
|
-
Parameter count scaling:
|
|
254
|
-
- nseg=
|
|
255
|
-
- nseg=
|
|
255
|
+
Parameter count scaling (n_rand = number of names in ranvars):
|
|
256
|
+
- nseg=1: n_beta + n_cov + n_omega(n_rand)
|
|
257
|
+
- nseg=2: + 1 segment param + n_rand betas + n_omega(n_rand)
|
|
258
|
+
- nseg=3: + 2 segment params + 2 * (n_rand betas + n_omega(n_rand))
|
|
259
|
+
Coefficients not listed in ranvars are shared across segments and
|
|
260
|
+
cost nothing extra.
|
|
256
261
|
- Identification requires sufficient variation in the data; too many
|
|
257
262
|
segments on a small dataset will produce near-flat likelihood
|
|
258
263
|
|
|
@@ -263,7 +268,7 @@ print("""
|
|
|
263
268
|
4. Use LR test or BIC to decide between nseg=1 and nseg=2
|
|
264
269
|
|
|
265
270
|
Rule of thumb:
|
|
266
|
-
- Each additional segment adds
|
|
271
|
+
- Each additional segment adds 1 + n_rand + n_omega(n_rand) free parameters
|
|
267
272
|
- Worthwhile only when delta-LL > (n_beta + 1) / 2 (BIC criterion)
|
|
268
273
|
- For n_beta=7: need delta-LL > 4 to justify a second segment
|
|
269
274
|
""")
|
{pybhatlib-0.3.2 → pybhatlib-0.4.0}/examples/tutorials/python_scripts/t04h_bhatlib_table1.py
RENAMED
|
@@ -320,6 +320,7 @@ model_d = MNPModel(
|
|
|
320
320
|
alternatives=alternatives,
|
|
321
321
|
spec=spec_7var,
|
|
322
322
|
control=MNPControl(iid=False, nseg=2, maxiter=200, verbose=0, seed=42),
|
|
323
|
+
ranvars=["OVTT"], # paper Model (d): OVTT varies by segment
|
|
323
324
|
)
|
|
324
325
|
res_d = model_d.fit()
|
|
325
326
|
t_d = time.perf_counter() - t0
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""Tutorial T09a: Linear Regression (Single Continuous Outcome).
|
|
2
|
+
|
|
3
|
+
This tutorial fits an ordinary least-squares linear regression with
|
|
4
|
+
pybhatlib's ``LRModel`` on the tourism-expenditure data used in t07a/t07b,
|
|
5
|
+
explaining log total trip expenditure with traveller and trip attributes.
|
|
6
|
+
|
|
7
|
+
What you will learn:
|
|
8
|
+
- How the ``spec`` convention shared by all pybhatlib models maps
|
|
9
|
+
coefficient names to data columns (``"uno"`` = constant)
|
|
10
|
+
- That estimation is analytical (SVD least squares): no optimizer,
|
|
11
|
+
starting values, or convergence settings
|
|
12
|
+
- How to read ``results.summary()``: coefficient table with the
|
|
13
|
+
residual standard deviation ``sigma`` as the trailing parameter,
|
|
14
|
+
R-squared, F-test, residual variance
|
|
15
|
+
- How ``LRControl(se_method=...)`` switches between classical,
|
|
16
|
+
heteroscedasticity-robust (White), and BHHH standard errors
|
|
17
|
+
- Prediction on the training data or on a new DataFrame
|
|
18
|
+
- Average treatment effects via the ``scenarios=`` API shared with
|
|
19
|
+
MNP / MNL / MORP / MDCEV, and ATEs from externally supplied coefficients
|
|
20
|
+
|
|
21
|
+
Prerequisites: t00 (quickstart).
|
|
22
|
+
|
|
23
|
+
Expected runtime: < 1 sec
|
|
24
|
+
"""
|
|
25
|
+
import os, sys
|
|
26
|
+
import numpy as np
|
|
27
|
+
import pandas as pd
|
|
28
|
+
|
|
29
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", "src"))
|
|
30
|
+
|
|
31
|
+
from pybhatlib.models.lr import LRControl, LRModel, lr_ate_from_params
|
|
32
|
+
|
|
33
|
+
data_path = os.path.join(os.path.dirname(__file__), "..", "..", "data", "WorkshopData_ToursimExp.csv")
|
|
34
|
+
|
|
35
|
+
# ============================================================
|
|
36
|
+
# Step 1: Data and Specification
|
|
37
|
+
# ============================================================
|
|
38
|
+
print("=" * 60)
|
|
39
|
+
print(" Step 1: Data and Specification")
|
|
40
|
+
print("=" * 60)
|
|
41
|
+
|
|
42
|
+
data = pd.read_csv(data_path)
|
|
43
|
+
|
|
44
|
+
# Outcome: log of total trip expenditure across the five spending categories
|
|
45
|
+
# (accommodation, food & beverage, transport, shopping, recreation).
|
|
46
|
+
data["total_exp"] = data[["Accomod", "FandB", "Transp", "Shp", "Recr"]].sum(axis=1)
|
|
47
|
+
data["ln_exp"] = np.log(data["total_exp"])
|
|
48
|
+
|
|
49
|
+
print(f" n = {len(data)} trips")
|
|
50
|
+
print(f" total expenditure: mean = {data['total_exp'].mean():,.0f}, "
|
|
51
|
+
f"median = {data['total_exp'].median():,.0f}")
|
|
52
|
+
print(f" ln_exp: mean = {data['ln_exp'].mean():.3f}, sd = {data['ln_exp'].std():.3f}")
|
|
53
|
+
|
|
54
|
+
print("""
|
|
55
|
+
The spec grammar is the one every pybhatlib model uses: a coefficient
|
|
56
|
+
name maps to a data column, to "uno" (a column of ones, i.e. the
|
|
57
|
+
constant), or to a numeric constant. Nothing is added implicitly, so
|
|
58
|
+
the intercept is declared explicitly. The outcome-keyed form used by
|
|
59
|
+
the multi-outcome models, e.g. {"B_RURAL": {"ln_exp": "rural"}}, is
|
|
60
|
+
also accepted.
|
|
61
|
+
""")
|
|
62
|
+
|
|
63
|
+
spec = {
|
|
64
|
+
"CON": "uno", # constant
|
|
65
|
+
"B_RURAL": "rural", # rural residence (urban = base)
|
|
66
|
+
"B_FEM": "fr_fem", # fraction of the travel party that is female
|
|
67
|
+
"B_HINC": "hinc20k", # household income above $20k
|
|
68
|
+
"B_ST410": "st410", # stay of 4-10 days (< 3 days = base)
|
|
69
|
+
"B_STGT10": "stgt10", # stay longer than 10 days
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for name, col in spec.items():
|
|
73
|
+
print(f" {name:<10s} <- {col}")
|
|
74
|
+
|
|
75
|
+
# ============================================================
|
|
76
|
+
# Step 2: Estimate and Summarize
|
|
77
|
+
# ============================================================
|
|
78
|
+
print("\n" + "=" * 60)
|
|
79
|
+
print(" Step 2: Estimate and Summarize")
|
|
80
|
+
print("=" * 60)
|
|
81
|
+
|
|
82
|
+
print("""
|
|
83
|
+
LRModel follows the same object surface as the other models
|
|
84
|
+
(fit / predict / ate / results.summary()), but the least-squares
|
|
85
|
+
solution is computed in closed form from the SVD of the design
|
|
86
|
+
matrix. There is no optimizer, so LRControl carries only covariance
|
|
87
|
+
and verbosity options; results.n_iter is 0 and results.converged is
|
|
88
|
+
True by construction.
|
|
89
|
+
""")
|
|
90
|
+
|
|
91
|
+
model = LRModel(
|
|
92
|
+
data=data,
|
|
93
|
+
dep_var="ln_exp",
|
|
94
|
+
spec=spec,
|
|
95
|
+
control=LRControl(verbose=1),
|
|
96
|
+
)
|
|
97
|
+
results = model.fit()
|
|
98
|
+
print()
|
|
99
|
+
results.summary()
|
|
100
|
+
|
|
101
|
+
print("""
|
|
102
|
+
Reading the summary:
|
|
103
|
+
- The last row, sigma, is the residual standard deviation: the
|
|
104
|
+
Gaussian MLE sqrt(SSE / N), reported as a parameter with its own
|
|
105
|
+
standard error exactly as MDCEV reports its scale (results.params
|
|
106
|
+
is [coefficients..., sigma]). Its t-statistic tests sigma = 0 and
|
|
107
|
+
carries no information.
|
|
108
|
+
- Mean log-likelihood is the Gaussian log-likelihood per observation
|
|
109
|
+
at sigma^2 = SSE / N (results.sigma2).
|
|
110
|
+
- Residual variance is the unbiased SSE / (N - K)
|
|
111
|
+
(results.residual_variance); the default se_method="hessian" with
|
|
112
|
+
df_correction=True gives the textbook OLS covariance
|
|
113
|
+
SSE / (N - K) * (X'X)^-1 for the coefficients. p-values use
|
|
114
|
+
Student's t with N - K degrees of freedom for every se_method.
|
|
115
|
+
- F(df_model, df_resid) jointly tests all non-constant slopes.
|
|
116
|
+
- R-squared is centered because the design spans a constant.
|
|
117
|
+
""")
|
|
118
|
+
|
|
119
|
+
print(" Coefficient table as a DataFrame:")
|
|
120
|
+
print(results.to_dataframe().round(4).to_string())
|
|
121
|
+
|
|
122
|
+
# ============================================================
|
|
123
|
+
# Step 3: Standard-Error Options
|
|
124
|
+
# ============================================================
|
|
125
|
+
print("\n" + "=" * 60)
|
|
126
|
+
print(" Step 3: Standard-Error Options")
|
|
127
|
+
print("=" * 60)
|
|
128
|
+
|
|
129
|
+
print("""
|
|
130
|
+
Expenditure data are typically heteroscedastic, so compare the
|
|
131
|
+
classical covariance with the White sandwich. With df_correction=True
|
|
132
|
+
(the default) the sandwich is the HC1 form; "bhhh" inverts the score
|
|
133
|
+
cross-product of the Gaussian likelihood over all parameters including
|
|
134
|
+
sigma. Estimates are identical across methods; only the standard
|
|
135
|
+
errors change.
|
|
136
|
+
""")
|
|
137
|
+
|
|
138
|
+
se_table = {}
|
|
139
|
+
for method in ["hessian", "sandwich", "bhhh"]:
|
|
140
|
+
ctrl = LRControl(se_method=method, verbose=0)
|
|
141
|
+
se_table[method] = LRModel(data, "ln_exp", spec, control=ctrl).fit().se
|
|
142
|
+
|
|
143
|
+
print(f" {'coef':<10s} {'estimate':>10s} {'se hessian':>12s} {'se sandwich':>12s} {'se bhhh':>10s}")
|
|
144
|
+
print(" " + "-" * 58)
|
|
145
|
+
for k, name in enumerate(results.param_names):
|
|
146
|
+
print(f" {name:<10s} {results.params[k]:>10.4f} {se_table['hessian'][k]:>12.4f} "
|
|
147
|
+
f"{se_table['sandwich'][k]:>12.4f} {se_table['bhhh'][k]:>10.4f}")
|
|
148
|
+
|
|
149
|
+
# ============================================================
|
|
150
|
+
# Step 4: Prediction
|
|
151
|
+
# ============================================================
|
|
152
|
+
print("\n" + "=" * 60)
|
|
153
|
+
print(" Step 4: Prediction")
|
|
154
|
+
print("=" * 60)
|
|
155
|
+
|
|
156
|
+
print("""
|
|
157
|
+
model.predict() with no argument returns the in-sample fitted values.
|
|
158
|
+
Passing a DataFrame rebuilds the design through the model's spec, so
|
|
159
|
+
new observations only need the columns the spec refers to. (Passing a
|
|
160
|
+
ready-made (N, K) array is also accepted.)
|
|
161
|
+
""")
|
|
162
|
+
|
|
163
|
+
y_hat = model.predict()
|
|
164
|
+
rmse = np.sqrt(np.mean((data["ln_exp"] - y_hat) ** 2))
|
|
165
|
+
print(f" In-sample RMSE of ln_exp: {rmse:.4f}")
|
|
166
|
+
print(f" R-squared recomputed from fitted values: "
|
|
167
|
+
f"{1 - np.sum((data['ln_exp'] - y_hat) ** 2) / np.sum((data['ln_exp'] - data['ln_exp'].mean()) ** 2):.6f}")
|
|
168
|
+
|
|
169
|
+
new_trips = pd.DataFrame({
|
|
170
|
+
"rural": [0, 1],
|
|
171
|
+
"fr_fem": [0.5, 0.5],
|
|
172
|
+
"hinc20k": [1, 0],
|
|
173
|
+
"st410": [1, 0],
|
|
174
|
+
"stgt10": [0, 1],
|
|
175
|
+
})
|
|
176
|
+
pred_ln = model.predict(new_trips)
|
|
177
|
+
print("\n Two hypothetical trips:")
|
|
178
|
+
print(f" {'trip':<6s} {'rural':>6s} {'hinc':>5s} {'stay':>8s} {'ln_exp':>8s} {'exp(ln_exp)':>12s}")
|
|
179
|
+
for i in range(len(new_trips)):
|
|
180
|
+
stay = "4-10 d" if new_trips.st410[i] else (">10 d" if new_trips.stgt10[i] else "<3 d")
|
|
181
|
+
print(f" {i + 1:<6d} {new_trips.rural[i]:>6d} {new_trips.hinc20k[i]:>5d} {stay:>8s} "
|
|
182
|
+
f"{pred_ln[i]:>8.3f} {np.exp(pred_ln[i]):>12,.0f}")
|
|
183
|
+
|
|
184
|
+
print("""
|
|
185
|
+
Note: exp(predicted ln_exp) is the conditional median-type prediction
|
|
186
|
+
of expenditure, not its conditional mean (Jensen's inequality).
|
|
187
|
+
""")
|
|
188
|
+
|
|
189
|
+
# ============================================================
|
|
190
|
+
# Step 5: Average Treatment Effects (scenarios= API)
|
|
191
|
+
# ============================================================
|
|
192
|
+
print("\n" + "=" * 60)
|
|
193
|
+
print(" Step 5: Average Treatment Effects (scenarios= API)")
|
|
194
|
+
print("=" * 60)
|
|
195
|
+
|
|
196
|
+
print("""
|
|
197
|
+
model.ate(scenarios=...) uses the scenario grammar shared with the other
|
|
198
|
+
models: each scenario overrides data columns with a scalar or with
|
|
199
|
+
another column, the design is rebuilt through the spec, and the mean
|
|
200
|
+
prediction is reported per scenario. For a linear model the effect in
|
|
201
|
+
outcome units of switching a dummy from 0 to 1 is exactly its
|
|
202
|
+
coefficient, which makes this a good check of the machinery.
|
|
203
|
+
""")
|
|
204
|
+
|
|
205
|
+
ate = model.ate(scenarios={
|
|
206
|
+
"urban": {"rural": 0},
|
|
207
|
+
"rural": {"rural": 1},
|
|
208
|
+
"long_stay_hinc": {"st410": 0, "stgt10": 1, "hinc20k": 1},
|
|
209
|
+
})
|
|
210
|
+
ate.summary()
|
|
211
|
+
|
|
212
|
+
effect_units = ate.comparison("urban", "rural")
|
|
213
|
+
print(f"\n ATE rural vs urban, in ln_exp units: {effect_units:+.4f}")
|
|
214
|
+
print(f" B_RURAL coefficient: {results.params[1]:+.4f}")
|
|
215
|
+
print(f" Implied expenditure ratio exp(B): {np.exp(results.params[1]):.3f}")
|
|
216
|
+
|
|
217
|
+
print("""
|
|
218
|
+
comparison() reports the effect in outcome units (here ln_exp).
|
|
219
|
+
percent=True gives the percentage change of the *predicted mean of
|
|
220
|
+
ln_exp*, the share-model convention; for a log outcome that is not the
|
|
221
|
+
percentage change in expenditure. Use exp(effect) - 1 for that, as
|
|
222
|
+
shown above.
|
|
223
|
+
""")
|
|
224
|
+
|
|
225
|
+
print(" Scenario table:")
|
|
226
|
+
print(ate.to_dataframe().round(4).to_string())
|
|
227
|
+
|
|
228
|
+
# ATEs from externally supplied estimates (e.g. GAUSS output) without
|
|
229
|
+
# re-fitting: pass the reported vector [coefficients..., sigma] exactly as
|
|
230
|
+
# printed by summary(); same scenario API, same numbers.
|
|
231
|
+
external = lr_ate_from_params(
|
|
232
|
+
results.params,
|
|
233
|
+
param_names=results.param_names,
|
|
234
|
+
data=data, spec=spec, dep_var="ln_exp",
|
|
235
|
+
scenarios={"urban": {"rural": 0}, "rural": {"rural": 1}},
|
|
236
|
+
)
|
|
237
|
+
print(f"\n lr_ate_from_params reproduces the fitted ATE: "
|
|
238
|
+
f"{external.comparison('urban', 'rural'):+.4f}")
|