Skip to content

Commit 6d214c9

Browse files
authored
Preparation for conda update.
2 parents cccea20 + 24160bd commit 6d214c9

26 files changed

+240
-212
lines changed

.conda/bld.bat

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"%PYTHON%" setup.py install
2+
if errorlevel 1 exit 1

.conda/build.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
$PYTHON setup.py install # Python command to install the script.

.conda/meta.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{% set data = load_setup_py_data() %}
2+
3+
package:
4+
name: ruspy
5+
version: {{ data.get('version') }}
6+
7+
source:
8+
# git_url is nice in that it won't capture devenv stuff. However, it only captures
9+
# committed code, so pay attention.
10+
git_url: ../
11+
12+
build:
13+
number: 0
14+
noarch: python
15+
16+
requirements:
17+
host:
18+
- python >=3.6,<3.8
19+
run:
20+
- python >=3.6,<3.8
21+
- statsmodels
22+
- numba >=0.42
23+
- numpy
24+
- pandas = 1.0.0
25+
- pytest
26+
- pytest-xdist
27+
- scipy
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Continuous Integration Workflow
2+
on:
3+
push:
4+
branches:
5+
- master
6+
pull_request:
7+
branches:
8+
- '*'
9+
10+
jobs:
11+
run-tests:
12+
name: Run tests for ${{ matrix.os }} on ${{ matrix.python-version }}
13+
runs-on: ${{ matrix.os }}
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
os: ['ubuntu-latest', 'macos-latest', 'windows-latest']
18+
python-version: ['3.6', '3.7']
19+
steps:
20+
- uses: actions/checkout@v2
21+
- uses: goanpeca/setup-miniconda@v1
22+
with:
23+
activate-environment: ruspy
24+
auto-update-conda: true
25+
environment-file: environment.yml
26+
python-version: ${{ matrix.python-version }}
27+
28+
- name: Run pytest.
29+
shell: bash -l {0}
30+
run: pytest --cov-report=xml --cov=./
31+
32+
- name: Upload coverage report.
33+
if: runner.os == 'Linux' && matrix.python-version == '3.7'
34+
uses: codecov/codecov-action@v1
35+
with:
36+
token: df3e71e7-316d-4a04-b4d1-f76b90dfced7
37+
38+
- name: Run pre-commits.
39+
shell: bash -l {0}
40+
run: |
41+
pre-commit install -f --install-hooks
42+
pre-commit run --all-files

README.md

Lines changed: 0 additions & 21 deletions
This file was deleted.

README.rst

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
ruspy
2+
======
3+
4+
.. image:: https://readthedocs.org/projects/ruspy/badge/?version=latest
5+
:target: https://ruspy.readthedocs.io/en/latest/?badge=latest
6+
7+
.. image:: https://img.shields.io/badge/License-MIT-yellow.svg
8+
:target: https://opensource.org/licenses/MIT
9+
10+
.. image:: https://github.com/OpenSourceEconomics/ruspy/workflows/Continuous%20Integration%20Workflow/badge.svg
11+
:target: https://github.com/OpenSourceEconomics/ruspy/actions
12+
13+
.. image:: https://codecov.io/gh/OpenSourceEconomics/robupy/branch/master/graph/badge.svg
14+
:target: https://codecov.io/gh/OpenSourceEconomics/robupy
15+
16+
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
17+
:target: https://github.com/psf/black
18+
19+
``ruspy`` is an open-source package for the simulation and estimation of a prototypical
20+
infinite-horizon dynamic discrete choice model based on
21+
22+
Rust, J. (1987). [Optimal replacement of GMC bus engines: An empirical model of Harold Zurcher.](https://doi.org/10.2307/1911259) *Econometrica, 55* (5), 999-1033.
23+
24+
You can install ``ruspy`` via conda with
25+
26+
.. code-block:: bash
27+
28+
$ conda config --add channels conda-forge
29+
$ conda install -c opensourceeconomics ruspy
30+
31+
Please visit our `online documentation <https://ruspy.readthedocs.io/>`_ for
32+
tutorials and other information.
33+
34+
35+
Citation
36+
--------
37+
38+
If you use ruspy for your research, do not forget to cite it with
39+
40+
.. code-block:: bash
41+
42+
@Unpublished{ruspy-1.0,
43+
Author = {Maximilian Blesch},
44+
Title = {robupy - An open-source package for the simulation and estimation of a prototypical infinite-horizon dynamic discrete choice model based on Rust (1987)},
45+
Year = {2019},
46+
Url = {https://github.com/OpenSourceEconomics/ruspy},
47+
}

codecov.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
coverage:
2+
range: 50...100
3+
precision: 2
4+
round: down
5+
status:
6+
project:
7+
threshold: 5%
8+
patch:
9+
threshold: 5%
10+
11+
ignore:
12+
- "setup.py"
13+
- "respy/config.py"
14+
- "ruspy/tests/**/*"

docs/source/economics.rst

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,30 @@ his expected total discounted utility with respect to the expected mileage usage
1414
bus. Each month :math:`t`, a bus arrives at the bus depot in state :math:`s_t = (x_t,
1515
\epsilon_t(a_t))` containing the mileage since last engine replacement :math:`x_t` and
1616
other signs of wear and tear plus decision specific information :math:`\epsilon_t(a_t)`.
17-
Harold Zurcher is faced with the decision to either conduct a complete engine replacement
18-
:math:`(a_t = 1)` or to perform basic maintenance work :math:`(a_t = 0)`. The cost of
19-
maintenance :math:`c(x_t, \theta_1)` increases with the mileage state, while the cost of
20-
replacement :math:`RC` remains constant. Notationwise :math:`\theta_1` captures the
21-
structural parameters shaping the maintenance cost function. In the case of an engine
22-
replacement, the mileage state is reset to zero.
17+
Harold Zurcher is faced with the decision to either conduct a complete engine
18+
replacement :math:`(a_t = 1)` or to perform basic maintenance work :math:`(a_t = 0)`.
19+
The cost of maintenance :math:`c(x_t, \theta_1)` increases with the mileage state,
20+
while the cost of replacement :math:`RC` remains constant. Notationwise
21+
:math:`\theta_1` captures the structural parameters shaping the maintenance cost
22+
function. In the case of an engine replacement, the mileage state is reset to zero.
2323

2424
The immediate utility of each action in month :math:`t` is assumed to be additively
2525
separable and given by:
2626

2727
.. math::
2828
2929
\begin{align}
30-
u(a_t, x_t, \theta_1, RC) + \epsilon_t(a_t) \quad \text{with} \quad u(a_t, x_t, \theta_1, RC) = \begin{cases}
30+
u(a_t, x_t, \theta_1, RC) + \epsilon_t(a_t) \quad \text{with} \quad u(a_t, x_t,
31+
\theta_1, RC) = \begin{cases}
3132
-RC - c(0, \theta_1) & a_t = 1 \\
3233
-c(x_t, \theta_1) & a_t = 0.
3334
\end{cases}
3435
\end{align}
3536
3637
3738
The objective of Harold Zurcher is to choose a strategy :math:`\pi` of all strategies
38-
:math:`\Pi` to maximize the utility over infinite horizon and therefore the current value
39-
in period :math:`t` and state :math:`s_t` is given by:
39+
:math:`\Pi` to maximize the utility over infinite horizon and therefore the current
40+
value in period :math:`t` and state :math:`s_t` is given by:
4041

4142
.. math::
4243
@@ -47,9 +48,9 @@ in period :math:`t` and state :math:`s_t` is given by:
4748
The discount factor :math:`\delta` weighs the utilities over all periods and therefore
4849
captures the preference of utility in current and future time periods. As the model
4950
assumes stationary utility, as well as stationary transition probabilities the future
50-
looks the same, whether the agent is at time :math:`t` in state :math:`s` or at any other
51-
time. Therefore the optimal decision in every period can be interferred by the Bellman
52-
equation:
51+
looks the same, whether the agent is at time :math:`t` in state :math:`s` or at any
52+
other time. Therefore the optimal decision in every period can be interferred by the
53+
Bellman equation:
5354

5455
.. math::
5556
@@ -85,8 +86,8 @@ observable and unobservable state variables, i.e.
8586
\end{equation}
8687
8788
and furthermore assumes that the unobservables :math:`\epsilon_t(a_t)` are independent
88-
and identically distributed according to an extreme value distribution with mean zero and
89-
scale parameter one, i.e.:
89+
and identically distributed according to an extreme value distribution with mean zero
90+
and scale parameter one, i.e.:
9091

9192
.. math::
9293
@@ -99,14 +100,14 @@ where :math:`\theta_2 = 0.577216`, i.e. the Euler-Mascheroni constant.
99100

100101
Rust (1988) shows that these two assumptions, together with the additive separability
101102
between the observed and unobserved state variables in the immediate utilities, imply
102-
that :math:`EV_\theta` is a function independent of :math:`\epsilon_t` and the unique fix
103-
point of a contraction mapping on the reduced space of all state action pairs :math:`(x,
104-
a)`. Furthermore, the regenerative property of the process yields for all states
105-
:math:`x`, that the expected value of replacement corresponds to the expected value of
106-
maintenance in state :math:`0`, i.e. :math:`EV_\theta(x, 1) = EV_\theta(0, 0)`. Thus
107-
:math:`EV_\theta` is the unique fixed point on the observed mileage state :math:`x` only.
108-
Therefore in the following :math:`EV_\theta(x)` refers to :math:`EV_\theta(x, 0)`. The
109-
contraction mapping is then given by:
103+
that :math:`EV_\theta` is a function independent of :math:`\epsilon_t` and the unique
104+
fix point of a contraction mapping on the reduced space of all state action pairs
105+
:math:`(x,a)`. Furthermore, the regenerative property of the process yields for all
106+
states :math:`x`, that the expected value of replacement corresponds to the expected
107+
value of maintenance in state :math:`0`, i.e. :math:`EV_\theta(x, 1) = EV_\theta(0,
108+
0)`. Thus :math:`EV_\theta` is the unique fixed point on the observed mileage state
109+
:math:`x` only. Therefore in the following :math:`EV_\theta(x)` refers to
110+
:math:`EV_\theta(x, 0)`. The contraction mapping is then given by:
110111

111112
.. math::
112113
@@ -122,15 +123,15 @@ closed-form solution given by the multinomial logit formula (McFadden, 1973):
122123
123124
\begin{equation}
124125
P(a|x, \theta) = \frac{\exp(u(a, x, RC, \theta_1) + \delta EV_\theta((a-1) \cdot
125-
x))}{\sum_{i \in \{0, 1\}} \exp(u(i, x, RC, \theta_1) + \delta EV_\theta((i - 1) x))}
126+
x))}{\sum_{i \in \{0, 1\}} \exp(u(i, x, RC, \theta_1) + \delta EV_\theta((i - 1)x))}
126127
\end{equation}
127128
128-
These closed form solutions allow to estimate the structural parameters driving Zurcher's
129-
decisions. Given the data :math:`\{a_0, ....a_T, x_0, ..., x_T\}` for a single bus, one
130-
can form the likelihood function :math:`l^f(a_1, ..., a_T, x_1, ...., x_T | a_0, x_0,
131-
\theta)` and estimate the parameter vector $\theta$ by maximum likelihood. Rust (1988)
132-
proofs that this function has due to the conditional independence assumption a simple
133-
form:
129+
These closed form solutions allow to estimate the structural parameters driving
130+
Zurcher's decisions. Given the data :math:`\{a_0, ....a_T, x_0, ..., x_T\}` for a
131+
single bus, one can form the likelihood function :math:`l^f(a_1, ..., a_T, x_1, ....,
132+
x_T | a_0, x_0, \theta)` and estimate the parameter vector $\theta$ by maximum
133+
likelihood. Rust (1988) proofs that this function has due to the conditional
134+
independence assumption a simple form:
134135

135136
.. math::
136137

docs/source/estimation.rst

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ If you want to replicate Rust (1987) you can download the raw data from John Rus
3636
`website <https://editorialexpress.com/jrust/research.html>`_ and prepare it yourself it
3737
the desired manner. Or you can use the functions in `zurcher-data
3838
<https://github.com/OpenSourceEconomics/zurcher-data>`_ which are provided by the
39-
`OpenSourceEconomics <https://github.com/OpenSourceEconomics>`_ community and tailored to
40-
the ruspy package. In the demonstration section these functions are used to replicate the
41-
results documented.
39+
`OpenSourceEconomics <https://github.com/OpenSourceEconomics>`_ community and
40+
tailored to the ruspy package. In the demonstration section these functions are used
41+
to replicate the results documented.
4242

4343

4444
The estimation function
@@ -61,11 +61,11 @@ Estimation initialization dictionary
6161
------------------------------------
6262

6363
The initialization dictionary contains model, optimizer and algorithmic specific
64-
information. The information on theses three categories is saved in subdictionaries under
65-
the keys **model_specifications**, **optimizer** and **alg_details**. The model specific
66-
information is mandatory, while the others are optional. If not given, just the default
67-
values are selected. See :ref:`alg_details` for the possible keys and the default values.
68-
The mandatory model specific information keys are:
64+
information. The information on theses three categories is saved in subdictionaries
65+
under the keys **model_specifications**, **optimizer** and **alg_details**. The
66+
model specific information is mandatory, while the others are optional. If not given,
67+
just the default values are selected. See :ref:`alg_details` for the possible keys
68+
and the default values. The mandatory model specific information keys are:
6969

7070
**discount_factor :** *(float)* The discount factor. See :ref:`disc_fac` for details.
7171

@@ -79,8 +79,10 @@ details.
7979

8080

8181

82-
The dictionary under **optimizer** allows to specify the optimizer from the `scipy library
83-
<http://lagrange.univ-lyon1.fr/docs/scipy/0.17.1/generated/scipy.optimize.minimize.html>`_. The entries of the dictionary are all *strings* and the following keys are so far possible:
82+
The dictionary under **optimizer** allows to specify the optimizer from the `scipy
83+
library <http://lagrange.univ-lyon1.fr/docs/scipy/0.17.1/generated/scipy.optimize
84+
.minimize.html>`_. The entries of the dictionary are all *strings* and the following
85+
keys are so far possible:
8486

8587
**optimizer_name :** The name of the optimizer. See `scipy library
8688
<http://lagrange.univ-lyon1.fr/docs/scipy/0.17.1/generated/
@@ -154,8 +156,8 @@ corresponding function in the code is:
154156

155157

156158
The ``estimate_transitions`` function minimizes now the ``loglike_trans`` function by
157-
calling a minimize routine from the `scipy library
158-
<http://lagrange.univ-lyon1.fr/docs/scipy/0.17.1/generated/scipy.optimize.minimize.html>`_.
159+
calling a minimize routine from the `scipy library <http://lagrange.univ-lyon1
160+
.fr/docs/scipy/0.17.1/generated/scipy.optimize.minimize.html>`_.
159161
Even though transition probabilities need to add up to 1 and have to be positive, there
160162
are no constraints on the minimized parameters. The constraints are applied inside
161163
``loglike_trans`` by a reparametrization function:
@@ -222,7 +224,10 @@ The transition matrix is then used for the cost parameter estimation.
222224
Cost parameter estimation
223225
-------------------------
224226

225-
The cost parameters are estimated directly by minimizing the log-likelihood and the corresponding jacobian function with a minimize function from the `scipy library <http://lagrange.univ-lyon1.fr/docs/scipy/0.17.1/generated/scipy.optimize.minimize.html>`_ . The functions can be found in ``ruspy.estimation.est_cost_params``:
227+
The cost parameters are estimated directly by minimizing the log-likelihood and the
228+
corresponding jacobian function with a minimize function from the `scipy library
229+
<http://lagrange.univ-lyon1.fr/docs/scipy/0.17.1/generated/scipy.optimize.minimize
230+
.html>`_ . The functions can be found in ``ruspy.estimation.est_cost_params``:
226231

227232

228233
.. currentmodule:: ruspy.estimation.est_cost_params
@@ -246,8 +251,8 @@ calculation of the same fixed point is avoided by the following function:
246251
get_ev
247252

248253
After successful minimization, some results of the `scipy result dictionary
249-
<https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.optimize.OptimizeResult.html>`_
250-
are used to construct ruspys cost parameter results:
254+
<https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.optimize
255+
.OptimizeResult.html>`_ are used to construct ruspys cost parameter results:
251256

252257

253258
.. _result_costs:
@@ -264,8 +269,9 @@ The dictionary containing the cost parameter results has the following keys:
264269

265270
**jac :** *(numpy.array)* The value of the estimates' jacobian.
266271

267-
**95_conf_interv :** *(numpy.array)* :math:`2 \times num\_states` dimensional numpy.array
268-
containing the bootstrapped (1000 replications) 95% confidence interval bounds.
272+
**95_conf_interv :** *(numpy.array)* :math:`2 \times num\_states` dimensional
273+
numpy.array containing the bootstrapped (1000 replications) 95% confidence interval
274+
bounds.
269275

270276
**std_errors :** *(numpy.array)* :math:`num\_states` dimensional numpy.array with
271277
bootstrapped standard errors for each parameter.
@@ -308,7 +314,7 @@ Demonstration
308314
-------------
309315

310316
In the promotion folder of the repository are two demonstration jupyter notebooks. The
311-
`replication
312-
<https://github.com/OpenSourceEconomics/ruspy/blob/kantevorich/promotion/replication/replication.ipynb>`_
313-
notebook allows to easily experiment with the estimation methods described here. If you
314-
have have everything setup, then it should be easy to run it.
317+
`replication <https://github.com/OpenSourceEconomics/ruspy/blob/kantevorich/promotion
318+
/replication/replication.ipynb>`_ notebook allows to easily experiment with the
319+
estimation methods described here. If you have have everything setup, then it should
320+
be easy to run it.

0 commit comments

Comments
 (0)