├── .github └── workflows │ ├── ci.yml │ └── cron.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── README.rst ├── convert_README_to_RST.sh ├── docs ├── .buildinfo ├── .nojekyll ├── _sources │ ├── about.rst.txt │ ├── examples.rst.txt │ ├── how_it_works.rst.txt │ ├── index.rst.txt │ ├── installation.rst.txt │ ├── license.rst.txt │ ├── modules.rst.txt │ ├── pwlf.rst.txt │ ├── requirements.rst.txt │ └── stubs │ │ └── pwlf.PiecewiseLinFit.rst.txt ├── _static │ ├── alabaster.css │ ├── basic.css │ ├── custom.css │ ├── doctools.js │ ├── documentation_options.js │ ├── favicon.ico │ ├── file.png │ ├── language_data.js │ ├── minus.png │ ├── plus.png │ ├── pygments.css │ ├── searchtools.js │ └── sphinx_highlight.js ├── about.html ├── examples.html ├── genindex.html ├── how_it_works.html ├── index.html ├── installation.html ├── license.html ├── modules.html ├── objects.inv ├── pwlf.html ├── requirements.html ├── search.html ├── searchindex.js └── stubs │ └── pwlf.PiecewiseLinFit.html ├── doctrees ├── about.doctree ├── environment.pickle ├── examples.doctree ├── how_it_works.doctree ├── index.doctree ├── installation.doctree ├── license.doctree ├── modules.doctree ├── pwlf.doctree ├── requirements.doctree └── stubs │ └── pwlf.PiecewiseLinFit.doctree ├── examples ├── EGO_integer_only.ipynb ├── README.md ├── README.rst ├── ex_data │ └── saved_parameters.npy ├── examplePiecewiseFit.png ├── experiment_with_batch_process.py.ipynb ├── figs │ ├── badfit.png │ ├── fit_breaks.png │ ├── fitfast.png │ ├── force.png │ ├── multi_degree.png │ ├── numberoflines.png │ ├── sin_en_net_fit.png │ └── true_pwlf.png ├── fitForSpecifiedNumberOfLineSegments.py ├── fitForSpecifiedNumberOfLineSegments_passDiffEvoKeywords.py ├── fitForSpecifiedNumberOfLineSegments_standard_deviation.py ├── fitWithKnownLineSegmentLocations.py ├── fit_begin_and_end.py ├── min_length_demo.ipynb ├── mixed_degree.py ├── mixed_degree_forcing_slope.py ├── model_persistence.py ├── model_persistence_prediction.py ├── prediction_variance.py ├── prediction_variance_degree2.py ├── robust_regression.py ├── run_opt_to_find_best_number_of_line_segments.py ├── sinWaveFit.png ├── sinWaveFit16.png ├── sineWave.py ├── sineWave_custom_opt_bounds.py ├── sineWave_degrees.py ├── sineWave_time_compare.py ├── slope_constraint_demo.ipynb ├── stack_overflow_example.py ├── standard_errrors_and_p-values.py ├── standard_errrors_and_p-values_non-linear.py ├── test0.py ├── test_for_model_significance.py ├── test_if_breaks_exact.py ├── tf │ ├── fit_begin_and_end.py │ ├── sine_benchmark_fixed_20_break_points.py │ ├── sine_benchmark_six_segments.py │ └── test_fit.py ├── understanding_higher_degrees │ ├── degree2_five_segment_equation.pdf │ ├── example_five_degree2_segments.png │ └── polynomials_in_pwlf.ipynb ├── useCustomOptimizationRoutine.py ├── weighted_least_squares_ex.py ├── weighted_least_squares_ex_stats.py └── weighted_least_squares_example.png ├── paper ├── pwlf_Jekel_Venter_v1.pdf ├── pwlf_Jekel_Venter_v2.pdf └── pwlf_Jekel_rev02_draft.pdf ├── pwlf ├── __init__.py ├── pwlf.py └── version.py ├── setup.py ├── sphinx_build_script.sh ├── sphinxdocs ├── Makefile ├── make.bat └── source │ ├── about.rst │ ├── conf.py │ ├── examples.rst │ ├── favicon.ico │ ├── how_it_works.rst │ ├── index.rst │ ├── installation.rst │ ├── license.rst │ ├── modules.rst │ ├── pwlf.rst │ └── requirements.rst └── tests ├── __init__.py └── tests.py /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: pwlf ci 2 | 3 | on: 4 | push: 5 | 6 | jobs: 7 | build: 8 | 9 | runs-on: ubuntu-22.04 10 | strategy: 11 | matrix: 12 | python-version: ['3.10', '3.11', '3.12', '3.13'] 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install flake8 coverage pytest pytest-cov 23 | - name: Install pwlf 24 | run: | 25 | python -m pip install . --no-cache-dir 26 | - name: Lint with flake8 27 | run: | 28 | flake8 pwlf 29 | flake8 tests/tests.py 30 | - name: Test with pytest 31 | run: | 32 | pytest --cov=pwlf --cov-report=xml -p no:warnings tests/tests.py 33 | - name: Upload coverage to Codecov 34 | uses: codecov/codecov-action@v1 35 | with: 36 | token: ${{ secrets.CODECOV_TOKEN }} 37 | file: ./coverage.xml 38 | directory: ./coverage/reports/ 39 | flags: unittests 40 | env_vars: OS,PYTHON 41 | name: codecov-umbrella 42 | fail_ci_if_error: false 43 | verbose: false 44 | -------------------------------------------------------------------------------- /.github/workflows/cron.yml: -------------------------------------------------------------------------------- 1 | name: pwlf cron 2 | 3 | on: 4 | schedule: 5 | # Also run every 24 hours 6 | - cron: '* */24 * * *' 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-22.04 12 | strategy: 13 | matrix: 14 | python-version: ['3.10', '3.11', '3.12', '3.13'] 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Set up Python ${{ matrix.python-version }} 19 | uses: actions/setup-python@v2 20 | with: 21 | python-version: ${{ matrix.python-version }} 22 | - name: Install dependencies 23 | run: | 24 | python -m pip install flake8 coverage pytest pytest-cov 25 | - name: Install pwlf 26 | run: | 27 | python -m pip install . --no-cache-dir 28 | - name: Lint with flake8 29 | run: | 30 | flake8 pwlf 31 | - name: Test with pytest 32 | run: | 33 | pytest --cov=pwlf --cov-report=xml -p no:warnings tests/tests.py 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .coverage 3 | MANIFEST 4 | /build/ 5 | setup.cfg 6 | # Setuptools distribution folder. 7 | /dist/ 8 | 9 | # Python egg metadata, regenerated from source files by setuptools. 10 | /*.egg-info 11 | 12 | # pip stuff 13 | /temp/ 14 | 15 | # vscode folder 16 | .vscode* 17 | 18 | # vim temp file 19 | *~ 20 | *.swp 21 | *.un~ 22 | 23 | # temporary documentation folder 24 | tempdocs 25 | tempdocs/* 26 | sphinxdocs/build 27 | sphinxdocs/build/* 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2022 Charles Jekel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.rst 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | A library for fitting continuous piecewise linear functions to data. Just specify the number of line segments you desire and provide the data. 3 | 4 | ![Downloads a month](https://img.shields.io/pypi/dm/pwlf.svg) ![pwlf ci](https://github.com/cjekel/piecewise_linear_fit_py/workflows/pwlf%20ci/badge.svg) [![codecov](https://codecov.io/gh/cjekel/piecewise_linear_fit_py/branch/master/graph/badge.svg?token=AgeDFEQXed)](https://codecov.io/gh/cjekel/piecewise_linear_fit_py) ![PyPI version](https://img.shields.io/pypi/v/pwlf) [![Conda](https://img.shields.io/conda/vn/conda-forge/pwlf)](https://anaconda.org/conda-forge/pwlf) 5 | 6 | Check out the [documentation](https://jekel.me/piecewise_linear_fit_py)! 7 | 8 | Read the [blog post](http://jekel.me/2017/Fit-a-piecewise-linear-function-to-data/). 9 | 10 | ![Example of a continuous piecewise linear fit to data.](https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/master/examples/examplePiecewiseFit.png) 11 | 12 | ![Example of a continuous piecewise linear fit to a sine wave.](https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/master/examples/sinWaveFit.png) 13 | 14 | Now you can perform segmented constant fitting and piecewise polynomials! 15 | ![Example of multiple degree fits to a sine wave.](https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/master/examples/figs/multi_degree.png) 16 | 17 | # Features 18 | For a specified number of line segments, you can determine (and predict from) the optimal continuous piecewise linear function f(x). See [this example](https://github.com/cjekel/piecewise_linear_fit_py/blob/master/examples/fitForSpecifiedNumberOfLineSegments.py). 19 | 20 | You can fit and predict a continuous piecewise linear function f(x) if you know the specific x locations where the line segments terminate. See [this example](https://github.com/cjekel/piecewise_linear_fit_py/blob/master/examples/fitWithKnownLineSegmentLocations.py). 21 | 22 | If you want to pass different keywords for the SciPy differential evolution algorithm see [this example](https://github.com/cjekel/piecewise_linear_fit_py/blob/master/examples/fitForSpecifiedNumberOfLineSegments_passDiffEvoKeywords.py). 23 | 24 | You can use a different optimization algorithm to find the optimal location for line segments by using the objective function that minimizes the sum of square of residuals. See [this example](https://github.com/cjekel/piecewise_linear_fit_py/blob/master/examples/useCustomOptimizationRoutine.py). 25 | 26 | Instead of using differential evolution, you can now use a multi-start gradient optimization with fitfast() function. You can specify the number of starting points to use. The default is 2. This means that a latin hyper cube sampling (space filling DOE) of 2 is used to run 2 L-BFGS-B optimizations. See [this example](https://github.com/cjekel/piecewise_linear_fit_py/blob/master/examples/sineWave_time_compare.py) which runs fit() function, then runs the fitfast() to compare the runtime differences! 27 | 28 | # Installation 29 | 30 | ## Python Package Index (PyPI) 31 | 32 | You can now install with pip. 33 | ``` 34 | python -m pip install pwlf 35 | ``` 36 | 37 | ## Conda 38 | 39 | If you have conda, you can also install from conda-forge. 40 | ``` 41 | conda install -c conda-forge pwlf 42 | ``` 43 | 44 | ## From source 45 | 46 | Or clone the repo 47 | ``` 48 | git clone https://github.com/cjekel/piecewise_linear_fit_py.git 49 | ``` 50 | 51 | then install with pip 52 | ``` 53 | python -m pip install ./piecewise_linear_fit_py 54 | ``` 55 | 56 | # How it works 57 | This [paper](https://github.com/cjekel/piecewise_linear_fit_py/raw/master/paper/pwlf_Jekel_Venter_v2.pdf) explains how this library works in detail. 58 | 59 | This is based on a formulation of a piecewise linear least squares fit, where the user must specify the location of break points. See [this post](http://jekel.me/2018/Continous-piecewise-linear-regression/) which goes through the derivation of a least squares regression problem if the break point locations are known. Alternatively check out [Golovchenko (2004)](http://golovchenko.org/docs/ContinuousPiecewiseLinearFit.pdf). 60 | 61 | Global optimization is used to find the best location for the user defined number of line segments. I specifically use the [differential evolution](https://docs.scipy.org/doc/scipy-0.17.0/reference/generated/scipy.optimize.differential_evolution.html) algorithm in SciPy. I default the differential evolution algorithm to be aggressive, and it is probably overkill for your problem. So feel free to pass your own differential evolution keywords to the library. See [this example](https://github.com/cjekel/piecewise_linear_fit_py/blob/master/examples/fitForSpecifiedNumberOfLineSegments_passDiffEvoKeywords.py). 62 | 63 | # Changelog 64 | All changes now stored in [CHANGELOG.md](https://github.com/cjekel/piecewise_linear_fit_py/blob/master/CHANGELOG.md) 65 | 66 | New ```weights=``` keyword allows you to perform weighted pwlf fits! Removed TensorFlow code which can now be found [here](https://github.com/cjekel/piecewise_linear_fit_py_tf). 67 | 68 | # Requirements 69 | 70 | NumPy >= 1.14.0 71 | 72 | SciPy >= 1.8.0 73 | 74 | 75 | # License 76 | MIT License 77 | 78 | # Citation 79 | 80 | ```bibtex 81 | @Manual{pwlf, 82 | author = {Jekel, Charles F. and Venter, Gerhard}, 83 | title = {{pwlf:} A Python Library for Fitting 1D Continuous Piecewise Linear Functions}, 84 | year = {2019}, 85 | url = {https://github.com/cjekel/piecewise_linear_fit_py} 86 | } 87 | ``` -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | 4 | A library for fitting continuous piecewise linear functions to data. 5 | Just specify the number of line segments you desire and provide the 6 | data. 7 | 8 | |Downloads a month| |pwlf ci| |codecov| |PyPI version| |Conda| 9 | 10 | Check out the 11 | `documentation `__! 12 | 13 | Read the `blog 14 | post `__. 15 | 16 | .. figure:: https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/master/examples/examplePiecewiseFit.png 17 | :alt: Example of a continuous piecewise linear fit to data. 18 | 19 | Example of a continuous piecewise linear fit to data. 20 | 21 | .. figure:: https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/master/examples/sinWaveFit.png 22 | :alt: Example of a continuous piecewise linear fit to a sine wave. 23 | 24 | Example of a continuous piecewise linear fit to a sine wave. 25 | 26 | Now you can perform segmented constant fitting and piecewise 27 | polynomials! |Example of multiple degree fits to a sine wave.| 28 | 29 | Features 30 | ======== 31 | 32 | For a specified number of line segments, you can determine (and predict 33 | from) the optimal continuous piecewise linear function f(x). See `this 34 | example `__. 35 | 36 | You can fit and predict a continuous piecewise linear function f(x) if 37 | you know the specific x locations where the line segments terminate. See 38 | `this 39 | example `__. 40 | 41 | If you want to pass different keywords for the SciPy differential 42 | evolution algorithm see `this 43 | example `__. 44 | 45 | You can use a different optimization algorithm to find the optimal 46 | location for line segments by using the objective function that 47 | minimizes the sum of square of residuals. See `this 48 | example `__. 49 | 50 | Instead of using differential evolution, you can now use a multi-start 51 | gradient optimization with fitfast() function. You can specify the 52 | number of starting points to use. The default is 2. This means that a 53 | latin hyper cube sampling (space filling DOE) of 2 is used to run 2 54 | L-BFGS-B optimizations. See `this 55 | example `__ 56 | which runs fit() function, then runs the fitfast() to compare the 57 | runtime differences! 58 | 59 | Installation 60 | ============ 61 | 62 | Python Package Index (PyPI) 63 | --------------------------- 64 | 65 | You can now install with pip. 66 | 67 | :: 68 | 69 | python -m pip install pwlf 70 | 71 | Conda 72 | ----- 73 | 74 | If you have conda, you can also install from conda-forge. 75 | 76 | :: 77 | 78 | conda install -c conda-forge pwlf 79 | 80 | From source 81 | ----------- 82 | 83 | Or clone the repo 84 | 85 | :: 86 | 87 | git clone https://github.com/cjekel/piecewise_linear_fit_py.git 88 | 89 | then install with pip 90 | 91 | :: 92 | 93 | python -m pip install ./piecewise_linear_fit_py 94 | 95 | How it works 96 | ============ 97 | 98 | This 99 | `paper `__ 100 | explains how this library works in detail. 101 | 102 | This is based on a formulation of a piecewise linear least squares fit, 103 | where the user must specify the location of break points. See `this 104 | post `__ 105 | which goes through the derivation of a least squares regression problem 106 | if the break point locations are known. Alternatively check out 107 | `Golovchenko 108 | (2004) `__. 109 | 110 | Global optimization is used to find the best location for the user 111 | defined number of line segments. I specifically use the `differential 112 | evolution `__ 113 | algorithm in SciPy. I default the differential evolution algorithm to be 114 | aggressive, and it is probably overkill for your problem. So feel free 115 | to pass your own differential evolution keywords to the library. See 116 | `this 117 | example `__. 118 | 119 | Changelog 120 | ========= 121 | 122 | All changes now stored in 123 | `CHANGELOG.md `__ 124 | 125 | New ``weights=`` keyword allows you to perform weighted pwlf fits! 126 | Removed TensorFlow code which can now be found 127 | `here `__. 128 | 129 | Requirements 130 | ============ 131 | 132 | NumPy >= 1.14.0 133 | 134 | SciPy >= 1.8.0 135 | 136 | 137 | License 138 | ======= 139 | 140 | MIT License 141 | 142 | Citation 143 | ======== 144 | 145 | .. code:: bibtex 146 | 147 | @Manual{pwlf, 148 | author = {Jekel, Charles F. and Venter, Gerhard}, 149 | title = {{pwlf:} A Python Library for Fitting 1D Continuous Piecewise Linear Functions}, 150 | year = {2019}, 151 | url = {https://github.com/cjekel/piecewise_linear_fit_py} 152 | } 153 | 154 | .. |Downloads a month| image:: https://img.shields.io/pypi/dm/pwlf.svg 155 | .. |pwlf ci| image:: https://github.com/cjekel/piecewise_linear_fit_py/workflows/pwlf%20ci/badge.svg 156 | .. |codecov| image:: https://codecov.io/gh/cjekel/piecewise_linear_fit_py/branch/master/graph/badge.svg?token=AgeDFEQXed 157 | :target: https://codecov.io/gh/cjekel/piecewise_linear_fit_py 158 | .. |PyPI version| image:: https://img.shields.io/pypi/v/pwlf 159 | .. |Conda| image:: https://img.shields.io/conda/vn/conda-forge/pwlf 160 | :target: https://anaconda.org/conda-forge/pwlf 161 | .. |Example of multiple degree fits to a sine wave.| image:: https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/master/examples/figs/multi_degree.png 162 | -------------------------------------------------------------------------------- /convert_README_to_RST.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | pandoc --from=markdown --to=rst --output=README.rst README.md 3 | -------------------------------------------------------------------------------- /docs/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 24f4d9dbb6c120d783c7146d16a6ffbd 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/docs/.nojekyll -------------------------------------------------------------------------------- /docs/_sources/about.rst.txt: -------------------------------------------------------------------------------- 1 | About 2 | ============ 3 | 4 | A library for fitting continuous piecewise linear functions to data. Just specify the number of line segments you desire and provide the data. 5 | 6 | .. figure:: https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/master/examples/examplePiecewiseFit.png 7 | :alt: Example of a continuous piecewise linear fit to data. 8 | 9 | Example of a continuous piecewise linear fit to data. 10 | 11 | All changes now stored in 12 | `CHANGELOG.md `__ 13 | 14 | Please cite pwlf in your publications if it helps your research. 15 | 16 | .. code:: bibtex 17 | 18 | @Manual{pwlf, 19 | author = {Jekel, Charles F. and Venter, Gerhard}, 20 | title = {{pwlf:} A Python Library for Fitting 1D Continuous Piecewise Linear Functions}, 21 | year = {2019}, 22 | url = {https://github.com/cjekel/piecewise_linear_fit_py} 23 | } 24 | -------------------------------------------------------------------------------- /docs/_sources/how_it_works.rst.txt: -------------------------------------------------------------------------------- 1 | How it works 2 | ============ 3 | 4 | This 5 | `paper `__ 6 | explains how this library works in detail. 7 | 8 | This is based on a formulation of a piecewise linear least squares fit, 9 | where the user must specify the location of break points. See `this 10 | post `__ 11 | which goes through the derivation of a least squares regression problem 12 | if the break point locations are known. Alternatively check out 13 | `Golovchenko 14 | (2004) `__. 15 | 16 | Global optimization is used to find the best location for the user 17 | defined number of line segments. I specifically use the `differential 18 | evolution `__ 19 | algorithm in SciPy. I default the differential evolution algorithm to be 20 | aggressive, and it is probably overkill for your problem. So feel free 21 | to pass your own differential evolution keywords to the library. See 22 | `this 23 | example `__. 24 | -------------------------------------------------------------------------------- /docs/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | pwlf: piecewise linear fitting 2 | ================================ 3 | 4 | Fit piecewise linear functions to data! 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | installation 10 | how_it_works 11 | examples 12 | pwlf 13 | about 14 | requirements 15 | license 16 | 17 | Indices and tables 18 | ================== 19 | 20 | * :ref:`genindex` 21 | * :ref:`search` 22 | -------------------------------------------------------------------------------- /docs/_sources/installation.rst.txt: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | Python Package Index (PyPI) 5 | --------------------------- 6 | 7 | You can now install with pip. 8 | 9 | :: 10 | 11 | python -m pip install pwlf 12 | 13 | Conda 14 | ----- 15 | 16 | If you have conda, you can also install from conda-forge. 17 | 18 | :: 19 | 20 | conda install -c conda-forge pwlf 21 | 22 | From source 23 | ----------- 24 | 25 | Or clone the repo 26 | 27 | :: 28 | 29 | git clone https://github.com/cjekel/piecewise_linear_fit_py.git 30 | 31 | then install with pip 32 | 33 | :: 34 | 35 | python -m pip install ./piecewise_linear_fit_py 36 | 37 | -------------------------------------------------------------------------------- /docs/_sources/license.rst.txt: -------------------------------------------------------------------------------- 1 | License 2 | ======= 3 | 4 | MIT License 5 | 6 | Copyright (c) 2017-2020 Charles Jekel 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | -------------------------------------------------------------------------------- /docs/_sources/modules.rst.txt: -------------------------------------------------------------------------------- 1 | pwlf 2 | ==== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | pwlf 8 | -------------------------------------------------------------------------------- /docs/_sources/pwlf.rst.txt: -------------------------------------------------------------------------------- 1 | pwlf package contents 2 | ============ 3 | 4 | .. autosummary:: 5 | :toctree: stubs 6 | 7 | pwlf.PiecewiseLinFit 8 | 9 | .. autoclass:: pwlf.PiecewiseLinFit 10 | :members: 11 | :undoc-members: 12 | :show-inheritance: 13 | -------------------------------------------------------------------------------- /docs/_sources/requirements.rst.txt: -------------------------------------------------------------------------------- 1 | Requirements 2 | ============ 3 | 4 | `NumPy `__ (>= 1.14.0) 5 | 6 | `SciPy `__ (>= 1.2.0) 7 | 8 | `pyDOE `__ ( >= 0.3.8) 9 | -------------------------------------------------------------------------------- /docs/_sources/stubs/pwlf.PiecewiseLinFit.rst.txt: -------------------------------------------------------------------------------- 1 | pwlf.PiecewiseLinFit 2 | ==================== 3 | 4 | .. currentmodule:: pwlf 5 | 6 | .. autoclass:: PiecewiseLinFit 7 | 8 | 9 | .. automethod:: __init__ 10 | 11 | 12 | .. rubric:: Methods 13 | 14 | .. autosummary:: 15 | 16 | ~PiecewiseLinFit.__init__ 17 | ~PiecewiseLinFit.assemble_regression_matrix 18 | ~PiecewiseLinFit.calc_slopes 19 | ~PiecewiseLinFit.conlstsq 20 | ~PiecewiseLinFit.fit 21 | ~PiecewiseLinFit.fit_force_points_opt 22 | ~PiecewiseLinFit.fit_guess 23 | ~PiecewiseLinFit.fit_with_breaks 24 | ~PiecewiseLinFit.fit_with_breaks_force_points 25 | ~PiecewiseLinFit.fit_with_breaks_opt 26 | ~PiecewiseLinFit.fitfast 27 | ~PiecewiseLinFit.lstsq 28 | ~PiecewiseLinFit.p_values 29 | ~PiecewiseLinFit.predict 30 | ~PiecewiseLinFit.prediction_variance 31 | ~PiecewiseLinFit.r_squared 32 | ~PiecewiseLinFit.standard_errors 33 | ~PiecewiseLinFit.use_custom_opt 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /docs/_static/custom.css: -------------------------------------------------------------------------------- 1 | /* This file intentionally left blank. */ 2 | -------------------------------------------------------------------------------- /docs/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Base JavaScript utilities for all Sphinx HTML documentation. 6 | * 7 | * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | "use strict"; 12 | 13 | const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ 14 | "TEXTAREA", 15 | "INPUT", 16 | "SELECT", 17 | "BUTTON", 18 | ]); 19 | 20 | const _ready = (callback) => { 21 | if (document.readyState !== "loading") { 22 | callback(); 23 | } else { 24 | document.addEventListener("DOMContentLoaded", callback); 25 | } 26 | }; 27 | 28 | /** 29 | * Small JavaScript module for the documentation. 30 | */ 31 | const Documentation = { 32 | init: () => { 33 | Documentation.initDomainIndexTable(); 34 | Documentation.initOnKeyListeners(); 35 | }, 36 | 37 | /** 38 | * i18n support 39 | */ 40 | TRANSLATIONS: {}, 41 | PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), 42 | LOCALE: "unknown", 43 | 44 | // gettext and ngettext don't access this so that the functions 45 | // can safely bound to a different name (_ = Documentation.gettext) 46 | gettext: (string) => { 47 | const translated = Documentation.TRANSLATIONS[string]; 48 | switch (typeof translated) { 49 | case "undefined": 50 | return string; // no translation 51 | case "string": 52 | return translated; // translation exists 53 | default: 54 | return translated[0]; // (singular, plural) translation tuple exists 55 | } 56 | }, 57 | 58 | ngettext: (singular, plural, n) => { 59 | const translated = Documentation.TRANSLATIONS[singular]; 60 | if (typeof translated !== "undefined") 61 | return translated[Documentation.PLURAL_EXPR(n)]; 62 | return n === 1 ? singular : plural; 63 | }, 64 | 65 | addTranslations: (catalog) => { 66 | Object.assign(Documentation.TRANSLATIONS, catalog.messages); 67 | Documentation.PLURAL_EXPR = new Function( 68 | "n", 69 | `return (${catalog.plural_expr})` 70 | ); 71 | Documentation.LOCALE = catalog.locale; 72 | }, 73 | 74 | /** 75 | * helper function to focus on search bar 76 | */ 77 | focusSearchBar: () => { 78 | document.querySelectorAll("input[name=q]")[0]?.focus(); 79 | }, 80 | 81 | /** 82 | * Initialise the domain index toggle buttons 83 | */ 84 | initDomainIndexTable: () => { 85 | const toggler = (el) => { 86 | const idNumber = el.id.substr(7); 87 | const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); 88 | if (el.src.substr(-9) === "minus.png") { 89 | el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; 90 | toggledRows.forEach((el) => (el.style.display = "none")); 91 | } else { 92 | el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; 93 | toggledRows.forEach((el) => (el.style.display = "")); 94 | } 95 | }; 96 | 97 | const togglerElements = document.querySelectorAll("img.toggler"); 98 | togglerElements.forEach((el) => 99 | el.addEventListener("click", (event) => toggler(event.currentTarget)) 100 | ); 101 | togglerElements.forEach((el) => (el.style.display = "")); 102 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); 103 | }, 104 | 105 | initOnKeyListeners: () => { 106 | // only install a listener if it is really needed 107 | if ( 108 | !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && 109 | !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS 110 | ) 111 | return; 112 | 113 | document.addEventListener("keydown", (event) => { 114 | // bail for input elements 115 | if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; 116 | // bail with special keys 117 | if (event.altKey || event.ctrlKey || event.metaKey) return; 118 | 119 | if (!event.shiftKey) { 120 | switch (event.key) { 121 | case "ArrowLeft": 122 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; 123 | 124 | const prevLink = document.querySelector('link[rel="prev"]'); 125 | if (prevLink && prevLink.href) { 126 | window.location.href = prevLink.href; 127 | event.preventDefault(); 128 | } 129 | break; 130 | case "ArrowRight": 131 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; 132 | 133 | const nextLink = document.querySelector('link[rel="next"]'); 134 | if (nextLink && nextLink.href) { 135 | window.location.href = nextLink.href; 136 | event.preventDefault(); 137 | } 138 | break; 139 | } 140 | } 141 | 142 | // some keyboard layouts may need Shift to get / 143 | switch (event.key) { 144 | case "/": 145 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; 146 | Documentation.focusSearchBar(); 147 | event.preventDefault(); 148 | } 149 | }); 150 | }, 151 | }; 152 | 153 | // quick alias for translations 154 | const _ = Documentation.gettext; 155 | 156 | _ready(Documentation.init); 157 | -------------------------------------------------------------------------------- /docs/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | const DOCUMENTATION_OPTIONS = { 2 | VERSION: '2.5.1', 3 | LANGUAGE: 'en', 4 | COLLAPSE_INDEX: false, 5 | BUILDER: 'html', 6 | FILE_SUFFIX: '.html', 7 | LINK_SUFFIX: '.html', 8 | HAS_SOURCE: true, 9 | SOURCELINK_SUFFIX: '.txt', 10 | NAVIGATION_WITH_KEYS: false, 11 | SHOW_SEARCH_SUMMARY: true, 12 | ENABLE_SEARCH_SHORTCUTS: true, 13 | }; -------------------------------------------------------------------------------- /docs/_static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/docs/_static/favicon.ico -------------------------------------------------------------------------------- /docs/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/docs/_static/file.png -------------------------------------------------------------------------------- /docs/_static/language_data.js: -------------------------------------------------------------------------------- 1 | /* 2 | * language_data.js 3 | * ~~~~~~~~~~~~~~~~ 4 | * 5 | * This script contains the language-specific data used by searchtools.js, 6 | * namely the list of stopwords, stemmer, scorer and splitter. 7 | * 8 | * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. 9 | * :license: BSD, see LICENSE for details. 10 | * 11 | */ 12 | 13 | var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; 14 | 15 | 16 | /* Non-minified version is copied as a separate JS file, if available */ 17 | 18 | /** 19 | * Porter Stemmer 20 | */ 21 | var Stemmer = function() { 22 | 23 | var step2list = { 24 | ational: 'ate', 25 | tional: 'tion', 26 | enci: 'ence', 27 | anci: 'ance', 28 | izer: 'ize', 29 | bli: 'ble', 30 | alli: 'al', 31 | entli: 'ent', 32 | eli: 'e', 33 | ousli: 'ous', 34 | ization: 'ize', 35 | ation: 'ate', 36 | ator: 'ate', 37 | alism: 'al', 38 | iveness: 'ive', 39 | fulness: 'ful', 40 | ousness: 'ous', 41 | aliti: 'al', 42 | iviti: 'ive', 43 | biliti: 'ble', 44 | logi: 'log' 45 | }; 46 | 47 | var step3list = { 48 | icate: 'ic', 49 | ative: '', 50 | alize: 'al', 51 | iciti: 'ic', 52 | ical: 'ic', 53 | ful: '', 54 | ness: '' 55 | }; 56 | 57 | var c = "[^aeiou]"; // consonant 58 | var v = "[aeiouy]"; // vowel 59 | var C = c + "[^aeiouy]*"; // consonant sequence 60 | var V = v + "[aeiou]*"; // vowel sequence 61 | 62 | var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 63 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 64 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 65 | var s_v = "^(" + C + ")?" + v; // vowel in stem 66 | 67 | this.stemWord = function (w) { 68 | var stem; 69 | var suffix; 70 | var firstch; 71 | var origword = w; 72 | 73 | if (w.length < 3) 74 | return w; 75 | 76 | var re; 77 | var re2; 78 | var re3; 79 | var re4; 80 | 81 | firstch = w.substr(0,1); 82 | if (firstch == "y") 83 | w = firstch.toUpperCase() + w.substr(1); 84 | 85 | // Step 1a 86 | re = /^(.+?)(ss|i)es$/; 87 | re2 = /^(.+?)([^s])s$/; 88 | 89 | if (re.test(w)) 90 | w = w.replace(re,"$1$2"); 91 | else if (re2.test(w)) 92 | w = w.replace(re2,"$1$2"); 93 | 94 | // Step 1b 95 | re = /^(.+?)eed$/; 96 | re2 = /^(.+?)(ed|ing)$/; 97 | if (re.test(w)) { 98 | var fp = re.exec(w); 99 | re = new RegExp(mgr0); 100 | if (re.test(fp[1])) { 101 | re = /.$/; 102 | w = w.replace(re,""); 103 | } 104 | } 105 | else if (re2.test(w)) { 106 | var fp = re2.exec(w); 107 | stem = fp[1]; 108 | re2 = new RegExp(s_v); 109 | if (re2.test(stem)) { 110 | w = stem; 111 | re2 = /(at|bl|iz)$/; 112 | re3 = new RegExp("([^aeiouylsz])\\1$"); 113 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 114 | if (re2.test(w)) 115 | w = w + "e"; 116 | else if (re3.test(w)) { 117 | re = /.$/; 118 | w = w.replace(re,""); 119 | } 120 | else if (re4.test(w)) 121 | w = w + "e"; 122 | } 123 | } 124 | 125 | // Step 1c 126 | re = /^(.+?)y$/; 127 | if (re.test(w)) { 128 | var fp = re.exec(w); 129 | stem = fp[1]; 130 | re = new RegExp(s_v); 131 | if (re.test(stem)) 132 | w = stem + "i"; 133 | } 134 | 135 | // Step 2 136 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; 137 | if (re.test(w)) { 138 | var fp = re.exec(w); 139 | stem = fp[1]; 140 | suffix = fp[2]; 141 | re = new RegExp(mgr0); 142 | if (re.test(stem)) 143 | w = stem + step2list[suffix]; 144 | } 145 | 146 | // Step 3 147 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; 148 | if (re.test(w)) { 149 | var fp = re.exec(w); 150 | stem = fp[1]; 151 | suffix = fp[2]; 152 | re = new RegExp(mgr0); 153 | if (re.test(stem)) 154 | w = stem + step3list[suffix]; 155 | } 156 | 157 | // Step 4 158 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; 159 | re2 = /^(.+?)(s|t)(ion)$/; 160 | if (re.test(w)) { 161 | var fp = re.exec(w); 162 | stem = fp[1]; 163 | re = new RegExp(mgr1); 164 | if (re.test(stem)) 165 | w = stem; 166 | } 167 | else if (re2.test(w)) { 168 | var fp = re2.exec(w); 169 | stem = fp[1] + fp[2]; 170 | re2 = new RegExp(mgr1); 171 | if (re2.test(stem)) 172 | w = stem; 173 | } 174 | 175 | // Step 5 176 | re = /^(.+?)e$/; 177 | if (re.test(w)) { 178 | var fp = re.exec(w); 179 | stem = fp[1]; 180 | re = new RegExp(mgr1); 181 | re2 = new RegExp(meq1); 182 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 183 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) 184 | w = stem; 185 | } 186 | re = /ll$/; 187 | re2 = new RegExp(mgr1); 188 | if (re.test(w) && re2.test(w)) { 189 | re = /.$/; 190 | w = w.replace(re,""); 191 | } 192 | 193 | // and turn initial Y back to y 194 | if (firstch == "y") 195 | w = firstch.toLowerCase() + w.substr(1); 196 | return w; 197 | } 198 | } 199 | 200 | -------------------------------------------------------------------------------- /docs/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/docs/_static/minus.png -------------------------------------------------------------------------------- /docs/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/docs/_static/plus.png -------------------------------------------------------------------------------- /docs/_static/pygments.css: -------------------------------------------------------------------------------- 1 | pre { line-height: 125%; } 2 | td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 3 | span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 4 | td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 5 | span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 6 | .highlight .hll { background-color: #ffffcc } 7 | .highlight { background: #f8f8f8; } 8 | .highlight .c { color: #8f5902; font-style: italic } /* Comment */ 9 | .highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ 10 | .highlight .g { color: #000000 } /* Generic */ 11 | .highlight .k { color: #004461; font-weight: bold } /* Keyword */ 12 | .highlight .l { color: #000000 } /* Literal */ 13 | .highlight .n { color: #000000 } /* Name */ 14 | .highlight .o { color: #582800 } /* Operator */ 15 | .highlight .x { color: #000000 } /* Other */ 16 | .highlight .p { color: #000000; font-weight: bold } /* Punctuation */ 17 | .highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ 18 | .highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ 19 | .highlight .cp { color: #8f5902 } /* Comment.Preproc */ 20 | .highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ 21 | .highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ 22 | .highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ 23 | .highlight .gd { color: #a40000 } /* Generic.Deleted */ 24 | .highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ 25 | .highlight .gr { color: #ef2929 } /* Generic.Error */ 26 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 27 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 28 | .highlight .go { color: #888888 } /* Generic.Output */ 29 | .highlight .gp { color: #745334 } /* Generic.Prompt */ 30 | .highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ 31 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 32 | .highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ 33 | .highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */ 34 | .highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */ 35 | .highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */ 36 | .highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */ 37 | .highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */ 38 | .highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */ 39 | .highlight .ld { color: #000000 } /* Literal.Date */ 40 | .highlight .m { color: #990000 } /* Literal.Number */ 41 | .highlight .s { color: #4e9a06 } /* Literal.String */ 42 | .highlight .na { color: #c4a000 } /* Name.Attribute */ 43 | .highlight .nb { color: #004461 } /* Name.Builtin */ 44 | .highlight .nc { color: #000000 } /* Name.Class */ 45 | .highlight .no { color: #000000 } /* Name.Constant */ 46 | .highlight .nd { color: #888888 } /* Name.Decorator */ 47 | .highlight .ni { color: #ce5c00 } /* Name.Entity */ 48 | .highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ 49 | .highlight .nf { color: #000000 } /* Name.Function */ 50 | .highlight .nl { color: #f57900 } /* Name.Label */ 51 | .highlight .nn { color: #000000 } /* Name.Namespace */ 52 | .highlight .nx { color: #000000 } /* Name.Other */ 53 | .highlight .py { color: #000000 } /* Name.Property */ 54 | .highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */ 55 | .highlight .nv { color: #000000 } /* Name.Variable */ 56 | .highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */ 57 | .highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */ 58 | .highlight .w { color: #f8f8f8 } /* Text.Whitespace */ 59 | .highlight .mb { color: #990000 } /* Literal.Number.Bin */ 60 | .highlight .mf { color: #990000 } /* Literal.Number.Float */ 61 | .highlight .mh { color: #990000 } /* Literal.Number.Hex */ 62 | .highlight .mi { color: #990000 } /* Literal.Number.Integer */ 63 | .highlight .mo { color: #990000 } /* Literal.Number.Oct */ 64 | .highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ 65 | .highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ 66 | .highlight .sc { color: #4e9a06 } /* Literal.String.Char */ 67 | .highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ 68 | .highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ 69 | .highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ 70 | .highlight .se { color: #4e9a06 } /* Literal.String.Escape */ 71 | .highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ 72 | .highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ 73 | .highlight .sx { color: #4e9a06 } /* Literal.String.Other */ 74 | .highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ 75 | .highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ 76 | .highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ 77 | .highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ 78 | .highlight .fm { color: #000000 } /* Name.Function.Magic */ 79 | .highlight .vc { color: #000000 } /* Name.Variable.Class */ 80 | .highlight .vg { color: #000000 } /* Name.Variable.Global */ 81 | .highlight .vi { color: #000000 } /* Name.Variable.Instance */ 82 | .highlight .vm { color: #000000 } /* Name.Variable.Magic */ 83 | .highlight .il { color: #990000 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/_static/sphinx_highlight.js: -------------------------------------------------------------------------------- 1 | /* Highlighting utilities for Sphinx HTML documentation. */ 2 | "use strict"; 3 | 4 | const SPHINX_HIGHLIGHT_ENABLED = true 5 | 6 | /** 7 | * highlight a given string on a node by wrapping it in 8 | * span elements with the given class name. 9 | */ 10 | const _highlight = (node, addItems, text, className) => { 11 | if (node.nodeType === Node.TEXT_NODE) { 12 | const val = node.nodeValue; 13 | const parent = node.parentNode; 14 | const pos = val.toLowerCase().indexOf(text); 15 | if ( 16 | pos >= 0 && 17 | !parent.classList.contains(className) && 18 | !parent.classList.contains("nohighlight") 19 | ) { 20 | let span; 21 | 22 | const closestNode = parent.closest("body, svg, foreignObject"); 23 | const isInSVG = closestNode && closestNode.matches("svg"); 24 | if (isInSVG) { 25 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 26 | } else { 27 | span = document.createElement("span"); 28 | span.classList.add(className); 29 | } 30 | 31 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 32 | const rest = document.createTextNode(val.substr(pos + text.length)); 33 | parent.insertBefore( 34 | span, 35 | parent.insertBefore( 36 | rest, 37 | node.nextSibling 38 | ) 39 | ); 40 | node.nodeValue = val.substr(0, pos); 41 | /* There may be more occurrences of search term in this node. So call this 42 | * function recursively on the remaining fragment. 43 | */ 44 | _highlight(rest, addItems, text, className); 45 | 46 | if (isInSVG) { 47 | const rect = document.createElementNS( 48 | "http://www.w3.org/2000/svg", 49 | "rect" 50 | ); 51 | const bbox = parent.getBBox(); 52 | rect.x.baseVal.value = bbox.x; 53 | rect.y.baseVal.value = bbox.y; 54 | rect.width.baseVal.value = bbox.width; 55 | rect.height.baseVal.value = bbox.height; 56 | rect.setAttribute("class", className); 57 | addItems.push({ parent: parent, target: rect }); 58 | } 59 | } 60 | } else if (node.matches && !node.matches("button, select, textarea")) { 61 | node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); 62 | } 63 | }; 64 | const _highlightText = (thisNode, text, className) => { 65 | let addItems = []; 66 | _highlight(thisNode, addItems, text, className); 67 | addItems.forEach((obj) => 68 | obj.parent.insertAdjacentElement("beforebegin", obj.target) 69 | ); 70 | }; 71 | 72 | /** 73 | * Small JavaScript module for the documentation. 74 | */ 75 | const SphinxHighlight = { 76 | 77 | /** 78 | * highlight the search words provided in localstorage in the text 79 | */ 80 | highlightSearchWords: () => { 81 | if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight 82 | 83 | // get and clear terms from localstorage 84 | const url = new URL(window.location); 85 | const highlight = 86 | localStorage.getItem("sphinx_highlight_terms") 87 | || url.searchParams.get("highlight") 88 | || ""; 89 | localStorage.removeItem("sphinx_highlight_terms") 90 | url.searchParams.delete("highlight"); 91 | window.history.replaceState({}, "", url); 92 | 93 | // get individual terms from highlight string 94 | const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); 95 | if (terms.length === 0) return; // nothing to do 96 | 97 | // There should never be more than one element matching "div.body" 98 | const divBody = document.querySelectorAll("div.body"); 99 | const body = divBody.length ? divBody[0] : document.querySelector("body"); 100 | window.setTimeout(() => { 101 | terms.forEach((term) => _highlightText(body, term, "highlighted")); 102 | }, 10); 103 | 104 | const searchBox = document.getElementById("searchbox"); 105 | if (searchBox === null) return; 106 | searchBox.appendChild( 107 | document 108 | .createRange() 109 | .createContextualFragment( 110 | '" 114 | ) 115 | ); 116 | }, 117 | 118 | /** 119 | * helper function to hide the search marks again 120 | */ 121 | hideSearchWords: () => { 122 | document 123 | .querySelectorAll("#searchbox .highlight-link") 124 | .forEach((el) => el.remove()); 125 | document 126 | .querySelectorAll("span.highlighted") 127 | .forEach((el) => el.classList.remove("highlighted")); 128 | localStorage.removeItem("sphinx_highlight_terms") 129 | }, 130 | 131 | initEscapeListener: () => { 132 | // only install a listener if it is really needed 133 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; 134 | 135 | document.addEventListener("keydown", (event) => { 136 | // bail for input elements 137 | if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; 138 | // bail with special keys 139 | if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; 140 | if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { 141 | SphinxHighlight.hideSearchWords(); 142 | event.preventDefault(); 143 | } 144 | }); 145 | }, 146 | }; 147 | 148 | _ready(() => { 149 | /* Do not call highlightSearchWords() when we are on the search page. 150 | * It will highlight words from the *previous* search query. 151 | */ 152 | if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); 153 | SphinxHighlight.initEscapeListener(); 154 | }); 155 | -------------------------------------------------------------------------------- /docs/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | About — pwlf 2.5.1 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
32 |
33 | 34 | 35 |
36 | 37 |
38 |

About

39 |

A library for fitting continuous piecewise linear functions to data. Just specify the number of line segments you desire and provide the data.

40 |
41 | Example of a continuous piecewise linear fit to data. 42 |
43 |

Example of a continuous piecewise linear fit to data.

44 |

All changes now stored in 45 | CHANGELOG.md

46 |

Please cite pwlf in your publications if it helps your research.

47 |
@Manual{pwlf,
 48 |     author = {Jekel, Charles F. and Venter, Gerhard},
 49 |     title = {{pwlf:} A Python Library for Fitting 1D Continuous Piecewise Linear Functions},
 50 |     year = {2019},
 51 |     url = {https://github.com/cjekel/piecewise_linear_fit_py}
 52 | }
 53 | 
54 |
55 |
56 | 57 | 58 |
59 | 60 |
61 |
62 | 113 |
114 |
115 | 126 | 127 | 128 | 129 | 130 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /docs/genindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Index — pwlf 2.5.1 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
30 | 31 | 32 |
33 | 34 | 35 |

Index

36 | 37 |
38 | _ 39 | | A 40 | | C 41 | | F 42 | | L 43 | | P 44 | | R 45 | | S 46 | | U 47 | 48 |
49 |

_

50 | 51 | 55 |
56 | 57 |

A

58 | 59 | 63 |
64 | 65 |

C

66 | 67 | 71 | 75 |
76 | 77 |

F

78 | 79 | 87 | 97 |
98 | 99 |

L

100 | 101 | 105 |
106 | 107 |

P

108 | 109 | 115 | 121 |
122 | 123 |

R

124 | 125 | 129 |
130 | 131 |

S

132 | 133 | 137 |
138 | 139 |

U

140 | 141 | 145 |
146 | 147 | 148 | 149 |
150 | 151 |
152 |
153 | 202 |
203 |
204 | 212 | 213 | 214 | 215 | 216 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /docs/how_it_works.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | How it works — pwlf 2.5.1 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
32 |
33 | 34 | 35 |
36 | 37 |
38 |

How it works

39 |

This 40 | paper 41 | explains how this library works in detail.

42 |

This is based on a formulation of a piecewise linear least squares fit, 43 | where the user must specify the location of break points. See this 44 | post 45 | which goes through the derivation of a least squares regression problem 46 | if the break point locations are known. Alternatively check out 47 | Golovchenko 48 | (2004).

49 |

Global optimization is used to find the best location for the user 50 | defined number of line segments. I specifically use the differential 51 | evolution 52 | algorithm in SciPy. I default the differential evolution algorithm to be 53 | aggressive, and it is probably overkill for your problem. So feel free 54 | to pass your own differential evolution keywords to the library. See 55 | this 56 | example.

57 |
58 | 59 | 60 |
61 | 62 |
63 |
64 | 115 |
116 |
117 | 128 | 129 | 130 | 131 | 132 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | pwlf: piecewise linear fitting — pwlf 2.5.1 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 93 | 143 |
144 |
145 | 156 | 157 | 158 | 159 | 160 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /docs/installation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Installation — pwlf 2.5.1 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
32 |
33 | 34 | 35 |
36 | 37 |
38 |

Installation

39 |
40 |

Python Package Index (PyPI)

41 |

You can now install with pip.

42 |
python -m pip install pwlf
 43 | 
44 |
45 |
46 |
47 |

Conda

48 |

If you have conda, you can also install from conda-forge.

49 |
conda install -c conda-forge pwlf
 50 | 
51 |
52 |
53 |
54 |

From source

55 |

Or clone the repo

56 |
git clone https://github.com/cjekel/piecewise_linear_fit_py.git
 57 | 
58 |
59 |

then install with pip

60 |
python -m pip install ./piecewise_linear_fit_py
 61 | 
62 |
63 |
64 |
65 | 66 | 67 |
68 | 69 |
70 |
71 | 127 |
128 |
129 | 140 | 141 | 142 | 143 | 144 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /docs/license.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | License — pwlf 2.5.1 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |
32 | 33 | 34 |
35 | 36 |
37 |

License

38 |

MIT License

39 |

Copyright (c) 2017-2020 Charles Jekel

40 |

Permission is hereby granted, free of charge, to any person obtaining a copy 41 | of this software and associated documentation files (the “Software”), to deal 42 | in the Software without restriction, including without limitation the rights 43 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 44 | copies of the Software, and to permit persons to whom the Software is 45 | furnished to do so, subject to the following conditions:

46 |

The above copyright notice and this permission notice shall be included in all 47 | copies or substantial portions of the Software.

48 |

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 49 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 50 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 51 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 52 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 53 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 54 | SOFTWARE.

55 |
56 | 57 | 58 |
59 | 60 |
61 |
62 | 112 |
113 |
114 | 125 | 126 | 127 | 128 | 129 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /docs/modules.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | pwlf — pwlf 2.5.1 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 78 | 127 |
128 |
129 | 140 | 141 | 142 | 143 | 144 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /docs/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/docs/objects.inv -------------------------------------------------------------------------------- /docs/requirements.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Requirements — pwlf 2.5.1 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
32 |
33 | 34 | 35 |
36 | 37 |
38 |

Requirements

39 |

NumPy (>= 1.14.0)

40 |

SciPy (>= 1.2.0)

41 |

pyDOE ( >= 0.3.8)

42 |
43 | 44 | 45 |
46 | 47 |
48 |
49 | 100 |
101 |
102 | 113 | 114 | 115 | 116 | 117 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /docs/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Search — pwlf 2.5.1 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |
36 |
37 | 38 | 39 |
40 | 41 |

Search

42 | 43 | 51 | 52 | 53 |

54 | Searching for multiple words only shows matches that contain 55 | all words. 56 |

57 | 58 | 59 |
60 | 61 | 62 | 63 |
64 | 65 | 66 |
67 | 68 | 69 |
70 | 71 |
72 |
73 | 112 |
113 |
114 | 122 | 123 | 124 | 125 | 126 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /doctrees/about.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/doctrees/about.doctree -------------------------------------------------------------------------------- /doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/doctrees/environment.pickle -------------------------------------------------------------------------------- /doctrees/examples.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/doctrees/examples.doctree -------------------------------------------------------------------------------- /doctrees/how_it_works.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/doctrees/how_it_works.doctree -------------------------------------------------------------------------------- /doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/doctrees/index.doctree -------------------------------------------------------------------------------- /doctrees/installation.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/doctrees/installation.doctree -------------------------------------------------------------------------------- /doctrees/license.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/doctrees/license.doctree -------------------------------------------------------------------------------- /doctrees/modules.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/doctrees/modules.doctree -------------------------------------------------------------------------------- /doctrees/pwlf.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/doctrees/pwlf.doctree -------------------------------------------------------------------------------- /doctrees/requirements.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/doctrees/requirements.doctree -------------------------------------------------------------------------------- /doctrees/stubs/pwlf.PiecewiseLinFit.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/doctrees/stubs/pwlf.PiecewiseLinFit.doctree -------------------------------------------------------------------------------- /examples/ex_data/saved_parameters.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/examples/ex_data/saved_parameters.npy -------------------------------------------------------------------------------- /examples/examplePiecewiseFit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/examples/examplePiecewiseFit.png -------------------------------------------------------------------------------- /examples/figs/badfit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/examples/figs/badfit.png -------------------------------------------------------------------------------- /examples/figs/fit_breaks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/examples/figs/fit_breaks.png -------------------------------------------------------------------------------- /examples/figs/fitfast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/examples/figs/fitfast.png -------------------------------------------------------------------------------- /examples/figs/force.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/examples/figs/force.png -------------------------------------------------------------------------------- /examples/figs/multi_degree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/examples/figs/multi_degree.png -------------------------------------------------------------------------------- /examples/figs/numberoflines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/examples/figs/numberoflines.png -------------------------------------------------------------------------------- /examples/figs/sin_en_net_fit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/examples/figs/sin_en_net_fit.png -------------------------------------------------------------------------------- /examples/figs/true_pwlf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/examples/figs/true_pwlf.png -------------------------------------------------------------------------------- /examples/fitForSpecifiedNumberOfLineSegments.py: -------------------------------------------------------------------------------- 1 | # fit for a specified number of line segments 2 | # you specify the number of line segments you want, the library does the rest 3 | 4 | # import our libraries 5 | import numpy as np 6 | import matplotlib.pyplot as plt 7 | import pwlf 8 | 9 | # your data 10 | y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02, 11 | 4.39052750e-02, 5.45343950e-02, 6.74104940e-02, 12 | 8.34831790e-02, 1.02580042e-01, 1.22767939e-01, 13 | 1.42172312e-01, 0.00000000e+00, 8.58600000e-06, 14 | 8.31543400e-03, 2.34184100e-02, 3.39709150e-02, 15 | 4.03581990e-02, 4.53545600e-02, 5.02345260e-02, 16 | 5.55253360e-02, 6.14750770e-02, 6.82125120e-02, 17 | 7.55892510e-02, 8.38356810e-02, 9.26413070e-02, 18 | 1.02039790e-01, 1.11688258e-01, 1.21390666e-01, 19 | 1.31196948e-01, 0.00000000e+00, 1.56706510e-02, 20 | 3.54628780e-02, 4.63739040e-02, 5.61442590e-02, 21 | 6.78542550e-02, 8.16388310e-02, 9.77756110e-02, 22 | 1.16531753e-01, 1.37038283e-01, 0.00000000e+00, 23 | 1.16951050e-02, 3.12089850e-02, 4.41776550e-02, 24 | 5.42877590e-02, 6.63321350e-02, 8.07655920e-02, 25 | 9.70363280e-02, 1.15706975e-01, 1.36687642e-01, 26 | 0.00000000e+00, 1.50144640e-02, 3.44519970e-02, 27 | 4.55907760e-02, 5.59556700e-02, 6.88450940e-02, 28 | 8.41374060e-02, 1.01254006e-01, 1.20605073e-01, 29 | 1.41881288e-01, 1.62618058e-01]) 30 | x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02, 31 | 5.66106800e-02, 7.95549800e-02, 1.00936330e-01, 32 | 1.20351520e-01, 1.37442010e-01, 1.51858250e-01, 33 | 1.64433570e-01, 0.00000000e+00, -2.12600000e-05, 34 | 7.03872000e-03, 1.85494500e-02, 3.00926700e-02, 35 | 4.17617000e-02, 5.37279600e-02, 6.54941000e-02, 36 | 7.68092100e-02, 8.76596300e-02, 9.80525800e-02, 37 | 1.07961810e-01, 1.17305210e-01, 1.26063930e-01, 38 | 1.34180360e-01, 1.41725010e-01, 1.48629710e-01, 39 | 1.55374770e-01, 0.00000000e+00, 1.65610200e-02, 40 | 3.91016100e-02, 6.18679400e-02, 8.30997400e-02, 41 | 1.02132890e-01, 1.19011260e-01, 1.34620080e-01, 42 | 1.49429370e-01, 1.63539960e-01, -0.00000000e+00, 43 | 1.01980300e-02, 3.28642800e-02, 5.59461900e-02, 44 | 7.81388400e-02, 9.84458400e-02, 1.16270210e-01, 45 | 1.31279040e-01, 1.45437090e-01, 1.59627540e-01, 46 | 0.00000000e+00, 1.63404300e-02, 4.00086000e-02, 47 | 6.34390200e-02, 8.51085900e-02, 1.04787860e-01, 48 | 1.22120350e-01, 1.36931660e-01, 1.50958760e-01, 49 | 1.65299640e-01, 1.79942720e-01]) 50 | 51 | # initialize piecewise linear fit with your x and y data 52 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 53 | 54 | # fit the data for four line segments 55 | res = my_pwlf.fit(4) 56 | 57 | # predict for the determined points 58 | xHat = np.linspace(min(x), max(x), num=10000) 59 | yHat = my_pwlf.predict(xHat) 60 | 61 | # Get the slopes 62 | my_slopes = my_pwlf.slopes 63 | 64 | # Get my model parameters 65 | beta = my_pwlf.beta 66 | 67 | # calculate the standard errors associated with each beta parameter 68 | se = my_pwlf.standard_errors() 69 | 70 | # calcualte the R^2 value 71 | Rsquared = my_pwlf.r_squared() 72 | 73 | # calculate the piecewise R^2 value 74 | R2values = np.zeros(my_pwlf.n_segments) 75 | for i in range(my_pwlf.n_segments): 76 | # segregate the data based on break point locations 77 | xmin = my_pwlf.fit_breaks[i] 78 | xmax = my_pwlf.fit_breaks[i+1] 79 | xtemp = my_pwlf.x_data 80 | ytemp = my_pwlf.y_data 81 | indtemp = np.where(xtemp >= xmin) 82 | xtemp = my_pwlf.x_data[indtemp] 83 | ytemp = my_pwlf.y_data[indtemp] 84 | indtemp = np.where(xtemp <= xmax) 85 | xtemp = xtemp[indtemp] 86 | ytemp = ytemp[indtemp] 87 | 88 | # predict for the new data 89 | yhattemp = my_pwlf.predict(xtemp) 90 | 91 | # calcualte ssr 92 | e = yhattemp - ytemp 93 | ssr = np.dot(e, e) 94 | 95 | # calculate sst 96 | ybar = np.ones(ytemp.size) * np.mean(ytemp) 97 | ydiff = ytemp - ybar 98 | sst = np.dot(ydiff, ydiff) 99 | 100 | R2values[i] = 1.0 - (ssr/sst) 101 | 102 | # plot the results 103 | plt.figure() 104 | plt.plot(x, y, 'o') 105 | plt.plot(xHat, yHat, '-') 106 | plt.show() 107 | -------------------------------------------------------------------------------- /examples/fitForSpecifiedNumberOfLineSegments_passDiffEvoKeywords.py: -------------------------------------------------------------------------------- 1 | # fit for a specified number of line segments 2 | # you specify the number of line segments you want, the library does the rest 3 | # same as fitForSpecifiedNumberOfLineSegments.py, with the exception of 4 | # passing custom keywords directly to the scipy differential evolution algo 5 | # see 6 | # https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.differential_evolution.html 7 | 8 | # import our libraries 9 | import numpy as np 10 | import matplotlib.pyplot as plt 11 | import pwlf 12 | 13 | # your data 14 | y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02, 15 | 4.39052750e-02, 5.45343950e-02, 6.74104940e-02, 16 | 8.34831790e-02, 1.02580042e-01, 1.22767939e-01, 17 | 1.42172312e-01, 0.00000000e+00, 8.58600000e-06, 18 | 8.31543400e-03, 2.34184100e-02, 3.39709150e-02, 19 | 4.03581990e-02, 4.53545600e-02, 5.02345260e-02, 20 | 5.55253360e-02, 6.14750770e-02, 6.82125120e-02, 21 | 7.55892510e-02, 8.38356810e-02, 9.26413070e-02, 22 | 1.02039790e-01, 1.11688258e-01, 1.21390666e-01, 23 | 1.31196948e-01, 0.00000000e+00, 1.56706510e-02, 24 | 3.54628780e-02, 4.63739040e-02, 5.61442590e-02, 25 | 6.78542550e-02, 8.16388310e-02, 9.77756110e-02, 26 | 1.16531753e-01, 1.37038283e-01, 0.00000000e+00, 27 | 1.16951050e-02, 3.12089850e-02, 4.41776550e-02, 28 | 5.42877590e-02, 6.63321350e-02, 8.07655920e-02, 29 | 9.70363280e-02, 1.15706975e-01, 1.36687642e-01, 30 | 0.00000000e+00, 1.50144640e-02, 3.44519970e-02, 31 | 4.55907760e-02, 5.59556700e-02, 6.88450940e-02, 32 | 8.41374060e-02, 1.01254006e-01, 1.20605073e-01, 33 | 1.41881288e-01, 1.62618058e-01]) 34 | x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02, 35 | 5.66106800e-02, 7.95549800e-02, 1.00936330e-01, 36 | 1.20351520e-01, 1.37442010e-01, 1.51858250e-01, 37 | 1.64433570e-01, 0.00000000e+00, -2.12600000e-05, 38 | 7.03872000e-03, 1.85494500e-02, 3.00926700e-02, 39 | 4.17617000e-02, 5.37279600e-02, 6.54941000e-02, 40 | 7.68092100e-02, 8.76596300e-02, 9.80525800e-02, 41 | 1.07961810e-01, 1.17305210e-01, 1.26063930e-01, 42 | 1.34180360e-01, 1.41725010e-01, 1.48629710e-01, 43 | 1.55374770e-01, 0.00000000e+00, 1.65610200e-02, 44 | 3.91016100e-02, 6.18679400e-02, 8.30997400e-02, 45 | 1.02132890e-01, 1.19011260e-01, 1.34620080e-01, 46 | 1.49429370e-01, 1.63539960e-01, -0.00000000e+00, 47 | 1.01980300e-02, 3.28642800e-02, 5.59461900e-02, 48 | 7.81388400e-02, 9.84458400e-02, 1.16270210e-01, 49 | 1.31279040e-01, 1.45437090e-01, 1.59627540e-01, 50 | 0.00000000e+00, 1.63404300e-02, 4.00086000e-02, 51 | 6.34390200e-02, 8.51085900e-02, 1.04787860e-01, 52 | 1.22120350e-01, 1.36931660e-01, 1.50958760e-01, 53 | 1.65299640e-01, 1.79942720e-01]) 54 | 55 | # initialize piecewise linear fit with your x and y data 56 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 57 | 58 | # fit the data for four line segments 59 | # My differential evolutions keywords are tuned to be very aggressive 60 | # it's probably overkill for your application 61 | # so you can pass your custom keywords here 62 | # for example I'll pass the only keyword that I want differential evolution 63 | # to print the iterations with disp=True 64 | # if any keyword is passed, all of my aggressively tuned differential evolution 65 | # keywords reset to the default 66 | res = my_pwlf.fit(4, disp=True) 67 | 68 | # predict for the determined points 69 | xHat = np.linspace(min(x), max(x), num=10000) 70 | yHat = my_pwlf.predict(xHat) 71 | 72 | # plot the results 73 | plt.figure() 74 | plt.plot(x, y, 'o') 75 | plt.plot(xHat, yHat, '-') 76 | plt.show() 77 | -------------------------------------------------------------------------------- /examples/fitForSpecifiedNumberOfLineSegments_standard_deviation.py: -------------------------------------------------------------------------------- 1 | # fit for a specified number of line segments 2 | # you specify the number of line segments you want, the library does the rest 3 | 4 | # import our libraries 5 | import numpy as np 6 | import matplotlib.pyplot as plt 7 | import pwlf 8 | 9 | # your data 10 | y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02, 11 | 4.39052750e-02, 5.45343950e-02, 6.74104940e-02, 12 | 8.34831790e-02, 1.02580042e-01, 1.22767939e-01, 13 | 1.42172312e-01, 0.00000000e+00, 8.58600000e-06, 14 | 8.31543400e-03, 2.34184100e-02, 3.39709150e-02, 15 | 4.03581990e-02, 4.53545600e-02, 5.02345260e-02, 16 | 5.55253360e-02, 6.14750770e-02, 6.82125120e-02, 17 | 7.55892510e-02, 8.38356810e-02, 9.26413070e-02, 18 | 1.02039790e-01, 1.11688258e-01, 1.21390666e-01, 19 | 1.31196948e-01, 0.00000000e+00, 1.56706510e-02, 20 | 3.54628780e-02, 4.63739040e-02, 5.61442590e-02, 21 | 6.78542550e-02, 8.16388310e-02, 9.77756110e-02, 22 | 1.16531753e-01, 1.37038283e-01, 0.00000000e+00, 23 | 1.16951050e-02, 3.12089850e-02, 4.41776550e-02, 24 | 5.42877590e-02, 6.63321350e-02, 8.07655920e-02, 25 | 9.70363280e-02, 1.15706975e-01, 1.36687642e-01, 26 | 0.00000000e+00, 1.50144640e-02, 3.44519970e-02, 27 | 4.55907760e-02, 5.59556700e-02, 6.88450940e-02, 28 | 8.41374060e-02, 1.01254006e-01, 1.20605073e-01, 29 | 1.41881288e-01, 1.62618058e-01]) 30 | x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02, 31 | 5.66106800e-02, 7.95549800e-02, 1.00936330e-01, 32 | 1.20351520e-01, 1.37442010e-01, 1.51858250e-01, 33 | 1.64433570e-01, 0.00000000e+00, -2.12600000e-05, 34 | 7.03872000e-03, 1.85494500e-02, 3.00926700e-02, 35 | 4.17617000e-02, 5.37279600e-02, 6.54941000e-02, 36 | 7.68092100e-02, 8.76596300e-02, 9.80525800e-02, 37 | 1.07961810e-01, 1.17305210e-01, 1.26063930e-01, 38 | 1.34180360e-01, 1.41725010e-01, 1.48629710e-01, 39 | 1.55374770e-01, 0.00000000e+00, 1.65610200e-02, 40 | 3.91016100e-02, 6.18679400e-02, 8.30997400e-02, 41 | 1.02132890e-01, 1.19011260e-01, 1.34620080e-01, 42 | 1.49429370e-01, 1.63539960e-01, -0.00000000e+00, 43 | 1.01980300e-02, 3.28642800e-02, 5.59461900e-02, 44 | 7.81388400e-02, 9.84458400e-02, 1.16270210e-01, 45 | 1.31279040e-01, 1.45437090e-01, 1.59627540e-01, 46 | 0.00000000e+00, 1.63404300e-02, 4.00086000e-02, 47 | 6.34390200e-02, 8.51085900e-02, 1.04787860e-01, 48 | 1.22120350e-01, 1.36931660e-01, 1.50958760e-01, 49 | 1.65299640e-01, 1.79942720e-01]) 50 | 51 | # initialize piecewise linear fit with your x and y data 52 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 53 | 54 | # fit the data for four line segments 55 | res = my_pwlf.fit(4) 56 | 57 | # predict for the determined points 58 | xHat = np.linspace(min(x), max(x), num=10000) 59 | yHat = my_pwlf.predict(xHat) 60 | 61 | # Get the slopes 62 | my_slopes = my_pwlf.slopes 63 | 64 | # Get my model parameters 65 | beta = my_pwlf.beta 66 | 67 | # calculate the standard errors associated with each beta parameter 68 | se = my_pwlf.standard_errors() 69 | 70 | # calculate the sum of the square of the residuals 71 | ssr = my_pwlf.fit_with_breaks(my_pwlf.fit_breaks) 72 | 73 | # calculate the unbiased standard deviation 74 | sigma = np.sqrt(ssr / (my_pwlf.n_data - my_pwlf.n_parameters)) 75 | # sigma can be used as a prediction variance 76 | 77 | # plot the results 78 | plt.figure() 79 | plt.plot(x, y, 'o') 80 | plt.plot(xHat, yHat, '-') 81 | plt.fill_between(xHat, yHat - (sigma*1.96), yHat + (sigma*1.96), alpha=0.1, 82 | color="r") 83 | plt.show() 84 | -------------------------------------------------------------------------------- /examples/fitWithKnownLineSegmentLocations.py: -------------------------------------------------------------------------------- 1 | # fit and predict with known line segment x locations 2 | 3 | # import our libraries 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import pwlf 7 | 8 | # your data 9 | y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02, 10 | 4.39052750e-02, 5.45343950e-02, 6.74104940e-02, 11 | 8.34831790e-02, 1.02580042e-01, 1.22767939e-01, 12 | 1.42172312e-01, 0.00000000e+00, 8.58600000e-06, 13 | 8.31543400e-03, 2.34184100e-02, 3.39709150e-02, 14 | 4.03581990e-02, 4.53545600e-02, 5.02345260e-02, 15 | 5.55253360e-02, 6.14750770e-02, 6.82125120e-02, 16 | 7.55892510e-02, 8.38356810e-02, 9.26413070e-02, 17 | 1.02039790e-01, 1.11688258e-01, 1.21390666e-01, 18 | 1.31196948e-01, 0.00000000e+00, 1.56706510e-02, 19 | 3.54628780e-02, 4.63739040e-02, 5.61442590e-02, 20 | 6.78542550e-02, 8.16388310e-02, 9.77756110e-02, 21 | 1.16531753e-01, 1.37038283e-01, 0.00000000e+00, 22 | 1.16951050e-02, 3.12089850e-02, 4.41776550e-02, 23 | 5.42877590e-02, 6.63321350e-02, 8.07655920e-02, 24 | 9.70363280e-02, 1.15706975e-01, 1.36687642e-01, 25 | 0.00000000e+00, 1.50144640e-02, 3.44519970e-02, 26 | 4.55907760e-02, 5.59556700e-02, 6.88450940e-02, 27 | 8.41374060e-02, 1.01254006e-01, 1.20605073e-01, 28 | 1.41881288e-01, 1.62618058e-01]) 29 | x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02, 30 | 5.66106800e-02, 7.95549800e-02, 1.00936330e-01, 31 | 1.20351520e-01, 1.37442010e-01, 1.51858250e-01, 32 | 1.64433570e-01, 0.00000000e+00, -2.12600000e-05, 33 | 7.03872000e-03, 1.85494500e-02, 3.00926700e-02, 34 | 4.17617000e-02, 5.37279600e-02, 6.54941000e-02, 35 | 7.68092100e-02, 8.76596300e-02, 9.80525800e-02, 36 | 1.07961810e-01, 1.17305210e-01, 1.26063930e-01, 37 | 1.34180360e-01, 1.41725010e-01, 1.48629710e-01, 38 | 1.55374770e-01, 0.00000000e+00, 1.65610200e-02, 39 | 3.91016100e-02, 6.18679400e-02, 8.30997400e-02, 40 | 1.02132890e-01, 1.19011260e-01, 1.34620080e-01, 41 | 1.49429370e-01, 1.63539960e-01, -0.00000000e+00, 42 | 1.01980300e-02, 3.28642800e-02, 5.59461900e-02, 43 | 7.81388400e-02, 9.84458400e-02, 1.16270210e-01, 44 | 1.31279040e-01, 1.45437090e-01, 1.59627540e-01, 45 | 0.00000000e+00, 1.63404300e-02, 4.00086000e-02, 46 | 6.34390200e-02, 8.51085900e-02, 1.04787860e-01, 47 | 1.22120350e-01, 1.36931660e-01, 1.50958760e-01, 48 | 1.65299640e-01, 1.79942720e-01]) 49 | 50 | # your desired line segment end locations 51 | x0 = np.array([min(x), 0.039, 0.10, max(x)]) 52 | 53 | # initialize piecewise linear fit with your x and y data 54 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 55 | 56 | # fit the data with the specified break points (ie the x locations of where 57 | # the line segments should end 58 | my_pwlf.fit_with_breaks(x0) 59 | 60 | # predict for the determined points 61 | xHat = np.linspace(min(x), max(x), num=10000) 62 | yHat = my_pwlf.predict(xHat) 63 | 64 | # plot the results 65 | plt.figure() 66 | plt.plot(x, y, 'o') 67 | plt.plot(xHat, yHat, '-') 68 | plt.show() 69 | -------------------------------------------------------------------------------- /examples/fit_begin_and_end.py: -------------------------------------------------------------------------------- 1 | # fit and predict between a known begging and known ending 2 | 3 | # import our libraries 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import pwlf 7 | from scipy.optimize import differential_evolution 8 | 9 | # your data 10 | y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02, 11 | 4.39052750e-02, 5.45343950e-02, 6.74104940e-02, 12 | 8.34831790e-02, 1.02580042e-01, 1.22767939e-01, 13 | 1.42172312e-01, 0.00000000e+00, 8.58600000e-06, 14 | 8.31543400e-03, 2.34184100e-02, 3.39709150e-02, 15 | 4.03581990e-02, 4.53545600e-02, 5.02345260e-02, 16 | 5.55253360e-02, 6.14750770e-02, 6.82125120e-02, 17 | 7.55892510e-02, 8.38356810e-02, 9.26413070e-02, 18 | 1.02039790e-01, 1.11688258e-01, 1.21390666e-01, 19 | 1.31196948e-01, 0.00000000e+00, 1.56706510e-02, 20 | 3.54628780e-02, 4.63739040e-02, 5.61442590e-02, 21 | 6.78542550e-02, 8.16388310e-02, 9.77756110e-02, 22 | 1.16531753e-01, 1.37038283e-01, 0.00000000e+00, 23 | 1.16951050e-02, 3.12089850e-02, 4.41776550e-02, 24 | 5.42877590e-02, 6.63321350e-02, 8.07655920e-02, 25 | 9.70363280e-02, 1.15706975e-01, 1.36687642e-01, 26 | 0.00000000e+00, 1.50144640e-02, 3.44519970e-02, 27 | 4.55907760e-02, 5.59556700e-02, 6.88450940e-02, 28 | 8.41374060e-02, 1.01254006e-01, 1.20605073e-01, 29 | 1.41881288e-01, 1.62618058e-01]) 30 | x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02, 31 | 5.66106800e-02, 7.95549800e-02, 1.00936330e-01, 32 | 1.20351520e-01, 1.37442010e-01, 1.51858250e-01, 33 | 1.64433570e-01, 0.00000000e+00, -2.12600000e-05, 34 | 7.03872000e-03, 1.85494500e-02, 3.00926700e-02, 35 | 4.17617000e-02, 5.37279600e-02, 6.54941000e-02, 36 | 7.68092100e-02, 8.76596300e-02, 9.80525800e-02, 37 | 1.07961810e-01, 1.17305210e-01, 1.26063930e-01, 38 | 1.34180360e-01, 1.41725010e-01, 1.48629710e-01, 39 | 1.55374770e-01, 0.00000000e+00, 1.65610200e-02, 40 | 3.91016100e-02, 6.18679400e-02, 8.30997400e-02, 41 | 1.02132890e-01, 1.19011260e-01, 1.34620080e-01, 42 | 1.49429370e-01, 1.63539960e-01, -0.00000000e+00, 43 | 1.01980300e-02, 3.28642800e-02, 5.59461900e-02, 44 | 7.81388400e-02, 9.84458400e-02, 1.16270210e-01, 45 | 1.31279040e-01, 1.45437090e-01, 1.59627540e-01, 46 | 0.00000000e+00, 1.63404300e-02, 4.00086000e-02, 47 | 6.34390200e-02, 8.51085900e-02, 1.04787860e-01, 48 | 1.22120350e-01, 1.36931660e-01, 1.50958760e-01, 49 | 1.65299640e-01, 1.79942720e-01]) 50 | 51 | # initialize piecewise linear fit with your x and y data 52 | myPWLF = pwlf.PiecewiseLinFit(x, y, disp_res=True) 53 | 54 | # fit the function with four line segments 55 | # force the function to go through the data points 56 | # (0.0, 0.0) and (0.19, 0.16) 57 | # where the data points are of the form (x, y) 58 | x_c = [0.0, 0.19] 59 | y_c = [0.0, 0.2] 60 | res = myPWLF.fit(4, x_c, y_c) 61 | 62 | # predict for the determined points 63 | xHat = np.linspace(min(x), 0.19, num=10000) 64 | yHat = myPWLF.predict(xHat) 65 | 66 | # plot the results 67 | plt.figure() 68 | plt.plot(x, y, 'o') 69 | plt.plot(xHat, yHat, '-') 70 | plt.show() 71 | -------------------------------------------------------------------------------- /examples/mixed_degree.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import pwlf 4 | 5 | x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 6 | y = [1, 2, 3, 4, 4.25, 3.75, 4, 5, 6, 7] 7 | 8 | degree_list = [1, 0, 1] 9 | 10 | my_pwlf = pwlf.PiecewiseLinFit(x, y, degree=degree_list) 11 | 12 | breaks = my_pwlf.fit(3) 13 | 14 | # generate predictions 15 | x_hat = np.linspace(min(x), max(x), 1000) 16 | y_hat = my_pwlf.predict(x_hat) 17 | 18 | plt.figure() 19 | plt.plot(x, y, 'o') 20 | plt.plot(x_hat, y_hat) 21 | plt.show() 22 | -------------------------------------------------------------------------------- /examples/mixed_degree_forcing_slope.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.optimize import differential_evolution 3 | import matplotlib.pyplot as plt 4 | import pwlf 5 | 6 | x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 7 | y = [1, 2, 3, 4, 4.25, 3.75, 4, 5, 6, 7] 8 | 9 | my_pwlf = pwlf.PiecewiseLinFit(x, y, degree=1) 10 | 11 | # perform initial fit 12 | breaks = my_pwlf.fit(2) 13 | 14 | 15 | def my_fun(beta): 16 | # assing variables to the pwlf object 17 | my_pwlf.beta[0] = beta[0] # first line offset 18 | my_pwlf.beta[1] = beta[1] # first line slope 19 | my_pwlf.beta[2] = -1*beta[1] 20 | my_pwlf.fit_breaks[1] = beta[2] # breakpoint 21 | # generate predictions 22 | y_temp = my_pwlf.predict(my_pwlf.x_data) 23 | # compute ssr 24 | e = y_temp - my_pwlf.y_data 25 | return np.dot(e, e) 26 | 27 | 28 | bounds = np.zeros((3, 2)) 29 | # first line offset 30 | bounds[0, 0] = -100.0 # lower bound 31 | bounds[0, 1] = 100.0 # upper bound 32 | # first line slope 33 | bounds[1, 0] = -100.0 # lower bound 34 | bounds[1, 1] = 100.0 # upper bound 35 | # breakpont 36 | bounds[2, 0] = 2. # lower bound 37 | bounds[2, 1] = 6. # upper bound 38 | 39 | res = differential_evolution(my_fun, bounds, maxiter=1000, popsize=30, 40 | disp=True) 41 | 42 | # assign optimum to my_pwlf object 43 | my_fun(res.x) 44 | 45 | # generate predictions 46 | x_hat = np.linspace(min(x), max(x), 1000) 47 | y_hat = my_pwlf.predict(x_hat) 48 | 49 | plt.figure() 50 | plt.plot(x, y, 'o') 51 | plt.plot(x_hat, y_hat) 52 | plt.show() 53 | -------------------------------------------------------------------------------- /examples/model_persistence.py: -------------------------------------------------------------------------------- 1 | # import our libraries 2 | import numpy as np 3 | import pwlf 4 | 5 | # if you use Python 2.x you should import cPickle 6 | # import cPickle as pickle 7 | # if you use Python 3.x you can just use pickle 8 | import pickle 9 | 10 | # your data 11 | y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02, 12 | 4.39052750e-02, 5.45343950e-02, 6.74104940e-02, 13 | 8.34831790e-02, 1.02580042e-01, 1.22767939e-01, 14 | 1.42172312e-01, 0.00000000e+00, 8.58600000e-06, 15 | 8.31543400e-03, 2.34184100e-02, 3.39709150e-02, 16 | 4.03581990e-02, 4.53545600e-02, 5.02345260e-02, 17 | 5.55253360e-02, 6.14750770e-02, 6.82125120e-02, 18 | 7.55892510e-02, 8.38356810e-02, 9.26413070e-02, 19 | 1.02039790e-01, 1.11688258e-01, 1.21390666e-01, 20 | 1.31196948e-01, 0.00000000e+00, 1.56706510e-02, 21 | 3.54628780e-02, 4.63739040e-02, 5.61442590e-02, 22 | 6.78542550e-02, 8.16388310e-02, 9.77756110e-02, 23 | 1.16531753e-01, 1.37038283e-01, 0.00000000e+00, 24 | 1.16951050e-02, 3.12089850e-02, 4.41776550e-02, 25 | 5.42877590e-02, 6.63321350e-02, 8.07655920e-02, 26 | 9.70363280e-02, 1.15706975e-01, 1.36687642e-01, 27 | 0.00000000e+00, 1.50144640e-02, 3.44519970e-02, 28 | 4.55907760e-02, 5.59556700e-02, 6.88450940e-02, 29 | 8.41374060e-02, 1.01254006e-01, 1.20605073e-01, 30 | 1.41881288e-01, 1.62618058e-01]) 31 | x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02, 32 | 5.66106800e-02, 7.95549800e-02, 1.00936330e-01, 33 | 1.20351520e-01, 1.37442010e-01, 1.51858250e-01, 34 | 1.64433570e-01, 0.00000000e+00, -2.12600000e-05, 35 | 7.03872000e-03, 1.85494500e-02, 3.00926700e-02, 36 | 4.17617000e-02, 5.37279600e-02, 6.54941000e-02, 37 | 7.68092100e-02, 8.76596300e-02, 9.80525800e-02, 38 | 1.07961810e-01, 1.17305210e-01, 1.26063930e-01, 39 | 1.34180360e-01, 1.41725010e-01, 1.48629710e-01, 40 | 1.55374770e-01, 0.00000000e+00, 1.65610200e-02, 41 | 3.91016100e-02, 6.18679400e-02, 8.30997400e-02, 42 | 1.02132890e-01, 1.19011260e-01, 1.34620080e-01, 43 | 1.49429370e-01, 1.63539960e-01, -0.00000000e+00, 44 | 1.01980300e-02, 3.28642800e-02, 5.59461900e-02, 45 | 7.81388400e-02, 9.84458400e-02, 1.16270210e-01, 46 | 1.31279040e-01, 1.45437090e-01, 1.59627540e-01, 47 | 0.00000000e+00, 1.63404300e-02, 4.00086000e-02, 48 | 6.34390200e-02, 8.51085900e-02, 1.04787860e-01, 49 | 1.22120350e-01, 1.36931660e-01, 1.50958760e-01, 50 | 1.65299640e-01, 1.79942720e-01]) 51 | 52 | # your desired line segment end locations 53 | x0 = np.array([min(x), 0.039, 0.10, max(x)]) 54 | 55 | # initialize piecewise linear fit with your x and y data 56 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 57 | 58 | # fit the data with the specified break points (ie the x locations of where 59 | # the line segments should end 60 | my_pwlf.fit_with_breaks(x0) 61 | 62 | # save the fitted model 63 | with open('my_fit.pkl', 'wb') as f: 64 | pickle.dump(my_pwlf, f, pickle.HIGHEST_PROTOCOL) 65 | 66 | # load the fitted model 67 | with open('my_fit.pkl', 'rb') as f: 68 | my_pwlf = pickle.load(f) 69 | -------------------------------------------------------------------------------- /examples/model_persistence_prediction.py: -------------------------------------------------------------------------------- 1 | # import our libraries 2 | import numpy as np 3 | import pwlf 4 | 5 | # your data 6 | y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02, 7 | 4.39052750e-02, 5.45343950e-02, 6.74104940e-02, 8 | 8.34831790e-02, 1.02580042e-01, 1.22767939e-01, 9 | 1.42172312e-01, 0.00000000e+00, 8.58600000e-06, 10 | 8.31543400e-03, 2.34184100e-02, 3.39709150e-02, 11 | 4.03581990e-02, 4.53545600e-02, 5.02345260e-02, 12 | 5.55253360e-02, 6.14750770e-02, 6.82125120e-02, 13 | 7.55892510e-02, 8.38356810e-02, 9.26413070e-02, 14 | 1.02039790e-01, 1.11688258e-01, 1.21390666e-01, 15 | 1.31196948e-01, 0.00000000e+00, 1.56706510e-02, 16 | 3.54628780e-02, 4.63739040e-02, 5.61442590e-02, 17 | 6.78542550e-02, 8.16388310e-02, 9.77756110e-02, 18 | 1.16531753e-01, 1.37038283e-01, 0.00000000e+00, 19 | 1.16951050e-02, 3.12089850e-02, 4.41776550e-02, 20 | 5.42877590e-02, 6.63321350e-02, 8.07655920e-02, 21 | 9.70363280e-02, 1.15706975e-01, 1.36687642e-01, 22 | 0.00000000e+00, 1.50144640e-02, 3.44519970e-02, 23 | 4.55907760e-02, 5.59556700e-02, 6.88450940e-02, 24 | 8.41374060e-02, 1.01254006e-01, 1.20605073e-01, 25 | 1.41881288e-01, 1.62618058e-01]) 26 | x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02, 27 | 5.66106800e-02, 7.95549800e-02, 1.00936330e-01, 28 | 1.20351520e-01, 1.37442010e-01, 1.51858250e-01, 29 | 1.64433570e-01, 0.00000000e+00, -2.12600000e-05, 30 | 7.03872000e-03, 1.85494500e-02, 3.00926700e-02, 31 | 4.17617000e-02, 5.37279600e-02, 6.54941000e-02, 32 | 7.68092100e-02, 8.76596300e-02, 9.80525800e-02, 33 | 1.07961810e-01, 1.17305210e-01, 1.26063930e-01, 34 | 1.34180360e-01, 1.41725010e-01, 1.48629710e-01, 35 | 1.55374770e-01, 0.00000000e+00, 1.65610200e-02, 36 | 3.91016100e-02, 6.18679400e-02, 8.30997400e-02, 37 | 1.02132890e-01, 1.19011260e-01, 1.34620080e-01, 38 | 1.49429370e-01, 1.63539960e-01, -0.00000000e+00, 39 | 1.01980300e-02, 3.28642800e-02, 5.59461900e-02, 40 | 7.81388400e-02, 9.84458400e-02, 1.16270210e-01, 41 | 1.31279040e-01, 1.45437090e-01, 1.59627540e-01, 42 | 0.00000000e+00, 1.63404300e-02, 4.00086000e-02, 43 | 6.34390200e-02, 8.51085900e-02, 1.04787860e-01, 44 | 1.22120350e-01, 1.36931660e-01, 1.50958760e-01, 45 | 1.65299640e-01, 1.79942720e-01]) 46 | 47 | # initialize piecewise linear fit with your x and y data 48 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 49 | 50 | # fit the data with for 4 line segments 51 | my_pwlf.fit(4) 52 | 53 | # save only the parameters necessary to predict for new data 54 | np.save('ex_data/saved_parameters.npy', [my_pwlf.beta, my_pwlf.fit_breaks]) 55 | 56 | # load the parameters necessary for prediction 57 | my_prev_model = np.load('ex_data/saved_parameters.npy') 58 | 59 | # initialize new object 60 | my_pwlf_new = pwlf.PiecewiseLinFit(x, y) 61 | 62 | # predict with the saved parameters 63 | y_hat = my_pwlf_new.predict(x, beta=my_prev_model[0], breaks=my_prev_model[1]) 64 | -------------------------------------------------------------------------------- /examples/prediction_variance.py: -------------------------------------------------------------------------------- 1 | # fit for a specified number of line segments 2 | # you specify the number of line segments you want, the library does the rest 3 | 4 | # import our libraries 5 | import numpy as np 6 | import matplotlib.pyplot as plt 7 | import pwlf 8 | 9 | # your data 10 | y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02, 11 | 4.39052750e-02, 5.45343950e-02, 6.74104940e-02, 12 | 8.34831790e-02, 1.02580042e-01, 1.22767939e-01, 13 | 1.42172312e-01, 0.00000000e+00, 8.58600000e-06, 14 | 8.31543400e-03, 2.34184100e-02, 3.39709150e-02, 15 | 4.03581990e-02, 4.53545600e-02, 5.02345260e-02, 16 | 5.55253360e-02, 6.14750770e-02, 6.82125120e-02, 17 | 7.55892510e-02, 8.38356810e-02, 9.26413070e-02, 18 | 1.02039790e-01, 1.11688258e-01, 1.21390666e-01, 19 | 1.31196948e-01, 0.00000000e+00, 1.56706510e-02, 20 | 3.54628780e-02, 4.63739040e-02, 5.61442590e-02, 21 | 6.78542550e-02, 8.16388310e-02, 9.77756110e-02, 22 | 1.16531753e-01, 1.37038283e-01, 0.00000000e+00, 23 | 1.16951050e-02, 3.12089850e-02, 4.41776550e-02, 24 | 5.42877590e-02, 6.63321350e-02, 8.07655920e-02, 25 | 9.70363280e-02, 1.15706975e-01, 1.36687642e-01, 26 | 0.00000000e+00, 1.50144640e-02, 3.44519970e-02, 27 | 4.55907760e-02, 5.59556700e-02, 6.88450940e-02, 28 | 8.41374060e-02, 1.01254006e-01, 1.20605073e-01, 29 | 1.41881288e-01, 1.62618058e-01]) 30 | x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02, 31 | 5.66106800e-02, 7.95549800e-02, 1.00936330e-01, 32 | 1.20351520e-01, 1.37442010e-01, 1.51858250e-01, 33 | 1.64433570e-01, 0.00000000e+00, -2.12600000e-05, 34 | 7.03872000e-03, 1.85494500e-02, 3.00926700e-02, 35 | 4.17617000e-02, 5.37279600e-02, 6.54941000e-02, 36 | 7.68092100e-02, 8.76596300e-02, 9.80525800e-02, 37 | 1.07961810e-01, 1.17305210e-01, 1.26063930e-01, 38 | 1.34180360e-01, 1.41725010e-01, 1.48629710e-01, 39 | 1.55374770e-01, 0.00000000e+00, 1.65610200e-02, 40 | 3.91016100e-02, 6.18679400e-02, 8.30997400e-02, 41 | 1.02132890e-01, 1.19011260e-01, 1.34620080e-01, 42 | 1.49429370e-01, 1.63539960e-01, -0.00000000e+00, 43 | 1.01980300e-02, 3.28642800e-02, 5.59461900e-02, 44 | 7.81388400e-02, 9.84458400e-02, 1.16270210e-01, 45 | 1.31279040e-01, 1.45437090e-01, 1.59627540e-01, 46 | 0.00000000e+00, 1.63404300e-02, 4.00086000e-02, 47 | 6.34390200e-02, 8.51085900e-02, 1.04787860e-01, 48 | 1.22120350e-01, 1.36931660e-01, 1.50958760e-01, 49 | 1.65299640e-01, 1.79942720e-01]) 50 | 51 | # initialize piecewise linear fit with your x and y data 52 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 53 | 54 | # fit the data for four line segments 55 | res = my_pwlf.fit(4) 56 | 57 | # predict for the determined points 58 | xHat = np.linspace(min(x), max(x), num=10000) 59 | yHat = my_pwlf.predict(xHat) 60 | 61 | # Get the slopes 62 | my_slopes = my_pwlf.slopes 63 | 64 | # Get my model parameters 65 | beta = my_pwlf.beta 66 | 67 | # calculate the prediction variance at the xHat locations 68 | pre_var = my_pwlf.prediction_variance(xHat) 69 | # turn variance into standard deviation 70 | pre_sigma = np.sqrt(pre_var) 71 | 72 | # plot the results 73 | plt.figure() 74 | plt.plot(x, y, 'o') 75 | plt.plot(xHat, yHat, '-') 76 | plt.fill_between(xHat, yHat - (pre_sigma*1.96), yHat + (pre_sigma*1.96), 77 | alpha=0.1, color="r") 78 | plt.show() 79 | -------------------------------------------------------------------------------- /examples/prediction_variance_degree2.py: -------------------------------------------------------------------------------- 1 | # fit for a specified number of line segments 2 | # you specify the number of line segments you want, the library does the rest 3 | 4 | # import our libraries 5 | import numpy as np 6 | import matplotlib.pyplot as plt 7 | import pwlf 8 | 9 | # your data 10 | y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02, 11 | 4.39052750e-02, 5.45343950e-02, 6.74104940e-02, 12 | 8.34831790e-02, 1.02580042e-01, 1.22767939e-01, 13 | 1.42172312e-01, 0.00000000e+00, 8.58600000e-06, 14 | 8.31543400e-03, 2.34184100e-02, 3.39709150e-02, 15 | 4.03581990e-02, 4.53545600e-02, 5.02345260e-02, 16 | 5.55253360e-02, 6.14750770e-02, 6.82125120e-02, 17 | 7.55892510e-02, 8.38356810e-02, 9.26413070e-02, 18 | 1.02039790e-01, 1.11688258e-01, 1.21390666e-01, 19 | 1.31196948e-01, 0.00000000e+00, 1.56706510e-02, 20 | 3.54628780e-02, 4.63739040e-02, 5.61442590e-02, 21 | 6.78542550e-02, 8.16388310e-02, 9.77756110e-02, 22 | 1.16531753e-01, 1.37038283e-01, 0.00000000e+00, 23 | 1.16951050e-02, 3.12089850e-02, 4.41776550e-02, 24 | 5.42877590e-02, 6.63321350e-02, 8.07655920e-02, 25 | 9.70363280e-02, 1.15706975e-01, 1.36687642e-01, 26 | 0.00000000e+00, 1.50144640e-02, 3.44519970e-02, 27 | 4.55907760e-02, 5.59556700e-02, 6.88450940e-02, 28 | 8.41374060e-02, 1.01254006e-01, 1.20605073e-01, 29 | 1.41881288e-01, 1.62618058e-01]) 30 | x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02, 31 | 5.66106800e-02, 7.95549800e-02, 1.00936330e-01, 32 | 1.20351520e-01, 1.37442010e-01, 1.51858250e-01, 33 | 1.64433570e-01, 0.00000000e+00, -2.12600000e-05, 34 | 7.03872000e-03, 1.85494500e-02, 3.00926700e-02, 35 | 4.17617000e-02, 5.37279600e-02, 6.54941000e-02, 36 | 7.68092100e-02, 8.76596300e-02, 9.80525800e-02, 37 | 1.07961810e-01, 1.17305210e-01, 1.26063930e-01, 38 | 1.34180360e-01, 1.41725010e-01, 1.48629710e-01, 39 | 1.55374770e-01, 0.00000000e+00, 1.65610200e-02, 40 | 3.91016100e-02, 6.18679400e-02, 8.30997400e-02, 41 | 1.02132890e-01, 1.19011260e-01, 1.34620080e-01, 42 | 1.49429370e-01, 1.63539960e-01, -0.00000000e+00, 43 | 1.01980300e-02, 3.28642800e-02, 5.59461900e-02, 44 | 7.81388400e-02, 9.84458400e-02, 1.16270210e-01, 45 | 1.31279040e-01, 1.45437090e-01, 1.59627540e-01, 46 | 0.00000000e+00, 1.63404300e-02, 4.00086000e-02, 47 | 6.34390200e-02, 8.51085900e-02, 1.04787860e-01, 48 | 1.22120350e-01, 1.36931660e-01, 1.50958760e-01, 49 | 1.65299640e-01, 1.79942720e-01]) 50 | 51 | # initialize piecewise linear fit with your x and y data 52 | my_pwlf = pwlf.PiecewiseLinFit(x, y, degree=2) 53 | 54 | # fit the data for four line segments 55 | res = my_pwlf.fit(4) 56 | 57 | # predict for the determined points 58 | xHat = np.linspace(min(x), max(x), num=10000) 59 | yHat = my_pwlf.predict(xHat) 60 | 61 | # Get the slopes 62 | my_slopes = my_pwlf.slopes 63 | 64 | # Get my model parameters 65 | beta = my_pwlf.beta 66 | 67 | # calculate the prediction variance at the xHat locations 68 | pre_var = my_pwlf.prediction_variance(xHat) 69 | # turn variance into standard deviation 70 | pre_sigma = np.sqrt(pre_var) 71 | 72 | # plot the results 73 | plt.figure() 74 | plt.plot(x, y, 'o') 75 | plt.plot(xHat, yHat, '-') 76 | plt.fill_between(xHat, yHat - (pre_sigma*1.96), yHat + (pre_sigma*1.96), 77 | alpha=0.1, color="r") 78 | plt.show() 79 | -------------------------------------------------------------------------------- /examples/robust_regression.py: -------------------------------------------------------------------------------- 1 | # import our libraries 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | import pwlf 5 | from scipy.optimize import least_squares 6 | 7 | # generate sin wave data 8 | np.random.seed(1213) 9 | x = np.linspace(0, 10, num=100) 10 | y = np.sin(x * np.pi / 2) 11 | # add noise to the data 12 | y = np.random.normal(0, 0.5, 100) + y 13 | 14 | # initialize piecewise linear fit with your x and y data 15 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 16 | num_of_line_segments = 5 # change this value 17 | num_in_breaks = num_of_line_segments - 1 18 | n_beta = num_of_line_segments + 1 19 | 20 | def my_fun(breaks_and_beta): 21 | """ 22 | Returns the residuals given an array of breaks and beta 23 | """ 24 | inner_breaks = breaks_and_beta[:num_in_breaks] # break point locations 25 | # breaks code is taken from pwlf... 26 | breaks = np.zeros(len(inner_breaks) + 2) 27 | breaks[1:-1] = inner_breaks.copy() 28 | breaks[0] = my_pwlf.break_0 # smallest x value 29 | breaks[-1] = my_pwlf.break_n # largest x value 30 | beta = breaks_and_beta[num_in_breaks:] # beta paramters (slopes and int) 31 | A = my_pwlf.assemble_regression_matrix(breaks, 32 | my_pwlf.x_data) 33 | y_hat = np.dot(A, beta) 34 | resids = y_hat - my_pwlf.y_data 35 | return resids 36 | 37 | 38 | n_runs = 50 # perfrom 50 robust regressions, and take the best one 39 | results = [] 40 | for i in range(n_runs): 41 | # fit the data three line segments and use this result as a starting point 42 | res = my_pwlf.fitfast(num_of_line_segments) 43 | 44 | breaks_and_beta_guess = np.zeros(num_in_breaks + n_beta) 45 | breaks_and_beta_guess[:num_in_breaks] = res[1:num_of_line_segments] 46 | breaks_and_beta_guess[num_in_breaks:] = my_pwlf.beta 47 | 48 | # use the result from pwlf to start a robust regresion 49 | # notes on soft_l1: from documentation 50 | # https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.least_squares.html 51 | # ‘soft_l1’ : rho(z) = 2 * ((1 + z)**0.5 - 1). The smooth approximation of l1 52 | # (absolute value) loss. Usually a good choice for robust least squares. 53 | result = least_squares(my_fun, 54 | breaks_and_beta_guess, # initial guess 55 | loss='soft_l1', 56 | f_scale=0.1) # inlier residuals less than 0.1 57 | results.append(result) 58 | # find the best result 59 | costs = [] 60 | for r in results: 61 | costs.append(r['cost']) # the objective function from each Robust Reg. 62 | index_best_result = np.argmin(costs) 63 | result = results[index_best_result] 64 | 65 | # least squares result 66 | xhat = np.linspace(0, 10, 1000) 67 | yhat_ols = my_pwlf.predict(xhat) # initial guess 68 | 69 | # put the results back into my_pwlf 70 | breaks = np.zeros(num_of_line_segments+1) 71 | breaks[0] = my_pwlf.break_0 72 | breaks[-1] = my_pwlf.break_n 73 | breaks[1:num_in_breaks+1] = result.x[:num_in_breaks] 74 | my_pwlf.fit_breaks = breaks 75 | beta = result.x[num_in_breaks:] 76 | my_pwlf.beta = beta 77 | 78 | yhat_robo = my_pwlf.predict(xhat) 79 | 80 | plt.figure() 81 | plt.plot(x, y, 'o') 82 | plt.plot(xhat, yhat_ols, '-', label='OLS') 83 | plt.plot(xhat, yhat_robo, '-', label='Robust Reg.') 84 | plt.legend() 85 | plt.show() 86 | -------------------------------------------------------------------------------- /examples/run_opt_to_find_best_number_of_line_segments.py: -------------------------------------------------------------------------------- 1 | # Running an optimization to find the best number of line segments 2 | 3 | # import our libraries 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import pwlf 7 | from GPyOpt.methods import BayesianOptimization 8 | 9 | # your data 10 | y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02, 11 | 4.39052750e-02, 5.45343950e-02, 6.74104940e-02, 12 | 8.34831790e-02, 1.02580042e-01, 1.22767939e-01, 13 | 1.42172312e-01, 0.00000000e+00, 8.58600000e-06, 14 | 8.31543400e-03, 2.34184100e-02, 3.39709150e-02, 15 | 4.03581990e-02, 4.53545600e-02, 5.02345260e-02, 16 | 5.55253360e-02, 6.14750770e-02, 6.82125120e-02, 17 | 7.55892510e-02, 8.38356810e-02, 9.26413070e-02, 18 | 1.02039790e-01, 1.11688258e-01, 1.21390666e-01, 19 | 1.31196948e-01, 0.00000000e+00, 1.56706510e-02, 20 | 3.54628780e-02, 4.63739040e-02, 5.61442590e-02, 21 | 6.78542550e-02, 8.16388310e-02, 9.77756110e-02, 22 | 1.16531753e-01, 1.37038283e-01, 0.00000000e+00, 23 | 1.16951050e-02, 3.12089850e-02, 4.41776550e-02, 24 | 5.42877590e-02, 6.63321350e-02, 8.07655920e-02, 25 | 9.70363280e-02, 1.15706975e-01, 1.36687642e-01, 26 | 0.00000000e+00, 1.50144640e-02, 3.44519970e-02, 27 | 4.55907760e-02, 5.59556700e-02, 6.88450940e-02, 28 | 8.41374060e-02, 1.01254006e-01, 1.20605073e-01, 29 | 1.41881288e-01, 1.62618058e-01]) 30 | x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02, 31 | 5.66106800e-02, 7.95549800e-02, 1.00936330e-01, 32 | 1.20351520e-01, 1.37442010e-01, 1.51858250e-01, 33 | 1.64433570e-01, 0.00000000e+00, -2.12600000e-05, 34 | 7.03872000e-03, 1.85494500e-02, 3.00926700e-02, 35 | 4.17617000e-02, 5.37279600e-02, 6.54941000e-02, 36 | 7.68092100e-02, 8.76596300e-02, 9.80525800e-02, 37 | 1.07961810e-01, 1.17305210e-01, 1.26063930e-01, 38 | 1.34180360e-01, 1.41725010e-01, 1.48629710e-01, 39 | 1.55374770e-01, 0.00000000e+00, 1.65610200e-02, 40 | 3.91016100e-02, 6.18679400e-02, 8.30997400e-02, 41 | 1.02132890e-01, 1.19011260e-01, 1.34620080e-01, 42 | 1.49429370e-01, 1.63539960e-01, -0.00000000e+00, 43 | 1.01980300e-02, 3.28642800e-02, 5.59461900e-02, 44 | 7.81388400e-02, 9.84458400e-02, 1.16270210e-01, 45 | 1.31279040e-01, 1.45437090e-01, 1.59627540e-01, 46 | 0.00000000e+00, 1.63404300e-02, 4.00086000e-02, 47 | 6.34390200e-02, 8.51085900e-02, 1.04787860e-01, 48 | 1.22120350e-01, 1.36931660e-01, 1.50958760e-01, 49 | 1.65299640e-01, 1.79942720e-01]) 50 | 51 | # initialize piecewise linear fit with your x and y data 52 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 53 | 54 | # define your objective function 55 | 56 | 57 | def my_obj(x): 58 | # define some penalty parameter l 59 | # you'll have to arbitrarily pick this 60 | # it depends upon the noise in your data, 61 | # and the value of your sum of square of residuals 62 | l = y.mean()*0.001 63 | f = np.zeros(x.shape[0]) 64 | for i, j in enumerate(x): 65 | my_pwlf.fit(j[0]) 66 | f[i] = my_pwlf.ssr + (l*j[0]) 67 | return f 68 | 69 | 70 | # define the lower and upper bound for the number of line segements 71 | bounds = [{'name': 'var_1', 'type': 'discrete', 'domain': np.arange(2, 40)}] 72 | 73 | np.random.seed(12121) 74 | 75 | myBopt = BayesianOptimization(my_obj, domain=bounds, model_type='GP', 76 | initial_design_numdata=10, 77 | initial_design_type='latin', 78 | exact_feval=True, verbosity=True, 79 | verbosity_model=False) 80 | max_iter = 30 81 | 82 | # perform the bayesian optimization to find the optimum number of line segments 83 | myBopt.run_optimization(max_iter=max_iter, verbosity=True) 84 | 85 | print('\n \n Opt found \n') 86 | print('Optimum number of line segments:', myBopt.x_opt) 87 | print('Function value:', myBopt.fx_opt) 88 | myBopt.plot_acquisition() 89 | myBopt.plot_convergence() 90 | 91 | # perform the fit for the optimum 92 | my_pwlf.fit(myBopt.x_opt) 93 | # predict for the determined points 94 | xHat = np.linspace(min(x), max(x), num=10000) 95 | yHat = my_pwlf.predict(xHat) 96 | 97 | # plot the results 98 | plt.figure() 99 | plt.plot(x, y, 'o') 100 | plt.plot(xHat, yHat, '-') 101 | plt.show() 102 | -------------------------------------------------------------------------------- /examples/sinWaveFit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/examples/sinWaveFit.png -------------------------------------------------------------------------------- /examples/sinWaveFit16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/examples/sinWaveFit16.png -------------------------------------------------------------------------------- /examples/sineWave.py: -------------------------------------------------------------------------------- 1 | # fit for a specified number of line segments 2 | # you specify the number of line segments you want, the library does the rest 3 | 4 | # import our libraries 5 | import numpy as np 6 | import matplotlib.pyplot as plt 7 | import pwlf 8 | 9 | # generate sin wave data 10 | x = np.linspace(0, 10, num=100) 11 | y = np.sin(x * np.pi / 2) 12 | # add noise to the data 13 | y = np.random.normal(0, 0.05, 100) + y 14 | 15 | # initialize piecewise linear fit with your x and y data 16 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 17 | 18 | # fit the data for four line segments 19 | res = my_pwlf.fit(16, disp=True) 20 | # i'm passing the argument disp=True to see the progress of the differential 21 | # evolution so you can be sure the program isn't just hanging... 22 | 23 | # Be patient! this one takes some time - It's a difficult problem 24 | # using this differential evolution algo + bfgs can be over 500,000.0 function 25 | # evaluations 26 | 27 | # predict for the determined points 28 | xHat = np.linspace(min(x), max(x), num=10000) 29 | yHat = my_pwlf.predict(xHat) 30 | 31 | # plot the results 32 | plt.figure() 33 | plt.plot(x, y, 'o') 34 | plt.plot(xHat, yHat, '-') 35 | plt.show() 36 | -------------------------------------------------------------------------------- /examples/sineWave_custom_opt_bounds.py: -------------------------------------------------------------------------------- 1 | # fit for a specified number of line segments 2 | # you specify the number of line segments you want, the library does the rest 3 | 4 | # import our libraries 5 | import numpy as np 6 | import matplotlib.pyplot as plt 7 | import pwlf 8 | 9 | # generate sin wave data 10 | x = np.linspace(0, 10, num=100) 11 | y = np.sin(x * np.pi / 2) 12 | # add noise to the data 13 | y = np.random.normal(0, 0.05, 100) + y 14 | 15 | # initialize piecewise linear fit with your x and y data 16 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 17 | 18 | # define custom bounds for the interior break points 19 | n_segments = 4 20 | bounds = np.zeros((n_segments-1, 2)) 21 | # first lower and upper bound 22 | bounds[0, 0] = 0.0 23 | bounds[0, 1] = 3.5 24 | # second lower and upper bound 25 | bounds[1, 0] = 3.0 26 | bounds[1, 1] = 7.0 27 | # third lower and upper bound 28 | bounds[2, 0] = 6.0 29 | bounds[2, 1] = 10.0 30 | res = my_pwlf.fit(n_segments, bounds=bounds) 31 | 32 | # predict for the determined points 33 | xHat = np.linspace(min(x), max(x), num=10000) 34 | yHat = my_pwlf.predict(xHat) 35 | 36 | # plot the results 37 | plt.figure() 38 | plt.plot(x, y, 'o') 39 | plt.plot(xHat, yHat, '-') 40 | plt.show() 41 | -------------------------------------------------------------------------------- /examples/sineWave_degrees.py: -------------------------------------------------------------------------------- 1 | # import our libraries 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | import pwlf 5 | 6 | # generate sin wave data 7 | x = np.linspace(0, 10, num=100) 8 | y = np.sin(x * np.pi / 2) 9 | # add noise to the data 10 | y = np.random.normal(0, 0.05, 100) + y 11 | 12 | # initialize piecewise linear fit with your x and y data 13 | # pwlf lets you fit continuous model for many degree polynomials 14 | # degree=0 constant 15 | # degree=1 linear (default) 16 | # degree=2 quadratic 17 | my_pwlf_0 = pwlf.PiecewiseLinFit(x, y, degree=0) 18 | my_pwlf_1 = pwlf.PiecewiseLinFit(x, y, degree=1) # default 19 | my_pwlf_2 = pwlf.PiecewiseLinFit(x, y, degree=2) 20 | 21 | # fit the data for four line segments 22 | res0 = my_pwlf_0.fitfast(5, pop=50) 23 | res1 = my_pwlf_1.fitfast(5, pop=50) 24 | res2 = my_pwlf_2.fitfast(5, pop=50) 25 | 26 | # predict for the determined points 27 | xHat = np.linspace(min(x), max(x), num=10000) 28 | yHat0 = my_pwlf_0.predict(xHat) 29 | yHat1 = my_pwlf_1.predict(xHat) 30 | yHat2 = my_pwlf_2.predict(xHat) 31 | 32 | # plot the results 33 | plt.figure() 34 | plt.plot(x, y, 'o', label='Data') 35 | plt.plot(xHat, yHat0, '-', label='degree=0') 36 | plt.plot(xHat, yHat1, '--', label='degree=1') 37 | plt.plot(xHat, yHat2, ':', label='degree=2') 38 | plt.legend() 39 | plt.show() 40 | -------------------------------------------------------------------------------- /examples/sineWave_time_compare.py: -------------------------------------------------------------------------------- 1 | # fit for a specified number of line segments 2 | # you specify the number of line segments you want, the library does the rest 3 | 4 | # import our libraries 5 | import numpy as np 6 | import matplotlib.pyplot as plt 7 | import pwlf 8 | from time import time 9 | 10 | # generate sin wave data 11 | x = np.linspace(0, 10, num=100) 12 | y = np.sin(x * np.pi / 2) 13 | # add noise to the data 14 | y = np.random.normal(0, 0.05, 100) + y 15 | 16 | # initialize piecewise linear fit with your x and y data 17 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 18 | 19 | # fit the data for sixteen line segments 20 | t0 = time() 21 | res1 = my_pwlf.fit(16, disp=True) 22 | t1 = time() 23 | # i'm passing the argument disp=True to see the progress of the differential 24 | # evolution so you can be sure the program isn't just hanging... 25 | 26 | # Be patient! this one takes some time - It's a difficult problem 27 | # using this differential evolution algo + bfgs can be over 500,000.0 function 28 | # evaluations 29 | 30 | # predict for the determined points 31 | xHat1 = np.linspace(min(x), max(x), num=10000) 32 | yHat1 = my_pwlf.predict(xHat1) 33 | 34 | # fit the data for sixteen line segments 35 | # using the default 50 number of multi starts 36 | t2 = time() 37 | res2 = my_pwlf.fitfast(16) # this is equivalent to my_pwlf.fitfast(16,50) 38 | t3 = time() 39 | 40 | # predict for the determined points 41 | xHat2 = np.linspace(min(x), max(x), num=10000) 42 | yHat2 = my_pwlf.predict(xHat2) 43 | 44 | print('Run time for differential_evolution', t1 - t0, 'seconds') 45 | print('Run time for multi-start', t3 - t2, 'seconds') 46 | 47 | # plot the results 48 | plt.figure() 49 | plt.plot(x, y, 'o') 50 | plt.plot(xHat1, yHat1, '-', label='Diff. evolution') 51 | plt.plot(xHat2, yHat2, '-', label='Multi start') 52 | plt.legend() 53 | plt.show() 54 | -------------------------------------------------------------------------------- /examples/stack_overflow_example.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import pwlf 4 | 5 | # https://stackoverflow.com/questions/29382903/how-to-apply-piecewise-linear-fit-in-python 6 | 7 | x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) 8 | y = np.array([5, 7, 9, 11, 13, 15, 28.92, 42.81, 56.7, 70.59, 84.47, 98.36, 112.25, 126.14, 140.03]) 9 | 10 | # pwlf has two approaches to perform your fit: 11 | # 1. You can fit for a specified number of line segments. 12 | # 2. You can specify the x locations where the continuous piecewise lines 13 | # should terminate. 14 | 15 | # Approach 1 16 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 17 | # breaks will return the end location of each line segment 18 | breaks = my_pwlf.fit(2) 19 | print(breaks) 20 | # The gradient change point you asked for is at breaks[1] 21 | 22 | x_hat = np.linspace(x.min(), x.max(), 100) 23 | y_hat = my_pwlf.predict(x_hat) 24 | 25 | plt.figure() 26 | plt.plot(x, y, 'o') 27 | plt.plot(x_hat, y_hat, '-') 28 | plt.show() -------------------------------------------------------------------------------- /examples/standard_errrors_and_p-values.py: -------------------------------------------------------------------------------- 1 | # example of how to calculate standard errors and p-values 2 | from __future__ import print_function 3 | import numpy as np 4 | import pwlf 5 | 6 | # your data 7 | y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02, 8 | 4.39052750e-02, 5.45343950e-02, 6.74104940e-02, 9 | 8.34831790e-02, 1.02580042e-01, 1.22767939e-01, 10 | 1.42172312e-01, 0.00000000e+00, 8.58600000e-06, 11 | 8.31543400e-03, 2.34184100e-02, 3.39709150e-02, 12 | 4.03581990e-02, 4.53545600e-02, 5.02345260e-02, 13 | 5.55253360e-02, 6.14750770e-02, 6.82125120e-02, 14 | 7.55892510e-02, 8.38356810e-02, 9.26413070e-02, 15 | 1.02039790e-01, 1.11688258e-01, 1.21390666e-01, 16 | 1.31196948e-01, 0.00000000e+00, 1.56706510e-02, 17 | 3.54628780e-02, 4.63739040e-02, 5.61442590e-02, 18 | 6.78542550e-02, 8.16388310e-02, 9.77756110e-02, 19 | 1.16531753e-01, 1.37038283e-01, 0.00000000e+00, 20 | 1.16951050e-02, 3.12089850e-02, 4.41776550e-02, 21 | 5.42877590e-02, 6.63321350e-02, 8.07655920e-02, 22 | 9.70363280e-02, 1.15706975e-01, 1.36687642e-01, 23 | 0.00000000e+00, 1.50144640e-02, 3.44519970e-02, 24 | 4.55907760e-02, 5.59556700e-02, 6.88450940e-02, 25 | 8.41374060e-02, 1.01254006e-01, 1.20605073e-01, 26 | 1.41881288e-01, 1.62618058e-01]) 27 | x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02, 28 | 5.66106800e-02, 7.95549800e-02, 1.00936330e-01, 29 | 1.20351520e-01, 1.37442010e-01, 1.51858250e-01, 30 | 1.64433570e-01, 0.00000000e+00, -2.12600000e-05, 31 | 7.03872000e-03, 1.85494500e-02, 3.00926700e-02, 32 | 4.17617000e-02, 5.37279600e-02, 6.54941000e-02, 33 | 7.68092100e-02, 8.76596300e-02, 9.80525800e-02, 34 | 1.07961810e-01, 1.17305210e-01, 1.26063930e-01, 35 | 1.34180360e-01, 1.41725010e-01, 1.48629710e-01, 36 | 1.55374770e-01, 0.00000000e+00, 1.65610200e-02, 37 | 3.91016100e-02, 6.18679400e-02, 8.30997400e-02, 38 | 1.02132890e-01, 1.19011260e-01, 1.34620080e-01, 39 | 1.49429370e-01, 1.63539960e-01, -0.00000000e+00, 40 | 1.01980300e-02, 3.28642800e-02, 5.59461900e-02, 41 | 7.81388400e-02, 9.84458400e-02, 1.16270210e-01, 42 | 1.31279040e-01, 1.45437090e-01, 1.59627540e-01, 43 | 0.00000000e+00, 1.63404300e-02, 4.00086000e-02, 44 | 6.34390200e-02, 8.51085900e-02, 1.04787860e-01, 45 | 1.22120350e-01, 1.36931660e-01, 1.50958760e-01, 46 | 1.65299640e-01, 1.79942720e-01]) 47 | 48 | # initialize piecewise linear fit with your x and y data 49 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 50 | 51 | # your desired line segment end locations 52 | x0 = np.array([min(x), 0.039, 0.10, max(x)]) 53 | 54 | # fit the data for our specified line segment locations 55 | res = my_pwlf.fit_with_breaks(x0) 56 | 57 | # predict for the determined points 58 | xHat = np.linspace(min(x), max(x), num=10000) 59 | yHat = my_pwlf.predict(xHat) 60 | 61 | # Get my model parameters 62 | beta = my_pwlf.beta 63 | 64 | # calculate the standard errors associated with each beta parameter 65 | # not that these standard errors and p-values are only meaningful if 66 | # you have specified the specific line segment end locations 67 | # at least for now... 68 | se = my_pwlf.standard_errors() 69 | 70 | # calculate my t-value 71 | t = beta / se 72 | 73 | k = len(beta) - 1 74 | 75 | # calculate the p-values 76 | pvalues = my_pwlf.p_values() 77 | 78 | # print the results 79 | values = np.zeros((k, 4)) 80 | values[:, 0] = beta 81 | values[:, 1] = se 82 | values[:, 2] = t 83 | values[:, 3] = pvalues 84 | header = ['Beta value', 'Standard error', 't', 'P > |t| (p-value)'] 85 | print(*header, sep=' & ') 86 | for row in values: 87 | print(*row, sep=' & ') 88 | -------------------------------------------------------------------------------- /examples/standard_errrors_and_p-values_non-linear.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import numpy as np 3 | import pwlf 4 | # import matplotlib.pyplot as plt 5 | 6 | # generate a true piecewise linear data 7 | np.random.seed(5) 8 | n_data = 100 9 | x = np.linspace(0, 1, num=n_data) 10 | y = np.random.random(n_data) 11 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 12 | true_beta = np.random.normal(size=5) 13 | true_breaks = np.array([0.0, 0.2, 0.5, 0.75, 1.0]) 14 | y = my_pwlf.predict(x, beta=true_beta, breaks=true_breaks) 15 | 16 | # plt.figure() 17 | # plt.title('True piecewise linear data') 18 | # plt.plot(x, y) 19 | # plt.show() 20 | 21 | # initialize piecewise linear fit with your x and y data 22 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 23 | # fit the data for our specified line segment locations 24 | res = my_pwlf.fitfast(4, pop=100) 25 | 26 | # calculate the non-linear standard errors 27 | se = my_pwlf.standard_errors(method='non-linear', step_size=1e-4) 28 | 29 | # calculate p-values 30 | p = my_pwlf.p_values(method='non-linear', step_size=1e-4) 31 | 32 | parameters = np.concatenate((my_pwlf.beta, my_pwlf.fit_breaks[1:-1])) 33 | 34 | header = ['Parmater type', 'Parameter value', 'Standard error', 't', 35 | 'P > |t| (p-value)'] 36 | print(*header, sep=' & ') 37 | values = np.zeros((parameters.size, 5), dtype=np.object_) 38 | values[:, 1] = np.around(parameters, decimals=3) 39 | values[:, 2] = np.around(se, decimals=3) 40 | values[:, 3] = np.around(parameters / se, decimals=3) 41 | values[:, 4] = np.around(p, decimals=3) 42 | 43 | for i, row in enumerate(values): 44 | if i < my_pwlf.beta.size: 45 | row[0] = 'Beta' 46 | print(*row, sep=' & ') 47 | else: 48 | row[0] = 'Breakpoint' 49 | print(*row, sep=' & ') 50 | -------------------------------------------------------------------------------- /examples/test0.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import pwlf 4 | from scipy.optimize import minimize 5 | 6 | y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02, 7 | 4.39052750e-02, 5.45343950e-02, 6.74104940e-02, 8 | 8.34831790e-02, 1.02580042e-01, 1.22767939e-01, 9 | 1.42172312e-01, 0.00000000e+00, 8.58600000e-06, 10 | 8.31543400e-03, 2.34184100e-02, 3.39709150e-02, 11 | 4.03581990e-02, 4.53545600e-02, 5.02345260e-02, 12 | 5.55253360e-02, 6.14750770e-02, 6.82125120e-02, 13 | 7.55892510e-02, 8.38356810e-02, 9.26413070e-02, 14 | 1.02039790e-01, 1.11688258e-01, 1.21390666e-01, 15 | 1.31196948e-01, 0.00000000e+00, 1.56706510e-02, 16 | 3.54628780e-02, 4.63739040e-02, 5.61442590e-02, 17 | 6.78542550e-02, 8.16388310e-02, 9.77756110e-02, 18 | 1.16531753e-01, 1.37038283e-01, 0.00000000e+00, 19 | 1.16951050e-02, 3.12089850e-02, 4.41776550e-02, 20 | 5.42877590e-02, 6.63321350e-02, 8.07655920e-02, 21 | 9.70363280e-02, 1.15706975e-01, 1.36687642e-01, 22 | 0.00000000e+00, 1.50144640e-02, 3.44519970e-02, 23 | 4.55907760e-02, 5.59556700e-02, 6.88450940e-02, 24 | 8.41374060e-02, 1.01254006e-01, 1.20605073e-01, 25 | 1.41881288e-01, 1.62618058e-01]) 26 | x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02, 27 | 5.66106800e-02, 7.95549800e-02, 1.00936330e-01, 28 | 1.20351520e-01, 1.37442010e-01, 1.51858250e-01, 29 | 1.64433570e-01, 0.00000000e+00, -2.12600000e-05, 30 | 7.03872000e-03, 1.85494500e-02, 3.00926700e-02, 31 | 4.17617000e-02, 5.37279600e-02, 6.54941000e-02, 32 | 7.68092100e-02, 8.76596300e-02, 9.80525800e-02, 33 | 1.07961810e-01, 1.17305210e-01, 1.26063930e-01, 34 | 1.34180360e-01, 1.41725010e-01, 1.48629710e-01, 35 | 1.55374770e-01, 0.00000000e+00, 1.65610200e-02, 36 | 3.91016100e-02, 6.18679400e-02, 8.30997400e-02, 37 | 1.02132890e-01, 1.19011260e-01, 1.34620080e-01, 38 | 1.49429370e-01, 1.63539960e-01, -0.00000000e+00, 39 | 1.01980300e-02, 3.28642800e-02, 5.59461900e-02, 40 | 7.81388400e-02, 9.84458400e-02, 1.16270210e-01, 41 | 1.31279040e-01, 1.45437090e-01, 1.59627540e-01, 42 | 0.00000000e+00, 1.63404300e-02, 4.00086000e-02, 43 | 6.34390200e-02, 8.51085900e-02, 1.04787860e-01, 44 | 1.22120350e-01, 1.36931660e-01, 1.50958760e-01, 45 | 1.65299640e-01, 1.79942720e-01]) 46 | x0 = np.array([min(x), 0.039, 0.10, max(x)]) 47 | 48 | # initialize piecewise linear fit with your x and y data 49 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 50 | 51 | # fit the data with the specified break points (ie the x locations of where 52 | # the line segments should end 53 | # my_pwlf.fit_with_breaks(x0) 54 | # my_pwlf.seperateData(x0) 55 | 56 | # res = my_pwlf.fit(3) 57 | 58 | # initialize custom optimization 59 | number_of_line_segments = 3 60 | my_pwlf.use_custom_opt(number_of_line_segments) 61 | 62 | res = minimize(my_pwlf.fit_with_breaks_opt, np.array([x0[1], x0[2]])) 63 | 64 | # set up the break point locations 65 | x0 = np.zeros(number_of_line_segments + 1) 66 | x0[0] = np.min(x) 67 | x0[-1] = np.max(x) 68 | x0[1:-1] = res.x 69 | 70 | # calculate the parameters based on the optimal break point locations 71 | my_pwlf.fit_with_breaks(x0) 72 | 73 | # predict for the determined points 74 | xHat = np.linspace(min(x), max(x), num=10000) 75 | yHat = my_pwlf.predict(xHat) 76 | 77 | plt.figure() 78 | plt.plot(x, y, 'o') 79 | plt.plot(xHat, yHat, '-') 80 | plt.show() 81 | -------------------------------------------------------------------------------- /examples/test_for_model_significance.py: -------------------------------------------------------------------------------- 1 | # example of how to calculate standard errors and p-values 2 | from __future__ import print_function 3 | import numpy as np 4 | import pwlf 5 | from scipy.stats import f 6 | 7 | # your data 8 | y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02, 9 | 4.39052750e-02, 5.45343950e-02, 6.74104940e-02, 10 | 8.34831790e-02, 1.02580042e-01, 1.22767939e-01, 11 | 1.42172312e-01, 0.00000000e+00, 8.58600000e-06, 12 | 8.31543400e-03, 2.34184100e-02, 3.39709150e-02, 13 | 4.03581990e-02, 4.53545600e-02, 5.02345260e-02, 14 | 5.55253360e-02, 6.14750770e-02, 6.82125120e-02, 15 | 7.55892510e-02, 8.38356810e-02, 9.26413070e-02, 16 | 1.02039790e-01, 1.11688258e-01, 1.21390666e-01, 17 | 1.31196948e-01, 0.00000000e+00, 1.56706510e-02, 18 | 3.54628780e-02, 4.63739040e-02, 5.61442590e-02, 19 | 6.78542550e-02, 8.16388310e-02, 9.77756110e-02, 20 | 1.16531753e-01, 1.37038283e-01, 0.00000000e+00, 21 | 1.16951050e-02, 3.12089850e-02, 4.41776550e-02, 22 | 5.42877590e-02, 6.63321350e-02, 8.07655920e-02, 23 | 9.70363280e-02, 1.15706975e-01, 1.36687642e-01, 24 | 0.00000000e+00, 1.50144640e-02, 3.44519970e-02, 25 | 4.55907760e-02, 5.59556700e-02, 6.88450940e-02, 26 | 8.41374060e-02, 1.01254006e-01, 1.20605073e-01, 27 | 1.41881288e-01, 1.62618058e-01]) 28 | 29 | x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02, 30 | 5.66106800e-02, 7.95549800e-02, 1.00936330e-01, 31 | 1.20351520e-01, 1.37442010e-01, 1.51858250e-01, 32 | 1.64433570e-01, 0.00000000e+00, -2.12600000e-05, 33 | 7.03872000e-03, 1.85494500e-02, 3.00926700e-02, 34 | 4.17617000e-02, 5.37279600e-02, 6.54941000e-02, 35 | 7.68092100e-02, 8.76596300e-02, 9.80525800e-02, 36 | 1.07961810e-01, 1.17305210e-01, 1.26063930e-01, 37 | 1.34180360e-01, 1.41725010e-01, 1.48629710e-01, 38 | 1.55374770e-01, 0.00000000e+00, 1.65610200e-02, 39 | 3.91016100e-02, 6.18679400e-02, 8.30997400e-02, 40 | 1.02132890e-01, 1.19011260e-01, 1.34620080e-01, 41 | 1.49429370e-01, 1.63539960e-01, -0.00000000e+00, 42 | 1.01980300e-02, 3.28642800e-02, 5.59461900e-02, 43 | 7.81388400e-02, 9.84458400e-02, 1.16270210e-01, 44 | 1.31279040e-01, 1.45437090e-01, 1.59627540e-01, 45 | 0.00000000e+00, 1.63404300e-02, 4.00086000e-02, 46 | 6.34390200e-02, 8.51085900e-02, 1.04787860e-01, 47 | 1.22120350e-01, 1.36931660e-01, 1.50958760e-01, 48 | 1.65299640e-01, 1.79942720e-01]) 49 | # initialize piecewise linear fit with your x and y data 50 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 51 | 52 | # your desired line segment end locations 53 | x0 = np.array([min(x), 0.039, 0.10, max(x)]) 54 | 55 | # fit the data for our specified line segment locations 56 | # this is a linear model 57 | res = my_pwlf.fit_with_breaks(x0) 58 | 59 | # calculate the p-value as a test for significance in Regression 60 | # H0: beta0 = 0, beta1 = 0... 61 | # H1: betaj != 00 for at least 1 j 62 | # As defined in Section 2.4.1 of Myers RH, Montgomery DC, Anderson-Cook CM. 63 | # Response surface methodology . Hoboken. New Jersey: John Wiley & Sons, Inc. 64 | # 2009;20:38-44. 65 | sse = my_pwlf.ssr # this is to follow the notation in the above textbook 66 | ybar = np.ones(my_pwlf.n_data) * np.mean(my_pwlf.y_data) 67 | ydiff = my_pwlf.y_data - ybar 68 | sst = np.dot(ydiff, ydiff) 69 | 70 | ssr = sst - sse 71 | k = my_pwlf.beta.size - 1 72 | n = my_pwlf.n_data 73 | f0 = (ssr / k) / (sse / (n - k - 1)) 74 | 75 | p_value = f.sf(f0, k, n-k-1) 76 | print(f"Linear p-value: {p_value}") 77 | 78 | # The above case is a linear model, where we know the breakpoints 79 | # The following case is for the non-linear model, where we do not know the 80 | # break point locations 81 | res = my_pwlf.fit(2) 82 | sse = my_pwlf.ssr # to follow the Book notation 83 | ybar = np.ones(my_pwlf.n_data) * np.mean(my_pwlf.y_data) 84 | ydiff = my_pwlf.y_data - ybar 85 | sst = np.dot(ydiff, ydiff) 86 | 87 | ssr = sst - sse 88 | nb = my_pwlf.beta.size + my_pwlf.fit_breaks.size - 2 89 | k = nb - 1 90 | n = my_pwlf.n_data 91 | f0 = (ssr / k) / (sse / (n - k - 1)) 92 | 93 | p_value = f.sf(f0, k, n-k-1) 94 | print(f"non-linear p_value: {p_value}") 95 | 96 | # in both these cases, the p_value is very small, so we reject H0 97 | # and thus our paramters are significant! 98 | -------------------------------------------------------------------------------- /examples/test_if_breaks_exact.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pwlf 3 | 4 | x = np.array((0.0, 1.0, 1.5, 2.0)) 5 | y = np.array((0.0, 1.0, 1.1, 1.5)) 6 | 7 | my_fit1 = pwlf.PiecewiseLinFit(x, y) 8 | x0 = x.copy() 9 | # check that I can fit when break poitns spot on a 10 | ssr = my_fit1.fit_with_breaks(x0) 11 | 12 | # check that i can fit when I slightly modify x0 13 | my_fit2 = pwlf.PiecewiseLinFit(x, y) 14 | x0[1] = 1.00001 15 | x0[2] = 1.50001 16 | ssr2 = my_fit2.fit_with_breaks(x0) 17 | 18 | # check if my duplicate is in a different location 19 | x0 = x.copy() 20 | my_fit3 = pwlf.PiecewiseLinFit(x, y) 21 | x0[1] = 0.9 22 | ssr3 = my_fit3.fit_with_breaks(x0) 23 | 24 | # check if my duplicate is in a different location 25 | x0 = x.copy() 26 | my_fit4 = pwlf.PiecewiseLinFit(x, y) 27 | x0[1] = 1.1 28 | ssr4 = my_fit4.fit_with_breaks(x0) 29 | 30 | # check if my duplicate is in a different location 31 | x0 = x.copy() 32 | my_fit5 = pwlf.PiecewiseLinFit(x, y) 33 | x0[2] = 1.6 34 | ssr5 = my_fit5.fit_with_breaks(x0) 35 | 36 | # check if my duplicate is in a different location 37 | x0 = x.copy() 38 | my_fit6 = pwlf.PiecewiseLinFit(x, y) 39 | x0[2] = 1.4 40 | ssr6 = my_fit6.fit_with_breaks(x0) 41 | -------------------------------------------------------------------------------- /examples/tf/fit_begin_and_end.py: -------------------------------------------------------------------------------- 1 | # fit and predict between a known begging and known ending 2 | 3 | # import our libraries 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import pwlf 7 | from scipy.optimize import differential_evolution 8 | 9 | # your data 10 | y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02, 11 | 4.39052750e-02, 5.45343950e-02, 6.74104940e-02, 12 | 8.34831790e-02, 1.02580042e-01, 1.22767939e-01, 13 | 1.42172312e-01, 0.00000000e+00, 8.58600000e-06, 14 | 8.31543400e-03, 2.34184100e-02, 3.39709150e-02, 15 | 4.03581990e-02, 4.53545600e-02, 5.02345260e-02, 16 | 5.55253360e-02, 6.14750770e-02, 6.82125120e-02, 17 | 7.55892510e-02, 8.38356810e-02, 9.26413070e-02, 18 | 1.02039790e-01, 1.11688258e-01, 1.21390666e-01, 19 | 1.31196948e-01, 0.00000000e+00, 1.56706510e-02, 20 | 3.54628780e-02, 4.63739040e-02, 5.61442590e-02, 21 | 6.78542550e-02, 8.16388310e-02, 9.77756110e-02, 22 | 1.16531753e-01, 1.37038283e-01, 0.00000000e+00, 23 | 1.16951050e-02, 3.12089850e-02, 4.41776550e-02, 24 | 5.42877590e-02, 6.63321350e-02, 8.07655920e-02, 25 | 9.70363280e-02, 1.15706975e-01, 1.36687642e-01, 26 | 0.00000000e+00, 1.50144640e-02, 3.44519970e-02, 27 | 4.55907760e-02, 5.59556700e-02, 6.88450940e-02, 28 | 8.41374060e-02, 1.01254006e-01, 1.20605073e-01, 29 | 1.41881288e-01, 1.62618058e-01]) 30 | x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02, 31 | 5.66106800e-02, 7.95549800e-02, 1.00936330e-01, 32 | 1.20351520e-01, 1.37442010e-01, 1.51858250e-01, 33 | 1.64433570e-01, 0.00000000e+00, -2.12600000e-05, 34 | 7.03872000e-03, 1.85494500e-02, 3.00926700e-02, 35 | 4.17617000e-02, 5.37279600e-02, 6.54941000e-02, 36 | 7.68092100e-02, 8.76596300e-02, 9.80525800e-02, 37 | 1.07961810e-01, 1.17305210e-01, 1.26063930e-01, 38 | 1.34180360e-01, 1.41725010e-01, 1.48629710e-01, 39 | 1.55374770e-01, 0.00000000e+00, 1.65610200e-02, 40 | 3.91016100e-02, 6.18679400e-02, 8.30997400e-02, 41 | 1.02132890e-01, 1.19011260e-01, 1.34620080e-01, 42 | 1.49429370e-01, 1.63539960e-01, -0.00000000e+00, 43 | 1.01980300e-02, 3.28642800e-02, 5.59461900e-02, 44 | 7.81388400e-02, 9.84458400e-02, 1.16270210e-01, 45 | 1.31279040e-01, 1.45437090e-01, 1.59627540e-01, 46 | 0.00000000e+00, 1.63404300e-02, 4.00086000e-02, 47 | 6.34390200e-02, 8.51085900e-02, 1.04787860e-01, 48 | 1.22120350e-01, 1.36931660e-01, 1.50958760e-01, 49 | 1.65299640e-01, 1.79942720e-01]) 50 | 51 | # initialize piecewise linear fit with your x and y data 52 | my_pwlf = pwlf.PiecewiseLinFitTF(x, y, disp_res=True) 53 | 54 | # fit the function with four line segments 55 | # force the function to go through the data points 56 | # (0.0, 0.0) and (0.19, 0.16) 57 | # where the data points are of the form (x, y) 58 | x_c = [0.0, 0.19] 59 | y_c = [0.0, 0.2] 60 | breaks = [0.00711605, 0.12014667, 0.1799223] 61 | L = my_pwlf.fit_with_breaks_force_points(breaks, x_c, y_c) 62 | 63 | # predict for the determined points 64 | xHat = np.linspace(min(x), 0.19, num=10000) 65 | yHat = my_pwlf.predict(xHat) 66 | 67 | # plot the results 68 | plt.figure() 69 | plt.plot(x, y, 'o') 70 | plt.plot(xHat, yHat, '-') 71 | plt.show() 72 | -------------------------------------------------------------------------------- /examples/tf/sine_benchmark_fixed_20_break_points.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pwlf 3 | from time import time 4 | import os 5 | 6 | breaks = np.linspace(0.0, 10.0, num=21) 7 | 8 | n = np.logspace(3, 7, num=15, dtype=np.int) 9 | n_repeats = 10 10 | run_times = np.zeros((3, n.size, n_repeats)) 11 | 12 | for i, n_data in enumerate(n): 13 | # set random seed 14 | np.random.seed(256) 15 | # generate sin wave data 16 | x = np.linspace(0, 10, num=n_data) 17 | y = np.sin(x * np.pi / 2) 18 | # add noise to the data 19 | y = np.random.normal(0, 0.05, size=n_data) + y 20 | for j in range(n_repeats): 21 | # normal PWLF fit 22 | t0 = time() 23 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 24 | ssr = my_pwlf.fit_with_breaks(breaks) 25 | t1 = time() 26 | # PWLF TF fit 27 | t2 = time() 28 | my_pwlf = pwlf.PiecewiseLinFitTF(x, y) 29 | ssr = my_pwlf.fit_with_breaks(breaks) 30 | t3 = time() 31 | run_times[0, i, j] = t1 - t0 32 | run_times[1, i, j] = t3 - t2 33 | 34 | np.save('bench_run_times/20_break_times.npy', run_times) 35 | np.save('bench_run_times/n.npy', n) 36 | -------------------------------------------------------------------------------- /examples/tf/sine_benchmark_six_segments.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import pwlf 4 | from time import time 5 | 6 | # set random seed 7 | np.random.seed(256) 8 | breaks = np.array((0.0, 0.94, 2.96, 4.93, 7.02, 9.04, 10.0)) 9 | 10 | n_data = int(1e6) 11 | 12 | # generate sin wave data 13 | x = np.linspace(0, 10, num=n_data) 14 | y = np.sin(x * np.pi / 2) 15 | # add noise to the data 16 | y = np.random.normal(0, 0.05, size=n_data) + y 17 | 18 | t0 = time() 19 | # initialize piecewise linear fit with your x and y data 20 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 21 | 22 | # # fit the data for four line segments 23 | # res = my_pwlf.fit(16) 24 | # breaks = my_pwlf.fit(6) 25 | 26 | ssr = my_pwlf.fit_with_breaks(breaks) 27 | t1 = time() 28 | print('run time:', t1 - t0) 29 | # predict for the determined points 30 | xHat = np.linspace(min(x), max(x), num=10000) 31 | yHat = my_pwlf.predict(xHat) 32 | 33 | # plot the results 34 | plt.figure() 35 | plt.plot(x, y, 'o') 36 | plt.plot(xHat, yHat, '-') 37 | plt.show() 38 | -------------------------------------------------------------------------------- /examples/tf/test_fit.py: -------------------------------------------------------------------------------- 1 | # fit and predict with known line segment x locations 2 | 3 | # import our libraries 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import pwlf 7 | 8 | # your data 9 | y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02, 10 | 4.39052750e-02, 5.45343950e-02, 6.74104940e-02, 11 | 8.34831790e-02, 1.02580042e-01, 1.22767939e-01, 12 | 1.42172312e-01, 0.00000000e+00, 8.58600000e-06, 13 | 8.31543400e-03, 2.34184100e-02, 3.39709150e-02, 14 | 4.03581990e-02, 4.53545600e-02, 5.02345260e-02, 15 | 5.55253360e-02, 6.14750770e-02, 6.82125120e-02, 16 | 7.55892510e-02, 8.38356810e-02, 9.26413070e-02, 17 | 1.02039790e-01, 1.11688258e-01, 1.21390666e-01, 18 | 1.31196948e-01, 0.00000000e+00, 1.56706510e-02, 19 | 3.54628780e-02, 4.63739040e-02, 5.61442590e-02, 20 | 6.78542550e-02, 8.16388310e-02, 9.77756110e-02, 21 | 1.16531753e-01, 1.37038283e-01, 0.00000000e+00, 22 | 1.16951050e-02, 3.12089850e-02, 4.41776550e-02, 23 | 5.42877590e-02, 6.63321350e-02, 8.07655920e-02, 24 | 9.70363280e-02, 1.15706975e-01, 1.36687642e-01, 25 | 0.00000000e+00, 1.50144640e-02, 3.44519970e-02, 26 | 4.55907760e-02, 5.59556700e-02, 6.88450940e-02, 27 | 8.41374060e-02, 1.01254006e-01, 1.20605073e-01, 28 | 1.41881288e-01, 1.62618058e-01]) 29 | x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02, 30 | 5.66106800e-02, 7.95549800e-02, 1.00936330e-01, 31 | 1.20351520e-01, 1.37442010e-01, 1.51858250e-01, 32 | 1.64433570e-01, 0.00000000e+00, -2.12600000e-05, 33 | 7.03872000e-03, 1.85494500e-02, 3.00926700e-02, 34 | 4.17617000e-02, 5.37279600e-02, 6.54941000e-02, 35 | 7.68092100e-02, 8.76596300e-02, 9.80525800e-02, 36 | 1.07961810e-01, 1.17305210e-01, 1.26063930e-01, 37 | 1.34180360e-01, 1.41725010e-01, 1.48629710e-01, 38 | 1.55374770e-01, 0.00000000e+00, 1.65610200e-02, 39 | 3.91016100e-02, 6.18679400e-02, 8.30997400e-02, 40 | 1.02132890e-01, 1.19011260e-01, 1.34620080e-01, 41 | 1.49429370e-01, 1.63539960e-01, -0.00000000e+00, 42 | 1.01980300e-02, 3.28642800e-02, 5.59461900e-02, 43 | 7.81388400e-02, 9.84458400e-02, 1.16270210e-01, 44 | 1.31279040e-01, 1.45437090e-01, 1.59627540e-01, 45 | 0.00000000e+00, 1.63404300e-02, 4.00086000e-02, 46 | 6.34390200e-02, 8.51085900e-02, 1.04787860e-01, 47 | 1.22120350e-01, 1.36931660e-01, 1.50958760e-01, 48 | 1.65299640e-01, 1.79942720e-01]) 49 | 50 | # your desired line segment end locations 51 | x0 = np.array([min(x), 0.039, 0.10, max(x)]) 52 | 53 | # initialize piecewise linear fit with your x and y data 54 | my_pwlf = pwlf.PiecewiseLinFitTF(x, y) 55 | 56 | # fit the data with the specified break points (ie the x locations of where 57 | # the line segments should end 58 | ssr = my_pwlf.fit_with_breaks(x0) 59 | 60 | # predict for the determined points 61 | xHat = np.linspace(min(x), max(x), num=10000) 62 | yHat = my_pwlf.predict(xHat) 63 | 64 | # plot the results 65 | plt.figure() 66 | plt.plot(x, y, 'o') 67 | plt.plot(xHat, yHat, '-') 68 | plt.show() 69 | -------------------------------------------------------------------------------- /examples/understanding_higher_degrees/degree2_five_segment_equation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/examples/understanding_higher_degrees/degree2_five_segment_equation.pdf -------------------------------------------------------------------------------- /examples/understanding_higher_degrees/example_five_degree2_segments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/examples/understanding_higher_degrees/example_five_degree2_segments.png -------------------------------------------------------------------------------- /examples/useCustomOptimizationRoutine.py: -------------------------------------------------------------------------------- 1 | # use your own custom optimization routine to find the optimal location 2 | # of line segment locations 3 | 4 | # import our libraries 5 | import numpy as np 6 | import matplotlib.pyplot as plt 7 | import pwlf 8 | # import custom optimization library 9 | from scipy.optimize import minimize 10 | 11 | # your data 12 | y = np.array([0.00000000e+00, 9.69801700e-03, 2.94350340e-02, 13 | 4.39052750e-02, 5.45343950e-02, 6.74104940e-02, 14 | 8.34831790e-02, 1.02580042e-01, 1.22767939e-01, 15 | 1.42172312e-01, 0.00000000e+00, 8.58600000e-06, 16 | 8.31543400e-03, 2.34184100e-02, 3.39709150e-02, 17 | 4.03581990e-02, 4.53545600e-02, 5.02345260e-02, 18 | 5.55253360e-02, 6.14750770e-02, 6.82125120e-02, 19 | 7.55892510e-02, 8.38356810e-02, 9.26413070e-02, 20 | 1.02039790e-01, 1.11688258e-01, 1.21390666e-01, 21 | 1.31196948e-01, 0.00000000e+00, 1.56706510e-02, 22 | 3.54628780e-02, 4.63739040e-02, 5.61442590e-02, 23 | 6.78542550e-02, 8.16388310e-02, 9.77756110e-02, 24 | 1.16531753e-01, 1.37038283e-01, 0.00000000e+00, 25 | 1.16951050e-02, 3.12089850e-02, 4.41776550e-02, 26 | 5.42877590e-02, 6.63321350e-02, 8.07655920e-02, 27 | 9.70363280e-02, 1.15706975e-01, 1.36687642e-01, 28 | 0.00000000e+00, 1.50144640e-02, 3.44519970e-02, 29 | 4.55907760e-02, 5.59556700e-02, 6.88450940e-02, 30 | 8.41374060e-02, 1.01254006e-01, 1.20605073e-01, 31 | 1.41881288e-01, 1.62618058e-01]) 32 | x = np.array([0.00000000e+00, 8.82678000e-03, 3.25615100e-02, 33 | 5.66106800e-02, 7.95549800e-02, 1.00936330e-01, 34 | 1.20351520e-01, 1.37442010e-01, 1.51858250e-01, 35 | 1.64433570e-01, 0.00000000e+00, -2.12600000e-05, 36 | 7.03872000e-03, 1.85494500e-02, 3.00926700e-02, 37 | 4.17617000e-02, 5.37279600e-02, 6.54941000e-02, 38 | 7.68092100e-02, 8.76596300e-02, 9.80525800e-02, 39 | 1.07961810e-01, 1.17305210e-01, 1.26063930e-01, 40 | 1.34180360e-01, 1.41725010e-01, 1.48629710e-01, 41 | 1.55374770e-01, 0.00000000e+00, 1.65610200e-02, 42 | 3.91016100e-02, 6.18679400e-02, 8.30997400e-02, 43 | 1.02132890e-01, 1.19011260e-01, 1.34620080e-01, 44 | 1.49429370e-01, 1.63539960e-01, -0.00000000e+00, 45 | 1.01980300e-02, 3.28642800e-02, 5.59461900e-02, 46 | 7.81388400e-02, 9.84458400e-02, 1.16270210e-01, 47 | 1.31279040e-01, 1.45437090e-01, 1.59627540e-01, 48 | 0.00000000e+00, 1.63404300e-02, 4.00086000e-02, 49 | 6.34390200e-02, 8.51085900e-02, 1.04787860e-01, 50 | 1.22120350e-01, 1.36931660e-01, 1.50958760e-01, 51 | 1.65299640e-01, 1.79942720e-01]) 52 | 53 | # initialize piecewise linear fit with your x and y data 54 | my_pwlf = pwlf.PiecewiseLinFit(x, y) 55 | 56 | # initialize custom optimization 57 | number_of_line_segments = 3 58 | my_pwlf.use_custom_opt(number_of_line_segments) 59 | 60 | # i have number_of_line_segments - 1 number of variables 61 | # let's guess the correct location of the two unknown variables 62 | # (the program defaults to have end segments at x0= min(x) and xn=max(x) 63 | xGuess = np.zeros(number_of_line_segments - 1) 64 | xGuess[0] = 0.02 65 | xGuess[1] = 0.10 66 | 67 | res = minimize(my_pwlf.fit_with_breaks_opt, xGuess) 68 | 69 | # set up the break point locations 70 | x0 = np.zeros(number_of_line_segments + 1) 71 | x0[0] = np.min(x) 72 | x0[-1] = np.max(x) 73 | x0[1:-1] = res.x 74 | 75 | # calculate the parameters based on the optimal break point locations 76 | my_pwlf.fit_with_breaks(x0) 77 | 78 | # predict for the determined points 79 | xHat = np.linspace(min(x), max(x), num=10000) 80 | yHat = my_pwlf.predict(xHat) 81 | 82 | plt.figure() 83 | plt.plot(x, y, 'o') 84 | plt.plot(xHat, yHat, '-') 85 | plt.show() 86 | -------------------------------------------------------------------------------- /examples/weighted_least_squares_ex.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | import os 3 | os.environ['OMP_NUM_THREADS'] = '1' 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import pwlf 7 | t0 = time() 8 | np.random.seed(123) 9 | n = 100 10 | n_data_sets = 100 11 | n_segments = 6 12 | # generate sine data 13 | x = np.linspace(0, 10, n) 14 | y = np.zeros((n_data_sets, n)) 15 | sigma_change = np.linspace(0.001, 0.05, 100) 16 | for i in range(n_data_sets): 17 | y[i] = np.sin(x * np.pi / 2) 18 | # add noise to the data 19 | y[i] = np.random.normal(0, sigma_change, 100) + y[i] 20 | X = np.tile(x, n_data_sets) 21 | 22 | # perform an ordinary pwlf fit to the entire data 23 | my_pwlf = pwlf.PiecewiseLinFit(X.flatten(), y.flatten()) 24 | my_pwlf.fit(n_segments) 25 | 26 | # compute the standard deviation in y 27 | y_std = np.std(y, axis=0) 28 | # set the weights to be one over the standard deviation 29 | weights = 1.0 / y_std 30 | 31 | # perform a weighted least squares to the data 32 | my_pwlf_w = pwlf.PiecewiseLinFit(x, y.mean(axis=0), weights=weights) 33 | my_pwlf_w.fit(n_segments) 34 | 35 | 36 | # compare the fits 37 | xhat = np.linspace(0, 10, 1000) 38 | yhat = my_pwlf.predict(xhat) 39 | yhat_w = my_pwlf_w.predict(xhat) 40 | t1 = time() 41 | print('Runtime:', t1-t0) 42 | plt.figure() 43 | plt.plot(X.flatten(), y.flatten(), '.') 44 | plt.plot(xhat, yhat, '-', label='Ordinary LS') 45 | plt.plot(xhat, yhat_w, '-', label='Weighted LS') 46 | plt.legend() 47 | plt.show() 48 | -------------------------------------------------------------------------------- /examples/weighted_least_squares_ex_stats.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | import os 3 | os.environ['OMP_NUM_THREADS'] = '1' 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import pwlf 7 | t0 = time() 8 | np.random.seed(123) 9 | n = 100 10 | n_data_sets = 100 11 | n_segments = 6 12 | # generate sine data 13 | x = np.linspace(0, 10, n) 14 | y = np.zeros((n_data_sets, n)) 15 | sigma_change = np.linspace(0.001, 0.05, 100) 16 | for i in range(n_data_sets): 17 | y[i] = np.sin(x * np.pi / 2) 18 | # add noise to the data 19 | y[i] = np.random.normal(0, sigma_change, 100) + y[i] 20 | X = np.tile(x, n_data_sets) 21 | 22 | # perform an ordinary pwlf fit to the entire data 23 | my_pwlf = pwlf.PiecewiseLinFit(X.flatten(), y.flatten()) 24 | my_pwlf.fit(n_segments) 25 | se = my_pwlf.standard_errors() 26 | pv = my_pwlf.prediction_variance(x) 27 | 28 | # compute the standard deviation in y 29 | y_std = np.std(y, axis=0) 30 | # set the weights to be one over the standard deviation 31 | weights = 1.0 / y_std 32 | 33 | # perform a weighted least squares to the data 34 | my_pwlf_w = pwlf.PiecewiseLinFit(x, y.mean(axis=0), weights=weights) 35 | my_pwlf_w.fit(n_segments) 36 | se_w = my_pwlf_w.standard_errors() 37 | pv_w = my_pwlf_w.prediction_variance(x) 38 | 39 | print('Standard errors', se, se_w) 40 | print('Prediction varance', pv, pv_w) 41 | 42 | # compare the fits 43 | xhat = np.linspace(0, 10, 1000) 44 | yhat = my_pwlf.predict(xhat) 45 | yhat_w = my_pwlf_w.predict(xhat) 46 | t1 = time() 47 | print('Runtime:', t1-t0) 48 | plt.figure() 49 | plt.plot(X.flatten(), y.flatten(), '.') 50 | plt.plot(xhat, yhat, '-', label='Ordinary LS') 51 | plt.plot(xhat, yhat_w, '-', label='Weighted LS') 52 | plt.legend() 53 | plt.show() 54 | -------------------------------------------------------------------------------- /examples/weighted_least_squares_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/examples/weighted_least_squares_example.png -------------------------------------------------------------------------------- /paper/pwlf_Jekel_Venter_v1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/paper/pwlf_Jekel_Venter_v1.pdf -------------------------------------------------------------------------------- /paper/pwlf_Jekel_Venter_v2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/paper/pwlf_Jekel_Venter_v2.pdf -------------------------------------------------------------------------------- /paper/pwlf_Jekel_rev02_draft.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/paper/pwlf_Jekel_rev02_draft.pdf -------------------------------------------------------------------------------- /pwlf/__init__.py: -------------------------------------------------------------------------------- 1 | from .pwlf import PiecewiseLinFit # noqa F401 2 | from .version import __version__ # noqa F401 3 | -------------------------------------------------------------------------------- /pwlf/version.py: -------------------------------------------------------------------------------- 1 | __version__ = "2.5.1" 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import io 2 | from distutils.core import setup 3 | 4 | # load the version from version.py 5 | version = {} 6 | with open("pwlf/version.py") as fp: 7 | exec(fp.read(), version) 8 | 9 | setup( 10 | name="pwlf", 11 | version=version["__version__"], 12 | author="Charles Jekel", 13 | author_email="cjekel@gmail.com", 14 | packages=["pwlf"], 15 | url="https://github.com/cjekel/piecewise_linear_fit_py", 16 | license="MIT License", 17 | description="fit piecewise linear functions to data", 18 | long_description=io.open("README.rst", encoding="utf-8").read(), 19 | # long_description_content_type='text/markdown', 20 | platforms=["any"], 21 | install_requires=[ 22 | "numpy >= 1.14.0", 23 | "scipy >= 1.8.0", 24 | ], 25 | classifiers=[ 26 | "License :: OSI Approved :: MIT License", 27 | "Programming Language :: Python :: 3", 28 | ], 29 | python_requires=">3.5", 30 | ) 31 | -------------------------------------------------------------------------------- /sphinx_build_script.sh: -------------------------------------------------------------------------------- 1 | 2 | #!/usr/bin/env bash 3 | pip install . 4 | # clean docs and doctrees 5 | rm -r docs/* 6 | rm -r doctrees/* 7 | # make the documentation in gitignore folder 8 | cp examples/README.rst sphinxdocs/source/examples.rst 9 | cd sphinxdocs 10 | make clean 11 | make html 12 | # copy sphinx build into docs and doctrees 13 | cp -r build/doctrees/* ../doctrees/ 14 | cp -r build/html/* ../docs/ 15 | # grep -rl -e "word-break: break-word" --exclude=sphinx_build_script.sh | xargs sed -i 's/word-break: break-word/word-break: break-all/g' 16 | #cd .. 17 | #sed -i 's/break-word/break-all/g' docs/_static/basic.css 18 | -------------------------------------------------------------------------------- /sphinxdocs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = source 8 | BUILDDIR = build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /sphinxdocs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /sphinxdocs/source/about.rst: -------------------------------------------------------------------------------- 1 | About 2 | ============ 3 | 4 | A library for fitting continuous piecewise linear functions to data. Just specify the number of line segments you desire and provide the data. 5 | 6 | .. figure:: https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/master/examples/examplePiecewiseFit.png 7 | :alt: Example of a continuous piecewise linear fit to data. 8 | 9 | Example of a continuous piecewise linear fit to data. 10 | 11 | All changes now stored in 12 | `CHANGELOG.md `__ 13 | 14 | Please cite pwlf in your publications if it helps your research. 15 | 16 | .. code:: bibtex 17 | 18 | @Manual{pwlf, 19 | author = {Jekel, Charles F. and Venter, Gerhard}, 20 | title = {{pwlf:} A Python Library for Fitting 1D Continuous Piecewise Linear Functions}, 21 | year = {2019}, 22 | url = {https://github.com/cjekel/piecewise_linear_fit_py} 23 | } 24 | -------------------------------------------------------------------------------- /sphinxdocs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file does only contain a selection of the most common options. For a 6 | # full list see the documentation: 7 | # http://www.sphinx-doc.org/en/master/config 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | import os 16 | import sys 17 | import pwlf 18 | sys.path.insert(0, os.path.abspath('../../pwlf')) 19 | 20 | 21 | # -- Project information ----------------------------------------------------- 22 | 23 | project = 'pwlf' 24 | copyright = '2024, Charles Jekel' 25 | author = 'Charles Jekel' 26 | 27 | # The short X.Y version 28 | version = '' 29 | # The full version, including alpha/beta/rc tags 30 | release = pwlf.__version__ 31 | 32 | # -- General configuration --------------------------------------------------- 33 | 34 | # autoclass_content = 'both' 35 | 36 | # If your documentation needs a minimal Sphinx version, state it here. 37 | # 38 | # needs_sphinx = '1.0' 39 | 40 | # Add any Sphinx extension module names here, as strings. They can be 41 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 42 | # ones. 43 | extensions = [ 44 | 'sphinx.ext.autodoc', 45 | 'sphinx.ext.intersphinx', 46 | 'sphinx.ext.githubpages', 47 | 'numpydoc', 48 | ] 49 | 50 | # Add any paths that contain templates here, relative to this directory. 51 | templates_path = ['_templates'] 52 | 53 | # The suffix(es) of source filenames. 54 | # You can specify multiple suffix as a list of string: 55 | # 56 | # source_suffix = ['.rst', '.md'] 57 | source_suffix = '.rst' 58 | 59 | # The master toctree document. 60 | master_doc = 'index' 61 | 62 | # The language for content autogenerated by Sphinx. Refer to documentation 63 | # for a list of supported languages. 64 | # 65 | # This is also used if you do content translation via gettext catalogs. 66 | # Usually you set "language" from the command line for these cases. 67 | language = "en" 68 | 69 | # List of patterns, relative to source directory, that match files and 70 | # directories to ignore when looking for source files. 71 | # This pattern also affects html_static_path and html_extra_path. 72 | exclude_patterns = [] 73 | 74 | # The name of the Pygments (syntax highlighting) style to use. 75 | pygments_style = None 76 | 77 | 78 | # -- Options for HTML output ------------------------------------------------- 79 | 80 | # The theme to use for HTML and HTML Help pages. See the documentation for 81 | # a list of builtin themes. 82 | # 83 | #html_theme = 'sphinx' 84 | 85 | # Theme options are theme-specific and customize the look and feel of a theme 86 | # further. For a list of options available for each theme, see the 87 | # documentation. 88 | # 89 | html_theme_options = {'analytics_id': 'G-QKPGZSZ8CD'} 90 | html_favicon = 'favicon.ico' 91 | # Add any paths that contain custom static files (such as style sheets) here, 92 | # relative to this directory. They are copied after the builtin static files, 93 | # so a file named "default.css" will overwrite the builtin "default.css". 94 | html_static_path = ['_static'] 95 | 96 | # Custom sidebar templates, must be a dictionary that maps document names 97 | # to template names. 98 | # 99 | # The default sidebars (for documents that don't match any pattern) are 100 | # defined by theme itself. Builtin themes are using these templates by 101 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 102 | # 'searchbox.html']``. 103 | # 104 | # html_sidebars = {} 105 | 106 | 107 | # -- Options for HTMLHelp output --------------------------------------------- 108 | 109 | # Output file base name for HTML help builder. 110 | htmlhelp_basename = 'pwlfdoc' 111 | 112 | 113 | # -- Options for LaTeX output ------------------------------------------------ 114 | 115 | latex_elements = { 116 | # The paper size ('letterpaper' or 'a4paper'). 117 | # 118 | # 'papersize': 'letterpaper', 119 | 120 | # The font size ('10pt', '11pt' or '12pt'). 121 | # 122 | # 'pointsize': '10pt', 123 | 124 | # Additional stuff for the LaTeX preamble. 125 | # 126 | # 'preamble': '', 127 | 128 | # Latex figure (float) alignment 129 | # 130 | # 'figure_align': 'htbp', 131 | } 132 | 133 | # Grouping the document tree into LaTeX files. List of tuples 134 | # (source start file, target name, title, 135 | # author, documentclass [howto, manual, or own class]). 136 | latex_documents = [ 137 | (master_doc, 'pwlf.tex', 'pwlf Documentation', 138 | 'Charles Jekel', 'manual'), 139 | ] 140 | 141 | 142 | # -- Options for manual page output ------------------------------------------ 143 | 144 | # One entry per manual page. List of tuples 145 | # (source start file, name, description, authors, manual section). 146 | man_pages = [ 147 | (master_doc, 'pwlf', 'pwlf Documentation', 148 | [author], 1) 149 | ] 150 | 151 | 152 | # -- Options for Texinfo output ---------------------------------------------- 153 | 154 | # Grouping the document tree into Texinfo files. List of tuples 155 | # (source start file, target name, title, author, 156 | # dir menu entry, description, category) 157 | texinfo_documents = [ 158 | (master_doc, 'pwlf', 'pwlf Documentation', 159 | author, 'pwlf', 'One line description of project.', 160 | 'Miscellaneous'), 161 | ] 162 | 163 | 164 | # -- Options for Epub output ------------------------------------------------- 165 | 166 | # Bibliographic Dublin Core info. 167 | epub_title = project 168 | 169 | # The unique identifier of the text. This can be a ISBN number 170 | # or the project homepage. 171 | # 172 | # epub_identifier = '' 173 | 174 | # A unique identification for the text. 175 | # 176 | # epub_uid = '' 177 | 178 | # A list of files that should not be packed into the epub file. 179 | epub_exclude_files = ['search.html'] 180 | 181 | 182 | # -- Extension configuration ------------------------------------------------- 183 | 184 | # -- Options for intersphinx extension --------------------------------------- 185 | 186 | # Example configuration for intersphinx: refer to the Python standard library. 187 | intersphinx_mapping = {'python': ('https://docs.python.org/3', None)} 188 | -------------------------------------------------------------------------------- /sphinxdocs/source/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/sphinxdocs/source/favicon.ico -------------------------------------------------------------------------------- /sphinxdocs/source/how_it_works.rst: -------------------------------------------------------------------------------- 1 | How it works 2 | ============ 3 | 4 | This 5 | `paper `__ 6 | explains how this library works in detail. 7 | 8 | This is based on a formulation of a piecewise linear least squares fit, 9 | where the user must specify the location of break points. See `this 10 | post `__ 11 | which goes through the derivation of a least squares regression problem 12 | if the break point locations are known. Alternatively check out 13 | `Golovchenko 14 | (2004) `__. 15 | 16 | Global optimization is used to find the best location for the user 17 | defined number of line segments. I specifically use the `differential 18 | evolution `__ 19 | algorithm in SciPy. I default the differential evolution algorithm to be 20 | aggressive, and it is probably overkill for your problem. So feel free 21 | to pass your own differential evolution keywords to the library. See 22 | `this 23 | example `__. 24 | -------------------------------------------------------------------------------- /sphinxdocs/source/index.rst: -------------------------------------------------------------------------------- 1 | pwlf: piecewise linear fitting 2 | ================================ 3 | 4 | Fit piecewise linear functions to data! 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | installation 10 | how_it_works 11 | examples 12 | pwlf 13 | about 14 | requirements 15 | license 16 | 17 | Indices and tables 18 | ================== 19 | 20 | * :ref:`genindex` 21 | * :ref:`search` 22 | -------------------------------------------------------------------------------- /sphinxdocs/source/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | Python Package Index (PyPI) 5 | --------------------------- 6 | 7 | You can now install with pip. 8 | 9 | :: 10 | 11 | python -m pip install pwlf 12 | 13 | Conda 14 | ----- 15 | 16 | If you have conda, you can also install from conda-forge. 17 | 18 | :: 19 | 20 | conda install -c conda-forge pwlf 21 | 22 | From source 23 | ----------- 24 | 25 | Or clone the repo 26 | 27 | :: 28 | 29 | git clone https://github.com/cjekel/piecewise_linear_fit_py.git 30 | 31 | then install with pip 32 | 33 | :: 34 | 35 | python -m pip install ./piecewise_linear_fit_py 36 | 37 | -------------------------------------------------------------------------------- /sphinxdocs/source/license.rst: -------------------------------------------------------------------------------- 1 | License 2 | ======= 3 | 4 | MIT License 5 | 6 | Copyright (c) 2017-2020 Charles Jekel 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | -------------------------------------------------------------------------------- /sphinxdocs/source/modules.rst: -------------------------------------------------------------------------------- 1 | pwlf 2 | ==== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | pwlf 8 | -------------------------------------------------------------------------------- /sphinxdocs/source/pwlf.rst: -------------------------------------------------------------------------------- 1 | pwlf package contents 2 | ============ 3 | 4 | .. autosummary:: 5 | :toctree: stubs 6 | 7 | pwlf.PiecewiseLinFit 8 | 9 | .. autoclass:: pwlf.PiecewiseLinFit 10 | :members: 11 | :undoc-members: 12 | :show-inheritance: 13 | -------------------------------------------------------------------------------- /sphinxdocs/source/requirements.rst: -------------------------------------------------------------------------------- 1 | Requirements 2 | ============ 3 | 4 | `NumPy `__ (>= 1.14.0) 5 | 6 | `SciPy `__ (>= 1.2.0) 7 | 8 | `pyDOE `__ ( >= 0.3.8) 9 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjekel/piecewise_linear_fit_py/33c9eecb5e014080c139ab860549fc947ddc07cb/tests/__init__.py --------------------------------------------------------------------------------