├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── __init__.py ├── datamp.png ├── datampanom.png ├── docs ├── Releases.md └── examples │ ├── Algorithm Comparison.ipynb │ ├── Anomalies_Matrix_Profile_Discords.ipynb │ ├── Matrix_Profile_Tutorial.ipynb │ ├── Motif Discovery.ipynb │ └── rawdata.csv ├── matrixprofile ├── __init__.py ├── annotation_vector.py ├── discords.py ├── distanceProfile.py ├── fluss.py ├── matrixProfile.py ├── motifs.py ├── order.py ├── regimes.py ├── scrimp.py └── utils.py ├── requirements.txt ├── setup.cfg ├── setup.py └── tests ├── __init__.py ├── mp.txt ├── sampledata.txt ├── test_discords.py ├── test_distanceProfile.py ├── test_fluss.py ├── test_matrixProfile.py ├── test_motifs.py ├── test_order.py ├── test_regimes.py ├── test_scrimp.py └── test_utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .pytest_cache 3 | *.pyc 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | - "3.4" 5 | - "3.5" 6 | - "3.6" 7 | # command to install dependencies 8 | install: 9 | - pip install -r requirements.txt 10 | # command to run tests 11 | script: pytest 12 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at 59 | [opensource@target.com](mailto:opensource@target.com). All 60 | complaints will be reviewed and investigated and will result in a response that 61 | is deemed necessary and appropriate to the circumstances. The project team is 62 | obligated to maintain confidentiality with regard to the reporter of an incident. 63 | Further details of specific enforcement policies may be posted separately. 64 | 65 | Project maintainers who do not follow or enforce the Code of Conduct in good 66 | faith may face temporary or permanent repercussions as determined by other 67 | members of the project's leadership. 68 | 69 | ## Attribution 70 | 71 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 72 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 73 | 74 | [homepage]: https://www.contributor-covenant.org 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to matrixprofile-ts 2 | 3 | ## Issues 4 | Please, raise issues in GitHub to help keep matrixprofile-ts updated! 5 | 6 | ## How to contribute to matrixprofile-ts 7 | 1. Fork matrixprofile-ts 8 | 2. Start a new branch for your feature 9 | * branch prefixes we use: 10 | * `feature_{}`: for features 11 | * `fix_{}`: something broke and we need to fix it now 12 | 3. Open and submit your Pull Request! A dialog will begin and thank you for your contributions. 13 | 14 | ## Guidelines 15 | 1. Be Respectful 16 | * We appreciate contributions to matrixprofile-ts and we ask you to respect 17 | one another. 18 | 2. Be Responsible 19 | * You are responsible for your Pull Request submission 20 | 3. Give Credit 21 | * If any submissions or contributions are built upon other work (e.g. research papers, open sourced projects, public code), please cite or attach any information about the original source. People should be credited for the work they've done. 22 | 23 | For further questions, please review the Code of Conduct. 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2018 Target Brands, Inc. 2 | 3 | 4 | Apache License 5 | Version 2.0, January 2004 6 | http://www.apache.org/licenses/ 7 | 8 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 9 | 10 | 1. Definitions. 11 | 12 | "License" shall mean the terms and conditions for use, reproduction, 13 | and distribution as defined by Sections 1 through 9 of this document. 14 | 15 | "Licensor" shall mean the copyright owner or entity authorized by 16 | the copyright owner that is granting the License. 17 | 18 | "Legal Entity" shall mean the union of the acting entity and all 19 | other entities that control, are controlled by, or are under common 20 | control with that entity. For the purposes of this definition, 21 | "control" means (i) the power, direct or indirect, to cause the 22 | direction or management of such entity, whether by contract or 23 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 24 | outstanding shares, or (iii) beneficial ownership of such entity. 25 | 26 | "You" (or "Your") shall mean an individual or Legal Entity 27 | exercising permissions granted by this License. 28 | 29 | "Source" form shall mean the preferred form for making modifications, 30 | including but not limited to software source code, documentation 31 | source, and configuration files. 32 | 33 | "Object" form shall mean any form resulting from mechanical 34 | transformation or translation of a Source form, including but 35 | not limited to compiled object code, generated documentation, 36 | and conversions to other media types. 37 | 38 | "Work" shall mean the work of authorship, whether in Source or 39 | Object form, made available under the License, as indicated by a 40 | copyright notice that is included in or attached to the work 41 | (an example is provided in the Appendix below). 42 | 43 | "Derivative Works" shall mean any work, whether in Source or Object 44 | form, that is based on (or derived from) the Work and for which the 45 | editorial revisions, annotations, elaborations, or other modifications 46 | represent, as a whole, an original work of authorship. For the purposes 47 | of this License, Derivative Works shall not include works that remain 48 | separable from, or merely link (or bind by name) to the interfaces of, 49 | the Work and Derivative Works thereof. 50 | 51 | "Contribution" shall mean any work of authorship, including 52 | the original version of the Work and any modifications or additions 53 | to that Work or Derivative Works thereof, that is intentionally 54 | submitted to Licensor for inclusion in the Work by the copyright owner 55 | or by an individual or Legal Entity authorized to submit on behalf of 56 | the copyright owner. For the purposes of this definition, "submitted" 57 | means any form of electronic, verbal, or written communication sent 58 | to the Licensor or its representatives, including but not limited to 59 | communication on electronic mailing lists, source code control systems, 60 | and issue tracking systems that are managed by, or on behalf of, the 61 | Licensor for the purpose of discussing and improving the Work, but 62 | excluding communication that is conspicuously marked or otherwise 63 | designated in writing by the copyright owner as "Not a Contribution." 64 | 65 | "Contributor" shall mean Licensor and any individual or Legal Entity 66 | on behalf of whom a Contribution has been received by Licensor and 67 | subsequently incorporated within the Work. 68 | 69 | 2. Grant of Copyright License. Subject to the terms and conditions of 70 | this License, each Contributor hereby grants to You a perpetual, 71 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 72 | copyright license to reproduce, prepare Derivative Works of, 73 | publicly display, publicly perform, sublicense, and distribute the 74 | Work and such Derivative Works in Source or Object form. 75 | 76 | 3. Grant of Patent License. Subject to the terms and conditions of 77 | this License, each Contributor hereby grants to You a perpetual, 78 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 79 | (except as stated in this section) patent license to make, have made, 80 | use, offer to sell, sell, import, and otherwise transfer the Work, 81 | where such license applies only to those patent claims licensable 82 | by such Contributor that are necessarily infringed by their 83 | Contribution(s) alone or by combination of their Contribution(s) 84 | with the Work to which such Contribution(s) was submitted. If You 85 | institute patent litigation against any entity (including a 86 | cross-claim or counterclaim in a lawsuit) alleging that the Work 87 | or a Contribution incorporated within the Work constitutes direct 88 | or contributory patent infringement, then any patent licenses 89 | granted to You under this License for that Work shall terminate 90 | as of the date such litigation is filed. 91 | 92 | 4. Redistribution. You may reproduce and distribute copies of the 93 | Work or Derivative Works thereof in any medium, with or without 94 | modifications, and in Source or Object form, provided that You 95 | meet the following conditions: 96 | 97 | (a) You must give any other recipients of the Work or 98 | Derivative Works a copy of this License; and 99 | 100 | (b) You must cause any modified files to carry prominent notices 101 | stating that You changed the files; and 102 | 103 | (c) You must retain, in the Source form of any Derivative Works 104 | that You distribute, all copyright, patent, trademark, and 105 | attribution notices from the Source form of the Work, 106 | excluding those notices that do not pertain to any part of 107 | the Derivative Works; and 108 | 109 | (d) If the Work includes a "NOTICE" text file as part of its 110 | distribution, then any Derivative Works that You distribute must 111 | include a readable copy of the attribution notices contained 112 | within such NOTICE file, excluding those notices that do not 113 | pertain to any part of the Derivative Works, in at least one 114 | of the following places: within a NOTICE text file distributed 115 | as part of the Derivative Works; within the Source form or 116 | documentation, if provided along with the Derivative Works; or, 117 | within a display generated by the Derivative Works, if and 118 | wherever such third-party notices normally appear. The contents 119 | of the NOTICE file are for informational purposes only and 120 | do not modify the License. You may add Your own attribution 121 | notices within Derivative Works that You distribute, alongside 122 | or as an addendum to the NOTICE text from the Work, provided 123 | that such additional attribution notices cannot be construed 124 | as modifying the License. 125 | 126 | You may add Your own copyright statement to Your modifications and 127 | may provide additional or different license terms and conditions 128 | for use, reproduction, or distribution of Your modifications, or 129 | for any such Derivative Works as a whole, provided Your use, 130 | reproduction, and distribution of the Work otherwise complies with 131 | the conditions stated in this License. 132 | 133 | 5. Submission of Contributions. Unless You explicitly state otherwise, 134 | any Contribution intentionally submitted for inclusion in the Work 135 | by You to the Licensor shall be under the terms and conditions of 136 | this License, without any additional terms or conditions. 137 | Notwithstanding the above, nothing herein shall supersede or modify 138 | the terms of any separate license agreement you may have executed 139 | with Licensor regarding such Contributions. 140 | 141 | 6. Trademarks. This License does not grant permission to use the trade 142 | names, trademarks, service marks, or product names of the Licensor, 143 | except as required for reasonable and customary use in describing the 144 | origin of the Work and reproducing the content of the NOTICE file. 145 | 146 | 7. Disclaimer of Warranty. Unless required by applicable law or 147 | agreed to in writing, Licensor provides the Work (and each 148 | Contributor provides its Contributions) on an "AS IS" BASIS, 149 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 150 | implied, including, without limitation, any warranties or conditions 151 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 152 | PARTICULAR PURPOSE. You are solely responsible for determining the 153 | appropriateness of using or redistributing the Work and assume any 154 | risks associated with Your exercise of permissions under this License. 155 | 156 | 8. Limitation of Liability. In no event and under no legal theory, 157 | whether in tort (including negligence), contract, or otherwise, 158 | unless required by applicable law (such as deliberate and grossly 159 | negligent acts) or agreed to in writing, shall any Contributor be 160 | liable to You for damages, including any direct, indirect, special, 161 | incidental, or consequential damages of any character arising as a 162 | result of this License or out of the use or inability to use the 163 | Work (including but not limited to damages for loss of goodwill, 164 | work stoppage, computer failure or malfunction, or any and all 165 | other commercial damages or losses), even if such Contributor 166 | has been advised of the possibility of such damages. 167 | 168 | 9. Accepting Warranty or Additional Liability. While redistributing 169 | the Work or Derivative Works thereof, You may choose to offer, 170 | and charge a fee for, acceptance of support, warranty, indemnity, 171 | or other liability obligations and/or rights consistent with this 172 | License. However, in accepting such obligations, You may act only 173 | on Your own behalf and on Your sole responsibility, not on behalf 174 | of any other Contributor, and only if You agree to indemnify, 175 | defend, and hold each Contributor harmless for any liability 176 | incurred by, or claims asserted against, such Contributor by reason 177 | of your accepting any such warranty or additional liability. 178 | 179 | END OF TERMS AND CONDITIONS 180 | 181 | APPENDIX: How to apply the Apache License to your work. 182 | 183 | To apply the Apache License to your work, attach the following 184 | boilerplate notice, with the fields enclosed by brackets "[]" 185 | replaced with your own identifying information. (Don't include 186 | the brackets!) The text should be enclosed in the appropriate 187 | comment syntax for the file format. We also recommend that a 188 | file or class name and description of purpose be included on the 189 | same "printed page" as the copyright notice for easier 190 | identification within third-party archives. 191 | 192 | Copyright [yyyy] [name of copyright owner] 193 | 194 | Licensed under the Apache License, Version 2.0 (the "License"); 195 | you may not use this file except in compliance with the License. 196 | You may obtain a copy of the License at 197 | 198 | http://www.apache.org/licenses/LICENSE-2.0 199 | 200 | Unless required by applicable law or agreed to in writing, software 201 | distributed under the License is distributed on an "AS IS" BASIS, 202 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 203 | See the License for the specific language governing permissions and 204 | limitations under the License. 205 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![PyPI version](https://badge.fury.io/py/matrixprofile-ts.svg)](https://badge.fury.io/py/matrixprofile-ts) 2 | [![Build Status](https://travis-ci.org/target/matrixprofile-ts.svg)](https://travis-ci.org/target/matrixprofile-ts) 3 | [![Downloads](https://pepy.tech/badge/matrixprofile-ts)](https://pepy.tech/project/matrixprofile-ts) 4 | [![Downloads/Week](https://pepy.tech/badge/matrixprofile-ts/week)](https://pepy.tech/project/matrixprofile-ts/week) 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | # matrixprofile-ts 7 | 8 | matrixprofile-ts is a Python 2 and 3 library for evaluating time series data using the Matrix Profile algorithms developed by the [Keogh](https://www.cs.ucr.edu/~eamonn/MatrixProfile.html) and [Mueen](https://www.cs.unm.edu/~mueen/) research groups at UC-Riverside and the University of New Mexico. Current implementations include MASS, STMP, STAMP, STAMPI, STOMP, SCRIMP++, and FLUSS. 9 | 10 | Read the Target blog post [here](https://tech.target.com/2018/12/11/matrix-profile.html). 11 | 12 | Further academic description can be found [here](https://www.cs.ucr.edu/~eamonn/MatrixProfile.html). 13 | 14 | The PyPi page for matrixprofile-ts is [here](https://pypi.org/project/matrixprofile-ts/) 15 | 16 | 17 | 18 | ## Contents 19 | - [Installation](#installation) 20 | - [Quick start](#quick-start) 21 | - [Detailed Example](#detailed-example) 22 | - [Algorithm Comparison](#algorithm-comparison) 23 | - [Matrix Profile in Other Languages](#matrix-profile-in-other-languages) 24 | - [Contact](#contact) 25 | - [Citations](#citations) 26 | 27 | 28 | 29 | ## Installation 30 | 31 | Major releases of matrixprofile-ts are available on the Python Package Index: 32 | 33 | `pip install matrixprofile-ts` 34 | 35 | Details about each release can be found [here](https://github.com/target/matrixprofile-ts/blob/master/docs/Releases.md). 36 | 37 | ## Quick start 38 | 39 | ``` 40 | >>> from matrixprofile import * 41 | >>> import numpy as np 42 | >>> a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 43 | >>> matrixProfile.stomp(a,4) 44 | (array([0., 0., 0., 0., 0., 0., 0., 0., 0.]), array([4., 5., 6., 7., 0., 1., 2., 3., 0.])) 45 | ``` 46 | Note that SCRIMP++ is highly recommended for calculating the Matrix Profile due to its speed and anytime ability. 47 | 48 | ## Examples 49 | 50 | Jupyter notebooks containing various examples of how to use matrixprofile-ts can be found under `docs/examples`. 51 | 52 | As a basic introduction, we can take a synthetic signal and use STOMP to calculate the corresponding Matrix Profile (this is the same synthetic signal as in the [Golang Matrix Profile library](https://github.com/aouyang1/go-matrixprofile)). Code for this example can be found [here](https://github.com/target/matrixprofile-ts/blob/master/docs/examples/Matrix_Profile_Tutorial.ipynb) 53 | 54 | ![datamp](https://github.com/target/matrixprofile-ts/blob/master/datamp.png) 55 | 56 | 57 | There are several items of note: 58 | 59 | - The Matrix Profile value jumps at each phase change. High Matrix Profile values are associated with "discords": time series behavior that hasn't been observed before. 60 | 61 | - Repeated patterns in the data (or "motifs") lead to low Matrix Profile values. 62 | 63 | 64 | We can introduce an anomaly to the end of the time series and use STAMPI to detect it 65 | 66 | ![datampanom](https://github.com/target/matrixprofile-ts/blob/master/datampanom.png) 67 | 68 | The Matrix Profile has spiked in value, highlighting the (potential) presence of a new behavior. Note that Matrix Profile anomaly detection capabilities will depend on the nature of the data, as well as the selected subquery length parameter. Like all good algorithms, it's important to try out different parameter values. 69 | 70 | ## Algorithm Comparison 71 | 72 | This section shows the matrix profile algorithms and the time it takes to compute them. It also discusses use cases on when to use one versus another. The timing comparison is based on the synthetic sample data set to show run time speed. 73 | 74 | For a more comprehensive runtime comparison, please review the notebook [docs/examples/Algorithm Comparison.ipynb](https://github.com/target/matrixprofile-ts/blob/master/docs/examples/Algorithm%20Comparison.ipynb). 75 | 76 | All time comparisons were ran on a 4 core 2.8 ghz processor with 16 GB of memory. The operating system used was Ubuntu 18.04LTS 64 bit. 77 | 78 | | Algorithm | Time to Complete | Description | 79 | |-----------|------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 80 | | STAMP | 310 ms ± 1.73 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) | STAMP is an anytime algorithm that lets you sample the data set to get an approximate solution. Our implementation provides you with the option to specify the sampling size in percent format. | 81 | | STOMP | 79.8 ms ± 473 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) | STOMP computes an exact solution in a very efficient manner. When you have a historic time series that you would like to examine, STOMP is typically the quickest at giving an exact solution. | 82 | | SCRIMP++ | 59 ms ± 278 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) | SCRIMP++ merges the concepts of STAMP and STOMP together to provide an anytime algorithm that enables "interactive analysis speed". Essentially, it provides an exact or approximate solution in a very timely manner. Our implementation allows you to specify the max number of seconds you are willing to wait for a solution to obtain an approximate solution. If you are wanting the exact solution, it is able to provide that as well. The original authors of this algorithm suggest that SCRIMP++ can be used in all use cases. | 83 | 84 | ## Matrix Profile in Other Languages 85 | - [R: tsmp](https://github.com/matrix-profile-foundation/tsmp) 86 | - [Golang: go-matrixprofile](https://github.com/matrix-profile-foundation/go-matrixprofile) 87 | - [C++: khiva](https://github.com/shapelets/khiva) 88 | - [Python: matrixprofile](https://github.com/matrix-profile-foundation/matrixprofile) 89 | 90 | ## Contact 91 | - Frankie Cancino (frankiecancino@gmail.com) 92 | - Andrew Van Benschoten (avbs89@gmail.com) 93 | 94 | ## Citations 95 | 1. Chin-Chia Michael Yeh, Yan Zhu, Liudmila Ulanova, Nurjahan Begum, Yifei Ding, Hoang Anh Dau, Diego Furtado Silva, Abdullah Mueen, Eamonn Keogh (2016). Matrix Profile I: All Pairs Similarity Joins for Time Series: A Unifying View that Includes Motifs, Discords and Shapelets. IEEE ICDM 2016 96 | 97 | 2. Matrix Profile II: Exploiting a Novel Algorithm and GPUs to break the one Hundred Million Barrier for Time Series Motifs and Joins. Yan Zhu, Zachary Zimmerman, Nader Shakibay Senobari, Chin-Chia Michael Yeh, Gareth Funning, Abdullah Mueen, Philip Berisk and Eamonn Keogh (2016). EEE ICDM 2016 98 | 99 | 3. Matrix Profile V: A Generic Technique to Incorporate Domain Knowledge into Motif Discovery. Hoang Anh Dau and Eamonn Keogh. KDD'17, Halifax, Canada. 100 | 101 | 4. Matrix Profile XI: SCRIMP++: Time Series Motif Discovery at Interactive Speed. Yan Zhu, Chin-Chia Michael Yeh, Zachary Zimmerman, Kaveh Kamgar and Eamonn Keogh, ICDM 2018. 102 | 103 | 5. Matrix Profile VIII: Domain Agnostic Online Semantic Segmentation at Superhuman Performance Levels. Shaghayegh Gharghabi, Yifei Ding, Chin-Chia Michael Yeh, Kaveh Kamgar, Liudmila Ulanova, and Eamonn Keogh. ICDM 2017. 104 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/target/matrixprofile-ts/6a6f62b72d351f958118a127e81b20469f13b0e7/__init__.py -------------------------------------------------------------------------------- /datamp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/target/matrixprofile-ts/6a6f62b72d351f958118a127e81b20469f13b0e7/datamp.png -------------------------------------------------------------------------------- /datampanom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/target/matrixprofile-ts/6a6f62b72d351f958118a127e81b20469f13b0e7/datampanom.png -------------------------------------------------------------------------------- /docs/Releases.md: -------------------------------------------------------------------------------- 1 | ## 0.0.8 (released 8/3/19) 2 | - Minor bug fixes (see Issue #70) 3 | 4 | ## 0.0.7 (released 8/3/19) 5 | - Implementation of FLUSS algorithm for determining the Corrected Arc Curve 6 | - Implementation of algorithm to extract regimes from the Corrected Arc Curve 7 | - Fix for handling missing NaN/inf values 8 | - Ability to read in non-numpy array data types (lists, tuples) 9 | - Bug fixes for SCRIMP++ implementation 10 | 11 | 12 | ## 0.0.6 (released 6/13/19) 13 | - Fixed bug by requiring a later version of numpy 14 | 15 | ## 0.0.5 (released 6/9/19) 16 | - SCRIMP++ implementation 17 | - Algorithm performance benchmarking and use case descriptions 18 | - Improved self-join handling 19 | 20 | 21 | ## 0.0.4 (released 2/11/19) 22 | - Python 2 compatibility 23 | - Added parallel STAMP function 24 | - Updated docstrings for better interpretability 25 | - Refactored self-join logic 26 | 27 | ## 0.0.3 (released 1/2/19) 28 | - Implementation of STOMP for faster matrix profile calculation 29 | - Fixed bugs in testing sute (note that this does not impact performance for matrixprofile-ts 0.0.2) 30 | - Functionality for applying annotation vector ("apply_av") 31 | - Functionality for locating top K discords ("discords") 32 | -------------------------------------------------------------------------------- /docs/examples/rawdata.csv: -------------------------------------------------------------------------------- 1 | data 2 | 0.010397810228292748 3 | 0.2999788221634467 4 | 0.5407523804199618 5 | 0.759207383826371 6 | 0.9013408204126397 7 | 1.0415821314612956 8 | 0.9600399347956455 9 | 0.8149562392820489 10 | 0.6193257693858093 11 | 0.3468181702401875 12 | -0.004155752142434815 13 | -0.2990004348426142 14 | -0.6351587372315037 15 | -0.7744337156501432 16 | -0.9760871961788041 17 | -0.9858215709200417 18 | -0.9763098555114907 19 | -0.8416514099026343 20 | -0.5785228769712287 21 | -0.2775775392782455 22 | 0.019383813651720705 23 | 0.26204924915824784 24 | 0.5917062628815675 25 | 0.8565844758736795 26 | 0.976132821943113 27 | 0.9794006312795015 28 | 0.9763726440688296 29 | 0.7741133988729083 30 | 0.573361978833397 31 | 0.3422100796719294 32 | -0.026816995806231946 33 | -0.2962335338749448 34 | -0.5879458221648752 35 | -0.8500333854489095 36 | -0.9985371203156639 37 | -1.0107783816845974 38 | -0.9421182076550738 39 | -0.7660558308300447 40 | -0.580576572148165 41 | -0.30015935986059966 42 | -0.008823731165498868 43 | 0.31427503335637136 44 | 0.5869459919056349 45 | 0.8548123857286984 46 | 0.9807773703862337 47 | 0.9607381112820752 48 | 0.9793600136347534 49 | 0.7983420942978364 50 | 0.5508266369098543 51 | 0.27802027100886884 52 | 0.02398257810158575 53 | -0.29361285345182053 54 | -0.6279468733938993 55 | -0.8069789658037253 56 | -0.99108354992316 57 | -1.03481565979181 58 | -0.9934374900647783 59 | -0.8274961858429344 60 | -0.6218201601459842 61 | -0.3452365882129938 62 | -0.01773893171322098 63 | 0.31292444607889636 64 | 0.5948704150270221 65 | 0.8102951701860561 66 | 0.9694740293048988 67 | 1.0153040205135362 68 | 0.9535064922501415 69 | 0.8244440078173636 70 | 0.6094220897826409 71 | 0.32268141577876447 72 | -0.04871740908935948 73 | -0.3559487747962311 74 | -0.6279821648118419 75 | -0.8221058234585136 76 | -0.9184111037316791 77 | -1.015231829140843 78 | -0.9666250145225178 79 | -0.8337170120101626 80 | -0.6161381376269769 81 | -0.30351678081147065 82 | -0.009792915472817918 83 | 0.3096667007425866 84 | 0.5546532191259058 85 | 0.7921538204056463 86 | 0.9838493259102092 87 | 1.0200287873145815 88 | 0.9068491422615875 89 | 0.8589329433969803 90 | 0.5789392886145216 91 | 0.27018445805143065 92 | 0.028075408455850364 93 | -0.34980523193087387 94 | -0.6324357898430344 95 | -0.7875474127857829 96 | -0.9759802887522352 97 | -0.9651367079096843 98 | -0.9036683288880878 99 | -0.8377608994699172 100 | -0.6356318739599136 101 | -0.2644975183360643 102 | -0.04070298445001228 103 | 0.3236003318273422 104 | 0.5689738065751804 105 | 0.8038634307689914 106 | 0.9497804411531902 107 | 0.958247967651135 108 | 0.9682394269186169 109 | 0.7990358233173095 110 | 0.6278127670189053 111 | 0.3540053149850723 112 | -0.018066873239290474 113 | -0.30907844499970993 114 | -0.5977420205782907 115 | -0.8570361273424024 116 | -0.9365526296932095 117 | -1.0071311569930068 118 | -0.9670968411564231 119 | -0.7702722442898972 120 | -0.6141525048620353 121 | -0.28251617288162245 122 | -0.046424535256393035 123 | 0.3317747199791 124 | 0.6003689149882874 125 | 0.8103257449837312 126 | 0.9083013519743882 127 | 1.0224229058459169 128 | 0.9890410009257239 129 | 0.8567933421107212 130 | 0.6225352785189425 131 | 0.3422367881899405 132 | -0.02521554768129532 133 | -0.2676770880812971 134 | -0.6302815312790008 135 | -0.7755066142595122 136 | -0.938123347130622 137 | -0.9748259421103265 138 | -0.9378561729162741 139 | -0.8493235730510784 140 | -0.636302515342984 141 | -0.3006335201886976 142 | -0.04312438047979335 143 | 0.3588443754757969 144 | 0.6027040939523113 145 | 0.8575635522382743 146 | 0.9845370923170743 147 | 0.983205608571906 148 | 0.9671958343534873 149 | 0.8546190570346104 150 | 0.5688362799149568 151 | 0.27745606377515014 152 | 0.04670943413718269 153 | -0.2756928128221285 154 | -0.6068304072397388 155 | -0.778429817621183 156 | -0.9593239320761159 157 | -0.9781469550647225 158 | -0.9603828387501151 159 | -0.7694366676005329 160 | -0.5419676160322182 161 | -0.3571456722609853 162 | 0.02916723090820636 163 | 0.30137230976353135 164 | 0.539303380014776 165 | 0.8022868183828517 166 | 0.9915341400017263 167 | 1.0355704414574887 168 | 0.9053486805127865 169 | 0.8249200476757251 170 | 0.5725711566054802 171 | 0.30936567337981685 172 | 0.03399474211706161 173 | -0.35670603753389607 174 | -0.6253489004325172 175 | -0.8328994324561254 176 | -0.9175617656458047 177 | -1.0185195204044024 178 | -1.0002883956477455 179 | -0.7690419818031948 180 | -0.6007584986474267 181 | -0.34899705344800297 182 | 0.014320402657018111 183 | 0.3360059033732486 184 | 0.6168977858586726 185 | 0.7852551851220179 186 | 0.9357429043330806 187 | 0.9714653715376942 188 | 0.9832658060128125 189 | 0.794130424341467 190 | 0.5977046775430631 191 | 0.31685212006806596 192 | -0.008641901202369934 193 | -0.3470319434846572 194 | -0.5466238816133056 195 | -0.8536384363643723 196 | -0.9781647576190918 197 | -1.017582603693861 198 | -0.9659800035304382 199 | -0.8240881195975257 200 | -0.6074050393070357 201 | -0.2621423783783703 202 | 0.7671526550460838 203 | 0.867740625910434 204 | 1.0340780691940354 205 | 0.9679843665780018 206 | 0.9277404211685991 207 | 0.7134084162750242 208 | 0.6478297158463375 209 | 0.526322353043037 210 | 0.5575617463512468 211 | 0.6340411095202769 212 | 0.7181590846757563 213 | 0.9412220502268556 214 | 1.020888232628165 215 | 0.9872321726519944 216 | 0.9324773477208124 217 | 0.7710743911819099 218 | 0.5804031625560415 219 | 0.5029991581154097 220 | 0.5532119991781235 221 | 0.6474934007969121 222 | 0.74986324518556 223 | 0.8758101440858491 224 | 1.0353536547237523 225 | 0.9830225767015964 226 | 0.8514453829409145 227 | -0.018463801848179247 228 | 0.3542076091869846 229 | 0.6129415605398968 230 | 0.8125960942739088 231 | 0.9680279751786642 232 | 1.0365174997483286 233 | 0.9469449616855425 234 | 0.8168720846245294 235 | 0.5859382344774394 236 | 0.31407857057326577 237 | 0.04506232438081556 238 | -0.30803045232765175 239 | -0.5635337793262897 240 | -0.8099375929335118 241 | -0.9944413748080846 242 | -1.0237509337350101 243 | -0.9085097218873537 244 | -0.8218683292091253 245 | -0.5968433122893659 246 | -0.317441797401548 247 | -0.0402738400263463 248 | 0.3491797568229165 249 | 0.5382297182722862 250 | 0.7864094487100505 251 | 0.9119871824068332 252 | 1.0355448412892954 253 | 0.9267620519590563 254 | 0.8579302035781498 255 | 0.6304263945292861 256 | 0.27611159758378706 257 | -0.01961128751067439 258 | -0.30567184939683156 259 | -0.6201362909448258 260 | -0.7776579168972932 261 | -0.9305428039150275 262 | -1.02427924425786 263 | -0.9760196242486553 264 | -0.8255075576850198 265 | -0.5626611891299474 266 | -0.3585290161141693 267 | 0.03409932064362553 268 | 0.2819743532446127 269 | 0.539113807065231 270 | 0.8540107350918265 271 | 0.9909936627608549 272 | 1.0462624201143886 273 | 0.905356552490646 274 | 0.830283255591415 275 | 0.5428946621182964 276 | 0.29977020486080574 277 | -0.0024302626003822087 278 | -0.32427015576800755 279 | -0.633713258421363 280 | -0.7992603738605073 281 | -0.9750440489348438 282 | -0.9667144144422612 283 | -0.9050067657653321 284 | -0.765346237484293 285 | -0.6148532284477397 286 | -0.28698568435603145 287 | 0.02564823242687567 288 | 0.3040323868825432 289 | 0.571682991132016 290 | 0.8062661996000595 291 | 0.9996559522959704 292 | 0.9862331477128943 293 | 0.9260220170255168 294 | 0.7638038291929254 295 | 0.6167569738454733 296 | 0.3346380125151739 297 | 0.015746904556001532 298 | -0.33691710917137674 299 | -0.5720347146542905 300 | -0.8199327498598329 301 | -0.9091310662982552 302 | -1.0350314518310402 303 | -0.9490173140891359 304 | -0.7824581411915235 305 | -0.6129670062757127 306 | -0.31347259749824996 307 | 0.0199789142427406 308 | 0.26721394107141155 309 | 0.5890124555867629 310 | 0.7856551737126005 311 | 0.9632566797548867 312 | 0.959907017544019 313 | 0.9151140450638515 314 | 0.7749373508417339 315 | 0.6321867620518986 316 | 0.3221646523816837 317 | -0.025420804550392938 318 | -0.2622664697727883 319 | -0.5840871906954355 320 | -0.8251988848530648 321 | -0.9338897522289928 322 | -0.9662534075926554 323 | -0.9276710325748233 324 | -0.808794913523073 325 | -0.5954378743266854 326 | -0.3127130441383296 327 | 0.04530446527484906 328 | 0.3334242561815594 329 | 0.5833042114821931 330 | 0.7838586925727303 331 | 0.9730450725734872 332 | 0.9923702199453777 333 | 0.9753156020322821 334 | 0.8247018202457266 335 | 0.6060374098324707 336 | 0.3226960838336432 337 | -0.025141716446182603 338 | -0.32033085065150657 339 | -0.5607881570120141 340 | -0.8463805584481151 341 | -0.9723927397438714 342 | -0.9988660821828385 343 | -0.9812003627577196 344 | -0.7595563576877147 345 | -0.5741899239399393 346 | -0.28628509504389094 347 | -0.00876845569849172 348 | 0.31041935125411974 349 | 0.5509042438304658 350 | 0.8135772577600695 351 | 0.9855600179459438 352 | 0.9793284353803121 353 | 0.9788868980391526 354 | 0.773194871253649 355 | 0.6032232046332479 356 | 0.32215181783963254 357 | -0.010603475521839913 358 | -0.35887583997245565 359 | -0.5811985908482209 360 | -0.7702377922110796 361 | -0.9558082328359783 362 | -0.990382134147687 363 | -0.9367729825814859 364 | -0.7760331540386426 365 | -0.5567472363850408 366 | -0.3524235111884259 367 | -0.04185252949254716 368 | 0.29256879659476526 369 | 0.5868487260271898 370 | 0.8209199621396149 371 | 0.9638223898916096 372 | 0.9824644108228286 373 | 0.9803779684688573 374 | 0.7674356829250691 375 | 0.5632178242975898 376 | 0.3277738134309904 377 | -0.007978263432443778 378 | -0.3568038811585704 379 | -0.6160855730437276 380 | -0.8511924208812964 381 | -0.9446720828812145 382 | -1.027142480733027 383 | -0.9642991596335408 384 | -0.8318125066497807 385 | -0.5477552756578856 386 | -0.2928339889581473 387 | -0.005487009100799601 388 | 0.35589145095658836 389 | 0.6045465862880259 390 | 0.7954570006839575 391 | 0.901719212849826 392 | 0.9706175284270224 393 | 0.9793020217214453 394 | 0.8356601213554062 395 | 0.6186094144917453 396 | 0.2878079429680395 397 | 0.010994971780476454 398 | -0.3137966015510032 399 | -0.6151342932162742 400 | -0.8201113552094168 401 | -0.9763700561814327 402 | -1.049247155081491 403 | -0.9857089454329965 404 | -0.8585680642257962 405 | -0.5468648975577606 406 | -0.33523290243841686 407 | -0.017273693146959097 408 | 0.3383326560276237 409 | 0.5808144302596852 410 | 0.803026466463951 411 | 0.9110174638408762 412 | 1.0284020676567298 413 | 0.9532644256630265 414 | 0.7692357829306108 415 | 0.6226139548665834 416 | 0.2754028384601261 417 | 0.006540421265437664 418 | -0.34689724874382244 419 | -0.6102498026754133 420 | -0.7591836864449258 421 | -0.908126531974794 422 | -1.0396711316699152 423 | -0.9799694066209607 424 | -0.8275579804511963 425 | -0.5905423372232297 426 | -0.34469030399475326 427 | -0.007888370711515809 428 | 0.11287237825136333 429 | -0.05693073725327355 430 | -0.08252125584276714 431 | -0.09494364175741773 432 | -0.025851206616728517 433 | -0.06277901186192104 434 | -0.07721143725469216 435 | 0.02175258136057842 436 | -0.04296394202195818 437 | -0.10223383089006631 438 | -0.07589304777315256 439 | -0.019696231642339822 440 | 0.08506224463420092 441 | -0.07261706350969112 442 | -0.09197975650474395 443 | 0.10135723322458672 444 | -0.07388884511147382 445 | 0.09146874839961694 446 | 0.071855424030679 447 | -0.015009429853623885 448 | -0.1578105668112625 449 | -0.09475470397169594 450 | 0.025001545131021957 451 | 0.10362150973460844 452 | -0.14148398785927227 453 | 0.016540946850849357 454 | -0.10176450005745144 455 | 0.09591106737712277 456 | -0.07181383935821861 457 | -0.13003553599659148 458 | 0.00706027437954878 459 | -0.017816081085432756 460 | -0.10656243577573944 461 | -0.05659082182227672 462 | -0.03817181746207342 463 | -0.058820448764098796 464 | -0.03702844636516331 465 | 0.05495741618055828 466 | 0.006055149959405766 467 | 0.13728674848245173 468 | -0.06660179843522954 469 | 0.0934344706381996 470 | -0.10951993919353703 471 | 0.09532139646597211 472 | -0.16806342932930346 473 | -0.0833059744648336 474 | 0.028142640540112195 475 | -0.0994445024900962 476 | -0.06274247828093724 477 | 0.17140222706602584 478 | 0.04310095361297417 479 | 0.11060799558885107 480 | 0.06608733742564077 481 | -0.05095475714040172 482 | -0.06253709677145608 483 | 0.14602312829031217 484 | 0.006636450201603687 485 | 0.10307106365834892 486 | 0.17174650965415852 487 | -0.16642679131192065 488 | -0.001730985977247107 489 | 0.15787053427029815 490 | 0.14102229306323677 491 | -0.055140125856387084 492 | 0.027482898402195532 493 | 0.04508903615582546 494 | 0.06425112226761194 495 | 0.07396753941647924 496 | 0.06199649897848222 497 | 0.04334991254059097 498 | -0.05170131764871183 499 | -0.09583956622555069 500 | 0.12137652575839598 501 | 0.13054914375275106 502 | -0.08444708187761386 503 | 0.11238717916389024 504 | 0.029015700923020513 505 | -0.08126264606735037 506 | 0.06241296017360042 507 | 0.054370537350141836 508 | -0.010847978664558586 509 | -0.12237039690982962 510 | 0.03701088767014695 511 | -0.07141324027415105 512 | -0.11603059726225283 513 | 0.0693146702399775 514 | -0.10846999521090835 515 | -0.061048430475357775 516 | -0.0550461665516337 517 | -0.06446912507471884 518 | 0.05032575375254287 519 | 0.0269379607904489 520 | 0.03289594959522359 521 | 0.06598430807536103 522 | 0.1358537854230438 523 | -0.14314197655467512 524 | 0.06145950291593861 525 | -0.019202726298444748 526 | -0.0042757641419169995 527 | 0.7854467105033583 528 | 0.9092593718845945 529 | 1.0072407628876279 530 | 0.9709831498957027 531 | 0.9161242858257466 532 | 0.7783743053021985 533 | 0.6319369262905925 534 | 0.5406949456437247 535 | 0.5474374864366578 536 | 0.5840026441047677 537 | 0.7027118954252389 538 | 0.8522226443363002 539 | 0.9555787311528374 540 | 0.9759734352182879 541 | 0.9243192292428726 542 | 0.7291793169543692 543 | 0.5903626478009439 544 | 0.5052315889269835 545 | 0.4838523410921063 546 | 0.5855618226188668 547 | 0.7501267527184017 548 | 0.8882362709469711 549 | 0.9665716518338079 550 | 1.0150450925373808 551 | 0.8711293246131051 552 | -0.03161875573835909 553 | 0.10154029567009609 554 | -0.10227927096999352 555 | -0.08028831595167671 556 | -0.04042496593182217 557 | -0.04151078078390403 558 | -0.061005486370080006 559 | -0.046753489928196904 560 | 0.05355853120212359 561 | -0.10931318744930976 562 | -0.13212305043677872 563 | -0.026196878072425857 564 | 0.012637127002804597 565 | 0.08467977470344745 566 | -0.018834877102156374 567 | -0.10819546055746734 568 | 0.09696485811065579 569 | -0.05332045575027434 570 | 0.07466160822695346 571 | 0.023814315285000075 572 | 0.018391173494860347 573 | -0.11762943412429559 574 | -0.09149218352899575 575 | 0.06823529956261401 576 | 0.1009896474457179 577 | -0.13465611374512082 578 | 0.05843733022295751 579 | -0.16113705697997752 580 | 0.017966619028245208 581 | -0.08828113729866802 582 | -0.09819645231687318 583 | -0.021397689620105208 584 | 0.05765523241208711 585 | -0.03442591939188099 586 | 0.005457167491178874 587 | -0.016351878173810466 588 | -0.06257881031607977 589 | -0.08995521176741865 590 | 0.09812995431939163 591 | -0.07102557657319665 592 | 0.07138918748296541 593 | -0.05832360214608245 594 | 0.15935935314582828 595 | -0.12420720495108412 596 | 0.17913583723347065 597 | -0.09935759008263506 598 | -0.04993351249654666 599 | 0.09411869134353432 600 | -0.06973150533094732 601 | -0.07712131284945539 602 | 0.0877337703905414 603 | 0.12031445886876138 604 | 0.09535846097361296 605 | 0.061491044685432275 606 | -0.09725657917715516 607 | -0.06580967330147668 608 | 0.08713655614282849 609 | 0.028872946090962064 610 | 0.15949891801532515 611 | 0.11831694952745135 612 | -0.09556133266195233 613 | -0.039136660644217176 614 | 0.09834194908490285 615 | 0.09660096844268798 616 | -0.019818412466997185 617 | 0.08825280323227232 618 | 0.06466866280777062 619 | -0.012289974066796144 620 | -0.0039050579882750416 621 | 0.019558386643973434 622 | 0.1244493225102745 623 | 0.0006705692401834523 624 | -0.11212273416055929 625 | 0.149864514966049 626 | 0.16409848983517772 627 | -0.029934556643791257 628 | 0.06322173699527787 629 | 0.02588057186920506 630 | -0.131915068475971 631 | 0.054934754301515475 632 | 0.04097263815208818 633 | -0.025236639096225665 634 | -0.054304321219679236 635 | -0.004481439183689418 636 | -0.06444623136805523 637 | -0.10978722228117183 638 | 0.0526605525699215 639 | -0.13219763380792707 640 | -0.044669980850928284 641 | 0.006488085654300122 642 | -0.011479175171805129 643 | 0.051950872234314105 644 | -0.03296843193582394 645 | 0.07848536739878759 646 | 0.0910368337388966 647 | 0.11478754439286994 648 | -0.11555564882570028 649 | 0.030753668339173992 650 | 0.017274904406073856 651 | 0.0007342868323461428 652 | 0.7174950706024954 653 | 0.9344917406578497 654 | 1.0048866737698032 655 | 0.9389251456803618 656 | 0.9230287696576204 657 | 0.740245872342546 658 | 0.5620869401953946 659 | 0.5024014544445986 660 | 0.47215300274854194 661 | 0.559766522407096 662 | 0.7187993625667801 663 | 0.893359285754124 664 | 0.9536111658366515 665 | 0.9660006837311863 666 | 0.8567126270314229 667 | 0.7926082191269462 668 | 0.6476582552881048 669 | 0.5232878878060832 670 | 0.4771382944106303 671 | 0.5553592424853785 672 | 0.7739291348873052 673 | 0.8542694652483942 674 | 1.027754609701046 675 | 0.9909669499472838 676 | 0.9277208973776913 677 | 0.026649556189938063 678 | 0.09867676443556421 679 | -0.11065415537812381 680 | -0.05000831677833724 681 | -0.015463798567587096 682 | 0.02060730235437247 683 | -0.08843486999338636 684 | -0.06734032439407714 685 | 0.016558972205430712 686 | -0.05692619248951518 687 | -0.10717803378464101 688 | -0.051409658805340416 689 | 0.027794911433661196 690 | 0.06593755886744662 691 | -0.08307801416928792 692 | -0.050202955285361936 693 | 0.10839802821056073 694 | -0.0750664297909512 695 | 0.07572199819698677 696 | 0.03001880842021743 697 | 0.02975914206080356 698 | -0.13309869625651 699 | -0.06009941784041426 700 | 0.0289356350455934 701 | 0.16670233806194856 702 | -0.1259042304266615 703 | 0.006800394215163867 704 | -0.08776160657203841 705 | 0.0384774035255454 706 | -0.03597635800905464 707 | -0.11100874466836481 708 | -0.025123388873983292 709 | -0.03598617034952947 710 | -0.11505426988703285 711 | -0.026020258075970536 712 | 0.013123936535610614 713 | -0.04889911158760987 714 | -0.06767907073523613 715 | 0.12265985955763602 716 | -0.07585420435443745 717 | 0.1183268078633329 718 | -0.08400975669879479 719 | 0.16308131986653643 720 | -0.09298058677364784 721 | 0.16681680048103054 722 | -0.11798502057773613 723 | -0.12598377502865152 724 | 0.058241360801199495 725 | -0.058905873168557055 726 | -0.07970014907586193 727 | 0.08398598053435224 728 | 0.08735920066206404 729 | 0.0816213338792202 730 | 0.10981176134119172 731 | -0.13780854449108676 732 | -0.0012468778348702463 733 | 0.08622770062722483 734 | 0.09368246995606483 735 | 0.18431575923629223 736 | 0.13955742649367295 737 | -0.10965153602200646 738 | -0.050461665198787774 739 | 0.10039399920633447 740 | 0.15094735007864823 741 | -0.04384556652522858 742 | 0.015887981406202463 743 | 0.03744900304125659 744 | 0.0465542725243462 745 | 0.06769645254250575 746 | 0.025230136460835727 747 | 0.052048936019307906 748 | 0.007625430870721227 749 | -0.11012825694518759 750 | 0.18716509841341908 751 | 0.15436763089413447 752 | -0.03453676818735799 753 | 0.04684279981692176 754 | 0.020735311248130595 755 | -0.0992487509925908 756 | 0.0734001929913437 757 | 0.019709926629224026 758 | -0.0612608205225081 759 | -0.03716096984353669 760 | 0.050230774825050534 761 | -0.06254490981566249 762 | -0.06798611920666606 763 | 0.08470069652274947 764 | -0.14964739119779608 765 | -0.054695985362861285 766 | -0.05550594235287376 767 | 0.02140862195072929 768 | 0.06852053184272189 769 | 0.04067829133517505 770 | 0.04604082361757479 771 | 0.047430042355174826 772 | 0.05739509837210592 773 | -0.18378112468226826 774 | 0.06803764204000429 775 | -0.056784979959146324 776 | 0.01130633690967402 777 | -0.4795603295167729 778 | -0.43657011932505463 779 | -0.3644648870192759 780 | -0.27013700101791516 781 | -0.179319479896551 782 | -0.1672995990326534 783 | -0.07007892887082552 784 | -0.05610659109886941 785 | 0.023085312543560464 786 | 0.1508602314462213 787 | 0.24053087962236228 788 | 0.284572422309976 789 | 0.37617738279850177 790 | 0.4420460417561523 791 | 0.4719097788285822 792 | -0.4118679246147953 793 | -0.34950031030272977 794 | -0.2913045384072341 795 | -0.2210971005577409 796 | -0.12818269124299042 797 | -0.14944165228210277 798 | -0.06136272946243235 799 | 0.07772190065290369 800 | 0.1574317613936445 801 | 0.18347442949063586 802 | 0.2748551918244141 803 | 0.32611077949797423 804 | 0.3415953690587671 805 | 0.42345081806691487 806 | -0.5061878858588633 807 | -0.4411607122970504 808 | -0.29792376849297253 809 | -0.21189838777602996 810 | -0.21649379173744515 811 | -0.1684097286165706 812 | -0.0519651604167376 813 | -0.010996585760019575 814 | 0.13078133244477838 815 | 0.1339553458738243 816 | 0.18139544299088728 817 | 0.28832159342580704 818 | 0.32260389275928936 819 | 0.4406672203677412 820 | -0.49559751012786113 821 | -0.44558593857177037 822 | -0.321724433661775 823 | -0.2671880018887138 824 | -0.16474259727810447 825 | -0.14883306865981938 826 | -0.021788405437735095 827 | -0.025582304492794288 828 | 0.11995424112844798 829 | 0.10402082695463927 830 | 0.20668609675810032 831 | 0.2816737939257502 832 | 0.3105744004985666 833 | 0.4243321982938547 834 | 0.4966570949246811 835 | -0.48788849861893885 836 | -0.3793019948820744 837 | -0.25498575591954087 838 | -0.1870961976474077 839 | -0.13025165071998498 840 | -0.09143178423396611 841 | -0.040951962305245254 842 | 0.03285772786058176 843 | 0.1401226805248037 844 | 0.14986088994182498 845 | 0.22223162372715619 846 | 0.3281311138441195 847 | 0.3600887345889013 848 | 0.5126744091860278 849 | -0.47533377194746557 850 | -0.40594540634948273 851 | -0.35285658391613917 852 | -0.23793131237103274 853 | -0.17806556020428965 854 | -0.12901178162698168 855 | -0.05001356132371597 856 | -0.003283180142911752 857 | 0.07180101484692407 858 | 0.21295246017496636 859 | 0.2540563483089249 860 | 0.3101779428848797 861 | 0.39027224873260363 862 | 0.41806163197991614 863 | -0.45135691697757285 864 | -0.40917850822796664 865 | -0.3294286043614043 866 | -0.2498083189903666 867 | -0.1775900881756558 868 | -0.1629298780906927 869 | -0.09821846760026859 870 | -0.036607997721088685 871 | 0.04688909909134364 872 | 0.16491926078828917 873 | 0.22830194636568457 874 | 0.2535212745010135 875 | 0.34083720070467627 876 | 0.47908832564397225 877 | -------------------------------------------------------------------------------- /matrixprofile/__init__.py: -------------------------------------------------------------------------------- 1 | name = "matrixprofile" 2 | __all__ = ['utils', 'order', 'distanceProfile', 'matrixProfile', 'fluss', 'regimes', 'motifs', 'annotation_vector'] 3 | -------------------------------------------------------------------------------- /matrixprofile/annotation_vector.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | from __future__ import unicode_literals 6 | 7 | range = getattr(__builtins__, 'xrange', range) 8 | # end of py2 compatability boilerplate 9 | 10 | import numpy as np 11 | from .utils import movmeanstd 12 | 13 | 14 | def make_complexity_AV(ts, m): 15 | """ 16 | returns a complexity annotation vector for timeseries ts with window m. 17 | The complexity of a window is the average absolute difference between consecutive data points. 18 | """ 19 | diffs = np.diff(ts, append=0)**2 20 | diff_mean, diff_std = movmeanstd(diffs, m) 21 | 22 | complexity = np.sqrt(diff_mean) 23 | complexity = complexity - complexity.min() 24 | complexity = complexity / complexity.max() 25 | return complexity 26 | 27 | 28 | def make_meanstd_AV(ts, m): 29 | """ returns boolean annotation vector which selects windows with a standard deviation greater than average """ 30 | _, std = movmeanstd(ts, m) 31 | mu = std.mean() 32 | return (std < mu).astype(int) 33 | 34 | 35 | def make_clipping_AV(ts, m): 36 | """ 37 | returns an annotation vector proportional to the number if mins/maxs in the window 38 | """ 39 | av = (ts == ts.min()) | (ts == ts.max()) 40 | av, _ = movmeanstd(av, m) 41 | return av 42 | -------------------------------------------------------------------------------- /matrixprofile/discords.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | from __future__ import unicode_literals 6 | 7 | range = getattr(__builtins__, 'xrange', range) 8 | # end of py2 compatability boilerplate 9 | 10 | import sys 11 | import numpy as np 12 | 13 | def discords(mp,ex_zone,k=3): 14 | """ 15 | Computes the top k discords from a matrix profile 16 | 17 | Parameters 18 | ---------- 19 | mp: matrix profile numpy array 20 | k: the number of discords to discover 21 | ex_zone: the number of samples to exclude and set to Inf on either side of a found discord 22 | 23 | Returns a list of indexes represent the discord starting locations. MaxInt indicates there 24 | were no more discords that could be found due to too many exclusions or profile being too 25 | small. Discord start indices are sorted by highest matrix profile value. 26 | """ 27 | k = len(mp) if k > len(mp) else k 28 | 29 | mp_current = np.copy(mp) 30 | d = np.zeros(k, dtype='int') 31 | for i in range(k): 32 | maxVal = 0 33 | maxIdx = sys.maxsize 34 | for j, val in enumerate(mp_current): 35 | if not np.isinf(val) and val > maxVal: 36 | maxVal = val 37 | maxIdx = j 38 | 39 | d[i] = maxIdx 40 | mp_current[max([maxIdx-ex_zone, 0]):min([maxIdx+ex_zone, len(mp_current)])] = np.inf 41 | 42 | return d 43 | -------------------------------------------------------------------------------- /matrixprofile/distanceProfile.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | from __future__ import unicode_literals 6 | 7 | range = getattr(__builtins__, 'xrange', range) 8 | # end of py2 compatability boilerplate 9 | 10 | from .utils import * 11 | import numpy as np 12 | 13 | def naiveDistanceProfile(tsA,idx,m,tsB = None): 14 | """ 15 | Returns the distance profile of a query within tsA against the time series tsB using the naive all-pairs comparison. 16 | 17 | Parameters 18 | ---------- 19 | tsA: Time series containing the query for which to calculate the distance profile. 20 | idx: Starting location of the query within tsA 21 | m: Length of query. 22 | tsB: Time series to compare the query against. Note that, if no value is provided, tsB = tsA by default. 23 | """ 24 | 25 | selfJoin = False 26 | if tsB is None: 27 | selfJoin = True 28 | tsB = tsA 29 | 30 | query = tsA[idx: (idx+m)] 31 | distanceProfile = [] 32 | n = len(tsB) 33 | 34 | for i in range(n-m+1): 35 | distanceProfile.append(zNormalizeEuclidian(query,tsB[i:i+m])) 36 | 37 | dp = np.array(distanceProfile) 38 | 39 | if selfJoin: 40 | trivialMatchRange = (int(max(0,idx - np.round(m/2,0))),int(min(idx + np.round(m/2+1,0),n))) 41 | 42 | dp[trivialMatchRange[0]: trivialMatchRange[1]] = np.inf 43 | 44 | return (dp,np.full(n-m+1,idx,dtype=float)) 45 | 46 | 47 | def massDistanceProfile(tsA,idx,m,tsB = None): 48 | """ 49 | Returns the distance profile of a query within tsA against the time series tsB using the more efficient MASS comparison. 50 | 51 | Parameters 52 | ---------- 53 | tsA: Time series containing the query for which to calculate the distance profile. 54 | idx: Starting location of the query within tsA 55 | m: Length of query. 56 | tsB: Time series to compare the query against. Note that, if no value is provided, tsB = tsA by default. 57 | """ 58 | 59 | selfJoin = False 60 | if tsB is None: 61 | selfJoin = True 62 | tsB = tsA 63 | 64 | query = tsA[idx:(idx+m)] 65 | n = len(tsB) 66 | distanceProfile = np.real(np.sqrt(mass(query,tsB).astype(complex))) 67 | if selfJoin: 68 | trivialMatchRange = (int(max(0,idx - np.round(m/2,0))),int(min(idx + np.round(m/2+1,0),n))) 69 | distanceProfile[trivialMatchRange[0]:trivialMatchRange[1]] = np.inf 70 | 71 | #Both the distance profile and corresponding matrix profile index (which should just have the current index) 72 | return (distanceProfile,np.full(n-m+1,idx,dtype=float)) 73 | 74 | def mass_distance_profile_parallel(indices, tsA=None, tsB=None, m=None): 75 | """ 76 | Computes distance profiles for the given indices either via self join or similarity search. 77 | 78 | Parameters 79 | ---------- 80 | indices: Array of indices to compute distance profile for. 81 | tsA: Time series containing the query for which to calculate the distance profile. 82 | tsB: Time series to compare the query against. Note that, for the time being, only tsB = tsA is allowed 83 | m: Length of query. 84 | """ 85 | distance_profiles = [] 86 | 87 | for index in indices: 88 | distance_profiles.append(massDistanceProfile(tsA, index, m, tsB=tsB)) 89 | 90 | return distance_profiles 91 | 92 | 93 | def STOMPDistanceProfile(tsA,idx,m,tsB,dot_first,dp,mean,std): 94 | """ 95 | Returns the distance profile of a query within tsA against the time series tsB using the even more efficient iterative STOMP calculation. Note that the method requires a pre-calculated 'initial' sliding dot product. 96 | 97 | Parameters 98 | ---------- 99 | tsA: Time series containing the query for which to calculate the distance profile. 100 | idx: Starting location of the query within tsA 101 | m: Length of query. 102 | tsB: Time series to compare the query against. Note that, for the time being, only tsB = tsA is allowed 103 | dot_first: The 'initial' sliding dot product, or QT(1,1) in Zhu et.al 104 | dp: The dot product between tsA and the query starting at index m-1 105 | mean: Array containing the mean of every subsequence of length m in tsA (moving window) 106 | std: Array containing the mean of every subsequence of length m in tsA (moving window) 107 | """ 108 | 109 | selfJoin = is_self_join(tsA, tsB) 110 | if selfJoin: 111 | tsB = tsA 112 | 113 | query = tsA[idx:(idx+m)] 114 | n = len(tsB) 115 | 116 | #Calculate the first distance profile via MASS 117 | if idx == 0: 118 | distanceProfile = np.real(np.sqrt(mass(query,tsB).astype(complex))) 119 | 120 | #Currently re-calculating the dot product separately as opposed to updating all of the mass function... 121 | dot = slidingDotProduct(query,tsB) 122 | 123 | #Calculate all subsequent distance profiles using the STOMP dot product shortcut 124 | else: 125 | res, dot = massStomp(query,tsB,dot_first,dp,idx,mean,std) 126 | distanceProfile = np.real(np.sqrt(res.astype(complex))) 127 | 128 | 129 | if selfJoin: 130 | trivialMatchRange = (int(max(0,idx - np.round(m/2,0))),int(min(idx + np.round(m/2+1,0),n))) 131 | distanceProfile[trivialMatchRange[0]:trivialMatchRange[1]] = np.inf 132 | 133 | #Both the distance profile and corresponding matrix profile index (which should just have the current index) 134 | return (distanceProfile,np.full(n-m+1,idx,dtype=float)), dot 135 | 136 | if __name__ == "__main__": 137 | import doctest 138 | doctest.method() 139 | -------------------------------------------------------------------------------- /matrixprofile/fluss.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | from __future__ import unicode_literals 6 | 7 | range = getattr(__builtins__, 'xrange', range) 8 | # end of py2 compatability boilerplate 9 | 10 | import numpy as np 11 | 12 | def _idealized_arc_curve(n, x): 13 | """ 14 | Returns the value at x for the parabola of width n and height n / 2. 15 | Formula taken from https://www.desmos.com/calculator/awtnrxh6rk. 16 | 17 | Parameters 18 | ---------- 19 | n: Length of the time series to calculate the parabola for. 20 | x: location to compute the parabola value at. 21 | """ 22 | height = n / 2 23 | width = n 24 | c = width / 2 25 | b = height 26 | a = height / (width / 2) ** 2 27 | y = -(a * (x - c) ** 2) + b 28 | return y 29 | 30 | 31 | def fluss(mpi, m=None): 32 | """ 33 | Returns the corrected arc curve (CAC) for the matrix profile index (MPI). 34 | The FLUSS algorithm provides Fast Low-cost Unipotent Semantic Segmantation. 35 | 36 | Parameters 37 | ---------- 38 | mpi: Matrix profile index accompanying a time series. 39 | m: Subsequence length that was used to compute the MPI. Note: leaving this empty omits the correction at the head 40 | and tail of the CAC. 41 | """ 42 | n = len(mpi) 43 | nnmark = np.zeros(n) 44 | 45 | # find the number of additional arcs starting to cross over each index 46 | for i in range(0, n): 47 | mpi_val = mpi[i] 48 | small = int(min(i, mpi_val)) 49 | large = int(max(i, mpi_val)) 50 | nnmark[small + 1] = nnmark[small + 1] + 1 51 | nnmark[large] = nnmark[large] - 1 52 | 53 | # cumulatively sum all crossing arcs at each index 54 | cross_count = np.cumsum(nnmark) 55 | 56 | # compute ideal arc curve for all indices 57 | idealized = np.apply_along_axis(lambda i: _idealized_arc_curve(n, i), 0, np.arange(0, n)) 58 | idealized = cross_count / idealized 59 | 60 | # correct the arc curve so that it is between 0 and 1 61 | idealized[idealized > 1] = 1 62 | corrected_arc_curve = idealized 63 | 64 | if m: 65 | corrected_arc_curve[:m] = 1 66 | corrected_arc_curve[-m:] = 1 67 | 68 | return corrected_arc_curve 69 | 70 | 71 | if __name__ == "__main__": 72 | import doctest 73 | doctest.method() 74 | -------------------------------------------------------------------------------- /matrixprofile/matrixProfile.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | from __future__ import unicode_literals 6 | 7 | range = getattr(__builtins__, 'xrange', range) 8 | # end of py2 compatability boilerplate 9 | 10 | from . import distanceProfile 11 | from . import order 12 | from .utils import mass, movmeanstd 13 | import numpy as np 14 | import multiprocessing 15 | from functools import partial 16 | import math 17 | 18 | from .scrimp import scrimp_plus_plus 19 | 20 | def is_array_like(a): 21 | """ 22 | Helper function to determine if a value is array like. 23 | Parameters 24 | ---------- 25 | a : obj 26 | Object to test. 27 | Returns 28 | ------- 29 | True or false respectively. 30 | """ 31 | return isinstance(a, tuple([list, tuple, np.ndarray])) 32 | 33 | def to_np_array(a): 34 | """ 35 | Helper function to convert tuple or list to np.ndarray. 36 | Parameters 37 | ---------- 38 | a : Tuple, list or np.ndarray 39 | The object to transform. 40 | Returns 41 | ------- 42 | The np.ndarray. 43 | Raises 44 | ------ 45 | ValueError 46 | If a is not a valid type. 47 | """ 48 | if not is_array_like(a): 49 | raise ValueError('Unable to convert to np.ndarray!') 50 | 51 | return np.array(a) 52 | 53 | def _clean_nan_inf(ts): 54 | """ 55 | Converts tuples & lists to Numpy arrays and replaces nan and inf values with zeros 56 | 57 | Parameters 58 | ---------- 59 | ts: Time series to clean 60 | """ 61 | 62 | #Convert time series to a Numpy array 63 | ts = to_np_array(ts) 64 | 65 | search = (np.isinf(ts) | np.isnan(ts)) 66 | ts[search] = 0 67 | 68 | return ts 69 | 70 | 71 | def _self_join_or_not_preprocess(tsA, tsB, m): 72 | """ 73 | Core method for determining if a self join is occuring and returns appropriate 74 | profile and index numpy arrays with correct dimensions as all np.nan values. 75 | 76 | Parameters 77 | ---------- 78 | tsA: Time series containing the queries for which to calculate the Matrix Profile. 79 | tsB: Time series to compare the query against. Note that, if no value is provided, ts_b = ts_a by default. 80 | m: Length of subsequence to compare. 81 | """ 82 | n = len(tsA) 83 | if tsB is not None: 84 | n = len(tsB) 85 | 86 | shape = n - m + 1 87 | 88 | return (np.full(shape, np.inf), np.full(shape, np.inf)) 89 | 90 | def _matrixProfile(tsA,m,orderClass,distanceProfileFunction,tsB=None): 91 | """ 92 | Core method for calculating the Matrix Profile 93 | 94 | Parameters 95 | ---------- 96 | tsA: Time series containing the queries for which to calculate the Matrix Profile. 97 | m: Length of subsequence to compare. 98 | orderClass: Method defining the order in which distance profiles are calculated. 99 | distanceProfileFunction: Method for calculating individual distance profiles. 100 | sampling: The percentage of all possible distance profiles to sample for the final Matrix Profile. 101 | """ 102 | 103 | order = orderClass(len(tsA)-m+1) 104 | mp, mpIndex = _self_join_or_not_preprocess(tsA, tsB, m) 105 | 106 | if not is_array_like(tsB): 107 | tsB = tsA 108 | 109 | tsA = _clean_nan_inf(tsA) 110 | tsB = _clean_nan_inf(tsB) 111 | 112 | idx=order.next() 113 | while idx != None: 114 | (distanceProfile,querySegmentsID) = distanceProfileFunction(tsA,idx,m,tsB) 115 | 116 | #Check which of the indices have found a new minimum 117 | idsToUpdate = distanceProfile < mp 118 | 119 | #Update the Matrix Profile Index to indicate that the current index is the minimum location for the aforementioned indices 120 | mpIndex[idsToUpdate] = querySegmentsID[idsToUpdate] 121 | 122 | #Update the matrix profile to include the new minimum values (where appropriate) 123 | mp = np.minimum(mp,distanceProfile) 124 | idx = order.next() 125 | 126 | return (mp,mpIndex) 127 | 128 | def _stamp_parallel(tsA, m, tsB=None, sampling=0.2, n_threads=-1, random_state=None): 129 | """ 130 | Computes distance profiles in parallel using all CPU cores by default. 131 | 132 | Parameters 133 | ---------- 134 | tsA: Time series containing the queries for which to calculate the Matrix Profile. 135 | m: Length of subsequence to compare. 136 | tsB: Time series to compare the query against. Note that, if no value is provided, tsB = tsA by default. 137 | sampling: The percentage of all possible distance profiles to sample for the final Matrix Profile. 0 to 1 138 | n_threads: Number of threads to use in parallel mode. Defaults to using all CPU cores. 139 | random_state: Set the random seed generator for reproducible results. 140 | """ 141 | if n_threads is -1: 142 | n_threads = multiprocessing.cpu_count() 143 | 144 | n = len(tsA) 145 | mp, mpIndex = _self_join_or_not_preprocess(tsA, tsB, m) 146 | 147 | if not is_array_like(tsB): 148 | tsB = tsA 149 | 150 | tsA = _clean_nan_inf(tsA) 151 | tsB = _clean_nan_inf(tsB) 152 | 153 | # determine sampling size 154 | sample_size = math.ceil((n - m + 1) * sampling) 155 | 156 | # generate indices to sample and split based on n_threads 157 | if random_state is not None: 158 | np.random.seed(random_state) 159 | 160 | indices = np.arange(n - m + 1) 161 | indices = np.random.choice(indices, size=sample_size, replace=False) 162 | indices = np.array_split(indices, n_threads) 163 | 164 | # create pool of workers and compute 165 | with multiprocessing.Pool(processes=n_threads) as pool: 166 | func = partial(distanceProfile.mass_distance_profile_parallel, tsA=tsA, tsB=tsB, m=m) 167 | results = pool.map(func, indices) 168 | 169 | # The overall matrix profile is the element-wise minimum of each sub-profile, and each element of the overall 170 | # matrix profile index is the time series position of the corresponding sub-profile. 171 | for result in results: 172 | for dp, querySegmentsID in result: 173 | #Check which of the indices have found a new minimum 174 | idsToUpdate = dp < mp 175 | 176 | #Update the Matrix Profile Index to indicate that the current index is the minimum location for the aforementioned indices 177 | mpIndex[idsToUpdate] = querySegmentsID[idsToUpdate] 178 | 179 | #Update the matrix profile to include the new minimum values (where appropriate) 180 | mp = np.minimum(mp, dp) 181 | 182 | return (mp, mpIndex) 183 | 184 | def _matrixProfile_sampling(tsA,m,orderClass,distanceProfileFunction,tsB=None,sampling=0.2,random_state=None): 185 | order = orderClass(len(tsA)-m+1, random_state=random_state) 186 | mp, mpIndex = _self_join_or_not_preprocess(tsA, tsB, m) 187 | 188 | if not is_array_like(tsB): 189 | tsB = tsA 190 | 191 | tsA = _clean_nan_inf(tsA) 192 | tsB = _clean_nan_inf(tsB) 193 | 194 | idx=order.next() 195 | 196 | #Define max numbers of iterations to sample 197 | iters = (len(tsA)-m+1)*sampling 198 | 199 | iter_val = 0 200 | 201 | while iter_val < iters: 202 | (distanceProfile,querySegmentsID) = distanceProfileFunction(tsA,idx,m,tsB) 203 | 204 | #Check which of the indices have found a new minimum 205 | idsToUpdate = distanceProfile < mp 206 | 207 | #Update the Matrix Profile Index to indicate that the current index is the minimum location for the aforementioned indices 208 | mpIndex[idsToUpdate] = querySegmentsID[idsToUpdate] 209 | 210 | #Update the matrix profile to include the new minimum values (where appropriate) 211 | mp = np.minimum(mp,distanceProfile) 212 | idx = order.next() 213 | 214 | iter_val += 1 215 | return (mp,mpIndex) 216 | 217 | 218 | #Write matrix profile function for STOMP and then consolidate later! (aka link to the previous distance profile) 219 | def _matrixProfile_stomp(tsA,m,orderClass,distanceProfileFunction,tsB=None): 220 | order = orderClass(len(tsA)-m+1) 221 | mp, mpIndex = _self_join_or_not_preprocess(tsA, tsB, m) 222 | 223 | if not is_array_like(tsB): 224 | tsB = tsA 225 | 226 | tsA = _clean_nan_inf(tsA) 227 | tsB = _clean_nan_inf(tsB) 228 | 229 | idx=order.next() 230 | 231 | #Get moving mean and standard deviation 232 | mean, std = movmeanstd(tsA,m) 233 | 234 | #Initialize code to set dot_prev to None for the first pass 235 | dp = None 236 | 237 | #Initialize dot_first to None for the first pass 238 | dot_first = None 239 | 240 | while idx != None: 241 | 242 | #Need to pass in the previous sliding dot product for subsequent distance profile calculations 243 | (distanceProfile,querySegmentsID),dot_prev = distanceProfileFunction(tsA,idx,m,tsB,dot_first,dp,mean,std) 244 | 245 | if idx == 0: 246 | dot_first = dot_prev 247 | 248 | #Check which of the indices have found a new minimum 249 | idsToUpdate = distanceProfile < mp 250 | 251 | #Update the Matrix Profile Index to indicate that the current index is the minimum location for the aforementioned indices 252 | mpIndex[idsToUpdate] = querySegmentsID[idsToUpdate] 253 | 254 | #Update the matrix profile to include the new minimum values (where appropriate) 255 | mp = np.minimum(mp,distanceProfile) 256 | idx = order.next() 257 | 258 | dp = dot_prev 259 | return (mp,mpIndex) 260 | 261 | def stampi_update(tsA,m,mp,mpIndex,newval,tsB=None,distanceProfileFunction=distanceProfile.massDistanceProfile): 262 | '''Updates the self-matched matrix profile for a time series TsA with the arrival of a new data point newval. Note that comparison of two separate time-series with new data arriving will be built later -> currently, tsB should be set to tsA''' 263 | 264 | #Update time-series array with recent value 265 | tsA_new = np.append(np.copy(tsA),newval) 266 | 267 | #Expand matrix profile and matrix profile index to include space for latest point 268 | mp_new= np.append(np.copy(mp),np.inf) 269 | mpIndex_new = np.append(np.copy(mpIndex),np.inf) 270 | 271 | #Determine new index value 272 | idx = len(tsA_new)-m 273 | 274 | (distanceProfile,querySegmentsID) = distanceProfileFunction(tsA_new,idx,m,tsB) 275 | 276 | #Check which of the indices have found a new minimum 277 | idsToUpdate = distanceProfile < mp_new 278 | 279 | #Update the Matrix Profile Index to indicate that the current index is the minimum location for the aforementioned indices 280 | mpIndex_new[idsToUpdate] = querySegmentsID[idsToUpdate] 281 | 282 | #Update the matrix profile to include the new minimum values (where appropriate) 283 | mp_final = np.minimum(np.copy(mp_new),distanceProfile) 284 | 285 | #Finally, set the last value in the matrix profile to the minimum of the distance profile (with corresponding index) 286 | mp_final[-1] = np.min(distanceProfile) 287 | mpIndex_new[-1] = np.argmin(distanceProfile) 288 | 289 | return (mp_final,mpIndex_new) 290 | 291 | 292 | def naiveMP(tsA,m,tsB=None): 293 | """ 294 | Calculate the Matrix Profile using the naive all-pairs calculation. 295 | 296 | Parameters 297 | ---------- 298 | tsA: Time series containing the queries for which to calculate the Matrix Profile. 299 | m: Length of subsequence to compare. 300 | tsB: Time series to compare the query against. Note that, if no value is provided, tsB = tsA by default. 301 | """ 302 | return _matrixProfile(tsA,m,order.linearOrder,distanceProfile.naiveDistanceProfile,tsB) 303 | 304 | def stmp(tsA,m,tsB=None): 305 | """ 306 | Calculate the Matrix Profile using the more efficient MASS calculation. Distance profiles are computed linearly across every time series index. 307 | 308 | Parameters 309 | ---------- 310 | tsA: Time series containing the queries for which to calculate the Matrix Profile. 311 | m: Length of subsequence to compare. 312 | tsB: Time series to compare the query against. Note that, if no value is provided, tsB = tsA by default. 313 | """ 314 | return _matrixProfile(tsA,m,order.linearOrder,distanceProfile.massDistanceProfile,tsB) 315 | 316 | def stamp(tsA,m,tsB=None,sampling=0.2, n_threads=None, random_state=None): 317 | """ 318 | Calculate the Matrix Profile using the more efficient MASS calculation. Distance profiles are computed in a random order. 319 | 320 | Parameters 321 | ---------- 322 | tsA: Time series containing the queries for which to calculate the Matrix Profile. 323 | m: Length of subsequence to compare. 324 | tsB: Time series to compare the query against. Note that, if no value is provided, tsB = tsA by default. 325 | sampling: The percentage of all possible distance profiles to sample for the final Matrix Profile. 0 to 1 326 | n_threads: Number of threads to use in parallel mode. Defaults to single threaded mode. Set to -1 to use all threads. 327 | random_state: Set the random seed generator for reproducible results. 328 | """ 329 | if sampling > 1 or sampling < 0: 330 | raise ValueError('Sampling value must be a percentage in decimal format from 0 to 1.') 331 | 332 | if n_threads is None: 333 | return _matrixProfile_sampling(tsA,m,order.randomOrder,distanceProfile.massDistanceProfile,tsB,sampling=sampling,random_state=random_state) 334 | 335 | return _stamp_parallel(tsA, m, tsB=tsB, sampling=sampling, n_threads=n_threads, random_state=random_state) 336 | 337 | def stomp(tsA,m,tsB=None): 338 | """ 339 | Calculate the Matrix Profile using the more efficient MASS calculation. Distance profiles are computed according to the directed STOMP procedure. 340 | 341 | Parameters 342 | ---------- 343 | tsA: Time series containing the queries for which to calculate the Matrix Profile. 344 | m: Length of subsequence to compare. 345 | tsB: Time series to compare the query against. Note that, if no value is provided, tsB = tsA by default. 346 | """ 347 | return _matrixProfile_stomp(tsA,m,order.linearOrder,distanceProfile.STOMPDistanceProfile,tsB) 348 | 349 | 350 | 351 | if __name__ == "__main__": 352 | import doctest 353 | doctest.method() 354 | -------------------------------------------------------------------------------- /matrixprofile/motifs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | from __future__ import unicode_literals 6 | 7 | range = getattr(__builtins__, 'xrange', range) 8 | # end of py2 compatability boilerplate 9 | 10 | from . import distanceProfile 11 | import numpy as np 12 | 13 | 14 | def motifs(ts, mp, max_motifs=3, radius=2, n_neighbors=None, ex_zone=None): 15 | """ 16 | Computes the top k motifs from a matrix profile 17 | 18 | Parameters 19 | ---------- 20 | ts: time series to used to calculate mp 21 | mp: tuple, (matrix profile numpy array, matrix profile indices) 22 | max_motifs: the maximum number of motifs to discover 23 | ex_zone: the number of samples to exclude and set to Inf on either side of a found motifs 24 | defaults to m/2 25 | 26 | Returns tuple (motifs, distances) 27 | motifs: a list of lists of indexes representing the motif starting locations. 28 | distances: list of minimum distances for each motif 29 | """ 30 | 31 | motifs = [] 32 | distances = [] 33 | try: 34 | mp_current, mp_idx = mp 35 | except TypeError: 36 | raise ValueError("argument mp must be a tuple") 37 | mp_current = np.copy(mp_current) 38 | 39 | if len(ts) <= 1 or len(mp_current) <= 1 or max_motifs == 0: 40 | return [], [] 41 | 42 | m = len(ts) - len(mp_current) + 1 43 | if m <= 1: 44 | raise ValueError('Matrix profile is longer than time series.') 45 | if ex_zone is None: 46 | ex_zone = m / 2 47 | 48 | for j in range(max_motifs): 49 | # find minimum distance and index location 50 | min_idx = mp_current.argmin() 51 | motif_distance = mp_current[min_idx] 52 | if motif_distance == np.inf: 53 | return motifs, distances 54 | if motif_distance == 0.0: 55 | motif_distance += np.finfo(mp_current.dtype).eps 56 | 57 | motif_set = set() 58 | initial_motif = [min_idx] 59 | pair_idx = int(mp[1][min_idx]) 60 | if mp_current[pair_idx] != np.inf: 61 | initial_motif += [pair_idx] 62 | 63 | motif_set = set(initial_motif) 64 | 65 | prof, _ = distanceProfile.massDistanceProfile(ts, initial_motif[0], m) 66 | 67 | # kill off any indices around the initial motif pair since they are 68 | # trivial solutions 69 | for idx in initial_motif: 70 | _applyExclusionZone(prof, idx, ex_zone) 71 | # exclude previous motifs 72 | for ms in motifs: 73 | for idx in ms: 74 | _applyExclusionZone(prof, idx, ex_zone) 75 | 76 | # keep looking for the closest index to the current motif. Each 77 | # index found will have an exclusion zone applied as to remove 78 | # trivial solutions. This eventually exits when there's nothing 79 | # found within the radius distance. 80 | prof_idx_sort = prof.argsort() 81 | 82 | for nn_idx in prof_idx_sort: 83 | if n_neighbors is not None and len(motif_set) >= n_neighbors: 84 | break 85 | if prof[nn_idx] == np.inf: 86 | continue 87 | if prof[nn_idx] < motif_distance * radius: 88 | motif_set.add(nn_idx) 89 | _applyExclusionZone(prof, nn_idx, ex_zone) 90 | else: 91 | break 92 | 93 | for motif in motif_set: 94 | _applyExclusionZone(mp_current, motif, ex_zone) 95 | 96 | if len(motif_set) < 2: 97 | continue 98 | motifs += [list(sorted(motif_set))] 99 | distances += [motif_distance] 100 | 101 | return motifs, distances 102 | 103 | 104 | def _applyExclusionZone(prof, idx, zone): 105 | start = int(max(0, idx - zone)) 106 | end = int(idx + zone + 1) 107 | prof[start:end] = np.inf 108 | -------------------------------------------------------------------------------- /matrixprofile/order.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | from __future__ import unicode_literals 6 | 7 | range = getattr(__builtins__, 'xrange', range) 8 | # end of py2 compatability boilerplate 9 | 10 | import numpy as np 11 | 12 | class Order: 13 | """ 14 | An object that defines the order in which the distance profiles are calculated for a given Matrix Profile 15 | """ 16 | def next(self): 17 | raise NotImplementedError("next() not implemented") 18 | 19 | class linearOrder(Order): 20 | """ 21 | An object that defines a linear (iterative) order in which the distance profiles are calculated for a given Matrix Profile 22 | """ 23 | def __init__(self,m): 24 | self.m = m 25 | self.idx = -1 26 | 27 | def next(self): 28 | """ 29 | Advances the Order object to the next index 30 | """ 31 | self.idx += 1 32 | if self.idx < self.m: 33 | return self.idx 34 | else: 35 | return None 36 | 37 | 38 | class randomOrder(Order): 39 | """ 40 | An object that defines a random order in which the distance profiles are calculated for a given Matrix Profile 41 | """ 42 | def __init__(self,m, random_state=None): 43 | self.idx = -1 44 | self.indices = np.arange(m) 45 | self.random_state = random_state 46 | 47 | if self.random_state is not None: 48 | np.random.seed(self.random_state) 49 | 50 | np.random.shuffle(self.indices) 51 | 52 | def next(self): 53 | """ 54 | Advances the Order object to the next index 55 | """ 56 | self.idx += 1 57 | try: 58 | return self.indices[self.idx] 59 | 60 | except IndexError: 61 | return None 62 | -------------------------------------------------------------------------------- /matrixprofile/regimes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | from __future__ import unicode_literals 6 | 7 | range = getattr(__builtins__, 'xrange', range) 8 | # end of py2 compatability boilerplate 9 | 10 | import numpy as np 11 | 12 | def extract_regimes(cac, window_size, num=3): 13 | """ 14 | Given a corrected arc curve extract a given number of regimes. The regimes 15 | are computed with an exclusion zone to avoid very close matches. By default 16 | it sets the exclusion zone to 5 times the window size per the author. 17 | 18 | Per the authors: 19 | 20 | This exclusion zone is based on an assumption that regimes will have 21 | multiple repetitions; FLUSS is not able to segment single gesture 22 | patterns. 23 | 24 | Parameters 25 | ---------- 26 | cac: Array like data structure representing the corrected arc curve. 27 | window_size: The window size used to compute the matrix profile. 28 | num: The number of regimes to return. 29 | 30 | Returns 31 | ------- 32 | Array of index locations for the starting point of the regimes. 33 | """ 34 | ez = window_size * 5 35 | found = [] 36 | tmp = np.copy(cac) 37 | n = len(tmp) 38 | 39 | for _ in range(num): 40 | min_index = np.argmin(tmp) 41 | found.append(min_index) 42 | 43 | # apply exclusion zone 44 | ez_start = np.max([0, min_index - ez]) 45 | ez_end = np.min([n, min_index + ez]) 46 | tmp[ez_start:ez_end] = np.inf 47 | 48 | return np.array(found, dtype=int) -------------------------------------------------------------------------------- /matrixprofile/scrimp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | This module consists of all code to implement the SCRIMP++ algorithm. SCRIMP++ 5 | is an anytime algorithm that computes the matrix profile for a given time 6 | series (ts) over a given window size (m). 7 | 8 | This algorithm was originally created at the University of California 9 | Riverside. For further academic understanding, please review this paper: 10 | 11 | Matrix Profile XI: SCRIMP++: Time Series Motif Discovery at Interactive 12 | Speed. Yan Zhu, Chin-Chia Michael Yeh, Zachary Zimmerman, Kaveh Kamgar 13 | Eamonn Keogh, ICDM 2018. 14 | 15 | https://www.cs.ucr.edu/~eamonn/SCRIMP_ICDM_camera_ready_updated.pdf 16 | """ 17 | 18 | from __future__ import absolute_import 19 | from __future__ import division 20 | from __future__ import print_function 21 | from __future__ import unicode_literals 22 | 23 | range = getattr(__builtins__, 'xrange', range) 24 | # end of py2 compatability boilerplate 25 | 26 | import math 27 | import time 28 | 29 | import warnings 30 | 31 | import numpy as np 32 | 33 | 34 | def fast_find_nn_pre(ts, m): 35 | n = len(ts) 36 | X = np.fft.fft(ts) 37 | cum_sumx = np.cumsum(ts) 38 | cum_sumx2 = np.cumsum(np.power(ts, 2)) 39 | sumx = cum_sumx[m-1:n] - np.insert(cum_sumx[0:n-m], 0, 0) 40 | sumx2 = cum_sumx2[m-1:n] - np.insert(cum_sumx2[0:n-m], 0, 0) 41 | meanx = sumx / m 42 | sigmax2 = (sumx2 / m) - np.power(meanx, 2) 43 | sigmax = np.sqrt(sigmax2) 44 | 45 | return (X, n, sumx2, sumx, meanx, sigmax2, sigmax) 46 | 47 | 48 | def calc_distance_profile(X, y, n, m, meanx, sigmax): 49 | # reverse the query 50 | y = np.flip(y, 0) 51 | 52 | # make y same size as ts with zero fill 53 | y = np.concatenate([y, np.zeros(n-m)]) 54 | 55 | # main trick of getting dot product in O(n log n) time 56 | Y = np.fft.fft(y) 57 | Z = X * Y 58 | z = np.fft.ifft(Z) 59 | 60 | # compute y stats in O(n) 61 | sumy = np.sum(y) 62 | sumy2 = np.sum(np.power(y, 2)) 63 | meany = sumy / m 64 | sigmay2 = sumy2 / m - meany ** 2 65 | sigmay = np.sqrt(sigmay2) 66 | 67 | dist = (z[m - 1:n] - m * meanx * meany) / (sigmax * sigmay) 68 | dist = m - dist 69 | dist = np.real(2 * dist) 70 | 71 | return np.sqrt(np.absolute(dist)) 72 | 73 | 74 | def calc_exclusion_zone(window_size): 75 | return window_size / 4 76 | 77 | 78 | def calc_step_size(window_size, step_size): 79 | return int(math.floor(window_size * step_size)) 80 | 81 | 82 | def calc_profile_len(n, window_size): 83 | return n - window_size + 1 84 | 85 | 86 | def next_subsequence(ts, idx, m): 87 | return ts[idx:idx + m] 88 | 89 | 90 | def calc_exclusion_start(idx, exclusion_zone): 91 | return int(np.max([0, idx - exclusion_zone])) 92 | 93 | 94 | def calc_exclusion_stop(idx, exclusion_zone, profile_len): 95 | return int(np.min([profile_len, idx + exclusion_zone])) 96 | 97 | 98 | def apply_exclusion_zone(idx, exclusion_zone, profile_len, distance_profile): 99 | exc_start = calc_exclusion_start(idx, exclusion_zone) 100 | exc_stop = calc_exclusion_stop(idx, exclusion_zone, profile_len) 101 | distance_profile[exc_start:exc_stop + 1] = np.inf 102 | 103 | return distance_profile 104 | 105 | 106 | def find_and_store_nn(iteration, idx, matrix_profile, mp_index, 107 | distance_profile): 108 | if iteration == 0: 109 | matrix_profile = distance_profile 110 | mp_index[:] = idx 111 | else: 112 | update_pos = distance_profile < matrix_profile 113 | mp_index[update_pos] = idx 114 | matrix_profile[update_pos] = distance_profile[update_pos] 115 | 116 | idx_min = np.argmin(distance_profile) 117 | matrix_profile[idx] = distance_profile[idx_min] 118 | mp_index[idx] = idx_min 119 | idx_nn = mp_index[idx] 120 | 121 | return (matrix_profile, mp_index, idx_nn) 122 | 123 | 124 | def calc_idx_diff(idx, idx_nn): 125 | return idx_nn - idx 126 | 127 | 128 | def calc_dotproduct_idx(dotproduct, m, mp, idx, sigmax, idx_nn, meanx): 129 | dotproduct[idx] = (m - mp[idx] ** 2 / 2) * \ 130 | sigmax[idx] * sigmax[idx_nn] + m * meanx[idx] * meanx[idx_nn] 131 | 132 | return dotproduct 133 | 134 | 135 | def calc_end_idx(profile_len, idx, step_size, idx_diff): 136 | return np.min([profile_len - 1, idx + step_size - 1, 137 | profile_len - idx_diff - 1]) 138 | 139 | 140 | def calc_dotproduct_end_idx(ts, dp, idx, m, endidx, idx_nn, idx_diff): 141 | tmp_a = ts[idx+m:endidx+m] 142 | tmp_b = ts[idx_nn+m:endidx+m+idx_diff] 143 | tmp_c = ts[idx:endidx] 144 | tmp_d = ts[idx_nn:endidx+idx_diff] 145 | tmp_f = tmp_a * tmp_b - tmp_c * tmp_d 146 | 147 | dp[idx+1:endidx+1] = dp[idx] + np.cumsum(tmp_f) 148 | 149 | return dp 150 | 151 | 152 | def calc_refine_distance_end_idx(refine_distance, dp, idx, endidx, meanx, 153 | sigmax, idx_nn, idx_diff, m): 154 | tmp_a = dp[idx+1:endidx+1] 155 | tmp_b = meanx[idx+1:endidx+1] 156 | tmp_c = meanx[idx_nn+1:endidx+idx_diff+1] 157 | tmp_d = sigmax[idx+1:endidx+1] 158 | tmp_e = sigmax[idx_nn+1:endidx+idx_diff+1] 159 | tmp_f = tmp_b * tmp_c 160 | tmp_g = tmp_d * tmp_e 161 | tmp_h = (m-(tmp_a - m * tmp_f) / (tmp_g)) 162 | refine_distance[idx+1:endidx+1] = np.sqrt(np.abs(2 * tmp_h)) 163 | 164 | return refine_distance 165 | 166 | 167 | def calc_begin_idx(idx, step_size, idx_diff): 168 | return np.max([0, idx - step_size + 1, 2 - idx_diff]) 169 | 170 | 171 | def calc_dotproduct_begin_idx(ts, dp, beginidx, idx, idx_diff, m, 172 | idx_nn): 173 | indices = list(range(idx - 1, beginidx - 1, -1)) 174 | 175 | if not indices: 176 | return dp 177 | 178 | tmp_a = ts[indices] 179 | indices_b = list(range(idx_nn - 1, beginidx + idx_diff - 1, -1)) 180 | tmp_b = ts[indices_b] 181 | indices_c = list(range(idx + m - 1, beginidx + m - 1, -1)) 182 | tmp_c = ts[indices_c] 183 | indices_d = list(range(idx_nn - 1 + m, beginidx + idx_diff + m - 1, -1)) 184 | tmp_d = ts[indices_d] 185 | 186 | dp[indices] = dp[idx] + \ 187 | np.cumsum((tmp_a * tmp_b) - (tmp_c * tmp_d)) 188 | 189 | return dp 190 | 191 | 192 | def calc_refine_distance_begin_idx(refine_distance, dp, beginidx, idx, 193 | idx_diff, idx_nn, sigmax, meanx, m): 194 | if not (beginidx < idx): 195 | return refine_distance 196 | 197 | tmp_a = dp[beginidx:idx] 198 | tmp_b = meanx[beginidx:idx] 199 | tmp_c = meanx[beginidx+idx_diff:idx_nn] 200 | tmp_d = sigmax[beginidx:idx] 201 | tmp_e = sigmax[beginidx+idx_diff:idx_nn] 202 | tmp_f = tmp_b * tmp_c 203 | tmp_g = tmp_d * tmp_e 204 | tmp_h = (m-(tmp_a - m * tmp_f) / (tmp_g)) 205 | 206 | refine_distance[beginidx:idx] = np.sqrt(np.abs(2 * tmp_h)) 207 | 208 | return refine_distance 209 | 210 | 211 | def apply_update_positions(matrix_profile, mp_index, refine_distance, beginidx, 212 | endidx, orig_index, idx_diff): 213 | tmp_a = refine_distance[beginidx:endidx+1] 214 | tmp_b = matrix_profile[beginidx:endidx+1] 215 | update_pos1 = np.argwhere(tmp_a < tmp_b).flatten() 216 | 217 | if len(update_pos1) > 0: 218 | update_pos1 = update_pos1 + beginidx 219 | matrix_profile[update_pos1] = refine_distance[update_pos1] 220 | mp_index[update_pos1] = orig_index[update_pos1] + idx_diff 221 | 222 | tmp_a = refine_distance[beginidx:endidx + 1] 223 | tmp_b = matrix_profile[beginidx + idx_diff:endidx + idx_diff + 1] 224 | update_pos2 = np.argwhere(tmp_a < tmp_b).flatten() 225 | 226 | if len(update_pos2) > 0: 227 | update_pos2 = update_pos2 + beginidx 228 | matrix_profile[update_pos2 + idx_diff] = refine_distance[update_pos2] 229 | mp_index[update_pos2 + idx_diff] = orig_index[update_pos2] 230 | 231 | return (matrix_profile, mp_index) 232 | 233 | 234 | def calc_curlastz(ts, m, n, idx, profile_len, curlastz): 235 | curlastz[idx] = np.sum(ts[0:m] * ts[idx:idx+m]) 236 | 237 | tmp_a = ts[m:n - idx] 238 | tmp_b = ts[idx + m:n] 239 | tmp_c = ts[0:profile_len - idx - 1] 240 | tmp_d = ts[idx:profile_len - 1] 241 | tmp_e = tmp_a * tmp_b 242 | tmp_f = tmp_c * tmp_d 243 | curlastz[idx+1:profile_len] = curlastz[idx] + np.cumsum(tmp_e - tmp_f) 244 | 245 | return curlastz 246 | 247 | 248 | def calc_curdistance(curlastz, meanx, sigmax, idx, profile_len, m, 249 | curdistance): 250 | tmp_a = curlastz[idx:profile_len+1] 251 | tmp_b = meanx[idx:profile_len] 252 | tmp_c = meanx[0:profile_len-idx] 253 | tmp_d = sigmax[idx:profile_len] 254 | tmp_e = sigmax[0:profile_len-idx] 255 | tmp_f = tmp_b * tmp_c 256 | tmp_g = (m-(tmp_a - m * tmp_f) / (tmp_d * tmp_e)) 257 | curdistance[idx:profile_len] = np.sqrt(np.abs(2 * tmp_g)) 258 | 259 | return curdistance 260 | 261 | 262 | def time_is_exceeded(start_time, runtime): 263 | """Helper method to determine if the runtime has exceeded or not. 264 | 265 | Returns 266 | ------- 267 | bool 268 | Whether or not hte runtime has exceeded. 269 | """ 270 | elapsed = time.time() - start_time 271 | exceeded = runtime is not None and elapsed >= runtime 272 | if exceeded: 273 | warnings.warn( 274 | 'Max runtime exceeded. Approximate solution is given.', 275 | RuntimeWarning 276 | ) 277 | 278 | return exceeded 279 | 280 | 281 | def scrimp_plus_plus(ts, m, step_size=0.25, runtime=None, random_state=None): 282 | """SCRIMP++ is an anytime algorithm that computes the matrix profile for a 283 | given time series (ts) over a given window size (m). Essentially, it allows 284 | for an approximate solution to be provided for quicker analysis. In the 285 | case of this implementation, the runtime is measured based on the wall 286 | clock. If the number of seconds exceeds the runtime, then the approximate 287 | solution is returned. If the runtime is None, the exact solution is 288 | returned. 289 | 290 | This algorithm was created at the University of California Riverside. For 291 | further academic understanding, please review this paper: 292 | 293 | Matrix Profile XI: SCRIMP++: Time Series Motif Discovery at Interactive 294 | Speed. Yan Zhu, Chin-Chia Michael Yeh, Zachary Zimmerman, Kaveh Kamgar 295 | Eamonn Keogh, ICDM 2018. 296 | 297 | https://www.cs.ucr.edu/~eamonn/SCRIMP_ICDM_camera_ready_updated.pdf 298 | 299 | Parameters 300 | ---------- 301 | ts : np.ndarray 302 | The time series to compute the matrix profile for. 303 | m : int 304 | The window size. 305 | step_size : float, default 0.25 306 | The sampling interval for the window. The paper suggest 0.25 is the 307 | most practical. It should be a float value between 0 and 1. 308 | runtime : int, default None 309 | The maximum number of seconds based on wall clock time for this 310 | algorithm to run. It computes the exact solution when it is set to 311 | None. 312 | random_state : int, default None 313 | Set the random seed generator for reproducible results. 314 | 315 | Returns 316 | ------- 317 | (np.array, np.array) 318 | The matrix profile and the matrix profile index respectively. 319 | """ 320 | # start the timer here 321 | start_time = time.time() 322 | 323 | # validate step_size 324 | if not isinstance(step_size, float) or step_size > 1 or step_size < 0: 325 | raise ValueError('step_size should be a float between 0 and 1.') 326 | 327 | # validate runtime 328 | if runtime is not None and (not isinstance(runtime, int) or runtime < 1): 329 | raise ValueError('runtime should be a valid positive integer.') 330 | 331 | # validate random_state 332 | if random_state is not None: 333 | try: 334 | np.random.seed(random_state) 335 | except: 336 | raise ValueError('Invalid random_state value given.') 337 | 338 | ts_len = len(ts) 339 | 340 | # set the trivial match range 341 | exclusion_zone = calc_exclusion_zone(m) 342 | 343 | # value checking 344 | if m > ts_len / 2: 345 | raise ValueError('Time series is too short relative to desired \ 346 | subsequence length') 347 | 348 | if m < 4: 349 | raise ValueError('Window size must be at least 4') 350 | 351 | # initialization 352 | step_size = calc_step_size(m, step_size) 353 | profile_len = calc_profile_len(ts_len, m) 354 | 355 | matrix_profile = np.zeros(profile_len) 356 | mp_index = np.zeros(profile_len, dtype='int32') 357 | 358 | X, n, sumx2, sumx, meanx, sigmax2, sigmax = fast_find_nn_pre(ts, m) 359 | 360 | ########################### 361 | # PreSCRIMP 362 | # 363 | # compute distance profile 364 | dotproduct = np.zeros(profile_len) 365 | refine_distance = np.full(profile_len, np.inf) 366 | orig_index = np.arange(profile_len) 367 | 368 | compute_order = list(range(0, profile_len, step_size)) 369 | np.random.shuffle(compute_order) 370 | 371 | for iteration, idx in enumerate(compute_order): 372 | # compute distance profile 373 | subsequence = next_subsequence(ts, idx, m) 374 | 375 | distance_profile = calc_distance_profile(X, subsequence, n, m, meanx, 376 | sigmax) 377 | 378 | # apply exclusion zone 379 | distance_profile = apply_exclusion_zone( 380 | idx, exclusion_zone, profile_len, distance_profile) 381 | 382 | # find and store nearest neighbor 383 | matrix_profile, mp_index, idx_nn = find_and_store_nn( 384 | iteration, idx, matrix_profile, mp_index, distance_profile) 385 | 386 | idx_diff = calc_idx_diff(idx, idx_nn) 387 | dotproduct = calc_dotproduct_idx(dotproduct, m, matrix_profile, idx, 388 | sigmax, idx_nn, meanx) 389 | 390 | endidx = calc_end_idx(profile_len, idx, step_size, idx_diff) 391 | 392 | dotproduct = calc_dotproduct_end_idx(ts, dotproduct, idx, m, 393 | endidx, idx_nn, idx_diff) 394 | 395 | refine_distance = calc_refine_distance_end_idx( 396 | refine_distance, dotproduct, idx, endidx, meanx, sigmax, idx_nn, 397 | idx_diff, m) 398 | 399 | beginidx = calc_begin_idx(idx, step_size, idx_diff) 400 | 401 | dotproduct = calc_dotproduct_begin_idx( 402 | ts, dotproduct, beginidx, idx, idx_diff, m, idx_nn) 403 | 404 | refine_distance = calc_refine_distance_begin_idx( 405 | refine_distance, dotproduct, beginidx, idx, idx_diff, idx_nn, 406 | sigmax, meanx, m) 407 | 408 | matrix_profile, mp_index = apply_update_positions(matrix_profile, 409 | mp_index, 410 | refine_distance, 411 | beginidx, 412 | endidx, 413 | orig_index, idx_diff) 414 | 415 | # check if time is up 416 | if time_is_exceeded(start_time, runtime): 417 | break 418 | 419 | if not time_is_exceeded(start_time, runtime): 420 | ########################### 421 | # SCRIMP 422 | # 423 | compute_order = orig_index[orig_index > exclusion_zone] 424 | np.random.shuffle(compute_order) 425 | 426 | curlastz = np.zeros(profile_len) 427 | curdistance = np.zeros(profile_len) 428 | dist1 = np.full(profile_len, np.inf) 429 | dist2 = np.full(profile_len, np.inf) 430 | 431 | for idx in compute_order: 432 | curlastz = calc_curlastz(ts, m, n, idx, profile_len, curlastz) 433 | curdistance = calc_curdistance(curlastz, meanx, sigmax, idx, 434 | profile_len, m, curdistance) 435 | 436 | dist1[0:idx-1] = np.inf 437 | dist1[idx:profile_len] = curdistance[idx:profile_len] 438 | 439 | dist2[0:profile_len - idx] = curdistance[idx:profile_len] 440 | dist2[profile_len - idx + 2:profile_len] = np.inf 441 | 442 | loc1 = dist1 < matrix_profile 443 | if loc1.any(): 444 | matrix_profile[loc1] = dist1[loc1] 445 | mp_index[loc1] = orig_index[loc1] - idx + 1 446 | 447 | loc2 = dist2 < matrix_profile 448 | if loc2.any(): 449 | matrix_profile[loc2] = dist2[loc2] 450 | mp_index[loc2] = orig_index[loc2] + idx - 1 451 | 452 | # check if time is up 453 | if time_is_exceeded(start_time, runtime): 454 | break 455 | 456 | return (matrix_profile, mp_index) -------------------------------------------------------------------------------- /matrixprofile/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | from __future__ import division 4 | from __future__ import print_function 5 | from __future__ import unicode_literals 6 | 7 | range = getattr(__builtins__, 'xrange', range) 8 | # end of py2 compatability boilerplate 9 | 10 | import numpy as np 11 | import numpy.fft as fft 12 | 13 | def zNormalize(ts): 14 | """ 15 | Returns a z-normalized version of a time series. 16 | 17 | Parameters 18 | ---------- 19 | ts: Time series to be normalized 20 | """ 21 | 22 | ts -= np.mean(ts) 23 | std = np.std(ts) 24 | 25 | if std == 0: 26 | raise ValueError("The Standard Deviation cannot be zero") 27 | else: 28 | ts /= std 29 | 30 | return ts 31 | 32 | def zNormalizeEuclidian(tsA,tsB): 33 | """ 34 | Returns the z-normalized Euclidian distance between two time series. 35 | 36 | Parameters 37 | ---------- 38 | tsA: Time series #1 39 | tsB: Time series #2 40 | """ 41 | 42 | if len(tsA) != len(tsB): 43 | raise ValueError("tsA and tsB must be the same length") 44 | 45 | return np.linalg.norm(zNormalize(tsA.astype("float64")) - zNormalize(tsB.astype("float64"))) 46 | 47 | def movmeanstd(ts,m): 48 | """ 49 | Calculate the mean and standard deviation within a moving window passing across a time series. 50 | 51 | Parameters 52 | ---------- 53 | ts: Time series to evaluate. 54 | m: Width of the moving window. 55 | """ 56 | if m <= 1: 57 | raise ValueError("Query length must be longer than one") 58 | 59 | ts = ts.astype("float") 60 | #Add zero to the beginning of the cumsum of ts 61 | s = np.insert(np.cumsum(ts),0,0) 62 | #Add zero to the beginning of the cumsum of ts ** 2 63 | sSq = np.insert(np.cumsum(ts ** 2),0,0) 64 | segSum = s[m:] - s[:-m] 65 | segSumSq = sSq[m:] -sSq[:-m] 66 | 67 | movmean = segSum/m 68 | movstd = np.sqrt(segSumSq / m - (segSum/m) ** 2) 69 | 70 | return [movmean,movstd] 71 | 72 | def movstd(ts,m): 73 | """ 74 | Calculate the standard deviation within a moving window passing across a time series. 75 | 76 | Parameters 77 | ---------- 78 | ts: Time series to evaluate. 79 | m: Width of the moving window. 80 | """ 81 | if m <= 1: 82 | raise ValueError("Query length must be longer than one") 83 | 84 | ts = ts.astype("float") 85 | #Add zero to the beginning of the cumsum of ts 86 | s = np.insert(np.cumsum(ts),0,0) 87 | #Add zero to the beginning of the cumsum of ts ** 2 88 | sSq = np.insert(np.cumsum(ts ** 2),0,0) 89 | segSum = s[m:] - s[:-m] 90 | segSumSq = sSq[m:] -sSq[:-m] 91 | 92 | return np.sqrt(segSumSq / m - (segSum/m) ** 2) 93 | 94 | def slidingDotProduct(query,ts): 95 | """ 96 | Calculate the dot product between a query and all subsequences of length(query) in the timeseries ts. Note that we use Numpy's rfft method instead of fft. 97 | 98 | Parameters 99 | ---------- 100 | query: Specific time series query to evaluate. 101 | ts: Time series to calculate the query's sliding dot product against. 102 | """ 103 | 104 | m = len(query) 105 | n = len(ts) 106 | 107 | 108 | #If length is odd, zero-pad time time series 109 | ts_add = 0 110 | if n%2 ==1: 111 | ts = np.insert(ts,0,0) 112 | ts_add = 1 113 | 114 | q_add = 0 115 | #If length is odd, zero-pad query 116 | if m%2 == 1: 117 | query = np.insert(query,0,0) 118 | q_add = 1 119 | 120 | #This reverses the array 121 | query = query[::-1] 122 | 123 | 124 | query = np.pad(query,(0,n-m+ts_add-q_add),'constant') 125 | 126 | #Determine trim length for dot product. Note that zero-padding of the query has no effect on array length, which is solely determined by the longest vector 127 | trim = m-1+ts_add 128 | 129 | dot_product = fft.irfft(fft.rfft(ts)*fft.rfft(query)) 130 | 131 | 132 | #Note that we only care about the dot product results from index m-1 onwards, as the first few values aren't true dot products (due to the way the FFT works for dot products) 133 | return dot_product[trim :] 134 | 135 | def DotProductStomp(ts,m,dot_first,dot_prev,order): 136 | """ 137 | Updates the sliding dot product for a time series ts from the previous dot product dot_prev. 138 | 139 | Parameters 140 | ---------- 141 | ts: Time series under analysis. 142 | m: Length of query within sliding dot product. 143 | dot_first: The dot product between ts and the beginning query (QT1,1 in Zhu et.al). 144 | dot_prev: The dot product between ts and the query starting at index-1. 145 | order: The location of the first point in the query. 146 | """ 147 | 148 | l = len(ts)-m+1 149 | dot = np.roll(dot_prev,1) 150 | 151 | dot += ts[order+m-1]*ts[m-1:l+m]-ts[order-1]*np.roll(ts[:l],1) 152 | 153 | #Update the first value in the dot product array 154 | dot[0] = dot_first[order] 155 | 156 | return dot 157 | 158 | 159 | def mass(query,ts): 160 | """ 161 | Calculates Mueen's ultra-fast Algorithm for Similarity Search (MASS): a Euclidian distance similarity search algorithm. Note that we are returning the square of MASS. 162 | 163 | Parameters 164 | ---------- 165 | :query: Time series snippet to evaluate. Note that the query does not have to be a subset of ts. 166 | :ts: Time series to compare against query. 167 | """ 168 | 169 | #query_normalized = zNormalize(np.copy(query)) 170 | m = len(query) 171 | q_mean = np.mean(query) 172 | q_std = np.std(query) 173 | mean, std = movmeanstd(ts,m) 174 | dot = slidingDotProduct(query,ts) 175 | 176 | #res = np.sqrt(2*m*(1-(dot-m*mean*q_mean)/(m*std*q_std))) 177 | res = 2*m*(1-(dot-m*mean*q_mean)/(m*std*q_std)) 178 | 179 | 180 | return res 181 | 182 | def massStomp(query,ts,dot_first,dot_prev,index,mean,std): 183 | """ 184 | Calculates Mueen's ultra-fast Algorithm for Similarity Search (MASS) between a query and timeseries using the STOMP dot product speedup. Note that we are returning the square of MASS. 185 | 186 | Parameters 187 | ---------- 188 | query: Time series snippet to evaluate. Note that, for STOMP, the query must be a subset of ts. 189 | ts: Time series to compare against query. 190 | dot_first: The dot product between ts and the beginning query (QT1,1 in Zhu et.al). 191 | dot_prev: The dot product between ts and the query starting at index-1. 192 | index: The location of the first point in the query. 193 | mean: Array containing the mean of every subsequence in ts. 194 | std: Array containing the standard deviation of every subsequence in ts. 195 | """ 196 | m = len(query) 197 | dot = DotProductStomp(ts,m,dot_first,dot_prev,index) 198 | 199 | #Return both the MASS calcuation and the dot product 200 | res = 2*m*(1-(dot-m*mean[index]*mean)/(m*std[index]*std)) 201 | 202 | return res, dot 203 | 204 | 205 | def apply_av(mp,av=[1.0]): 206 | """ 207 | Applies an annotation vector to a Matrix Profile. 208 | 209 | Parameters 210 | ---------- 211 | mp: Tuple containing the Matrix Profile and Matrix Profile Index. 212 | av: Numpy array containing the annotation vector. 213 | """ 214 | 215 | if len(mp[0]) != len(av): 216 | raise ValueError( 217 | "Annotation Vector must be the same length as the matrix profile") 218 | else: 219 | av_max = np.max(av) 220 | av_min = np.min(av) 221 | if av_max > 1 or av_min < 0: 222 | raise ValueError("Annotation Vector must be between 0 and 1") 223 | mp_corrected = mp[0] + (1 - np.array(av)) * np.max(mp[0]) 224 | return (mp_corrected, mp[1]) 225 | 226 | 227 | def is_self_join(tsA, tsB): 228 | """ 229 | Helper function to determine if a self join is occurring or not. When tsA 230 | is absolutely equal to tsB, a self join is occurring. 231 | 232 | Parameters 233 | ---------- 234 | tsA: Primary time series. 235 | tsB: Subquery time series. 236 | """ 237 | return tsB is None or np.array_equal(tsA, tsB) 238 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy==1.16.2 2 | pytest==3.5.1 3 | setuptools==39.1.0 4 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | license_file = LICENSE 3 | 4 | [bdist_wheel] 5 | universal = 1 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name="matrixprofile-ts", 8 | version="0.0.8", 9 | author="Andrew Van Benschoten", 10 | author_email="avbs89@gmail.com", 11 | description="An Open Source Python Time Series Library For Motif Discovery using Matrix Profile", 12 | long_description=long_description, 13 | long_description_content_type="text/markdown", 14 | url="https://github.com/target/matrixprofile-ts", 15 | packages = ['matrixprofile'], 16 | install_requires=['numpy>=1.11.3'], 17 | classifiers=[ 18 | "Programming Language :: Python :: 2", 19 | "Programming Language :: Python :: 3", 20 | "License :: OSI Approved :: Apache Software License", 21 | "Operating System :: OS Independent", 22 | ], 23 | ) 24 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/target/matrixprofile-ts/6a6f62b72d351f958118a127e81b20469f13b0e7/tests/__init__.py -------------------------------------------------------------------------------- /tests/mp.txt: -------------------------------------------------------------------------------- 1 | 0.31604 2 | 0.3062 3 | 0.30308 4 | 0.29103 5 | 0.28242 6 | 0.25885 7 | 0.26178 8 | 0.26299 9 | 0.26445 10 | 0.26976 11 | 0.26683 12 | 0.26496 13 | 0.25899 14 | 0.2446 15 | 0.22847 16 | 0.22437 17 | 0.22992 18 | 0.24117 19 | 0.25282 20 | 0.26205 21 | 0.27501 22 | 0.27407 23 | 0.25156 24 | 0.24707 25 | 0.22693 26 | 0.23479 27 | 0.2432 28 | 0.2606 29 | 0.25808 30 | 0.26123 31 | 0.26173 32 | 0.25654 33 | 0.24558 34 | 0.23783 35 | 0.2351 36 | 0.24068 37 | 0.25392 38 | 0.25052 39 | 0.25583 40 | 0.2549 41 | 0.25472 42 | 0.24075 43 | 0.23709 44 | 0.23448 45 | 0.23529 46 | 0.24191 47 | 0.245 48 | 0.25154 49 | 0.25539 50 | 0.24606 51 | 0.26755 52 | 0.2464 53 | 0.24023 54 | 0.2315 55 | 0.22415 56 | 0.23996 57 | 0.24344 58 | 0.25386 59 | 0.26955 60 | 0.28033 61 | 0.28508 62 | 0.27185 63 | 0.26481 64 | 0.24829 65 | 0.24286 66 | 0.22918 67 | 0.25004 68 | 0.25345 69 | 0.26197 70 | 0.28151 71 | 0.27259 72 | 0.26556 73 | 0.25024 74 | 0.25085 75 | 0.24249 76 | 0.25057 77 | 0.26022 78 | 0.26711 79 | 0.27755 80 | 0.27766 81 | 0.26697 82 | 0.25675 83 | 0.24756 84 | 0.24019 85 | 0.22753 86 | 0.22774 87 | 0.23044 88 | 0.23635 89 | 0.2472 90 | 0.26293 91 | 0.27872 92 | 0.26918 93 | 0.25351 94 | 0.2583 95 | 0.25707 96 | 0.2527 97 | 0.28275 98 | 0.28023 99 | 0.28479 100 | 0.28037 101 | 0.26038 102 | 0.25711 103 | 0.25298 104 | 0.24618 105 | 0.25279 106 | 0.25406 107 | 0.2532 108 | 0.27701 109 | 0.28501 110 | 0.2976 111 | 0.28652 112 | 0.27576 113 | 0.27382 114 | 0.26613 115 | 0.25211 116 | 0.25765 117 | 0.2626 118 | 0.27251 119 | 0.27468 120 | 0.28033 121 | 0.28508 122 | 0.27185 123 | 0.26481 124 | 0.2571 125 | 0.2561 126 | 0.25046 127 | 0.26572 128 | 0.29436 129 | 0.29881 130 | 0.30328 131 | 0.30528 132 | 0.31383 133 | 0.3045 134 | 0.28434 135 | 0.2707 136 | 0.27498 137 | 0.27813 138 | 0.28295 139 | 0.25951 140 | 0.2549 141 | 0.25472 142 | 0.24075 143 | 0.23709 144 | 0.23448 145 | 0.23529 146 | 0.24858 147 | 0.25724 148 | 0.26455 149 | 0.27346 150 | 0.27787 151 | 0.27968 152 | 0.2879 153 | 0.27839 154 | 0.27207 155 | 0.26589 156 | 0.26673 157 | 0.27765 158 | 0.28455 159 | 0.2911 160 | 0.27479 161 | 0.28315 162 | 0.28765 163 | 0.27923 164 | 0.26863 165 | 0.26661 166 | 0.2662 167 | 0.26683 168 | 0.26908 169 | 0.27791 170 | 1.0735 171 | 1.2519 172 | 1.3004 173 | 1.2619 174 | 1.2571 175 | 1.3548 176 | 1.4747 177 | 1.5839 178 | 1.6364 179 | 1.7245 180 | 2.0069 181 | 2.6304 182 | 3.3618 183 | 3.6642 184 | 3.719 185 | 3.6288 186 | 3.462 187 | 3.2606 188 | 3.1059 189 | 2.8264 190 | 2.4863 191 | 2.0686 192 | 1.752 193 | 1.7586 194 | 1.8037 195 | 2.1242 196 | 2.3976 197 | 2.9364 198 | 3.5663 199 | 4.4534 200 | 5.1813 201 | 5.9703 202 | 6.1385 203 | 6.0828 204 | 5.6218 205 | 5.3813 206 | 4.6153 207 | 3.9413 208 | 3.5873 209 | 3.2155 210 | 2.6749 211 | 2.5676 212 | 2.5102 213 | 2.445 214 | 2.5044 215 | 2.6201 216 | 2.8789 217 | 3.1272 218 | 3.5818 219 | 3.9757 220 | 3.9727 221 | 3.9998 222 | 4.03 223 | 3.5595 224 | 2.718 225 | 1.6753 226 | 0.29155 227 | 0.2845 228 | 0.27413 229 | 0.27042 230 | 0.26365 231 | 0.25937 232 | 0.24644 233 | 0.25197 234 | 0.26025 235 | 0.26537 236 | 0.28566 237 | 0.27994 238 | 0.28697 239 | 0.26953 240 | 0.26656 241 | 0.26622 242 | 0.27277 243 | 0.2671 244 | 0.27823 245 | 0.2823 246 | 0.28688 247 | 0.27873 248 | 0.26751 249 | 0.24829 250 | 0.24286 251 | 0.22918 252 | 0.25004 253 | 0.25345 254 | 0.26197 255 | 0.28151 256 | 0.27259 257 | 0.26556 258 | 0.25024 259 | 0.25085 260 | 0.24249 261 | 0.25057 262 | 0.26022 263 | 0.26711 264 | 0.27755 265 | 0.27766 266 | 0.26697 267 | 0.25675 268 | 0.24756 269 | 0.24019 270 | 0.22753 271 | 0.22774 272 | 0.23044 273 | 0.23635 274 | 0.2472 275 | 0.26171 276 | 0.268 277 | 0.26472 278 | 0.24795 279 | 0.23723 280 | 0.23914 281 | 0.23996 282 | 0.24531 283 | 0.25735 284 | 0.25899 285 | 0.26272 286 | 0.28315 287 | 0.28052 288 | 0.26759 289 | 0.26572 290 | 0.26226 291 | 0.26268 292 | 0.26592 293 | 0.26954 294 | 0.28205 295 | 0.28152 296 | 0.2701 297 | 0.26999 298 | 0.24715 299 | 0.23634 300 | 0.22415 301 | 0.24193 302 | 0.24344 303 | 0.25386 304 | 0.26689 305 | 0.27255 306 | 0.27047 307 | 0.26872 308 | 0.26588 309 | 0.26572 310 | 0.26226 311 | 0.26268 312 | 0.26178 313 | 0.26299 314 | 0.26445 315 | 0.26976 316 | 0.26683 317 | 0.2464 318 | 0.24023 319 | 0.2315 320 | 0.22483 321 | 0.22437 322 | 0.22992 323 | 0.24117 324 | 0.24757 325 | 0.25105 326 | 0.24726 327 | 0.23655 328 | 0.25156 329 | 0.24644 330 | 0.22693 331 | 0.23479 332 | 0.2432 333 | 0.2606 334 | 0.25808 335 | 0.26123 336 | 0.26173 337 | 0.25654 338 | 0.24558 339 | 0.23783 340 | 0.2351 341 | 0.24068 342 | 0.25019 343 | 0.24748 344 | 0.24757 345 | 0.25105 346 | 0.24726 347 | 0.23655 348 | 0.25549 349 | 0.24644 350 | 0.24109 351 | 0.24156 352 | 0.25417 353 | 0.26296 354 | 0.28087 355 | 0.27879 356 | 0.27953 357 | 0.28057 358 | 0.24923 359 | 0.25916 360 | 0.2576 361 | 0.26095 362 | 0.2662 363 | 0.274 364 | 0.28391 365 | 0.28037 366 | 0.26038 367 | 0.25711 368 | 0.25298 369 | 0.24618 370 | 0.24929 371 | 0.24929 372 | 0.2532 373 | 0.27258 374 | 0.2721 375 | 0.26257 376 | 0.30062 377 | 0.29446 378 | 0.27275 379 | 0.27366 380 | 0.2677 381 | 0.26091 382 | 0.27402 383 | 0.28009 384 | 0.27613 385 | 0.28946 386 | 0.28893 387 | 0.27888 388 | 0.27173 389 | 0.27019 390 | 0.26152 391 | 0.24191 392 | 0.245 393 | 0.25197 394 | 0.26025 395 | 0.24606 396 | 0.35759 397 | 0.9248 398 | 1.5071 399 | 2.0003 400 | 2.368 401 | 2.6762 402 | 2.9086 403 | 3.0525 404 | 3.1513 405 | 3.1962 406 | 3.2552 407 | 3.412 408 | 3.5058 409 | 3.6217 410 | 3.7169 411 | 3.6288 412 | 3.462 413 | 3.2606 414 | 3.0847 415 | 2.6083 416 | 2.3142 417 | 2.0821 418 | 1.9822 419 | 2.0272 420 | 2.2869 421 | 2.6276 422 | 2.9872 423 | 3.5663 424 | 4.6157 425 | 5.9107 426 | 2.404 427 | 2.406 428 | 2.4865 429 | 2.3677 430 | 2.428 431 | 2.2423 432 | 2.2363 433 | 2.2795 434 | 2.5757 435 | 2.451 436 | 2.4431 437 | 2.4797 438 | 2.4445 439 | 2.3909 440 | 2.378 441 | 2.4112 442 | 2.3846 443 | 2.4668 444 | 2.4812 445 | 2.6483 446 | 2.6014 447 | 2.5517 448 | 2.6044 449 | 2.7349 450 | 2.8064 451 | 2.8736 452 | 3.0721 453 | 3.0276 454 | 2.991 455 | 2.8854 456 | 2.9335 457 | 3.0061 458 | 2.9006 459 | 2.882 460 | 2.9582 461 | 2.9653 462 | 2.9357 463 | 2.9548 464 | 2.9949 465 | 2.9389 466 | 2.8719 467 | 2.8438 468 | 2.8616 469 | 2.7841 470 | 2.8578 471 | 2.898 472 | 3.0321 473 | 3.02 474 | 2.9997 475 | 3.079 476 | 3.1664 477 | 3.1189 478 | 3.0922 479 | 3.0419 480 | 2.9827 481 | 2.8425 482 | 2.7373 483 | 2.6692 484 | 2.4899 485 | 2.5532 486 | 2.6529 487 | 2.737 488 | 2.6605 489 | 2.6486 490 | 2.9264 491 | 2.7944 492 | 2.7686 493 | 2.8028 494 | 2.8138 495 | 1.4024 496 | 0.9934 497 | 0.74153 498 | 0.63479 499 | 0.58324 500 | 0.55608 501 | 0.55836 502 | 0.54048 503 | 0.54495 504 | 0.53968 505 | 0.52275 506 | 0.53058 507 | 0.51169 508 | 0.49582 509 | 0.48263 510 | 0.50618 511 | 0.53788 512 | 0.55492 513 | 0.56001 514 | 0.5753 515 | 0.5944 516 | 0.5897 517 | 0.61282 518 | 0.62017 519 | 0.6429 520 | 0.64458 521 | 0.64363 522 | 0.61999 523 | 0.62768 524 | 0.61497 525 | 0.62589 526 | 0.628 527 | 0.58112 528 | 0.57739 529 | 0.58091 530 | 0.56845 531 | 0.57106 532 | 0.56005 533 | 0.52998 534 | 0.53657 535 | 0.50668 536 | 0.49952 537 | 0.50242 538 | 0.50737 539 | 0.52406 540 | 0.54526 541 | 0.52055 542 | 0.52349 543 | 0.53295 544 | 0.56585 545 | 0.56601 546 | 0.59091 547 | 0.6544 548 | 0.70132 549 | 0.84478 550 | 1.1507 551 | 2.6125 552 | 2.7377 553 | 2.9837 554 | 3.0057 555 | 3.0316 556 | 2.9839 557 | 3.0615 558 | 2.9992 559 | 2.9749 560 | 2.9171 561 | 2.8781 562 | 2.7237 563 | 2.6792 564 | 2.5039 565 | 2.4892 566 | 2.5051 567 | 2.4374 568 | 2.4888 569 | 2.4724 570 | 2.463 571 | 2.4296 572 | 2.389 573 | 2.4694 574 | 2.4338 575 | 2.5122 576 | 2.4682 577 | 2.6187 578 | 2.4308 579 | 2.2916 580 | 2.2363 581 | 2.1546 582 | 2.1734 583 | 2.1837 584 | 1.9645 585 | 2.0079 586 | 2.0094 587 | 2.0752 588 | 2.2072 589 | 2.2322 590 | 2.3957 591 | 2.442 592 | 2.3378 593 | 2.3041 594 | 2.314 595 | 2.341 596 | 2.4245 597 | 2.5259 598 | 2.3924 599 | 2.3533 600 | 2.4198 601 | 2.4942 602 | 2.4785 603 | 2.5273 604 | 2.4843 605 | 2.444 606 | 2.5001 607 | 2.2869 608 | 2.2866 609 | 2.31 610 | 2.4663 611 | 2.5367 612 | 2.7612 613 | 2.8035 614 | 2.9011 615 | 2.9604 616 | 2.8984 617 | 2.7966 618 | 2.9272 619 | 2.8408 620 | 1.4024 621 | 0.9934 622 | 0.74153 623 | 0.63479 624 | 0.58324 625 | 0.55608 626 | 0.55836 627 | 0.54048 628 | 0.54495 629 | 0.53968 630 | 0.52275 631 | 0.53058 632 | 0.51169 633 | 0.49582 634 | 0.48263 635 | 0.50618 636 | 0.53788 637 | 0.55492 638 | 0.56001 639 | 0.5753 640 | 0.5944 641 | 0.5897 642 | 0.61282 643 | 0.62017 644 | 0.6429 645 | 0.64458 646 | 0.64363 647 | 0.61999 648 | 0.62768 649 | 0.61497 650 | 0.62589 651 | 0.628 652 | 0.58112 653 | 0.57739 654 | 0.58091 655 | 0.56845 656 | 0.57106 657 | 0.56005 658 | 0.52998 659 | 0.53657 660 | 0.50668 661 | 0.49952 662 | 0.50242 663 | 0.50737 664 | 0.52406 665 | 0.54526 666 | 0.52055 667 | 0.52349 668 | 0.53295 669 | 0.56585 670 | 0.56601 671 | 0.59091 672 | 0.6544 673 | 0.70132 674 | 0.84478 675 | 1.1507 676 | 2.404 677 | 2.406 678 | 2.4865 679 | 2.3677 680 | 2.428 681 | 2.2423 682 | 2.2363 683 | 2.2795 684 | 2.5757 685 | 2.451 686 | 2.4431 687 | 2.4797 688 | 2.4445 689 | 2.3909 690 | 2.378 691 | 2.4112 692 | 2.3846 693 | 2.4668 694 | 2.4724 695 | 2.463 696 | 2.4296 697 | 2.389 698 | 2.4694 699 | 2.4338 700 | 2.5122 701 | 2.4682 702 | 2.6187 703 | 2.4308 704 | 2.2916 705 | 2.2363 706 | 2.1546 707 | 2.1734 708 | 2.1837 709 | 1.9645 710 | 2.0079 711 | 2.0094 712 | 2.0752 713 | 2.2072 714 | 2.2322 715 | 2.3957 716 | 2.442 717 | 2.3378 718 | 2.3041 719 | 2.314 720 | 2.341 721 | 2.4245 722 | 2.5259 723 | 2.3924 724 | 2.3533 725 | 2.4198 726 | 2.4942 727 | 2.4785 728 | 2.5273 729 | 2.4843 730 | 2.444 731 | 2.5001 732 | 2.2869 733 | 2.2866 734 | 2.31 735 | 2.4663 736 | 2.5367 737 | 2.737 738 | 2.6605 739 | 2.6486 740 | 2.9264 741 | 2.7944 742 | 2.7686 743 | 2.8028 744 | 2.8138 745 | 5.1528 746 | 4.3669 747 | 3.9494 748 | 3.8026 749 | 3.7303 750 | 3.736 751 | 3.8808 752 | 3.8661 753 | 3.9307 754 | 4.0215 755 | 3.9851 756 | 3.9757 757 | 3.9727 758 | 3.9998 759 | 4.03 760 | 4.4465 761 | 4.2131 762 | 3.9045 763 | 3.3557 764 | 3.2526 765 | 3.1325 766 | 3.0702 767 | 2.9186 768 | 2.719 769 | 2.6701 770 | 2.6377 771 | 2.5885 772 | 2.5315 773 | 2.1584 774 | 1.9855 775 | 1.5644 776 | 0.78455 777 | 0.80386 778 | 0.8399 779 | 0.85502 780 | 0.86649 781 | 0.87099 782 | 0.88477 783 | 0.86957 784 | 0.88231 785 | 0.82405 786 | 0.80587 787 | 0.78094 788 | 0.82375 789 | 0.78011 790 | 0.78656 791 | 0.723 792 | 0.72794 793 | 0.74097 794 | 0.78132 795 | 0.77986 796 | 0.81324 797 | 0.77697 798 | 0.77069 799 | 0.75848 800 | 0.69656 801 | 0.6981 802 | 3.2717 803 | 3.217 804 | 3.232 805 | 3.3354 806 | 3.3557 807 | 3.2526 808 | 3.1325 809 | 3.0702 810 | 2.9186 811 | 2.719 812 | 2.6701 813 | 2.6377 814 | 2.5885 815 | 2.5315 816 | 2.1584 817 | 1.9855 818 | 1.5644 819 | 0.78455 820 | 0.723 821 | 0.72794 822 | 0.74097 823 | 0.78132 824 | 0.77986 825 | 0.81324 826 | 0.77697 827 | 0.77069 828 | 0.75848 829 | 0.69656 830 | 0.6981 831 | 0.82375 832 | 0.78011 833 | 0.78656 834 | 0.80386 835 | 0.79505 836 | 0.80194 837 | 0.84524 838 | 0.85109 839 | 0.84093 840 | 0.82222 841 | 0.81322 842 | 0.77774 843 | 0.73618 844 | 0.76575 845 | -------------------------------------------------------------------------------- /tests/sampledata.txt: -------------------------------------------------------------------------------- 1 | 0.010397810228292748 2 | 0.2999788221634467 3 | 0.5407523804199618 4 | 0.759207383826371 5 | 0.9013408204126397 6 | 1.0415821314612956 7 | 0.9600399347956455 8 | 0.8149562392820489 9 | 0.6193257693858093 10 | 0.3468181702401875 11 | -0.004155752142434815 12 | -0.2990004348426142 13 | -0.6351587372315037 14 | -0.7744337156501432 15 | -0.9760871961788041 16 | -0.9858215709200417 17 | -0.9763098555114907 18 | -0.8416514099026343 19 | -0.5785228769712287 20 | -0.2775775392782455 21 | 0.019383813651720705 22 | 0.26204924915824784 23 | 0.5917062628815675 24 | 0.8565844758736795 25 | 0.976132821943113 26 | 0.9794006312795015 27 | 0.9763726440688296 28 | 0.7741133988729083 29 | 0.573361978833397 30 | 0.3422100796719294 31 | -0.026816995806231946 32 | -0.2962335338749448 33 | -0.5879458221648752 34 | -0.8500333854489095 35 | -0.9985371203156639 36 | -1.0107783816845974 37 | -0.9421182076550738 38 | -0.7660558308300447 39 | -0.580576572148165 40 | -0.30015935986059966 41 | -0.008823731165498868 42 | 0.31427503335637136 43 | 0.5869459919056349 44 | 0.8548123857286984 45 | 0.9807773703862337 46 | 0.9607381112820752 47 | 0.9793600136347534 48 | 0.7983420942978364 49 | 0.5508266369098543 50 | 0.27802027100886884 51 | 0.02398257810158575 52 | -0.29361285345182053 53 | -0.6279468733938993 54 | -0.8069789658037253 55 | -0.99108354992316 56 | -1.03481565979181 57 | -0.9934374900647783 58 | -0.8274961858429344 59 | -0.6218201601459842 60 | -0.3452365882129938 61 | -0.01773893171322098 62 | 0.31292444607889636 63 | 0.5948704150270221 64 | 0.8102951701860561 65 | 0.9694740293048988 66 | 1.0153040205135362 67 | 0.9535064922501415 68 | 0.8244440078173636 69 | 0.6094220897826409 70 | 0.32268141577876447 71 | -0.04871740908935948 72 | -0.3559487747962311 73 | -0.6279821648118419 74 | -0.8221058234585136 75 | -0.9184111037316791 76 | -1.015231829140843 77 | -0.9666250145225178 78 | -0.8337170120101626 79 | -0.6161381376269769 80 | -0.30351678081147065 81 | -0.009792915472817918 82 | 0.3096667007425866 83 | 0.5546532191259058 84 | 0.7921538204056463 85 | 0.9838493259102092 86 | 1.0200287873145815 87 | 0.9068491422615875 88 | 0.8589329433969803 89 | 0.5789392886145216 90 | 0.27018445805143065 91 | 0.028075408455850364 92 | -0.34980523193087387 93 | -0.6324357898430344 94 | -0.7875474127857829 95 | -0.9759802887522352 96 | -0.9651367079096843 97 | -0.9036683288880878 98 | -0.8377608994699172 99 | -0.6356318739599136 100 | -0.2644975183360643 101 | -0.04070298445001228 102 | 0.3236003318273422 103 | 0.5689738065751804 104 | 0.8038634307689914 105 | 0.9497804411531902 106 | 0.958247967651135 107 | 0.9682394269186169 108 | 0.7990358233173095 109 | 0.6278127670189053 110 | 0.3540053149850723 111 | -0.018066873239290474 112 | -0.30907844499970993 113 | -0.5977420205782907 114 | -0.8570361273424024 115 | -0.9365526296932095 116 | -1.0071311569930068 117 | -0.9670968411564231 118 | -0.7702722442898972 119 | -0.6141525048620353 120 | -0.28251617288162245 121 | -0.046424535256393035 122 | 0.3317747199791 123 | 0.6003689149882874 124 | 0.8103257449837312 125 | 0.9083013519743882 126 | 1.0224229058459169 127 | 0.9890410009257239 128 | 0.8567933421107212 129 | 0.6225352785189425 130 | 0.3422367881899405 131 | -0.02521554768129532 132 | -0.2676770880812971 133 | -0.6302815312790008 134 | -0.7755066142595122 135 | -0.938123347130622 136 | -0.9748259421103265 137 | -0.9378561729162741 138 | -0.8493235730510784 139 | -0.636302515342984 140 | -0.3006335201886976 141 | -0.04312438047979335 142 | 0.3588443754757969 143 | 0.6027040939523113 144 | 0.8575635522382743 145 | 0.9845370923170743 146 | 0.983205608571906 147 | 0.9671958343534873 148 | 0.8546190570346104 149 | 0.5688362799149568 150 | 0.27745606377515014 151 | 0.04670943413718269 152 | -0.2756928128221285 153 | -0.6068304072397388 154 | -0.778429817621183 155 | -0.9593239320761159 156 | -0.9781469550647225 157 | -0.9603828387501151 158 | -0.7694366676005329 159 | -0.5419676160322182 160 | -0.3571456722609853 161 | 0.02916723090820636 162 | 0.30137230976353135 163 | 0.539303380014776 164 | 0.8022868183828517 165 | 0.9915341400017263 166 | 1.0355704414574887 167 | 0.9053486805127865 168 | 0.8249200476757251 169 | 0.5725711566054802 170 | 0.30936567337981685 171 | 0.03399474211706161 172 | -0.35670603753389607 173 | -0.6253489004325172 174 | -0.8328994324561254 175 | -0.9175617656458047 176 | -1.0185195204044024 177 | -1.0002883956477455 178 | -0.7690419818031948 179 | -0.6007584986474267 180 | -0.34899705344800297 181 | 0.014320402657018111 182 | 0.3360059033732486 183 | 0.6168977858586726 184 | 0.7852551851220179 185 | 0.9357429043330806 186 | 0.9714653715376942 187 | 0.9832658060128125 188 | 0.794130424341467 189 | 0.5977046775430631 190 | 0.31685212006806596 191 | -0.008641901202369934 192 | -0.3470319434846572 193 | -0.5466238816133056 194 | -0.8536384363643723 195 | -0.9781647576190918 196 | -1.017582603693861 197 | -0.9659800035304382 198 | -0.8240881195975257 199 | -0.6074050393070357 200 | -0.2621423783783703 201 | 0.7671526550460838 202 | 0.867740625910434 203 | 1.0340780691940354 204 | 0.9679843665780018 205 | 0.9277404211685991 206 | 0.7134084162750242 207 | 0.6478297158463375 208 | 0.526322353043037 209 | 0.5575617463512468 210 | 0.6340411095202769 211 | 0.7181590846757563 212 | 0.9412220502268556 213 | 1.020888232628165 214 | 0.9872321726519944 215 | 0.9324773477208124 216 | 0.7710743911819099 217 | 0.5804031625560415 218 | 0.5029991581154097 219 | 0.5532119991781235 220 | 0.6474934007969121 221 | 0.74986324518556 222 | 0.8758101440858491 223 | 1.0353536547237523 224 | 0.9830225767015964 225 | 0.8514453829409145 226 | -0.018463801848179247 227 | 0.3542076091869846 228 | 0.6129415605398968 229 | 0.8125960942739088 230 | 0.9680279751786642 231 | 1.0365174997483286 232 | 0.9469449616855425 233 | 0.8168720846245294 234 | 0.5859382344774394 235 | 0.31407857057326577 236 | 0.04506232438081556 237 | -0.30803045232765175 238 | -0.5635337793262897 239 | -0.8099375929335118 240 | -0.9944413748080846 241 | -1.0237509337350101 242 | -0.9085097218873537 243 | -0.8218683292091253 244 | -0.5968433122893659 245 | -0.317441797401548 246 | -0.0402738400263463 247 | 0.3491797568229165 248 | 0.5382297182722862 249 | 0.7864094487100505 250 | 0.9119871824068332 251 | 1.0355448412892954 252 | 0.9267620519590563 253 | 0.8579302035781498 254 | 0.6304263945292861 255 | 0.27611159758378706 256 | -0.01961128751067439 257 | -0.30567184939683156 258 | -0.6201362909448258 259 | -0.7776579168972932 260 | -0.9305428039150275 261 | -1.02427924425786 262 | -0.9760196242486553 263 | -0.8255075576850198 264 | -0.5626611891299474 265 | -0.3585290161141693 266 | 0.03409932064362553 267 | 0.2819743532446127 268 | 0.539113807065231 269 | 0.8540107350918265 270 | 0.9909936627608549 271 | 1.0462624201143886 272 | 0.905356552490646 273 | 0.830283255591415 274 | 0.5428946621182964 275 | 0.29977020486080574 276 | -0.0024302626003822087 277 | -0.32427015576800755 278 | -0.633713258421363 279 | -0.7992603738605073 280 | -0.9750440489348438 281 | -0.9667144144422612 282 | -0.9050067657653321 283 | -0.765346237484293 284 | -0.6148532284477397 285 | -0.28698568435603145 286 | 0.02564823242687567 287 | 0.3040323868825432 288 | 0.571682991132016 289 | 0.8062661996000595 290 | 0.9996559522959704 291 | 0.9862331477128943 292 | 0.9260220170255168 293 | 0.7638038291929254 294 | 0.6167569738454733 295 | 0.3346380125151739 296 | 0.015746904556001532 297 | -0.33691710917137674 298 | -0.5720347146542905 299 | -0.8199327498598329 300 | -0.9091310662982552 301 | -1.0350314518310402 302 | -0.9490173140891359 303 | -0.7824581411915235 304 | -0.6129670062757127 305 | -0.31347259749824996 306 | 0.0199789142427406 307 | 0.26721394107141155 308 | 0.5890124555867629 309 | 0.7856551737126005 310 | 0.9632566797548867 311 | 0.959907017544019 312 | 0.9151140450638515 313 | 0.7749373508417339 314 | 0.6321867620518986 315 | 0.3221646523816837 316 | -0.025420804550392938 317 | -0.2622664697727883 318 | -0.5840871906954355 319 | -0.8251988848530648 320 | -0.9338897522289928 321 | -0.9662534075926554 322 | -0.9276710325748233 323 | -0.808794913523073 324 | -0.5954378743266854 325 | -0.3127130441383296 326 | 0.04530446527484906 327 | 0.3334242561815594 328 | 0.5833042114821931 329 | 0.7838586925727303 330 | 0.9730450725734872 331 | 0.9923702199453777 332 | 0.9753156020322821 333 | 0.8247018202457266 334 | 0.6060374098324707 335 | 0.3226960838336432 336 | -0.025141716446182603 337 | -0.32033085065150657 338 | -0.5607881570120141 339 | -0.8463805584481151 340 | -0.9723927397438714 341 | -0.9988660821828385 342 | -0.9812003627577196 343 | -0.7595563576877147 344 | -0.5741899239399393 345 | -0.28628509504389094 346 | -0.00876845569849172 347 | 0.31041935125411974 348 | 0.5509042438304658 349 | 0.8135772577600695 350 | 0.9855600179459438 351 | 0.9793284353803121 352 | 0.9788868980391526 353 | 0.773194871253649 354 | 0.6032232046332479 355 | 0.32215181783963254 356 | -0.010603475521839913 357 | -0.35887583997245565 358 | -0.5811985908482209 359 | -0.7702377922110796 360 | -0.9558082328359783 361 | -0.990382134147687 362 | -0.9367729825814859 363 | -0.7760331540386426 364 | -0.5567472363850408 365 | -0.3524235111884259 366 | -0.04185252949254716 367 | 0.29256879659476526 368 | 0.5868487260271898 369 | 0.8209199621396149 370 | 0.9638223898916096 371 | 0.9824644108228286 372 | 0.9803779684688573 373 | 0.7674356829250691 374 | 0.5632178242975898 375 | 0.3277738134309904 376 | -0.007978263432443778 377 | -0.3568038811585704 378 | -0.6160855730437276 379 | -0.8511924208812964 380 | -0.9446720828812145 381 | -1.027142480733027 382 | -0.9642991596335408 383 | -0.8318125066497807 384 | -0.5477552756578856 385 | -0.2928339889581473 386 | -0.005487009100799601 387 | 0.35589145095658836 388 | 0.6045465862880259 389 | 0.7954570006839575 390 | 0.901719212849826 391 | 0.9706175284270224 392 | 0.9793020217214453 393 | 0.8356601213554062 394 | 0.6186094144917453 395 | 0.2878079429680395 396 | 0.010994971780476454 397 | -0.3137966015510032 398 | -0.6151342932162742 399 | -0.8201113552094168 400 | -0.9763700561814327 401 | -1.049247155081491 402 | -0.9857089454329965 403 | -0.8585680642257962 404 | -0.5468648975577606 405 | -0.33523290243841686 406 | -0.017273693146959097 407 | 0.3383326560276237 408 | 0.5808144302596852 409 | 0.803026466463951 410 | 0.9110174638408762 411 | 1.0284020676567298 412 | 0.9532644256630265 413 | 0.7692357829306108 414 | 0.6226139548665834 415 | 0.2754028384601261 416 | 0.006540421265437664 417 | -0.34689724874382244 418 | -0.6102498026754133 419 | -0.7591836864449258 420 | -0.908126531974794 421 | -1.0396711316699152 422 | -0.9799694066209607 423 | -0.8275579804511963 424 | -0.5905423372232297 425 | -0.34469030399475326 426 | -0.007888370711515809 427 | 0.11287237825136333 428 | -0.05693073725327355 429 | -0.08252125584276714 430 | -0.09494364175741773 431 | -0.025851206616728517 432 | -0.06277901186192104 433 | -0.07721143725469216 434 | 0.02175258136057842 435 | -0.04296394202195818 436 | -0.10223383089006631 437 | -0.07589304777315256 438 | -0.019696231642339822 439 | 0.08506224463420092 440 | -0.07261706350969112 441 | -0.09197975650474395 442 | 0.10135723322458672 443 | -0.07388884511147382 444 | 0.09146874839961694 445 | 0.071855424030679 446 | -0.015009429853623885 447 | -0.1578105668112625 448 | -0.09475470397169594 449 | 0.025001545131021957 450 | 0.10362150973460844 451 | -0.14148398785927227 452 | 0.016540946850849357 453 | -0.10176450005745144 454 | 0.09591106737712277 455 | -0.07181383935821861 456 | -0.13003553599659148 457 | 0.00706027437954878 458 | -0.017816081085432756 459 | -0.10656243577573944 460 | -0.05659082182227672 461 | -0.03817181746207342 462 | -0.058820448764098796 463 | -0.03702844636516331 464 | 0.05495741618055828 465 | 0.006055149959405766 466 | 0.13728674848245173 467 | -0.06660179843522954 468 | 0.0934344706381996 469 | -0.10951993919353703 470 | 0.09532139646597211 471 | -0.16806342932930346 472 | -0.0833059744648336 473 | 0.028142640540112195 474 | -0.0994445024900962 475 | -0.06274247828093724 476 | 0.17140222706602584 477 | 0.04310095361297417 478 | 0.11060799558885107 479 | 0.06608733742564077 480 | -0.05095475714040172 481 | -0.06253709677145608 482 | 0.14602312829031217 483 | 0.006636450201603687 484 | 0.10307106365834892 485 | 0.17174650965415852 486 | -0.16642679131192065 487 | -0.001730985977247107 488 | 0.15787053427029815 489 | 0.14102229306323677 490 | -0.055140125856387084 491 | 0.027482898402195532 492 | 0.04508903615582546 493 | 0.06425112226761194 494 | 0.07396753941647924 495 | 0.06199649897848222 496 | 0.04334991254059097 497 | -0.05170131764871183 498 | -0.09583956622555069 499 | 0.12137652575839598 500 | 0.13054914375275106 501 | -0.08444708187761386 502 | 0.11238717916389024 503 | 0.029015700923020513 504 | -0.08126264606735037 505 | 0.06241296017360042 506 | 0.054370537350141836 507 | -0.010847978664558586 508 | -0.12237039690982962 509 | 0.03701088767014695 510 | -0.07141324027415105 511 | -0.11603059726225283 512 | 0.0693146702399775 513 | -0.10846999521090835 514 | -0.061048430475357775 515 | -0.0550461665516337 516 | -0.06446912507471884 517 | 0.05032575375254287 518 | 0.0269379607904489 519 | 0.03289594959522359 520 | 0.06598430807536103 521 | 0.1358537854230438 522 | -0.14314197655467512 523 | 0.06145950291593861 524 | -0.019202726298444748 525 | -0.0042757641419169995 526 | 0.7854467105033583 527 | 0.9092593718845945 528 | 1.0072407628876279 529 | 0.9709831498957027 530 | 0.9161242858257466 531 | 0.7783743053021985 532 | 0.6319369262905925 533 | 0.5406949456437247 534 | 0.5474374864366578 535 | 0.5840026441047677 536 | 0.7027118954252389 537 | 0.8522226443363002 538 | 0.9555787311528374 539 | 0.9759734352182879 540 | 0.9243192292428726 541 | 0.7291793169543692 542 | 0.5903626478009439 543 | 0.5052315889269835 544 | 0.4838523410921063 545 | 0.5855618226188668 546 | 0.7501267527184017 547 | 0.8882362709469711 548 | 0.9665716518338079 549 | 1.0150450925373808 550 | 0.8711293246131051 551 | -0.03161875573835909 552 | 0.10154029567009609 553 | -0.10227927096999352 554 | -0.08028831595167671 555 | -0.04042496593182217 556 | -0.04151078078390403 557 | -0.061005486370080006 558 | -0.046753489928196904 559 | 0.05355853120212359 560 | -0.10931318744930976 561 | -0.13212305043677872 562 | -0.026196878072425857 563 | 0.012637127002804597 564 | 0.08467977470344745 565 | -0.018834877102156374 566 | -0.10819546055746734 567 | 0.09696485811065579 568 | -0.05332045575027434 569 | 0.07466160822695346 570 | 0.023814315285000075 571 | 0.018391173494860347 572 | -0.11762943412429559 573 | -0.09149218352899575 574 | 0.06823529956261401 575 | 0.1009896474457179 576 | -0.13465611374512082 577 | 0.05843733022295751 578 | -0.16113705697997752 579 | 0.017966619028245208 580 | -0.08828113729866802 581 | -0.09819645231687318 582 | -0.021397689620105208 583 | 0.05765523241208711 584 | -0.03442591939188099 585 | 0.005457167491178874 586 | -0.016351878173810466 587 | -0.06257881031607977 588 | -0.08995521176741865 589 | 0.09812995431939163 590 | -0.07102557657319665 591 | 0.07138918748296541 592 | -0.05832360214608245 593 | 0.15935935314582828 594 | -0.12420720495108412 595 | 0.17913583723347065 596 | -0.09935759008263506 597 | -0.04993351249654666 598 | 0.09411869134353432 599 | -0.06973150533094732 600 | -0.07712131284945539 601 | 0.0877337703905414 602 | 0.12031445886876138 603 | 0.09535846097361296 604 | 0.061491044685432275 605 | -0.09725657917715516 606 | -0.06580967330147668 607 | 0.08713655614282849 608 | 0.028872946090962064 609 | 0.15949891801532515 610 | 0.11831694952745135 611 | -0.09556133266195233 612 | -0.039136660644217176 613 | 0.09834194908490285 614 | 0.09660096844268798 615 | -0.019818412466997185 616 | 0.08825280323227232 617 | 0.06466866280777062 618 | -0.012289974066796144 619 | -0.0039050579882750416 620 | 0.019558386643973434 621 | 0.1244493225102745 622 | 0.0006705692401834523 623 | -0.11212273416055929 624 | 0.149864514966049 625 | 0.16409848983517772 626 | -0.029934556643791257 627 | 0.06322173699527787 628 | 0.02588057186920506 629 | -0.131915068475971 630 | 0.054934754301515475 631 | 0.04097263815208818 632 | -0.025236639096225665 633 | -0.054304321219679236 634 | -0.004481439183689418 635 | -0.06444623136805523 636 | -0.10978722228117183 637 | 0.0526605525699215 638 | -0.13219763380792707 639 | -0.044669980850928284 640 | 0.006488085654300122 641 | -0.011479175171805129 642 | 0.051950872234314105 643 | -0.03296843193582394 644 | 0.07848536739878759 645 | 0.0910368337388966 646 | 0.11478754439286994 647 | -0.11555564882570028 648 | 0.030753668339173992 649 | 0.017274904406073856 650 | 0.0007342868323461428 651 | 0.7174950706024954 652 | 0.9344917406578497 653 | 1.0048866737698032 654 | 0.9389251456803618 655 | 0.9230287696576204 656 | 0.740245872342546 657 | 0.5620869401953946 658 | 0.5024014544445986 659 | 0.47215300274854194 660 | 0.559766522407096 661 | 0.7187993625667801 662 | 0.893359285754124 663 | 0.9536111658366515 664 | 0.9660006837311863 665 | 0.8567126270314229 666 | 0.7926082191269462 667 | 0.6476582552881048 668 | 0.5232878878060832 669 | 0.4771382944106303 670 | 0.5553592424853785 671 | 0.7739291348873052 672 | 0.8542694652483942 673 | 1.027754609701046 674 | 0.9909669499472838 675 | 0.9277208973776913 676 | 0.026649556189938063 677 | 0.09867676443556421 678 | -0.11065415537812381 679 | -0.05000831677833724 680 | -0.015463798567587096 681 | 0.02060730235437247 682 | -0.08843486999338636 683 | -0.06734032439407714 684 | 0.016558972205430712 685 | -0.05692619248951518 686 | -0.10717803378464101 687 | -0.051409658805340416 688 | 0.027794911433661196 689 | 0.06593755886744662 690 | -0.08307801416928792 691 | -0.050202955285361936 692 | 0.10839802821056073 693 | -0.0750664297909512 694 | 0.07572199819698677 695 | 0.03001880842021743 696 | 0.02975914206080356 697 | -0.13309869625651 698 | -0.06009941784041426 699 | 0.0289356350455934 700 | 0.16670233806194856 701 | -0.1259042304266615 702 | 0.006800394215163867 703 | -0.08776160657203841 704 | 0.0384774035255454 705 | -0.03597635800905464 706 | -0.11100874466836481 707 | -0.025123388873983292 708 | -0.03598617034952947 709 | -0.11505426988703285 710 | -0.026020258075970536 711 | 0.013123936535610614 712 | -0.04889911158760987 713 | -0.06767907073523613 714 | 0.12265985955763602 715 | -0.07585420435443745 716 | 0.1183268078633329 717 | -0.08400975669879479 718 | 0.16308131986653643 719 | -0.09298058677364784 720 | 0.16681680048103054 721 | -0.11798502057773613 722 | -0.12598377502865152 723 | 0.058241360801199495 724 | -0.058905873168557055 725 | -0.07970014907586193 726 | 0.08398598053435224 727 | 0.08735920066206404 728 | 0.0816213338792202 729 | 0.10981176134119172 730 | -0.13780854449108676 731 | -0.0012468778348702463 732 | 0.08622770062722483 733 | 0.09368246995606483 734 | 0.18431575923629223 735 | 0.13955742649367295 736 | -0.10965153602200646 737 | -0.050461665198787774 738 | 0.10039399920633447 739 | 0.15094735007864823 740 | -0.04384556652522858 741 | 0.015887981406202463 742 | 0.03744900304125659 743 | 0.0465542725243462 744 | 0.06769645254250575 745 | 0.025230136460835727 746 | 0.052048936019307906 747 | 0.007625430870721227 748 | -0.11012825694518759 749 | 0.18716509841341908 750 | 0.15436763089413447 751 | -0.03453676818735799 752 | 0.04684279981692176 753 | 0.020735311248130595 754 | -0.0992487509925908 755 | 0.0734001929913437 756 | 0.019709926629224026 757 | -0.0612608205225081 758 | -0.03716096984353669 759 | 0.050230774825050534 760 | -0.06254490981566249 761 | -0.06798611920666606 762 | 0.08470069652274947 763 | -0.14964739119779608 764 | -0.054695985362861285 765 | -0.05550594235287376 766 | 0.02140862195072929 767 | 0.06852053184272189 768 | 0.04067829133517505 769 | 0.04604082361757479 770 | 0.047430042355174826 771 | 0.05739509837210592 772 | -0.18378112468226826 773 | 0.06803764204000429 774 | -0.056784979959146324 775 | 0.01130633690967402 776 | -0.4795603295167729 777 | -0.43657011932505463 778 | -0.3644648870192759 779 | -0.27013700101791516 780 | -0.179319479896551 781 | -0.1672995990326534 782 | -0.07007892887082552 783 | -0.05610659109886941 784 | 0.023085312543560464 785 | 0.1508602314462213 786 | 0.24053087962236228 787 | 0.284572422309976 788 | 0.37617738279850177 789 | 0.4420460417561523 790 | 0.4719097788285822 791 | -0.4118679246147953 792 | -0.34950031030272977 793 | -0.2913045384072341 794 | -0.2210971005577409 795 | -0.12818269124299042 796 | -0.14944165228210277 797 | -0.06136272946243235 798 | 0.07772190065290369 799 | 0.1574317613936445 800 | 0.18347442949063586 801 | 0.2748551918244141 802 | 0.32611077949797423 803 | 0.3415953690587671 804 | 0.42345081806691487 805 | -0.5061878858588633 806 | -0.4411607122970504 807 | -0.29792376849297253 808 | -0.21189838777602996 809 | -0.21649379173744515 810 | -0.1684097286165706 811 | -0.0519651604167376 812 | -0.010996585760019575 813 | 0.13078133244477838 814 | 0.1339553458738243 815 | 0.18139544299088728 816 | 0.28832159342580704 817 | 0.32260389275928936 818 | 0.4406672203677412 819 | -0.49559751012786113 820 | -0.44558593857177037 821 | -0.321724433661775 822 | -0.2671880018887138 823 | -0.16474259727810447 824 | -0.14883306865981938 825 | -0.021788405437735095 826 | -0.025582304492794288 827 | 0.11995424112844798 828 | 0.10402082695463927 829 | 0.20668609675810032 830 | 0.2816737939257502 831 | 0.3105744004985666 832 | 0.4243321982938547 833 | 0.4966570949246811 834 | -0.48788849861893885 835 | -0.3793019948820744 836 | -0.25498575591954087 837 | -0.1870961976474077 838 | -0.13025165071998498 839 | -0.09143178423396611 840 | -0.040951962305245254 841 | 0.03285772786058176 842 | 0.1401226805248037 843 | 0.14986088994182498 844 | 0.22223162372715619 845 | 0.3281311138441195 846 | 0.3600887345889013 847 | 0.5126744091860278 848 | -0.47533377194746557 849 | -0.40594540634948273 850 | -0.35285658391613917 851 | -0.23793131237103274 852 | -0.17806556020428965 853 | -0.12901178162698168 854 | -0.05001356132371597 855 | -0.003283180142911752 856 | 0.07180101484692407 857 | 0.21295246017496636 858 | 0.2540563483089249 859 | 0.3101779428848797 860 | 0.39027224873260363 861 | 0.41806163197991614 862 | -0.45135691697757285 863 | -0.40917850822796664 864 | -0.3294286043614043 865 | -0.2498083189903666 866 | -0.1775900881756558 867 | -0.1629298780906927 868 | -0.09821846760026859 869 | -0.036607997721088685 870 | 0.04688909909134364 871 | 0.16491926078828917 872 | 0.22830194636568457 873 | 0.2535212745010135 874 | 0.34083720070467627 875 | 0.47908832564397225 -------------------------------------------------------------------------------- /tests/test_discords.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from matrixprofile.discords import * 3 | import numpy as np 4 | import pytest 5 | 6 | class TestClass(object): 7 | 8 | def test_discords_no_exclusion(self): 9 | mp = np.array([1.0, 2.0, 3.0, 4.0]) 10 | outcome = np.array([3,3,3,3]) 11 | 12 | assert(np.allclose(discords(mp, 0, 4), outcome)) 13 | 14 | def test_discords_exclude_one(self): 15 | mp = np.array([1.0, 2.0, 3.0, 4.0]) 16 | outcome = np.array([3,1,sys.maxsize,sys.maxsize]) 17 | 18 | assert(np.allclose(discords(mp, 1, 4), outcome)) 19 | 20 | def test_discords_exclude_big(self): 21 | mp = np.array([1.0, 2.0, 3.0, 4.0]) 22 | outcome = np.array([3,sys.maxsize,sys.maxsize,sys.maxsize]) 23 | 24 | assert(np.allclose(discords(mp, 10, 4), outcome)) 25 | 26 | def test_discords_empty_mp(self): 27 | mp = np.array([]) 28 | outcome = np.array([]) 29 | 30 | assert(np.allclose(discords(mp, 1, 4), outcome)) 31 | 32 | def test_discords_k_larger_than_mp(self): 33 | mp = np.array([1.0, 2.0, 3.0, 4.0]) 34 | outcome = np.array([3,1,sys.maxsize,sys.maxsize]) 35 | 36 | assert(np.allclose(discords(mp, 1, 10), outcome)) 37 | 38 | -------------------------------------------------------------------------------- /tests/test_distanceProfile.py: -------------------------------------------------------------------------------- 1 | from matrixprofile.distanceProfile import * 2 | import numpy as np 3 | import pytest 4 | 5 | class TestClass(object): 6 | 7 | def test_naiveDistanceProfile_self(self): 8 | outcome = (np.array([0.0,2.828,np.inf,np.inf,np.inf,np.inf,np.inf,2.828,0.0]),np.array([4.,4.,4.,4.,4.,4.,4.,4.,4.])) 9 | 10 | b = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 11 | 12 | assert(np.round(naiveDistanceProfile(b,4,4),3) == outcome).all() 13 | 14 | #Need to confirm that we're not updating the original variable via shared memory 15 | assert(b == np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0])).all() 16 | 17 | 18 | def test_naiveDistanceProfile_tsa_tsb(self): 19 | outcome = (np.array([0.0,2.828,4.0,2.828,0.0,2.828,4.0,2.828,0.0]),np.array([0.,0.,0.,0.,0.,0.,0.,0.,0.])) 20 | 21 | a = np.array([0.0,1.0,1.0,0.0]) 22 | b = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 23 | 24 | assert(np.round(naiveDistanceProfile(a,0,4,b),3) == outcome).all() 25 | assert(a == np.array([0.0,1.0,1.0,0.0])).all() 26 | 27 | 28 | 29 | def test_massDistanceProfile_self(self): 30 | outcome = (np.array([0.0,2.828,np.inf,np.inf,np.inf,np.inf,np.inf,2.828,0.0]),np.array([4.,4.,4.,4.,4.,4.,4.,4.,4.])) 31 | 32 | b = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 33 | 34 | assert(np.round(massDistanceProfile(b,4,4),3) == outcome).all() 35 | 36 | #Need to confirm that we're not updating the original variable via shared memory 37 | assert(b == np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0])).all() 38 | 39 | 40 | def test_massDistanceProfile_tsa_tsb(self): 41 | outcome = (np.array([0.0,2.828,4.0,2.828,0.0,2.828,4.0,2.828,0.0]),np.array([0.,0.,0.,0.,0.,0.,0.,0.,0.])) 42 | 43 | a = np.array([0.0,1.0,1.0,0.0]) 44 | b = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 45 | 46 | assert(np.round(massDistanceProfile(a,0,4,b),3) == outcome).all() 47 | assert(a == np.array([0.0,1.0,1.0,0.0])).all() 48 | 49 | 50 | def test_STOMPDistanceProfile_self(self): 51 | outcome = (np.array([0.0,2.828,np.inf,np.inf,np.inf,np.inf,np.inf,2.828,0.0]),np.array([4.,4.,4.,4.,4.,4.,4.,4.,4.])) 52 | 53 | tsA = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 54 | idx = 4 55 | m = 4 56 | tsB = None 57 | dot_first = np.array([2., 1., 0., 1., 2., 1., 0., 1., 2.]) 58 | dp = np.array([1., 0., 1., 2., 1., 0., 1., 2., 1.]) 59 | mean = np.array([0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]) 60 | std = np.array([0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]) 61 | 62 | result, out = STOMPDistanceProfile(tsA,idx,m,tsB,dot_first,dp,mean,std) 63 | 64 | assert(np.round(result,3) == outcome).all() 65 | 66 | #Need to confirm that we're not updating the original variable via shared memory 67 | assert(tsA == np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0])).all() 68 | -------------------------------------------------------------------------------- /tests/test_fluss.py: -------------------------------------------------------------------------------- 1 | from matrixprofile.fluss import * 2 | import numpy as np 3 | 4 | class TestClass(object): 5 | 6 | def test_fluss(self): 7 | cac = fluss([3, 0, 1, 1, 1, 7, 3, 8, 6, 7, 8, 7], 2) 8 | outcome = np.array([1., 1., 0.9, 0.22222222, 0.1875, 0.17142857, 0.16666667, 0.17142857, 0.375, 0.44444444, 9 | 1., 1.]) 10 | 11 | assert(np.allclose(cac, outcome)) 12 | -------------------------------------------------------------------------------- /tests/test_matrixProfile.py: -------------------------------------------------------------------------------- 1 | from matrixprofile.matrixProfile import * 2 | import numpy as np 3 | import pytest 4 | 5 | class TestClass(object): 6 | 7 | def test_naiveMP_self_mp(self): 8 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 9 | mp_outcome = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0.]) 10 | 11 | 12 | r = naiveMP(a,4) 13 | 14 | assert(r[0] == mp_outcome).all() 15 | 16 | 17 | def test_naiveMP_self_mpi(self): 18 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 19 | mpi_outcome = np.array([0., 1., 2., 3., 0., 1., 2., 3., 0.]) 20 | 21 | r = naiveMP(a,4) 22 | 23 | assert(r[1] == mpi_outcome).all() 24 | 25 | def test_naiveMP_dual_mp(self): 26 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 27 | b = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 28 | mp_outcome = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0.]) 29 | 30 | 31 | r = naiveMP(a,4,b) 32 | 33 | assert(r[0] == mp_outcome).all() 34 | 35 | 36 | def test_naiveMP_dual_mpi(self): 37 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 38 | b = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 39 | mpi_outcome = np.array([0., 1., 2., 3., 0., 1., 2., 3., 0.]) 40 | 41 | r = naiveMP(a,4,b) 42 | 43 | assert(r[1] == mpi_outcome).all() 44 | 45 | def test_stmp_self_mp(self): 46 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 47 | mp_outcome = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0.]) 48 | 49 | 50 | r = stmp(a,4) 51 | 52 | assert(r[0] == mp_outcome).all() 53 | 54 | 55 | def test_stmp_self_mpi(self): 56 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 57 | mpi_outcome = np.array([0., 1., 2., 3., 0., 1., 2., 3., 0.]) 58 | 59 | r = stmp(a,4) 60 | 61 | assert(r[1] == mpi_outcome).all() 62 | 63 | def test_stmp_dual_mp(self): 64 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 65 | b = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 66 | mp_outcome = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0.]) 67 | 68 | 69 | r = stmp(a,4,b) 70 | 71 | assert(r[0] == mp_outcome).all() 72 | 73 | 74 | def test_stmp_dual_mpi(self): 75 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 76 | b = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 77 | mpi_outcome = np.array([0., 1., 2., 3., 0., 1., 2., 3., 0.]) 78 | 79 | r = stmp(a,4,b) 80 | 81 | assert(r[1] == mpi_outcome).all() 82 | 83 | 84 | def test_stamp_self_mp(self): 85 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 86 | mp_outcome = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0.]) 87 | 88 | 89 | r = stamp(a,4, sampling=1.0) 90 | 91 | assert(r[0] == mp_outcome).all() 92 | 93 | 94 | def test_stamp_self_mpi(self): 95 | #Note that we're only testing for the length of the matrix profile index and not the specific values 96 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 97 | mpi_outcome = np.array([4., 5., 6., 7., 0., 1., 2., 3., 0.]) 98 | 99 | r = stamp(a,4,sampling=1.0) 100 | 101 | assert(len(r[1]) == 9) 102 | 103 | def test_stamp_dual_mp(self): 104 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 105 | b = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 106 | mp_outcome = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0.]) 107 | 108 | 109 | r = stamp(a,4,b,sampling=1.0) 110 | 111 | assert(r[0] == mp_outcome).all() 112 | 113 | def test_stamp_dual_mp_nan_inf(self): 114 | a = np.array([0.0,1.0,1.0,0.0,np.nan,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 115 | b = np.array([0.0,1.0,1.0,0.0,np.inf,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 116 | mp_outcome = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0.]) 117 | 118 | 119 | r = stamp(a,4,b,sampling=1.0) 120 | 121 | assert(r[0] == mp_outcome).all() 122 | 123 | 124 | def test_stamp_dual_mpi(self): 125 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 126 | b = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 127 | mpi_outcome = np.array([0., 1., 2., 3., 0., 1., 2., 3., 0.]) 128 | 129 | r = stamp(a,4,b,sampling=1.0) 130 | 131 | assert(len(r[1]) == 9) 132 | 133 | 134 | def test_stampi_mp(self): 135 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0]) 136 | r = stamp(a,4, sampling=1.0) 137 | final = np.round(stampi_update(a,4,r[0],r[1],95),2) 138 | 139 | mp_outcome = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.83]) 140 | 141 | assert(np.allclose(final[0],mp_outcome)) 142 | 143 | 144 | # def test_stampi_mpi(self): 145 | # #Note: given the new self-join logic in v0.0.7 and above, STAMPI will not guarantee the same MPI where there are multiple "true" outcomes. There are 128 possible outcomes, so this test needs to be better designed... 146 | # a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0]) 147 | # r = stamp(a,4, sampling=1.0) 148 | # final = np.round(stampi_update(a,4,r[0],r[1],95),2) 149 | # 150 | # mpi_outcome_1 = np.array([0.0, 1.0, 2.0, 3.0, 0.0, 1.0, 2.0, 3.0, 3.0]) 151 | # mpi_outcome_2 = np.array([4.0, 1.0, 6.0, 7.0, 4.0, 1.0, 6.0, 7.0, 3.0]) 152 | # mpi_outcome_3 = np.array([4.0, 5.0, 6.0, 7.0, 0.0, 1.0, 2.0, 3.0, 3.0]) 153 | # mpi_outcome_4 = np.array([0.0, 5.0, 6.0, 7.0, 0.0, 5.0, 6.0, 7.0, 3.0]) 154 | # ... 155 | # 156 | # 157 | # 158 | # assert(np.allclose(final[1],mpi_outcome_1) | np.allclose(final[1],mpi_outcome_2) | np.allclose(final[1],mpi_outcome_3) | np.allclose(final[1],mpi_outcome_4)) 159 | 160 | 161 | def test_stomp_self_mp(self): 162 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 163 | mp_outcome = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0.]) 164 | 165 | 166 | r = stomp(a,4) 167 | 168 | assert(r[0] == mp_outcome).all() 169 | 170 | 171 | def test_stomp_self_mpi(self): 172 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 173 | mpi_outcome = np.array([4., 5., 6., 7., 0., 1., 2., 3., 0.]) 174 | 175 | r = stomp(a,4) 176 | 177 | assert(r[1] == mpi_outcome).all() 178 | 179 | def test_stamp_sampling_over_one(self): 180 | with pytest.raises(ValueError) as excinfo: 181 | stamp(None,None,sampling=2) 182 | assert 'Sampling value must be a percentage' in str(excinfo.value) 183 | 184 | def test_stamp_sampling_under_zero(self): 185 | with pytest.raises(ValueError) as excinfo: 186 | stamp(None,None,sampling=-1) 187 | assert 'Sampling value must be a percentage' in str(excinfo.value) 188 | 189 | def test_stamp_random_state_same_results_self_join(self): 190 | random_state = 99 191 | sampling = 0.30 192 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 193 | 194 | r = stamp(a,4,None,sampling=sampling,random_state=random_state) 195 | r2 = stamp(a,4,None,sampling=sampling,random_state=random_state) 196 | 197 | all_same = (r[0] == r2[0]).all() and (r[1] == r2[1]).all() 198 | assert(all_same == True) 199 | 200 | 201 | def test_stamp_random_state_same_results_dual_join(self): 202 | random_state = 99 203 | sampling = 0.30 204 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 205 | b = np.array([0.0,1.0,1.0,0.2,0.0,1.0,1.0,0.3,0.0,1.0,1.0,0.0]) 206 | 207 | r = stamp(a,4,b,sampling=sampling,random_state=random_state) 208 | r2 = stamp(a,4,b,sampling=sampling,random_state=random_state) 209 | 210 | all_same = (r[0] == r2[0]).all() and (r[1] == r2[1]).all() 211 | assert(all_same == True) 212 | 213 | def test_stamp_with_parallel_version_random_state_set_self_join(self): 214 | random_state = 99 215 | sampling = 0.1 216 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 217 | r = stamp(a,4,None,sampling=sampling,random_state=random_state) 218 | r2 = stamp(a,4,None,sampling=sampling,random_state=random_state) 219 | 220 | all_same = (r[0] == r2[0]).all() and (r[1] == r2[1]).all() 221 | assert(all_same == True) 222 | 223 | def test_stamp_with_parallel_version_random_state_set_dual_join(self): 224 | random_state = 99 225 | sampling = 0.1 226 | a = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,2.0,0.0,1.1,1.0,0.0]) 227 | b = np.array([0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,0.0]) 228 | r = stamp(a,4,b,sampling=sampling,random_state=random_state) 229 | r2 = stamp(a,4,b,sampling=sampling,random_state=random_state) 230 | 231 | all_same = (r[0] == r2[0]).all() and (r[1] == r2[1]).all() 232 | assert(all_same == True) 233 | -------------------------------------------------------------------------------- /tests/test_motifs.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from matrixprofile.motifs import * 3 | import numpy as np 4 | import pytest 5 | 6 | 7 | class TestClass(object): 8 | 9 | def test_motifs_ind(self): 10 | a = np.array([0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 11 | 1.0, 0.0, 0.0, 1.0, 1.0, 0.0]) 12 | mp = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0.]) 13 | mpi = np.array([4., 5., 6., 7., 0., 1., 2., 3., 0.]) 14 | 15 | motif, _ = motifs(a, (mp, mpi)) 16 | motif_outcome = [[0, 4, 8]] 17 | 18 | assert(motif == motif_outcome) 19 | 20 | def test_motifs_dist(self): 21 | a = np.array([0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 22 | 1.0, 0.0, 0.0, 1.0, 1.0, 0.0]) 23 | mp = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0.]) 24 | mpi = np.array([4., 5., 6., 7., 0., 1., 2., 3., 0.]) 25 | 26 | _, dist = motifs(a, (mp, mpi)) 27 | motif_dist = [0] 28 | 29 | assert(np.allclose(dist, motif_dist)) 30 | 31 | def test_motifs_noexclude(self): 32 | a = np.array([0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 33 | 1.0, 0.0, 0.0, 1.0, 1.0, 0.0]) 34 | mp = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0.]) 35 | mpi = np.array([4., 5., 6., 7., 0., 1., 2., 3., 0.]) 36 | 37 | motif, _ = motifs(a, (mp, mpi), ex_zone=0) 38 | motif_outcome = [[0, 4, 8], [1, 5], [2, 6]] 39 | assert(motif == motif_outcome) 40 | 41 | def test_motifs_more(self): 42 | a = np.array([0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 43 | 1.0, 0.0, 0.0, 1.0, 1.0, 0.0]) 44 | mp = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0.]) 45 | mpi = np.array([4., 5., 6., 7., 0., 1., 2., 3., 0.]) 46 | 47 | motif, _ = motifs(a, (mp, mpi), max_motifs=5, ex_zone=0) 48 | motif_outcome = [[0, 4, 8], [1, 5], [2, 6], [3, 7]] 49 | 50 | assert(motif == motif_outcome) 51 | 52 | def test_motifs_nneighbors(self): 53 | a = np.array([0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 54 | 1.0, 0.0, 0.0, 1.0, 1.0, 0.0]) 55 | mp = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0.]) 56 | mpi = np.array([4., 5., 6., 7., 0., 1., 2., 3., 0.]) 57 | 58 | motif, _ = motifs(a, (mp, mpi), n_neighbors=2) 59 | motif_outcome = [[0, 4]] 60 | 61 | assert(motif == motif_outcome) 62 | 63 | def test_motifs_empty(self): 64 | res = motifs([], ([], [])) 65 | assert(res == ([], [])) 66 | 67 | def test_motifs_len_check(self): 68 | with pytest.raises(ValueError) as excinfo: 69 | motifs([1, 2], ([1, 2], [1, 2])) 70 | assert 'Matrix profile is longer than time series.' in str( 71 | excinfo.value) 72 | -------------------------------------------------------------------------------- /tests/test_order.py: -------------------------------------------------------------------------------- 1 | from matrixprofile.order import * 2 | import numpy as np 3 | import pytest 4 | 5 | class TestClass(object): 6 | 7 | def test_linearOrder_length(self): 8 | ord = linearOrder(10) 9 | 10 | t = 0 11 | indices = [] 12 | 13 | while t is not None: 14 | indices.append(t) 15 | t = ord.next() 16 | 17 | assert(len(indices[1:]) == 10) 18 | 19 | 20 | def test_linearOrder_vals(self): 21 | ord = linearOrder(10) 22 | 23 | t = 0 24 | indices = [] 25 | 26 | while t is not None: 27 | indices.append(t) 28 | t = ord.next() 29 | 30 | unique_vals = np.unique(indices[1:]) 31 | outcome = np.array([0,1,2,3,4,5,6,7,8,9]) 32 | 33 | assert(unique_vals == outcome).all() 34 | 35 | 36 | def test_randomOrder_length(self): 37 | ord = randomOrder(10) 38 | 39 | t = 0 40 | indices = [] 41 | 42 | while t is not None: 43 | indices.append(t) 44 | t = ord.next() 45 | 46 | assert(len(indices[1:]) == 10) 47 | 48 | 49 | def test_randomOrder_vals(self): 50 | ord = randomOrder(10) 51 | 52 | t = 0 53 | indices = [] 54 | 55 | while t is not None: 56 | indices.append(t) 57 | t = ord.next() 58 | 59 | unique_vals = np.unique(indices[1:]) 60 | outcome = np.array([0,1,2,3,4,5,6,7,8,9]) 61 | 62 | assert(unique_vals == outcome).all() 63 | 64 | def test_randomOrder_random_state_same_results(self): 65 | random_state=99 66 | order = randomOrder(10, random_state=random_state) 67 | order2 = randomOrder(10, random_state=random_state) 68 | 69 | indices = [] 70 | t = order.next() 71 | while t is not None: 72 | indices.append(t) 73 | t = order.next() 74 | 75 | indices2 = [] 76 | t2 = order2.next() 77 | while t2 is not None: 78 | indices2.append(t2) 79 | t2 = order2.next() 80 | 81 | indices = np.array(indices) 82 | indices2 = np.array(indices2) 83 | 84 | all_same = (indices == indices2).all() 85 | assert(all_same == True) -------------------------------------------------------------------------------- /tests/test_regimes.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from matrixprofile.matrixProfile import stomp 4 | from matrixprofile.fluss import fluss 5 | from matrixprofile.regimes import extract_regimes 6 | 7 | import matrixprofile 8 | 9 | MODULE_PATH = matrixprofile.__path__[0] 10 | 11 | import numpy as np 12 | 13 | class TestClass(object): 14 | 15 | def test_extract_regimes(self): 16 | data_file = os.path.join(MODULE_PATH, '..', 'docs', 'examples', 'rawdata.csv') 17 | ts = np.loadtxt(data_file, skiprows=1) 18 | m = 32 19 | mp, pi = stomp(ts, m) 20 | 21 | cac = fluss(pi, m) 22 | 23 | # test with 3 regimes 24 | regimes = extract_regimes(cac, m) 25 | expected = np.array([759, 423, 583]) 26 | 27 | np.testing.assert_array_equal(regimes, expected) 28 | 29 | # test with 2 regimes 30 | regimes = extract_regimes(cac, m, num=2) 31 | expected = np.array([759, 423]) 32 | 33 | np.testing.assert_array_equal(regimes, expected) -------------------------------------------------------------------------------- /tests/test_scrimp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import absolute_import 5 | from __future__ import division 6 | from __future__ import print_function 7 | from __future__ import unicode_literals 8 | 9 | range = getattr(__builtins__, 'xrange', range) 10 | # end of py2 compatability boilerplate 11 | 12 | """Tests for `scrimp` package.""" 13 | 14 | import os 15 | import pytest 16 | 17 | import numpy as np 18 | 19 | import matrixprofile 20 | from matrixprofile import scrimp 21 | 22 | MODULE_PATH = matrixprofile.__path__[0] 23 | 24 | def test_fast_find_nn_pre(): 25 | """Validate the computations for fast find nn pre.""" 26 | ts = np.array([1, 2, 3, 4, 5, 6, 7, 8]) 27 | m = 4 28 | 29 | X, n, sumx2, sumx, meanx, sigmax2, sigmax = scrimp.fast_find_nn_pre(ts, m) 30 | assert(n == 8) 31 | 32 | expected_sumx2 = np.array([30, 54, 86, 126, 174]) 33 | assert((sumx2 == expected_sumx2)).all() 34 | 35 | expected_sumx = np.array([10, 14, 18, 22, 26]) 36 | assert((sumx == expected_sumx)).all() 37 | 38 | expected_meanx = np.array([2.5, 3.5, 4.5, 5.5, 6.5]) 39 | assert((meanx == expected_meanx).all()) 40 | 41 | expected_sigmax2 = np.array([1.25, 1.25, 1.25, 1.25, 1.25]) 42 | assert(np.allclose(sigmax2, expected_sigmax2)) 43 | 44 | expected_sigmax = np.array([1.118, 1.118, 1.118, 1.118, 1.118]) 45 | assert(np.allclose(sigmax, expected_sigmax, 1e-02)) 46 | 47 | 48 | def test_calc_exclusion_zone(): 49 | assert(scrimp.calc_exclusion_zone(4) == 1) 50 | 51 | 52 | def test_calc_step_size(): 53 | assert(scrimp.calc_step_size(4, 0.25) == 1) 54 | 55 | 56 | def test_calc_profile_len(): 57 | assert(scrimp.calc_profile_len(8, 4) == 5) 58 | 59 | 60 | def test_time_series_too_short_exception(): 61 | with pytest.raises(ValueError) as excinfo: 62 | scrimp.scrimp_plus_plus([1, 2, 3, 4, 5], 4, 0.25) 63 | assert 'Time series is too short' in str(excinfo.value) 64 | 65 | 66 | def test_window_size_minimum_exception(): 67 | with pytest.raises(ValueError) as excinfo: 68 | scrimp.scrimp_plus_plus([1, 2, 3, 4, 5], 2, 0.25) 69 | assert 'Window size must be at least 4' in str(excinfo.value) 70 | 71 | 72 | def test_invalid_step_size_negative(): 73 | exc = 'step_size should be a float between 0 and 1.' 74 | with pytest.raises(ValueError) as excinfo: 75 | scrimp.scrimp_plus_plus([1, 2, 3, 4, 5], 2, -1) 76 | assert exc in str(excinfo.value) 77 | 78 | 79 | def test_invalid_step_size_str(): 80 | exc = 'step_size should be a float between 0 and 1.' 81 | with pytest.raises(ValueError) as excinfo: 82 | scrimp.scrimp_plus_plus([1, 2, 3, 4, 5], 2, 'a') 83 | assert exc in str(excinfo.value) 84 | 85 | 86 | def test_invalid_step_size_greater(): 87 | exc = 'step_size should be a float between 0 and 1.' 88 | with pytest.raises(ValueError) as excinfo: 89 | scrimp.scrimp_plus_plus([1, 2, 3, 4, 5], 2, 2) 90 | assert exc in str(excinfo.value) 91 | 92 | 93 | def test_invalid_runtime_str(): 94 | exc = 'runtime should be a valid positive integer.' 95 | with pytest.raises(ValueError) as excinfo: 96 | scrimp.scrimp_plus_plus([1, 2, 3, 4, 5], 2, runtime='1') 97 | assert exc in str(excinfo.value) 98 | 99 | 100 | def test_invalid_runtime_zero(): 101 | exc = 'runtime should be a valid positive integer.' 102 | with pytest.raises(ValueError) as excinfo: 103 | scrimp.scrimp_plus_plus([1, 2, 3, 4, 5], 2, runtime=0) 104 | assert exc in str(excinfo.value) 105 | 106 | 107 | def test_invalid_runtime_negative(): 108 | exc = 'runtime should be a valid positive integer.' 109 | with pytest.raises(ValueError) as excinfo: 110 | scrimp.scrimp_plus_plus([1, 2, 3, 4, 5], 2, runtime=-1) 111 | assert exc in str(excinfo.value) 112 | 113 | 114 | def test_invalid_random_state_exception(): 115 | exc = 'Invalid random_state value given.' 116 | with pytest.raises(ValueError) as excinfo: 117 | scrimp.scrimp_plus_plus([1, 2, 3, 4, 5], 2, random_state='adsf') 118 | assert exc in str(excinfo.value) 119 | 120 | 121 | def test_next_subsequence(): 122 | ts = [1, 2, 3, 4, 5, 6, 7, 8] 123 | m = 4 124 | idx = 0 125 | 126 | expected_subsequence = [1, 2, 3, 4] 127 | assert((scrimp.next_subsequence(ts, idx, m) == expected_subsequence)) 128 | 129 | 130 | def test_calc_distance_profile(): 131 | ts = [1, 2, 3, 4, 5, 6, 7, 8] 132 | m = 4 133 | idx = 0 134 | 135 | expected_dp = np.array([ 136 | 4.21468485e-08, 137 | 4.21468485e-08, 138 | 0.00000000e+00, 139 | 4.21468485e-08, 140 | 4.21468485e-08 141 | ]) 142 | 143 | subsequence = scrimp.next_subsequence(ts, idx, m) 144 | X, n, sumx2, sumx, meanx, sigmax2, sigmax = scrimp.fast_find_nn_pre(ts, m) 145 | dp = scrimp.calc_distance_profile(X, subsequence, n, m, meanx, sigmax) 146 | 147 | np.testing.assert_almost_equal(dp, expected_dp) 148 | 149 | # test idx 1 150 | idx = 1 151 | 152 | expected_dp = np.array([ 153 | 4.21468485e-08, 154 | 4.21468485e-08, 155 | 4.21468485e-08, 156 | 4.21468485e-08, 157 | 4.21468485e-08 158 | ]) 159 | 160 | subsequence = scrimp.next_subsequence(ts, idx, m) 161 | X, n, sumx2, sumx, meanx, sigmax2, sigmax = scrimp.fast_find_nn_pre(ts, m) 162 | dp = scrimp.calc_distance_profile(X, subsequence, n, m, meanx, sigmax) 163 | 164 | np.testing.assert_almost_equal(dp, expected_dp) 165 | 166 | 167 | def test_calc_exclusion_start(): 168 | ts = [1, 2, 3, 4, 5, 6, 7, 8] 169 | m = 4 170 | idx = 0 171 | exclusion_zone = scrimp.calc_exclusion_zone(m) 172 | start = scrimp.calc_exclusion_start(idx, exclusion_zone) 173 | expected_start = 0 174 | 175 | assert(expected_start == start) 176 | 177 | idx = 2 178 | start = scrimp.calc_exclusion_start(idx, exclusion_zone) 179 | expected_start = 1 180 | assert(expected_start == start) 181 | 182 | idx = 3 183 | start = scrimp.calc_exclusion_start(idx, exclusion_zone) 184 | expected_start = 2 185 | assert(expected_start == start) 186 | 187 | 188 | def test_calc_exclusion_stop(): 189 | ts = [1, 2, 3, 4, 5, 6, 7, 8] 190 | m = 4 191 | idx = 0 192 | profile_len = scrimp.calc_profile_len(len(ts), m) # 4 193 | exclusion_zone = scrimp.calc_exclusion_zone(m) # 1 194 | stop = scrimp.calc_exclusion_stop(idx, exclusion_zone, profile_len) 195 | expected_stop = 1 196 | assert(expected_stop == stop) 197 | 198 | idx = 2 199 | stop = scrimp.calc_exclusion_stop(idx, exclusion_zone, profile_len) 200 | expected_stop = 3 201 | assert(expected_stop == stop) 202 | 203 | idx = 3 204 | stop = scrimp.calc_exclusion_stop(idx, exclusion_zone, profile_len) 205 | expected_stop = 4 206 | assert(expected_stop == stop) 207 | 208 | idx = 4 209 | stop = scrimp.calc_exclusion_stop(idx, exclusion_zone, profile_len) 210 | expected_stop = 5 211 | assert(expected_stop == stop) 212 | 213 | 214 | def test_apply_exclusion_zone(): 215 | ts = [1, 2, 3, 4, 5, 6, 7, 8] 216 | m = 4 217 | 218 | # test index 0 219 | idx = 0 220 | profile_len = scrimp.calc_profile_len(len(ts), m) # 4 221 | exclusion_zone = scrimp.calc_exclusion_zone(m) # 1 222 | subsequence = scrimp.next_subsequence(ts, idx, m) 223 | X, n, sumx2, sumx, meanx, sigmax2, sigmax = scrimp.fast_find_nn_pre(ts, m) 224 | dp = scrimp.calc_distance_profile(X, subsequence, n, m, meanx, sigmax) 225 | dp = scrimp.apply_exclusion_zone(idx, exclusion_zone, profile_len, dp) 226 | 227 | expected_dp = np.array([ 228 | np.inf, 229 | np.inf, 230 | 0.4215e-07, 231 | 0.4215e-07, 232 | 0.4215e-07, 233 | ]) 234 | 235 | np.testing.assert_almost_equal(dp, expected_dp) 236 | 237 | # test idx 1 238 | idx = 1 239 | subsequence = scrimp.next_subsequence(ts, idx, m) 240 | dp = scrimp.calc_distance_profile(X, subsequence, n, m, meanx, sigmax) 241 | dp = scrimp.apply_exclusion_zone(idx, exclusion_zone, profile_len, dp) 242 | 243 | expected_dp = np.array([ 244 | np.inf, 245 | np.inf, 246 | np.inf, 247 | 0.4215e-07, 248 | 0.4215e-07, 249 | ]) 250 | 251 | np.testing.assert_almost_equal(dp, expected_dp) 252 | 253 | # test idx 2 254 | idx = 2 255 | subsequence = scrimp.next_subsequence(ts, idx, m) 256 | dp = scrimp.calc_distance_profile(X, subsequence, n, m, meanx, sigmax) 257 | dp = scrimp.apply_exclusion_zone(idx, exclusion_zone, profile_len, dp) 258 | 259 | expected_dp = np.array([ 260 | 0.4215e-07, 261 | np.inf, 262 | np.inf, 263 | np.inf, 264 | 0.4215e-07, 265 | ]) 266 | 267 | np.testing.assert_almost_equal(dp, expected_dp) 268 | 269 | # test idx 3 270 | idx = 3 271 | subsequence = scrimp.next_subsequence(ts, idx, m) 272 | dp = scrimp.calc_distance_profile(X, subsequence, n, m, meanx, sigmax) 273 | dp = scrimp.apply_exclusion_zone(idx, exclusion_zone, profile_len, dp) 274 | 275 | expected_dp = np.array([ 276 | 0.1115e-06, 277 | 0.0421e-06, 278 | np.inf, 279 | np.inf, 280 | np.inf, 281 | ]) 282 | 283 | np.testing.assert_almost_equal(dp, expected_dp) 284 | 285 | 286 | def test_find_and_store_nn(): 287 | ts = [1, 2, 3, 4, 5, 6, 7, 8] 288 | m = 4 289 | 290 | # test index 0 291 | idx = 0 292 | profile_len = scrimp.calc_profile_len(len(ts), m) 293 | exclusion_zone = scrimp.calc_exclusion_zone(m) 294 | subsequence = scrimp.next_subsequence(ts, idx, m) 295 | X, n, sumx2, sumx, meanx, sigmax2, sigmax = scrimp.fast_find_nn_pre(ts, m) 296 | dp = scrimp.calc_distance_profile(X, subsequence, n, m, meanx, sigmax) 297 | dp = scrimp.apply_exclusion_zone(idx, exclusion_zone, profile_len, dp) 298 | 299 | mp = np.zeros(profile_len) 300 | mp_index = np.zeros(profile_len, dtype='int32') 301 | 302 | mp, mp_index, idx_nn = scrimp.find_and_store_nn(0, idx, mp, mp_index, dp) 303 | expected_mp = np.array([ 304 | 0.4215e-07, 305 | np.inf, 306 | 0.4215e-07, 307 | 0.4215e-07, 308 | 0.4215e-07, 309 | ]) 310 | expected_idx_nn = 2 311 | expected_mp_index = np.array([ 312 | 2, 313 | 0, 314 | 0, 315 | 0, 316 | 0, 317 | ]) 318 | 319 | np.testing.assert_almost_equal(mp, expected_mp) 320 | np.testing.assert_almost_equal(mp_index, expected_mp_index) 321 | assert(idx_nn == expected_idx_nn) 322 | 323 | # test index 0 324 | idx = 1 325 | subsequence = scrimp.next_subsequence(ts, idx, m) 326 | dp = scrimp.calc_distance_profile(X, subsequence, n, m, meanx, sigmax) 327 | dp = scrimp.apply_exclusion_zone(idx, exclusion_zone, profile_len, dp) 328 | 329 | mp, mp_index, idx_nn = scrimp.find_and_store_nn(1, idx, mp, mp_index, dp) 330 | expected_idx_nn = 3 331 | assert(idx_nn == expected_idx_nn) 332 | 333 | 334 | def test_calc_idx_diff(): 335 | ts = [1, 2, 3, 4, 5, 6, 7, 8] 336 | m = 4 337 | 338 | # test index 0 339 | idx = 0 340 | profile_len = scrimp.calc_profile_len(len(ts), m) 341 | exclusion_zone = scrimp.calc_exclusion_zone(m) 342 | subsequence = scrimp.next_subsequence(ts, idx, m) 343 | X, n, sumx2, sumx, meanx, sigmax2, sigmax = scrimp.fast_find_nn_pre(ts, m) 344 | dp = scrimp.calc_distance_profile(X, subsequence, n, m, meanx, sigmax) 345 | dp = scrimp.apply_exclusion_zone(idx, exclusion_zone, profile_len, dp) 346 | 347 | mp = np.zeros(profile_len) 348 | mp_index = np.zeros(profile_len, dtype='int32') 349 | 350 | mp, mp_index, idx_nn = scrimp.find_and_store_nn(0, idx, mp, mp_index, dp) 351 | 352 | idx_diff = scrimp.calc_idx_diff(idx, idx_nn) 353 | expected_idx_diff = 2 354 | 355 | assert(idx_diff == expected_idx_diff) 356 | 357 | 358 | def test_calc_dotproduct_idx(): 359 | ts = [1, 2, 3, 4, 5, 6, 7, 8] 360 | m = 4 361 | 362 | # test index 0 363 | idx = 0 364 | profile_len = scrimp.calc_profile_len(len(ts), m) 365 | exclusion_zone = scrimp.calc_exclusion_zone(m) 366 | subsequence = scrimp.next_subsequence(ts, idx, m) 367 | X, n, sumx2, sumx, meanx, sigmax2, sigmax = scrimp.fast_find_nn_pre(ts, m) 368 | dp = scrimp.calc_distance_profile(X, subsequence, n, m, meanx, sigmax) 369 | dp = scrimp.apply_exclusion_zone(idx, exclusion_zone, profile_len, dp) 370 | mp = np.zeros(profile_len) 371 | mp_index = np.zeros(profile_len, dtype='int32') 372 | mp, mp_index, idx_nn = scrimp.find_and_store_nn(0, idx, mp, mp_index, dp) 373 | dotproduct = np.zeros(profile_len) 374 | val = scrimp.calc_dotproduct_idx(dotproduct, m, mp, idx, 375 | sigmax, idx_nn, meanx) 376 | expected_val = np.array([50, 0, 0, 0, 0]) 377 | 378 | np.testing.assert_almost_equal(val, expected_val) 379 | 380 | 381 | def test_calc_end_idx(): 382 | end_idx = scrimp.calc_end_idx(5, 0, 1, 2) 383 | expecected_idx = 0 384 | 385 | assert(end_idx == expecected_idx) 386 | 387 | end_idx = scrimp.calc_end_idx(5, 1, 1, 2) 388 | expecected_idx = 1 389 | 390 | assert(end_idx == expecected_idx) 391 | 392 | end_idx = scrimp.calc_end_idx(5, 2, 1, 2) 393 | expecected_idx = 2 394 | 395 | assert(end_idx == expecected_idx) 396 | 397 | 398 | def test_calc_dotproduct_end_idx(): 399 | ts = np.array([1, 2, 3, 4, 5, 6, 7, 8]) 400 | m = 4 401 | 402 | # test index 0 403 | idx = 0 404 | dp = np.array([50, 0, 0, 0, 0]) 405 | endidx = 0 406 | idx_nn = 2 407 | idx_diff = 2 408 | val = scrimp.calc_dotproduct_end_idx(ts, dp, idx, m, endidx, idx_nn, 409 | idx_diff) 410 | expected_val = dp 411 | 412 | np.testing.assert_almost_equal(val, expected_val) 413 | 414 | 415 | def test_calc_refine_distance_end_idx(): 416 | ts = [1, 2, 3, 4, 5, 6, 7, 8] 417 | m = 4 418 | step_size = 0.25 419 | 420 | # test index 0 421 | idx = 0 422 | profile_len = scrimp.calc_profile_len(len(ts), m) 423 | exclusion_zone = scrimp.calc_exclusion_zone(m) 424 | subsequence = scrimp.next_subsequence(ts, idx, m) 425 | X, n, sumx2, sumx, meanx, sigmax2, sigmax = scrimp.fast_find_nn_pre(ts, m) 426 | dp = scrimp.calc_distance_profile(X, subsequence, n, m, meanx, sigmax) 427 | dp = scrimp.apply_exclusion_zone(idx, exclusion_zone, profile_len, dp) 428 | mp = np.zeros(profile_len) 429 | mp_index = np.zeros(profile_len, dtype='int32') 430 | mp, mp_index, idx_nn = scrimp.find_and_store_nn(0, idx, mp, mp_index, dp) 431 | idx_diff = scrimp.calc_idx_diff(idx, idx_nn) 432 | step_size = scrimp.calc_step_size(m, step_size) 433 | endidx = scrimp.calc_end_idx(profile_len, idx, step_size, idx_diff) 434 | refine_distance = np.full(profile_len, np.inf) 435 | result = scrimp.calc_refine_distance_end_idx( 436 | refine_distance, dp, idx, endidx, meanx, sigmax, idx_nn, idx_diff, m) 437 | expected_result = np.array([np.inf, np.inf, np.inf, np.inf, np.inf]) 438 | 439 | np.testing.assert_almost_equal(result, expected_result) 440 | 441 | 442 | def test_calc_begin_idx(): 443 | step_size = 1 444 | idx_diff = 2 445 | 446 | idx = 0 447 | val = scrimp.calc_begin_idx(idx, step_size, idx_diff) 448 | assert(val == 0) 449 | 450 | idx = 1 451 | val = scrimp.calc_begin_idx(idx, step_size, idx_diff) 452 | assert(val == 1) 453 | 454 | 455 | def test_calc_dotproduct_begin_idx(): 456 | ts = [1, 2, 3, 4, 5, 6, 7, 8] 457 | m = 4 458 | 459 | # test index 0 460 | idx = 0 461 | dp = np.array([50, 0, 0, 0, 0]) 462 | beginidx = 0 463 | idx_nn = 2 464 | idx_diff = 2 465 | val = scrimp.calc_dotproduct_begin_idx( 466 | ts, dp, beginidx, idx, idx_diff, m, idx_nn) 467 | expected_val = np.array([50, 0, 0, 0, 0]) 468 | 469 | np.testing.assert_almost_equal(val, expected_val) 470 | 471 | 472 | def test_calc_refine_distance_begin_idx(): 473 | ts = [1, 2, 3, 4, 5, 6, 7, 8] 474 | m = 4 475 | step_size = 0.25 476 | 477 | # test index 0 478 | idx = 0 479 | profile_len = scrimp.calc_profile_len(len(ts), m) 480 | exclusion_zone = scrimp.calc_exclusion_zone(m) 481 | subsequence = scrimp.next_subsequence(ts, idx, m) 482 | X, n, sumx2, sumx, meanx, sigmax2, sigmax = scrimp.fast_find_nn_pre(ts, m) 483 | dp = scrimp.calc_distance_profile(X, subsequence, n, m, meanx, sigmax) 484 | dp = scrimp.apply_exclusion_zone(idx, exclusion_zone, profile_len, dp) 485 | mp = np.zeros(profile_len) 486 | mp_index = np.zeros(profile_len, dtype='int32') 487 | mp, mp_index, idx_nn = scrimp.find_and_store_nn(0, idx, mp, mp_index, dp) 488 | idx_diff = scrimp.calc_idx_diff(idx, idx_nn) 489 | step_size = scrimp.calc_step_size(m, step_size) 490 | beginidx = scrimp.calc_begin_idx(idx, step_size, idx_diff) 491 | 492 | refine_distance = np.full(profile_len, np.inf) 493 | result = scrimp.calc_refine_distance_begin_idx( 494 | refine_distance, dp, beginidx, idx, idx_diff, idx_nn, sigmax, meanx, m) 495 | expected_result = np.array([np.inf, np.inf, np.inf, np.inf, np.inf]) 496 | 497 | np.testing.assert_almost_equal(result, expected_result) 498 | 499 | 500 | def test_calc_curlastz(): 501 | ts = np.array([1, 2, 3, 4, 5, 6, 7, 8]) 502 | m = 4 503 | n = len(ts) 504 | profile_len = scrimp.calc_profile_len(n, m) 505 | 506 | # test index 2 507 | idx = 2 508 | curlastz = np.zeros(profile_len) 509 | curlastz = scrimp.calc_curlastz(ts, m, n, idx, profile_len, curlastz) 510 | expected_result = np.array([0, 0, 50, 82, 122]) 511 | 512 | np.testing.assert_almost_equal(curlastz, expected_result) 513 | 514 | # test index 3 515 | idx = 3 516 | curlastz = np.zeros(profile_len) 517 | curlastz = scrimp.calc_curlastz(ts, m, n, idx, profile_len, curlastz) 518 | expected_result = np.array([0, 0, 0, 60, 96]) 519 | 520 | np.testing.assert_almost_equal(curlastz, expected_result) 521 | 522 | 523 | def test_calc_curdistance(): 524 | ts = np.array([1, 2, 3, 4, 5, 6, 7, 8]) 525 | m = 4 526 | 527 | # test index 2 528 | idx = 2 529 | profile_len = scrimp.calc_profile_len(len(ts), m) 530 | X, n, sumx2, sumx, meanx, sigmax2, sigmax = scrimp.fast_find_nn_pre(ts, m) 531 | curlastz = np.zeros(profile_len) 532 | curlastz = scrimp.calc_curlastz(ts, m, n, idx, profile_len, curlastz) 533 | 534 | curdistance = np.zeros(profile_len) 535 | curdistance = scrimp.calc_curdistance(curlastz, meanx, sigmax, idx, 536 | profile_len, m, curdistance) 537 | 538 | expected_result = np.array([ 539 | 0, 540 | 0, 541 | 0.4215e-07, 542 | 0.4215e-07, 543 | 0.4215e-07, 544 | ]) 545 | 546 | np.testing.assert_almost_equal(curdistance, expected_result) 547 | 548 | # test index 3 549 | idx = 3 550 | curlastz = np.zeros(profile_len) 551 | curlastz = scrimp.calc_curlastz(ts, m, n, idx, profile_len, curlastz) 552 | curdistance = np.zeros(profile_len) 553 | curdistance = scrimp.calc_curdistance(curlastz, meanx, sigmax, idx, 554 | profile_len, m, curdistance) 555 | 556 | np.testing.assert_almost_equal(curdistance, expected_result) 557 | 558 | 559 | def test_scrimp_plus_plus(): 560 | ts = np.array([0, 0, 1, 0, 0, 0, 1, 0]) 561 | m = 4 562 | step_size = 0.25 563 | mp, mpidx = scrimp.scrimp_plus_plus(ts, m, step_size) 564 | 565 | expected_mpidx = np.array([ 566 | 4, 567 | 3, 568 | 0, 569 | 0, 570 | 0, 571 | ]) 572 | 573 | np.testing.assert_almost_equal(mpidx, expected_mpidx) 574 | 575 | ts = np.loadtxt(os.path.join(MODULE_PATH, '..', 'tests', 'sampledata.txt')) 576 | m = 32 577 | step_size = 0.25 578 | mp, mpidx = scrimp.scrimp_plus_plus(ts, m, step_size) 579 | expected_mp = np.loadtxt(os.path.join(MODULE_PATH, '..', 'tests', 'mp.txt')) 580 | 581 | np.testing.assert_almost_equal(mp, expected_mp, decimal=4) 582 | 583 | 584 | def test_runtime_exceeded_warns(): 585 | ts = np.arange(2**18) 586 | m = 2**8 587 | runtime = 1 588 | 589 | warn_text = 'Max runtime exceeded. Approximate solution is given.' 590 | with pytest.warns(RuntimeWarning, match=warn_text): 591 | scrimp.scrimp_plus_plus(ts, m, runtime=runtime) -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | from matrixprofile.utils import * 2 | import numpy as np 3 | import pytest 4 | 5 | 6 | class TestClass(object): 7 | 8 | def test_zNormalize_zero_error(self): 9 | with pytest.raises(ValueError): 10 | zNormalize(np.array([2.0, 2.0, 2.0])) 11 | 12 | def test_zNormalize(self): 13 | outcome = np.array([-1.0, 1.0, 1.0, -1.0]) 14 | assert np.allclose(zNormalize(np.array([0.0, 1.0, 1.0, 0.0])), outcome) 15 | 16 | def test_zNormalizeEuclidian_length_error(self): 17 | with pytest.raises(ValueError): 18 | zNormalizeEuclidian(np.array([1, 2, 3]), np.array([1, 2])) 19 | 20 | def test_zNormalizeEuclidian(self): 21 | a = np.array([0.0, 1.0, 1.0, 0.0]) 22 | b = np.array([1.0, 2.0, 1.0, 2.0]) 23 | 24 | assert np.round(zNormalizeEuclidian(a, b), 25 | 4) == np.round(2.0 * np.sqrt(2.0), 4) 26 | 27 | def test_movmeanstd_mean(self): 28 | a = np.array([1.0, 2.0, 4.0, 8.0]) 29 | m = 2 30 | 31 | assert np.allclose(movmeanstd(a, m)[0], np.array([1.5, 3.0, 6.0])) 32 | 33 | def test_movmeanstd_std(self): 34 | a = np.array([1.0, 2.0, 4.0, 8.0]) 35 | m = 2 36 | 37 | assert np.allclose(movmeanstd(a, m)[1], np.array([0.5, 1.0, 2.0])) 38 | 39 | def test_movstd(self): 40 | a = np.array([1.0, 2.0, 4.0, 8.0]) 41 | m = 2 42 | 43 | assert np.allclose(movstd(a, m), np.array([0.5, 1.0, 2.0])) 44 | 45 | def test_slidingDotProduct(self): 46 | a = np.array([1.0, 2.0]) 47 | b = np.array([1.0, 2.0, 3.0, 4.0]) 48 | 49 | outcome = np.array([5.0, 8.0, 11.0]) 50 | 51 | assert np.allclose(slidingDotProduct(a, b), outcome) 52 | 53 | def test_slidingDotProduct_odd_ts(self): # Good 54 | a = np.array([1.0, 2.0]) 55 | b = np.array([1.0, 2.0, 3.0]) 56 | 57 | outcome = np.array([5.0, 8.0]) 58 | 59 | assert np.allclose(slidingDotProduct(a, b), outcome) 60 | 61 | def test_slidingDotProduct_odd_query(self): 62 | a = np.array([1.0, 2.0, 3.0]) 63 | b = np.array([1.0, 2.0, 3.0, 4.0]) 64 | 65 | outcome = np.array([14.0, 20.0]) 66 | 67 | assert np.allclose(slidingDotProduct(a, b), outcome) 68 | 69 | def test_slidingDotProduct_odd_both(self): 70 | 71 | a = np.array([1.0, 2.0, 3.0]) 72 | b = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) 73 | 74 | outcome = np.array([14., 20., 26.]) 75 | 76 | assert np.allclose(slidingDotProduct(a, b), outcome) 77 | 78 | def test_DotProductStomp(self): 79 | 80 | ts = np.array([1.0, 2.0, 3.0]) 81 | m = 2 82 | dot_first = np.array([5., 8.]) 83 | dot_prev = np.array([5., 8.]) 84 | order = 1 85 | 86 | outcome = np.array([8., 13.]) 87 | 88 | assert np.allclose(DotProductStomp( 89 | ts, m, dot_first, dot_prev, order), outcome) 90 | 91 | def test_mass(self): 92 | a = np.array([0.0, 1.0, 1.0, 0.0]) 93 | b = np.array([0.0, 4.0, 4.0, 0.0]) 94 | 95 | outcome = np.array([0.0]) 96 | 97 | assert np.allclose(np.sqrt(mass(a, b)), outcome) 98 | 99 | def test_massStomp(self): 100 | query = np.array([2., 1.]) 101 | ts = np.array([1., 2., 1.]) 102 | m = 2 103 | dot_first = np.array([5., 4.]) 104 | dot_prev = np.array([5., 4.]) 105 | index = 1 106 | mean = np.array([1.5, 1.5]) 107 | std = np.array([0.5, 0.5]) 108 | 109 | outcome = np.array([2.82842712, 0.]) 110 | 111 | mass, dot = massStomp(query, ts, dot_first, dot_prev, index, mean, std) 112 | 113 | assert np.allclose(np.sqrt(mass), outcome) 114 | 115 | def test_apply_av(self): 116 | a = [np.array([1.0, 2.0, 1.0, 2.0]), np.array([0.0, 0.0, 0.0, 0.0])] 117 | av = np.array([0.0, 1.0, 1.0, 0.0]) 118 | 119 | outcome = (np.array([3., 2., 1., 4.]), np.array([0., 0., 0., 0.])) 120 | assert np.allclose(apply_av(a, av), outcome) 121 | 122 | def test_apply_av_length_error(self): 123 | a = [np.array([1.0, 2.0, 1.0, 2.0]), np.array([0.0, 0.0, 0.0, 0.0])] 124 | av = np.array([2.0, 1.0, 2.0]) 125 | 126 | with pytest.raises(ValueError): 127 | apply_av(a, av) 128 | 129 | def test_self_join_tsb_none(self): 130 | tsA = np.array([1, 2, 3]) 131 | tsB = None 132 | 133 | assert(is_self_join(tsA, tsB) == True) 134 | 135 | def test_self_join_tsb_different_size(self): 136 | tsA = np.array([1, 2, 3]) 137 | tsB = np.array([1]) 138 | 139 | assert(is_self_join(tsA, tsB) == False) 140 | 141 | def test_self_join_tsb_same(self): 142 | tsA = np.array([1, 2, 3]) 143 | tsB = np.array([1, 2, 3]) 144 | 145 | assert(is_self_join(tsA, tsB) == True) 146 | --------------------------------------------------------------------------------