├── .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 | [![Build Status](https://travis-ci.org/probcomp/trcrpm.svg?branch=master)](https://travis-ci.org/probcomp/trcrpm) 4 | [![Anaconda Version Badge](https://anaconda.org/probcomp/trcrpm/badges/version.svg)](https://anaconda.org/probcomp/trcrpm) 5 | [![Anaconda Platforms Badge](https://anaconda.org/probcomp/trcrpm/badges/platforms.svg)](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 | --------------------------------------------------------------------------------