├── larch
├── scikit.py
├── math
│ └── __init__.py
├── log
│ ├── __init__.py
│ ├── light.py
│ └── normal.py
├── data_services
│ ├── h5
│ │ ├── token_manip.py
│ │ ├── __init__.py
│ │ ├── h5pod
│ │ │ ├── __init__.py
│ │ │ ├── id0a.py
│ │ │ └── idga.py
│ │ └── h5util.py
│ ├── dbf
│ │ └── __init__.py
│ ├── exceptions.py
│ ├── __init__.py
│ └── sqlite
│ │ └── __init__.py
├── general_precision.pxi
├── data_warehouse
│ ├── MTCwork.h5d
│ ├── arc.csv.gz
│ ├── MTCwork.csv.gz
│ ├── MTCwork.sqlite
│ ├── US-STATES.dbf
│ ├── optima.csv.gz
│ ├── swissmetro.csv.gz
│ ├── swissmetro.h5.gz
│ ├── exampville_taz.zip
│ ├── exampville_skims.omx
│ ├── exampville_taz_42.zip
│ ├── itinerary_data.csv.gz
│ ├── exampville_skims_42.omx
│ ├── exampville_tours.csv.gz
│ ├── exampville_persons.csv.gz
│ ├── exampville_tours_42.csv.gz
│ ├── exampville_employment.csv.gz
│ ├── exampville_households.csv.gz
│ ├── exampville_persons_42.csv.gz
│ ├── exampville_demographics.csv.gz
│ ├── exampville_employment_42.csv.gz
│ ├── exampville_households_42.csv.gz
│ ├── exampville_demographics_42.csv.gz
│ └── __init__.py
├── doc
│ ├── _static
│ │ ├── trophy-64.png
│ │ ├── larch_favicon.ico
│ │ ├── larch_favicon.png
│ │ └── elm_doc_style.css
│ ├── math
│ │ └── agg-choice-variance.png
│ ├── data.rst
│ ├── machine-learning
│ │ ├── index.rst
│ │ └── prelearn.rst
│ ├── math.rst
│ ├── example
│ │ ├── exampville.rst
│ │ ├── swissmetro.rst
│ │ ├── larch_nbval_sanitize.cfg
│ │ ├── examples.rst
│ │ ├── mtc.rst
│ │ ├── itinerary.rst
│ │ ├── 020_mtc.rst
│ │ ├── 021_mtc.rst
│ │ ├── 019_mtc.rst
│ │ ├── 024_mtc.rst
│ │ ├── 023_mtc.rst
│ │ ├── 025_mtc.rst
│ │ ├── 003_mtc.rst
│ │ ├── 009_mtc.rst
│ │ └── 002_mtc.rst
│ ├── latentclass.rst
│ ├── data-pods.rst
│ ├── linear-functions.rst
│ ├── model.rst
│ ├── compiling.rst
│ ├── dataservice.rst
│ └── index.rst
├── util
│ ├── activitysim
│ │ ├── __init__.py
│ │ ├── data_maker.py
│ │ ├── auto_ownership.py
│ │ └── tour_mode_choice.py
│ ├── uid.py
│ ├── colors.py
│ ├── bitmasking.py
│ ├── aster.py
│ ├── counting.py
│ ├── timesize.py
│ ├── cache_requests.py
│ ├── rate_limiter.py
│ ├── naming.py
│ ├── signal_dict.py
│ ├── __init__.py
│ └── png.py
├── torch.py
├── conftest.py
├── model
│ ├── __init__.py
│ ├── persist_flags.pyx
│ ├── parameter_frame.pxd
│ ├── persist_flags.pxd
│ ├── mnl.pxd
│ ├── abstract_model.pxd
│ ├── tree_struct.pxd
│ ├── fastmath.pxi
│ ├── linear.pxd
│ ├── tree_struct.pyx
│ ├── controller.pxd
│ ├── possible_overspec.py
│ └── cast.h
├── dataset
│ ├── dim_names.py
│ └── patch.py
├── general_precision.pxd
├── general_precision.pyx
├── exceptions.py
├── warning.py
├── numba
│ ├── __init__.py
│ └── fast_mapping.py
├── workspace.py
├── linalg
│ └── __init__.py
├── scoring.py
└── __init__.py
├── tests
├── test_examples.py
├── __init__.py
├── stored_dataframes
│ ├── like_ratio_test.pkl.gz
│ ├── parameter_summary_test.pkl.gz
│ ├── joint_parameter_summary.pkl.gz
│ └── __init__.py
├── test_nl_1
│ ├── test_compute_covariance.csv.gz
│ ├── test_compute_utility_array.csv.gz
│ └── test_compute_probability_array.csv.gz
├── test_constraints
│ ├── test_parameter_summary.csv.gz
│ └── test_constraint_parameter_summary_ratios.csv.gz
├── test_dataset.py
├── test_histograms.py
├── test_util_manipulation.py
├── test_doctor.py
├── test_nl_1.py
├── test_data_arrays.py
├── test_qmnl.py
└── test_model_group.py
├── .gitattributes
├── book
├── requirements.txt
├── api
│ ├── ~data.md
│ ├── ~models.md
│ ├── dataframes.rst
│ ├── nesting.rst
│ ├── modelgroup.rst
│ ├── datatree.rst
│ ├── dataset.rst
│ ├── linear.rst
│ └── model.rst
├── bibliography.md
├── img
│ ├── larch-logo.png
│ └── larch_favicon.png
├── example
│ ├── legacy
│ │ ├── mtc.md
│ │ ├── swissmetro.md
│ │ ├── itinerary.md
│ │ ├── 020_mtc.rst
│ │ ├── 021_mtc.rst
│ │ ├── 019_mtc.rst
│ │ ├── 024_mtc.rst
│ │ ├── 023_mtc.rst
│ │ ├── 025_mtc.rst
│ │ ├── 003_mtc.rst
│ │ ├── 009_mtc.rst
│ │ └── 002_mtc.rst
│ ├── legacy.md
│ ├── exampville.rst
│ ├── swissmetro.md
│ ├── larch_nbval_sanitize.cfg
│ ├── mtc.rst
│ └── itinerary.rst
├── _build.sh
├── user-guide
│ ├── larch-demo-xlsx.jpg
│ └── example-data
│ │ ├── tiny_points.csv
│ │ ├── tiny_idco.csv
│ │ └── tiny_idca.csv
├── _static
│ ├── larch-book.css
│ └── switcher.json
├── references.bib
├── _scripts
│ ├── developer_doc_title.py
│ └── hide_test_cells.py
├── _toc.yml
└── intro.md
├── bump_patch.sh
├── img
├── larch.icns
├── trophy-64.png
├── larch_favicon.ico
└── larch_favicon.png
├── .idea
├── encodings.xml
├── codeStyles
│ └── codeStyleConfig.xml
├── other.xml
├── modules.xml
├── misc.xml
├── runConfigurations
│ ├── Docs.xml
│ ├── Sphinx_Test.xml
│ └── PyTest.xml
├── Larch.iml
└── inspectionProfiles
│ └── Project_Default.xml
├── conda-recipe
├── conda_build_config.yaml
├── conda_upload.sh
└── meta.yaml
├── bumpversion.cfg
├── .pre-commit-config.yaml
├── pyproject.toml
├── strip_metadata.sh
├── tools
├── get_version.py
├── appveyor
│ └── run_with_env.cmd
└── gitversion.py
├── environments
└── development.yml
├── .travis.yml
├── .gitignore
├── README.rst
└── .github
└── workflows
└── dev-docs.yml
/larch/scikit.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/larch/math/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/test_examples.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | sqlite/* linguist-vendored
--------------------------------------------------------------------------------
/larch/log/__init__.py:
--------------------------------------------------------------------------------
1 |
2 | logger_name = "Larch"
3 |
--------------------------------------------------------------------------------
/larch/data_services/h5/token_manip.py:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/book/requirements.txt:
--------------------------------------------------------------------------------
1 | jupyter-book
2 | matplotlib
3 | numpy
4 |
--------------------------------------------------------------------------------
/bump_patch.sh:
--------------------------------------------------------------------------------
1 | bumpversion --config-file bumpversion.cfg patch
2 |
--------------------------------------------------------------------------------
/larch/data_services/dbf/__init__.py:
--------------------------------------------------------------------------------
1 | from .dbf_reader import DBF
2 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 | from .test_qmnl import test_saturate_cpu
2 |
3 |
--------------------------------------------------------------------------------
/book/api/~data.md:
--------------------------------------------------------------------------------
1 | # Data
2 |
3 | ```{tableofcontents}
4 | ```
5 |
6 |
--------------------------------------------------------------------------------
/book/api/~models.md:
--------------------------------------------------------------------------------
1 | # Models
2 |
3 | ```{tableofcontents}
4 | ```
5 |
6 |
--------------------------------------------------------------------------------
/img/larch.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/img/larch.icns
--------------------------------------------------------------------------------
/larch/general_precision.pxi:
--------------------------------------------------------------------------------
1 |
2 |
3 | DEF DOUBLE_PRECISION = True
4 |
5 |
--------------------------------------------------------------------------------
/img/trophy-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/img/trophy-64.png
--------------------------------------------------------------------------------
/book/bibliography.md:
--------------------------------------------------------------------------------
1 | # References
2 |
3 | ```{bibliography}
4 | :style: plain
5 | ```
6 |
--------------------------------------------------------------------------------
/book/img/larch-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/book/img/larch-logo.png
--------------------------------------------------------------------------------
/img/larch_favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/img/larch_favicon.ico
--------------------------------------------------------------------------------
/img/larch_favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/img/larch_favicon.png
--------------------------------------------------------------------------------
/book/example/legacy/mtc.md:
--------------------------------------------------------------------------------
1 | # MTC Mode Choice Examples
2 |
3 | ```{tableofcontents}
4 | ```
5 |
--------------------------------------------------------------------------------
/book/_build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | conda info
3 | python _scripts/hide_test_cells.py
4 | jb build .
5 |
--------------------------------------------------------------------------------
/book/img/larch_favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/book/img/larch_favicon.png
--------------------------------------------------------------------------------
/larch/data_warehouse/MTCwork.h5d:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/MTCwork.h5d
--------------------------------------------------------------------------------
/larch/data_warehouse/arc.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/arc.csv.gz
--------------------------------------------------------------------------------
/larch/doc/_static/trophy-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/doc/_static/trophy-64.png
--------------------------------------------------------------------------------
/book/user-guide/larch-demo-xlsx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/book/user-guide/larch-demo-xlsx.jpg
--------------------------------------------------------------------------------
/larch/data_warehouse/MTCwork.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/MTCwork.csv.gz
--------------------------------------------------------------------------------
/larch/data_warehouse/MTCwork.sqlite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/MTCwork.sqlite
--------------------------------------------------------------------------------
/larch/data_warehouse/US-STATES.dbf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/US-STATES.dbf
--------------------------------------------------------------------------------
/larch/data_warehouse/optima.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/optima.csv.gz
--------------------------------------------------------------------------------
/larch/doc/_static/larch_favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/doc/_static/larch_favicon.ico
--------------------------------------------------------------------------------
/larch/doc/_static/larch_favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/doc/_static/larch_favicon.png
--------------------------------------------------------------------------------
/larch/util/activitysim/__init__.py:
--------------------------------------------------------------------------------
1 | from .data_maker import *
2 | from .general import *
3 | from .cdap import *
4 |
--------------------------------------------------------------------------------
/larch/util/uid.py:
--------------------------------------------------------------------------------
1 | _uidn = 0
2 |
3 | def uid():
4 | global _uidn
5 | _uidn += 1
6 | return "rx{}".format(_uidn)
7 |
--------------------------------------------------------------------------------
/larch/data_warehouse/swissmetro.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/swissmetro.csv.gz
--------------------------------------------------------------------------------
/larch/data_warehouse/swissmetro.h5.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/swissmetro.h5.gz
--------------------------------------------------------------------------------
/larch/doc/math/agg-choice-variance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/doc/math/agg-choice-variance.png
--------------------------------------------------------------------------------
/larch/data_warehouse/exampville_taz.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/exampville_taz.zip
--------------------------------------------------------------------------------
/larch/data_warehouse/exampville_skims.omx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/exampville_skims.omx
--------------------------------------------------------------------------------
/larch/data_warehouse/exampville_taz_42.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/exampville_taz_42.zip
--------------------------------------------------------------------------------
/larch/data_warehouse/itinerary_data.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/itinerary_data.csv.gz
--------------------------------------------------------------------------------
/larch/data_warehouse/exampville_skims_42.omx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/exampville_skims_42.omx
--------------------------------------------------------------------------------
/larch/data_warehouse/exampville_tours.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/exampville_tours.csv.gz
--------------------------------------------------------------------------------
/larch/data_services/exceptions.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | class NoKnownShape(ValueError):
4 | pass
5 |
6 | class NoKnownType(ValueError):
7 | pass
8 |
--------------------------------------------------------------------------------
/larch/data_warehouse/exampville_persons.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/exampville_persons.csv.gz
--------------------------------------------------------------------------------
/larch/data_warehouse/exampville_tours_42.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/exampville_tours_42.csv.gz
--------------------------------------------------------------------------------
/tests/stored_dataframes/like_ratio_test.pkl.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/tests/stored_dataframes/like_ratio_test.pkl.gz
--------------------------------------------------------------------------------
/tests/test_nl_1/test_compute_covariance.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/tests/test_nl_1/test_compute_covariance.csv.gz
--------------------------------------------------------------------------------
/larch/data_warehouse/exampville_employment.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/exampville_employment.csv.gz
--------------------------------------------------------------------------------
/larch/data_warehouse/exampville_households.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/exampville_households.csv.gz
--------------------------------------------------------------------------------
/larch/data_warehouse/exampville_persons_42.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/exampville_persons_42.csv.gz
--------------------------------------------------------------------------------
/tests/test_nl_1/test_compute_utility_array.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/tests/test_nl_1/test_compute_utility_array.csv.gz
--------------------------------------------------------------------------------
/larch/data_warehouse/exampville_demographics.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/exampville_demographics.csv.gz
--------------------------------------------------------------------------------
/larch/data_warehouse/exampville_employment_42.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/exampville_employment_42.csv.gz
--------------------------------------------------------------------------------
/larch/data_warehouse/exampville_households_42.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/exampville_households_42.csv.gz
--------------------------------------------------------------------------------
/tests/stored_dataframes/parameter_summary_test.pkl.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/tests/stored_dataframes/parameter_summary_test.pkl.gz
--------------------------------------------------------------------------------
/tests/test_constraints/test_parameter_summary.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/tests/test_constraints/test_parameter_summary.csv.gz
--------------------------------------------------------------------------------
/tests/test_nl_1/test_compute_probability_array.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/tests/test_nl_1/test_compute_probability_array.csv.gz
--------------------------------------------------------------------------------
/larch/data_warehouse/exampville_demographics_42.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/larch/data_warehouse/exampville_demographics_42.csv.gz
--------------------------------------------------------------------------------
/tests/stored_dataframes/joint_parameter_summary.pkl.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/tests/stored_dataframes/joint_parameter_summary.pkl.gz
--------------------------------------------------------------------------------
/book/_static/larch-book.css:
--------------------------------------------------------------------------------
1 | #site-navigation h1.site-logo {
2 | font-size: 0.7em;
3 | color: #4D4D4D;
4 | font-weight: 700;
5 | margin-top: 0;
6 | }
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/larch/torch.py:
--------------------------------------------------------------------------------
1 |
2 | try:
3 | from _larch_torch import *
4 | except ImportError:
5 | import warnings
6 | warnings.warn("larch.torch is not installed")
7 |
8 |
--------------------------------------------------------------------------------
/larch/conftest.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import pytest
3 |
4 |
5 | @pytest.fixture(autouse=True)
6 | def add_np(doctest_namespace):
7 | doctest_namespace["np"] = np
8 |
--------------------------------------------------------------------------------
/larch/doc/data.rst:
--------------------------------------------------------------------------------
1 |
2 | ======
3 | Data
4 | ======
5 |
6 | .. toctree::
7 | :maxdepth: 4
8 |
9 | data-fundamentals
10 | data-frames
11 | dataservice
12 |
13 |
--------------------------------------------------------------------------------
/tests/test_constraints/test_constraint_parameter_summary_ratios.csv.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpn--/larch/HEAD/tests/test_constraints/test_constraint_parameter_summary_ratios.csv.gz
--------------------------------------------------------------------------------
/book/_static/switcher.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "v5.7.2 (latest)",
4 | "version": "5.7.2"
5 | },
6 | {
7 | "version": "5.4.1"
8 | }
9 | ]
10 |
--------------------------------------------------------------------------------
/conda-recipe/conda_build_config.yaml:
--------------------------------------------------------------------------------
1 | python:
2 | - 3.9 # [not win]
3 | - 3.10
4 |
5 | numpy:
6 | - 1.21.4
7 |
8 | #CONDA_BUILD_SYSROOT:
9 | # - /opt/MacOSX10.9.sdk # [osx]
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/larch/doc/machine-learning/index.rst:
--------------------------------------------------------------------------------
1 |
2 |
3 | ================
4 | Machine Learning
5 | ================
6 |
7 | .. toctree::
8 | :maxdepth: 4
9 |
10 | machine_learning
11 | prelearn
12 |
13 |
--------------------------------------------------------------------------------
/.idea/other.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/book/user-guide/example-data/tiny_points.csv:
--------------------------------------------------------------------------------
1 | caseid,orig_x,orig_y,dest_x,dest_y
2 | 1,946907,925689,884665,494832
3 | 2,952193,945628,892053,478115
4 | 3,889200,491106,959298,919813
5 | 4,877274,476391,947965,925746
6 |
--------------------------------------------------------------------------------
/larch/model/__init__.py:
--------------------------------------------------------------------------------
1 |
2 | from .model import Model
3 | from .systematic_alternatives import SystematicAlternatives
4 | from .linear import DictOfLinearFunction_C, LinearFunction_C
5 | from .persist_flags import *
6 |
7 |
--------------------------------------------------------------------------------
/book/user-guide/example-data/tiny_idco.csv:
--------------------------------------------------------------------------------
1 | caseid,Income,CarTime,CarCost,BusTime,BusCost,WalkTime,Chosen
2 | 1,30000,30,150,40,100,20,Car
3 | 2,30000,25,125,35,100,0,Bus
4 | 3,40000,40,125,50,75,30,Walk
5 | 4,50000,15,225,20,150,10,Walk
6 |
--------------------------------------------------------------------------------
/larch/dataset/dim_names.py:
--------------------------------------------------------------------------------
1 | # Dense
2 | CASEID = '_caseid_'
3 | ALTID = '_altid_'
4 |
5 | # Sparse
6 | CASEALT = '_casealt_'
7 | ALTIDX = '_alt_idx_'
8 | CASEPTR = '_caseptr_'
9 | GROUPID = '_groupid_'
10 | INGROUP = '_ingroup_'
11 |
--------------------------------------------------------------------------------
/larch/general_precision.pxd:
--------------------------------------------------------------------------------
1 | # cython: language_level=3, embedsignature=True
2 |
3 |
4 | include "general_precision.pxi"
5 |
6 | IF DOUBLE_PRECISION:
7 | ctypedef double l4_float_t
8 | ELSE:
9 | ctypedef float l4_float_t
10 |
11 |
12 |
--------------------------------------------------------------------------------
/larch/data_services/__init__.py:
--------------------------------------------------------------------------------
1 |
2 | _reserved_names_ = {'_VAULT_', '_BUNCHES_'}
3 |
4 | # import numpy
5 | from .pod import Pod
6 | from .podlist import Pods
7 | from .service import DataService
8 | from .h5 import *
9 | from .general import SystematicAlternatives
--------------------------------------------------------------------------------
/larch/general_precision.pyx:
--------------------------------------------------------------------------------
1 | # cython: language_level=3, embedsignature=True
2 |
3 | import numpy
4 |
5 | include "general_precision.pxi"
6 |
7 | IF DOUBLE_PRECISION:
8 | l4_float_dtype = numpy.float64
9 | ELSE:
10 | l4_float_dtype = numpy.float64
11 |
12 |
--------------------------------------------------------------------------------
/larch/doc/machine-learning/prelearn.rst:
--------------------------------------------------------------------------------
1 |
2 |
3 | .. currentmodule:: larch.prelearning
4 |
5 | ===============================
6 | Using Scikit-Learn within Larch
7 | ===============================
8 |
9 | .. autoclass:: Prelearner
10 |
11 | .. autoclass:: XGBoostPrelearner
12 |
13 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/bumpversion.cfg:
--------------------------------------------------------------------------------
1 | [bumpversion]
2 | current_version = 5.7.2
3 | commit = True
4 | tag = True
5 |
6 | [bumpversion:file:larch/__init__.py]
7 |
8 | [bumpversion:file:conda-recipe/meta.yaml]
9 |
10 | [bumpversion:file:book/_config.yml]
11 |
12 | [bumpversion:file:book/_static/switcher.json]
13 |
--------------------------------------------------------------------------------
/larch/exceptions.py:
--------------------------------------------------------------------------------
1 |
2 | # Errors
3 |
4 | class MissingDataError(ValueError):
5 | pass
6 |
7 | class DuplicateColumnNames(ValueError):
8 | pass
9 |
10 | class BHHHSimpleStepFailure(RuntimeError):
11 | pass
12 |
13 | # Warnings
14 |
15 | class ParameterNotInModelWarning(UserWarning):
16 | pass
17 |
18 |
--------------------------------------------------------------------------------
/book/example/legacy.md:
--------------------------------------------------------------------------------
1 | (deprecated-examples)=
2 | # Legacy Example Models
3 |
4 | Future development of Larch will be on version that sits on numba, xarray, and sharrow,
5 | as that code is faster and more extensible. Examples using the legacy `DataFrames` code
6 | are provided here.
7 |
8 | ```{tableofcontents}
9 | ```
10 |
--------------------------------------------------------------------------------
/book/api/dataframes.rst:
--------------------------------------------------------------------------------
1 | ==========
2 | DataFrames
3 | ==========
4 |
5 | The :ref:`DataFrames` interface for larch remains available for use, but future
6 | development on this package will be on the :ref:`Dataset`/:ref:`DataTree` interface.
7 |
8 | .. autosummary::
9 | :toctree: generated/
10 |
11 | larch.DataFrames
12 |
--------------------------------------------------------------------------------
/book/user-guide/example-data/tiny_idca.csv:
--------------------------------------------------------------------------------
1 | caseid,altid,Income,Time,Cost,Chosen
2 | 1,Car,30000,30,150,1
3 | 1,Bus,30000,40,100,0
4 | 1,Walk,30000,20,0,0
5 | 2,Car,30000,25,125,0
6 | 2,Bus,30000,35,100,1
7 | 3,Car,40000,40,125,0
8 | 3,Bus,40000,50,75,0
9 | 3,Walk,40000,30,0,1
10 | 4,Car,50000,15,225,0
11 | 4,Bus,50000,20,150,0
12 | 4,Walk,50000,10,0,1
--------------------------------------------------------------------------------
/conda-recipe/conda_upload.sh:
--------------------------------------------------------------------------------
1 | # Only need to change these two variables
2 | PKG_NAME=larch
3 | USER=jpn
4 |
5 | OS=$TRAVIS_OS_NAME-64
6 | mkdir ~/conda-bld
7 | conda config --set anaconda_upload no
8 | export CONDA_BLD_PATH=~/conda-bld
9 | conda build conda-recipe
10 | anaconda -t $CONDA_UPLOAD_TOKEN upload -u $USER $CONDA_BLD_PATH/$OS/$PKG_NAME-*.tar.bz2 --force
11 |
--------------------------------------------------------------------------------
/larch/log/light.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | LIGHT_CONSOLE_LOG_FORMAT = '%(message)s'
4 |
5 | from .normal import *
6 |
7 | # Remove timestamps from console stream
8 |
9 | for entry in logger.handlers:
10 | if (isinstance(entry, logging.StreamHandler)) and (entry.formatter._fmt == CONSOLE_LOG_FORMAT):
11 | entry.setFormatter(logging.Formatter(LIGHT_CONSOLE_LOG_FORMAT))
12 |
13 |
--------------------------------------------------------------------------------
/larch/doc/math.rst:
--------------------------------------------------------------------------------
1 |
2 | ====================================
3 | Mathematics of Logit Choice Modeling
4 | ====================================
5 |
6 | This documentation will eventually provide instruction on some of the more interesting topics on the underlying
7 | mathematics of logit models.
8 |
9 | .. toctree::
10 | :maxdepth: 2
11 |
12 | math/aggregate-choice
13 |
--------------------------------------------------------------------------------
/book/references.bib:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 | @book{koppelman2006self,
4 | title={A self instructing course in mode choice modeling: multinomial and nested logit models},
5 | author={Koppelman, Frank S and Bhat, Chandra},
6 | year={2006},
7 | publisher={FTA US Department of Transportation},
8 | url={https://www.caee.utexas.edu/prof/bhat/COURSES/LM_Draft_060131Final-060630.pdf}
9 | }
10 |
--------------------------------------------------------------------------------
/book/example/exampville.rst:
--------------------------------------------------------------------------------
1 |
2 | ======================
3 | Exampville Simulator
4 | ======================
5 |
6 | Exampville is a simulation tool provided with Larch that can quickly simulate the
7 | kind of data that a transportation planner might have available when building
8 | a travel model.
9 |
10 | **Exampville Models**
11 |
12 | .. toctree::
13 | :glob:
14 |
15 | 2*
16 |
17 |
--------------------------------------------------------------------------------
/larch/doc/example/exampville.rst:
--------------------------------------------------------------------------------
1 |
2 | ======================
3 | Exampville Simulator
4 | ======================
5 |
6 | Exampville is a simulation tool provided with Larch that can quickly simulate the
7 | kind of data that a transportation planner might have available when building
8 | a travel model.
9 |
10 | **Exampville Models**
11 |
12 | .. toctree::
13 | :glob:
14 |
15 | 2*
16 |
17 |
--------------------------------------------------------------------------------
/larch/warning.py:
--------------------------------------------------------------------------------
1 |
2 | import warnings
3 | import contextlib
4 |
5 | @contextlib.contextmanager
6 | def ignore_warnings():
7 | with warnings.catch_warnings():
8 | warnings.simplefilter("ignore")
9 | yield
10 |
11 |
12 | class NoDefaultValueWarning(Warning):
13 | pass
14 |
15 | def no_default_value(message):
16 | #warnings.warn(message, NoDefaultValueWarning, stacklevel=2)
17 | pass
18 |
19 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 |
3 | - repo: https://github.com/pre-commit/pre-commit-hooks
4 | rev: v4.1.0
5 | hooks:
6 | - id: check-yaml
7 | exclude: conda-recipe/meta.yaml
8 | - id: end-of-file-fixer
9 | exclude: .*\.ipynb|bumpversion.cfg
10 | - id: trailing-whitespace
11 |
12 | - repo: https://github.com/kynan/nbstripout
13 | rev: 0.5.0
14 | hooks:
15 | - id: nbstripout
16 |
--------------------------------------------------------------------------------
/larch/model/persist_flags.pyx:
--------------------------------------------------------------------------------
1 | # cython: language_level=3, embedsignature=True
2 |
3 |
4 | __all__ = (
5 | "PERSIST_UTILITY",
6 | "PERSIST_EXP_UTILITY",
7 | "PERSIST_PROBABILITY",
8 | "PERSIST_COND_LOG_PROB",
9 | "PERSIST_LOGLIKE_CASEWISE",
10 | "PERSIST_D_UTILITY",
11 | "PERSIST_D_PROBABILITY",
12 | "PERSIST_D_LOGLIKE_CASEWISE",
13 | "PERSIST_BHHH",
14 | "PERSIST_PARTS",
15 | "PERSIST_ALL",
16 | )
17 |
18 |
--------------------------------------------------------------------------------
/book/api/nesting.rst:
--------------------------------------------------------------------------------
1 | ===========
2 | NestingTree
3 | ===========
4 |
5 | .. currentmodule:: larch.model.tree
6 |
7 |
8 | .. autosummary::
9 | :toctree: generated/
10 |
11 | NestingTree
12 |
13 | Methods
14 | =======
15 |
16 | .. autosummary::
17 | :toctree: generated/
18 |
19 | NestingTree.new_node
20 | NestingTree.add_node
21 | NestingTree.remove_node
22 | NestingTree.add_edge
23 | NestingTree.remove_edge
24 |
--------------------------------------------------------------------------------
/tests/test_dataset.py:
--------------------------------------------------------------------------------
1 | import larch.numba as lx
2 | import pytest
3 | from pytest import approx
4 |
5 | def test_dataset():
6 | d = lx.examples.MTC(format='dataset')
7 |
8 | assert d.dc.CASEID == 'caseid'
9 | assert d.dc.ALTID == 'altid'
10 |
11 | assert d.dc.chose.dc.CASEID == 'caseid'
12 | assert d.dc.chose.dc.ALTID == 'altid'
13 |
14 | assert d.dc['chose'].dc.CASEID == 'caseid'
15 | assert d.dc['chose'].dc.ALTID == 'altid'
16 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/book/example/swissmetro.md:
--------------------------------------------------------------------------------
1 |
2 | # Swissmetro Examples
3 |
4 | **Example Data**
5 |
6 | The Swissmetro sample dataset is the same data used in the examples for [Biogeme](http://biogeme.epfl.ch/).
7 | Note that Larch and Biogeme have been developed for different applicationsa and have different capabilities.
8 | It is not possible to reproduce using Larch every example model shown for Biogeme.
9 |
10 | **Example Models**
11 |
12 | ```{tableofcontents}
13 | ```
14 |
--------------------------------------------------------------------------------
/book/example/legacy/swissmetro.md:
--------------------------------------------------------------------------------
1 |
2 | # Swissmetro Examples
3 |
4 | **Example Data**
5 |
6 | The Swissmetro sample dataset is the same data used in the examples for [Biogeme](http://biogeme.epfl.ch/).
7 | Note that Larch and Biogeme have been developed for different applicationsa and have different capabilities.
8 | It is not possible to reproduce using Larch every example model shown for Biogeme.
9 |
10 | **Example Models**
11 |
12 | ```{tableofcontents}
13 | ```
14 |
--------------------------------------------------------------------------------
/larch/model/parameter_frame.pxd:
--------------------------------------------------------------------------------
1 | # cython: language_level=3, embedsignature=True
2 |
3 | from ..general_precision cimport *
4 |
5 | cdef class ParameterFrame:
6 |
7 | cdef public:
8 | object _frame # pandas.DataFrame
9 | object _matrixes # Dict[ndarray]
10 |
11 | cdef:
12 | unicode _title
13 | bint _mangled
14 | object _prior_frame_values # pandas.Series
15 | object _display_order
16 | object _display_order_tail
17 |
18 |
--------------------------------------------------------------------------------
/larch/model/persist_flags.pxd:
--------------------------------------------------------------------------------
1 | # cython: language_level=3, embedsignature=True
2 |
3 | cpdef enum:
4 | PERSIST_UTILITY = 0x1
5 | PERSIST_EXP_UTILITY = 0x2
6 | PERSIST_PROBABILITY = 0x4
7 | PERSIST_LOGLIKE_CASEWISE = 0x8
8 |
9 | PERSIST_D_UTILITY = 0x10
10 | PERSIST_COND_LOG_PROB = 0x20
11 | PERSIST_D_PROBABILITY = 0x40
12 | PERSIST_D_LOGLIKE_CASEWISE = 0x80
13 |
14 | PERSIST_BHHH = 0x100
15 | PERSIST_PARTS = 0x200
16 | PERSIST_QUANTITY = 0x400
17 |
18 | PERSIST_ALL = 0xFFFF
19 |
--------------------------------------------------------------------------------
/larch/model/mnl.pxd:
--------------------------------------------------------------------------------
1 | # cython: language_level=3, embedsignature=True
2 |
3 | from ..general_precision cimport *
4 |
5 | cdef l4_float_t _mnl_log_likelihood_from_probability(
6 | int n_alts,
7 | l4_float_t* probability, # input [n_alts]
8 | l4_float_t* choice, # input [n_alts]
9 | ) nogil
10 |
11 | cdef l4_float_t _mnl_log_likelihood_from_probability_stride(
12 | int n_alts,
13 | l4_float_t[:] probability, # input [n_alts]
14 | l4_float_t[:] choice, # input [n_alts]
15 | ) nogil
16 |
--------------------------------------------------------------------------------
/tests/stored_dataframes/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | def stable_df(df, filename):
4 | import os, pandas
5 | from pandas.io.formats.style import Styler
6 | if isinstance(df, Styler):
7 | df = df.data
8 | f = os.path.join(os.path.dirname(__file__), f'{filename}.pkl.gz')
9 | if os.path.exists(f):
10 | comparison = pandas.read_pickle(f)
11 | pandas.testing.assert_frame_equal(df, comparison)
12 | else:
13 | try:
14 | df.to_pickle(f)
15 | except FileNotFoundError:
16 | raise FileNotFoundError(f)
17 |
18 |
--------------------------------------------------------------------------------
/larch/data_services/sqlite/__init__.py:
--------------------------------------------------------------------------------
1 |
2 | import apsw
3 | import numpy
4 | import pandas
5 |
6 | try:
7 | from .sqlite_arrays import _sqlite_array_2d_float64
8 | except ImportError:
9 | _sqlite_array_2d_float64 = None
10 |
11 | class Connection(apsw.Connection):
12 |
13 | def read_data(self, query, dtype=numpy.float64, index_col=None):
14 | if dtype==numpy.float64:
15 | return _sqlite_array_2d_float64(self.sqlite3pointer(), query, index_col=index_col)
16 | raise TypeError("cannot read in {}".format(dtype))
17 |
--------------------------------------------------------------------------------
/larch/doc/example/swissmetro.rst:
--------------------------------------------------------------------------------
1 |
2 | ======================
3 | Swissmetro Examples
4 | ======================
5 |
6 | **Example Data**
7 |
8 | The Swissmetro sample dataset is the same data used in the examples for `Biogeme `_.
9 | Note that Larch and Biogeme have been developed for different applicationsa and have different capabilities.
10 | It is not possible to reproduce using Larch every example model shown for Biogeme.
11 | **Example Models**
12 |
13 | .. toctree::
14 | :glob:
15 |
16 | 1*
17 |
18 |
--------------------------------------------------------------------------------
/larch/model/abstract_model.pxd:
--------------------------------------------------------------------------------
1 | # cython: language_level=3, embedsignature=True
2 |
3 | from ..general_precision cimport *
4 | from .parameter_frame cimport ParameterFrame
5 |
6 |
7 |
8 | cdef class AbstractChoiceModel(ParameterFrame):
9 |
10 | cdef public:
11 |
12 | object _most_recent_estimation_result
13 | object _possible_overspecification
14 |
15 | double _cached_loglike_null
16 | double _cached_loglike_nil
17 | double _cached_loglike_constants_only
18 | double _cached_loglike_best
19 |
20 | object _dashboard
21 |
22 |
--------------------------------------------------------------------------------
/book/example/larch_nbval_sanitize.cfg:
--------------------------------------------------------------------------------
1 | [regex1]
2 | regex: \d{1,2}/\d{1,2}/\d{2,4}
3 | replace: DATE-STAMP
4 |
5 | [regex2]
6 | regex: \d{2}:\d{2}:\d{2}
7 | replace: TIME-STAMP
8 |
9 | [regex3]
10 | regex: \d{2}:\d{2}:\d{2}.\d{6}
11 | replace: ELAPSED-TIME-STAMP
12 |
13 | [regex4]
14 | regex: \d{1}:\d{2}:\d{2}.\d{6}
15 | replace: ELAPSED-TIME-STAMP
16 |
17 | [regex5]
18 | regex: seconds=\d{2}, microseconds=\d{6}
19 | replace: SECONDS-MICROSECONDS
20 |
21 | [regex6]
22 | regex:
Iteration [0-9]* .*<\/h3>
23 | replace: ITERATION-HEADER
24 |
25 |
--------------------------------------------------------------------------------
/larch/doc/example/larch_nbval_sanitize.cfg:
--------------------------------------------------------------------------------
1 | [regex1]
2 | regex: \d{1,2}/\d{1,2}/\d{2,4}
3 | replace: DATE-STAMP
4 |
5 | [regex2]
6 | regex: \d{2}:\d{2}:\d{2}
7 | replace: TIME-STAMP
8 |
9 | [regex3]
10 | regex: \d{2}:\d{2}:\d{2}.\d{6}
11 | replace: ELAPSED-TIME-STAMP
12 |
13 | [regex4]
14 | regex: \d{1}:\d{2}:\d{2}.\d{6}
15 | replace: ELAPSED-TIME-STAMP
16 |
17 | [regex5]
18 | regex: seconds=\d{2}, microseconds=\d{6}
19 | replace: SECONDS-MICROSECONDS
20 |
21 | [regex6]
22 | regex: Iteration [0-9]* .*<\/h3>
23 | replace: ITERATION-HEADER
24 |
25 |
--------------------------------------------------------------------------------
/book/_scripts/developer_doc_title.py:
--------------------------------------------------------------------------------
1 | import os
2 | from pathlib import Path
3 | import sys
4 |
5 | from ruamel.yaml import YAML
6 |
7 | config_file = Path(
8 | os.path.join(
9 | os.path.dirname(__file__),
10 | "..",
11 | "_config.yml",
12 | )
13 | )
14 |
15 | if len(sys.argv) >= 2:
16 | title = sys.argv[1]
17 | else:
18 | title = "DEVELOPMENT DOCS"
19 | yaml = YAML(typ="rt") # default, if not specfied, is 'rt' (round-trip)
20 | content = yaml.load(config_file)
21 | content["title"] = title
22 | yaml.dump(content, config_file)
23 |
--------------------------------------------------------------------------------
/book/api/modelgroup.rst:
--------------------------------------------------------------------------------
1 | ===========
2 | ModelGroup
3 | ===========
4 |
5 | .. currentmodule:: larch.model.model_group
6 |
7 |
8 | .. autosummary::
9 | :toctree: generated/
10 |
11 | ModelGroup
12 |
13 |
14 | Attributes
15 | ==========
16 |
17 | .. autosummary::
18 | :toctree: generated/
19 |
20 | ModelGroup.n_cases
21 |
22 |
23 | Methods
24 | =======
25 |
26 | .. autosummary::
27 | :toctree: generated/
28 |
29 | ModelGroup.set_values
30 | ModelGroup.total_weight
31 | ModelGroup.loglike
32 | ModelGroup.loglike2
33 | ModelGroup.to_xlsx
34 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = [
3 | "setuptools>=42",
4 | "wheel",
5 | "oldest-supported-numpy",
6 | "Cython",
7 | ]
8 | build-backend = "setuptools.build_meta"
9 |
10 |
11 | [tool.pytest.ini_options]
12 | minversion = "6.0"
13 | addopts = """\
14 | -v --nbmake --disable-warnings \
15 | --doctest-modules \
16 | --ignore=sandbox.py \
17 | --ignore=larch/util/visual_processing.py \
18 | --ignore-glob=book/_build/* \
19 | """
20 | testpaths = [
21 | "tests",
22 | "book/example",
23 | ]
24 | doctest_optionflags = "NORMALIZE_WHITESPACE"
25 |
--------------------------------------------------------------------------------
/strip_metadata.sh:
--------------------------------------------------------------------------------
1 | #!/bin/zsh
2 |
3 | eval "$(conda shell.bash hook)"
4 | conda activate garage37
5 |
6 | nbstripout larch/doc/example/000_mtc_data.ipynb --keep-output --keep-count
7 | nbstripout larch/doc/example/200_exampville.ipynb --keep-output --keep-count
8 | nbstripout larch/doc/example/201_exville_mode_choice.ipynb --keep-output --keep-count
9 | nbstripout larch/doc/example/202_exville_mc_logsums.ipynb --keep-output --keep-count
10 | nbstripout larch/doc/example/203_exville_dest.ipynb --keep-output --keep-count
11 | nbstripout larch/doc/example/300_itinerary.ipynb --keep-output --keep-count
12 |
--------------------------------------------------------------------------------
/larch/doc/example/examples.rst:
--------------------------------------------------------------------------------
1 |
2 | .. currentmodule:: larch.examples
3 |
4 | .. _examples:
5 |
6 | ========
7 | Examples
8 | ========
9 |
10 | Here we provide some illustrative examples.
11 | You can reproduce all of these examples in your own install of Larch,
12 | as all of the example data is included with the standard distribution.
13 | To work with any objects you find in the examples, a handy function
14 | is provided to extract that object directly into your workspace:
15 |
16 | .. autofunction:: larch.example
17 |
18 |
19 | .. toctree::
20 |
21 | mtc
22 | swissmetro
23 | exampville
24 | itinerary
25 |
26 |
--------------------------------------------------------------------------------
/larch/util/colors.py:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | _color_rgb256 = {}
5 | _color_rgb256['sky'] = (35,192,241)
6 | _color_rgb256['ocean'] = (29,139,204)
7 | _color_rgb256['night'] = (100,120,186)
8 | _color_rgb256['forest'] = (39,182,123)
9 | _color_rgb256['lime'] = (128,189,1)
10 | _color_rgb256['orange'] = (246,147,0)
11 | _color_rgb256['red'] = (246,1,0)
12 |
13 | def hexcolor(color):
14 | c = _color_rgb256[color.casefold()]
15 | return "#{}{}{}".format(*(hex(c[i])[-2:] if c[i]>15 else "0"+hex(c[i])[-1:] for i in range(3)))
16 |
17 | def strcolor_rgb256(color):
18 | c = _color_rgb256[color.casefold()]
19 | return "{},{},{}".format(*c)
20 |
--------------------------------------------------------------------------------
/larch/numba/__init__.py:
--------------------------------------------------------------------------------
1 | from .model import NumbaModel as Model
2 | from .. import DataFrames, P, X, PX, OMX, DBF, Reporter, NumberedCaption, read_metadata, examples, util, __version__
3 | from ..examples import example as _example
4 | from ..data_warehouse import example_file
5 | from ..model.model_group import ModelGroup
6 |
7 | try:
8 | from .. import dataset
9 | from ..dataset import Dataset, DataArray, DataTree, merge
10 | except RuntimeError:
11 | pass
12 |
13 | def example(*args, **kwargs):
14 | import importlib
15 | kwargs['larch'] = importlib.import_module(__name__)
16 | return _example(*args, **kwargs)
17 |
--------------------------------------------------------------------------------
/tools/get_version.py:
--------------------------------------------------------------------------------
1 | import os
2 | import setuptools_scm
3 | import sys
4 |
5 | def get_version():
6 | version = setuptools_scm.get_version(root='..', relative_to=__file__)
7 | os.environ["LARCH_VERSION"] = version
8 | if len(sys.argv) >= 2:
9 | v_file = sys.argv[1]
10 | else:
11 | v_file = os.path.normpath(
12 | os.path.join(os.path.dirname(__file__), "..", "LARCH_VERSION.txt")
13 | )
14 | with open(v_file, 'wt') as f:
15 | f.write(f"LARCH_VERSION={version}\n")
16 | print(f"LARCH_VERSION={version}")
17 | return version
18 |
19 | if __name__ == '__main__':
20 | get_version()
21 |
--------------------------------------------------------------------------------
/larch/workspace.py:
--------------------------------------------------------------------------------
1 |
2 | from xmle import Reporter, NumberedCaption
3 |
4 | def make_reporter(title, notes=True, viz=True):
5 | """
6 | Make a standard reporter.
7 | """
8 |
9 | REPORT = Reporter(title)
10 | REPORT.MAIN = REPORT.section("Estimated Model", "Estimated Model")
11 | if notes is True:
12 | REPORT.NOTES = REPORT.section("Model Notes", "Notes")
13 | elif notes:
14 | REPORT.NOTES = REPORT.section(notes, notes)
15 | if viz is True:
16 | REPORT.VIZ = REPORT.section("Model Visualization", "Visualization")
17 | elif viz:
18 | REPORT.VIZ = REPORT.section(viz, viz)
19 |
20 | REPORT.FIG = NumberedCaption('Figure')
21 | REPORT.TAB = NumberedCaption('Table')
22 |
23 | return REPORT
24 |
25 |
--------------------------------------------------------------------------------
/larch/linalg/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | def general_inverse(a, small_eig=0.0001):
4 | """Find the matrix inverse if possible, otherwise find the pseudo-inverse."""
5 | import numpy
6 | if not numpy.isfinite(a).all():
7 | raise ValueError("nonfinite values in array")
8 | min_eig = None
9 | try:
10 | eig = numpy.linalg.eigvalsh(a)
11 | min_eig = numpy.min(numpy.abs(eig))
12 | if min_eig < small_eig:
13 | raise numpy.linalg.linalg.LinAlgError()
14 | x = numpy.linalg.inv(a)
15 | except numpy.linalg.linalg.LinAlgError:
16 | if min_eig is not None and min_eig < small_eig:
17 | import warnings
18 | warnings.warn(f"minimum eig {min_eig} in general_inverse")
19 | import scipy.linalg
20 | x = scipy.linalg.pinvh(a)
21 | return x
22 |
23 |
24 |
--------------------------------------------------------------------------------
/book/api/datatree.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ========
4 | DataTree
5 | ========
6 |
7 | .. autosummary::
8 | :toctree: generated/
9 |
10 | DataTree
11 |
12 |
13 | Attributes
14 | ----------
15 |
16 | .. autosummary::
17 | :toctree: generated/
18 |
19 | DataTree.root_node_name
20 | DataTree.subspaces
21 | DataTree.relationships_are_digitized
22 | DataTree.n_cases
23 | DataTree.n_alts
24 | DataTree.CASEID
25 | DataTree.ALTID
26 |
27 |
28 | Methods
29 | -------
30 |
31 | .. autosummary::
32 | :toctree: generated/
33 |
34 | DataTree.add_dataset
35 | DataTree.add_relationship
36 | DataTree.digitize_relationships
37 | DataTree.query_cases
38 | DataTree.replace_datasets
39 | DataTree.setup_flow
40 |
--------------------------------------------------------------------------------
/tests/test_histograms.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | import numpy as np
4 | import matplotlib
5 | from matplotlib.testing.decorators import image_comparison
6 | import matplotlib.pyplot as plt
7 |
8 | # @image_comparison(baseline_images=['histogram'],
9 | # extensions=['png'])
10 | # def test_spines_axes_positions():
11 | # # SF bug 2852168
12 | # fig = plt.figure()
13 | # x = np.linspace(0,2*np.pi,100)
14 | # y = 2*np.sin(x)
15 | # ax = fig.add_subplot(1,1,1)
16 | # ax.set_title('centered spines')
17 | # ax.plot(x,y)
18 | # ax.spines['right'].set_position(('axes',0.1))
19 | # ax.yaxis.set_ticks_position('right')
20 | # ax.spines['top'].set_position(('axes',0.25))
21 | # ax.xaxis.set_ticks_position('top')
22 | # ax.spines['left'].set_color('none')
23 | # ax.spines['bottom'].set_color('none')
24 | #
25 |
--------------------------------------------------------------------------------
/larch/model/tree_struct.pxd:
--------------------------------------------------------------------------------
1 | # cython: language_level=3, embedsignature=True
2 |
3 | from ..general_precision cimport *
4 |
5 |
6 | cdef class TreeStructure:
7 |
8 | cdef:
9 | int n_nodes
10 | int n_elementals
11 | l4_float_t[:] model_mu_param_values # [n_nodes]
12 | #l4_float_t[:,:] model_alpha_param_values # [n_nodes, n_nodes]
13 | int[:] model_mu_param_slots # [n_nodes]
14 |
15 | int n_edges
16 | int[:] edge_dn # [n_edges]
17 | int[:] edge_up # [n_edges]
18 | l4_float_t[:] edge_alpha_values # [n_edges]
19 | l4_float_t[:] edge_logalpha_values # [n_edges]
20 | int[:] first_edge_for_up # [n_nodes] index of first edge where this node is the up
21 | int[:] n_edges_for_up # [n_nodes] n edge where this node is the up
22 |
--------------------------------------------------------------------------------
/larch/model/fastmath.pxi:
--------------------------------------------------------------------------------
1 |
2 |
3 | # from libc.math cimport exp, log, pow
4 | # from numpy.math cimport expf, logf
5 |
6 | cdef extern from "fastexp.h" nogil:
7 | float fastexp (float p)
8 | float fasterexp (float p)
9 |
10 | cdef extern from "fastlog.h" nogil:
11 | float fastlog (float p)
12 | float fasterlog (float p)
13 |
14 | cdef inline float f_exp(float x) nogil:
15 | return fastexp(x)
16 |
17 | cdef inline float f_log(float x) nogil:
18 | return fastlog(x)
19 |
20 |
21 |
22 | cdef inline double exp_fast2(double x):
23 | x = 1.0 + x / 1024
24 | x *= x
25 | x *= x
26 | x *= x
27 | x *= x
28 | x *= x
29 | x *= x
30 | x *= x
31 | x *= x
32 | x *= x
33 | x *= x
34 | return x
35 |
36 | cdef inline double exp_fast3(double x):
37 | x = 1.0 + x / 256.0
38 | x *= x
39 | x *= x
40 | x *= x
41 | x *= x
42 | x *= x
43 | x *= x
44 | x *= x
45 | x *= x
46 | return x
47 |
--------------------------------------------------------------------------------
/tests/test_util_manipulation.py:
--------------------------------------------------------------------------------
1 | import pandas
2 | import os
3 | import gzip
4 | import pickle
5 | import base64
6 | import io
7 | import numpy
8 | from pytest import approx
9 | from larch.util.data_manipulation import periodize
10 |
11 | def test_periodize():
12 | h = pandas.Series(range(21))
13 | p = periodize(h, default='OP', AM=(6.5, 9), PM=(16, 19))
14 | assert p.dtype == 'category'
15 | assert p[0] == 'OP'
16 | assert p[1] == 'OP'
17 | assert p[2] == 'OP'
18 | assert p[3] == 'OP'
19 | assert p[4] == 'OP'
20 | assert p[5] == 'OP'
21 | assert p[6] == 'OP'
22 | assert p[7] == 'AM'
23 | assert p[8] == 'AM'
24 | assert p[9] == 'AM'
25 | assert p[10] == 'OP'
26 | assert p[11] == 'OP'
27 | assert p[12] == 'OP'
28 | assert p[13] == 'OP'
29 | assert p[14] == 'OP'
30 | assert p[15] == 'OP'
31 | assert p[16] == 'PM'
32 | assert p[17] == 'PM'
33 | assert p[18] == 'PM'
34 | assert p[19] == 'PM'
35 | assert p[20] == 'OP'
36 |
--------------------------------------------------------------------------------
/tests/test_doctor.py:
--------------------------------------------------------------------------------
1 | import larch, pytest
2 | from larch import P,X,PX
3 | from pytest import approx, raises
4 | import numpy
5 | import pandas
6 |
7 | def test_nan_weight():
8 | m = larch.Model.Example(1, legacy=True)
9 | m.weight_co_var = 'hhowndum'
10 | m.load_data()
11 | m.dataframes.data_wt = m.dataframes.data_wt.div(m.dataframes.data_wt)
12 |
13 | m, diagnosis = m.doctor()
14 | assert type(m) is larch.Model
15 | assert 'nan_weight' in diagnosis
16 | assert diagnosis.nan_weight['n'].iloc[0] == 1882
17 |
18 | m, diagnosis = m.doctor(repair_nan_wt=True)
19 | assert type(m) is larch.Model
20 | assert 'nan_weight' in diagnosis
21 | assert diagnosis.nan_weight['n'].iloc[0] == 1882
22 |
23 | m, diagnosis = m.doctor()
24 | assert type(m) is larch.Model
25 | assert 'nan_weight' not in diagnosis
26 | assert len(diagnosis) == 0
27 |
28 | def test_doctor_before_load_data():
29 |
30 | with raises(ValueError):
31 | larch.Model().doctor()
32 |
--------------------------------------------------------------------------------
/larch/model/linear.pxd:
--------------------------------------------------------------------------------
1 | # cython: language_level=3, embedsignature=True
2 |
3 | from ..general_precision cimport *
4 |
5 | cdef class UnicodeRef_C(unicode):
6 | pass
7 |
8 | cdef class Ref_Gen:
9 | cdef:
10 | object _kind
11 |
12 | cdef class ParameterRef_C(UnicodeRef_C):
13 | cdef public unicode _formatting
14 |
15 | cdef class DataRef_C(UnicodeRef_C):
16 | pass
17 |
18 | cdef class LinearComponent_C:
19 | cdef:
20 | unicode _param
21 | unicode _data
22 | l4_float_t _scale
23 |
24 |
25 | cdef class LinearFunction_C:
26 | cdef:
27 | object _func
28 | object _instance
29 | unicode name
30 | unicode private_name
31 |
32 |
33 | cdef class DictOfLinearFunction_C:
34 | cdef:
35 | object _map
36 | object _instance
37 | object _alts_validator
38 | unicode name
39 | unicode private_name
40 |
41 |
42 | cdef class GenericContainerCy:
43 | cdef public:
44 | LinearFunction_C _lf
45 | DictOfLinearFunction_C _dlf
46 | object ident
--------------------------------------------------------------------------------
/book/example/mtc.rst:
--------------------------------------------------------------------------------
1 |
2 | ===============
3 | MTC Examples
4 | ===============
5 |
6 | **Example Data**
7 |
8 | The MTC sample dataset is the same data used in the
9 | `Self Instructing Manual `_
10 | for discrete choice modeling::
11 |
12 | The San Francisco Bay Area work mode choice data set comprises 5029 home-to-work commute trips in the
13 | San Francisco Bay Area. The data is drawn from the San Francisco Bay Area Household Travel Survey
14 | conducted by the Metropolitan Transportation Commission (MTC) in the spring and fall of 1990. This
15 | survey included a one day travel diary for each household member older than five years and detailed
16 | individual and household socio-demographic information.
17 |
18 | You can use this data to reproduce the example models in that
19 | document, and several examples are provided here to do so.
20 |
21 | **Example Models**
22 |
23 | .. toctree::
24 | :glob:
25 |
26 | 0*
27 |
--------------------------------------------------------------------------------
/larch/util/activitysim/data_maker.py:
--------------------------------------------------------------------------------
1 | import os
2 | import subprocess
3 |
4 |
5 | def make_estimation_data(directory="test"):
6 | if os.path.exists(directory):
7 | check_file = os.path.join(directory, ".gitignore")
8 | if not os.path.exists(check_file):
9 | import importlib
10 | asim_create = importlib.import_module("activitysim.cli.create")
11 | asim_create.get_example("example_estimation_sf", directory)
12 | os.chdir(directory)
13 | cp = subprocess.run(
14 | [
15 | "activitysim",
16 | "run",
17 | "-c",
18 | "configs_estimation/configs",
19 | "-c",
20 | "configs",
21 | "-o",
22 | "output",
23 | "-d",
24 | "data_sf",
25 | ],
26 | capture_output=True,
27 | )
28 | with open(check_file, 'rt') as f:
29 | f.write("**/*.csv\n")
30 | f.write("**/*.txt\n")
31 | f.write("**/*.yaml\n")
32 | f.write("**/.gitignore\n")
33 | else:
34 | print(f"using existing directory `{directory}`")
35 | os.chdir(directory)
36 |
--------------------------------------------------------------------------------
/larch/doc/example/mtc.rst:
--------------------------------------------------------------------------------
1 |
2 | ===============
3 | MTC Examples
4 | ===============
5 |
6 | **Example Data**
7 |
8 | The MTC sample dataset is the same data used in the
9 | `Self Instructing Manual `_
10 | for discrete choice modeling::
11 |
12 | The San Francisco Bay Area work mode choice data set comprises 5029 home-to-work commute trips in the
13 | San Francisco Bay Area. The data is drawn from the San Francisco Bay Area Household Travel Survey
14 | conducted by the Metropolitan Transportation Commission (MTC) in the spring and fall of 1990. This
15 | survey included a one day travel diary for each household member older than five years and detailed
16 | individual and household socio-demographic information.
17 |
18 | You can use this data to reproduce the example models in that
19 | document, and several examples are provided here to do so.
20 |
21 | **Example Models**
22 |
23 | .. toctree::
24 | :glob:
25 |
26 | 0*
27 |
--------------------------------------------------------------------------------
/larch/model/tree_struct.pyx:
--------------------------------------------------------------------------------
1 | # cython: language_level=3, embedsignature=True
2 |
3 | import numpy
4 |
5 | cdef class TreeStructure:
6 |
7 | def __init__(self, model, graph):
8 | self.n_nodes = len(graph)
9 | self.n_elementals = graph.n_elementals()
10 | mu, muslots, up, dn, num, start, val = graph._get_simple_mu_and_alpha(model)
11 | self.model_mu_param_values = mu # [n_nodes]
12 | self.model_mu_param_slots = muslots # [n_nodes]
13 | # self.model_alpha_param_values = alpha # [n_nodes,n_nodes]
14 | self.n_edges = dn.shape[0] #
15 | self.edge_dn = dn # [n_edges]
16 | self.edge_up = up # [n_edges]
17 | self.edge_alpha_values = val # [n_edges]
18 | self.edge_logalpha_values = numpy.log(val) # [n_edges]
19 | self.first_edge_for_up = start # [n_nodes] index of first edge where this node is the up
20 | self.n_edges_for_up = num # [n_nodes] n edge where this node is the up
21 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Docs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/larch/doc/latentclass.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch.model.latentclass
2 |
3 | =======================
4 | Latent Class Models
5 | =======================
6 |
7 | Larch is able to handle latent class models. A latent class model can be thought of
8 | as a two-stage model: first, to which class does an observation belong; and then, given
9 | membership in that class, what are the choice probabilities. Each class can have its
10 | own distinct choice model, and parameters can be either independent or shared across
11 | classes.
12 |
13 | The class membership model is a choice model where "class membership" is the implied
14 | choice, instead of the actual alternatives in the choice set.
15 |
16 | Because the class membership model is not about the choices, it cannot use data about
17 | the choices in the model definition -- the class membership model can be based only on
18 | the `co` data.
19 |
20 | A :class:`LatentClassModel` is the core object used to represent a latent class model.
21 |
22 | .. autoclass:: LatentClassModel
23 |
24 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Sphinx_Test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/larch/model/controller.pxd:
--------------------------------------------------------------------------------
1 | # cython: language_level=3, embedsignature=True
2 |
3 | from ..general_precision cimport *
4 | from ..dataframes cimport DataFrames
5 | from .linear cimport LinearFunction_C, DictOfLinearFunction_C
6 | from .abstract_model cimport AbstractChoiceModel
7 | from .tree_struct cimport TreeStructure
8 |
9 | cdef class Model5c(AbstractChoiceModel):
10 |
11 | cdef public:
12 | DictOfLinearFunction_C _utility_co
13 | LinearFunction_C _utility_ca
14 | LinearFunction_C _quantity_ca
15 | DataFrames _dataframes
16 | object _dataservice
17 |
18 | cdef:
19 | object _quantity_scale
20 | object _logsum_parameter
21 | object rename_parameters
22 |
23 | str _choice_ca_var
24 | object _choice_co_vars
25 | str _choice_co_code
26 | bint _choice_any
27 | str _weight_co_var
28 | str _availability_var
29 | object _availability_co_vars
30 | bint _availability_any
31 |
32 | bint _is_clone
33 | bint _does_not_require_choice
34 |
35 | object _graph
36 | TreeStructure _tree_struct
37 |
38 | int _n_threads
39 |
40 |
--------------------------------------------------------------------------------
/book/example/legacy/itinerary.md:
--------------------------------------------------------------------------------
1 |
2 | # Itinerary Choice Examples
3 |
4 | **Example Data**
5 |
6 | The example itinerary choice bundled with Larch is based on data derived from a ticketing database
7 | provided by the Airlines Reporting Corporation. The data represent ten origin destination
8 | pairs for travel in U.S. continental markets in May of 2013. Itinerary characteristics
9 | have been masked, e.g., carriers are labeled generically as "carrier X" and departure times
10 | have been aggregated into categories. A fare is provided but is not completely accurate (a
11 | random error has been added to each fare). These modifications were made to satisfy
12 | nondisclosure agreements, so that the data can be published freely for teaching and
13 | demostration purposes. It is generally representative of real itinerary choice data used
14 | in practice, and the results obtained from this data are intuitive from a behavioral
15 | perspective, but it is not quite accurate and should not be used for behavioral studies.
16 |
17 | **Example Models**
18 |
19 | ```{tableofcontents}
20 | ```
21 |
--------------------------------------------------------------------------------
/larch/util/bitmasking.py:
--------------------------------------------------------------------------------
1 | import numpy, pandas
2 |
3 |
4 | def define_masks(bitmask_sizes):
5 | """
6 |
7 | Parameters
8 | ----------
9 | bitmask_sizes : iterable
10 |
11 | Returns
12 | -------
13 | list
14 | """
15 |
16 | shifts = ((numpy.cumsum(bitmask_sizes[::-1]))[::-1]).astype(int)
17 | shifts[:-1] = shifts[1:]
18 | shifts[-1] = 0
19 |
20 | masks = [
21 | ((2**bitmask_sizes[b])-1)
22 | <=0.29
10 | - numpy>=1.19,<1.22
11 | - addicty >=2022.2.1
12 | - appdirs >=1.4
13 | - beautifulsoup4 >=4.6.3
14 | - blosc >=1.14.3
15 | - cloudpickle >=0.6.1
16 | - dask-core
17 | - docutils >=0.13.1
18 | - filelock
19 | - geopandas
20 | - gh
21 | - jinja2 >=2.10
22 | - joblib
23 | - jupyter-book
24 | - llvm-openmp # [osx]
25 | - lxml >=4.2.5
26 | - matplotlib >=3.0
27 | - nbmake
28 | - nbsphinx
29 | - networkx >=2.4
30 | - numba>=0.53
31 | - numexpr
32 | - openmatrix
33 | - openpyxl
34 | - pandas >=1.2,<1.5
35 | - pillow
36 | - pyarrow
37 | - pydot
38 | - pytables >=3.4.4
39 | - pytest >=4.0
40 | - pytest-regressions
41 | - pyyaml
42 | - ruamel.yaml
43 | - scikit-learn >=0.21
44 | - scipy >=1.1
45 | - seaborn >=0.9.0
46 | - setuptools_scm
47 | - sphinx
48 | - sphinx_rtd_theme
49 | - tabulate
50 | - tqdm >=4.28.1
51 | - xarray
52 | - xlsxwriter
53 | - xmle >=0.1.3
54 | - zarr
55 | - xgboost
56 |
--------------------------------------------------------------------------------
/larch/doc/example/itinerary.rst:
--------------------------------------------------------------------------------
1 |
2 | ==========================
3 | Itinerary Choice Examples
4 | ==========================
5 |
6 | **Example Data**
7 |
8 | The example itinerary choice bundled with Larch is based on data derived from a ticketing database
9 | provided by the Airlines Reporting Corporation. The data represent ten origin destination
10 | pairs for travel in U.S. continental markets in May of 2013. Itinerary characteristics
11 | have been masked, e.g., carriers are labeled generically as "carrier X" and departure times
12 | have been aggregated into categories. A fare is provided but is not completely accurate (a
13 | random error has been added to each fare). These modifications were made to satisfy
14 | nondisclosure agreements, so that the data can be published freely for teaching and
15 | demostration purposes. It is generally representative of real itinerary choice data used
16 | in practice, and the results obtained from this data are intuitive from a behavioral
17 | perspective, but it is not quite accurate and should not be used for behavioral studies.
18 |
19 | **Example Models**
20 |
21 | .. toctree::
22 | :glob:
23 |
24 | 3*
25 |
26 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/PyTest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/larch/util/aster.py:
--------------------------------------------------------------------------------
1 |
2 | import ast
3 | import numpy
4 |
5 |
6 |
7 | def inXd(a,*b,**c):
8 | return numpy.in1d(a,*b,**c).reshape(a.shape)
9 |
10 |
11 | class AstWrapper(ast.NodeTransformer):
12 |
13 | def visit_Compare(self, node):
14 | "Change the `in` operator to numpy.in1d"
15 | if len(node.ops)==1 and isinstance(node.ops[0], ast.In):
16 | #in1d = ast.Attribute(value=ast.Name(id='numpy', ctx=ast.Load()), attr='in1d', ctx=ast.Load())
17 | in1d = ast.Name(id='inXd', ctx=ast.Load())
18 | return ast.Call(in1d, [node.left, node.comparators[0]], [])
19 | elif len(node.ops)==1 and isinstance(node.ops[0], ast.NotIn):
20 | #in1d = ast.Attribute(value=ast.Name(id='numpy', ctx=ast.Load()), attr='in1d', ctx=ast.Load())
21 | in1d = ast.Name(id='inXd', ctx=ast.Load())
22 | return ast.Call(in1d, [node.left, node.comparators[0]], [ast.keyword('invert',ast.NameConstant(True))])
23 | else:
24 | return node
25 |
26 |
27 | def asterize(cmd, mode="eval"):
28 | tree = ast.parse(cmd, mode=mode)
29 | tree = AstWrapper().visit(tree)
30 | # Add lineno & col_offset to the nodes we created
31 | ast.fix_missing_locations(tree)
32 | co = compile(tree, "", mode)
33 | return co
34 |
35 |
--------------------------------------------------------------------------------
/larch/data_services/h5/__init__.py:
--------------------------------------------------------------------------------
1 | from .h5data import *
2 | from .h5vault import *
3 |
4 | import tables
5 | import sys, atexit
6 | from distutils.version import StrictVersion
7 |
8 | def my_close_open_files(verbose):
9 | open_files = tables.file._open_files
10 |
11 | are_open_files = len(open_files) > 0
12 |
13 | if verbose and are_open_files:
14 | sys.stderr.write("Closing remaining open files:")
15 |
16 | if StrictVersion(tables.__version__) >= StrictVersion("3.1.0"):
17 | # make a copy of the open_files.handlers container for the iteration
18 | handlers = list(open_files.handlers)
19 | else:
20 | # for older versions of pytables, setup the handlers list from the
21 | # keys
22 | keys = open_files.keys()
23 | handlers = []
24 | for key in keys:
25 | handlers.append(open_files[key])
26 |
27 | for fileh in handlers:
28 | if verbose:
29 | sys.stderr.write("\n%s..." % fileh.filename)
30 |
31 | fileh.close()
32 |
33 | if verbose:
34 | sys.stderr.write("done")
35 |
36 | if verbose and are_open_files:
37 | sys.stderr.write("\n")
38 |
39 | atexit.register(my_close_open_files, False)
--------------------------------------------------------------------------------
/book/api/dataset.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | =======
4 | Dataset
5 | =======
6 |
7 | Constructors
8 | ------------
9 |
10 | .. autosummary::
11 | :toctree: generated/
12 |
13 | Dataset
14 | Dataset.from_idca
15 | Dataset.from_idce
16 | Dataset.from_idco
17 | Dataset.construct
18 | Dataset.from_table
19 | Dataset.from_omx
20 | Dataset.from_omx_3d
21 | Dataset.from_zarr
22 | Dataset.from_named_objects
23 |
24 |
25 | Attributes
26 | ----------
27 |
28 | .. autosummary::
29 | :toctree: generated/
30 |
31 | Dataset.n_cases
32 | Dataset.n_alts
33 | Dataset.CASEID
34 | Dataset.ALTID
35 | Dataset.alts_mapping
36 | Dataset.dims
37 | Dataset.sizes
38 | Dataset.data_vars
39 | Dataset.coords
40 | Dataset.attrs
41 | Dataset.encoding
42 | Dataset.indexes
43 | Dataset.chunks
44 | Dataset.chunksizes
45 | Dataset.nbytes
46 |
47 | Methods
48 | -------
49 |
50 | .. autosummary::
51 | :toctree: generated/
52 |
53 | Dataset.caseids
54 | Dataset.dissolve_zero_variance
55 | Dataset.query_cases
56 | Dataset.set_altids
57 | Dataset.set_altnames
58 | Dataset.set_dtypes
59 | Dataset.setup_flow
60 | Dataset.get_expr
61 |
--------------------------------------------------------------------------------
/larch/data_services/h5/h5pod/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 | from pathlib import Path
3 | import tables as tb
4 | import numpy
5 | import pandas
6 | import warnings
7 | from ....util import Dict
8 | from ....util.aster import asterize
9 | from ....util.text_manip import truncate_path_for_display
10 |
11 |
12 | from ... import _reserved_names_
13 |
14 | from .generic import H5Pod, NoKnownShape, IncompatibleShape
15 | from .idca import H5PodCA
16 | from .idco import H5PodCO
17 | from .idcs import H5PodCS
18 | from .idce import H5PodCE
19 | from .idga import H5PodGA
20 | from .idrc import H5PodRC
21 | from .id0a import H5Pod0A
22 |
23 | def H5PodFactory(uri:str):
24 | from urllib.parse import urlparse, parse_qs
25 | p = urlparse(uri, scheme='file')
26 | q = parse_qs(p.query)
27 | if 'type' in q:
28 | cls = _pod_types[q['type'][0]]
29 | else:
30 | cls = H5Pod
31 | if 'mode' in q:
32 | mode = q['mode'][0]
33 | else:
34 | mode = 'r'
35 | return cls(filename=p.path, mode=mode, groupnode=p.fragment)
36 |
37 |
38 | _pod_types = {
39 | 'idco': H5PodCO,
40 | 'idca': H5PodCA,
41 | 'idce': H5PodCE,
42 | 'idga': H5PodGA,
43 | 'idcs': H5PodCS,
44 | 'co': H5PodCO,
45 | 'ca': H5PodCA,
46 | 'ce': H5PodCE,
47 | 'ga': H5PodGA,
48 | 'cs': H5PodCS,
49 | }
50 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: xenial
2 | language: python
3 | python:
4 | - 3.7
5 | install:
6 | - sudo apt-get update
7 | # We do this conditionally because it saves us some downloading if the
8 | # version is the same.
9 | - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then
10 | wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O miniconda.sh;
11 | else
12 | wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh;
13 | fi
14 | - bash miniconda.sh -b -p $HOME/miniconda
15 | - export PATH="$HOME/miniconda/bin:$PATH"
16 | - hash -r
17 | - conda config --set always_yes yes --set changeps1 no
18 | - conda update -q conda
19 | # Useful for debugging any issues with conda
20 | - conda info -a
21 |
22 | - export DEPENDS="conda-build anaconda-client numpy cython>=0.29.21 numba conda-verify"
23 | - conda create -q -n larch-build-environment python=$TRAVIS_PYTHON_VERSION $DEPENDS
24 | - source activate larch-build-environment
25 |
26 | script:
27 | - mkdir ~/conda-bld
28 | - conda config --set anaconda_upload no
29 | - export CONDA_BLD_PATH=~/conda-bld
30 | - travis_wait conda build conda-recipe -c conda-forge
31 |
32 | after_success:
33 | - bash conda-recipe/conda_upload.sh
34 |
35 |
36 |
--------------------------------------------------------------------------------
/larch/model/possible_overspec.py:
--------------------------------------------------------------------------------
1 | import numpy
2 | import warnings
3 |
4 |
5 | class PossibleOverspecification(Warning):
6 | pass
7 |
8 |
9 | def compute_possible_overspecification(a, holdfast_vector=None):
10 | """
11 |
12 | Parameters
13 | ----------
14 | a : matrix
15 | The hessian matrix
16 | holdfast_vector : vector
17 | A vectgor of indicators for which row/cols should be ignored as holdfast-ed
18 |
19 | Returns
20 | -------
21 |
22 | """
23 | ret = []
24 | if holdfast_vector is None:
25 | holdfast_vector = numpy.zeros(a.shape[0], dtype=bool)
26 | else:
27 | holdfast_vector = holdfast_vector.astype(bool, copy=True)
28 | holdfast_vector |= ((a == 0).all(0))
29 | a_packed = a[~holdfast_vector, :][:, ~holdfast_vector]
30 | try:
31 | eigenvalues_packed, eigenvectors_packed = numpy.linalg.eigh(a_packed)
32 | except numpy.linalg.linalg.LinAlgError as err:
33 | return [('LinAlgError', str(err), '')]
34 | for i in range(len(eigenvalues_packed)):
35 | if numpy.abs(eigenvalues_packed[i]) < 0.001:
36 | v = eigenvectors_packed[:, i]
37 | v = numpy.round(v, 7)
38 | v_unpacked = numpy.zeros(a.shape[0])
39 | v_unpacked[~holdfast_vector.astype(bool)] = v
40 | ret.append((eigenvalues_packed[i], numpy.where(v_unpacked)[0], v_unpacked))
41 | return ret
42 |
43 |
--------------------------------------------------------------------------------
/larch/util/counting.py:
--------------------------------------------------------------------------------
1 |
2 | import pandas, numpy
3 | from ..data_services.h5.h5pod.generic import CArray
4 |
5 | def tally(arr):
6 |
7 | if isinstance(arr, CArray):
8 | arr_ = arr
9 | arr = arr[:]
10 | values, freqs = numpy.unique(arr[~pandas.isnull(arr)], return_counts=True)
11 | try:
12 | d = arr_.DICTIONARY
13 | except:
14 | d = {}
15 | s = pandas.Series(freqs, index=[d.get(i,i) for i in values]).sort_values(ascending=False)
16 | nan_count = pandas.isnull(arr).sum()
17 | if nan_count:
18 | s[numpy.NaN] = nan_count
19 | return s
20 | else:
21 | values, freqs = numpy.unique( arr[~pandas.isnull(arr)], return_counts=True)
22 | s = pandas.Series(freqs, index=values).sort_values(ascending=False)
23 | nan_count = pandas.isnull(arr).sum()
24 | if nan_count:
25 | s[numpy.NaN] = nan_count
26 | return s
27 |
28 | def tally_weighted(arr, wgt):
29 |
30 | if isinstance(arr, CArray):
31 | raise NotImplementedError()
32 | else:
33 | arr = arr.reshape(-1)
34 | wgt = wgt.reshape(-1)
35 |
36 | values, inv = numpy.unique(arr[~pandas.isnull(arr)], return_inverse=True)
37 | s = pandas.Series(numpy.bincount(inv, wgt), index=values).sort_values(ascending=False)
38 | nan_count = wgt[pandas.isnull(arr)].sum()
39 | if nan_count:
40 | s[numpy.NaN] = nan_count
41 | return s
--------------------------------------------------------------------------------
/larch/data_services/h5/h5util.py:
--------------------------------------------------------------------------------
1 |
2 | import tables as tb
3 |
4 | def get_or_create_group(h5file, where, name=None, title='', filters=None, createparents=False, skip_on_readonly=False):
5 | if "/" in name:
6 | names = name.split('/')
7 | where += '/' + '/'.join(names[:-1])
8 | name = names[-1]
9 | try:
10 | return h5file.get_node(where, name=name)
11 | except tb.NoSuchNodeError:
12 | if name is not None:
13 | try:
14 | return h5file.create_group(where, name, title=title, filters=filters, createparents=createparents)
15 | except tb.FileModeError:
16 | if skip_on_readonly:
17 | return
18 | else:
19 | raise
20 |
21 |
22 |
23 | def get_or_create_subgroup(parentgroup, name, title='', filters=None, skip_on_readonly=False):
24 | from .h5pod.generic import H5Pod
25 | if isinstance(parentgroup, H5Pod):
26 | parentgroup = parentgroup._groupnode
27 | if parentgroup is None and skip_on_readonly:
28 | return None
29 | h5file = parentgroup._v_file
30 | try:
31 | return h5file.get_node(parentgroup, name=name)
32 | except tb.NoSuchNodeError:
33 | if name is not None:
34 | try:
35 | return h5file.create_group(parentgroup, name, title=title, filters=filters, createparents=False)
36 | except tb.FileModeError:
37 | if skip_on_readonly:
38 | return
39 | else:
40 | raise
41 |
--------------------------------------------------------------------------------
/larch/doc/data-pods.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch.data_services
2 |
3 | =======================
4 | Pods
5 | =======================
6 |
7 | A :class:`DataService` is essentially a collection of one or more separate but related
8 | data :class:`Pod`. Each pod can contain multiple data elements, stored in a variety of
9 | formats. :class:`Pod` are roughly grouped into two families: those that provide :ref:`idco`
10 | data, and those that provide :ref:`idca` data. The abstract base class :class:`Pod`
11 | provides some basic common functionality and defines a consistent interface.
12 |
13 | .. autoclass:: Pod
14 |
15 | .. automethod:: get_data_item
16 |
17 | .. automethod:: load_data_item
18 |
19 | .. automethod:: names
20 |
21 | .. automethod:: nameset
22 |
23 | .. autoattribute:: shape
24 |
25 | .. autoattribute:: dims
26 |
27 | .. autoattribute:: n_cases
28 |
29 | .. autoattribute:: ident
30 |
31 | .. autoattribute:: durable_mask
32 |
33 | .. autoattribute:: filename
34 |
35 |
36 |
37 | Creating a :class:`Pod` requires a particular subclass to instantiate.
38 |
39 |
40 | :ref:`idco` Pods
41 | ----------------
42 |
43 | .. autoclass:: H5PodCO
44 |
45 | .. autoclass:: H5PodCS
46 |
47 | .. autoclass:: H5PodRC
48 |
49 |
50 |
51 | :ref:`idca` Pods
52 | ----------------
53 |
54 | .. autoclass:: H5PodCA
55 |
56 | .. autoclass:: H5PodGA
57 |
58 |
--------------------------------------------------------------------------------
/larch/data_warehouse/__init__.py:
--------------------------------------------------------------------------------
1 |
2 | import os
3 | import glob
4 |
5 | warehouse_directory = os.path.dirname(__file__)
6 |
7 | def file_list():
8 | return list(glob.glob(os.path.join(warehouse_directory,"*")))
9 |
10 | def example_file(f, missing_ok=False, rel=False):
11 | """
12 | Get the file path of a particular example file.
13 |
14 | All example files are stored in larch/data_warehouse.
15 |
16 | Parameters
17 | ----------
18 | f : str
19 | The base filename to find.
20 | missing_ok : bool, default False
21 | Do not raise a FileNotFoundError if the file is missing. This
22 | is generally only useful in larch scripts that will create the
23 | example file, not for end users, who generally should not
24 | create new data files in the data_warehouse directory.
25 | rel : bool or str, default False
26 | Return the file path relative to this directory, or if given as
27 | True, relative to the current working directory.
28 |
29 | Returns
30 | -------
31 | str
32 | """
33 | fi = os.path.join(warehouse_directory,f)
34 | if not os.path.exists(fi) and not missing_ok:
35 | candidates = list(glob.glob(os.path.join(warehouse_directory,f"*{f}*")))
36 | if len(candidates)==1:
37 | return candidates[0]
38 | raise FileNotFoundError(f)
39 | if rel is True:
40 | rel = os.getcwd()
41 | if rel:
42 | fi = os.path.relpath(fi, start=rel)
43 | return fi
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/book/api/linear.rst:
--------------------------------------------------------------------------------
1 |
2 |
3 | .. currentmodule:: larch.model.linear
4 |
5 | =======================
6 | Linear Functions
7 | =======================
8 |
9 | At the heart of most common discrete choice models is a linear-in-parameters utility
10 | function. Larch is written with this design specifically in mind.
11 | It is designed to integrate with Panda and NumPy and facilitate fast processing of linear models.
12 |
13 | .. note::
14 |
15 | If you want to estimate *non-linear* models, try `Biogeme `_,
16 | which is more flexible in form and can be used for almost any model structure.
17 |
18 | The basic structure of any linear-in-parameters function is that it is a summation of a sequence
19 | of terms, where each term is the product of a parameter and some data. The data could be
20 | a simple single named value (e.g. ``travel_cost``), or it could be some function of one or more other pieces of data,
21 | but the important salient feature of data is that it can be computed without knowing the
22 | value of any model parameter to be estimated (e.g. ``log(1+travel_cost)``).
23 |
24 |
25 | .. autoclass:: UnicodeRef_C
26 | :show-inheritance:
27 | :members:
28 |
29 |
30 | Parameters
31 | ----------
32 |
33 | .. autoclass:: ParameterRef_C
34 | :show-inheritance:
35 | :members:
36 |
37 | Data
38 | ----
39 |
40 | .. autoclass:: DataRef_C
41 | :show-inheritance:
42 | :members:
43 |
44 |
45 |
--------------------------------------------------------------------------------
/larch/doc/linear-functions.rst:
--------------------------------------------------------------------------------
1 |
2 |
3 | .. currentmodule:: larch.model.linear
4 |
5 | =======================
6 | Linear Functions
7 | =======================
8 |
9 | At the heart of most common discrete choice models is a linear-in-parameters utility
10 | function. Larch is written with this design specifically in mind.
11 | It is designed to integrate with Panda and NumPy and facilitate fast processing of linear models.
12 |
13 | .. note::
14 |
15 | If you want to estimate *non-linear* models, try `Biogeme `_,
16 | which is more flexible in form and can be used for almost any model structure.
17 |
18 | The basic structure of any linear-in-parameters function is that it is a summation of a sequence
19 | of terms, where each term is the product of a parameter and some data. The data could be
20 | a simple single named value (e.g. ``travel_cost``), or it could be some function of one or more other pieces of data,
21 | but the important salient feature of data is that it can be computed without knowing the
22 | value of any model parameter to be estimated (e.g. ``log(1+travel_cost)``).
23 |
24 |
25 | .. autoclass:: UnicodeRef_C
26 | :show-inheritance:
27 | :members:
28 |
29 |
30 | Parameters
31 | ----------
32 |
33 | .. autoclass:: ParameterRef_C
34 | :show-inheritance:
35 | :members:
36 |
37 | Data
38 | ----
39 |
40 | .. autoclass:: DataRef_C
41 | :show-inheritance:
42 | :members:
43 |
44 |
45 |
--------------------------------------------------------------------------------
/book/_scripts/hide_test_cells.py:
--------------------------------------------------------------------------------
1 | import io
2 | import os.path
3 | from glob import glob
4 |
5 | import nbformat as nbf
6 |
7 | # Collect a list of all notebooks in the docs folder
8 | notebooks = glob(
9 | os.path.join(os.path.dirname(__file__), "..", "**", "*.ipynb"),
10 | recursive=True,
11 | )
12 |
13 | # Text to look for in adding tags
14 | text_search_dict = {
15 | "# TEST": "remove_cell", # Remove the whole cell
16 | "# HIDDEN": "hide_cell", # Hide the whole cell
17 | "# NO CODE": "remove_input", # Remove only the input
18 | "# HIDE CODE": "hide_input", # Hide the input w/ a button to show
19 | }
20 |
21 | # Search through each notebook and look for th text, add a tag if necessary
22 | for ipath in notebooks:
23 | if "/_build/" in ipath:
24 | continue
25 | touch = False
26 | ntbk = nbf.read(ipath, nbf.NO_CONVERT)
27 |
28 | for cell in ntbk.cells:
29 | cell_tags = cell.get("metadata", {}).get("tags", [])
30 | for key, val in text_search_dict.items():
31 | if key in cell["source"]:
32 | if val not in cell_tags:
33 | cell_tags.append(val)
34 | touch = True
35 | if len(cell_tags) > 0:
36 | cell["metadata"]["tags"] = cell_tags
37 | if 1:
38 | print(f"hiding test cells in {ipath}")
39 | with io.open(ipath, 'w', encoding='utf-8') as f:
40 | nbf.write(ntbk, f)
41 | else:
42 | print(f"no changes in {ipath}")
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *__pycache__*
2 | build/*
3 | dist/*
4 | OpenBLAS-v*
5 | playground/*
6 | wayground/*
7 | larch_test_osx.sh
8 | win/*
9 | *.obj
10 | *.o
11 | *.xcodeproj*
12 | *.so
13 | LarchTest.app/*
14 | build/lib.macosx-10.6-intel-3.4/larch/libelmsqlite.so
15 | build/lib.macosx-10.6-intel-3.4/larch/apsw.so
16 | build/lib.macosx-10.6-intel-3.5/larch/libelmsqlite.so
17 | build/lib.macosx-10.6-intel-3.5/larch/apsw.so
18 | Larch.xcodeproj/xcuserdata/jpn.xcuserdatad/xcschemes/xcschememanagement.plist
19 | larch.egg-info*
20 | wheelhouse*
21 | Icon
22 | *.graffle
23 | src/larch.py
24 |
25 | local/*
26 |
27 | src/sandbox/sandbox.py
28 |
29 | py/version/__init__.py
30 |
31 | elm_swig_external.h
32 |
33 | src/swig/elmcore_wrap.cpp
34 |
35 | src/swig/elmcore_wrap.h
36 |
37 | src/.DS_Store
38 |
39 | .DS_Store
40 |
41 | **.ipynb_checkpoints
42 | pingpong*.bat
43 | gemm.c
44 | .idea/**/workspace.xml
45 | v4/.idea/**/workspace.xml
46 | v4/.idea/**/tasks.xml
47 | gemv.c
48 | nl_utility.c
49 | elementwise.c
50 | nl_prob.c
51 | lastfailed
52 | v4/cython_debug
53 | vcs.xml
54 | sandbook.ipynb
55 | *.c
56 | cython_debug
57 | larch4.egg-info
58 | larch/model/*.html
59 | larch/**/*.html
60 | Icon?
61 |
62 |
63 | docs/.doctrees
64 | conda-build
65 | *.cp37-win_amd64.pyd
66 | notebooks/_sharrow_cache_
67 | book/_build
68 | book/api/generated
69 | book/example/_sharrow_cache_
70 | latent-class-example-report.html
71 | doc/build
72 | sandbox.py
73 | /LARCH_VERSION.txt
74 | exampville_dest_choice.html
75 | exampville_mode_choice.html
76 | logsums.zarr.zip
77 |
--------------------------------------------------------------------------------
/larch/util/timesize.py:
--------------------------------------------------------------------------------
1 | import time
2 | from datetime import datetime
3 |
4 | def timesize_single(t):
5 | if t<60:
6 | return f"{t:.2f}s"
7 | elif t<3600:
8 | return f"{t/60:.2f}m"
9 | elif t<86400:
10 | return f"{t/3600:.2f}h"
11 | else:
12 | return f"{t/86400:.2f}d"
13 |
14 |
15 | def timesize_stack(t):
16 | if t<60:
17 | return f"{t:.2f}s"
18 | elif t<3600:
19 | return f"{t//60:.0f}m {timesize_stack(t%60)}"
20 | elif t<86400:
21 | return f"{t//3600:.0f}h {timesize_stack(t%3600)}"
22 | else:
23 | return f"{t//86400:.0f}d {timesize_stack(t%86400)}"
24 |
25 |
26 |
27 | class Seconds(float):
28 |
29 | def __repr__(self):
30 | t = self
31 | if t < 60:
32 | return f"{t:.2f}s"
33 | elif t < 3600:
34 | return f"{t//60:.0f}m {timesize_stack(t%60)}"
35 | elif t < 86400:
36 | return f"{t//3600:.0f}h {timesize_stack(t%3600)}"
37 | else:
38 | return f"{t//86400:.0f}d {timesize_stack(t%86400)}"
39 |
40 |
41 | class Timer:
42 |
43 | def __init__(self):
44 | self.restart()
45 |
46 | def restart(self):
47 | self._starttime = datetime.now()
48 | self._stoptime = None
49 | return self._starttime
50 |
51 | def start(self):
52 | return self._starttime
53 |
54 | def stop(self):
55 | self._stoptime = datetime.now()
56 | return self._stoptime
57 |
58 | def elapsed(self):
59 | if self._stoptime:
60 | return (self._stoptime - self._starttime)
61 | else:
62 | return (datetime.now() - self._starttime)
63 |
64 | def __enter__(self):
65 | self.restart()
66 | return self
67 |
68 | def __exit__(self, exc_type, exc_val, exc_tb):
69 | self.stop()
70 |
--------------------------------------------------------------------------------
/larch/doc/model.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | =======================
4 | Models
5 | =======================
6 |
7 | A :class:`Model` is the core object used to represent a discrete choice model.
8 |
9 | .. autoclass:: Model
10 |
11 |
12 | Utility Function Definition
13 | ---------------------------
14 |
15 | Note that these function definitions act like *properties* of
16 | the Model object, instead of methods on Model objects.
17 |
18 | .. autoattribute:: Model.utility_ca
19 |
20 | .. autoattribute:: Model.utility_co
21 |
22 | .. autoattribute:: Model.quantity_ca
23 |
24 |
25 | Parameter Manipulation
26 | ----------------------
27 |
28 | .. automethod:: Model.set_value
29 |
30 | .. automethod:: Model.lock_value
31 |
32 | .. automethod:: Model.set_values
33 |
34 |
35 | Estimation and Application
36 | --------------------------
37 |
38 | .. automethod:: Model.load_data
39 |
40 | .. automethod:: Model.maximize_loglike
41 |
42 | .. automethod:: Model.calculate_parameter_covariance
43 |
44 | .. automethod:: Model.estimate
45 |
46 |
47 | Scikit-Learn Interface
48 | ----------------------
49 |
50 | .. automethod:: Model.fit
51 |
52 | .. automethod:: Model.predict
53 |
54 | .. automethod:: Model.predict_proba
55 |
56 |
57 | Reporting and Outputs
58 | ---------------------
59 |
60 | .. automethod:: Model.parameter_summary
61 |
62 | .. automethod:: Model.utility_functions
63 |
64 | .. automethod:: Model.estimation_statistics
65 |
66 |
67 | Visualization Tools
68 | -------------------
69 |
70 | .. automethod:: Model.distribution_on_idca_variable
71 |
72 | .. automethod:: Model.distribution_on_idco_variable
73 |
--------------------------------------------------------------------------------
/.idea/Larch.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/book/_toc.yml:
--------------------------------------------------------------------------------
1 | # Table of contents
2 | # Learn more at https://jupyterbook.org/customize/toc.html
3 |
4 | format: jb-book
5 | root: intro
6 | parts:
7 | - caption: Users Guide
8 | maxdepth: 1
9 | chapters:
10 | - file: installation
11 | - file: user-guide/choice-models
12 | sections:
13 | - file: user-guide/data-fundamentals
14 | title: Data Fundamentals
15 | - file: user-guide/linear-funcs
16 | - file: user-guide/machine-learning
17 | - file: bibliography
18 |
19 | - caption: API Reference
20 | maxdepth: 1
21 | chapters:
22 | - file: api/~data
23 | sections:
24 | - file: api/dataset
25 | - file: api/datatree
26 | - file: api/dataframes
27 | - file: api/~models
28 | sections:
29 | - file: api/model
30 | - file: api/linear
31 | - file: api/nesting
32 | - file: api/modelgroup
33 |
34 | - caption: Examples
35 | chapters:
36 | - file: example/mtc
37 | title: MTC Mode Choice
38 | - file: example/swissmetro
39 | title: Swissmetro
40 | sections:
41 | - glob: example/1*
42 | - file: example/exampville
43 | title: Exampville
44 | - file: example/itinerary
45 | title: Itinerary Choice
46 | - file: example/legacy
47 | title: Legacy Examples
48 | sections:
49 | - file: example/legacy/mtc
50 | sections:
51 | - glob: example/legacy/0*
52 | - file: example/legacy/swissmetro
53 | sections:
54 | - glob: example/legacy/1*
55 | - file: example/legacy/itinerary
56 | sections:
57 | - glob: example/legacy/3*
58 |
--------------------------------------------------------------------------------
/tests/test_nl_1.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from pytest import approx, raises
3 | import larch
4 | from .test_regressions import *
5 |
6 | @pytest.fixture(scope="module")
7 | def simple_nl():
8 | m = larch.example(1, legacy=True)
9 | mot = m.graph.new_node(parameter="MuSR", children=[2, 3])
10 | m.set_values(**{
11 | 'ASC_BIKE': -2.369583417234141,
12 | 'ASC_SR2': -2.1003772176440494,
13 | 'ASC_SR3P': -3.1652310001168176,
14 | 'ASC_TRAN': -0.6716514097035154,
15 | 'ASC_WALK': -0.20573835594484602,
16 | 'MuSR': 0.656152401524297,
17 | 'hhinc#2': -0.0018493368570253318,
18 | 'hhinc#3': -0.0005878635612891558,
19 | 'hhinc#4': -0.0051673845801924615,
20 | 'hhinc#5': -0.012777495307881156,
21 | 'hhinc#6': -0.00967649032713631,
22 | 'totcost': -0.004808452908429623,
23 | 'tottime': -0.05107162036470516,
24 | })
25 | m.load_data()
26 | return m
27 |
28 |
29 | def test_loglike(simple_nl):
30 | assert simple_nl.loglike() == approx(-3623.8414801535305)
31 |
32 | def test_compute_utility_array(simple_nl, dataframe_regression):
33 | arr = simple_nl.utility()
34 | dataframe_regression.check(
35 | np.concatenate([
36 | arr[:3],
37 | arr[-3:],
38 | ])
39 | )
40 |
41 | def test_compute_probability_array(simple_nl, dataframe_regression):
42 | arr = simple_nl.probability()
43 | dataframe_regression.check(
44 | np.concatenate([
45 | arr[:3],
46 | arr[-3:],
47 | ])
48 | )
49 |
50 | def test_compute_exputility_array(simple_nl):
51 | with raises(NotImplementedError):
52 | simple_nl.exputility()
53 |
54 | def test_compute_covariance(simple_nl, dataframe_regression):
55 | simple_nl.calculate_parameter_covariance()
56 | dataframe_regression.check(simple_nl.pf)
57 |
--------------------------------------------------------------------------------
/larch/doc/compiling.rst:
--------------------------------------------------------------------------------
1 |
2 |
3 | .. _compiling:
4 |
5 | Compiling from Source
6 | =====================
7 |
8 | It is highly recommended that you not compile Larch from the source code, but instead
9 | just use the pre-built packages available through conda.
10 |
11 | If you do attempt to compile from source and have some trouble, here are some tips:
12 |
13 |
14 | MacOS
15 | -----
16 |
17 | - You will find it easier to use *clang* and *openmp* from conda, but you probably
18 | also need the plain XCode installed (get it from the Mac App Store).
19 |
20 | .. code-block:: console
21 |
22 | xcode-select --install
23 | conda install clangdev llvmdev openmp -c conda-forge
24 |
25 | - If you encounter `fatal error: 'stdio.h' file not found` when compiling,
26 | you may need to update your SDK headers. `See a discussion here
27 | `_
28 | or skip to the solution:
29 |
30 | .. code-block:: console
31 |
32 | open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg
33 |
34 |
35 | Windows
36 | -------
37 |
38 | - Don't.
39 |
40 | - The Windows build for Larch is generated automatically after successful testing on
41 | *Appveyor*. If you want to build Larch for Windows yourself, the first step is to
42 | look deep inside your soul and ask yourself if it's really worth giving up that much
43 | of your life to do so. If after doing that you still want to proceed, you can try
44 | to follow along with the build instructions from *appveyor.yml* in the Larch github
45 | repository.
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/larch/util/cache_requests.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import appdirs
4 | import joblib
5 | import requests
6 |
7 | cache_dir = None
8 | memory = None
9 |
10 | def set_cache_dir(location=None, compress=True, verbose=0, **kwargs):
11 | """
12 | Set up a cache directory for use with requests.
13 |
14 | Parameters
15 | ----------
16 | location: str or None or False
17 | The path of the base directory to use as a data store
18 | or None or False. If None, a default directory is created
19 | using appdirs.user_cache_dir.
20 | If False is given, no caching is done and
21 | the Memory object is completely transparent.
22 |
23 | compress: boolean, or integer, optional
24 | Whether to zip the stored data on disk. If an integer is
25 | given, it should be between 1 and 9, and sets the amount
26 | of compression.
27 |
28 | verbose: int, optional
29 | Verbosity flag, controls the debug messages that are issued
30 | as functions are evaluated.
31 |
32 | bytes_limit: int, optional
33 | Limit in bytes of the size of the cache.
34 |
35 | """
36 | global memory, cache_dir
37 |
38 | if location is None:
39 | location = appdirs.user_cache_dir('cached_requests')
40 |
41 | if location is False:
42 | location = None
43 |
44 | memory = joblib.Memory(location, compress=compress, verbose=verbose, **kwargs)
45 |
46 | make_cache = (
47 | (requests, 'get'),
48 | (requests, 'post'),
49 | )
50 |
51 | for module, func_name in make_cache:
52 | try:
53 | func = getattr(module, f"_{func_name}_orig")
54 | except AttributeError:
55 | func = getattr(module, func_name)
56 | setattr(module, f"_{func_name}_orig", func)
57 | setattr(module, func_name, memory.cache(func))
58 |
59 |
60 |
61 |
62 |
63 | set_cache_dir()
64 |
65 |
66 |
--------------------------------------------------------------------------------
/larch/util/rate_limiter.py:
--------------------------------------------------------------------------------
1 |
2 | # This file is under the Creative Commons Attribution Share Alike license
3 | # https://creativecommons.org/licenses/by-sa/3.0/
4 | # original source:
5 | # https://stackoverflow.com/questions/20643184/using-python-threads-to-make-thousands-of-calls-to-a-slow-api-with-a-rate-limit
6 |
7 | from collections.abc import Iterator
8 | from threading import Lock
9 | import time
10 | import functools
11 |
12 |
13 | class BlockingRateLimiter(Iterator):
14 | """Iterator that yields a value at most once every 'interval' seconds."""
15 | def __init__(self, interval):
16 | self.lock = Lock()
17 | self.interval = interval
18 | self.next_yield = 0
19 |
20 | def __next__(self):
21 | with self.lock:
22 | t = time.monotonic()
23 | if t < self.next_yield:
24 | time.sleep(self.next_yield - t)
25 | t = time.monotonic()
26 | self.next_yield = t + self.interval
27 |
28 |
29 | class NonBlockingRateLimiter():
30 | def __init__(self, interval):
31 | self.lock = Lock()
32 | self.interval = interval
33 | self.next_greenlight = 0
34 |
35 | def __call__(self, fn):
36 | @functools.wraps(fn)
37 | def decorated(*args, **kwargs):
38 | t = time.monotonic()
39 | if t >= self.next_greenlight:
40 | with self.lock:
41 | self.next_greenlight = t + self.interval
42 | fn(*args, **kwargs)
43 | return decorated
44 |
45 | def __bool__(self):
46 | t = time.monotonic()
47 | if t >= self.next_greenlight:
48 | with self.lock:
49 | self.next_greenlight = t + self.interval
50 | return True
51 | return False
52 |
53 |
54 | _global_rate_limiters = {}
55 |
56 | def GlobalRateLimiter(tag, interval=1, wait_now=True):
57 | global _global_rate_limiters
58 | if tag not in _global_rate_limiters:
59 | _global_rate_limiters[tag] = RateLimiter(interval)
60 | if wait_now:
61 | return next(_global_rate_limiters[tag])
62 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/larch/util/naming.py:
--------------------------------------------------------------------------------
1 | import warnings
2 | import keyword
3 | import re
4 |
5 |
6 | class NotAPythonIdentifier(Warning):
7 | pass
8 |
9 |
10 | def make_valid_identifier(x, suppress_warnings=False):
11 | x = str(x)
12 | if keyword.iskeyword(x):
13 | y = "_" + x
14 | if not suppress_warnings:
15 | warnings.warn("name {0} is a python keyword, converting to {1}".format(x, y), stacklevel=2)
16 | else:
17 | y = x
18 | replacer = re.compile('(\W+)')
19 | y = replacer.sub("_", y.strip())
20 | if not y.isidentifier():
21 | y = "_" + y
22 | if y != x:
23 | if not suppress_warnings:
24 | warnings.warn("name {0} is not a valid python identifier, converting to {1}".format(x, y), stacklevel=2,
25 | category=NotAPythonIdentifier)
26 | return y
27 |
28 |
29 |
30 | def valid_identifier_or_parenthized_string(x, leading_dot=True):
31 | x = str(x)
32 | if x.isidentifier():
33 | if leading_dot:
34 | return "."+x
35 | return x
36 | else:
37 | if "'" in x and '"' not in x:
38 | return '("{}")'.format(x)
39 | if "'" not in x and '"' in x:
40 | return "('{}')".format(x)
41 | raise NotImplementedError("cannot handle strings with both quote types")
42 |
43 |
44 |
45 | def parenthize(x, signs_qualify=False):
46 | """Wrap a string in parenthesis if needed for unambiguous clarity.
47 |
48 | Parameters
49 | ----------
50 | x : str
51 | The string to wrap
52 | signs_qualify : bool
53 | If True, a leading + or - on a number triggers the parenthesis (defaults False)
54 |
55 | """
56 | x = str(x).strip()
57 | replacer = re.compile('(\W+)')
58 | if replacer.search(x):
59 | if signs_qualify:
60 | numeric = re.compile('^(([1-9][0-9]*\.?[0-9]*)|(\.[0-9]+))([Ee][+-]?[0-9]+)?\Z')
61 | else:
62 | numeric = re.compile('^[+-]?(([1-9][0-9]*\.?[0-9]*)|(\.[0-9]+))([Ee][+-]?[0-9]+)?\Z')
63 | if numeric.search(x):
64 | return x
65 | return "({})".format(x)
66 | return x
67 |
--------------------------------------------------------------------------------
/larch/numba/fast_mapping.py:
--------------------------------------------------------------------------------
1 | import numba as nb
2 | import numpy as np
3 | import pandas as pd
4 |
5 |
6 | @nb.njit
7 | def _fast_map(fm, target, dtype=np.int32):
8 | out = np.zeros(len(target), dtype=dtype)
9 | for n in range(target.size):
10 | out[n] = fm[target[n]]
11 | return out
12 |
13 |
14 | class FastMapping:
15 |
16 | def __init__(self, source, to_range=np.int64):
17 | if isinstance(source, pd.Series):
18 | m = nb.typed.Dict.empty(
19 | key_type=nb.from_dtype(source.index.dtype),
20 | value_type=nb.from_dtype(source.dtype),
21 | )
22 | for k, v in source.items():
23 | m[k] = v
24 | self._in_dtype = source.index.dtype
25 | self._out_dtype = source.dtype
26 | self._mapper = m
27 | elif to_range:
28 | m = nb.typed.Dict.empty(
29 | key_type=nb.from_dtype(source.dtype),
30 | value_type=nb.from_dtype(to_range),
31 | )
32 | for v, k in enumerate(source):
33 | m[k] = v
34 | self._in_dtype = source.dtype
35 | self._out_dtype = to_range
36 | self._mapper = m
37 | else:
38 | raise ValueError("invalid input")
39 |
40 | def __len__(self):
41 | return len(self._mapper)
42 |
43 | def __contains__(self, item):
44 | return item in self._mapper
45 |
46 | def __getitem__(self, item):
47 | return self._mapper[item]
48 |
49 | def apply_to(self, target):
50 | if isinstance(target, pd.Series):
51 | return pd.Series(
52 | _fast_map(self._mapper, target.astype(self._in_dtype).to_numpy(), dtype=self._out_dtype),
53 | index=target.index,
54 | )
55 | return _fast_map(self._mapper, np.asarray(target, dtype=self._in_dtype), dtype=self._out_dtype)
56 |
57 |
--------------------------------------------------------------------------------
/larch/util/signal_dict.py:
--------------------------------------------------------------------------------
1 |
2 | from itertools import chain
3 | _RaiseKeyError = object() # singleton for no-default behavior
4 |
5 | class SignalDict(dict): # dicts take a mapping or iterable as their optional first argument
6 | __slots__ = ('_SignalDict__getitem_callback',
7 | '_SignalDict__setitem_callback',
8 | '_SignalDict__delitem_callback',) # no __dict__ - that would be redundant
9 | @staticmethod # because this doesn't make sense as a global function.
10 | def _process_args(mapping=(), **kwargs):
11 | if hasattr(mapping, 'items'):
12 | mapping = getattr(mapping, 'items')()
13 | return ((k, v) for k, v in chain(mapping, getattr(kwargs, 'items')()))
14 | def __init__(self, getitem_callback, setitem_callback, delitem_callback, mapping=(), **kwargs):
15 | self.__getitem_callback = getitem_callback
16 | self.__setitem_callback = setitem_callback
17 | self.__delitem_callback = delitem_callback
18 | super().__init__(self._process_args(mapping, **kwargs))
19 | def __getitem__(self, k):
20 | ret = super().__getitem__(k)
21 | self.__getitem_callback(k)
22 | return ret
23 | def __setitem__(self, k, v):
24 | ret = super().__setitem__(k, v)
25 | self.__setitem_callback(k,v)
26 | return ret
27 | def __delitem__(self, k):
28 | ret = super().__delitem__(k)
29 | self.__delitem_callback(k)
30 | return ret
31 | def get(self, k, default=None):
32 | return super().get(k, default)
33 | def setdefault(self, k, default=None):
34 | return super().setdefault(k, default)
35 | def pop(self, k, v=_RaiseKeyError):
36 | if v is _RaiseKeyError:
37 | return super().pop(k)
38 | return super().pop(k, v)
39 | def update(self, mapping=(), **kwargs):
40 | super().update(self._process_args(mapping, **kwargs))
41 | def __contains__(self, k):
42 | return super().__contains__(k)
43 | def copy(self): # don't delegate w/ super - dict.copy() -> dict :(
44 | return type(self)(self)
45 | def __repr__(self):
46 | return '{0}({1})'.format(type(self).__name__, super().__repr__())
47 |
--------------------------------------------------------------------------------
/larch/util/activitysim/auto_ownership.py:
--------------------------------------------------------------------------------
1 | import os
2 | import numpy as np
3 | import pandas as pd
4 | import yaml
5 | from typing import Collection
6 | from .. import Dict
7 |
8 | from .general import (
9 | remove_apostrophes,
10 | apply_coefficients,
11 | simple_simulate_data,
12 | dict_of_linear_utility_from_spec,
13 | )
14 | from ... import Model, DataFrames, P, X
15 |
16 |
17 |
18 | def auto_ownership_model(
19 | name="auto_ownership",
20 | edb_directory="output/estimation_data_bundle/{name}/",
21 | return_data=False,
22 | ):
23 | data = simple_simulate_data(
24 | name=name,
25 | edb_directory=edb_directory,
26 | values_index_col="household_id",
27 | )
28 | coefficients = data.coefficients
29 | # coef_template = data.coef_template # not used
30 | spec = data.spec
31 | chooser_data = data.chooser_data
32 | settings = data.settings
33 |
34 | altnames = list(spec.columns[3:])
35 | altcodes = range(len(altnames))
36 |
37 | chooser_data = remove_apostrophes(chooser_data)
38 | chooser_data.fillna(0, inplace=True)
39 |
40 | # Remove choosers with invalid observed choice
41 | chooser_data = chooser_data[chooser_data['override_choice'] >= 0]
42 |
43 | m = Model()
44 | # One of the alternatives is coded as 0, so
45 | # we need to explicitly initialize the MNL nesting graph
46 | # and set to root_id to a value other than zero.
47 | m.initialize_graph(alternative_codes=altcodes, root_id=99)
48 |
49 | m.utility_co = dict_of_linear_utility_from_spec(
50 | spec, 'Label', dict(zip(altnames, altcodes)),
51 | )
52 |
53 | apply_coefficients(coefficients, m)
54 |
55 | d = DataFrames(
56 | co=chooser_data,
57 | av=True,
58 | alt_codes=altcodes,
59 | alt_names=altnames,
60 | )
61 |
62 | m.dataservice = d
63 | m.choice_co_code = 'override_choice'
64 |
65 | if return_data:
66 | return m, Dict(
67 | edb_directory=data.edb_directory,
68 | chooser_data=chooser_data,
69 | coefficients=coefficients,
70 | spec=spec,
71 | altnames=altnames,
72 | altcodes=altcodes,
73 | )
74 |
75 | return m
--------------------------------------------------------------------------------
/tools/appveyor/run_with_env.cmd:
--------------------------------------------------------------------------------
1 | :: To build extensions for 64 bit Python 3, we need to configure environment
2 | :: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of:
3 | :: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1)
4 | ::
5 | :: To build extensions for 64 bit Python 2, we need to configure environment
6 | :: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of:
7 | :: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0)
8 | ::
9 | :: 32 bit builds do not require specific environment configurations.
10 | ::
11 | :: Note: this script needs to be run with the /E:ON and /V:ON flags for the
12 | :: cmd interpreter, at least for (SDK v7.0)
13 | ::
14 | :: More details at:
15 | :: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows
16 | :: http://stackoverflow.com/a/13751649/163740
17 | ::
18 | :: Author: Olivier Grisel
19 | :: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/
20 | @ECHO OFF
21 |
22 | SET COMMAND_TO_RUN=%*
23 | SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows
24 |
25 | SET MAJOR_PYTHON_VERSION="%PYTHON_VERSION:~0,1%"
26 | IF %MAJOR_PYTHON_VERSION% == "2" (
27 | SET WINDOWS_SDK_VERSION="v7.0"
28 | ) ELSE IF %MAJOR_PYTHON_VERSION% == "3" (
29 | SET WINDOWS_SDK_VERSION="v7.1"
30 | ) ELSE (
31 | ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%"
32 | EXIT 1
33 | )
34 |
35 | IF "%PLATFORM%"=="X64" (
36 | ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture
37 | SET DISTUTILS_USE_SDK=1
38 | SET MSSdk=1
39 | "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION%
40 | "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release
41 | ECHO Executing: %COMMAND_TO_RUN%
42 | call %COMMAND_TO_RUN% || EXIT 1
43 | ) ELSE (
44 | ECHO Using default MSVC build environment for 32 bit architecture
45 | ECHO Executing: %COMMAND_TO_RUN%
46 | call %COMMAND_TO_RUN% || EXIT 1
47 | )
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | larch
2 | =====
3 |
4 | **We've moved! Find version 6 and up at [https://github.com/driftlesslabs/larch].**
5 |
6 | .. image:: https://img.shields.io/conda/v/conda-forge/larch
7 | :target: https://anaconda.org/conda-forge/larch
8 | :class: statusbadge
9 |
10 | .. image:: https://img.shields.io/conda/dn/conda-forge/larch
11 | :target: https://anaconda.org/conda-forge/larch
12 | :class: statusbadge
13 |
14 | .. image:: https://img.shields.io/badge/source-github-yellow.svg
15 | :target: https://github.com/jpn--/larch
16 | :class: statusbadge
17 |
18 | .. image:: https://img.shields.io/conda/l/conda-forge/larch
19 | :target: https://github.com/jpn--/larch/blob/master/LICENSE
20 | :class: statusbadge
21 |
22 | **Larch**: the logit architect
23 |
24 | This is a tool for the estimation and application of logit-based discrete choice models.
25 | It is designed to integrate with NumPy and facilitate fast processing of linear models.
26 | If you want to estimate *non-linear* models, try `Biogeme `_,
27 | which is more flexible in form and can be used for almost any model structure.
28 | If you don't know what the difference is, you probably want to start with linear models.
29 |
30 | Larch is undergoing a transformation, with a new computational architecture
31 | that can significantly improve performance when working with large datasets.
32 | The new code relies on [numba](https://numba.pydata.org/),
33 | [xarray](https://xarray.pydata.org/en/stable/), and
34 | [sharrow](https://activitysim.github.io/sharrow) to enable super-fast estimation
35 | of choice models. Many (but not yet all) of the core features of Larch have been moved
36 | over to this new platform.
37 |
38 | You can still use the old version of Larch as normal, but to try out the new version
39 | just import `larch.numba` instead of larch itself.
40 |
41 | This project is very much under development. There are plenty of undocumented functions
42 | and features; use them at your own risk. Undocumented features may be non-functional,
43 | not rigorously tested, deprecated or removed without notice in a future version.
44 |
--------------------------------------------------------------------------------
/larch/scoring.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | import numpy
4 | import pandas
5 |
6 | def tiny_perturbation(arr):
7 | fr = numpy.frexp(arr)
8 | perturb = numpy.random.random(fr[0].shape) * 1e-12
9 | return numpy.ldexp(fr[0]+perturb, fr[1])
10 |
11 |
12 | def rank_within_cases(df, values, groupdef, output_col=None, tiny_perturb=False):
13 | if tiny_perturb:
14 | x = tiny_perturbation(df[values])
15 | else:
16 | x = df[values]
17 |
18 | result = x.groupby(df[groupdef]).rank("first", ascending=False)
19 |
20 | if output_col is not None:
21 | df[output_col] = result
22 |
23 | return result
24 |
25 |
26 | def probability_to_rank(arr):
27 | """Convert a probability array to ranks.
28 |
29 | Parameters
30 | ----------
31 | arr: array-like
32 | The probability array to convert. Each row is a case observation
33 | and should have probability that sums to 1.
34 |
35 | Returns
36 | -------
37 | ranks
38 | An array or DataFrame (as arr) with ranks.
39 | """
40 |
41 | temp = arr.argsort(axis=-1)
42 | ranks = numpy.empty_like(temp)
43 | seq = numpy.arange(1,arr.shape[1]+1)[::-1]
44 | for i in range(arr.shape[0]):
45 | ranks[i,temp[i]] = seq
46 |
47 | if isinstance(arr, pandas.DataFrame):
48 | return pandas.DataFrame(ranks, columns=arr.columns, index=arr.index)
49 | else:
50 | return ranks
51 |
52 |
53 | def top_k_accuracy(ranks, choices, k=1):
54 | """
55 | Compute top-N accuracy.
56 |
57 | Parameters
58 | ----------
59 | ranks: array-like
60 | Ranked alternatives. The best alternative in a row should be ranked 1,
61 | the next 2, and so on.
62 | choices
63 | A one-hot encoded or weighted choice array.
64 | k : int or iterable of ints
65 | k
66 |
67 | Returns
68 | -------
69 | float
70 | Fraction of rows where the chosen alternative is ranked at k or better.
71 | """
72 | if isinstance(k, (int, numpy.integer)):
73 | ranks = numpy.asarray(ranks)
74 | choices = numpy.asarray(choices)
75 | _1 = (ranks[choices > 0] <= k)
76 | _2 = (choices[choices > 0])
77 | return (_1*_2).sum(axis=None) / (choices).sum(axis=None)
78 |
79 | results = {}
80 | for k_ in k:
81 | results[k_] = top_k_accuracy(ranks, choices, k=k_)
82 |
83 | return pandas.Series(results)
84 |
85 |
--------------------------------------------------------------------------------
/larch/doc/_static/elm_doc_style.css:
--------------------------------------------------------------------------------
1 | .fa-home::before, .icon-home::before {
2 | display: inline-block !important;
3 | width: 16px !important;
4 | height: 16px !important;
5 | content: "" !important;
6 | background: url("../_static/larch_favicon.png") no-repeat 0 2px !important;
7 | background-size: 100% !important;
8 |
9 | }
10 |
11 | #mainlogo
12 | {
13 | -ms-transform-origin: left 75%; /* IE 9 */
14 | -webkit-transform-origin: left 75%; /* Chrome, Safari, Opera */
15 | transform-origin: left 75%;
16 | -ms-transform: scale(0.5,0.5); /* IE 9 */
17 | -webkit-transform: scale(0.5,0.5); /* Chrome, Safari, Opera */
18 | transform: scale(0.5,0.5);
19 | float:none;
20 | margin: 0 0 0 0;
21 | }
22 |
23 | img.logo
24 | {
25 | width:200px;
26 | }
27 |
28 | div.body h1
29 | {
30 | font-family: 'Roboto Slab', sans-serif;
31 | font-size: 150%;
32 | font-weight: 700;
33 | }
34 |
35 | div.body h2
36 | {
37 | font-family: 'Roboto Slab', sans-serif;
38 | font-size: 130%;
39 | font-weight: 400;
40 | }
41 |
42 | div.body h3, div.body h4, div.body h5, div.body h6
43 | {
44 | font-family: 'Roboto Slab', sans-serif;
45 | font-weight: 100;
46 |
47 | }
48 |
49 | div.body h3 { font-size: 110%; padding-left: 50px; }
50 | div.body h4 { font-size: 100%; padding-left: 65px; }
51 | div.body h5 { font-size: 90%; padding-left: 80px; }
52 | div.body h6 { font-size: 80%; padding-left: 95px; }
53 |
54 | .wy-side-nav-search
55 | {
56 | background-color: rgb(101,0,122) !important;
57 | }
58 |
59 |
60 | .wy-breadcrumbs li:first-child::before
61 | {
62 | display: inline-block !important;
63 | width: 16px !important;
64 | height: 16px !important;
65 | margin-right:5px !important;
66 | content: "" !important;
67 | background: url("../_static/larch_favicon.png") no-repeat 0 2px !important;
68 | background-size: 100% !important;
69 | }
70 |
71 | img.htmlrendering
72 | {
73 | box-shadow: 1px 2px 3px 2px rgba(0, 0, 0, 0.3);
74 | border-radius: 3px;
75 |
76 | }
77 | /*
78 | tt {
79 | font-family: 'Source Code Pro', monospace;
80 | font-size: 80%;
81 | }
82 |
83 | tt.descname {
84 | font-weight: 700;
85 | font-size: 90%;
86 | }
87 |
88 | tt.descclassname {
89 | font-weight: 300;
90 | font-size: 90%;
91 | }
92 | */
93 |
--------------------------------------------------------------------------------
/tests/test_data_arrays.py:
--------------------------------------------------------------------------------
1 | from pytest import approx, importorskip
2 | import numpy as np
3 | sh = importorskip("sharrow")
4 | import larch.numba as lx
5 | import larch.numba.data_arrays as lxd
6 | from larch.dataset.dim_names import CASEID, ALTID
7 |
8 | def test_weighted():
9 | m = lx.example(1, legacy=True)
10 | m.choice_ca_var = 'chose'
11 | m.weight_co_var = 'hhinc+100'
12 | ds = lxd.to_dataset(m.dataservice)
13 | y, flows = lxd.prepare_data(ds, m)
14 | assert isinstance(y, sh.Dataset)
15 | assert sorted(y.coords.keys()) == sorted([CASEID, 'alt_names', ALTID, 'var_co', 'var_ca'])
16 | assert list(y.keys()) == ['co', 'ca', 'ch', 'wt', 'av']
17 | assert y.dims == {CASEID: 5029, ALTID: 6, 'var_co': 1, 'var_ca': 2}
18 | assert y.wt.values[:3] == approx(np.array([142.5, 117.5, 112.5], dtype=np.float32))
19 |
20 |
21 | def test_choice_code():
22 | m = lx.example(1, legacy=True)
23 | m.choice_co_code = 'chosen_alt'
24 | m.weight_co_var = 'hhinc+100'
25 | chosen_alt = np.where(m.dataservice.data_ca['chose'].unstack())[1] + 1
26 | ds = lxd.to_dataset(m.dataservice)
27 | ds['chosen_alt'] = sh.DataArray(
28 | chosen_alt,
29 | dims=['_caseid_',],
30 | )
31 | y, flows = lxd.prepare_data(ds, m)
32 | assert isinstance(y, sh.Dataset)
33 | assert sorted(y.coords.keys()) == sorted([CASEID, 'alt_names', ALTID, 'var_co', 'var_ca'])
34 | assert list(y.keys()) == ['co', 'ca', 'ch', 'wt', 'av']
35 | assert y.dims == {CASEID: 5029, ALTID: 6, 'var_co': 1, 'var_ca': 2}
36 | assert y.wt.values[:3] == approx(np.array([142.5, 117.5, 112.5], dtype=np.float32))
37 |
38 |
39 | def test_shared_data():
40 | m = lx.example(1, legacy=True)
41 | m.choice_ca_var = 'chose'
42 | m.weight_co_var = 'hhinc+100'
43 | ds = lxd.to_dataset(m.dataservice)
44 | pool = lx.DataTree(base=ds)
45 | y, flows = lxd.prepare_data(pool, m)
46 | assert isinstance(y, sh.Dataset)
47 | assert sorted(y.coords.keys()) == sorted([CASEID, 'alt_names', ALTID, 'var_co', 'var_ca'])
48 | assert list(y.keys()) == ['co', 'ca', 'ch', 'wt', 'av']
49 | assert y.dims == {CASEID: 5029, ALTID: 6, 'var_co': 1, 'var_ca': 2}
50 | assert y.wt.values[:3] == approx(np.array([142.5, 117.5, 112.5], dtype=np.float32))
51 |
--------------------------------------------------------------------------------
/tools/gitversion.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Get the version string from the git tag."""
3 |
4 | from __future__ import print_function
5 |
6 | import subprocess
7 |
8 |
9 | __all__ = ['get_gitversion']
10 |
11 |
12 | TEMPLATES = {
13 | 'python': """\
14 | \"""Do not edit this file because it will be overwritten before packaging.
15 | The output of git describe was: {git_describe}
16 | \"""
17 | __version__ = '{git_tag_version}'""",
18 | 'cmake': """\
19 | # This file is automatically generated. Changes will be overwritten before packaging.
20 | set(GIT_DESCRIBE "{git_describe}")
21 | set(GIT_TAG_VERSION "{git_tag_version}")
22 | set(GIT_TAG_SOVERSION "{git_tag_soversion}")
23 | set(GIT_TAG_VERSION_MAJOR "{git_tag_version_major}")
24 | set(GIT_TAG_VERSION_MINOR "{git_tag_version_minor}")
25 | set(GIT_TAG_VERSION_PATCH "{git_tag_version_patch}")"""}
26 |
27 |
28 | def get_gitversion():
29 | """Return a conda-compatible version string derived from git describe --tags."""
30 | git_describe = subprocess.check_output(['git', 'describe', '--tags']).strip()
31 | version_words = git_describe.decode('utf-8').strip().split('-')
32 | version = version_words[0]
33 | if len(version_words) > 1:
34 | version += '.post' + version_words[1]
35 | return version, git_describe
36 |
37 |
38 | def main():
39 | """Print the version derived from ``git describe --tags`` in a useful format."""
40 | from argparse import ArgumentParser
41 | parser = ArgumentParser('Determine version string from `git describe --tags`')
42 | parser.add_argument('output', choices=['plain', 'python', 'cmake'], default='plain', nargs='?',
43 | help='format of the output.')
44 | args = parser.parse_args()
45 | version, git_describe = get_gitversion()
46 | if args.output == 'plain':
47 | print(version)
48 | else:
49 | major, minor, patch = version.split('.', 2)
50 | print(TEMPLATES[args.output].format(
51 | git_describe=git_describe,
52 | git_tag_version=version,
53 | git_tag_soversion='.'.join([major, minor]),
54 | git_tag_version_major=major,
55 | git_tag_version_minor=minor,
56 | git_tag_version_patch=patch,
57 | ))
58 |
59 |
60 | if __name__ == '__main__':
61 | main()
--------------------------------------------------------------------------------
/larch/doc/dataservice.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | =======================
4 | DataService
5 | =======================
6 |
7 | Larch has two closely related interfaces to manage data: :class:`DataFrames` and
8 | :class:`DataService`. The former is a set of related data tables, while the
9 | latter is an interface that generates instances of the former. You can think
10 | of a :class:`DataService` as a place to get data, and :class:`DataFrames` as the
11 | data you get.
12 |
13 | In fact, :class:`DataFrames` is itself a subclass of :class:`DataService`,
14 | allowing you to pull subsets of the data you have stored, so you can use
15 | :class:`DataFrames` in both places.
16 |
17 | A Larch Model object will generally have a :class:`DataService` attached
18 | by the user, and then it will use that :class:`DataService` to automatically
19 | generate, format, and pre-process the particular :class:`DataFrames` it needs
20 | for analysis.
21 |
22 |
23 | .. py:class:: DataService
24 |
25 | An object that implements the :class:`DataService` interface must provide these
26 | methods.
27 |
28 | .. py:method:: make_dataframes(req_data, *, selector=None, float_dtype=numpy.float64)
29 |
30 | Create a DataFrames object that will satisfy a data request.
31 |
32 | :param req_data:
33 | The requested data. The keys for this dictionary may include {'ca', 'co',
34 | 'choice_ca', 'choice_co', 'weight_co', 'avail_ca', 'standardize'}.
35 | Currently, the keys {'choice_co_code', 'avail_co'} are not implemented and
36 | will raise an error.
37 | Other keys are silently ignored.
38 | :type req_data: Dict or str
39 | :param selector:
40 | If given, the selector filters the cases. This argument can only be given
41 | as a keyword argument.
42 | :type selector: array-like[bool] or slice, optional
43 | :param float_dtype:
44 | The dtype to use for all float-type arrays. Note that the availability
45 | arrays are always returned as int8 regardless of the float type.
46 | This argument can only be given
47 | as a keyword argument.
48 | :type float_dtype: dtype, default float64
49 | :rtype: DataFrames
50 | :return: This object should satisfy the request.
51 |
52 |
--------------------------------------------------------------------------------
/larch/data_services/h5/h5pod/id0a.py:
--------------------------------------------------------------------------------
1 |
2 | from .generic import *
3 |
4 | class H5Pod0A(H5Pod):
5 | """
6 | A HDF5 group node containing :ref:`id0a` format data.
7 |
8 | The 0A pod is conceptually similar to the CA pod, although cases share a single
9 | row of data instead of varying individually. Each array of data is stored as an array, where
10 | each array has one row of values attributable to all observations,
11 | and columns attributable to individual choice alternatives. The common use case is an OMX
12 | matrix file containing lookups, where the observations all share a single lookup.
13 |
14 | """
15 |
16 | def __init__(self, filename, n_cases=-1, n_alts=-1, *args, **kwargs):
17 | super().__init__(*args, filename=filename, **kwargs)
18 | self._n_cases = n_cases
19 | self._n_alts = n_alts
20 |
21 | @property
22 | def podtype(self):
23 | return 'id0a'
24 |
25 |
26 | def __getitem__(self, item):
27 |
28 | if isinstance(item, tuple) and len(item)>=2 and isinstance(item[-1], slice):
29 | names, slice_ = item[:-1], item[-1]
30 | else:
31 | names = item
32 | slice_ = None
33 |
34 | # convert a single name string to a one item list
35 | if isinstance(names, str):
36 | names = [names, ]
37 |
38 | dtype = numpy.float64
39 |
40 | result = numpy.zeros( [selector_len_for(slice_, self.shape[0]), *self.shape[1:], len(names)], dtype=dtype)
41 |
42 | for i, cmd in enumerate(names):
43 | temp = self._evaluate_single_item(cmd, None).squeeze()
44 | result[...,i] = temp[None,:]
45 | return result
46 |
47 | @property
48 | def shape(self):
49 | """The shape of the pod.
50 |
51 | """
52 | return (self._n_cases,) + self.metashape[1:]
53 |
54 | @property
55 | def metashape(self):
56 | """The shape of the underlying skims.
57 |
58 | """
59 | return (1, self._n_alts)
60 |
61 | def load_data_item(self, name, result, selector=None):
62 | """Load a slice of the pod arrays into an array in memory"""
63 | # convert a single name string to a one item list
64 |
65 | from ...general import _sqz_same
66 | _sqz_same(result.shape, [selector_len_for(selector, self.shape[0]), *self.shape[1:]])
67 |
68 | temp = self._evaluate_single_item(name, None).squeeze()
69 | if selector is not None:
70 | result[:] = temp[None, :]
71 | else:
72 | result[:] = temp[None, :]
73 | return result
74 |
75 |
--------------------------------------------------------------------------------
/tests/test_qmnl.py:
--------------------------------------------------------------------------------
1 | from larch.model import Model
2 | import numpy
3 | from pytest import approx
4 | import pandas
5 | import pytest
6 |
7 | def qmnl_straw_man_model_1():
8 |
9 | from larch.roles import P,X
10 |
11 | altcodes = (1, 2, 3, 4, 5, 6)
12 | from larch.data_services.examples import MTC
13 | dt = MTC()
14 |
15 | p = Model(parameters=[], alts=altcodes, dataservice=dt, graph=None)
16 |
17 |
18 | from larch.roles import P,X
19 |
20 | p.utility_ca = (
21 | + P('tottime') * X('tottime')
22 | + P('totcost') * X('totcost')
23 | )
24 |
25 | p.utility_co = {
26 | 2: (P('ASC#2') * X('1') + P('hhinc#2') * X('hhinc')),
27 | 3: (P('ASC#3') * X('1') + P('hhinc#3') * X('hhinc')),
28 | 4: (P('ASC#4') * X('1') + P('hhinc#4') * X('hhinc')),
29 | 5: (P('ASC#5') * X('1') + P('hhinc#5') * X('hhinc')),
30 | 6: (P('ASC#6') * X('1') + P('hhinc#6') * X('hhinc')),
31 | }
32 |
33 | p.quantity_ca = (
34 | + P("FakeSizeAlt") * X('altnum+1')
35 | + P("FakeSizeIvtt") * X('ivtt+1')
36 | )
37 |
38 | p.availability_var = '_avail_'
39 | p.choice_ca_var = '_choice_'
40 |
41 | p.load_data()
42 | return p
43 |
44 |
45 | def test_qmnl():
46 | pq = qmnl_straw_man_model_1()
47 | assert( pq.loglike() == approx(-8486.55377320886))
48 |
49 | dll = pq.d_loglike()
50 |
51 | names = ['ASC#2', 'ASC#5', 'hhinc#3', 'ASC#4', 'ASC#6', 'hhinc#6', 'ASC#3',
52 | 'hhinc#4', 'tottime', 'hhinc#2', 'hhinc#5', 'totcost', 'FakeSizeIvtt',
53 | 'FakeSizeAlt']
54 |
55 | correct = pandas.Series(
56 | [-6.76598075e+02, -4.43123432e+02, -6.73124640e+04,
57 | -4.91818432e+02, 3.97095316e+00, -1.38966274e+03,
58 | -1.16626503e+03, -3.06932152e+04, -4.87328610e+04,
59 | -4.02490548e+04, -2.72367637e+04, 1.45788603e+05,
60 | 8.69414603e+01, -8.69414603e+01],
61 | index = names)
62 |
63 | for n in names:
64 | assert (correct.loc[n] == approx(dll.loc[n]))
65 |
66 | @pytest.mark.skip(reason="test is for manual use to check multithreading")
67 | def test_saturate_cpu(howlong=15):
68 | pq = qmnl_straw_man_model_1()
69 | pq.initialize_graph(alternative_codes=[1,2,3,4,5,6])
70 | pq.graph.add_node(10, children=(5, 6), parameter='MU_nonmotor')
71 | pq.graph.add_node(11, children=(1, 2, 3), parameter='MU_car')
72 | assert( pq.loglike() == approx(-8486.55377320886))
73 |
74 | import time
75 | starttime = time.time()
76 | while time.time() < starttime + howlong:
77 | dll = pq.d_loglike()
78 |
--------------------------------------------------------------------------------
/larch/__init__.py:
--------------------------------------------------------------------------------
1 |
2 | __version__ = '5.7.2'
3 |
4 | from .util.interface_info import Info, ipython_status
5 | import sys
6 | from .util.styles import css
7 |
8 | info = Info('Larch', False, __version__)
9 | version = Info('Larch', False, __version__, minimal=True)
10 |
11 |
12 | def require_version(n):
13 | from . import __version__
14 | try:
15 | from packaging import version
16 | except:
17 | def int_from(x):
18 | import re
19 | nums = re.findall(r'\d+', x)
20 | if nums:
21 | return int(nums[0])
22 | return 0
23 | r = [int_from(i) for i in n.split(".")[:2]]
24 | v = [int_from(i) for i in __version__.split(".")[:2]]
25 | if v[0] > r[0]:
26 | return
27 | if v[0] < r[0]:
28 | raise ValueError("the installed larch is version {}".format(__version__))
29 | if len(r)>=2:
30 | if v[1] > r[1]:
31 | return
32 | if v[1] < r[1]:
33 | raise ValueError("the installed larch is version {}".format(__version__))
34 | if len(r)>=3:
35 | if v[2] > r[2]:
36 | return
37 | if v[2] < r[2]:
38 | raise ValueError("the installed larch is version {}".format(__version__))
39 | else:
40 | if version.parse(n) > version.parse(__version__):
41 | raise ValueError("the installed larch is version {}".format(__version__))
42 |
43 |
44 | if 'IPython' in ipython_status():
45 | from .util.display import display
46 | try:
47 | # from .util.styles import stylesheet
48 | # stylesheet()
49 | # display(info)
50 | pass
51 | except:
52 | # print(repr(info))
53 | jupyter_active = False
54 | else:
55 | jupyter_active = True
56 | else:
57 | jupyter_active = False
58 | # print(repr(info))
59 |
60 |
61 | from .roles import P, X, PX
62 | from .data_services import DataService
63 | from .omx import OMX
64 | from .data_services.dbf.dbf_reader import DBF
65 |
66 | from .model import Model
67 | from .dataframes import DataFrames
68 | from .dataset import Dataset, DataTree
69 |
70 | from .examples import example
71 | from .util import figures
72 | from .util.excel import ExcelWriter
73 |
74 | _doctest_mode_ = False
75 |
76 | _larch_self = sys.modules[__name__]
77 |
78 | from xmle import Reporter, NumberedCaption
79 | from xmle import load_metadata as read_metadata
80 | from .workspace import make_reporter
81 |
--------------------------------------------------------------------------------
/book/intro.md:
--------------------------------------------------------------------------------
1 | # Larch Documentation
2 |
3 | [](https://anaconda.org/conda-forge/larch)
4 | [](https://anaconda.org/conda-forge/larch)
5 | [](https://dev.azure.com/wire-paladin/larch/_build?definitionId=1&_a=summary&repositoryFilter=1&branchFilter=5%2C5%2C5%2C5%2C5%2C5)
6 |
7 | 🏆︁ Winner of the [AGIFORS 56th Annual Symposium Best Innovation award](http://agifors.org/Symposium).
8 |
9 | This documentation is for the Python interface for Larch. If this is your first go
10 | with Larch, or the first go on a new computer, you might want to start with
11 | [installation](larch-installation).
12 |
13 | **Are you ready for something shiny, new, and *fast*?** Larch is undergoing
14 | a transformation, with a new computational architecture
15 | that can significantly improve performance when working with large datasets.
16 | The old version of Larch used a carefully customized `DataFrames` object to
17 | organize several different aspects of discrete choice data.
18 | The new code uses a more standardized (although still enhanced) `xarray.Dataset`
19 | interface for data, and relies on [numba](https://numba.pydata.org/),
20 | [xarray](https://xarray.pydata.org/en/stable/), and
21 | [sharrow](https://activitysim.github.io/sharrow) to enable super-fast estimation
22 | of choice models. Many (but not yet all) of the core features of Larch have been moved
23 | over to this new platform.
24 |
25 | If you want to try out the new version, just import `larch.numba` instead of `larch`
26 | itself. These docs adopt the convention of `import larch.numba as lx`. All of the
27 | compatible examples in this documentation are being migrated over to the new platform,
28 | but the old examples remain available under the [Legacy Examples](deprecated-examples)
29 | section. If you're not ready for all this awesomeness, or if you need to use some
30 | features of Larch that are not yet in the new version,
31 | **you can still use the legacy (i.e. "old") version of Larch as normal.**
32 |
33 | :::{note}
34 | This project is very much under development. There are plenty of undocumented functions
35 | and features; use them at your own risk. Undocumented features may be non-functional,
36 | not rigorously tested, deprecated or removed without notice in a future version. If a
37 | function or method is documented here, it is intended to be stable in future updates.
38 | :::
39 |
--------------------------------------------------------------------------------
/book/example/legacy/020_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ===========================================================
4 | 20: MTC Shared Ride Nested Mode Choice
5 | ===========================================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | .. testcode::
18 |
19 | m = larch.example(17)
20 |
21 | Model 20's nesting structure groups both shared ride alternatives. (`pp. 176 `_)
22 |
23 | .. testcode::
24 |
25 | shared = m.graph.new_node(parameter='mu', children=[2,3], name='Shared')
26 |
27 | .. testcode::
28 |
29 | m.ordering = (
30 | ("CostbyInc","costbyincome", ),
31 | ("TravelTime",".*time.*",".*dist.*", ),
32 | ("Household","hhinc.*","vehbywrk.*", ),
33 | ("Zonal","wkcbd.*","wkempden.*", ),
34 | ("ASCs","ASC.*", ),
35 | )
36 |
37 | .. doctest::
38 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
39 |
40 | >>> m.load_data()
41 | >>> m.maximize_loglike('slsqp')
42 | ┣ ...Optimization terminated successfully...
43 | >>> m.loglike()
44 | -3442.414...
45 |
46 | >>> print(m.pfo()[['value']])
47 | value
48 | Category Parameter
49 | CostbyInc costbyincome -0.0455
50 | TravelTime motorized_time -0.0206
51 | nonmotorized_time -0.0452
52 | motorized_ovtbydist -0.1338
53 | Household hhinc#4 -0.0054
54 | hhinc#5 -0.0089
55 | hhinc#6 -0.0062
56 | vehbywrk_BIKE -0.7031
57 | vehbywrk_SR -0.3147
58 | vehbywrk_TRANSIT -0.9381
59 | vehbywrk_WALK -0.7241
60 | Zonal wkcbd_BIKE 0.5011
61 | wkcbd_SR2 0.3958
62 | wkcbd_SR3 0.6406
63 | wkcbd_TRANSIT 1.3168
64 | wkcbd_WALK 0.1143
65 | wkempden_BIKE 0.0020
66 | wkempden_SR2 0.0019
67 | wkempden_SR3 0.0019
68 | wkempden_TRANSIT 0.0032
69 | wkempden_WALK 0.0030
70 | ASCs ASC_BIKE -1.6199
71 | ASC_SR2 -1.6847
72 | ASC_SR3 -2.2055
73 | ASC_TRANSIT -0.6797
74 | ASC_WALK 0.0732
75 | Other mu 0.3288
76 |
--------------------------------------------------------------------------------
/larch/doc/example/020_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ===========================================================
4 | 20: MTC Shared Ride Nested Mode Choice
5 | ===========================================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | .. testcode::
18 |
19 | m = larch.example(17)
20 |
21 | Model 20's nesting structure groups both shared ride alternatives. (`pp. 176 `_)
22 |
23 | .. testcode::
24 |
25 | shared = m.graph.new_node(parameter='mu', children=[2,3], name='Shared')
26 |
27 | .. testcode::
28 |
29 | m.ordering = (
30 | ("CostbyInc","costbyincome", ),
31 | ("TravelTime",".*time.*",".*dist.*", ),
32 | ("Household","hhinc.*","vehbywrk.*", ),
33 | ("Zonal","wkcbd.*","wkempden.*", ),
34 | ("ASCs","ASC.*", ),
35 | )
36 |
37 | .. doctest::
38 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
39 |
40 | >>> m.load_data()
41 | >>> m.maximize_loglike('slsqp')
42 | ┣ ...Optimization terminated successfully...
43 | >>> m.loglike()
44 | -3442.414...
45 |
46 | >>> print(m.pfo()[['value']])
47 | value
48 | Category Parameter
49 | CostbyInc costbyincome -0.0455
50 | TravelTime motorized_time -0.0206
51 | nonmotorized_time -0.0452
52 | motorized_ovtbydist -0.1338
53 | Household hhinc#4 -0.0054
54 | hhinc#5 -0.0089
55 | hhinc#6 -0.0062
56 | vehbywrk_BIKE -0.7031
57 | vehbywrk_SR -0.3147
58 | vehbywrk_TRANSIT -0.9381
59 | vehbywrk_WALK -0.7241
60 | Zonal wkcbd_BIKE 0.5011
61 | wkcbd_SR2 0.3958
62 | wkcbd_SR3 0.6406
63 | wkcbd_TRANSIT 1.3168
64 | wkcbd_WALK 0.1143
65 | wkempden_BIKE 0.0020
66 | wkempden_SR2 0.0019
67 | wkempden_SR3 0.0019
68 | wkempden_TRANSIT 0.0032
69 | wkempden_WALK 0.0030
70 | ASCs ASC_BIKE -1.6199
71 | ASC_SR2 -1.6847
72 | ASC_SR3 -2.2055
73 | ASC_TRANSIT -0.6797
74 | ASC_WALK 0.0732
75 | Other mu 0.3288
76 |
--------------------------------------------------------------------------------
/book/example/legacy/021_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ===========================================================
4 | 21: MTC Non-Motorized Nested Mode Choice
5 | ===========================================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | .. testcode::
18 |
19 | m = larch.example(17)
20 |
21 | Model 21's nesting structure groups the non-motorized alternatives. (`pp. 176 `_)
22 |
23 | .. testcode::
24 |
25 | nonmotorized = m.graph.new_node(parameter='mu', children=[5,6], name='Nonmotorized')
26 |
27 | .. testcode::
28 |
29 | m.ordering = (
30 | ("CostbyInc","costbyincome", ),
31 | ("TravelTime",".*time.*",".*dist.*", ),
32 | ("Household","hhinc.*","vehbywrk.*", ),
33 | ("Zonal","wkcbd.*","wkempden.*", ),
34 | ("ASCs","ASC.*", ),
35 | )
36 |
37 | .. doctest::
38 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
39 |
40 | >>> m.load_data()
41 | >>> m.maximize_loglike(method='bhhh')
42 | ┣ ...Optimization terminated successfully...
43 | >>> m.loglike()
44 | -3443.554...
45 |
46 | >>> print(m.pfo()[['value']])
47 | value
48 | Category Parameter
49 | CostbyInc costbyincome -0.0519
50 | TravelTime motorized_time -0.0199
51 | nonmotorized_time -0.0454
52 | motorized_ovtbydist -0.1351
53 | Household hhinc#4 -0.0053
54 | hhinc#5 -0.0092
55 | hhinc#6 -0.0056
56 | vehbywrk_BIKE -0.6928
57 | vehbywrk_SR -0.3165
58 | vehbywrk_TRANSIT -0.9470
59 | vehbywrk_WALK -0.7141
60 | Zonal wkcbd_BIKE 0.4142
61 | wkcbd_SR2 0.2604
62 | wkcbd_SR3 1.0703
63 | wkcbd_TRANSIT 1.3083
64 | wkcbd_WALK 0.1034
65 | wkempden_BIKE 0.0022
66 | wkempden_SR2 0.0016
67 | wkempden_SR3 0.0023
68 | wkempden_TRANSIT 0.0031
69 | wkempden_WALK 0.0028
70 | ASCs ASC_BIKE -1.4459
71 | ASC_SR2 -1.8088
72 | ASC_SR3 -3.4350
73 | ASC_TRANSIT -0.6817
74 | ASC_WALK 0.0791
75 | Other mu 0.7665
76 |
--------------------------------------------------------------------------------
/larch/doc/example/021_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ===========================================================
4 | 21: MTC Non-Motorized Nested Mode Choice
5 | ===========================================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | .. testcode::
18 |
19 | m = larch.example(17)
20 |
21 | Model 21's nesting structure groups the non-motorized alternatives. (`pp. 176 `_)
22 |
23 | .. testcode::
24 |
25 | nonmotorized = m.graph.new_node(parameter='mu', children=[5,6], name='Nonmotorized')
26 |
27 | .. testcode::
28 |
29 | m.ordering = (
30 | ("CostbyInc","costbyincome", ),
31 | ("TravelTime",".*time.*",".*dist.*", ),
32 | ("Household","hhinc.*","vehbywrk.*", ),
33 | ("Zonal","wkcbd.*","wkempden.*", ),
34 | ("ASCs","ASC.*", ),
35 | )
36 |
37 | .. doctest::
38 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
39 |
40 | >>> m.load_data()
41 | >>> m.maximize_loglike(method='bhhh')
42 | ┣ ...Optimization terminated successfully...
43 | >>> m.loglike()
44 | -3443.554...
45 |
46 | >>> print(m.pfo()[['value']])
47 | value
48 | Category Parameter
49 | CostbyInc costbyincome -0.0519
50 | TravelTime motorized_time -0.0199
51 | nonmotorized_time -0.0454
52 | motorized_ovtbydist -0.1351
53 | Household hhinc#4 -0.0053
54 | hhinc#5 -0.0092
55 | hhinc#6 -0.0056
56 | vehbywrk_BIKE -0.6928
57 | vehbywrk_SR -0.3165
58 | vehbywrk_TRANSIT -0.9470
59 | vehbywrk_WALK -0.7141
60 | Zonal wkcbd_BIKE 0.4142
61 | wkcbd_SR2 0.2604
62 | wkcbd_SR3 1.0703
63 | wkcbd_TRANSIT 1.3083
64 | wkcbd_WALK 0.1034
65 | wkempden_BIKE 0.0022
66 | wkempden_SR2 0.0016
67 | wkempden_SR3 0.0023
68 | wkempden_TRANSIT 0.0031
69 | wkempden_WALK 0.0028
70 | ASCs ASC_BIKE -1.4459
71 | ASC_SR2 -1.8088
72 | ASC_SR3 -3.4350
73 | ASC_TRANSIT -0.6817
74 | ASC_WALK 0.0791
75 | Other mu 0.7665
76 |
--------------------------------------------------------------------------------
/conda-recipe/meta.yaml:
--------------------------------------------------------------------------------
1 | package:
2 | name: larch
3 | version: "5.7.2"
4 |
5 | source:
6 | path: ../
7 |
8 | build:
9 | number: 1
10 | skip: True # [py<37]
11 | script:
12 | - "{{ PYTHON }} -m pip install . --no-deps -vv" # [not osx]
13 | - "export SDKROOT=$(xcrun --sdk macosx --show-sdk-path) && export LDFLAGS=-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib && {{ PYTHON }} -m pip install . --no-deps -vv" # [osx]
14 |
15 | requirements:
16 |
17 | build:
18 | - python {{ python }}
19 | - pip
20 | - {{ compiler('c') }}
21 | - llvm-openmp # [osx]
22 | - {{ pin_compatible('numpy', upper_bound='1.22') }}
23 |
24 | host:
25 | - python {{ python }}
26 | - pip
27 | - llvm-openmp # [osx]
28 | - cython >=0.29
29 | - {{ pin_compatible('numpy', upper_bound='1.22') }}
30 |
31 | run:
32 | - python {{ python }}
33 | - ipython >=7.1
34 | - llvm-openmp # [osx]
35 | - {{ pin_compatible('numpy', upper_bound='1.22') }}
36 | - scipy >=1.1
37 | - pandas >=0.24,<1.5
38 | - pytables >=3.4.4 # https://github.com/conda-forge/pytables-feedstock/issues/31
39 | - blosc >=1.14.3
40 | - matplotlib >=3.0
41 | - networkx >=2.4
42 | - tqdm >=4.28.1
43 | - cloudpickle >=0.6.1
44 | - appdirs >=1.4
45 | - docutils >=0.13.1
46 | - jinja2 >=2.10
47 | - beautifulsoup4 >=4.6.3
48 | - lxml >=4.2.5
49 | - seaborn >=0.9.0
50 | - scikit-learn >=0.21
51 | - joblib
52 | - pytest >=4.0
53 | - xmle >=0.1.3
54 | - addicty >=2022.2.1
55 | - pyyaml
56 | - pillow
57 | - xlsxwriter
58 | - openpyxl
59 | - pydot
60 | - pyarrow
61 | - numba
62 | - xarray
63 | - sharrow >=2.0
64 |
65 | test:
66 | source_files:
67 | - tests
68 | - book
69 | - pyproject.toml
70 | imports:
71 | - larch
72 | commands:
73 | - pytest
74 | - pytest --pyargs larch
75 |
76 | # You can also put a file called run_test.py in the recipe that will be run
77 | # at test time.
78 |
79 | requires:
80 | # Put any additional test requirements here. For example
81 | - pytest
82 | - pytest-regressions
83 | - sphinx
84 | - sphinx_rtd_theme
85 | - nbsphinx
86 | - geopandas
87 | - nbmake
88 | - tabulate
89 | - zarr
90 | - altair
91 | - apsw
92 |
93 | about:
94 | home: https://larch.newman.me
95 | license: GPLv3
96 | summary: 'Discrete Choice Modeling in Python'
97 | license_family: GPL
98 |
99 | # See
100 | # http://docs.continuum.io/conda/build.html for
101 | # more information about meta.yaml
102 |
--------------------------------------------------------------------------------
/larch/doc/example/019_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ===========================================================
4 | 19: MTC Private Auto Nested Mode Choice
5 | ===========================================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | .. testcode::
18 |
19 | m = larch.example(17)
20 |
21 | Model 19's nesting structure groups all private automobile alternatives. (`pp. 176 `_)
22 |
23 | .. testcode::
24 |
25 | private_auto = m.graph.new_node(parameter='mu', children=[1,2,3], name='Motorized')
26 |
27 | m.unmangle(True)
28 | m.set_value('mu',maximum=2.0)
29 |
30 | .. testcode::
31 |
32 | m.ordering = (
33 | ("CostbyInc","costbyincome",),
34 | ("TravelTime",".*time.*",".*dist.*", ),
35 | ("Household","hhinc.*","vehbywrk.*",),
36 | ("Zonal","wkcbd.*","wkempden.*",),
37 | ("ASCs","ASC.*",),
38 | )
39 |
40 | .. doctest::
41 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
42 |
43 | >>> m.load_data()
44 | >>> m.maximize_loglike(method='bhhh')
45 | ┣ ...Optimization terminated successfully...
46 | >>> m.loglike()
47 | -3435.995...
48 |
49 | >>> print(m.pfo()[['value']])
50 | value
51 | Category Parameter
52 | CostbyInc costbyincome -0.0607
53 | TravelTime motorized_time -0.0202
54 | nonmotorized_time -0.0462
55 | motorized_ovtbydist -0.1358
56 | Household hhinc#4 -0.0045
57 | hhinc#5 -0.0074
58 | hhinc#6 -0.0050
59 | vehbywrk_BIKE -0.6107
60 | vehbywrk_SR -0.5121
61 | vehbywrk_TRANSIT -0.8725
62 | vehbywrk_WALK -0.6143
63 | Zonal wkcbd_BIKE 0.5234
64 | wkcbd_SR2 0.3982
65 | wkcbd_SR3 1.5869
66 | wkcbd_TRANSIT 1.3667
67 | wkcbd_WALK 0.1170
68 | wkempden_BIKE 0.0023
69 | wkempden_SR2 0.0024
70 | wkempden_SR3 0.0036
71 | wkempden_TRANSIT 0.0035
72 | wkempden_WALK 0.0033
73 | ASCs ASC_BIKE -1.8205
74 | ASC_SR2 -2.5667
75 | ASC_SR3 -4.9623
76 | ASC_TRANSIT -0.8474
77 | ASC_WALK -0.1166
78 | Other mu 1.4656
--------------------------------------------------------------------------------
/book/example/legacy/019_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ===========================================================
4 | 19: MTC Private Auto Nested Mode Choice
5 | ===========================================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | .. testcode::
18 |
19 | m = larch.example(17)
20 |
21 | Model 19's nesting structure groups all private automobile alternatives. (`pp. 176 `_)
22 |
23 | .. testcode::
24 |
25 | private_auto = m.graph.new_node(parameter='mu', children=[1,2,3], name='Motorized')
26 |
27 | m.unmangle(True)
28 | m.set_value('mu',maximum=2.0)
29 |
30 | .. testcode::
31 |
32 | m.ordering = (
33 | ("CostbyInc","costbyincome",),
34 | ("TravelTime",".*time.*",".*dist.*", ),
35 | ("Household","hhinc.*","vehbywrk.*",),
36 | ("Zonal","wkcbd.*","wkempden.*",),
37 | ("ASCs","ASC.*",),
38 | )
39 |
40 | .. doctest::
41 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
42 |
43 | >>> m.load_data()
44 | >>> m.maximize_loglike(method='bhhh')
45 | ┣ ...Optimization terminated successfully...
46 | >>> m.loglike()
47 | -3435.995...
48 |
49 | >>> print(m.pfo()[['value']])
50 | value
51 | Category Parameter
52 | CostbyInc costbyincome -0.0607
53 | TravelTime motorized_time -0.0202
54 | nonmotorized_time -0.0462
55 | motorized_ovtbydist -0.1358
56 | Household hhinc#4 -0.0045
57 | hhinc#5 -0.0074
58 | hhinc#6 -0.0050
59 | vehbywrk_BIKE -0.6107
60 | vehbywrk_SR -0.5121
61 | vehbywrk_TRANSIT -0.8725
62 | vehbywrk_WALK -0.6143
63 | Zonal wkcbd_BIKE 0.5234
64 | wkcbd_SR2 0.3982
65 | wkcbd_SR3 1.5869
66 | wkcbd_TRANSIT 1.3667
67 | wkcbd_WALK 0.1170
68 | wkempden_BIKE 0.0023
69 | wkempden_SR2 0.0024
70 | wkempden_SR3 0.0036
71 | wkempden_TRANSIT 0.0035
72 | wkempden_WALK 0.0033
73 | ASCs ASC_BIKE -1.8205
74 | ASC_SR2 -2.5667
75 | ASC_SR3 -4.9623
76 | ASC_TRANSIT -0.8474
77 | ASC_WALK -0.1166
78 | Other mu 1.4656
79 |
--------------------------------------------------------------------------------
/larch/util/__init__.py:
--------------------------------------------------------------------------------
1 | from .signal_dict import SignalDict
2 |
3 | import pprint
4 | import datetime
5 | from .addict_yaml import Dict
6 | from .data_expansion import piece, hard_sigmoid
7 |
8 |
9 | def _prettyprint_fallback(x):
10 | if isinstance(x, dictx):
11 | return repr(x)
12 | else:
13 | return pprint.pformat(x)
14 |
15 |
16 |
17 | class dictx(dict):
18 | """Python dict with attribute access and xml output."""
19 |
20 | def __repr__(self):
21 | if self.keys():
22 | m = max(map(len, list(str(_) for _ in self.keys()))) + 1
23 | return '\n'.join(['┣'+str(k).rjust(m) + ': ' + _prettyprint_fallback(v).replace('\n','\n┃'+' '*(m+2)) for k, v in self.items()])
24 | else:
25 | return self.__class__.__name__ + "()"
26 |
27 | def __getattr__(self, item):
28 | if item[0]=="_" and item[-1]=="_":
29 | raise AttributeError(item)
30 | return self.__getitem__(item)
31 |
32 | def __setattr__(self, name, value):
33 | if hasattr(dictx, name):
34 | raise AttributeError("'dictx' object attribute "
35 | "'{0}' is read-only".format(name))
36 | else:
37 | self[name] = value
38 |
39 | def __xml__(self):
40 | from xmle import Elem, Show
41 | x = Elem('div')
42 | t = x.elem('table', style="margin-top:1px;")
43 | if len(self):
44 | tr = t.elem('tr')
45 | tr.elem('th', text="key")
46 | tr.elem('th', text='value', style='text-align:left;')
47 | for k,v in self.items():
48 | tr = t.elem('tr')
49 | tr.elem('td', text=str(k))
50 | try:
51 | if isinstance(v, str) and '\n' not in v and v[:1] != "#":
52 | raise ValueError()
53 | v_ = Show(v)
54 | except (AttributeError, ValueError):
55 | plaintext = pprint.pformat(v)
56 | if isinstance(v, datetime.timedelta):
57 | plaintext = str(v)
58 | if "\n" in plaintext:
59 | tr.elem('td', style='text-align:left;').elem('pre', text=plaintext)
60 | else:
61 | tr.elem('td', style='text-align:left;', text=plaintext)
62 | else:
63 | tr.elem('td', style='text-align:left;') << v_
64 | else:
65 | tr = t.elem('tr')
66 | tr.elem('td', text="")
67 | return x
68 |
69 | def _repr_html_(self):
70 | return self.__xml__().tostring()
71 |
72 | def copy(self):
73 | return dictx(super().copy())
74 |
75 | dicta = Dict
76 |
77 |
78 | # Add statistics to pandas.DataFrame and pandas.Series
79 | import pandas
80 | from .statistics import statistics_for_dataframe, statistics_for_array5, uniques, invmap
81 | pandas.DataFrame.statistics = statistics_for_dataframe
82 | pandas.Series.statistics = statistics_for_array5
83 | pandas.Series.uniques = uniques
84 | pandas.Series.invmap = invmap
85 |
86 | from .dataframe import compute
87 | pandas.DataFrame.compute = compute
88 |
--------------------------------------------------------------------------------
/book/api/model.rst:
--------------------------------------------------------------------------------
1 | ===========
2 | Model
3 | ===========
4 |
5 | .. currentmodule:: larch.numba
6 |
7 |
8 | .. autosummary::
9 | :toctree: generated/
10 |
11 | Model
12 |
13 |
14 | Attributes
15 | ==========
16 |
17 | Data Connection
18 | ---------------
19 |
20 | .. autosummary::
21 | :toctree: generated/
22 |
23 | Model.datatree
24 | Model.dataset
25 | Model.n_cases
26 |
27 |
28 | Choice Definition
29 | -----------------
30 |
31 | .. autosummary::
32 | :toctree: generated/
33 |
34 | Model.choice_ca_var
35 | Model.choice_co_vars
36 | Model.choice_co_code
37 |
38 |
39 | Alternative Availability
40 | ------------------------
41 |
42 | .. autosummary::
43 | :toctree: generated/
44 |
45 | Model.availability_ca_var
46 | Model.availability_co_vars
47 |
48 |
49 | Utility Definition
50 | ------------------
51 |
52 | .. autosummary::
53 | :toctree: generated/
54 |
55 | Model.utility_ca
56 | Model.utility_co
57 | Model.quantity_ca
58 |
59 |
60 | Parameters
61 | ----------
62 |
63 | .. autosummary::
64 | :toctree: generated/
65 |
66 | Model.pf
67 |
68 | Estimation Results
69 | ------------------
70 |
71 | .. autosummary::
72 | :toctree: generated/
73 |
74 | Model.most_recent_estimation_result
75 | Model.possible_overspecification
76 |
77 |
78 | Methods
79 | =======
80 |
81 | Setting Parameters
82 | ------------------
83 |
84 | .. autosummary::
85 | :toctree: generated/
86 |
87 | Model.set_values
88 | Model.lock_value
89 | Model.set_cap
90 | Model.remove_unused_parameters
91 |
92 |
93 | Parameter Estimation
94 | --------------------
95 |
96 | .. autosummary::
97 | :toctree: generated/
98 |
99 | Model.maximize_loglike
100 | Model.calculate_parameter_covariance
101 |
102 |
103 | Model Fitness
104 | -------------
105 |
106 | .. autosummary::
107 | :toctree: generated/
108 |
109 | Model.loglike_nil
110 | Model.loglike_null
111 | Model.rho_sq_nil
112 | Model.rho_sq_null
113 |
114 |
115 | Reporting
116 | ---------
117 |
118 | .. autosummary::
119 | :toctree: generated/
120 |
121 | Model.parameter_summary
122 | Model.estimation_statistics
123 | Model.to_xlsx
124 |
125 |
126 | Ancillary Computation
127 | ---------------------
128 |
129 | .. autosummary::
130 | :toctree: generated/
131 |
132 | Model.bhhh
133 | Model.check_d_loglike
134 | Model.d_loglike
135 | Model.d_loglike_casewise
136 | Model.loglike
137 | Model.loglike_casewise
138 | Model.logsums
139 | Model.probability
140 | Model.quantity
141 | Model.total_weight
142 | Model.utility
143 |
--------------------------------------------------------------------------------
/larch/util/png.py:
--------------------------------------------------------------------------------
1 |
2 | import io
3 | import base64
4 | from xmle import Elem
5 | from PIL import Image
6 |
7 |
8 | def make_png(
9 | content,
10 | dpi=None,
11 | compress=True,
12 | output='Elem',
13 | close_after=True,
14 | pad_inches=0.1,
15 | facecolor=None,
16 | return_size=False,
17 | return_dpi=False,
18 | ):
19 |
20 | _bytes_io = None
21 | _img = None
22 | _size = None
23 |
24 | if hasattr(content, '_repr_png_'):
25 | content = content._repr_png_()
26 |
27 | if isinstance(content, bytes):
28 | _bytes_io = io.BytesIO(content)
29 |
30 | if 'matplotlib' in str(type(content)):
31 | from matplotlib import pyplot as plt
32 | import matplotlib.figure
33 | if not isinstance(content, matplotlib.figure.Figure):
34 | try:
35 | content = content.get_figure()
36 | except AttributeError:
37 | if not hasattr(content, 'savefig'):
38 | raise TypeError('matplotlib content must provide `get_figure` or `savefig` method.')
39 |
40 | # content is a Figure or otherwise has a savefig method
41 | try:
42 | fig_number = content.number
43 | except AttributeError:
44 | fig_number = None
45 |
46 | if facecolor is None:
47 | try:
48 | facecolor = content.get_facecolor()
49 | except:
50 | facecolor = 'w'
51 |
52 | try:
53 | edgecolor = content.get_edgecolor()
54 | except:
55 | edgecolor = 'none'
56 |
57 | _bytes_io = io.BytesIO()
58 | content.savefig(
59 | _bytes_io,
60 | dpi=dpi,
61 | orientation='portrait',
62 | format='png',
63 | bbox_inches='tight',
64 | pad_inches=pad_inches,
65 | facecolor=facecolor,
66 | edgecolor=edgecolor,
67 | )
68 |
69 | if close_after and fig_number is not None:
70 | plt.close(fig_number)
71 |
72 | if _bytes_io.getvalue()[:4] == b'\x89PNG':
73 | _img = Image.open(_bytes_io)
74 | dpi = _img.info.get('dpi', dpi)
75 | if dpi is None:
76 | dpi = [96, 96]
77 | if compress:
78 | _img = _img.convert(mode='P', palette='ADAPTIVE')
79 | _bytes_io = io.BytesIO()
80 | _img.save(_bytes_io, dpi=dpi, format='png')
81 | _size = _img.size
82 | else:
83 | raise ValueError("Not valid PNG data")
84 |
85 | if output.lower() == 'elem':
86 | result = Elem(
87 | tag='img',
88 | src="data:image/png;base64,{}".format(base64.standard_b64encode(_bytes_io.getvalue()).decode()),
89 | )
90 | elif output.lower() == 'bytesio':
91 | result = _bytes_io
92 | else:
93 | result = _img
94 |
95 | if return_size:
96 | if isinstance(result, tuple):
97 | result = (*result, _size)
98 | else:
99 | result = result, _size
100 |
101 | if return_dpi:
102 | if isinstance(result, tuple):
103 | result = (*result, dpi)
104 | else:
105 | result = result, dpi
106 |
107 | return result
108 |
109 |
--------------------------------------------------------------------------------
/larch/doc/example/024_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ===========================================================
4 | 23W: MTC Shared Ride - Non-Motorized Nested Mode Choice
5 | ===========================================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | .. testcode::
18 |
19 | m = larch.example(17)
20 |
21 | Model 24's nesting structure groups shared ride and non-motorized alternatives. (`pp. 179 `_)
22 |
23 | .. testcode::
24 |
25 | shared = m.graph.new_node(parameter='mu_shared', children=[2,3], name='Shared')
26 | nonmotorized = m.graph.new_node(parameter='mu_nonmoto', children=[5,6], name='Nonmotorized')
27 |
28 | .. testcode::
29 |
30 | m.ordering = (
31 | ("CostbyInc","costbyincome",),
32 | ("TravelTime",".*time.*",".*dist.*", ),
33 | ("Household","hhinc.*","vehbywrk.*",),
34 | ("Zonal","wkcbd.*","wkempden.*",),
35 | ("ASCs","ASC.*",),
36 | )
37 |
38 | .. doctest::
39 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
40 |
41 | >>> m.load_data()
42 | >>> m.maximize_loglike('slsqp')
43 | ┣ ...Optimization terminated successfully...
44 | >>> m.loglike()
45 | -3441.76...
46 |
47 | .. doctest::
48 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF +SKIP
49 |
50 | >>> print(m.pfo()[['value']])
51 | value
52 | Category Parameter
53 | CostbyInc costbyincome -0.0450
54 | TravelTime motorized_time -0.0203
55 | nonmotorized_time -0.0452
56 | motorized_ovtbydist -0.1361
57 | Household hhinc#4 -0.0054
58 | hhinc#5 -0.0095
59 | hhinc#6 -0.0058
60 | vehbywrk_BIKE -0.6938
61 | vehbywrk_SR -0.3146
62 | vehbywrk_TRANSIT -0.9388
63 | vehbywrk_WALK -0.7162
64 | Zonal wkcbd_BIKE 0.4252
65 | wkcbd_SR2 0.3973
66 | wkcbd_SR3 0.6389
67 | wkcbd_TRANSIT 1.3162
68 | wkcbd_WALK 0.1162
69 | wkempden_BIKE 0.0023
70 | wkempden_SR2 0.0019
71 | wkempden_SR3 0.0019
72 | wkempden_TRANSIT 0.0032
73 | wkempden_WALK 0.0029
74 | ASCs ASC_BIKE -1.4331
75 | ASC_SR2 -1.6849
76 | ASC_SR3 -2.1988
77 | ASC_TRANSIT -0.6760
78 | ASC_WALK 0.0840
79 | Other mu_nonmoto 0.7622
80 | mu_shared 0.3245
--------------------------------------------------------------------------------
/book/example/legacy/024_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ===========================================================
4 | 23W: MTC Shared Ride - Non-Motorized Nested Mode Choice
5 | ===========================================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | .. testcode::
18 |
19 | m = larch.example(17)
20 |
21 | Model 24's nesting structure groups shared ride and non-motorized alternatives. (`pp. 179 `_)
22 |
23 | .. testcode::
24 |
25 | shared = m.graph.new_node(parameter='mu_shared', children=[2,3], name='Shared')
26 | nonmotorized = m.graph.new_node(parameter='mu_nonmoto', children=[5,6], name='Nonmotorized')
27 |
28 | .. testcode::
29 |
30 | m.ordering = (
31 | ("CostbyInc","costbyincome",),
32 | ("TravelTime",".*time.*",".*dist.*", ),
33 | ("Household","hhinc.*","vehbywrk.*",),
34 | ("Zonal","wkcbd.*","wkempden.*",),
35 | ("ASCs","ASC.*",),
36 | )
37 |
38 | .. doctest::
39 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
40 |
41 | >>> m.load_data()
42 | >>> m.maximize_loglike('slsqp')
43 | ┣ ...Optimization terminated successfully...
44 | >>> m.loglike()
45 | -3441.76...
46 |
47 | .. doctest::
48 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF +SKIP
49 |
50 | >>> print(m.pfo()[['value']])
51 | value
52 | Category Parameter
53 | CostbyInc costbyincome -0.0450
54 | TravelTime motorized_time -0.0203
55 | nonmotorized_time -0.0452
56 | motorized_ovtbydist -0.1361
57 | Household hhinc#4 -0.0054
58 | hhinc#5 -0.0095
59 | hhinc#6 -0.0058
60 | vehbywrk_BIKE -0.6938
61 | vehbywrk_SR -0.3146
62 | vehbywrk_TRANSIT -0.9388
63 | vehbywrk_WALK -0.7162
64 | Zonal wkcbd_BIKE 0.4252
65 | wkcbd_SR2 0.3973
66 | wkcbd_SR3 0.6389
67 | wkcbd_TRANSIT 1.3162
68 | wkcbd_WALK 0.1162
69 | wkempden_BIKE 0.0023
70 | wkempden_SR2 0.0019
71 | wkempden_SR3 0.0019
72 | wkempden_TRANSIT 0.0032
73 | wkempden_WALK 0.0029
74 | ASCs ASC_BIKE -1.4331
75 | ASC_SR2 -1.6849
76 | ASC_SR3 -2.1988
77 | ASC_TRANSIT -0.6760
78 | ASC_WALK 0.0840
79 | Other mu_nonmoto 0.7622
80 | mu_shared 0.3245
81 |
--------------------------------------------------------------------------------
/larch/doc/example/023_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ===========================================================
4 | 23W: MTC Private Auto - Non-Motorized Nested Mode Choice
5 | ===========================================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | .. testcode::
18 |
19 | m = larch.example(17)
20 |
21 | Model 23's nesting structure groups private automobiles and non-motorized alternatives. (`pp. 179 `_)
22 |
23 | .. testcode::
24 |
25 | private_auto = m.graph.new_node(parameter='mu_auto', children=[1,2,3], name='Private_Auto')
26 | nonmotorized = m.graph.new_node(parameter='mu_nonmoto', children=[5,6], name='Nonmotorized')
27 |
28 | m.unmangle(True)
29 | m.set_value('mu_auto',maximum=2.0)
30 |
31 | .. testcode::
32 |
33 | m.ordering = (
34 | ("CostbyInc","costbyincome",),
35 | ("TravelTime",".*time.*",".*dist.*", ),
36 | ("Household","hhinc.*","vehbywrk.*",),
37 | ("Zonal","wkcbd.*","wkempden.*",),
38 | ("ASCs","ASC.*",),
39 | )
40 |
41 | .. doctest::
42 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
43 |
44 | >>> m.load_data()
45 | >>> m.maximize_loglike(method='bhhh')
46 | ┣ ...Optimization terminated successfully...
47 | >>> m.loglike()
48 | -3435.357...
49 |
50 | >>> print(m.pfo()[['value']])
51 | value
52 | Category Parameter
53 | CostbyInc costbyincome -0.0600
54 | TravelTime motorized_time -0.0199
55 | nonmotorized_time -0.0460
56 | motorized_ovtbydist -0.1383
57 | Household hhinc#4 -0.0046
58 | hhinc#5 -0.0081
59 | hhinc#6 -0.0046
60 | vehbywrk_BIKE -0.5952
61 | vehbywrk_SR -0.5120
62 | vehbywrk_TRANSIT -0.8737
63 | vehbywrk_WALK -0.6076
64 | Zonal wkcbd_BIKE 0.4459
65 | wkcbd_SR2 0.3987
66 | wkcbd_SR3 1.5877
67 | wkcbd_TRANSIT 1.3660
68 | wkcbd_WALK 0.1202
69 | wkempden_BIKE 0.0026
70 | wkempden_SR2 0.0025
71 | wkempden_SR3 0.0036
72 | wkempden_TRANSIT 0.0035
73 | wkempden_WALK 0.0032
74 | ASCs ASC_BIKE -1.6467
75 | ASC_SR2 -2.5664
76 | ASC_SR3 -4.9614
77 | ASC_TRANSIT -0.8427
78 | ASC_WALK -0.1102
79 | Other mu_auto 1.4652
80 | mu_nonmoto 0.7661
--------------------------------------------------------------------------------
/book/example/legacy/023_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ===========================================================
4 | 23W: MTC Private Auto - Non-Motorized Nested Mode Choice
5 | ===========================================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | .. testcode::
18 |
19 | m = larch.example(17)
20 |
21 | Model 23's nesting structure groups private automobiles and non-motorized alternatives. (`pp. 179 `_)
22 |
23 | .. testcode::
24 |
25 | private_auto = m.graph.new_node(parameter='mu_auto', children=[1,2,3], name='Private_Auto')
26 | nonmotorized = m.graph.new_node(parameter='mu_nonmoto', children=[5,6], name='Nonmotorized')
27 |
28 | m.unmangle(True)
29 | m.set_value('mu_auto',maximum=2.0)
30 |
31 | .. testcode::
32 |
33 | m.ordering = (
34 | ("CostbyInc","costbyincome",),
35 | ("TravelTime",".*time.*",".*dist.*", ),
36 | ("Household","hhinc.*","vehbywrk.*",),
37 | ("Zonal","wkcbd.*","wkempden.*",),
38 | ("ASCs","ASC.*",),
39 | )
40 |
41 | .. doctest::
42 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
43 |
44 | >>> m.load_data()
45 | >>> m.maximize_loglike(method='bhhh')
46 | ┣ ...Optimization terminated successfully...
47 | >>> m.loglike()
48 | -3435.357...
49 |
50 | >>> print(m.pfo()[['value']])
51 | value
52 | Category Parameter
53 | CostbyInc costbyincome -0.0600
54 | TravelTime motorized_time -0.0199
55 | nonmotorized_time -0.0460
56 | motorized_ovtbydist -0.1383
57 | Household hhinc#4 -0.0046
58 | hhinc#5 -0.0081
59 | hhinc#6 -0.0046
60 | vehbywrk_BIKE -0.5952
61 | vehbywrk_SR -0.5120
62 | vehbywrk_TRANSIT -0.8737
63 | vehbywrk_WALK -0.6076
64 | Zonal wkcbd_BIKE 0.4459
65 | wkcbd_SR2 0.3987
66 | wkcbd_SR3 1.5877
67 | wkcbd_TRANSIT 1.3660
68 | wkcbd_WALK 0.1202
69 | wkempden_BIKE 0.0026
70 | wkempden_SR2 0.0025
71 | wkempden_SR3 0.0036
72 | wkempden_TRANSIT 0.0035
73 | wkempden_WALK 0.0032
74 | ASCs ASC_BIKE -1.6467
75 | ASC_SR2 -2.5664
76 | ASC_SR3 -4.9614
77 | ASC_TRANSIT -0.8427
78 | ASC_WALK -0.1102
79 | Other mu_auto 1.4652
80 | mu_nonmoto 0.7661
81 |
--------------------------------------------------------------------------------
/.github/workflows/dev-docs.yml:
--------------------------------------------------------------------------------
1 | name: dev-docs
2 |
3 | on:
4 | push:
5 | branches: [ develop ]
6 | workflow_dispatch:
7 |
8 | jobs:
9 | deploy-docs:
10 | runs-on: ubuntu-latest
11 | environment: developer
12 | defaults:
13 | run:
14 | shell: bash -l {0}
15 | steps:
16 | - uses: actions/checkout@v2
17 | with:
18 | fetch-depth: 0
19 | path: larch
20 | - name: Set up Python 3.9
21 | uses: actions/setup-python@v2
22 | with:
23 | python-version: 3.9
24 | - name: Cache conda
25 | uses: actions/cache@v2
26 | env:
27 | # Increase this value to reset cache if larch/environments/development.yml has not changed
28 | CACHE_NUMBER: 0
29 | with:
30 | path: ~/conda_pkgs_dir
31 | key:
32 | ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-${{ hashFiles('larch/environments/development.yml') }}
33 | - name: Install dependencies
34 | uses: conda-incubator/setup-miniconda@v2
35 | with:
36 | miniforge-variant: Mambaforge
37 | miniforge-version: latest
38 | use-mamba: true
39 | environment-file: larch/environments/development.yml
40 | python-version: 3.9
41 | activate-environment: development
42 | auto-activate-base: false
43 | auto-update-conda: false
44 | use-only-tar-bz2: true # IMPORTANT: This needs to be set for caching to work properly!
45 | - name: Infer larch version
46 | run: |
47 | python larch/tools/get_version.py LARCH_VERSION.txt
48 | cat LARCH_VERSION.txt >> $GITHUB_ENV
49 | - name: Install sharrow
50 | run: |
51 | git clone https://github.com/ActivitySim/sharrow.git
52 | cd sharrow
53 | python -m pip install --no-deps -e .
54 | - name: Install larch
55 | run: |
56 | cd larch
57 | python -m pip install --no-deps -e .
58 | - name: Conda checkup
59 | run: |
60 | conda info -a
61 | conda list
62 | - name: Build the docs
63 | run: |
64 | python larch/book/_scripts/hide_test_cells.py
65 | python larch/book/_scripts/developer_doc_title.py v${{ env.LARCH_VERSION }}
66 | jb build larch/book
67 | - name: Push to GitHub Pages
68 | uses: peaceiris/actions-gh-pages@v3.8.0
69 | with:
70 | github_token: ${{ secrets.GITHUB_TOKEN }}
71 | # Token is created automatically by Github Actions, no other config needed
72 | publish_dir: larch/book/_build/html
73 | - name: Push to External GitHub Pages
74 | uses: peaceiris/actions-gh-pages@v3.8.0
75 | if: "!contains(env.LARCH_VERSION, '+')"
76 | with:
77 | personal_token: ${{ secrets.PERSONAL_TOKEN }}
78 | publish_dir: larch/book/_build/html
79 | external_repository: jpndashdash/larchdocs
80 | destination_dir: v${{ env.LARCH_VERSION }}
81 |
--------------------------------------------------------------------------------
/book/example/legacy/025_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ===========================================================
4 | 25: MTC Private Auto - Motorized Nested Mode Choice
5 | ===========================================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | .. testcode::
18 |
19 | m = larch.example(17)
20 |
21 | Model 25's nesting structure groups motorized and shared ride alternatives. (`pp. 182 `_)
22 |
23 | Since private auto is completely contained within motorized, we can use the private nest when we define the motorized nest.
24 |
25 |
26 | .. testcode::
27 |
28 | private_auto = m.graph.new_node(parameter='mu_private', children=[1,2,3], name='Private_Auto')
29 | motorized = m.graph.new_node(parameter='mu_moto', children=[private_auto,4], name='Motorized')
30 |
31 | .. testcode::
32 |
33 | m.ordering = (
34 | ("CostbyInc","costbyincome",),
35 | ("TravelTime",".*time.*",".*dist.*", ),
36 | ("Household","hhinc.*","vehbywrk.*",),
37 | ("Zonal","wkcbd.*","wkempden.*",),
38 | ("ASCs","ASC.*",),
39 | )
40 |
41 | .. doctest::
42 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
43 |
44 | >>> m.load_data()
45 | >>> m.maximize_loglike(method='bhhh')
46 | ┣ ...Optimization terminated successfully...
47 | >>> m.loglike()
48 | -3427.166...
49 |
50 | >>> print(m.pfo()[['value']])
51 | value
52 | Category Parameter
53 | CostbyInc costbyincome -0.0363
54 | TravelTime motorized_time -0.0106
55 | nonmotorized_time -0.0471
56 | motorized_ovtbydist -0.0995
57 | Household hhinc#4 -0.0022
58 | hhinc#5 -0.0090
59 | hhinc#6 -0.0061
60 | vehbywrk_BIKE -0.6885
61 | vehbywrk_SR -0.3218
62 | vehbywrk_TRANSIT -0.4628
63 | vehbywrk_WALK -0.7052
64 | Zonal wkcbd_BIKE 0.5003
65 | wkcbd_SR2 0.2753
66 | wkcbd_SR3 1.0253
67 | wkcbd_TRANSIT 0.7310
68 | wkcbd_WALK 0.1416
69 | wkempden_BIKE 0.0014
70 | wkempden_SR2 0.0016
71 | wkempden_SR3 0.0024
72 | wkempden_TRANSIT 0.0019
73 | wkempden_WALK 0.0022
74 | ASCs ASC_BIKE -1.3790
75 | ASC_SR2 -1.6337
76 | ASC_SR3 -3.1469
77 | ASC_TRANSIT -0.4069
78 | ASC_WALK 0.3391
79 | Other mu_moto 0.5322
80 | mu_private 0.9236
81 |
--------------------------------------------------------------------------------
/larch/doc/example/025_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ===========================================================
4 | 25: MTC Private Auto - Motorized Nested Mode Choice
5 | ===========================================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | .. testcode::
18 |
19 | m = larch.example(17)
20 |
21 | Model 25's nesting structure groups motorized and shared ride alternatives. (`pp. 182 `_)
22 |
23 | Since private auto is completely contained within motorized, we can use the private nest when we define the motorized nest.
24 |
25 |
26 | .. testcode::
27 |
28 | private_auto = m.graph.new_node(parameter='mu_private', children=[1,2,3], name='Private_Auto')
29 | motorized = m.graph.new_node(parameter='mu_moto', children=[private_auto,4], name='Motorized')
30 |
31 | .. testcode::
32 |
33 | m.ordering = (
34 | ("CostbyInc","costbyincome",),
35 | ("TravelTime",".*time.*",".*dist.*", ),
36 | ("Household","hhinc.*","vehbywrk.*",),
37 | ("Zonal","wkcbd.*","wkempden.*",),
38 | ("ASCs","ASC.*",),
39 | )
40 |
41 | .. doctest::
42 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
43 |
44 | >>> m.load_data()
45 | >>> m.maximize_loglike(method='bhhh')
46 | ┣ ...Optimization terminated successfully...
47 | >>> m.loglike()
48 | -3427.166...
49 |
50 | >>> print(m.pfo()[['value']])
51 | value
52 | Category Parameter
53 | CostbyInc costbyincome -0.0363
54 | TravelTime motorized_time -0.0106
55 | nonmotorized_time -0.0471
56 | motorized_ovtbydist -0.0995
57 | Household hhinc#4 -0.0022
58 | hhinc#5 -0.0090
59 | hhinc#6 -0.0061
60 | vehbywrk_BIKE -0.6885
61 | vehbywrk_SR -0.3218
62 | vehbywrk_TRANSIT -0.4628
63 | vehbywrk_WALK -0.7052
64 | Zonal wkcbd_BIKE 0.5003
65 | wkcbd_SR2 0.2753
66 | wkcbd_SR3 1.0253
67 | wkcbd_TRANSIT 0.7310
68 | wkcbd_WALK 0.1416
69 | wkempden_BIKE 0.0014
70 | wkempden_SR2 0.0016
71 | wkempden_SR3 0.0024
72 | wkempden_TRANSIT 0.0019
73 | wkempden_WALK 0.0022
74 | ASCs ASC_BIKE -1.3790
75 | ASC_SR2 -1.6337
76 | ASC_SR3 -3.1469
77 | ASC_TRANSIT -0.4069
78 | ASC_WALK 0.3391
79 | Other mu_moto 0.5322
80 | mu_private 0.9236
--------------------------------------------------------------------------------
/larch/dataset/patch.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import pandas as pd
3 | import xarray as xr
4 | from typing import Mapping
5 | import warnings
6 |
7 | from sharrow.accessors import register_dataset_method, register_dataarray_method
8 |
9 |
10 | class NameConflictWarning(Warning):
11 | """Warning for conflicts in name registration."""
12 |
13 |
14 | def _register_classmethod(name, cls):
15 | def decorator(classmeth):
16 | if hasattr(cls, name):
17 | warnings.warn(
18 | f"registration of classmethod {classmeth!r} under name {name!r} "
19 | f"for type {cls!r} is overriding a preexisting attribute with "
20 | f"the same name.",
21 | NameConflictWarning,
22 | stacklevel=2,
23 | )
24 | setattr(cls, name, classmethod(classmeth))
25 | return classmeth
26 |
27 | return decorator
28 |
29 |
30 | def register_dataarray_classmethod(func):
31 | """
32 | Register a custom classmethod on xarray.DataArray objects.
33 |
34 | Use this as a decorator to add class methods.
35 |
36 | Parameters
37 | ----------
38 | func : Callable
39 | Class method to add. The name is inferred from the
40 | original name of this function.
41 | """
42 | return _register_classmethod(func.__name__, xr.DataArray)(func)
43 |
44 |
45 | def register_dataset_classmethod(func):
46 | """
47 | Register a custom classmethod on xarray.Dataset objects.
48 |
49 | Use this as a decorator to add class methods.
50 |
51 | Parameters
52 | ----------
53 | func : Callable
54 | Class method to add. The name is inferred from the
55 | original name of this function.
56 | """
57 | return _register_classmethod(func.__name__, xr.Dataset)(func)
58 |
59 |
60 | @register_dataset_method
61 | def set_dtypes(self, dtypes, inplace=False, on_error='warn'):
62 | """
63 | Set the dtypes for the variables in this Dataset.
64 |
65 | Parameters
66 | ----------
67 | dtypes : Mapping or DataFrame
68 | Mapping of names to dtypes, or a DataFrame to infer such a
69 | mapping.
70 | inplace : bool, default False
71 | Whether to convert dtypes inplace.
72 | on_error : {'warn', 'raise', 'ignore'}
73 | What to do when a type conversion triggers an error.
74 |
75 | Returns
76 | -------
77 | Dataset
78 | """
79 | if isinstance(dtypes, pd.DataFrame):
80 | dtypes = dtypes.dtypes
81 | if inplace:
82 | obj = self
83 | else:
84 | obj = self.copy()
85 | for k in obj:
86 | if k not in dtypes:
87 | continue
88 | try:
89 | obj[k] = obj[k].astype(dtypes[k])
90 | except Exception as err:
91 | if on_error == 'warn':
92 | warnings.warn(f"{err!r} on converting {k}")
93 | elif on_error == 'raise':
94 | raise
95 | return obj
96 |
--------------------------------------------------------------------------------
/larch/doc/index.rst:
--------------------------------------------------------------------------------
1 | .. larch documentation master file
2 |
3 | ==============================
4 | |treelogo| Larch Documentation
5 | ==============================
6 |
7 | .. image:: https://img.shields.io/conda/vn/conda-forge/larch.svg
8 | :target: https://anaconda.org/conda-forge/larch
9 | :class: statusbadge
10 |
11 | .. image:: https://img.shields.io/conda/dn/conda-forge/larch
12 | :target: https://anaconda.org/conda-forge/larch
13 | :class: statusbadge
14 |
15 | .. image:: https://img.shields.io/badge/source-github-yellow.svg
16 | :target: https://github.com/jpn--/larch
17 | :class: statusbadge
18 |
19 | .. image:: https://img.shields.io/appveyor/ci/jpn--/larch.svg?label=windows%20build
20 | :target: https://ci.appveyor.com/project/jpn--/larch
21 | :class: statusbadge
22 |
23 | .. image:: https://img.shields.io/travis/jpn--/larch.svg?label=linux%20build
24 | :target: https://travis-ci.org/jpn--/larch
25 | :class: statusbadge
26 |
27 | .. |treelogo| image:: ../../img/larch_favicon.png
28 | :height: 0.9em
29 | :class: treelogo
30 |
31 | .. |trophy| image:: ../../img/trophy-64.png
32 | :class: trophyicon
33 |
34 |
35 | ..
36 |
37 | |trophy| Winner of the `AGIFORS 56th Annual Symposium Best Innovation award `__.
38 |
39 | This documentation is for the Python interface for Larch. If this is your first go
40 | with Larch, or the first go on a new computer, you might want to start with :ref:`installation`.
41 |
42 | This project is very much under development. There are plenty of undocumented functions
43 | and features; use them at your own risk. Undocumented features may be non-functional,
44 | not rigorously tested, deprecated or removed without notice in a future version. If a
45 | function or method is documented here, it is intended to be stable in future updates.
46 |
47 | You may also find these links useful:
48 |
49 | * `Python `_ 3.7: http://docs.python.org/3.7/index.html
50 | * `NumPy `__: http://docs.scipy.org/doc/numpy/
51 | * `SciPy `__: http://docs.scipy.org/doc/scipy/reference/
52 | * `scikit-learn `__: https://scikit-learn.org/stable/documentation.html
53 |
54 | For learning Python itself:
55 |
56 | * If you are new to Python but have some experience with some other programming language,
57 | there is an online quick introduction `Python for Transportation Modeling `__,
58 | available free online, sponsored by the Florida Department of Transportation.
59 | * If you are new to programming in general, there are better (slower) guides at
60 | `Learning Python `__
61 |
62 |
63 |
64 | Contents
65 | ========
66 |
67 | .. toctree::
68 | :maxdepth: 4
69 |
70 | getting-started
71 | data
72 | model
73 | latentclass
74 | math
75 | machine-learning/index
76 | example/examples
77 |
78 |
79 |
--------------------------------------------------------------------------------
/larch/data_services/h5/h5pod/idga.py:
--------------------------------------------------------------------------------
1 |
2 | from .generic import *
3 | from tables import NoSuchNodeError
4 |
5 | class H5PodGA(H5Pod):
6 | """
7 | A HDF5 group node containing :ref:`idga` format data.
8 |
9 | The GA pod is conceptually similar to the CA pod, although cases are included
10 | by a lookup key instead of individually. Each array of data is stored as an array, where
11 | each array has rows of values attributable to arbitrary groups of casewise observations,
12 | and columns attributable to individual choice alternatives. The common use case is an OMX
13 | matrix file containing skims, where the case-wise observations each pull a single row
14 | from the skims.
15 |
16 | The H5PodGA has one special attribute , naming the row-index to use for each case. This
17 | name must exist in :ref:`idco` data.
18 |
19 | """
20 |
21 | def __init__(self, filename, rowindexes, *args, **kwargs):
22 | super().__init__(*args, filename=filename, **kwargs)
23 | if isinstance(rowindexes, str):
24 | self.rowindexes = self._groupnode._v_children[rowindexes][:]
25 | elif isinstance(rowindexes, numpy.ndarray):
26 | self.rowindexes = rowindexes.squeeze()
27 | else:
28 | raise TypeError('rowindexes must be str or ndarray')
29 |
30 | @property
31 | def podtype(self):
32 | return 'idga'
33 |
34 |
35 | def __getitem__(self, item):
36 |
37 | if isinstance(item, tuple) and len(item)>=2 and isinstance(item[-1], slice):
38 | names, slice_ = item[:-1], item[-1]
39 | else:
40 | names = item
41 | slice_ = None
42 |
43 | # convert a single name string to a one item list
44 | if isinstance(names, str):
45 | names = [names, ]
46 |
47 | dtype = numpy.float64
48 |
49 | result = numpy.zeros( [selector_len_for(slice_, self.shape[0]), *self.shape[1:], len(names)], dtype=dtype)
50 |
51 | for i, cmd in enumerate(names):
52 | temp = self._evaluate_single_item(cmd, None)
53 | if slice_ is not None:
54 | result[...,i] = temp[self.rowindexes[slice_],:]
55 | else:
56 | result[...,i] = temp[self.rowindexes,:]
57 | return result
58 |
59 | @property
60 | def shape(self):
61 | """The shape of the pod.
62 |
63 | """
64 | return tuple(self.rowindexes.shape) + self.metashape[1:]
65 |
66 | @property
67 | def metashape(self):
68 | """The shape of the underlying skims.
69 |
70 | """
71 | try:
72 | return tuple(self._groupnode._v_attrs.SHAPE)
73 | except (AttributeError, NoSuchNodeError):
74 | return tuple(self._groupnode._v_parent._v_attrs.SHAPE)
75 |
76 | def load_data_item(self, name, result, selector=None):
77 | """Load a slice of the pod arrays into an array in memory"""
78 | # convert a single name string to a one item list
79 |
80 | from ...general import _sqz_same
81 | _sqz_same(result.shape, [selector_len_for(selector, self.shape[0]), *self.shape[1:]])
82 |
83 | temp = self._evaluate_single_item(name, None)
84 | if selector is not None:
85 | result[:] = temp[self.rowindexes[selector], :]
86 | else:
87 | result[:] = temp[self.rowindexes, :]
88 | return result
89 |
90 |
--------------------------------------------------------------------------------
/book/example/legacy/003_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ============================================
4 | 3: MTC MNL Mode Choice, Zeroed Shared
5 | ============================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | .. testcode::
18 |
19 | d = larch.examples.MTC()
20 | m = larch.Model(dataservice=d)
21 |
22 | Model 3 posits the effect of income on all the automobile modes (drive alone, shared ride 2, and shared ride 3+)
23 | is the same, but the effect is different for the other modes. We include this
24 | constraint by setting the income coefficients in the utilities of the automobile modes to be equal.
25 | In this case, we set them to zero since drive alone is the reference mode. (`pp. 108 `_)
26 |
27 | We set the terms to zero can be done by omitting the terms altogether.
28 |
29 | .. testcode::
30 |
31 | from larch.roles import P, X, PX
32 | m.utility_co[2] = P("ASC_SR2")
33 | m.utility_co[3] = P("ASC_SR3P")
34 | m.utility_co[4] = P("ASC_TRAN") + P("hhinc#4") * X("hhinc")
35 | m.utility_co[5] = P("ASC_BIKE") + P("hhinc#5") * X("hhinc")
36 | m.utility_co[6] = P("ASC_WALK") + P("hhinc#6") * X("hhinc")
37 |
38 | .. testcode::
39 |
40 | m.utility_ca = PX("tottime") + PX("totcost")
41 |
42 | .. testcode::
43 |
44 | m.availability_var = '_avail_'
45 | m.choice_ca_var = '_choice_'
46 |
47 | .. testcode::
48 |
49 | m.ordering = (
50 | ("LOS", "totcost", "tottime", ),
51 | ("Income", "hhinc.*", ),
52 | ("ASCs", "ASC.*", ),
53 | )
54 |
55 | .. testcode::
56 |
57 | m.title = "MTC Example 3, Zeroed Shared"
58 |
59 | .. doctest::
60 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
61 |
62 | >>> m.load_data()
63 | >>> m.maximize_loglike()
64 | ┣ ...Optimization terminated successfully...
65 | >>> m.calculate_parameter_covariance()
66 | >>> m.loglike()
67 | -3627.233...
68 |
69 | >>> print(m.pfo()[['value','std_err','t_stat','robust_std_err','robust_t_stat']])
70 | value std_err t_stat robust_std_err robust_t_stat
71 | Category Parameter
72 | LOS totcost -0.0049 0.0002 -20.6062 0.0003 -17.3891
73 | tottime -0.0513 0.0031 -16.5594 0.0035 -14.8580
74 | Income hhinc#4 -0.0049 0.0018 -2.7295 0.0018 -2.7644
75 | hhinc#5 -0.0125 0.0053 -2.3451 0.0065 -1.9014
76 | hhinc#6 -0.0093 0.0030 -3.0977 0.0032 -2.9143
77 | ASCs ASC_BIKE -2.3981 0.3038 -7.8934 0.3600 -6.6622
78 | ASC_SR2 -2.3043 0.0547 -42.1387 0.0574 -40.1180
79 | ASC_SR3P -3.7036 0.0930 -39.8127 0.0950 -38.9751
80 | ASC_TRAN -0.6975 0.1304 -5.3504 0.1277 -5.4600
81 | ASC_WALK -0.2292 0.1933 -1.1857 0.2057 -1.1141
82 |
--------------------------------------------------------------------------------
/larch/doc/example/003_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ============================================
4 | 3: MTC MNL Mode Choice, Zeroed Shared
5 | ============================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | .. testcode::
18 |
19 | d = larch.examples.MTC()
20 | m = larch.Model(dataservice=d)
21 |
22 | Model 3 posits the effect of income on all the automobile modes (drive alone, shared ride 2, and shared ride 3+)
23 | is the same, but the effect is different for the other modes. We include this
24 | constraint by setting the income coefficients in the utilities of the automobile modes to be equal.
25 | In this case, we set them to zero since drive alone is the reference mode. (`pp. 108 `_)
26 |
27 | We set the terms to zero can be done by omitting the terms altogether.
28 |
29 | .. testcode::
30 |
31 | from larch.roles import P, X, PX
32 | m.utility_co[2] = P("ASC_SR2")
33 | m.utility_co[3] = P("ASC_SR3P")
34 | m.utility_co[4] = P("ASC_TRAN") + P("hhinc#4") * X("hhinc")
35 | m.utility_co[5] = P("ASC_BIKE") + P("hhinc#5") * X("hhinc")
36 | m.utility_co[6] = P("ASC_WALK") + P("hhinc#6") * X("hhinc")
37 |
38 | .. testcode::
39 |
40 | m.utility_ca = PX("tottime") + PX("totcost")
41 |
42 | .. testcode::
43 |
44 | m.availability_var = '_avail_'
45 | m.choice_ca_var = '_choice_'
46 |
47 | .. testcode::
48 |
49 | m.ordering = (
50 | ("LOS", "totcost", "tottime", ),
51 | ("Income", "hhinc.*", ),
52 | ("ASCs", "ASC.*", ),
53 | )
54 |
55 | .. testcode::
56 |
57 | m.title = "MTC Example 3, Zeroed Shared"
58 |
59 | .. doctest::
60 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
61 |
62 | >>> m.load_data()
63 | >>> m.maximize_loglike()
64 | ┣ ...Optimization terminated successfully...
65 | >>> m.calculate_parameter_covariance()
66 | >>> m.loglike()
67 | -3627.233...
68 |
69 | >>> print(m.pfo()[['value','std_err','t_stat','robust_std_err','robust_t_stat']])
70 | value std_err t_stat robust_std_err robust_t_stat
71 | Category Parameter
72 | LOS totcost -0.0049 0.0002 -20.6062 0.0003 -17.3891
73 | tottime -0.0513 0.0031 -16.5594 0.0035 -14.8580
74 | Income hhinc#4 -0.0049 0.0018 -2.7295 0.0018 -2.7644
75 | hhinc#5 -0.0125 0.0053 -2.3451 0.0065 -1.9014
76 | hhinc#6 -0.0093 0.0030 -3.0977 0.0032 -2.9143
77 | ASCs ASC_BIKE -2.3981 0.3038 -7.8934 0.3600 -6.6622
78 | ASC_SR2 -2.3043 0.0547 -42.1387 0.0574 -40.1180
79 | ASC_SR3P -3.7036 0.0930 -39.8127 0.0950 -38.9751
80 | ASC_TRAN -0.6975 0.1304 -5.3504 0.1277 -5.4600
81 | ASC_WALK -0.2292 0.1933 -1.1857 0.2057 -1.1141
82 |
--------------------------------------------------------------------------------
/larch/model/cast.h:
--------------------------------------------------------------------------------
1 | /*=====================================================================*
2 | * Copyright (C) 2012 Paul Mineiro *
3 | * All rights reserved. *
4 | * *
5 | * Redistribution and use in source and binary forms, with *
6 | * or without modification, are permitted provided that the *
7 | * following conditions are met: *
8 | * *
9 | * * Redistributions of source code must retain the *
10 | * above copyright notice, this list of conditions and *
11 | * the following disclaimer. *
12 | * *
13 | * * Redistributions in binary form must reproduce the *
14 | * above copyright notice, this list of conditions and *
15 | * the following disclaimer in the documentation and/or *
16 | * other materials provided with the distribution. *
17 | * *
18 | * * Neither the name of Paul Mineiro nor the names *
19 | * of other contributors may be used to endorse or promote *
20 | * products derived from this software without specific *
21 | * prior written permission. *
22 | * *
23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
24 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
25 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES *
26 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE *
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER *
28 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, *
29 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
30 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE *
31 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR *
32 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
33 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
34 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *
35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
36 | * POSSIBILITY OF SUCH DAMAGE. *
37 | * *
38 | * Contact: Paul Mineiro *
39 | *=====================================================================*/
40 |
41 | #ifndef __CAST_H_
42 |
43 | #ifdef __cplusplus
44 | #define cast_uint32_t static_cast
45 | #else
46 | #define cast_uint32_t (uint32_t)
47 | #endif
48 |
49 | #endif // __CAST_H_
50 |
--------------------------------------------------------------------------------
/book/example/legacy/009_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ================================================
4 | 9: MTC MNL Mode Choice, TTR = 4.0
5 | ================================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | Model 9 is formulated the same way as Model 8, only with the TTR set to 4. (`pp. 114 `_)
18 |
19 | .. testcode::
20 |
21 | d = larch.examples.MTC()
22 | m = larch.Model(dataservice=d)
23 |
24 | .. testcode::
25 |
26 | from larch.roles import P, X, PX
27 | m.utility_co[2] = P("ASC_SR2") + P("hhinc#2,3") * X("hhinc")
28 | m.utility_co[3] = P("ASC_SR3P") + P("hhinc#2,3") * X("hhinc")
29 | m.utility_co[4] = P("ASC_TRAN") + P("hhinc#4") * X("hhinc")
30 | m.utility_co[5] = P("ASC_BIKE") + P("hhinc#5") * X("hhinc")
31 | m.utility_co[6] = P("ASC_WALK") + P("hhinc#6") * X("hhinc")
32 |
33 | .. testcode::
34 |
35 | m.utility_ca = (
36 | + P("nonmotorized_time") * X("(altnum>4) * tottime")
37 | + P("motorized_time") * (X("(altnum <= 4) * ivtt")
38 | + 4 * X("(altnum <= 4) * ovtt")) + PX("totcost")
39 | )
40 |
41 | .. testcode::
42 |
43 | m.availability_var = '_avail_'
44 | m.choice_ca_var = '_choice_'
45 |
46 | .. testcode::
47 |
48 | m.ordering = (
49 | ("LOS", ".*cost.*", ".*time.*", ".*ivtt.*", ),
50 | ("Income", "hhinc.*", ),
51 | ("ASCs", "ASC.*", ),
52 | )
53 |
54 | .. doctest::
55 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
56 |
57 | >>> m.load_data()
58 | >>> m.maximize_loglike()
59 | ┣ ...Optimization terminated successfully...
60 | >>> m.calculate_parameter_covariance()
61 | >>> m.loglike()
62 | -3590.916...
63 |
64 | >>> print(m.pfo()[['value','std_err','t_stat','robust_std_err','robust_t_stat']])
65 | value std_err t_stat robust_std_err robust_t_stat
66 | Category Parameter
67 | LOS totcost -0.0048 0.0002 -20.3016 0.0003 -17.0773
68 | motorized_time -0.0173 0.0013 -13.6780 0.0014 -12.2046
69 | nonmotorized_time -0.0652 0.0053 -12.3235 0.0053 -12.2437
70 | Income hhinc#2,3 -0.0016 0.0014 -1.1304 0.0015 -1.0462
71 | hhinc#4 -0.0056 0.0018 -3.0211 0.0018 -3.1421
72 | hhinc#5 -0.0123 0.0052 -2.3445 0.0063 -1.9483
73 | hhinc#6 -0.0094 0.0031 -3.0885 0.0032 -2.9163
74 | ASCs ASC_BIKE -1.7748 0.3232 -5.4910 0.3693 -4.8052
75 | ASC_SR2 -2.3642 0.0969 -24.4032 0.1055 -22.4135
76 | ASC_SR3P -3.7992 0.1222 -31.0899 0.1276 -29.7836
77 | ASC_TRAN -0.5270 0.1479 -3.5636 0.1469 -3.5866
78 | ASC_WALK 0.4292 0.2526 1.6989 0.2583 1.6613
79 |
--------------------------------------------------------------------------------
/larch/doc/example/009_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ================================================
4 | 9: MTC MNL Mode Choice, TTR = 4.0
5 | ================================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | Model 9 is formulated the same way as Model 8, only with the TTR set to 4. (`pp. 114 `_)
18 |
19 | .. testcode::
20 |
21 | d = larch.examples.MTC()
22 | m = larch.Model(dataservice=d)
23 |
24 | .. testcode::
25 |
26 | from larch.roles import P, X, PX
27 | m.utility_co[2] = P("ASC_SR2") + P("hhinc#2,3") * X("hhinc")
28 | m.utility_co[3] = P("ASC_SR3P") + P("hhinc#2,3") * X("hhinc")
29 | m.utility_co[4] = P("ASC_TRAN") + P("hhinc#4") * X("hhinc")
30 | m.utility_co[5] = P("ASC_BIKE") + P("hhinc#5") * X("hhinc")
31 | m.utility_co[6] = P("ASC_WALK") + P("hhinc#6") * X("hhinc")
32 |
33 | .. testcode::
34 |
35 | m.utility_ca = (
36 | + P("nonmotorized_time") * X("(altnum>4) * tottime")
37 | + P("motorized_time") * (X("(altnum <= 4) * ivtt")
38 | + 4 * X("(altnum <= 4) * ovtt")) + PX("totcost")
39 | )
40 |
41 | .. testcode::
42 |
43 | m.availability_var = '_avail_'
44 | m.choice_ca_var = '_choice_'
45 |
46 | .. testcode::
47 |
48 | m.ordering = (
49 | ("LOS", ".*cost.*", ".*time.*", ".*ivtt.*", ),
50 | ("Income", "hhinc.*", ),
51 | ("ASCs", "ASC.*", ),
52 | )
53 |
54 | .. doctest::
55 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
56 |
57 | >>> m.load_data()
58 | >>> m.maximize_loglike()
59 | ┣ ...Optimization terminated successfully...
60 | >>> m.calculate_parameter_covariance()
61 | >>> m.loglike()
62 | -3590.916...
63 |
64 | >>> print(m.pfo()[['value','std_err','t_stat','robust_std_err','robust_t_stat']])
65 | value std_err t_stat robust_std_err robust_t_stat
66 | Category Parameter
67 | LOS totcost -0.0048 0.0002 -20.3016 0.0003 -17.0773
68 | motorized_time -0.0173 0.0013 -13.6780 0.0014 -12.2046
69 | nonmotorized_time -0.0652 0.0053 -12.3235 0.0053 -12.2437
70 | Income hhinc#2,3 -0.0016 0.0014 -1.1304 0.0015 -1.0462
71 | hhinc#4 -0.0056 0.0018 -3.0211 0.0018 -3.1421
72 | hhinc#5 -0.0123 0.0052 -2.3445 0.0063 -1.9483
73 | hhinc#6 -0.0094 0.0031 -3.0885 0.0032 -2.9163
74 | ASCs ASC_BIKE -1.7748 0.3232 -5.4910 0.3693 -4.8052
75 | ASC_SR2 -2.3642 0.0969 -24.4032 0.1055 -22.4135
76 | ASC_SR3P -3.7992 0.1222 -31.0899 0.1276 -29.7836
77 | ASC_TRAN -0.5270 0.1479 -3.5636 0.1469 -3.5866
78 | ASC_WALK 0.4292 0.2526 1.6989 0.2583 1.6613
--------------------------------------------------------------------------------
/larch/util/activitysim/tour_mode_choice.py:
--------------------------------------------------------------------------------
1 | import os
2 | import numpy as np
3 | import pandas as pd
4 | import yaml
5 | from typing import Collection
6 | from .. import Dict
7 | from pathlib import Path
8 |
9 | from .general import (
10 | remove_apostrophes,
11 | construct_nesting_tree,
12 | linear_utility_from_spec,
13 | explicit_value_parameters,
14 | apply_coefficients,
15 | clean_values,
16 | simple_simulate_data,
17 | )
18 | from ... import Model, DataFrames, P, X
19 |
20 |
21 |
22 | def tour_mode_choice_model(
23 | edb_directory="output/estimation_data_bundle/{name}/",
24 | return_data=False,
25 | ):
26 | data = simple_simulate_data(
27 | name="tour_mode_choice",
28 | edb_directory=edb_directory,
29 | )
30 | coefficients = data.coefficients
31 | coef_template = data.coef_template
32 | spec = data.spec
33 | chooser_data = data.chooser_data
34 | settings = data.settings
35 |
36 | chooser_data = clean_values(
37 | chooser_data,
38 | data.alt_names,
39 | alt_names_to_codes=data.alt_names_to_codes,
40 | choice_code='override_choice_code',
41 | )
42 |
43 | tree = construct_nesting_tree(data.alt_names, settings['NESTS'])
44 |
45 | purposes = list(coef_template.columns)
46 |
47 | # Setup purpose specific models
48 | m = {purpose: Model(graph=tree) for purpose in purposes}
49 | for alt_code, alt_name in tree.elemental_names().items():
50 | # Read in base utility function for this alt_name
51 | u = linear_utility_from_spec(
52 | spec,
53 | x_col='Label',
54 | p_col=alt_name,
55 | ignore_x=('#',),
56 | )
57 | for purpose in purposes:
58 | # Modify utility function based on template for purpose
59 | u_purp = sum(
60 | (
61 | P(coef_template[purpose].get(i.param, i.param))
62 | * i.data * i.scale
63 | )
64 | for i in u
65 | )
66 | m[purpose].utility_co[alt_code] = u_purp
67 |
68 | for model in m.values():
69 | explicit_value_parameters(model)
70 | apply_coefficients(coefficients, m)
71 |
72 | avail = {}
73 | for acode, aname in data.alt_codes_to_names.items():
74 | unavail_cols = list(
75 | (chooser_data[i.data] if i.data in chooser_data else chooser_data.eval(i.data))
76 | for i in m[purposes[0]].utility_co[acode]
77 | if i.param == "-999"
78 | )
79 | if len(unavail_cols):
80 | avail[acode] = sum(unavail_cols) == 0
81 | else:
82 | avail[acode] = 1
83 | avail = pd.DataFrame(avail).astype(np.int8)
84 | avail.index = chooser_data.index
85 |
86 | d = DataFrames(
87 | co=chooser_data,
88 | av=avail,
89 | alt_codes=data.alt_codes,
90 | alt_names=data.alt_names,
91 | )
92 |
93 | for purpose, model in m.items():
94 | model.dataservice = d.selector_co(f"tour_type=='{purpose}'")
95 | model.choice_co_code = 'override_choice_code'
96 |
97 | from larch.model.model_group import ModelGroup
98 | mg = ModelGroup(m.values())
99 |
100 | if return_data:
101 | return mg, Dict(
102 | edb_directory=Path(edb_directory),
103 | chooser_data=chooser_data,
104 | avail=avail,
105 | coefficients=coefficients,
106 | coef_template=coef_template,
107 | spec=spec,
108 | )
109 |
110 | return mg
--------------------------------------------------------------------------------
/book/example/legacy/002_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ============================================
4 | 2: MTC MNL Mode Choice, Motorized
5 | ============================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | Model 2 posits that the effect of income relative to drive alone is the same for both
18 | shared ride modes and transit but is different for the other modes.
19 | This is represented in the model by constraining the income coefficients in both
20 | shared ride modes and the transit mode to be equal. (`pp. 108 `_)
21 |
22 | To accomplish this, we give the income parameters for all three alternatives the same name.
23 |
24 | .. testcode::
25 |
26 | d = larch.examples.MTC()
27 | m = larch.Model(dataservice=d)
28 |
29 | .. testcode::
30 |
31 | from larch.roles import P, X, PX
32 | m.utility_co[2] = P("ASC_SR2") + P("hhinc#Moto") * X("hhinc")
33 | m.utility_co[3] = P("ASC_SR3P") + P("hhinc#Moto") * X("hhinc")
34 | m.utility_co[4] = P("ASC_TRAN") + P("hhinc#Moto") * X("hhinc")
35 | m.utility_co[5] = P("ASC_BIKE") + P("hhinc#5") * X("hhinc")
36 | m.utility_co[6] = P("ASC_WALK") + P("hhinc#6") * X("hhinc")
37 |
38 | .. testcode::
39 |
40 | m.utility_ca = PX("tottime") + PX("totcost")
41 |
42 | .. testcode::
43 |
44 | m.availability_var = '_avail_'
45 | m.choice_ca_var = '_choice_'
46 |
47 | .. testcode::
48 |
49 | m.ordering = (
50 | ("LOS", "totcost", "tottime", ),
51 | ("Income", "hhinc.*", ),
52 | ("ASCs", "ASC.*", ),
53 | )
54 |
55 | .. testcode::
56 |
57 | m.title = "MTC Example 2, Motorized"
58 |
59 | .. doctest::
60 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
61 |
62 | >>> m.load_data()
63 | >>> m.maximize_loglike()
64 | ┣ ...Optimization terminated successfully...
65 | >>> m.calculate_parameter_covariance()
66 | >>> m.loglike()
67 | -3628.285...
68 |
69 | .. doctest::
70 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
71 |
72 | >>> print(m.pfo()[['value','std_err','t_stat','robust_std_err','robust_t_stat']])
73 | value std_err t_stat robust_std_err robust_t_stat
74 | Category Parameter
75 | LOS totcost -0.0049 0.0002 -20.5689 0.0003 -17.2879
76 | tottime -0.0514 0.0031 -16.6075 0.0035 -14.8957
77 | Income hhinc#5 -0.0125 0.0053 -2.3500 0.0066 -1.9071
78 | hhinc#6 -0.0092 0.0030 -3.0658 0.0032 -2.8985
79 | hhinc#Moto -0.0029 0.0012 -2.3483 0.0012 -2.3163
80 | ASCs ASC_BIKE -2.3903 0.3043 -7.8545 0.3604 -6.6321
81 | ASC_SR2 -2.1370 0.0884 -24.1778 0.0917 -23.2969
82 | ASC_SR3P -3.5322 0.1153 -30.6408 0.1176 -30.0402
83 | ASC_TRAN -0.7995 0.1124 -7.1099 0.1117 -7.1582
84 | ASC_WALK -0.2297 0.1933 -1.1879 0.2051 -1.1197
85 |
--------------------------------------------------------------------------------
/larch/doc/example/002_mtc.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: larch
2 |
3 | ============================================
4 | 2: MTC MNL Mode Choice, Motorized
5 | ============================================
6 |
7 | .. testsetup:: *
8 |
9 | import larch
10 | import larch.examples
11 | import pandas
12 | pandas.set_option('display.max_columns',999)
13 | pandas.set_option('expand_frame_repr',False)
14 | pandas.set_option('display.precision',4)
15 | larch._doctest_mode_ = True
16 |
17 | Model 2 posits that the effect of income relative to drive alone is the same for both
18 | shared ride modes and transit but is different for the other modes.
19 | This is represented in the model by constraining the income coefficients in both
20 | shared ride modes and the transit mode to be equal. (`pp. 108 `_)
21 |
22 | To accomplish this, we give the income parameters for all three alternatives the same name.
23 |
24 | .. testcode::
25 |
26 | d = larch.examples.MTC()
27 | m = larch.Model(dataservice=d)
28 |
29 | .. testcode::
30 |
31 | from larch.roles import P, X, PX
32 | m.utility_co[2] = P("ASC_SR2") + P("hhinc#Moto") * X("hhinc")
33 | m.utility_co[3] = P("ASC_SR3P") + P("hhinc#Moto") * X("hhinc")
34 | m.utility_co[4] = P("ASC_TRAN") + P("hhinc#Moto") * X("hhinc")
35 | m.utility_co[5] = P("ASC_BIKE") + P("hhinc#5") * X("hhinc")
36 | m.utility_co[6] = P("ASC_WALK") + P("hhinc#6") * X("hhinc")
37 |
38 | .. testcode::
39 |
40 | m.utility_ca = PX("tottime") + PX("totcost")
41 |
42 | .. testcode::
43 |
44 | m.availability_var = '_avail_'
45 | m.choice_ca_var = '_choice_'
46 |
47 | .. testcode::
48 |
49 | m.ordering = (
50 | ("LOS", "totcost", "tottime", ),
51 | ("Income", "hhinc.*", ),
52 | ("ASCs", "ASC.*", ),
53 | )
54 |
55 | .. testcode::
56 |
57 | m.title = "MTC Example 2, Motorized"
58 |
59 | .. doctest::
60 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
61 |
62 | >>> m.load_data()
63 | >>> m.maximize_loglike()
64 | ┣ ...Optimization terminated successfully...
65 | >>> m.calculate_parameter_covariance()
66 | >>> m.loglike()
67 | -3628.285...
68 |
69 | .. doctest::
70 | :options: +ELLIPSIS, +NORMALIZE_WHITESPACE, +REPORT_NDIFF
71 |
72 | >>> print(m.pfo()[['value','std_err','t_stat','robust_std_err','robust_t_stat']])
73 | value std_err t_stat robust_std_err robust_t_stat
74 | Category Parameter
75 | LOS totcost -0.0049 0.0002 -20.5689 0.0003 -17.2879
76 | tottime -0.0514 0.0031 -16.6075 0.0035 -14.8957
77 | Income hhinc#5 -0.0125 0.0053 -2.3500 0.0066 -1.9071
78 | hhinc#6 -0.0092 0.0030 -3.0658 0.0032 -2.8985
79 | hhinc#Moto -0.0029 0.0012 -2.3483 0.0012 -2.3163
80 | ASCs ASC_BIKE -2.3903 0.3043 -7.8545 0.3604 -6.6321
81 | ASC_SR2 -2.1370 0.0884 -24.1778 0.0917 -23.2969
82 | ASC_SR3P -3.5322 0.1153 -30.6408 0.1176 -30.0402
83 | ASC_TRAN -0.7995 0.1124 -7.1099 0.1117 -7.1582
84 | ASC_WALK -0.2297 0.1933 -1.1879 0.2051 -1.1197
85 |
--------------------------------------------------------------------------------
/tests/test_model_group.py:
--------------------------------------------------------------------------------
1 | import larch
2 | import pandas as pd
3 | from larch import P,X,PX
4 | from pytest import approx
5 | from larch.data_warehouse import example_file
6 |
7 | def test_simple_model_group():
8 |
9 | df = pd.read_csv(example_file("MTCwork.csv.gz"))
10 | df.set_index(['casenum','altnum'], inplace=True)
11 | d = larch.DataFrames(df, ch='chose', crack=True)
12 | d.set_alternative_names({
13 | 1: 'DA',
14 | 2: 'SR2',
15 | 3: 'SR3+',
16 | 4: 'Transit',
17 | 5: 'Bike',
18 | 6: 'Walk',
19 | })
20 |
21 | m0 = larch.Model(dataservice=d)
22 | m0.utility_co[2] = P("ASC_SR2") + P("hhinc#2") * X("hhinc")
23 | m0.utility_co[3] = P("ASC_SR3P") + P("hhinc#3") * X("hhinc")
24 | m0.utility_co[4] = P("ASC_TRAN") + P("hhinc#4") * X("hhinc")
25 | m0.utility_co[5] = P("ASC_BIKE") + P("hhinc#5") * X("hhinc")
26 | m0.utility_co[6] = P("ASC_WALK") + P("hhinc#6") * X("hhinc")
27 | m0.utility_ca = (
28 | (P("tottime_m")*X("tottime") + P("totcost_m")*X("totcost"))*X("femdum == 0")
29 | +
30 | (P("tottime_f")*X("tottime") + P("totcost_f")*X("totcost"))*X("femdum == 1")
31 | )
32 |
33 | m1 = larch.Model(dataservice=d.selector_co("femdum == 0"))
34 | m1.utility_co[2] = P("ASC_SR2") + P("hhinc#2") * X("hhinc")
35 | m1.utility_co[3] = P("ASC_SR3P") + P("hhinc#3") * X("hhinc")
36 | m1.utility_co[4] = P("ASC_TRAN") + P("hhinc#4") * X("hhinc")
37 | m1.utility_co[5] = P("ASC_BIKE") + P("hhinc#5") * X("hhinc")
38 | m1.utility_co[6] = P("ASC_WALK") + P("hhinc#6") * X("hhinc")
39 | m1.utility_ca = P("tottime_m")*X("tottime") + P("totcost_m")*X("totcost")
40 |
41 | m2 = larch.Model(dataservice=d.selector_co("femdum == 1"))
42 | m2.utility_co[2] = P("ASC_SR2") + P("hhinc#2") * X("hhinc")
43 | m2.utility_co[3] = P("ASC_SR3P") + P("hhinc#3") * X("hhinc")
44 | m2.utility_co[4] = P("ASC_TRAN") + P("hhinc#4") * X("hhinc")
45 | m2.utility_co[5] = P("ASC_BIKE") + P("hhinc#5") * X("hhinc")
46 | m2.utility_co[6] = P("ASC_WALK") + P("hhinc#6") * X("hhinc")
47 | m2.utility_ca = P("tottime_f")*X("tottime") + P("totcost_f")*X("totcost")
48 |
49 | m0.load_data()
50 | assert m0.loglike2().ll == approx(-7309.600971749625)
51 |
52 | m1.load_data()
53 | assert m1.loglike2().ll == approx(-4068.8091617468717)
54 |
55 | m2.load_data()
56 | assert m2.loglike2().ll == approx(-3240.7918100027578)
57 |
58 | from larch.model.model_group import ModelGroup
59 |
60 | mg = ModelGroup([m1,m2])
61 |
62 | assert mg.loglike2().ll == approx(-7309.600971749625)
63 | assert mg.loglike() == approx(-7309.600971749625)
64 |
65 | pd.testing.assert_series_equal(
66 | mg.loglike2().dll.sort_index(), m0.loglike2().dll.sort_index()
67 | )
68 |
69 | m0.simple_step_bhhh()
70 | mg.set_values(**m0.pf.value)
71 |
72 | pd.testing.assert_series_equal(
73 | mg.loglike2().dll.sort_index(), m0.loglike2().dll.sort_index()
74 | )
75 |
76 | assert mg.loglike2().ll == approx(-4926.4822036792275)
77 | assert mg.check_d_loglike().data.similarity.min() > 4
78 |
79 | result = mg.maximize_loglike(method='slsqp')
80 | assert result.loglike == approx(-3620.697668335103)
81 |
82 | mg2 = ModelGroup([])
83 | mg2.append(m1)
84 | mg2.append(m2)
85 | assert mg2.loglike() == approx(-3620.697667552756)
86 |
87 | mg3 = ModelGroup([])
88 | mg3.append(m1)
89 | mg3.append(m2)
90 | mg3.doctor()
91 | assert mg3.loglike() == approx(-3620.697667552756)
92 |
--------------------------------------------------------------------------------
/larch/log/normal.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import sys
3 | from contextlib import contextmanager
4 | import time
5 | from ..util.timesize import timesize_stack
6 |
7 | FILE_LOG_FORMAT = '%(name)s.%(levelname)s: %(message)s'
8 | CONSOLE_LOG_FORMAT = '[%(asctime)s] %(name)s.%(levelname)s: %(message)s'
9 |
10 | DEFAULT_LOG_LEVEL = logging.WARNING
11 |
12 | from . import logger_name
13 |
14 | def log_to_console(level=None):
15 |
16 | if level is None:
17 | level = DEFAULT_LOG_LEVEL
18 |
19 | logger = logging.getLogger(logger_name)
20 |
21 | # avoid creation of multiple stream handlers for logging to console
22 | for entry in logger.handlers:
23 | if (isinstance(entry, logging.StreamHandler)) and (entry.formatter._fmt == CONSOLE_LOG_FORMAT):
24 | return logger
25 |
26 | console_handler = logging.StreamHandler(stream=sys.stdout)
27 | console_handler.setLevel(level)
28 | console_handler.setFormatter(logging.Formatter(CONSOLE_LOG_FORMAT))
29 | logger.addHandler(console_handler)
30 | if level < logger.getEffectiveLevel():
31 | logger.setLevel(level)
32 |
33 | return logger
34 |
35 |
36 | def log_to_file(filename, level=None):
37 |
38 | if level is None:
39 | level = DEFAULT_LOG_LEVEL
40 |
41 | logger = logging.getLogger(logger_name)
42 |
43 | # avoid creation of multiple file handlers for logging to the same file
44 | for entry in logger.handlers:
45 | if (isinstance(entry, logging.FileHandler)) and (entry.baseFilename == filename):
46 | return logger
47 |
48 | file_handler = logging.FileHandler(filename)
49 | file_handler.setLevel(level)
50 | file_handler.setFormatter(logging.Formatter(FILE_LOG_FORMAT))
51 | logger.addHandler(file_handler)
52 |
53 | return logger
54 |
55 |
56 | logger = log = log_to_console()
57 |
58 |
59 | @contextmanager
60 | def timing_log(label=''):
61 | start_time = time.time()
62 | log.critical(f"