├── .gitignore
├── .travis.yml
├── INSTALLING.md
├── LICENSE.txt
├── MANIFEST.in
├── Makefile
├── README.md
├── check.sh
├── conda
├── meta.yaml
└── upload.sh
├── doc
├── api.rst
├── conf.py
└── index.rst
├── docker
└── ubuntu1604
├── pythenv.sh
├── setup.py
├── src
├── __init__.py
└── trcrpm.py
├── tests
├── __init__.py
├── test_data_transforms.py
├── test_resample_crashes.py
└── test_serialize.py
└── tutorials
├── basic-tutorial.ipynb
├── bimodal-posteriors.ipynb
├── flu-forecasting.ipynb
└── resources
├── bimodal-posteriors-data.ipynb
├── bimodal-posteriors.png
├── cdc-usa.csv
├── ggToyMissing.csv
└── up-or-down.csv
/.gitignore:
--------------------------------------------------------------------------------
1 | .cache/
2 | *.egg-info/
3 | .eggs
4 | .ipynb_checkpoints/
5 |
6 | /bayeslite.egg-info/
7 | /build/
8 | /dist/
9 | /src/version.py
10 |
11 | __pycache__
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | dist: trusty
3 |
4 | env:
5 | global:
6 | - PACKAGE_NAME=trcrpm
7 | # Get all the branches referencing this commit
8 | - REAL_BRANCH=$(git ls-remote origin | sed -n "\|$TRAVIS_COMMIT\s\+refs/heads/|{s///p}")
9 |
10 | python:
11 | - 2.7
12 |
13 | install:
14 | - wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O miniconda.sh
15 | - bash miniconda.sh -b -p ${HOME}/miniconda
16 | - export PATH="${HOME}/miniconda/bin${PATH:+:${PATH}}"
17 | - hash -r
18 | - conda config --set always_yes yes --set changeps1 no
19 | - conda install -q conda=4.6.14 conda-build
20 |
21 | script:
22 | - export CONDA_PACKAGE_VERSION="${TRAVIS_TAG:-$(date +%Y.%m.%d)}"
23 | # Remove leading v from tags if they exist.
24 | - CONDA_PACKAGE_VERSION="$(sed s/^v// <<<$CONDA_PACKAGE_VERSION)"
25 | # Use "edge" channel (latest master) for testing with cgpm/crosscat.
26 | - conda build . -c probcomp/label/edge -c anaconda
27 |
28 | after_success:
29 | - bash conda/upload.sh
30 |
--------------------------------------------------------------------------------
/INSTALLING.md:
--------------------------------------------------------------------------------
1 | # Installation Guide
2 |
3 | There are various ways to install this package.
4 |
5 | ### Installing using conda
6 |
7 | The package is available on the [probcomp Anaconda cloud channel](https://anaconda.org/probcomp/trcrpm).
8 | The latest release can be installed into your conda environment using:
9 |
10 | ```bash
11 | $ conda install -c probcomp trcrpm
12 | ```
13 |
14 | To install the nightly "bleeding edge" version, which has not yet been tagged
15 | into a new release, use:
16 |
17 | ```bash
18 | $ conda install -c probcomp/label/edge trcrpm
19 | ```
20 |
21 | If you have not used conda before and do not have an environment, these
22 | steps show an end-to-end installation process:
23 |
24 | ```bash
25 | $ wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O miniconda.sh
26 | $ bash miniconda.sh -b -p ${HOME}/miniconda
27 | $ export PATH="${HOME}/miniconda/bin:${PATH}"
28 | $ conda update --yes conda
29 | $ conda create --name probcomp --channel probcomp/label/edge --yes python=2.7 trcrpm
30 | $ source activate probcomp
31 | ```
32 |
33 | ### Installing into a virtualenv (for Linux users)
34 |
35 | All 3rd party dependencies are available in the Ubuntu 16.04 LTS standard
36 | repositories (apt), so the process should be straightforward. Replicate the
37 | instructions in the [Dockerfile](./docker/ubuntu1604) by writing the commands
38 | directly in your own Linux environment.
39 |
40 | ### Installing into a docker image
41 |
42 | If all else fails, you can obtain obtain a shell with the package by building
43 | the Dockerfile. The first step builds an image called `probcomp/trcrpm`, the
44 | second step runs a container with name `trcrp` and gives you a shell, and the
45 | third step activates the python virtualenv containing the software.
46 |
47 | ```bash
48 | $ docker build -t probcomp/trcrpm -f docker/ubuntu1604 .
49 | $ docker run -it --name trcrp probcomp/trcrpm /bin/bash
50 | root@9cc66a75a0e2:/# source /venv/bin/activate
51 | ```
52 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE.txt
2 | include README.md
3 | include VERSION
4 | include check.sh
5 | include pythenv.sh
6 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: default-target
2 | default-target: build
3 |
4 | ###############################################################################
5 | ### User-settable variables
6 |
7 | # Commands to run in the build process.
8 | PYTHON = python
9 | SPHINX_BUILD = sphinx-build
10 | SPHINX_FLAGS =
11 |
12 | # Options for above commands.
13 | SPHINXOPTS =
14 | PYTHONOPTS =
15 | SETUPPYOPTS =
16 |
17 | ###############################################################################
18 | ### Targets
19 |
20 | # build: Build trcrpm python package.
21 | .PHONY: build
22 | build: setup.py
23 | $(PYTHON) $(PYTHONOPTS) setup.py $(SETUPPYOPTS) build
24 |
25 | # doc: Build the trcrpm documentation.
26 | .PHONY: doc
27 | doc: pythenv.sh build
28 | rm -rf build/doc.tmp && \
29 | ./pythenv.sh $(SPHINX_BUILD) $(SPHINX_FLAGS) \
30 | -b html doc/ build/doc.tmp && \
31 | rm -rf build/doc && \
32 | mv -f build/doc.tmp build/doc
33 |
34 | # check: (Build trcrpm and) run the tests.
35 | .PHONY: check
36 | check: check.sh
37 | ./check.sh
38 |
39 | # clean: Remove build products.
40 | .PHONY: clean
41 | clean:
42 | -rm -rf build
43 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Temporally-Reweighted Chinese Restaurant Process Mixture Models
2 |
3 | [](https://travis-ci.org/probcomp/trcrpm)
4 | [](https://anaconda.org/probcomp/trcrpm)
5 | [](https://anaconda.org/probcomp/trcrpm)
6 |
7 |
8 | A nonparametric Bayesian method for clustering, imputation, and forecasting
9 | in multivariate time series data.
10 |
11 | ## Installing
12 |
13 | There are various ways to install this package. The easiest way is to pull
14 | the package from conda,
15 |
16 | ```bash
17 | $ conda install -c probcomp trcrpm
18 | ```
19 |
20 | For more information, see [INSTALLING.md](./INSTALLING.md)
21 |
22 | ## Getting started
23 |
24 | For tutorials showing how to use the method, refer to the
25 | [tutorials](./tutorials) directory.
26 |
27 |
28 |
29 | ## Documentation
30 |
31 | The [API reference](https://probcomp-1.csail.mit.edu/trcrpm/doc/api.html) is
32 | available online. Use `make doc` to build the documentation locally (needs
33 | [`sphinx`](http://www.sphinx-doc.org/en/stable/install.html) and
34 | [`napoleon`](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/)).
35 |
36 | ## References
37 |
38 | Feras A. Saad and Vikash K. Mansinghka, [Temporally-Reweighted Chinese
39 | Restaurant Process Mixtures For Clustering, Imputing, and
40 | Forecasting Multivariate Time Series](http://proceedings.mlr.press/v84/saad18a.html).
41 | In AISTATS 2018: _Proceedings of the 20th International Conference on Artificial
42 | Intelligence and Statistics_, Proceedings of Machine Learning Research 84,
43 | Playa Blanca, Lanzarote, Canary Islands, 2018.
44 |
45 | To cite this work, please use the following BibTeX reference.
46 |
47 | ```bibtex
48 | @inproceedings{saad2018trcrpm,
49 | author = {Saad, Feras A. and Mansinghka, Vikash K.},
50 | title = {Temporally-reweighted {C}hinese restaurant process mixtures for clustering, imputing, and forecasting multivariate time series},
51 | booktitle = {AISTATS 2018: Proceedings of the 21st International Conference on Artificial Intelligence and Statistics},
52 | series = {Proceedings of Machine Learning Research},
53 | volume = 84,
54 | pages = {755--764},
55 | publisher = {PMLR},
56 | address = {Playa Blanca, Lanzarote, Canary Islands},
57 | year = {2018},
58 | keywords = {probabilistic inference, multivariate time series, nonparametric Bayes, structure learning},
59 | }
60 |
61 | ```
62 |
63 | ## License
64 |
65 | Copyright (c) 2015-2018 MIT Probabilistic Computing Project
66 |
67 | Licensed under the Apache License, Version 2.0 (the "License");
68 | you may not use this file except in compliance with the License.
69 | You may obtain a copy of the License at
70 |
71 | http://www.apache.org/licenses/LICENSE-2.0
72 |
73 | Unless required by applicable law or agreed to in writing, software
74 | distributed under the License is distributed on an "AS IS" BASIS,
75 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
76 | See the License for the specific language governing permissions and
77 | limitations under the License.
78 |
--------------------------------------------------------------------------------
/check.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Copyright (c) 2018 MIT Probabilistic Computing Project.
4 | # Released under Apache 2.0; refer to LICENSE.txt.
5 |
6 | set -Ceux
7 |
8 | : ${PYTHON:=python}
9 |
10 | root=$(cd -- "$(dirname -- "$0")" && pwd)
11 |
12 | (
13 | set -Ceu
14 | cd -- "${root}"
15 | rm -rf build
16 | "$PYTHON" setup.py build
17 | if [ $# -eq 0 ]; then
18 | # By default, when running all tests, skip tests that have
19 | # been marked for continuous integration by using __ci_ in
20 | # their names. (git grep __ci_ to find these.)
21 | ./pythenv.sh "$PYTHON" -m pytest --pyargs trcrpm -k "not __ci_"
22 | else
23 | # If args are specified, run all tests, including continuous
24 | # integration tests, for the selected components.
25 | ./pythenv.sh "$PYTHON" -m pytest "$@"
26 | fi
27 | )
28 |
--------------------------------------------------------------------------------
/conda/meta.yaml:
--------------------------------------------------------------------------------
1 | package:
2 | name: trcrpm
3 | version: {{ CONDA_PACKAGE_VERSION }}
4 |
5 | source:
6 | path: ../
7 |
8 | build:
9 | script: python setup.py install
10 |
11 | requirements:
12 | build:
13 | - cython 0.23.*
14 | - git
15 | - numpy 1.11.*
16 | - python 2.7.*
17 | run:
18 | - cgpm
19 | - crosscat
20 | - ipykernel 4.8.*
21 | - jupyter
22 | - matplotlib 1.5.*
23 | - numpy 1.11.*
24 | - pandas 0.18.*
25 | - six 1.10.*
26 |
27 | test:
28 | requires:
29 | - cgpm
30 | - crosscat
31 | - numpy 1.11.*
32 | - pandas 0.18.*
33 | - pytest 2.8.*
34 | commands:
35 | - python -m pytest --pyargs trcrpm
36 |
37 | about:
38 | home: https://github.com/probcomp/trcrpm
39 | license: Apache
40 | license_file: LICENSE.txt
41 |
--------------------------------------------------------------------------------
/conda/upload.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -ev
3 |
4 | MASTER_BRANCH="master"
5 | echo ${REAL_BRANCH}
6 |
7 | # The logic below is necessary due to the fact that on a tagged build,
8 | # TRAVIS_BRANCH and TRAVIS_TAG are the same in the case of a tagged build, use
9 | # the REAL_BRANCH environment variable defined in .travis.yml.
10 | if [ -n "${TRAVIS_TAG}" ]; then
11 | conda install anaconda-client
12 |
13 | # If tag did not come from master, add the "dev" label.
14 | if [ ${REAL_BRANCH} = ${MASTER_BRANCH} ]; then
15 | echo 'Uploading to anaconda main'
16 | anaconda -t ${CONDA_UPLOAD_TOKEN} upload \
17 | -u ${CONDA_USER} \
18 | ~/miniconda/conda-bld/linux-64/${PACKAGE_NAME}-*.tar.bz2 --force
19 | echo 'Uploaded'
20 | else
21 | anaconda -t ${CONDA_UPLOAD_TOKEN} upload \
22 | -u ${CONDA_USER} \
23 | -l dev \
24 | ~/miniconda/conda-bld/linux-64/${PACKAGE_NAME}-*.tar.bz2 --force
25 | fi
26 |
27 | elif [ ${TRAVIS_BRANCH} = ${MASTER_BRANCH} ]; then
28 | if [ ${TRAVIS_EVENT_TYPE} = "cron" ]; then
29 | # Do build package for nightly cron, this is just for test stability info.
30 | exit 0
31 | else
32 | conda install anaconda-client
33 | anaconda -t ${CONDA_UPLOAD_TOKEN} \
34 | upload -u ${CONDA_USER} \
35 | -l edge \
36 | ~/miniconda/conda-bld/linux-64/${PACKAGE_NAME}-*.tar.bz2 --force
37 | fi
38 |
39 | else
40 | exit 0
41 |
42 | fi
43 |
--------------------------------------------------------------------------------
/doc/api.rst:
--------------------------------------------------------------------------------
1 | trcrpm API reference
2 | =======================
3 |
4 | :mod:`trcrpm`: trcrpm
5 | -------------------------------
6 |
7 | .. automodule:: trcrpm
8 | :members:
9 |
--------------------------------------------------------------------------------
/doc/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright (c) 2010-2016, MIT Probabilistic Computing Project
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | """Sphinx configuration file."""
18 |
19 | extensions = [
20 | 'sphinx.ext.autodoc',
21 | 'sphinxcontrib.napoleon',
22 | ]
23 |
24 | autodoc_member_order = 'bysource'
25 |
26 | copyright = '2010-2018, MIT Probabilistic Computing Project'
27 | master_doc = 'index'
28 | project = 'trcrpm'
29 | release = '0.1.0'
30 | version = '0.1.0'
31 |
32 | nitpicky = False
33 | html_theme = 'sphinxdoc'
34 |
--------------------------------------------------------------------------------
/doc/index.rst:
--------------------------------------------------------------------------------
1 | Temporally Reweighted Chinese Restaurant Process Mixtures
2 | =========================================================
3 |
4 | A Bayesian nonparametric method for multivariate time series data.
5 |
6 | .. toctree::
7 | :maxdepth: 1
8 |
9 | api
10 |
11 | Indices and tables
12 | ==================
13 |
14 | * :ref:`genindex`
15 | * :ref:`modindex`
16 | * :ref:`search`
17 |
--------------------------------------------------------------------------------
/docker/ubuntu1604:
--------------------------------------------------------------------------------
1 | FROM ubuntu:16.04
2 | MAINTAINER MIT Probabilistic Computing Project
3 |
4 | # Fetch dependencies from apt.
5 | RUN apt-get update -qq \
6 | && apt-get install -qq -y \
7 | build-essential \
8 | cython \
9 | git \
10 | python-matplotlib \
11 | python-numpy \
12 | python-pandas \
13 | python-pytest \
14 | python-scipy \
15 | python-six \
16 | python-sklearn \
17 | python-statsmodels \
18 | python-virtualenv
19 |
20 | # Create a virtualenv with jupyter.
21 | RUN virtualenv --system-site-packages /venv
22 | RUN . /venv/bin/activate && pip install jupyter-core==4.6.1
23 |
24 | # Clone the probcomp dependencies.
25 | RUN git clone https://github.com/probcomp/crosscat.git
26 | RUN git clone https://github.com/probcomp/cgpm.git
27 | RUN git clone https://github.com/probcomp/trcrpm.git
28 |
29 | # Install into the virtualenv.
30 | RUN . /venv/bin/activate && cd /crosscat && pip install .
31 | RUN . /venv/bin/activate && cd /cgpm && pip install .
32 | RUN . /venv/bin/activate && cd /trcrpm && pip install .
33 |
34 | # Run the tests.
35 | RUN . /venv/bin/activate && python -m pytest --pyargs trcrpm
36 |
--------------------------------------------------------------------------------
/pythenv.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Copyright (c) 2018 MIT Probabilistic Computing Project.
4 | # Released under Apache 2.0; refer to LICENSE.txt.
5 |
6 | set -Ceu
7 |
8 | : ${PYTHON:=python}
9 | root=`cd -- "$(dirname -- "$0")" && pwd`
10 | platform=`"${PYTHON}" -c 'import distutils.util as u; print u.get_platform()'`
11 | version=`"${PYTHON}" -c 'import sys; print sys.version[0:3]'`
12 |
13 | # The lib directory varies depending on
14 | #
15 | # (a) whether there are extension modules (here, no); and
16 | # (b) whether some Debian maintainer decided to patch the local Python
17 | # to behave as though there were.
18 | #
19 | # But there's no obvious way to just ask distutils what the name will
20 | # be. There's no harm in naming a pathname that doesn't exist, other
21 | # than a handful of microseconds of runtime, so we'll add both.
22 | libdir="${root}/build/lib"
23 | plat_libdir="${libdir}.${platform}-${version}"
24 | export PYTHONPATH="${libdir}:${plat_libdir}${PYTHONPATH:+:${PYTHONPATH}}"
25 |
26 | bindir="${root}/build/scripts-${version}"
27 | export PATH="${bindir}${PATH:+:${PATH}}"
28 |
29 | exec "$@"
30 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright (c) 2018 MIT Probabilistic Computing Project.
4 | # Released under Apache 2.0; refer to LICENSE.txt.
5 |
6 | import os
7 | import re
8 | import subprocess
9 |
10 | try:
11 | from setuptools import setup
12 | from setuptools.command.build_py import build_py
13 | from setuptools.command.sdist import sdist
14 | from setuptools.command.test import test
15 | except ImportError:
16 | from distutils.core import setup
17 | from distutils.cmd import Command
18 | from distutils.command.build_py import build_py
19 | from distutils.command.sdist import sdist
20 |
21 | class test(Command):
22 | def __init__(self, *args, **kwargs):
23 | Command.__init__(self, *args, **kwargs)
24 | def initialize_options(self): pass
25 | def finalize_options(self): pass
26 | def run(self): self.run_tests()
27 | def run_tests(self): Command.run_tests(self)
28 | def set_undefined_options(self, opt, val):
29 | Command.set_undefined_options(self, opt, val)
30 |
31 | def get_version():
32 | # The .git directory does not exist in the sdist, so read VERSION.
33 | if not os.path.exists('.git'):
34 | with open('VERSION', 'r') as f:
35 | version = f.read().strip()
36 | return version, version
37 |
38 | # git describe a commit using the most recent tag reachable from it.
39 | # Release tags start with v* (XXX what about other tags starting with v?)
40 | # and are of the form `v1.1.2`.
41 | #
42 | # The output `desc` will be of the form v1.1.2-2-gb92bef6[-dirty]:
43 | # - verpart v1.1.2
44 | # - revpart 2
45 | # - localpart gb92bef6[-dirty]
46 | desc = subprocess.check_output([
47 | 'git', 'describe', '--dirty', '--long', '--match', 'v*',
48 | ])
49 | match = re.match(r'^v([^-]*)-([0-9]+)-(.*)$', desc)
50 | assert match is not None
51 | verpart, revpart, localpart = match.groups()
52 | # Create a post version.
53 | if revpart > '0' or 'dirty' in localpart:
54 | # Local part may be g0123abcd or g0123abcd-dirty.
55 | # Hyphens not kosher here, so replace by dots.
56 | localpart = localpart.replace('-', '.')
57 | full_version = '%s.post%s+%s' % (verpart, revpart, localpart)
58 | # Create a release version.
59 | else:
60 | full_version = verpart
61 |
62 | # Strip the local part if there is one, to appease pkg_resources,
63 | # which handles only PEP 386, not PEP 440.
64 | if '+' in full_version:
65 | pkg_version = full_version[:full_version.find('+')]
66 | else:
67 | pkg_version = full_version
68 |
69 | # Sanity-check the result. XXX Consider checking the full PEP 386
70 | # and PEP 440 regular expressions here?
71 | assert '-' not in full_version, '%r' % (full_version,)
72 | assert '-' not in pkg_version, '%r' % (pkg_version,)
73 | assert '+' not in pkg_version, '%r' % (pkg_version,)
74 |
75 | return pkg_version, full_version
76 |
77 | pkg_version, full_version = get_version()
78 |
79 | def write_version_py(path):
80 | try:
81 | with open(path, 'rb') as f:
82 | version_old = f.read()
83 | except IOError:
84 | version_old = None
85 | version_new = '__version__ = %r\n' % (full_version,)
86 | if version_old != version_new:
87 | print 'writing %s' % (path,)
88 | with open(path, 'wb') as f:
89 | f.write(version_new)
90 |
91 | def readme_contents():
92 | readme_path = os.path.join(
93 | os.path.abspath(os.path.dirname(__file__)),
94 | 'README.md')
95 | with open(readme_path) as readme_file:
96 | return unicode(readme_file.read(), 'UTF-8')
97 |
98 | class local_build_py(build_py):
99 | def run(self):
100 | write_version_py(version_py)
101 | build_py.run(self)
102 |
103 | # Make sure the VERSION file in the sdist is exactly specified, even
104 | # if it is a development version, so that we do not need to run git to
105 | # discover it -- which won't work because there's no .git directory in
106 | # the sdist.
107 | class local_sdist(sdist):
108 | def make_release_tree(self, base_dir, files):
109 | sdist.make_release_tree(self, base_dir, files)
110 | version_file = os.path.join(base_dir, 'VERSION')
111 | print('updating %s' % (version_file,))
112 | # Write to temporary file first and rename over permanent not
113 | # just to avoid atomicity issues (not likely an issue since if
114 | # interrupted the whole sdist directory is only partially
115 | # written) but because the upstream sdist may have made a hard
116 | # link, so overwriting in place will edit the source tree.
117 | with open(version_file + '.tmp', 'wb') as f:
118 | f.write('%s\n' % (pkg_version,))
119 | os.rename(version_file + '.tmp', version_file)
120 |
121 | # XXX These should be attributes of `setup', but helpful distutils
122 | # doesn't pass them through when it doesn't know about them a priori.
123 | version_py = 'src/version.py'
124 |
125 | setup(
126 | name='trcrpm',
127 | version=pkg_version,
128 | description='Temporally-Reweighted CRP Mixture: '\
129 | 'A nonparametric Bayesian method for multivariate time series',
130 | long_description=readme_contents(),
131 | url='https://github.com/probcomp/trcrpm',
132 | license='Apache-2.0',
133 | maintainer='Feras Saad',
134 | maintainer_email='fsaad@remove-this-component.mit.edu',
135 | classifiers=[
136 | 'Development Status :: 2 - Pre-Alpha',
137 | 'Intended Audience :: Science/Research',
138 | 'License :: OSI Approved :: Apache Software License',
139 | 'Programming Language :: Python :: 2.7',
140 | 'Topic :: Scientific/Engineering :: Information Analysis',
141 | ],
142 | packages=[
143 | 'trcrpm',
144 | 'trcrpm.tests',
145 | ],
146 | package_dir={
147 | 'trcrpm': 'src',
148 | 'trcrpm.tests': 'tests',
149 | },
150 | cmdclass={
151 | 'build_py': local_build_py,
152 | 'sdist': local_sdist,
153 | },
154 | zip_safe=False,
155 | )
156 |
--------------------------------------------------------------------------------
/src/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright (c) 2018 MIT Probabilistic Computing Project.
4 | # Released under Apache 2.0; refer to LICENSE.txt.
5 |
6 | from .trcrpm import Hierarchical_TRCRP_Mixture
7 | from .trcrpm import TRCRP_Mixture
8 | from .version import __version__
9 |
10 | __all__ = [
11 | 'TRCRP_Mixture',
12 | 'Hierarchical_TRCRP_Mixture',
13 | ]
14 |
--------------------------------------------------------------------------------
/src/trcrpm.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright (c) 2018 MIT Probabilistic Computing Project.
4 | # Released under Apache 2.0; refer to LICENSE.txt.
5 |
6 | import itertools
7 |
8 | import numpy as np
9 | import pandas as pd
10 |
11 | from cgpm.crosscat.engine import Engine
12 | from cgpm.utils.parallel_map import parallel_map
13 |
14 |
15 | class Hierarchical_TRCRP_Mixture(object):
16 | """Hierarchical Temporally-Reweighted Chinese Restaurant Process Mixture.
17 |
18 | The data frame being modeled has an integer-valued index indicating the
19 | discrete time step, and has one column per time-varying variable, as shown
20 | below:
21 |
22 | +------+----------+----------+----------+
23 | | Time | Var A | Var B | Var C |
24 | +======+==========+==========+==========+
25 | | 1997 | 0.62 | 0.38 | 1.34 |
26 | +------+----------+----------+----------+
27 | | 1998 | 0.82 | 0.23 | nan |
28 | +------+----------+----------+----------+
29 | | 1999 | nan | 0.13 | 2.19 |
30 | +------+----------+----------+----------+
31 | | 2000 | 1.62 | 0.22 | 1.70 |
32 | +------+----------+----------+----------+
33 | | 2001 | 0.78 | 2.89 | nan |
34 | +------+----------+----------+----------+
35 |
36 | Parameters
37 | ----------
38 | chains: int
39 | Number of parallel MCMC chains to use for inference.
40 | lag : int
41 | Number of time points in the history to use for reweighting the
42 | CRP. If lag is zero, then all temporal dependencies are removed
43 | and the model becomes a standard CRP mixture.
44 | variables : list of str
45 | Human-readable names of the time series to be modeled.
46 | rng : numpy.random.RandomState
47 | Source of entropy.
48 | dependencies : list of tuple, optional
49 | Blocks of variables which are deterministically constrained to be
50 | modeled jointly. Defaults to no deterministic constraints.
51 | """
52 |
53 | def __init__(self, chains, lag, variables, rng, dependencies=None):
54 | """Initialize a Hierarchical TRCRP Mixture instance."""
55 | # From constructor.
56 | self.chains = chains
57 | self.lag = lag
58 | self.variables = list(variables)
59 | self.rng = rng
60 | self.dependencies = dependencies
61 | # Derived attributes.
62 | self.window = self.lag + 1
63 | self.variables_lagged = list(itertools.chain.from_iterable([
64 | ['%s.lag.%d' % (varname, i,) for i in xrange(self.lag, -1, -1)]
65 | for varname in self.variables
66 | ]))
67 | self.variable_index = {var: i for i, var in enumerate(self.variables)}
68 | for variable in self.variables:
69 | variable_idx = self._variable_to_index(variable)
70 | assert self.variables_lagged[variable_idx]=='%s.lag.0' % (variable,)
71 | # Internal attributes.
72 | self.dataset = pd.DataFrame()
73 | self.engine = None
74 | self.initialized = None
75 |
76 | def incorporate(self, frame):
77 | """Incorporate new observations.
78 |
79 | Parameters
80 | ----------
81 | frame : pd.DataFrame
82 | DataFrame containing new observations. The columns must match
83 | `self.variables`.
84 | """
85 | assert set(frame.columns) == set(self.variables)
86 | self._incorporate_new_timepoints(frame)
87 | # XXX Improve this function.
88 | self._incorporate_existing_timepoints(frame)
89 | assert self.engine.states[0].n_rows() == len(self.dataset)
90 |
91 | def resample_all(self, steps=None, seconds=None):
92 | """Run MCMC inference on entire latent state
93 |
94 | Parameters
95 | ----------
96 | steps : int, optional
97 | Number of full Gibbs sweeps through all kernels, default is 1.
98 | seconds : int, optional
99 | Maximum number of seconds to run inference steps before timing out,
100 | default is None.
101 |
102 | Notes
103 | -----
104 | If both `steps` and `seconds` are specified, then the min is taken. That
105 | is, inference will run until the given number Gibbs steps are taken, or
106 | until the given number of seconds elapse, whichever comes first.
107 | """
108 | self._transition(N=steps, S=seconds, backend='lovecat')
109 |
110 | def resample_hyperparameters(self, steps=None, seconds=None, variables=None):
111 | """Run empirical Bayes on variable hyperparameters.
112 |
113 | Parameters
114 | ----------
115 | steps : int, optional
116 | Number of full Gibbs sweeps through all kernels, default is 1.
117 | seconds : int, optional
118 | Maximum number of seconds to run inference before timing out,
119 | default is None.
120 | variables : list of str
121 | List of time series variables whose hyperparameters to target,
122 | default is all.
123 |
124 | See Also
125 | --------
126 | resample_all
127 | """
128 | variables_transition = variables or self.variables
129 | variable_indexes = list(itertools.chain.from_iterable([
130 | self._variable_to_window_indexes(v) for v in variables_transition
131 | ]))
132 | self._transition(N=steps, S=seconds, cols=variable_indexes,
133 | kernels=['view_alphas','column_hypers'], backend='cgpm')
134 |
135 | def simulate(self, timepoints, variables, nsamples, multiprocess=1):
136 | """Generate simulations from the posterior distribution.
137 |
138 | Parameters
139 | ----------
140 | timepoints : list of int
141 | List of integer-valued time steps to simulate
142 | variables : list of str
143 | Names of time series which to simulate from.
144 | nsamples : int
145 | Number of predictive samples to generate from each chain.
146 |
147 | Returns
148 | -------
149 | numpy.ndarray
150 | 3D array of generated samples. The dimensions of the returned list
151 | are `(self.chains*nsamples, len(timepoints), len(variables))`, so
152 | that `result[i][j][k]` contains a simulation of `variables[k],` at
153 | timepoint `j`, from chain `i`. A dissection of the output is shown
154 | below:
155 |
156 | .. code-block:: text
157 |
158 | # model has 2 chains, so chains * nsamples = 6 samples returned.
159 | >> model.simulate([1, 4], ['a', 'b'], 3)
160 | |<-----------chain 0----------->||<-----------chain 1----------->|
161 | [sample0.0, sample0.1, sample0.2, sample1.0, sample1.1, sample1.2]
162 |
163 | sample0.0: ((sim0.0_a1, sim0.0_b1), (sim0.0_a40, sim0.0_b40))
164 | sample0.1: ((sim0.1_a1, sim0.1_b1), (sim0.1_a40, sim0.1_b40))
165 | sample0.2: ((sim0.2_a1, sim0.2_b1), (sim0.2_a40, sim0.2_b40))
166 | sample1.0: ((sim1.0_a1, sim1.0_b1), (sim1.0_a40, sim1.0_b40))
167 | sample1.1: ((sim1.1_a1, sim1.1_b1), (sim1.1_a40, sim1.1_b40))
168 | sample1.2: ((sim1.2_a1, sim1.2_b1), (sim1.2_a40, sim1.2_b40))
169 | """
170 | cgpm_rowids = [self._timepoint_to_rowid(t) for t in timepoints]
171 | constraints_list = [self._get_cgpm_constraints(t) for t in timepoints]
172 | targets = [self._variable_to_index(var) for var in variables]
173 | targets_list = [targets] * len(cgpm_rowids)
174 | Ns = [nsamples] * len(cgpm_rowids)
175 | samples_raw_bulk = self.engine.simulate_bulk(cgpm_rowids, targets_list,
176 | constraints_list, Ns=Ns, multiprocess=multiprocess)
177 | samples_raw = list(itertools.chain.from_iterable(
178 | zip(*sample) for sample in samples_raw_bulk))
179 | samples = np.asarray([
180 | [[sample[t] for t in targets] for sample in sample_chain]
181 | for sample_chain in samples_raw
182 | ])
183 | return samples
184 |
185 | def simulate_ancestral(self, timepoints, variables, nsamples, multiprocess=1):
186 | """Generate simulations from the posterior distribution ancestrally.
187 |
188 | See Also
189 | --------
190 | simulate
191 | """
192 | assert timepoints == sorted(timepoints)
193 | targets = [self._variable_to_index(var) for var in variables]
194 | rowids = [self._timepoint_to_rowid(t) for t in timepoints]
195 | constraints = [self._get_cgpm_constraints(t) for t in timepoints]
196 | windows = {timepoint: set(self._get_timepoint_window(timepoint))
197 | for timepoint in timepoints}
198 | parents = {timepoint: self._get_parents_from_windows(timepoint, windows)
199 | for timepoint in timepoints}
200 | args = [
201 | (state, timepoints, variables, rowids, targets,
202 | constraints, parents, self._variable_to_index, nsamples)
203 | for state in self.engine.states
204 | ]
205 | mapper = parallel_map if multiprocess else map
206 | self.engine._seed_states()
207 | samples_raw_list = mapper(_simulate_ancestral_mp, args)
208 | samples_raw = itertools.chain.from_iterable(samples_raw_list)
209 | samples = np.asarray([
210 | [[sample[t][variable] for variable in targets] for t in timepoints]
211 | for sample in samples_raw
212 | ])
213 | return samples
214 |
215 | def dependence_probability_pairwise(self, variables=None):
216 | """Compute posterior dependence probabilities between time series.
217 |
218 | Parameters
219 | ----------
220 | variables : list of str, optional
221 | List of time series variables to include in the returned array.
222 | Defaults to `self.variables`.
223 |
224 | Returns
225 | -------
226 | numpy.ndarray
227 | 3D array containing pairwise dependence probabilities of time series
228 | `variables` from each chain. The dimensions of the returned
229 | array are `(self.chains, len(variables), len(variables))`, so
230 | that `result[i,j,k] == 1` if `variables[j]` and `variables[k]` are
231 | dependent according to chain `i`, and `0` otherwise.
232 | """
233 | if variables is None:
234 | variables = self.variables
235 | varnos = [self._variable_to_index(var) for var in variables]
236 | D = self.engine.dependence_probability_pairwise(colnos=varnos)
237 | return np.asarray(D)
238 |
239 | def get_temporal_regimes(self, variable, timepoints=None):
240 | """Return latent temporal regime at `timepoints` of the given `variable`.
241 |
242 | Parameters
243 | ----------
244 | variable : str
245 | Name of the time series variable to query.
246 | timepoints : list of int, optional
247 | List of timepoints at which to get the latent temporal regime value,
248 | defaults to all observed timepoints.
249 |
250 | Returns
251 | -------
252 | numpy.ndarray
253 | 2D array containing latent temporal regime at `timepoints` of the
254 | given variable, for each chain. The dimensions of the returned array
255 | are `(self.chains, len(timepoints))`, where `result[i][t]` is the
256 | value of the hidden temporal regime at `timepoints[t]`, according to
257 | chain `i`.
258 |
259 | *Note*: The actual integer values of the regimes are immaterial.
260 | """
261 | if timepoints is None:
262 | timepoints = self.dataset.index
263 | rowids = [self._timepoint_to_rowid(t) for t in timepoints]
264 | varno = self._variable_to_index(variable)
265 | regimes = [[state.view_for(varno).Zr(rowid) for rowid in rowids]
266 | for state in self.engine.states]
267 | return np.asarray(regimes)
268 |
269 | def _transition(self, **kwargs):
270 | """Helper for MCMC resample methods (full interface not exposed)."""
271 | if self.engine is None:
272 | raise ValueError('No data incorporate yet.')
273 | backend = kwargs.pop('backend', None)
274 | kwargs['cols'] = kwargs.pop('cols', self._variable_indexes())
275 | if backend in ['cgpm', None]:
276 | self.engine.transition(**kwargs)
277 | elif backend in ['lovecat']:
278 | self.engine.transition_lovecat(**kwargs)
279 | elif backend in ['loom']:
280 | self.engine.transition_loom(**kwargs)
281 | else:
282 | raise ValueError('Unknown backend: %s' % (backend,))
283 |
284 | def _incorporate_new_timepoints(self, frame):
285 | """Incorporate fresh sample ids as new cgpm rows."""
286 | new_timepoints = frame.index[~frame.index.isin(self.dataset.index)]
287 | new_observations = frame[self.variables].loc[new_timepoints]
288 | self.dataset = self.dataset.append(new_observations)
289 | new_rows = [self._get_timepoint_row(t) for t in new_timepoints]
290 | if self.initialized:
291 | outputs = self.engine.states[0].outputs
292 | assert all(len(row) == len(outputs) for row in new_rows)
293 | rowids_cgpm = range(
294 | self.engine.states[0].n_rows(),
295 | self.engine.states[0].n_rows() + len(new_rows)
296 | )
297 | observations_cgpm = [
298 | {i: row[i] for i in outputs if not np.isnan(row[i])}
299 | for row in new_rows
300 | ]
301 | assert all(
302 | rowid_cgpm == self._timepoint_to_rowid(timepoint)
303 | for timepoint, rowid_cgpm in zip(new_timepoints, rowids_cgpm)
304 | )
305 | self.engine.incorporate_bulk(rowids_cgpm, observations_cgpm)
306 | # XXX Do not initialize here! Instead, consider including a dummy row of
307 | # all zeros or similar. The reason that we initialize with the full
308 | # training set is to ensure that we have a good initial set of
309 | # hyperparameter grids. Instead, we should consider redefining the grids
310 | # after incorporating new data (a slight heuristic).
311 | else:
312 | self.engine = Engine(
313 | np.asarray(new_rows),
314 | num_states=self.chains,
315 | cctypes=['normal']*len(self.variables_lagged),
316 | Cd=self._get_variable_dependence_constraints(),
317 | rng=self.rng,
318 | )
319 | self.initialized = True
320 |
321 | def _incorporate_existing_timepoints(self, frame):
322 | """Update existing timepoints with NaN entries in cgpm cells."""
323 | nan_mask = pd.isnull(self.dataset) & ~pd.isnull(frame)
324 | nan_mask = nan_mask[nan_mask.any(axis=1)]
325 | if len(nan_mask) == 0:
326 | return
327 | cgpm_rowids_cells = []
328 | # For each new timepoint, get the cgpm rowids and cell values to force.
329 | for nan_timepoint, nan_timepoint_mask in nan_mask.iterrows():
330 | self._update_dataset_nan_timepoint(
331 | frame, nan_timepoint, nan_timepoint_mask)
332 | timepoint_rowids_cells = \
333 | self._convert_nan_timepoint_to_cgpm_rowid_cells(
334 | frame, nan_timepoint, nan_timepoint_mask)
335 | cgpm_rowids_cells.extend(timepoint_rowids_cells)
336 | # Force the cells in bulk.
337 | cgpm_rowids, cgpm_cells = zip(*cgpm_rowids_cells)
338 | self.engine.force_cell_bulk(cgpm_rowids, cgpm_cells)
339 | # XXX Also force any other sample ids which may have the new sample ids
340 | # in the window set at nan. Refer to the test case in
341 | # tests/test_data_transforms.test_incorporate_sampleid_wedged.
342 |
343 | def _update_dataset_nan_timepoint(
344 | self, frame, nan_timepoint, nan_timepoint_mask):
345 | """Populates timepoint with nan values in self.dataset using frame."""
346 | nan_col_names = nan_timepoint_mask[nan_timepoint_mask].index
347 | nan_col_values = frame.loc[nan_timepoint, nan_col_names]
348 | self.dataset.loc[nan_timepoint, nan_col_names] = nan_col_values
349 |
350 | def _convert_nan_timepoint_to_cgpm_rowid_cells(
351 | self, frame, nan_timepoint, nan_timepoint_mask):
352 | """Returns the cgpm rowid of all windows that nan_timepoint participates
353 | in, and dict containing columns and values to populate."""
354 | nan_col_names = nan_timepoint_mask[nan_timepoint_mask].index
355 | nan_col_idxs = [self._variable_to_index(col) for col in nan_col_names]
356 | nan_col_values = frame.loc[nan_timepoint, nan_col_names].as_matrix()
357 | cgpm_rowids = self._timepoint_to_rowids(nan_timepoint)
358 | cgpm_rowids_cells = [
359 | {col_idx - lag: value
360 | for col_idx, value in zip(nan_col_idxs, nan_col_values)}
361 | if rowid is not None else None
362 | for lag, rowid in enumerate(cgpm_rowids)
363 | ]
364 | return [
365 | (rowid, cells)
366 | for rowid, cells in zip(cgpm_rowids, cgpm_rowids_cells)
367 | if rowid is not None
368 | ]
369 |
370 | def _get_parents_from_windows(self, timepoint, windows):
371 | """Return list of timepoints of parents of the given timepoint."""
372 | return [
373 | timepoint2 for timepoint2 in windows
374 | if timepoint2 != timepoint and timepoint in windows[timepoint2]
375 | ]
376 |
377 | def _timepoint_to_rowid(self, timepoint):
378 | """Return the cgpm rowid representing the timepoint."""
379 | try:
380 | return self.dataset.index.get_loc(timepoint)
381 | except KeyError:
382 | return None
383 |
384 | def _timepoint_to_rowids(self, timepoint):
385 | """Return the list of cgpm rowids that timepoint participates in."""
386 |
387 | # Assuming self.window = 3, the first cgpm rowid that timepoint of value
388 | # 13 participates in is the rowid of timepoint, and the last cgpm rowid
389 | # is the rowid of timepoint+lag.
390 |
391 | # Example:
392 | # lag L2,L1,L0
393 | # rowid=7 11,12,13
394 | # rowid=8 12,13,14
395 | # rowid=9 13,14,15
396 | timepoints_window = self._get_timepoint_window(timepoint)
397 | return [self._timepoint_to_rowid(t) for t in timepoints_window]
398 |
399 | def _get_timepoint_window(self, timepoint):
400 | """Return the previous timepoints in the window of this timepoint."""
401 | return range(timepoint, timepoint + self.window)
402 |
403 | def _variable_to_index(self, variable, lag=0):
404 | """Convert variable name to cgpm output index."""
405 | assert 0 <= lag <= self.lag
406 | return self.variable_index[variable] * self.window + (self.lag - lag)
407 |
408 | def _variable_to_window_indexes(self, variable):
409 | """Convert variable name to list of cgpm output indexes in its window."""
410 | return [self._variable_to_index(variable, l) for l in xrange(self.window)]
411 |
412 | def _variable_indexes(self):
413 | """Return list of cgpm output indexes, one per variable at lag 0."""
414 | return [self._variable_to_index(var) for var in self.variables]
415 |
416 | def _get_variable_dependence_constraints(self):
417 | """Ensure lagged columns and user constraints are modeled as a block."""
418 | cgpm_dependencies = self._make_dependencies(self.dependencies)
419 | dependencies = [
420 | list(itertools.chain.from_iterable(
421 | [self._variable_to_window_indexes(c) for c in block
422 | ]))
423 | for block in cgpm_dependencies
424 | ]
425 | # Filter out any singleton dependencies.
426 | return [colnos for colnos in dependencies if len(colnos) > 1]
427 |
428 | def _get_cgpm_constraints(self, timepoint):
429 | # An already incorporated timepoint requires no constraints.
430 | if timepoint in self.dataset.index:
431 | return None
432 | # Retrieve existing observations in window of a fresh timepoint.
433 | row_values = self._get_timepoint_row(timepoint)
434 | assert len(row_values) == len(self.variables_lagged)
435 | # XXX Require user to specify columns to ignore.
436 | return {i : v for i, v in enumerate(row_values) if not np.isnan(v)}
437 |
438 | def _get_timepoint_row(self, timepoint):
439 | """Convert timepoint to row representation with timepoint at lag0."""
440 | timepoints_lag = range(timepoint - self.lag, timepoint + 1)
441 | return list(itertools.chain.from_iterable(
442 | (self.dataset[col].get(t, float('nan')) for t in timepoints_lag)
443 | for col in self.variables
444 | ))
445 |
446 | def _make_dependencies(self, dependencies):
447 | """Return combination of default and user's dependence constraints."""
448 | if dependencies is None:
449 | dependencies = []
450 | seen = set(col for block in dependencies for col in block)
451 | deps_default = [[col] for col in self.variables if col not in seen]
452 | deps_external = [block for block in dependencies]
453 | return tuple(tuple(itertools.chain(deps_default, deps_external)))
454 |
455 | def to_metadata(self):
456 | """Return a JSON representation that can be saved to disk.
457 |
458 | The typical usage pattern for serializing `model` and deserializing
459 | it into `model2` is:
460 |
461 | .. code-block:: python
462 |
463 | >> import importlib
464 | >> metadata = model.to_metadata()
465 | >> modname, attrname = metadata['factory']
466 | >> module = importlib.import_module(modname)
467 | >> klass = getattr(module, attrname)
468 | >> model2 = klass.from_metadata(binary)
469 | """
470 | metadata = dict()
471 | # From constructor.
472 | metadata['chains'] = self.chains
473 | metadata['lag'] = self.lag
474 | metadata['variables'] = self.variables
475 | metadata['dependencies'] = self.dependencies
476 | # Internal fields.
477 | metadata['initialized'] = self.initialized
478 | metadata['engine'] = self.engine.to_metadata() \
479 | if self.initialized else None
480 | metadata['dataset.values'] = self.dataset.values.tolist()
481 | metadata['dataset.index'] = list(self.dataset.index)
482 | metadata['dataset.columns'] = list(self.dataset.columns)
483 | # Factory.
484 | metadata['factory'] = ('trcrpm', 'Hierarchical_TRCRP_Mixture')
485 | return metadata
486 |
487 |
488 | @classmethod
489 | def from_metadata(cls, metadata, seed):
490 | """Load object from its JSON representation.
491 |
492 | Parameters
493 | ----------
494 | metadata : json blob
495 | JSON blob return from call to :meth:`to_metadata`.
496 | seed : int
497 | Seed for the random number generator to use.
498 |
499 | See Also
500 | --------
501 | to_metadata
502 | """
503 | model = cls(
504 | chains=metadata['chains'],
505 | lag=metadata['lag'],
506 | variables=metadata['variables'],
507 | rng=np.random.RandomState(seed),
508 | dependencies=metadata['dependencies'],
509 | )
510 | # Return model with populated internal fields.
511 | return model._populate_from_metadata(model, metadata)
512 |
513 |
514 | @staticmethod
515 | def _populate_from_metadata(model, metadata):
516 | model.initialized = metadata['initialized']
517 | model.dataset = pd.DataFrame(
518 | metadata['dataset.values'],
519 | index=metadata['dataset.index'],
520 | columns=metadata['dataset.columns'])
521 | model.engine = Engine.from_metadata(metadata['engine']) \
522 | if model.initialized else None
523 | return model
524 |
525 |
526 | class TRCRP_Mixture(Hierarchical_TRCRP_Mixture):
527 | """Temporally-Reweighted Chinese Restaurant Process Mixture.
528 |
529 | The TRCRP_Mixture is a special case of the
530 | :class:`Hierarchical_TRCRP_Mixture` where all time series are
531 | deterministically constrained to be modeled jointly.
532 |
533 | Parameters
534 | ----------
535 | chains: int
536 | Number of parallel MCMC chains to use for inference.
537 | lag : int
538 | Number of time points in the history to use for reweighting the
539 | CRP. If lag is zero, then all temporal dependencies are removed
540 | and the model becomes a standard CRP mixture.
541 | variables : list of str
542 | Human-readable names of the time series to be modeled.
543 | rng : numpy.random.RandomState
544 | Source of entropy.
545 |
546 | See Also
547 | --------
548 | Hierarchical_TRCRP_Mixture
549 | """
550 | def __init__(self, chains, lag, variables, rng):
551 | """Initialize a TRCRP Mixture instance."""
552 | super(TRCRP_Mixture, self).__init__(
553 | chains, lag, variables, rng, dependencies=[list(variables)])
554 |
555 | @classmethod
556 | def from_metadata(cls, metadata, seed):
557 | model = cls(
558 | chains=metadata['chains'],
559 | lag=metadata['lag'],
560 | variables=metadata['variables'],
561 | rng=np.random.RandomState(seed),
562 | )
563 | # Return model with populated internal fields.
564 | return model._populate_from_metadata(model, metadata)
565 |
566 | def to_metadata(self):
567 | metadata = super(TRCRP_Mixture, self).to_metadata()
568 | metadata['factory'] = ('trcrpm', 'TRCRP_Mixture')
569 | return metadata
570 |
571 |
572 | # Multiprocessing helpers
573 | # -----------------------
574 | # These functions must be defined top-level in the module to work with
575 | # parallel_map.
576 |
577 | def _simulate_ancestral_mp((state, timepoints, variables, rowids, targets,
578 | constraints, parents, variable_to_index, nsamples)):
579 | """Simulate timepoints and variables ancestrally (multiple samples)."""
580 | return [
581 | _simulate_ancestral_one(
582 | state, timepoints, variables, rowids, targets, constraints, parents,
583 | variable_to_index)
584 | for _i in xrange(nsamples)
585 | ]
586 |
587 |
588 | def _simulate_ancestral_one(state, timepoints, variables, rowids, targets,
589 | constraints, parents, variable_to_index):
590 | """Simulate timepoints and variables ancestrally (one sample)."""
591 | samples = dict()
592 | for i, timepoint in enumerate(timepoints):
593 | simulated_parents = {
594 | variable_to_index(var, timepoint-timepoint_parent) :
595 | samples[timepoint_parent][var_idx]
596 | for timepoint_parent in parents[timepoint]
597 | for var, var_idx in zip(variables, targets)
598 | }
599 | if constraints[i] is not None:
600 | constraints[i].update(simulated_parents)
601 | sample = state.simulate(rowids[i], targets, constraints[i])
602 | samples[timepoint] = sample
603 | return samples
604 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright (c) 2018 MIT Probabilistic Computing Project.
4 | # Released under Apache 2.0; refer to LICENSE.txt.
5 |
--------------------------------------------------------------------------------
/tests/test_data_transforms.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright (c) 2018 MIT Probabilistic Computing Project.
4 | # Released under Apache 2.0; refer to LICENSE.txt.
5 |
6 | import numpy as np
7 | import pandas as pd
8 | import pytest
9 |
10 | from trcrpm import Hierarchical_TRCRP_Mixture
11 | from trcrpm import TRCRP_Mixture
12 |
13 | nan = float('nan')
14 |
15 | DATA_RAW = [
16 | [86., 57., 65.],
17 | [19., 62., nan],
18 | [17., 41., 17.],
19 | [nan, 7., 30.],
20 | [75., 1., 12.],
21 | [45., nan, 72.],
22 | [48., 77., 8.],
23 | [29., nan, 86.],
24 | [83., 46., 38.],
25 | [nan, 54., nan]
26 | ]
27 |
28 | FRAME = pd.DataFrame(DATA_RAW, columns=['a', 'b', 'c'])
29 |
30 | def test_column_indexing():
31 | rng = np.random.RandomState(2)
32 |
33 | trcrpm = TRCRP_Mixture(chains=1, lag=0, variables=FRAME.columns, rng=rng)
34 | assert trcrpm.variables_lagged == [
35 | 'a.lag.0',
36 | 'b.lag.0',
37 | 'c.lag.0',
38 | ]
39 | # lag 0
40 | assert trcrpm._variable_to_index('a') == 0
41 | assert trcrpm._variable_to_index('b') == 1
42 | assert trcrpm._variable_to_index('c') == 2
43 | assert trcrpm._variable_indexes() == [0, 1, 2]
44 |
45 | trcrpm = TRCRP_Mixture(chains=1, lag=2, variables=FRAME.columns, rng=rng)
46 | assert trcrpm.variables_lagged == [
47 | 'a.lag.2', 'a.lag.1', 'a.lag.0',
48 | 'b.lag.2', 'b.lag.1', 'b.lag.0',
49 | 'c.lag.2', 'c.lag.1', 'c.lag.0'
50 | ]
51 | # lag 0
52 | assert trcrpm._variable_to_index('a') == 2
53 | assert trcrpm._variable_to_index('b') == 5
54 | assert trcrpm._variable_to_index('c') == 8
55 | # lag 1
56 | assert trcrpm._variable_to_index('a', lag=1) == 1
57 | assert trcrpm._variable_to_index('b', lag=1) == 4
58 | assert trcrpm._variable_to_index('c', lag=1) == 7
59 | # lag 2
60 | assert trcrpm._variable_to_index('a', lag=2) == 0
61 | assert trcrpm._variable_to_index('b', lag=2) == 3
62 | assert trcrpm._variable_to_index('c', lag=2) == 6
63 |
64 | assert trcrpm._variable_indexes() == [2, 5, 8]
65 | assert trcrpm._variable_to_window_indexes('a') == [2,1,0]
66 | assert trcrpm._variable_to_window_indexes('b') == [5,4,3]
67 | assert trcrpm._variable_to_window_indexes('c') == [8,7,6]
68 |
69 | trcrpm = TRCRP_Mixture(chains=1, lag=5, variables=FRAME.columns, rng=rng)
70 | assert trcrpm.variables_lagged == [
71 | 'a.lag.5', 'a.lag.4', 'a.lag.3', 'a.lag.2', 'a.lag.1', 'a.lag.0',
72 | 'b.lag.5', 'b.lag.4', 'b.lag.3', 'b.lag.2', 'b.lag.1', 'b.lag.0',
73 | 'c.lag.5', 'c.lag.4', 'c.lag.3', 'c.lag.2', 'c.lag.1', 'c.lag.0'
74 | ]
75 | # lag 0
76 | assert trcrpm._variable_to_index('a') == 5
77 | assert trcrpm._variable_to_index('b') == 11
78 | assert trcrpm._variable_to_index('c') == 17
79 | # lag 1
80 | assert trcrpm._variable_to_index('a', lag=1) == 4
81 | assert trcrpm._variable_to_index('b', lag=1) == 10
82 | assert trcrpm._variable_to_index('c', lag=1) == 16
83 | # lag 2
84 | assert trcrpm._variable_to_index('a', lag=2) == 3
85 | assert trcrpm._variable_to_index('b', lag=2) == 9
86 | assert trcrpm._variable_to_index('c', lag=2) == 15
87 | # lag 5
88 | assert trcrpm._variable_to_index('a', lag=5) == 0
89 | assert trcrpm._variable_to_index('b', lag=5) == 6
90 | assert trcrpm._variable_to_index('c', lag=5) == 12
91 |
92 | assert trcrpm._variable_indexes() == [5, 11, 17]
93 | assert trcrpm._variable_to_window_indexes('a') == [5,4,3,2,1,0]
94 | assert trcrpm._variable_to_window_indexes('b') == [11,10,9,8,7,6]
95 | assert trcrpm._variable_to_window_indexes('c') == [17,16,15,14,13,12]
96 |
97 |
98 | def test_dependence_constraints():
99 | rng = np.random.RandomState(2)
100 |
101 | # All variables in TRCRP_Mixture are dependent.
102 | trcrpm = TRCRP_Mixture(chains=1, lag=0, variables=FRAME.columns, rng=rng)
103 | assert trcrpm._get_variable_dependence_constraints() == [[0,1,2]]
104 |
105 | # All variables in TRCRP_Mixture are dependent.
106 | trcrpm = TRCRP_Mixture(chains=1, lag=2, variables=FRAME.columns, rng=rng)
107 | assert trcrpm._get_variable_dependence_constraints() == \
108 | [[2,1,0, 5,4,3, 8,7,6]]
109 |
110 | # All variables in TRCRP_Mixture are dependent.
111 | trcrpm = TRCRP_Mixture(chains=1, lag=5, variables=FRAME.columns, rng=rng)
112 | assert trcrpm._get_variable_dependence_constraints() == \
113 | [[5,4,3,2,1,0, 11,10,9,8,7,6, 17,16,15,14,13,12]]
114 |
115 | # Lag 0 dependencies should skip singleton 'c'.
116 | trcrpm = Hierarchical_TRCRP_Mixture(
117 | chains=1, lag=0, variables=FRAME.columns, rng=rng,
118 | dependencies=[['a','b'],['c']])
119 | assert trcrpm._get_variable_dependence_constraints() == [[0,1]]
120 |
121 | # Lag 0 dependencies with all constrained.
122 | trcrpm = Hierarchical_TRCRP_Mixture(
123 | chains=1, lag=0, variables=FRAME.columns, rng=rng,
124 | dependencies=[['a','b','c']])
125 | assert trcrpm._get_variable_dependence_constraints() == [[0,1,2]]
126 |
127 | # Lag 1 dependencies with two constraints.
128 | trcrpm = Hierarchical_TRCRP_Mixture(
129 | chains=1, lag=1, variables=FRAME.columns, rng=rng,
130 | dependencies=[['a','b']])
131 | assert trcrpm._get_variable_dependence_constraints() == [[5,4], [1,0,3,2]]
132 |
133 | # Lag 1 dependency with single constraint.
134 | trcrpm = Hierarchical_TRCRP_Mixture(
135 | chains=1, lag=1, variables=FRAME.columns, rng=rng,
136 | dependencies=[['a']])
137 | assert trcrpm._get_variable_dependence_constraints() == [
138 | [3,2] , [5,4], [1,0]
139 | ]
140 |
141 | # Multiple customer dependencies. Capturing a runtime error since
142 | # incorporate is going to use multiprocess so parallel_map captures the
143 | # ValueError and throws a RuntimeError.
144 | with pytest.raises(RuntimeError):
145 | trcrpm = Hierarchical_TRCRP_Mixture(
146 | chains=1, lag=0, variables=FRAME.columns, rng=rng,
147 | dependencies=[['a','c'], ['a','b']])
148 | trcrpm.incorporate(FRAME)
149 |
150 |
151 | def test_tabulate_lagged_data():
152 | rng = np.random.RandomState(2)
153 |
154 | trcrpm = TRCRP_Mixture(chains=1, lag=0, variables=FRAME.columns, rng=rng)
155 | trcrpm.incorporate(FRAME)
156 | tabulated_data = trcrpm.engine.states[0].data_array()
157 | expected_data = DATA_RAW
158 | assert np.allclose(tabulated_data, DATA_RAW, equal_nan=True)
159 |
160 | trcrpm = TRCRP_Mixture(chains=1, lag=2, variables=FRAME.columns, rng=rng)
161 | trcrpm.incorporate(FRAME)
162 | tabulated_data = trcrpm.engine.states[0].data_array()
163 | expected_data = [
164 | [nan, nan, 86., nan, nan, 57., nan, nan, 65.],
165 | [nan, 86., 19., nan, 57., 62., nan, 65., nan],
166 | [86., 19., 17., 57., 62., 41., 65., nan, 17.],
167 | [19., 17., nan, 62., 41., 7., nan, 17., 30.],
168 | [17., nan, 75., 41., 7., 1., 17., 30., 12.],
169 | [nan, 75., 45., 7., 1., nan, 30., 12., 72.],
170 | [75., 45., 48., 1., nan, 77., 12., 72., 8.],
171 | [45., 48., 29., nan, 77., nan, 72., 8., 86.],
172 | [48., 29., 83., 77., nan, 46., 8., 86., 38.],
173 | [29., 83., nan, nan, 46., 54., 86., 38., nan],
174 | ]
175 | assert np.allclose(tabulated_data, expected_data, equal_nan=True)
176 |
177 |
178 | def test_incorporate_sampleid_existing():
179 | rng = np.random.RandomState()
180 | trcrpm = TRCRP_Mixture(chains=1, lag=2, variables=FRAME.columns, rng=rng)
181 | trcrpm.incorporate(FRAME)
182 |
183 | new_frame = pd.DataFrame([
184 | [nan, nan, 99.],
185 | [44., nan, nan],
186 | [21., nan, 88.],
187 | ],
188 | index=[1, 3, 9],
189 | columns=['a', 'b', 'c'],
190 | )
191 | trcrpm.incorporate(new_frame)
192 | tabulated_data = trcrpm.engine.states[0].data_array()
193 | expected_data = [
194 | [nan, nan, 86., nan, nan, 57., nan, nan, 65.],
195 | [nan, 86., 19., nan, 57., 62., nan, 65., 99.],
196 | [86., 19., 17., 57., 62., 41., 65., 99., 17.],
197 | [19., 17., 44., 62., 41., 7., 99., 17., 30.],
198 | [17., 44., 75., 41., 7., 1., 17., 30., 12.],
199 | [44., 75., 45., 7., 1., nan, 30., 12., 72.],
200 | [75., 45., 48., 1., nan, 77., 12., 72., 8.],
201 | [45., 48., 29., nan, 77., nan, 72., 8., 86.],
202 | [48., 29., 83., 77., nan, 46., 8., 86., 38.],
203 | [29., 83., 21., nan, 46., 54., 86., 38., 88.],
204 | ]
205 | assert np.allclose(tabulated_data, expected_data, equal_nan=True)
206 |
207 |
208 | def test_incorporate_sampleid_non_contiguous():
209 | rng = np.random.RandomState(1)
210 | trcrpm = TRCRP_Mixture(chains=1, lag=2, variables=FRAME.columns, rng=rng)
211 | trcrpm.incorporate(FRAME)
212 |
213 | # Now incorporate an non-contiguous row skipping last_sampled_id+1.
214 | last_sampled_id = max(FRAME.index)
215 | frame_new = pd.DataFrame(
216 | [[11, 12, float('nan')]],
217 | columns=['a', 'b', 'c'],
218 | index=[last_sampled_id+2]
219 | )
220 | trcrpm.incorporate(frame_new)
221 | tabulated_data = trcrpm.engine.states[0].data_array()
222 | expected_data = [
223 | [nan, nan, 86., nan, nan, 57., nan, nan, 65.],
224 | [nan, 86., 19., nan, 57., 62., nan, 65., nan],
225 | [86., 19., 17., 57., 62., 41., 65., nan, 17.],
226 | [19., 17., nan, 62., 41., 7., nan, 17., 30.],
227 | [17., nan, 75., 41., 7., 1., 17., 30., 12.],
228 | [nan, 75., 45., 7., 1., nan, 30., 12., 72.],
229 | [75., 45., 48., 1., nan, 77., 12., 72., 8.],
230 | [45., 48., 29., nan, 77., nan, 72., 8., 86.],
231 | [48., 29., 83., 77., nan, 46., 8., 86., 38.],
232 | [29., 83., nan, nan, 46., 54., 86., 38., nan],
233 | [nan, nan, 11, 54., nan, 12, nan, nan, nan],
234 | ]
235 | assert np.allclose(tabulated_data, expected_data, equal_nan=True)
236 |
237 | @pytest.mark.xfail(strict=True, reason='Feature not implemented.')
238 | def test_incorporate_sampleid_wedged():
239 | rng = np.random.RandomState(1)
240 | trcrpm = TRCRP_Mixture(chains=1, lag=2, variables=FRAME.columns, rng=rng)
241 | trcrpm.incorporate(FRAME)
242 |
243 | # Now incorporate an non-contiguous row skipping last_sampled_id+1.
244 | last_sampled_id = max(FRAME.index)
245 | frame_new = pd.DataFrame(
246 | [[11, 12, float('nan')]],
247 | columns=['a', 'b', 'c'],
248 | index=[last_sampled_id+2]
249 | )
250 | trcrpm.incorporate(frame_new)
251 |
252 | # Now bring back the missing observation at last_sampled_id+1.
253 | # XXX The xfailure happens in this step.
254 | frame_new = pd.DataFrame(
255 | [[3, 3, 3]],
256 | columns=['a', 'b', 'c'],
257 | index=[last_sampled_id+1]
258 | )
259 | trcrpm.incorporate(frame_new)
260 | tabulated_data = trcrpm.engine.states[0].data_array()
261 | expected_data = [
262 | [nan, nan, 86., nan, nan, 57., nan, nan, 65.],
263 | [nan, 86., 19., nan, 57., 62., nan, 65., nan],
264 | [86., 19., 17., 57., 62., 41., 65., nan, 17.],
265 | [19., 17., nan, 62., 41., 7., nan, 17., 30.],
266 | [17., nan, 75., 41., 7., 1., 17., 30., 12.],
267 | [nan, 75., 45., 7., 1., nan, 30., 12., 72.],
268 | [75., 45., 48., 1., nan, 77., 12., 72., 8.],
269 | [45., 48., 29., nan, 77., nan, 72., 8., 86.],
270 | [48., 29., 83., 77., nan, 46., 8., 86., 38.],
271 | [29., 83., nan, nan, 46., 54., 86., 38., nan],
272 | [nan, 3., 11., 54., 3., 12., nan, 3., nan], # row updated.
273 | [83., nan, 3., 46., 54., 3., 38., nan, 3.], # new row.
274 | ]
275 | assert np.allclose(tabulated_data, expected_data, equal_nan=True)
276 |
277 |
278 | def test_timepoint_to_rowid():
279 | rng = np.random.RandomState(2)
280 | trcrpm = TRCRP_Mixture(chains=1, lag=0, variables=FRAME.columns, rng=rng)
281 | trcrpm.incorporate(FRAME)
282 | for i in xrange(len(FRAME)):
283 | assert trcrpm._timepoint_to_rowid(i) == i
284 | assert trcrpm._timepoint_to_rowid(i) == i
285 |
286 |
287 | def test_simulate_dimensions():
288 | rng = np.random.RandomState(1)
289 | trcrpm = TRCRP_Mixture(chains=4, lag=3, variables=FRAME.columns, rng=rng)
290 | trcrpm.incorporate(FRAME)
291 |
292 | def check_dims_correct(timepoints, variables, nsamples, simulator):
293 | samples = simulator(timepoints, variables, nsamples, multiprocess=0)
294 | # Number of chains is 4.
295 | assert len(samples) == nsamples * 4
296 | for sample in samples:
297 | assert len(sample) == len(timepoints)
298 | for subsample in sample:
299 | assert len(subsample) == len(variables)
300 |
301 | for simulator in [trcrpm.simulate, trcrpm.simulate_ancestral]:
302 | check_dims_correct([1], ['a'], 4, simulator)
303 | check_dims_correct([1], ['a', 'b'], 7, simulator)
304 | check_dims_correct([1,2], ['a'], 1, simulator)
305 | check_dims_correct([1,2,3], ['c', 'b'], 10, simulator)
306 | check_dims_correct([9, 10, 11, 12], ['c', 'b'], 10, simulator)
307 |
308 |
309 | def test_get_cgpm_constraints():
310 | rng = np.random.RandomState(2)
311 | trcrpm = TRCRP_Mixture(chains=1, lag=3, variables=FRAME.columns, rng=rng)
312 | trcrpm.incorporate(FRAME)
313 |
314 | # For reference, lag=3 implies the following tabulation
315 | # column a column b column c
316 | # 0 1 2 3 4 5 6 7 8 9 10 11
317 |
318 | # Incorporated timepoints should have no constraints.
319 | for timepoint in trcrpm.dataset.index:
320 | assert trcrpm._get_cgpm_constraints(timepoint) is None
321 |
322 | # Fresh timepoint 10 should have appropriate constraints.
323 | constraints_10 = trcrpm._get_cgpm_constraints(10)
324 | assert constraints_10 == {
325 | # column a column b column c
326 | 0:29, 1:83, 5:46, 6:54, 8:86 , 9:38
327 | }
328 | # Incorporating data into timepoint 9 should update constraints for 10.
329 | frame_new = pd.DataFrame(
330 | [[-12, nan, -12]],
331 | columns=['a', 'b', 'c'],
332 | index=[9]
333 | )
334 | trcrpm.incorporate(frame_new)
335 | constraints_10 = trcrpm._get_cgpm_constraints(10)
336 | assert constraints_10 == {
337 | # column a column b column c
338 | 0:29, 1:83, 2:-12, 5:46, 6:54, 8:86 , 9:38, 10:-12
339 | }
340 |
341 | # Fresh timepoint 12 should have appropriate constraints.
342 | constraints_12 = trcrpm._get_cgpm_constraints(12)
343 | assert constraints_12 == {
344 | # column a column b column c
345 | 0:-12, 4:54, 8:-12
346 | }
347 | # Incorporating data into timepoint 11 should update constraints for 12.
348 | frame_new = pd.DataFrame(
349 | [[-44, -48, -47]],
350 | columns=['a', 'b', 'c'],
351 | index=[11]
352 | )
353 | trcrpm.incorporate(frame_new)
354 | constraints_12 = trcrpm._get_cgpm_constraints(12)
355 | assert constraints_12 == {
356 | # column a column b column c
357 | 0:-12, 2:-44, 4:54, 6:-48, 8:-12, 10:-47
358 | }
359 |
360 |
361 | def test_get_temporal_regimes_crash():
362 | rng = np.random.RandomState(1)
363 | trcrpm = TRCRP_Mixture(chains=4, lag=3, variables=FRAME.columns, rng=rng)
364 | trcrpm.incorporate(FRAME)
365 | for variable in trcrpm.variables:
366 | regimes_all = trcrpm.get_temporal_regimes(variable)
367 | assert np.shape(regimes_all) == (trcrpm.chains, len(trcrpm.dataset))
368 | regimes_some = trcrpm.get_temporal_regimes(variable, timepoints=[0,1,2])
369 | assert np.shape(regimes_some) == (trcrpm.chains, 3)
370 |
371 | def test_get_dependence_probabilities_crash():
372 | rng = np.random.RandomState(1)
373 | trcrpm = TRCRP_Mixture(chains=4, lag=3, variables=FRAME.columns, rng=rng)
374 | trcrpm.incorporate(FRAME)
375 | nvars = len(trcrpm.variables)
376 | dependencies = trcrpm.dependence_probability_pairwise()
377 | assert np.allclose(dependencies, np.ones((nvars, nvars)))
378 |
--------------------------------------------------------------------------------
/tests/test_resample_crashes.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright (c) 2018 MIT Probabilistic Computing Project.
4 | # Released under Apache 2.0; refer to LICENSE.txt.
5 |
6 | import numpy as np
7 | import pandas as pd
8 | import pytest
9 |
10 | from trcrpm import Hierarchical_TRCRP_Mixture
11 | from trcrpm import TRCRP_Mixture
12 |
13 | nan = float('nan')
14 |
15 | DATA_RAW = [
16 | [86., 57., 65.],
17 | [19., 62., nan],
18 | [17., 41., 17.],
19 | [nan, 7., 30.],
20 | [75., 1., 12.],
21 | [45., nan, 72.],
22 | [48., 77., 8.],
23 | [29., nan, 86.],
24 | [83., 46., 38.],
25 | [nan, 54., nan]
26 | ]
27 |
28 | FRAME = pd.DataFrame(DATA_RAW, columns=['a', 'b', 'c'])
29 |
30 | def test_trcrp_mixture_all_dependent():
31 | rng = np.random.RandomState(2)
32 | trcrpm = TRCRP_Mixture(chains=3, lag=3, variables=FRAME.columns, rng=rng)
33 | with pytest.raises(ValueError):
34 | # No data incorporated yet.
35 | trcrpm.resample_all(steps=10)
36 | trcrpm.incorporate(FRAME)
37 | trcrpm.resample_all(steps=10)
38 | for state in trcrpm.engine.states:
39 | assert len(state.views) == 1
40 | trcrpm.resample_hyperparameters(steps=5)
41 | regimes_a = trcrpm.get_temporal_regimes('a')
42 | regimes_b = trcrpm.get_temporal_regimes('b')
43 | regimes_c = trcrpm.get_temporal_regimes('c')
44 | assert np.all(regimes_a == regimes_b)
45 | assert np.all(regimes_a == regimes_c)
46 |
47 | def test_trcrp_hierarchical_mixture_crashes():
48 | rng = np.random.RandomState(2)
49 | trcrpm = Hierarchical_TRCRP_Mixture(
50 | chains=3, lag=3, variables=FRAME.columns, rng=rng)
51 | with pytest.raises(ValueError):
52 | # No data incorporated yet.
53 | trcrpm.resample_all(steps=10)
54 | trcrpm.incorporate(FRAME)
55 | trcrpm.resample_all(steps=10)
56 | trcrpm.resample_hyperparameters(steps=5)
57 | regimes_a = trcrpm.get_temporal_regimes('a')
58 | regimes_b = trcrpm.get_temporal_regimes('b')
59 | regimes_c = trcrpm.get_temporal_regimes('c')
60 | assert not (
61 | np.all(regimes_a == regimes_b) and np.all(regimes_a == regimes_c))
62 |
--------------------------------------------------------------------------------
/tests/test_serialize.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright (c) 2018 MIT Probabilistic Computing Project.
4 | # Released under Apache 2.0; refer to LICENSE.txt.
5 |
6 | import numpy as np
7 | import pandas as pd
8 | import importlib
9 |
10 | from trcrpm import Hierarchical_TRCRP_Mixture
11 | from trcrpm import TRCRP_Mixture
12 |
13 | nan = float('nan')
14 |
15 | DATA_RAW = [
16 | [86., 57., 65.],
17 | [19., 62., nan],
18 | [17., 41., 17.],
19 | [nan, 7., 30.],
20 | [75., 1., 12.],
21 | [45., nan, 72.],
22 | [48., 77., 8.],
23 | [29., nan, 86.],
24 | [83., 46., 38.],
25 | [nan, 54., nan]
26 | ]
27 |
28 | FRAME = pd.DataFrame(DATA_RAW, columns=['a', 'b', 'c'])
29 |
30 |
31 | def test_serialize_trcrp_mixture():
32 | rng = np.random.RandomState(1)
33 | trcrpm = TRCRP_Mixture(chains=4, lag=3, variables=FRAME.columns, rng=rng)
34 | trcrpm.incorporate(FRAME)
35 |
36 | metadata = trcrpm.to_metadata()
37 | modulename, attributename = metadata['factory']
38 | module = importlib.import_module(modulename)
39 | builder = getattr(module, attributename)
40 | trcrpm2 = builder.from_metadata(metadata, seed=1)
41 | assert isinstance(trcrpm2, TRCRP_Mixture)
42 |
43 | def test_serialize_hierarchical_trcrp_mixture():
44 | rng = np.random.RandomState(1)
45 | trcrpm = Hierarchical_TRCRP_Mixture(
46 | chains=4, lag=3, variables=FRAME.columns, rng=rng)
47 | trcrpm.incorporate(FRAME)
48 |
49 | metadata = trcrpm.to_metadata()
50 | modulename, attributename = metadata['factory']
51 | module = importlib.import_module(modulename)
52 | builder = getattr(module, attributename)
53 | trcrpm2 = builder.from_metadata(metadata, seed=1)
54 | assert isinstance(trcrpm2, Hierarchical_TRCRP_Mixture)
55 |
56 |
--------------------------------------------------------------------------------
/tutorials/resources/bimodal-posteriors-data.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "metadata": {},
7 | "outputs": [],
8 | "source": [
9 | "%matplotlib inline\n",
10 | "import matplotlib.pyplot as plt"
11 | ]
12 | },
13 | {
14 | "cell_type": "code",
15 | "execution_count": 2,
16 | "metadata": {},
17 | "outputs": [],
18 | "source": [
19 | "import numpy as np\n",
20 | "import pandas as pd"
21 | ]
22 | },
23 | {
24 | "cell_type": "code",
25 | "execution_count": 3,
26 | "metadata": {},
27 | "outputs": [],
28 | "source": [
29 | "ys = []\n",
30 | "ys.extend([0]*20)\n",
31 | "ys.extend(np.linspace(0, 2.5, 10))\n",
32 | "ys.extend(np.linspace(2.5, 5, 10))\n",
33 | "ys.extend([5]*15)\n",
34 | "ys.extend(np.linspace(5, 0, 15))\n",
35 | "ys.extend([0]*20)\n",
36 | "ys.extend(np.linspace(0, 2.5, 10))\n",
37 | "ys.extend(np.linspace(2.5, -2.5, 15))\n",
38 | "ys.extend(np.linspace(-2.5, 0, 15))\n",
39 | "ys.extend([0]*15)\n",
40 | "ys.extend(np.linspace(0, 2.5, 10))\n",
41 | "ys.extend(np.linspace(2.5, -2.5, 15))\n",
42 | "ys.extend(np.linspace(-2.5, 0, 15))\n",
43 | "ys.extend([0]*20)\n",
44 | "ys.extend(np.linspace(0, 2.5, 10))\n",
45 | "ys.extend(np.linspace(2.5, 5, 10))\n",
46 | "ys.extend([5]*15)\n",
47 | "ys.extend(np.linspace(5, 0, 15))\n",
48 | "ys.extend([0]*15)\n",
49 | "ys.extend(np.linspace(0, 2.5, 10))"
50 | ]
51 | },
52 | {
53 | "cell_type": "code",
54 | "execution_count": 4,
55 | "metadata": {},
56 | "outputs": [
57 | {
58 | "data": {
59 | "text/plain": [
60 | ""
61 | ]
62 | },
63 | "execution_count": 4,
64 | "metadata": {},
65 | "output_type": "execute_result"
66 | },
67 | {
68 | "data": {
69 | "image/png": "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\n",
70 | "text/plain": [
71 | ""
72 | ]
73 | },
74 | "metadata": {},
75 | "output_type": "display_data"
76 | }
77 | ],
78 | "source": [
79 | "fig, ax = plt.subplots()\n",
80 | "xs = range(len(ys))\n",
81 | "ax.scatter(xs, ys)"
82 | ]
83 | },
84 | {
85 | "cell_type": "code",
86 | "execution_count": 5,
87 | "metadata": {},
88 | "outputs": [],
89 | "source": [
90 | "frame = pd.DataFrame(ys, columns=['y'])"
91 | ]
92 | },
93 | {
94 | "cell_type": "code",
95 | "execution_count": 6,
96 | "metadata": {},
97 | "outputs": [],
98 | "source": [
99 | "frame.to_csv('up-or-down.csv', index=False)"
100 | ]
101 | }
102 | ],
103 | "metadata": {
104 | "kernelspec": {
105 | "display_name": "Python 2",
106 | "language": "python",
107 | "name": "python2"
108 | },
109 | "language_info": {
110 | "codemirror_mode": {
111 | "name": "ipython",
112 | "version": 2
113 | },
114 | "file_extension": ".py",
115 | "mimetype": "text/x-python",
116 | "name": "python",
117 | "nbconvert_exporter": "python",
118 | "pygments_lexer": "ipython2",
119 | "version": "2.7.12"
120 | }
121 | },
122 | "nbformat": 4,
123 | "nbformat_minor": 2
124 | }
125 |
--------------------------------------------------------------------------------
/tutorials/resources/bimodal-posteriors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/probcomp/trcrpm/c7b50685274cb31f1c0b90dca83708fb612bfc5f/tutorials/resources/bimodal-posteriors.png
--------------------------------------------------------------------------------
/tutorials/resources/ggToyMissing.csv:
--------------------------------------------------------------------------------
1 | ,a,b,c,d
2 | 0,-0.17931243767,-0.292974168008,2.79251929077,1.86928341258
3 | 1,-0.414784304536,-0.391833788179,1.73412763784,1.4727377567
4 | 2,-0.483769290203,-0.4360329358,0.627319292158,0.960280230976
5 | 3,-0.45910278411,-0.573394956407,0.257649357651,0.819960184875
6 | 4,-0.489039982231,-0.521472471427,-0.0191404777995,-0.239752739911
7 | 5,-0.48188485049,-0.492339376929,-0.307167238901,-1.26176740952
8 | 6,-0.555727226358,-0.553404478151,-1.01016642708,-0.743759889463
9 | 7,-0.539024706541,-0.602280832879,-1.00859221518,-1.39287291058
10 | 8,-0.738412343161,-0.602902030843,-2.11457318534,-2.29842137052
11 | 9,-0.768917162284,-0.720355418576,-2.04250907698,-2.59417466767
12 | 10,-0.749563728177,-0.589364020732,-3.46358915257,-3.92952213904
13 | 11,-0.762709734087,-0.791908379442,-4.36019029162,-4.52691543113
14 | 12,-0.866095665124,-0.805394885858,-4.57234042252,-5.46880489804
15 | 13,-0.922569600299,-0.909150245625,-5.997458314,-5.57089682714
16 | 14,-1.07880133185,-0.935908100975,-7.25701257977,-6.83487414144
17 | 15,-0.999286663101,-0.976495874292,-7.10755034781,-6.87972128419
18 | 16,-1.00161900736,-0.943809378838,-7.28883353358,-7.42628195028
19 | 17,-1.11670103663,-1.03477349419,-7.30325806876,-7.06442722602
20 | 18,-1.04278266711,-1.09382975053,-7.02201312774,-7.04544938805
21 | 19,-1.08573146253,-1.01383510421,-8.07045885615,-7.02074778002
22 | 20,-0.993966390357,-1.10756310157,-7.36957629383,
23 | 21,-1.02681002124,-1.07051201863,-6.53891230924,
24 | 22,-1.06987084799,-1.04057194514,-6.87530983536,
25 | 23,-1.04925144746,-1.10007116538,-6.34522780789,
26 | 24,-0.999196152108,-1.04060565504,-5.7628445881,
27 | 25,-1.01617356747,-1.08475795706,-6.2371166521,
28 | 26,-1.01028887142,-1.00383396367,-4.49292820463,
29 | 27,-0.88826992641,-0.755630693864,-3.39923749694,
30 | 28,-0.876498690544,-0.729213074475,-2.45054246875,
31 | 29,-0.804019286202,-0.697503493928,-3.04850091819,
32 | 30,-0.638961862734,-0.774696633008,-2.00664562651,
33 | 31,-0.561536189568,-0.626411695988,-2.10825771141,
34 | 32,-0.577258469797,-0.536326127664,-2.21398229768,
35 | 33,-0.456474908502,-0.388589058816,-1.89354712914,
36 | 34,-0.40274078375,-0.365621066605,-1.24473688046,
37 | 35,-0.36826160905,-0.381772799734,-1.1617396941,
38 | 36,-0.270869359685,-0.220637172282,-1.32854051571,
39 | 37,0.131534782467,-0.0522709910816,-1.92048380434,
40 | 38,0.0465234590277,-0.0472656625551,-0.653311681788,
41 | 39,0.11819210267,0.0453546638924,-0.305353190529,
42 | 40,0.0961432834555,0.137325205769,-0.414675299422,
43 | 41,0.24694665642,0.178547346931,-0.416920075507,
44 | 42,0.0849358835698,0.182315416506,-0.349482469129,
45 | 43,0.202884795905,0.31425325432,0.730267965137,
46 | 44,0.36918077979,0.394181436577,1.87272501666,
47 | 45,0.251117148124,0.265717548279,2.25548872592,
48 | 46,0.347026746968,0.380646760519,2.07543812431,
49 | 47,0.339635558372,0.287233783145,2.63891415118,
50 | 48,0.23572643886,0.520832041789,3.56811434318,
51 | 49,0.477583486016,0.347676799045,2.85791982111,
52 | 50,0.388026578593,0.406110630739,4.72631570056,
53 | 51,0.413461165104,0.355747350033,4.59195291216,
54 | 52,0.527056824438,0.464793626638,4.69834742772,
55 | 53,0.558386938821,0.437846661747,5.20712422376,
56 | 54,0.454774237143,0.484058638793,5.23996972621,
57 | 55,0.386152471799,0.451936388104,5.86157510512,
58 | 56,0.494786339204,0.411595971591,5.30942186796,
59 | 57,0.373864523987,0.554641375626,5.74347301816,
60 | 58,0.366995220565,0.425072050389,5.57848492965,
61 | 59,0.4147627126,0.4973381191,5.55014359357,
62 | 60,0.403154355535,0.35185803537,5.66201000288,
63 | 61,0.375679175812,0.468054619754,5.26138591023,
64 | 62,0.34657858748,0.404047274042,5.11772321072,
65 | 63,0.320576958273,0.426272994773,5.15505301457,
66 | 64,0.330387688775,0.374129656483,4.34654575173,
67 | 65,0.319804314276,0.27422676843,4.05721495168,
68 | 66,0.262433474045,0.271435788352,3.91648234728,
69 | 67,0.0229774587798,0.0621793065418,0.273374330882,
70 | 68,0.0895110295288,-0.00875693968781,0.660415800554,
71 | 69,-0.0244138966938,0.0338096967996,0.310787393987,
72 | 70,-0.00621018403663,-0.041340598981,-0.934193703543,
73 | 71,-0.0618860893405,-0.104948247613,-0.737796709392,
74 | 72,-0.0448827973927,-0.0519408518404,-1.25504780041,
75 | 73,-0.19707152874,-0.0856726117883,-2.60208605761,
76 | 74,-0.185548503234,-0.108623213222,-2.36537532116,
77 | 75,-0.195956944486,-0.267228149994,-2.44176487652,
78 | 76,-0.253216794751,-0.342755086234,-2.81974001005,
79 | 77,-0.194117755947,-0.340613549698,-4.08020346844,
80 | 78,-0.371591934946,-0.446129901351,-4.57880157612,
81 | 79,-0.441940143491,-0.381028421048,-4.22829058178,
82 | 80,-0.384128444603,-0.408240509196,-5.25553224592,
83 | 81,-0.535910388988,-0.496559953537,-5.39966091501,
84 | 82,-0.538940455647,-0.534588151551,-4.81491821424,
85 | 83,-0.526230405652,-0.512116302755,-5.51213402325,
86 | 84,-0.54315687376,-0.534730949589,-4.67560791878,
87 | 85,-0.627384537738,-0.512470503976,-5.16470926488,
88 | 86,-0.513792914445,-0.526184441474,-4.3996079997,
89 | 87,-0.621201169962,-0.654601758986,-4.16341513576,
90 | 88,-0.655393451869,-0.562106349189,-2.69999251443,
91 | 89,-0.575741124795,-0.609792840587,-2.46933780786,
92 | 90,-0.628991615438,-0.59686088715,-2.73918510371,-2.3382674271
93 | 91,-0.62583276838,-0.524876351371,-2.54085727502,-1.9757667032
94 | 92,-0.527102503294,-0.457859996393,-2.11208036884,-1.33026379785
95 | 93,-0.602801861811,-0.511033690741,0.108076949795,-1.12144209759
96 | 94,-0.567523587952,-0.601851720247,-0.429703648461,-0.937054281839
97 | 95,-0.588562812968,-0.589995384128,-0.280099821981,-0.694963977988
98 | 96,-0.628469795574,-0.522985410838,-0.14731360488,-1.28176199057
99 | 97,-0.578262186051,-0.608735752905,-0.0129922349977,-0.188999248821
100 | 98,-0.63682113281,-0.503519919152,-0.11608811149,-0.794658307851
101 | 99,-0.617420047553,-0.592875017365,0.0607937241199,-0.199218737203
102 | 100,,-0.581751884194,0.0196134729741,-0.781691029335
103 | 101,,-0.564875308564,0.0627871855993,-0.377686650678
104 | 102,,-0.545189087455,0.987035697219,-0.456999656305
105 | 103,,-0.606665522195,-0.251298010275,-0.0356259467834
106 | 104,,-0.597310277444,-0.531742603815,-0.623569540165
107 | 105,,-0.677107469364,0.0344524562043,-0.879300571312
108 | 106,,-0.689373157484,-0.618612905316,-0.914105914888
109 | 107,,-0.802125884127,-1.13748646695,-0.941450341563
110 | 108,,-0.651032216268,-0.921428847536,-0.445192493217
111 | 109,,-0.592487637926,-1.17153226234,-0.917613006423
112 | 110,,-0.731178604776,-0.780296022452,-1.09696201549
113 | 111,,-0.677446095701,-1.13081047613,-1.10211451498
114 | 112,,-0.811508794428,-1.36696577843,-1.17894217581
115 | 113,,-0.706018841551,-2.28562941671,-2.07699367178
116 | 114,,-0.715293204131,-1.90001968699,-3.08423303085
117 | 115,,-0.755221633145,-1.42429665995,-2.32106264164
118 | 116,,-0.82279815825,-2.39453003866,-2.51639428312
119 | 117,,-0.932563582129,-2.53711486594,-3.36080145608
120 | 118,,-0.868934346204,-3.70760387423,-3.61879603892
121 | 119,,-0.886588705011,-3.53518593956,-3.5658394145
122 | 120,,-0.934191725195,-4.05101335276,-4.14199761667
123 | 121,,-0.917459921059,-3.94389363147,-4.23112471648
124 | 122,,-1.00168012822,-3.8069667558,-4.88145454873
125 | 123,,-0.995552069642,-5.8543628772,-5.41235214768
126 | 124,,-1.07418718212,-6.05945106001,-5.88355209758
127 | 125,,-1.0170354388,-5.8071617479,-6.0212035886
128 | 126,,-1.05377397847,-7.10011593883,-6.26527891709
129 | 127,,-1.08785409697,-6.48030115528,-6.85344458341
130 | 128,,-1.0055364924,-6.72427853812,-7.16520155528
131 | 129,,-0.909885443219,-6.98346538684,-7.14125090122
132 | 130,,-1.05112394303,-6.96577499828,-6.98866454209
133 | 131,,-0.943035468788,-7.0042301516,-6.42087692129
134 | 132,,-0.934202438682,-7.0806300868,-7.29202493675
135 | 133,,-0.931593999607,-7.10960819852,-6.68454653917
136 | 134,,-0.90809878576,-6.63063819084,-6.85357725829
137 | 135,,-0.783408985953,-5.78930463428,-6.08765031424
138 | 136,,-0.839915040658,-6.43009730531,-6.4788462517
139 | 137,,-0.693831629366,-6.53198601393,-5.88137467391
140 | 138,,-0.763467113806,-6.2205887938,-5.56751634457
141 | 139,,-0.660178128912,-5.34335545216,-5.17350330689
142 | 140,,-0.564761595567,-5.19964440327,-4.30125140308
143 | 141,,-0.444577661918,-4.29037159297,-4.19980692241
144 | 142,,-0.51385563255,-4.56423456059,-3.90216741884
145 | 143,,-0.410714254598,-3.50636337065,-3.26981655628
146 | 144,,-0.33808530724,-3.46753228226,-3.00588381908
147 | 145,,-0.28078110458,-2.97202393436,-2.8595914268
148 | 146,,-0.27592216201,-2.7874559884,-2.74106903314
149 | 147,,-0.072222606287,-1.2768773316,-0.740493884202
150 | 148,,-0.167862511982,-0.603035778658,-0.841071189925
151 | 149,,-0.0434109376964,0.109467321499,0.00224602055382
152 | 150,,-0.0274024703419,0.928557042607,1.40598489626
153 | 151,,0.246090062523,3.56383390666,2.00557675533
154 | 152,,0.196745496222,2.94618292502,2.92392146161
155 | 153,,0.301690456599,3.88390648454,4.30429234271
156 | 154,,0.370070558434,4.43286043312,3.63068237163
157 | 155,,0.5145470924,5.56212347055,4.87935637392
158 | 156,,0.517208160265,6.27951875629,5.54776792642
159 | 157,,0.593807367457,6.54487444961,5.55449937738
160 | 158,,0.54327462674,6.32335418483,5.22310103097
161 | 159,,0.600743939808,6.55567594344,6.28621206916
162 | 160,0.71716025497,0.596788115717,5.6179593775,5.41502348819
163 | 161,0.654320840577,0.754666706475,6.36990987443,5.7292198581
164 | 162,0.845022402766,0.789180037137,6.36696780309,6.32231141791
165 | 163,0.655467952214,0.657019305401,5.78792908121,6.21085131329
166 | 164,0.653928980344,0.72327999849,5.80939110916,5.80037430586
167 | 165,0.693646272389,0.792321300659,4.73667687742,4.65926318083
168 | 166,0.637629666669,0.662774517696,3.90364480855,4.67362735788
169 | 167,0.746705652218,0.593542670092,3.9435586017,4.88312115222
170 | 168,0.660136604142,0.598074367042,4.07660027767,3.8117544075
171 | 169,0.617708412898,0.653607893265,4.12753490538,4.43114083713
172 | 170,0.481677188905,0.665434207281,3.31969763439,3.57849604908
173 | 171,0.480487208914,0.467947876609,2.77967655101,3.38155610332
174 | 172,0.458310071032,0.358955289442,3.08212097436,3.08683682987
175 | 173,0.377531214162,0.360464175025,2.43560685412,2.04952634877
176 | 174,0.369916799631,0.416183898041,1.6107718036,2.67562935122
177 | 175,0.260021884513,0.296005824387,2.11652861928,2.20466402802
178 | 176,0.340217941161,0.273920455141,2.14299416896,1.53220637103
179 | 177,0.288288118858,0.300467870378,1.58893146226,1.47902866982
180 | 178,0.309966445761,0.308226111529,1.00015182281,1.44310058381
181 | 179,0.149166577953,0.180957385769,0.813123118386,0.928808138286
182 | 180,-0.0238289840001,0.1164498681,0.876646727027,0.425869897337
183 | 181,0.0461211531084,-0.0579444904018,-0.177485252276,-0.76370390296
184 | 182,-0.150292334773,-0.0536490277302,-0.818367988869,-1.51913555719
185 | 183,-0.166792751333,-0.0943750548034,-1.48801385091,-1.86180911804
186 | 184,-0.143122921347,-0.144203552956,-1.79967752701,-1.72638585992
187 | 185,-0.185316705718,-0.25738346124,-1.41068854073,-1.94786897097
188 | 186,-0.33931057671,-0.410797896303,-3.08250086287,-3.23646548844
189 | 187,-0.245985989086,-0.325261617224,-3.04328434967,-3.06459859034
190 | 188,-0.3889494658,-0.38658194076,-3.97589602577,-3.34086965027
191 | 189,-0.286185999952,-0.254986161145,-4.42443892118,-2.80713367932
192 | 190,-0.414064955646,-0.478097248064,-3.67798376458,-4.07086179361
193 | 191,-0.429343511921,-0.45797521645,-4.50371105496,-4.45735822411
194 | 192,-0.439900649186,-0.546673475188,-5.40232827146,-4.17942165926
195 | 193,-0.466384393878,-0.399297859863,-4.74653536493,-4.92613441427
196 | 194,-0.458320494289,-0.418123946719,-4.755217806,-4.89266487666
197 | 195,-0.426274118133,-0.430658743687,-5.68348216853,-4.50955858591
198 | 196,-0.398264982146,-0.346140460657,-4.65355999793,-4.19488032682
199 | 197,-0.337008349942,-0.378226101813,-4.51529750126,-3.74818433608
200 | 198,-0.38089926474,-0.304667907512,-3.3299742552,-3.91669191384
201 | 199,-0.303056510721,-0.365472853066,-4.37731356292,-4.13301600421
202 |
--------------------------------------------------------------------------------
/tutorials/resources/up-or-down.csv:
--------------------------------------------------------------------------------
1 | y
2 | 0.0
3 | 0.0
4 | 0.0
5 | 0.0
6 | 0.0
7 | 0.0
8 | 0.0
9 | 0.0
10 | 0.0
11 | 0.0
12 | 0.0
13 | 0.0
14 | 0.0
15 | 0.0
16 | 0.0
17 | 0.0
18 | 0.0
19 | 0.0
20 | 0.0
21 | 0.0
22 | 0.0
23 | 0.2777777777777778
24 | 0.5555555555555556
25 | 0.8333333333333334
26 | 1.1111111111111112
27 | 1.3888888888888888
28 | 1.6666666666666667
29 | 1.9444444444444446
30 | 2.2222222222222223
31 | 2.5
32 | 2.5
33 | 2.7777777777777777
34 | 3.0555555555555554
35 | 3.3333333333333335
36 | 3.611111111111111
37 | 3.888888888888889
38 | 4.166666666666667
39 | 4.444444444444445
40 | 4.722222222222222
41 | 5.0
42 | 5.0
43 | 5.0
44 | 5.0
45 | 5.0
46 | 5.0
47 | 5.0
48 | 5.0
49 | 5.0
50 | 5.0
51 | 5.0
52 | 5.0
53 | 5.0
54 | 5.0
55 | 5.0
56 | 5.0
57 | 5.0
58 | 4.642857142857143
59 | 4.285714285714286
60 | 3.928571428571429
61 | 3.571428571428571
62 | 3.2142857142857144
63 | 2.857142857142857
64 | 2.5
65 | 2.142857142857143
66 | 1.7857142857142856
67 | 1.4285714285714284
68 | 1.0714285714285712
69 | 0.7142857142857144
70 | 0.35714285714285676
71 | 0.0
72 | 0.0
73 | 0.0
74 | 0.0
75 | 0.0
76 | 0.0
77 | 0.0
78 | 0.0
79 | 0.0
80 | 0.0
81 | 0.0
82 | 0.0
83 | 0.0
84 | 0.0
85 | 0.0
86 | 0.0
87 | 0.0
88 | 0.0
89 | 0.0
90 | 0.0
91 | 0.0
92 | 0.0
93 | 0.2777777777777778
94 | 0.5555555555555556
95 | 0.8333333333333334
96 | 1.1111111111111112
97 | 1.3888888888888888
98 | 1.6666666666666667
99 | 1.9444444444444446
100 | 2.2222222222222223
101 | 2.5
102 | 2.5
103 | 2.142857142857143
104 | 1.7857142857142856
105 | 1.4285714285714286
106 | 1.0714285714285714
107 | 0.7142857142857142
108 | 0.3571428571428572
109 | 0.0
110 | -0.3571428571428572
111 | -0.7142857142857144
112 | -1.0714285714285716
113 | -1.4285714285714288
114 | -1.7857142857142856
115 | -2.1428571428571432
116 | -2.5
117 | -2.5
118 | -2.3214285714285716
119 | -2.142857142857143
120 | -1.9642857142857144
121 | -1.7857142857142856
122 | -1.6071428571428572
123 | -1.4285714285714286
124 | -1.25
125 | -1.0714285714285714
126 | -0.8928571428571428
127 | -0.7142857142857142
128 | -0.5357142857142856
129 | -0.3571428571428572
130 | -0.17857142857142838
131 | 0.0
132 | 0.0
133 | 0.0
134 | 0.0
135 | 0.0
136 | 0.0
137 | 0.0
138 | 0.0
139 | 0.0
140 | 0.0
141 | 0.0
142 | 0.0
143 | 0.0
144 | 0.0
145 | 0.0
146 | 0.0
147 | 0.0
148 | 0.2777777777777778
149 | 0.5555555555555556
150 | 0.8333333333333334
151 | 1.1111111111111112
152 | 1.3888888888888888
153 | 1.6666666666666667
154 | 1.9444444444444446
155 | 2.2222222222222223
156 | 2.5
157 | 2.5
158 | 2.142857142857143
159 | 1.7857142857142856
160 | 1.4285714285714286
161 | 1.0714285714285714
162 | 0.7142857142857142
163 | 0.3571428571428572
164 | 0.0
165 | -0.3571428571428572
166 | -0.7142857142857144
167 | -1.0714285714285716
168 | -1.4285714285714288
169 | -1.7857142857142856
170 | -2.1428571428571432
171 | -2.5
172 | -2.5
173 | -2.3214285714285716
174 | -2.142857142857143
175 | -1.9642857142857144
176 | -1.7857142857142856
177 | -1.6071428571428572
178 | -1.4285714285714286
179 | -1.25
180 | -1.0714285714285714
181 | -0.8928571428571428
182 | -0.7142857142857142
183 | -0.5357142857142856
184 | -0.3571428571428572
185 | -0.17857142857142838
186 | 0.0
187 | 0.0
188 | 0.0
189 | 0.0
190 | 0.0
191 | 0.0
192 | 0.0
193 | 0.0
194 | 0.0
195 | 0.0
196 | 0.0
197 | 0.0
198 | 0.0
199 | 0.0
200 | 0.0
201 | 0.0
202 | 0.0
203 | 0.0
204 | 0.0
205 | 0.0
206 | 0.0
207 | 0.0
208 | 0.2777777777777778
209 | 0.5555555555555556
210 | 0.8333333333333334
211 | 1.1111111111111112
212 | 1.3888888888888888
213 | 1.6666666666666667
214 | 1.9444444444444446
215 | 2.2222222222222223
216 | 2.5
217 | 2.5
218 | 2.7777777777777777
219 | 3.0555555555555554
220 | 3.3333333333333335
221 | 3.611111111111111
222 | 3.888888888888889
223 | 4.166666666666667
224 | 4.444444444444445
225 | 4.722222222222222
226 | 5.0
227 | 5.0
228 | 5.0
229 | 5.0
230 | 5.0
231 | 5.0
232 | 5.0
233 | 5.0
234 | 5.0
235 | 5.0
236 | 5.0
237 | 5.0
238 | 5.0
239 | 5.0
240 | 5.0
241 | 5.0
242 | 5.0
243 | 4.642857142857143
244 | 4.285714285714286
245 | 3.928571428571429
246 | 3.571428571428571
247 | 3.2142857142857144
248 | 2.857142857142857
249 | 2.5
250 | 2.142857142857143
251 | 1.7857142857142856
252 | 1.4285714285714284
253 | 1.0714285714285712
254 | 0.7142857142857144
255 | 0.35714285714285676
256 | 0.0
257 | 0.0
258 | 0.0
259 | 0.0
260 | 0.0
261 | 0.0
262 | 0.0
263 | 0.0
264 | 0.0
265 | 0.0
266 | 0.0
267 | 0.0
268 | 0.0
269 | 0.0
270 | 0.0
271 | 0.0
272 | 0.0
273 | 0.2777777777777778
274 | 0.5555555555555556
275 | 0.8333333333333334
276 | 1.1111111111111112
277 | 1.3888888888888888
278 | 1.6666666666666667
279 | 1.9444444444444446
280 | 2.2222222222222223
281 | 2.5
282 |
--------------------------------------------------------------------------------