├── tests ├── __init__.py ├── test_doctests.py └── test_trendypy.py ├── trendypy ├── __init__.py ├── utils.py ├── algos.py └── trendy.py ├── docs ├── README.md ├── image_data.zip ├── _static │ ├── trendypy.png │ └── favicon │ │ ├── favicon.ico │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── apple-touch-icon.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ └── site.webmanifest ├── image_data │ ├── car-01.gif │ ├── car-02.gif │ ├── car-03.gif │ ├── car-20.gif │ ├── chopper-01.gif │ ├── chopper-02.gif │ ├── chopper-03.gif │ ├── chopper-08.gif │ ├── carriage-02.gif │ ├── carriage-03.gif │ ├── carriage-04.gif │ └── carriage-20.gif ├── source │ ├── algos.rst │ ├── utils.rst │ ├── trendy.rst │ ├── api_reference.rst │ └── seeinaction.rst ├── requirements.txt ├── index.rst ├── Makefile ├── make.bat ├── conf.py └── stock_data.csv ├── requirements.txt ├── .github └── workflows │ ├── upload_pypi.yml │ ├── test.yml │ └── test_coverage.yml ├── LICENSE ├── setup.py ├── .gitignore └── README.md /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /trendypy/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy>=1.20.2 2 | pandas>=1.2.4 3 | fastdtw>=0.3.4 4 | -------------------------------------------------------------------------------- /docs/image_data.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/image_data.zip -------------------------------------------------------------------------------- /docs/_static/trendypy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/_static/trendypy.png -------------------------------------------------------------------------------- /docs/image_data/car-01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/image_data/car-01.gif -------------------------------------------------------------------------------- /docs/image_data/car-02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/image_data/car-02.gif -------------------------------------------------------------------------------- /docs/image_data/car-03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/image_data/car-03.gif -------------------------------------------------------------------------------- /docs/image_data/car-20.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/image_data/car-20.gif -------------------------------------------------------------------------------- /docs/image_data/chopper-01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/image_data/chopper-01.gif -------------------------------------------------------------------------------- /docs/image_data/chopper-02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/image_data/chopper-02.gif -------------------------------------------------------------------------------- /docs/image_data/chopper-03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/image_data/chopper-03.gif -------------------------------------------------------------------------------- /docs/image_data/chopper-08.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/image_data/chopper-08.gif -------------------------------------------------------------------------------- /docs/_static/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/_static/favicon/favicon.ico -------------------------------------------------------------------------------- /docs/image_data/carriage-02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/image_data/carriage-02.gif -------------------------------------------------------------------------------- /docs/image_data/carriage-03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/image_data/carriage-03.gif -------------------------------------------------------------------------------- /docs/image_data/carriage-04.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/image_data/carriage-04.gif -------------------------------------------------------------------------------- /docs/image_data/carriage-20.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/image_data/carriage-20.gif -------------------------------------------------------------------------------- /docs/_static/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/_static/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /docs/_static/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/_static/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /docs/source/algos.rst: -------------------------------------------------------------------------------- 1 | algos 2 | ----- 3 | 4 | .. automodule:: algos 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: -------------------------------------------------------------------------------- /docs/source/utils.rst: -------------------------------------------------------------------------------- 1 | utils 2 | ----- 3 | 4 | .. automodule:: utils 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: -------------------------------------------------------------------------------- /docs/_static/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/_static/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/source/trendy.rst: -------------------------------------------------------------------------------- 1 | trendy 2 | ------ 3 | 4 | .. automodule:: trendy 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: -------------------------------------------------------------------------------- /docs/_static/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/_static/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/_static/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddaskan/trendypy/HEAD/docs/_static/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /docs/source/api_reference.rst: -------------------------------------------------------------------------------- 1 | API Reference 2 | ============= 3 | .. toctree:: 4 | :maxdepth: 2 5 | 6 | trendy 7 | algos 8 | utils -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | Jinja2<3.0.0 2 | sphinx==3.0.3 3 | pydata-sphinx-theme==0.3.0 4 | sphinxcontrib-napoleon==0.7 5 | sphinx-copybutton==0.2.12 6 | recommonmark==0.6.0 7 | matplotlib==3.2.2 8 | ipython==7.15.0 9 | Pillow==8.3.1 -------------------------------------------------------------------------------- /docs/_static/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /tests/test_doctests.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unittest 3 | import doctest 4 | sys.path.append('../') 5 | from trendypy import trendy 6 | from trendypy import algos 7 | from trendypy import utils 8 | 9 | def load_tests(loader, tests, ignore): 10 | tests.addTests(doctest.DocTestSuite(trendy)) 11 | tests.addTests(doctest.DocTestSuite(algos)) 12 | tests.addTests(doctest.DocTestSuite(utils)) 13 | return tests 14 | 15 | if __name__ == '__main__': 16 | unittest.main() 17 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. TrendyPy documentation master file, created by 2 | sphinx-quickstart on Fri Jun 19 23:29:35 2020. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | TrendyPy Documentation 7 | ====================== 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | 12 | README.md 13 | source/seeinaction 14 | source/api_reference 15 | 16 | 17 | Indices and tables 18 | ------------------ 19 | 20 | * :ref:`genindex` 21 | * :ref:`modindex` 22 | * :ref:`search` 23 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/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=. 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% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /.github/workflows/upload_pypi.yml: -------------------------------------------------------------------------------- 1 | # This workflows will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Upload to PyPI 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | deploy: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: '3.x' 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install setuptools wheel twine 25 | - name: Build and publish 26 | env: 27 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 28 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 29 | run: | 30 | python setup.py sdist bdist_wheel 31 | twine upload dist/* 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2021 Dogan Askan 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 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | with open('requirements.txt', 'r') as f: 7 | install_requires = f.read().splitlines() 8 | 9 | setuptools.setup( 10 | name="trendypy", 11 | version="0.2.2", 12 | author="Dogan Askan", 13 | author_email="doganaskan@gmail.com", 14 | description="A package for trend line clustering.", 15 | long_description=long_description, 16 | long_description_content_type="text/markdown", 17 | url="https://github.com/ddaskan/trendypy", 18 | download_url="https://pypi.python.org/pypi/trendypy", 19 | project_urls={ 20 | "Bug Tracker": "https://github.com/ddaskan/trendypy/issues", 21 | "Documentation": "https://trendypy.readthedocs.io/", 22 | "Source Code": "https://github.com/ddaskan/trendypy", 23 | }, 24 | license='MIT', 25 | keywords='ml ai data-analysis machine-learning clustering', 26 | packages=setuptools.find_packages(), 27 | classifiers=[ 28 | "Programming Language :: Python :: 3", 29 | "License :: OSI Approved :: MIT License", 30 | "Operating System :: OS Independent", 31 | 'Topic :: Scientific/Engineering', 32 | "Topic :: Scientific/Engineering :: Artificial Intelligence", 33 | "Topic :: Software Development", 34 | ], 35 | python_requires='>=3.7', 36 | install_requires=install_requires, 37 | ) -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # Weekly tests to be sure dependencies are up-to-date 2 | 3 | name: tests 4 | 5 | on: 6 | workflow_dispatch: 7 | schedule: 8 | - cron: '17 10 * * 5' 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | matrix: 16 | os: [macos-latest, ubuntu-latest, windows-latest] 17 | python-version: [3.7, 3.8, 3.9] 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Set up Python ${{ matrix.python-version }} 22 | uses: actions/setup-python@v2 23 | with: 24 | python-version: ${{ matrix.python-version }} 25 | - name: Install dependencies 26 | run: | 27 | python -m pip install --upgrade pip 28 | pip install -r requirements.txt 29 | - name: Test with pytest 30 | run: | 31 | pip install pytest 32 | pip install pytest-cov 33 | python -m doctest README.md 34 | pytest --doctest-modules --junitxml=junit/test-results-${{ matrix.os }}-py${{ matrix.python-version }}.xml --cov=trendypy --cov-report=xml --cov-report=html 35 | - name: Upload pytest test results 36 | uses: actions/upload-artifact@v2 37 | with: 38 | name: pytest-results-${{ matrix.os }}-py${{ matrix.python-version }} 39 | path: junit/test-results-${{ matrix.os }}-py${{ matrix.python-version }}.xml 40 | # Use always() to always run this step to publish test results when there are test failures 41 | if: ${{ always() }} -------------------------------------------------------------------------------- /trendypy/utils.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Utility functions for the package. 3 | ''' 4 | import numpy as np 5 | 6 | def scale_01(x): 7 | '''Scales array to 0-1. 8 | 9 | Args: 10 | x (iter): 1d array of float 11 | 12 | Returns: 13 | np.array: scaled 1d array 14 | 15 | Example: 16 | >>> scale_01([1, 2, 3, 5]).tolist() 17 | [0.0, 0.25, 0.5, 1.0] 18 | 19 | ''' 20 | arr_min = min(x) 21 | x = np.array(x) - float(arr_min) 22 | arr_max = max(x) 23 | return x / float(arr_max) 24 | 25 | def abs_distance(x, y): 26 | '''Returns absolute distance. 27 | 28 | Args: 29 | x (float): input 1 30 | y (float): input 2 31 | 32 | Returns: 33 | float: \|x-y\| 34 | 35 | Example: 36 | >>> abs_distance(5, 7) 37 | 2.0 38 | >>> abs_distance(4, 1) 39 | 3.0 40 | 41 | ''' 42 | return float(abs(x - y)) 43 | 44 | def euclidean_distance(x, y): 45 | '''Returns Euclidean distance. 46 | 47 | Args: 48 | x (float or iter): input 1 49 | y (float or iter): input 2 50 | 51 | Returns: 52 | float: Euclidean distance 53 | 54 | References: 55 | https://numpy.org/doc/stable/reference/generated/numpy.linalg.norm.html 56 | 57 | Examples: 58 | >>> x, y = 1, 2 59 | >>> euclidean_distance(x, y) 60 | 1.0 61 | >>> x, y = [1, 2], [4, 6] 62 | >>> euclidean_distance(x, y) 63 | 5.0 64 | 65 | ''' 66 | return np.linalg.norm(np.array(x) - np.array(y)) 67 | 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | docs/*.png 74 | 75 | # PyBuilder 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | .python-version 87 | 88 | # pipenv 89 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 90 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 91 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 92 | # install all needed dependencies. 93 | #Pipfile.lock 94 | 95 | # celery beat schedule file 96 | celerybeat-schedule 97 | 98 | # SageMath parsed files 99 | *.sage.py 100 | 101 | # Environments 102 | .env 103 | .venv 104 | env/ 105 | venv/ 106 | ENV/ 107 | env.bak/ 108 | venv.bak/ 109 | 110 | # Spyder project settings 111 | .spyderproject 112 | .spyproject 113 | 114 | # Rope project settings 115 | .ropeproject 116 | 117 | # mkdocs documentation 118 | /site 119 | 120 | # mypy 121 | .mypy_cache/ 122 | .dmypy.json 123 | dmypy.json 124 | 125 | # Pyre type checker 126 | .pyre/ 127 | Icon? 128 | .DS_Store 129 | trendypy/sandbox_.py 130 | -------------------------------------------------------------------------------- /.github/workflows/test_coverage.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and create a coverage report. 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: tests_and_coverage 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | matrix: 18 | os: [macos-latest, ubuntu-latest, windows-latest] 19 | python-version: [3.7, 3.8, 3.9] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | pip install -r requirements.txt 31 | - name: Test with pytest 32 | run: | 33 | pip install pytest 34 | pip install pytest-cov 35 | python -m doctest README.md 36 | pytest --doctest-modules --junitxml=junit/test-results-${{ matrix.os }}-py${{ matrix.python-version }}.xml --cov=trendypy --cov-report=xml --cov-report=html 37 | - name: Upload pytest test results 38 | uses: actions/upload-artifact@v2 39 | with: 40 | name: pytest-results-${{ matrix.os }}-py${{ matrix.python-version }} 41 | path: junit/test-results-${{ matrix.os }}-py${{ matrix.python-version }}.xml 42 | # Use always() to always run this step to publish test results when there are test failures 43 | if: ${{ always() }} 44 | - name: Upload coverage to Codecov 45 | uses: codecov/codecov-action@v1 46 | with: 47 | directory: ./ 48 | name: codecov-umbrella 49 | fail_ci_if_error: true 50 | path_to_write_report: ./codecov_report_${{ matrix.os }}_py${{ matrix.python-version }}.gz 51 | 52 | publish-test-results: 53 | name: "Publish Unit Tests Results" 54 | needs: build 55 | runs-on: ubuntu-latest 56 | # the build job might be skipped, we don't need to run this job then 57 | if: success() || failure() 58 | 59 | steps: 60 | - name: Download Artifacts 61 | uses: actions/download-artifact@v2 62 | with: 63 | path: artifacts 64 | 65 | - name: Publish Unit Test Results 66 | uses: EnricoMi/publish-unit-test-result-action@v1 67 | with: 68 | files: artifacts/**/*.xml 69 | -------------------------------------------------------------------------------- /tests/test_trendypy.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import sys 3 | sys.path.append('../') 4 | sys.path.append('../trendypy/') 5 | from trendypy.trendy import Trendy 6 | from trendypy.algos import levenshtein_distance 7 | 8 | class TestTrendy(unittest.TestCase): 9 | 10 | def test___init__(self): 11 | self.assertRaises(TypeError, Trendy) 12 | self.assertRaises(TypeError, Trendy, *[None]) 13 | self.assertRaises( 14 | TypeError, 15 | Trendy, 16 | **{'n_clusters':5, 'algorithm':'error'}) 17 | self.assertRaises(ValueError, Trendy, **{'n_clusters':'1'}) 18 | self.assertRaises(ValueError, Trendy, **{'n_clusters':1}) 19 | 20 | trendy = Trendy(n_clusters=3) 21 | self.assertEqual(trendy.n_clusters, 3) 22 | self.assertTrue(callable(trendy.dist_func)) 23 | 24 | test_func = lambda x,y:abs(x-y) 25 | trendy = Trendy(n_clusters=3, algorithm=test_func) 26 | self.assertTrue(callable(trendy.dist_func)) 27 | self.assertEqual( 28 | trendy.dist_func.__code__.co_code, 29 | test_func.__code__.co_code) 30 | 31 | def test_fit(self): 32 | a = [0.8, 2, 3, 4, 5] 33 | b = [1, 1.9, 2.5, 4.4, 5.1] 34 | c = [6.2, 4, 4, 2, 0] 35 | d = [7, 6, 5, 4, 3, 2, 1] 36 | 37 | trendy = Trendy(6) 38 | self.assertRaises(ValueError, trendy.fit, *[[a, b, c, d]]) 39 | 40 | trendy = Trendy(2) 41 | trendy.fit([a, b, c, d]) 42 | self.assertListEqual(trendy.labels_, [0, 0, 1, 1]) 43 | 44 | e = [3, 4, 3, 2, 5, 5.5] 45 | 46 | trendy = Trendy(3) 47 | trendy.fit([a, b, c, d, e]) 48 | self.assertListEqual(trendy.labels_, [0, 0, 1, 1, 2]) 49 | 50 | trendy = Trendy(5) 51 | trendy.fit([a, b, c, d, e]) 52 | self.assertListEqual(trendy.labels_, [0, 1, 2, 3, 4]) 53 | self.assertListEqual(trendy.cluster_centers_, [a, b, c, d, e]) 54 | 55 | def test_predict(self): 56 | a = [0.8, 2, 3, 4, 5] 57 | b = [1, 1.9, 2.5, 4.4, 5.1] 58 | c = [6.2, 4, 4, 2, 0] 59 | d = [7, 6, 5, 4, 3, 2, 1] 60 | trendy = Trendy(2) 61 | trendy.fit([a, b, c, d]) 62 | 63 | preds = trendy.predict([[0.9, 2, 2.9, 3.8, 5]]) 64 | self.assertEqual(preds, [0]) 65 | 66 | preds = trendy.predict([ 67 | [0.9, 2, 2.9, 3.8, 5], 68 | [6.4, 4.4, 3.9, 3], 69 | [6, 3.5, 3.9, 3, 1.5]]) 70 | self.assertEqual(preds, [0, 1, 1]) 71 | 72 | def test_string_clustering(self): 73 | company_names = [ 74 | 'apple inc', 75 | 'Apple Inc.', 76 | 'Microsoft Corporation', 77 | 'Microsft Corp.'] 78 | trendy = Trendy(n_clusters=2, algorithm=levenshtein_distance) 79 | trendy.fit(company_names) 80 | self.assertEqual(trendy.labels_, [0, 0, 1, 1]) 81 | self.assertEqual(trendy.predict(['Apple']), [0]) 82 | 83 | if __name__ == '__main__': 84 | unittest.main() 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TrendyPy 2 | 3 | 4 | 5 | [![PyPI](https://img.shields.io/pypi/v/trendypy)](https://pypi.org/project/trendypy/) 6 | [![tests](https://github.com/ddaskan/trendypy/workflows/tests/badge.svg)](https://github.com/ddaskan/trendypy/actions?query=workflow%3Atests) 7 | [![Codecov](https://codecov.io/gh/ddaskan/trendypy/master.svg)](https://codecov.io/gh/ddaskan/trendypy/) 8 | [![Documentation Status](https://readthedocs.org/projects/trendypy/badge/?version=latest)](https://trendypy.readthedocs.io/en/latest/?badge=latest) 9 | [![Downloads](https://pepy.tech/badge/trendypy)](https://pepy.tech/project/trendypy) 10 | [![GitHub last commit](https://img.shields.io/github/last-commit/ddaskan/trendypy)](https://github.com/ddaskan/trendypy) 11 | [![Twitter](https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Fgithub.com%2Fddaskan%2Ftrendypy)](https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fgithub.com%2Fddaskan%2Ftrendypy) 12 | 13 | TrendyPy is a small Python package for sequence clustering. It is initially developed to create time series clusters by calculating trend similarity distance with [Dynamic Time Warping](https://en.wikipedia.org/wiki/Dynamic_time_warping). 14 | 15 | ## Installation 16 | 17 | You can install TrendyPy with pip. 18 | 19 | ``` 20 | pip install trendypy 21 | ``` 22 | 23 | TrendyPy depends on Pandas, Numpy and fastdtw and works in Python 3.7+. 24 | 25 | ## Quickstart 26 | 27 | Trendy has scikit-learn like api to allow easy integration to existing programs. Below is a quick example to show how it clusters increasing and decreasing trends. 28 | 29 | ```python 30 | >>> from trendypy.trendy import Trendy 31 | >>> a = [1, 2, 3, 4, 5] # increasing trend 32 | >>> b = [1, 2.1, 2.9, 4.4, 5.1] # increasing trend 33 | >>> c = [6.2, 5, 4, 3, 2] # decreasing trend 34 | >>> d = [7, 6, 5, 4, 3, 2, 1] # decreasing trend 35 | >>> trendy = Trendy(n_clusters=2) 36 | >>> trendy.fit([a, b, c, d]) 37 | >>> print(trendy.labels_) 38 | [0, 0, 1, 1] 39 | >>> trendy.predict([[0.9, 2, 3.1, 4]]) # another increasing trend 40 | [0] 41 | 42 | ``` 43 | 44 | It can also be utilized to cluster strings by using string similarity metrics. 45 | 46 | ```python 47 | >>> from trendypy.trendy import Trendy 48 | >>> from trendypy.algos import levenshtein_distance 49 | >>> company_names = [ 50 | ... 'apple inc', 51 | ... 'Apple Inc.', 52 | ... 'Microsoft Corporation', 53 | ... 'Microsft Corp.'] 54 | >>> trendy = Trendy(n_clusters=2, algorithm=levenshtein_distance) 55 | >>> trendy.fit(company_names) 56 | >>> print(trendy.labels_) 57 | [0, 0, 1, 1] 58 | >>> trendy.predict(['Apple']) 59 | [0] 60 | 61 | ``` 62 | 63 | Refer to [extensive demo](https://trendypy.readthedocs.io/en/latest/source/seeinaction.html) to see it in clustering [stock trends](https://trendypy.readthedocs.io/en/latest/source/seeinaction.html#stock-data), [images](https://trendypy.readthedocs.io/en/latest/source/seeinaction.html#image-clustering) or to see how to [define your own metric](https://trendypy.readthedocs.io/en/latest/source/seeinaction.html#custom-metric) or just check [API Reference](https://trendypy.readthedocs.io/en/latest/source/api_reference.html) for details. 64 | 65 | ## Post 66 | The idea is originated from the post [Trend Clustering](http://www.doganaskan.com/blog/posts/trendcluster.html). 67 | -------------------------------------------------------------------------------- /trendypy/algos.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Algorithms for the package. 3 | ''' 4 | import sys 5 | sys.path.append('../') 6 | import numpy as np 7 | from fastdtw import fastdtw 8 | from trendypy import utils 9 | 10 | def dtw_distance(x, y, d=utils.euclidean_distance, scaled=False): 11 | '''Returns the distance of two arrays with dynamic time warping method. 12 | 13 | Args: 14 | x (iter): input array 1 15 | y (iter): input array 2 16 | d (func): distance function, default is euclidean 17 | scaled (bool): should arrays be scaled (i.e. 0-1) before calculation 18 | 19 | Returns: 20 | float: distance, 0.0 means arrays are exactly same, upper limit is\ 21 | positive infinity 22 | 23 | References: 24 | https://en.wikipedia.org/wiki/Dynamic_time_warping 25 | 26 | Examples: 27 | >>> dtw_distance([1, 2, 3, 4], [1, 2, 3, 4]) 28 | 0.0 29 | >>> dtw_distance([1, 2, 3, 4], [0, 0, 0]) 30 | 10.0 31 | >>> dtw_distance([1, 2, 3, 4], [0, 2, 0, 4]) 32 | 4.0 33 | >>> dtw_distance([1, 2, 3, 4], [10, 20, 30, 40]) 34 | 90.0 35 | >>> dtw_distance([1, 2, 3, 4], [10, 20, 30, 40], scaled=True) 36 | 0.0 37 | 38 | ''' 39 | if scaled: 40 | x = utils.scale_01(x) 41 | y = utils.scale_01(y) 42 | n = len(x) + 1 43 | m = len(y) + 1 44 | DTW = np.zeros((n, m)) 45 | DTW[:, 0] = float('Inf') 46 | DTW[0, :] = float('Inf') 47 | DTW[0, 0] = 0 48 | 49 | for i in range(1, n): 50 | for j in range(1, m): 51 | cost = d(x[i-1], y[j-1]) 52 | DTW[i, j] = cost + min(DTW[i-1, j], DTW[i, j-1], DTW[i-1, j-1]) 53 | 54 | return DTW[n-1, m-1] 55 | 56 | def fastdtw_distance(x, y, d=utils.euclidean_distance): 57 | '''Dynamic Time Warping (DTW) algorithm with an O(N) time and memory 58 | complexity. 59 | 60 | Args: 61 | x (iter): input array 1 62 | y (iter): input array 2 63 | d (func): distance function, default is euclidean 64 | 65 | Returns: 66 | float: distance, 0.0 means arrays are exactly same, upper limit is\ 67 | positive infinity 68 | 69 | References: 70 | https://pypi.org/project/fastdtw/ 71 | 72 | Examples: 73 | >>> fastdtw_distance([1, 2, 3, 4], [1, 2, 3, 4]) 74 | 0.0 75 | >>> fastdtw_distance([1, 2, 3, 4], [0, 0, 0]) 76 | 10.0 77 | >>> fastdtw_distance([1, 2, 3, 4], [0, 2, 0, 4]) 78 | 4.0 79 | >>> fastdtw_distance([1, 2, 3, 4], [10, 20, 30, 40]) 80 | 90.0 81 | 82 | ''' 83 | return fastdtw(x, y, dist=d)[0] 84 | 85 | def levenshtein_distance(x, y): 86 | """Levenshtein distance for string similarity. 87 | 88 | Args: 89 | x (str): input string 1 90 | y (str): input string 2 91 | 92 | Returns: 93 | int: distance, 0 means strings are exactly same, upper limit is\ 94 | positive infinity 95 | 96 | References: 97 | https://en.wikipedia.org/wiki/Levenshtein_distance 98 | 99 | Examples: 100 | >>> levenshtein_distance('Apple', 'Apple') 101 | 0 102 | >>> levenshtein_distance('Apple', 'apple') 103 | 1 104 | >>> levenshtein_distance('Apple Inc.', 'apple inc') 105 | 3 106 | 107 | """ 108 | m, n = len(x), len(y) 109 | v0 = list(range(n + 1)) 110 | v1 = [None] * (n + 1) 111 | 112 | for i in range(m): 113 | v1[0] = i + 1 114 | 115 | for j in range(n): 116 | deletion_cost = v0[j + 1] + 1 117 | insertion_cost = v1[j] + 1 118 | if x[i] == y[j]: 119 | substitution_cost = v0[j] 120 | else: 121 | substitution_cost = v0[j] + 1 122 | 123 | v1[j + 1] = min(deletion_cost, insertion_cost, substitution_cost) 124 | 125 | v0, v1 = v1, v0 126 | 127 | return v0[n] 128 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | import inspect 16 | sys.path.insert(0, os.path.abspath('.')) 17 | sys.path.append('../') 18 | sys.path.append('../trendypy/') 19 | 20 | 21 | # -- Project information ----------------------------------------------------- 22 | 23 | project = 'TrendyPy' 24 | copyright = '2020-2021, Dogan Askan' 25 | author = 'Dogan Askan' 26 | 27 | # The full version, including alpha/beta/rc tags 28 | release = '0.2.2' 29 | 30 | 31 | # -- General configuration --------------------------------------------------- 32 | 33 | # Add any Sphinx extension module names here, as strings. They can be 34 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 35 | # ones. 36 | extensions = [ 37 | 'sphinx.ext.autodoc', 38 | 'sphinxcontrib.napoleon', 39 | #'sphinx.ext.viewcode', 40 | 'sphinx.ext.linkcode', 41 | 'sphinx_copybutton', 42 | 'recommonmark' 43 | ] 44 | 45 | extensions += [#'matplotlib.sphinxext.only_directives', 46 | 'matplotlib.sphinxext.plot_directive', 47 | 'IPython.sphinxext.ipython_directive', 48 | 'IPython.sphinxext.ipython_console_highlighting', 49 | 'sphinx.ext.mathjax', 50 | 'sphinx.ext.doctest', 51 | ] 52 | 53 | # Add any paths that contain templates here, relative to this directory. 54 | templates_path = ['_templates'] 55 | 56 | # List of patterns, relative to source directory, that match files and 57 | # directories to ignore when looking for source files. 58 | # This pattern also affects html_static_path and html_extra_path. 59 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 60 | 61 | 62 | # -- Options for HTML output ------------------------------------------------- 63 | 64 | # The theme to use for HTML and HTML Help pages. See the documentation for 65 | # a list of builtin themes. 66 | # 67 | #html_theme = 'alabaster' 68 | html_theme = "pydata_sphinx_theme" 69 | html_logo = "_static/trendypy.png" 70 | html_favicon = "_static/favicon/favicon.ico" 71 | html_theme_options = { 72 | "github_url": "https://github.com/ddaskan/trendypy/", 73 | "twitter_url": "https://twitter.com/_DoganAskan", 74 | "google_analytics_id": "UA-100432301-2", 75 | "navigation_with_keys": True, 76 | "use_edit_page_button": True, 77 | # "search_bar_position": "navbar", 78 | } 79 | 80 | html_context = { 81 | "github_user": "ddaskan", 82 | "github_repo": "trendypy", 83 | "github_version": "master", 84 | "doc_path": "docs", 85 | 'page_source_suffix': '.rst', 86 | } 87 | 88 | # Add any paths that contain custom static files (such as style sheets) here, 89 | # relative to this directory. They are copied after the builtin static files, 90 | # so a file named "default.css" will overwrite the builtin "default.css". 91 | html_static_path = ['_static'] 92 | 93 | autodoc_member_order = 'bysource' 94 | 95 | # Napoleon settings 96 | napoleon_google_docstring = True 97 | napoleon_include_init_with_doc = True 98 | napoleon_include_private_with_doc = True 99 | napoleon_use_admonition_for_examples = False 100 | napoleon_use_ivar = False 101 | napoleon_use_param = True 102 | napoleon_use_rtype = True 103 | napoleon_use_keyword = True 104 | napoleon_custom_sections = None 105 | 106 | copybutton_prompt_text = '>>> |\\\\.\\\\.\\\\. |\\\\$ |In \\\\[\\\\d*\\\\]: |\\\\s+\\.\\.\\.\\.: ' 107 | copybutton_prompt_is_regexp = True 108 | 109 | ipython_savefig_dir = "" 110 | 111 | def linkcode_resolve(domain, info): 112 | """ 113 | Determine the URL corresponding to Python object 114 | """ 115 | if domain != "py": 116 | return None 117 | 118 | modname = info["module"] 119 | fullname = info["fullname"] 120 | 121 | submod = sys.modules.get(modname) 122 | if submod is None: 123 | return None 124 | 125 | obj = submod 126 | for part in fullname.split("."): 127 | try: 128 | obj = getattr(obj, part) 129 | except AttributeError: 130 | return None 131 | 132 | try: 133 | fn = inspect.getsourcefile(inspect.unwrap(obj)) 134 | except TypeError: 135 | fn = None 136 | if not fn: 137 | return None 138 | 139 | try: 140 | source, lineno = inspect.getsourcelines(obj) 141 | except OSError: 142 | lineno = None 143 | 144 | if lineno: 145 | linespec = f"#L{lineno}-L{lineno + len(source) - 1}" 146 | else: 147 | linespec = "" 148 | 149 | filename = info['module'].replace('.', '/') 150 | return "https://github.com/ddaskan/trendypy/blob/master/trendypy/%s.py" % filename+linespec 151 | -------------------------------------------------------------------------------- /trendypy/trendy.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from itertools import combinations 3 | import pickle 4 | import pandas as pd 5 | sys.path.append('../') 6 | from trendypy import algos 7 | 8 | class Trendy(): 9 | '''Estimator to cluster trend-lines and assign new lines accordingly. 10 | 11 | Notes: 12 | Scaling and missing values need to be handled externally. 13 | 14 | Args: 15 | n_clusters (int): The number of clusters to form. 16 | algorithm (callable): Algorithm to calculate the difference. Default 17 | is `fast DTW with Euclidean `_. 18 | 19 | Example: 20 | >>> a = [1, 2, 3, 4, 5] # increasing trend 21 | >>> b = [1, 2.1, 2.9, 4.4, 5.1] # increasing trend 22 | >>> c = [6.2, 5, 4, 3, 2] # decreasing trend 23 | >>> d = [7, 6, 5, 4, 3, 2, 1] # decreasing trend 24 | >>> trendy = Trendy(n_clusters=2) 25 | >>> trendy.fit([a, b, c, d]) 26 | >>> print(trendy.labels_) 27 | [0, 0, 1, 1] 28 | >>> trendy.predict([[0.9, 2, 3.1, 4]]) # another increasing trend 29 | [0] 30 | 31 | ''' 32 | def __init__(self, n_clusters, algorithm=algos.fastdtw_distance): 33 | 34 | self.labels_ = None 35 | self.cluster_centers_ = None 36 | 37 | self.n_clusters = int(n_clusters) 38 | if not self.n_clusters >= 2: 39 | raise ValueError('cluster count must be >= 2') 40 | 41 | self.dist_func = algorithm 42 | if not callable(self.dist_func): 43 | raise TypeError('distance `algorithm` must be a callable') 44 | 45 | def fit(self, X): 46 | '''Compute clustering based on given distance algorithm. 47 | 48 | Args: 49 | X (array of arrays): Training instances to cluster. 50 | 51 | Example: 52 | >>> a = [1, 2, 3, 4, 5] # increasing 53 | >>> b = [1, 2.1, 2.9, 4.4, 5.1] # increasing 54 | >>> c = [6.2, 5, 4, 3, 2] # decreasing 55 | >>> d = [7, 6, 5, 4, 3, 2, 1] # decreasing 56 | >>> trendy = Trendy(2) 57 | >>> trendy.fit([a, b, c, d]) 58 | >>> print(trendy.labels_) 59 | [0, 0, 1, 1] 60 | 61 | ''' 62 | X_len = len(X) 63 | if X_len < self.n_clusters: 64 | raise ValueError('length of `X` < `n_clusters`') 65 | 66 | X_idx = pd.Series(range(X_len)) 67 | combs = combinations(X_idx, self.n_clusters) 68 | combs = [[list(c), -1] for c in combs] 69 | 70 | d_matrix = pd.DataFrame( 71 | X_idx.apply( 72 | lambda x: X_idx.apply( 73 | lambda y: self.dist_func(X[x], X[y])))) 74 | d_matrix.columns, d_matrix.index = X_idx, X_idx 75 | for c in combs: 76 | c[1] = d_matrix.loc[c[0], :].min(axis=0).sum() 77 | 78 | combs.sort(key=lambda x: x[1]) 79 | cluster_idx = combs[0][0] 80 | self.cluster_centers_ = [X[c] for c in cluster_idx] 81 | 82 | self.labels_ = [] 83 | for i in X_idx: 84 | self.labels_.append( 85 | cluster_idx.index( 86 | d_matrix.loc[cluster_idx, i].idxmin())) 87 | 88 | def predict(self, X): 89 | '''Predict the closest cluster each sample in X belongs to. 90 | 91 | Args: 92 | X (array of arrays): New data to predict. 93 | 94 | Returns: 95 | list: Index of the cluster each sample belongs to. 96 | 97 | Example: 98 | >>> a = [1, 2, 3, 4, 5] # increasing 99 | >>> b = [1, 2.1, 2.9, 4.4, 5.1] # increasing 100 | >>> c = [6.2, 5, 4, 3, 2] # decreasing 101 | >>> d = [7, 6, 5, 4, 3, 2, 1] # decreasing 102 | >>> trendy = Trendy(2) 103 | >>> trendy.fit([a, b, c, d]) 104 | >>> trendy.predict([[0.9, 2, 3.1, 4]]) 105 | [0] 106 | >>> trendy.predict([[0.9, 2, 3.1], [7, 6.6, 5.5, 4.4]]) 107 | [0, 1] 108 | 109 | ''' 110 | preds = [] 111 | for x in X: 112 | dists = [self.dist_func(x, c) for c in self.cluster_centers_] 113 | preds.append(pd.Series(dists).idxmin()) 114 | return preds 115 | 116 | def assign(self, X): 117 | '''Alias of `predict()`''' 118 | return self.predict(X) 119 | 120 | def fit_predict(self, X): 121 | '''Compute cluster centers and predict cluster index for each sample. 122 | 123 | Args: 124 | X (array of arrays): Training instances to cluster. 125 | 126 | Returns: 127 | list: predicted labels 128 | 129 | Example: 130 | >>> a = [1, 2, 3, 4, 5] # increasing 131 | >>> b = [1, 2.1, 2.9, 4.4, 5.1] # increasing 132 | >>> c = [6.2, 5, 4, 3, 2] # decreasing 133 | >>> d = [7, 6, 5, 4, 3, 2, 1] # decreasing 134 | >>> trendy = Trendy(2) 135 | >>> trendy.fit_predict([a, b, c, d]) 136 | [0, 0, 1, 1] 137 | 138 | ''' 139 | self.fit(X) 140 | return self.labels_ 141 | 142 | def to_pickle(self, path): 143 | '''Pickle (serialize) object to a file. 144 | 145 | Args: 146 | path (str): file path where the pickled object will be stored 147 | 148 | Example: 149 | To save a `*.pkl` file: 150 | 151 | >>> t1 = Trendy(n_clusters=2) 152 | >>> t1.fit([[1, 2, 3], [2, 3, 3]]) 153 | >>> t1.to_pickle(path='trendy.pkl') 154 | 155 | To load the same object later: 156 | 157 | >>> import pickle, os 158 | >>> pkl_file = open('trendy.pkl', 'rb') 159 | >>> t2 = pickle.load(pkl_file) 160 | >>> pkl_file.close() 161 | >>> os.remove('trendy.pkl') 162 | 163 | ''' 164 | output = open(path, 'wb') 165 | pickle.dump(self, output, -1) 166 | output.close() 167 | -------------------------------------------------------------------------------- /docs/source/seeinaction.rst: -------------------------------------------------------------------------------- 1 | See in Action 2 | ============= 3 | 4 | Let's see how TrendyPy works with a few use cases. 5 | 6 | Stock Data 7 | ---------- 8 | 9 | In this demo, I'd like to show you how to use TrendyPy in some :download:`stock data <../stock_data.csv>` between 2018-01-01 and 2020-06-28. You can download the data from :download:`here <../stock_data.csv>` to reproduce the demo. 10 | 11 | Let's say we have some stock data from a combination of tech and banking. And, we want to identify an unknown trend if it's a tech stock or banking. For this purpose, we'll use FB (i.e. Facebook), GOOGL (i.e. Google), AMZN (i.e Amazon), BAC (i.e. Bank of America) and WFC (i.e. Wells Fargo) for training data then AAPL (i.e. Apple) and c (i.e. Citigroup) for prediction data. 12 | 13 | But first, here is how the data looks. 14 | 15 | .. ipython:: python 16 | 17 | import pandas as pd 18 | import matplotlib.pyplot as plt 19 | df = pd.read_csv('stock_data.csv') 20 | @savefig ticks_raw.png 21 | df.plot() 22 | 23 | If we cluster like this, the expensive stocks like GOOGL and AMZN will alone constitute one cluster which it's clearly not intended. So, let's scale first. 24 | 25 | .. ipython:: python 26 | 27 | from trendypy import utils 28 | df = df.apply(utils.scale_01) 29 | @savefig ticks_scaled.png 30 | df.plot() 31 | 32 | It's a bit apparent that BAC, WFC and c are different than the others. Let's put sectors side by side to see the difference better. 33 | 34 | .. ipython:: python 35 | 36 | fig, axes_ = plt.subplots(nrows=1, ncols=2) 37 | axes_[0].set_title('Tech') 38 | axes_[1].set_title('Banking') 39 | df[['AAPL', 'FB', 'GOOGL', 'AMZN']].plot(ax=axes_[0]) 40 | @savefig ticks_scaled_subplot.png 41 | df[['BAC', 'WFC', 'c']].plot(ax=axes_[1]) 42 | 43 | 44 | Now, we can use the training data to fit. Remember, we're setting AAPL and c aside to predict later and only fit by using the rest. 45 | 46 | .. ipython:: python 47 | 48 | from trendypy.trendy import Trendy 49 | trendy = Trendy(n_clusters=2) # 2 for tech and banking 50 | trendy.fit([df.FB, df.GOOGL, df.AMZN, df.BAC, df.WFC]) 51 | trendy.labels_ 52 | 53 | .. ipython:: python 54 | :suppress: 55 | 56 | assert trendy.labels_ == [0, 0, 0, 1, 1] 57 | 58 | You can also use `fit_predict `_ method for this purpose, it's essentially the same. 59 | 60 | .. ipython:: python 61 | 62 | trendy.fit_predict([df.FB, df.GOOGL, df.AMZN, df.BAC, df.WFC]) 63 | 64 | .. ipython:: python 65 | :suppress: 66 | 67 | assert trendy.fit_predict([df.FB, df.GOOGL, df.AMZN, df.BAC, df.WFC]) == [0, 0, 0, 1, 1] 68 | 69 | As expected, it successfully assigns FB, GOOGL and AMZN into the first cluster (i.e. ``0``) and BAC and WFC into the second (i.e. ``1``). So, we can name ``0`` as tech and ``1`` as banking. 70 | 71 | Now, let's make predictions on the prediction data that we set aside earlier (i.e. AAPL, c). 72 | 73 | .. ipython:: python 74 | :suppress: 75 | 76 | assert trendy.predict([df.AAPL]) == [0] 77 | assert trendy.predict([df.c]) == [1] 78 | 79 | .. ipython:: python 80 | 81 | trendy.predict([df.AAPL]) # expecting `0` since AAPL is a part of tech 82 | trendy.predict([df.c]) # expecting `1` since c is a part of banking 83 | 84 | As seen above, it correctly predicts trends. 85 | 86 | You can easily pickle the model object to be used later with `to_pickle `_ method. 87 | 88 | .. ipython:: python 89 | 90 | trendy.to_pickle('my_first_trendy.pkl') 91 | 92 | .. ipython:: python 93 | :suppress: 94 | 95 | import os 96 | os.remove('my_first_trendy.pkl') 97 | 98 | And, that's all. 99 | 100 | 101 | Image Clustering 102 | ---------------- 103 | 104 | If you have the proper distance metric function for the right data, you can use TrendyPy to even cluster images. In this demo, I'll use black & white images from `MPEG7 CE Shape-1 Part B `_ database. The goal is to correctly cluster the images and assign new ones to the appropriate clusters. Here are some simple images that'll be used to create the clusters. Each image is slightly different than the others in the same group. You can :download:`download the images <../image_data.zip>` if you want to reproduce the demo. 105 | 106 | +------------------------------------------+------------------------------------------+------------------------------------------+ 107 | | .. figure:: ../image_data/car-01.gif | .. figure:: ../image_data/car-02.gif | .. figure:: ../image_data/car-03.gif | 108 | | | | | 109 | | car-01.gif | car-02.gif | car-03.gif | 110 | +------------------------------------------+------------------------------------------+------------------------------------------+ 111 | | .. figure:: ../image_data/carriage-02.gif| .. figure:: ../image_data/carriage-03.gif| .. figure:: ../image_data/carriage-04.gif| 112 | | | | | 113 | | carriage-02.gif | carriage-03.gif | carriage-04.gif | 114 | +------------------------------------------+------------------------------------------+------------------------------------------+ 115 | | .. figure:: ../image_data/chopper-01.gif | .. figure:: ../image_data/chopper-02.gif | .. figure:: ../image_data/chopper-03.gif | 116 | | | | | 117 | | chopper-01.gif | chopper-02.gif | chopper-03.gif | 118 | +------------------------------------------+------------------------------------------+------------------------------------------+ 119 | 120 | Define a function to read the image and convert to a numpy array. 121 | 122 | .. ipython:: python 123 | 124 | from PIL import Image 125 | import numpy as np 126 | def load_image(file): 127 | img = Image.open(file) 128 | img.load() 129 | return np.asarray(img, dtype="int32") 130 | 131 | Read images and assign them into lists. 132 | 133 | .. ipython:: python 134 | 135 | cars = [ 136 | load_image('image_data/car-01.gif'), 137 | load_image('image_data/car-02.gif'), 138 | load_image('image_data/car-03.gif') 139 | ] 140 | carriages = [ 141 | load_image('image_data/carriage-02.gif'), 142 | load_image('image_data/carriage-03.gif'), 143 | load_image('image_data/carriage-04.gif') 144 | ] 145 | choppers = [ 146 | load_image('image_data/chopper-01.gif'), 147 | load_image('image_data/chopper-02.gif'), 148 | load_image('image_data/chopper-03.gif') 149 | ] 150 | 151 | `Euclidean Distance `_ can be used to calculate the similarity between images. So, let's import `euclidean_distance `_ from `utils `_ module, then assign it as `algorithm` argument during the initialization. 152 | 153 | .. ipython:: python 154 | 155 | from trendypy.trendy import Trendy 156 | from trendypy.utils import euclidean_distance 157 | trendy = Trendy(n_clusters=3, algorithm=euclidean_distance) 158 | trendy.fit(cars + carriages + choppers) 159 | trendy.labels_ 160 | 161 | .. ipython:: python 162 | :suppress: 163 | 164 | assert trendy.labels_ == [0, 0, 0, 1, 1, 1, 2, 2, 2] 165 | 166 | As expected, it correctly clusters these simple images. Let's see if it predicts new data correctly. 167 | 168 | +------------------------------------------+------------------------------------------+------------------------------------------+ 169 | | .. figure:: ../image_data/car-20.gif | .. figure:: ../image_data/carriage-20.gif| .. figure:: ../image_data/chopper-08.gif | 170 | | | | | 171 | | car-20.gif | carriage-20.gif | chopper-08.gif | 172 | +------------------------------------------+------------------------------------------+------------------------------------------+ 173 | 174 | .. ipython:: python 175 | 176 | new_car = load_image('image_data/car-20.gif') 177 | new_carriage = load_image('image_data/carriage-20.gif') 178 | new_chopper = load_image('image_data/chopper-08.gif') 179 | trendy.predict([new_car, new_carriage, new_chopper]) 180 | 181 | .. ipython:: python 182 | :suppress: 183 | 184 | assert trendy.predict([new_car, new_carriage, new_chopper]) == [0, 1, 2] 185 | 186 | Looks like it correctly predicts new data as well. 187 | 188 | .. note:: 189 | 190 | Because of the limitation of the selected metric function (i.e. `Euclidean Distance `_), I had to cherry pick images with exact same sizes (i.e. 352×288). Depending on the function you choose, you may or may not do the same. 191 | 192 | 193 | Custom Metric 194 | ------------- 195 | 196 | TrendyPy is flexible enough to be able to utilize user defined metrics. In this example, I'll show how to create your own metric and use it during the clustering. 197 | 198 | Let's say we want to cluster DNA sequences and need a metric to do that. `Needleman–Wunsch algorithm `_ is an algorithm used in bioinformatics to align protein or nucleotide sequences. It's not a metric but it inspires us to create our own metric. The metric basically compares two sequences with same length and it penalizes each mismatch by increasing the distance by `p` then divides it to total length. 199 | 200 | .. ipython:: python 201 | 202 | def my_metric(x, y, p=1): 203 | assert len(x) == len(y) 204 | dist = 0 205 | for i in range(len(x)): 206 | if x[i] != y[i]: 207 | dist += p 208 | return dist/len(x) 209 | 210 | As you can see, you just need to consider inputs and output of your custom function. Specifically, 211 | 212 | #. Input must have `x` and `y` for two data points to compare. You may have other default arguments (e.g. `p`). 213 | #. Output must be a float. 0 indicates same and greater is farther. 214 | 215 | .. note:: 216 | 217 | Technically, any float range should work as the output of the custom function as long as greater is farther. However, it won't be named as `metric` in that case. 218 | 219 | Anyway, let's use it. 220 | 221 | .. ipython:: python 222 | 223 | set_of_sequences = [ 224 | 'AAATTT', 'AAACTT', 'AAATCT', # group 1 225 | 'GACTAG', 'GGCTAG', 'GACAAG' # group 2 226 | ] 227 | 228 | .. ipython:: python 229 | 230 | from trendypy.trendy import Trendy 231 | trendy = Trendy( 232 | n_clusters=2, # there are 2 groups 233 | algorithm=my_metric # this is where to set custom metric 234 | ) 235 | trendy.fit(set_of_sequences) 236 | trendy.labels_ 237 | 238 | .. ipython:: python 239 | :suppress: 240 | 241 | assert trendy.labels_ == [0, 0, 0, 1, 1, 1] 242 | 243 | It clearly clusters first and second group. Now, let's see on new data. 244 | 245 | .. ipython:: python 246 | 247 | new_seq1 = 'AAAGGT' # similar to group 1 248 | new_seq2 = 'GTCCAG' # similar to group 2 249 | trendy.predict([new_seq1, new_seq2]) 250 | 251 | .. ipython:: python 252 | :suppress: 253 | 254 | assert trendy.predict([new_seq1, new_seq2]) == [0, 1] 255 | 256 | Very simple. 257 | -------------------------------------------------------------------------------- /docs/stock_data.csv: -------------------------------------------------------------------------------- 1 | AAPL,FB,GOOGL,AMZN,BAC,WFC,c 2 | 170.16,177.68,1053.02,1172.0,29.75,61.04,75.09 3 | 172.53,181.88,1073.93,1188.3,29.9,61.22,74.35 4 | 172.54,184.9,1097.09,1205.0,29.97,61.98,75.01 5 | 173.44,185.59,1103.45,1217.51,30.37,62.76,75.71 6 | 174.35,187.2,1111.0,1236.0,30.23,62.66,75.17 7 | 174.55,188.7,1118.44,1256.9,30.2,62.3,74.76 8 | 173.16,186.94,1107.0,1245.15,30.37,62.48,75.7 9 | 174.59,188.4,1112.31,1259.74,30.66,63.65,75.98 10 | 176.18,178.06,1110.1,1273.39,30.88,63.2,75.9 11 | 177.9,181.5,1140.31,1323.0,31.74,62.79,77.76 12 | 176.15,179.26,1136.36,1312.24,31.0,62.85,76.89 13 | 179.37,178.13,1139.35,1293.95,31.33,63.87,77.66 14 | 178.61,180.85,1138.03,1312.0,31.58,64.15,77.57 15 | 177.3,180.8,1143.82,1297.17,31.67,64.0,78.24 16 | 177.3,186.05,1170.62,1338.09,31.86,64.31,78.46 17 | 177.25,189.89,1184.98,1374.82,32.01,64.95,78.96 18 | 174.505,187.95,1180.71,1368.0,32.24,65.7,79.85 19 | 172.0,187.75,1187.53,1392.01,32.11,65.89,79.61 20 | 170.16,188.75,1188.0,1409.18,32.25,65.78,80.12 21 | 165.525,183.01,1177.72,1403.17,31.95,65.43,79.2 22 | 166.87,188.37,1183.81,1451.3,32.05,65.27,78.78 23 | 167.165,188.22,1175.99,1445.0,32.0,65.37,78.27 24 | 166.0,192.04,1127.42,1477.39,32.44,65.33,78.65 25 | 159.1,186.93,1100.61,1402.62,31.12,58.7,75.52 26 | 154.83,178.57,1033.98,1361.46,29.41,56.905,71.52 27 | 163.085,184.15,1084.97,1449.0,31.12,56.94,74.77 28 | 160.29,181.01,1059.87,1429.67,31.31,57.33,75.07 29 | 157.07,174.76,1025.88,1373.49,30.17,56.14,73.02 30 | 158.5,177.06,1056.67,1364.67,30.62,56.36,74.47 31 | 161.95,175.62,1050.0,1385.93,31.04,56.06,74.34 32 | 163.045,173.45,1054.32,1406.25,31.17,57.85,75.03 33 | 169.79,180.5,1083.45,1466.89,32.39,59.82,77.18 34 | 172.36,178.99,1093.38,1457.37,32.0,59.67,76.8 35 | 172.05,175.77,1092.76,1446.49,31.96,59.86,76.64 36 | 172.83,176.71,1109.1,1485.0,31.8,59.86,76.4 37 | 171.8,178.7,1119.17,1495.36,31.99,59.88,77.28 38 | 173.67,179.9,1118.66,1495.34,31.8,59.07,76.48 39 | 176.35,184.58,1131.86,1509.2,32.17,59.75,77.23 40 | 179.1,184.45,1143.7,1524.5,32.32,60.03,77.52 41 | 179.26,182.3,1122.0,1519.51,32.49,59.47,76.6 42 | 178.54,179.01,1109.54,1513.6,32.07,58.25,75.5 43 | 172.8,173.29,1057.98,1469.1,31.12,56.92,73.17 44 | 175.21,176.2,1078.13,1494.24,31.35,56.81,73.07 45 | 177.91,181.78,1102.1,1533.2,32.3,57.71,74.93 46 | 174.94,178.74,1092.82,1526.52,31.7,56.4,73.18 47 | 175.48,183.56,1117.2,1550.0,32.19,56.75,74.14 48 | 177.96,183.91,1139.5,1563.5,32.47,57.16,74.95 49 | 180.29,185.23,1165.05,1592.6,32.69,58.27,75.95 50 | 182.59,185.61,1171.83,1615.96,32.97,58.39,76.14 51 | 180.32,182.6,1145.8,1597.0,32.55,57.86,75.28 52 | 178.5,183.24,1149.57,1595.0,32.29,56.77,73.78 53 | 178.65,184.49,1155.35,1583.45,32.11,56.99,73.17 54 | 177.32,177.01,1117.76,1554.53,32.13,55.88,73.47 55 | 175.24,167.47,1098.4,1550.34,32.06,55.77,73.05 56 | 175.04,164.8,1092.57,1586.45,32.0,55.02,73.18 57 | 170.0,166.13,1080.01,1565.47,31.44,54.21,72.33 58 | 168.39,165.44,1051.37,1539.01,30.69,52.72,70.32 59 | 168.07,160.82,1050.6,1530.0,29.89,51.95,69.11 60 | 173.68,156.31,1063.9,1572.4,30.65,52.4,70.24 61 | 167.25,151.65,1001.91,1447.0,29.79,51.16,68.48 62 | 167.805,155.15,1011.21,1406.0,29.57,52.06,68.52 63 | 167.88,157.81,1027.62,1417.62,29.8,52.37,68.33 64 | 167.64,156.55,1016.15,1391.38,29.54,51.72,68.05 65 | 164.88,152.025,998.23,1358.24,29.0,51.13,67.16 66 | 172.58,161.56,1046.39,1441.99,30.18,53.34,70.07 67 | 170.97,157.73,1023.1,1429.97,30.01,52.65,69.52 68 | 169.88,157.82,1020.04,1425.03,29.82,52.5,69.06 69 | 173.0,157.93,1030.26,1431.99,30.48,52.99,71.05 70 | 172.23,165.36,1032.0,1439.44,30.15,52.32,69.97 71 | 173.41,166.98,1031.47,1439.5,30.22,52.16,70.47 72 | 174.78,164.58,1046.89,1449.14,31.13,52.01,73.21 73 | 175.0301,165.7249,1045.55,1445.0,30.08,50.59,71.09 74 | 176.49,165.83,1061.2,1462.3,30.17,50.95,70.65 75 | 177.81,166.88,1079.01,1514.65,30.03,50.78,69.81 76 | 174.95,166.2,1069.02,1543.22,29.55,50.51,69.1 77 | 170.595,167.79,1084.27,1561.2,30.26,51.93,70.4 78 | 166.8348,167.27,1082.98,1546.69,30.27,52.55,70.0 79 | 165.67,165.43,1059.2,1535.8,30.46,52.95,69.76 80 | 162.62,160.1448,1029.75,1458.0,30.09,52.44,68.77 81 | 164.12,173.22,1033.22,1485.01,30.04,52.99,69.11 82 | 164.0,176.81,1045.54,1634.01,29.99,52.28,69.0 83 | 162.1302,173.79,1034.42,1582.5,30.27,52.56,69.2 84 | 166.4102,172.0,1016.3,1563.22,29.92,51.76,68.24 85 | 175.225,174.246,1034.0,1580.98,29.95,52.38,67.95 86 | 175.88,175.13,1025.37,1560.01,29.51,51.62,67.8 87 | 178.25,173.08,1019.61,1562.45,28.99,51.17,67.05 88 | 185.18,177.35,1053.89,1589.34,29.44,52.97,68.27 89 | 184.99,178.25,1064.62,1595.0,29.77,52.72,69.65 90 | 186.55,179.67,1064.1,1600.0,30.1,53.45,71.23 91 | 187.74,183.15,1095.0,1608.48,30.61,54.13,71.96 92 | 189.49,184.85,1100.41,1610.99,30.91,54.69,72.53 93 | 189.01,187.71,1105.57,1604.0,31.04,54.78,72.93 94 | 186.78,184.88,1096.9,1587.8,30.92,54.43,72.51 95 | 186.07,183.6952,1085.09,1577.5,31.18,54.7,72.44 96 | 188.0,182.68,1081.46,1580.56,31.04,54.4,71.73 97 | 187.19,183.49,1066.0,1581.33,30.77,54.15,71.26 98 | 188.0,183.77,1079.0,1585.0,30.52,54.08,70.65 99 | 188.375,184.93,1089.8,1589.89,30.6,54.63,70.79 100 | 186.35,182.5,1070.0,1571.05,30.71,55.03,70.65 101 | 188.77,185.88,1086.9,1598.03,30.4,55.87,69.84 102 | 188.23,186.02,1086.55,1603.0,30.01,54.6,68.93 103 | 187.6,184.34,1076.0,1600.71,29.76,54.04,67.12 104 | 187.72,186.54,1073.48,1618.1,29.29,53.66,66.56 105 | 187.22,187.87,1082.0,1623.0,29.2,54.21,66.55 106 | 187.9912,193.065,1112.87,1637.03,29.49,54.56,67.49 107 | 191.635,191.84,1138.5,1648.9,29.55,54.79,67.44 108 | 193.065,194.3,1154.66,1672.99,29.31,54.61,67.08 109 | 193.63,191.0252,1152.77,1704.51,29.29,54.72,67.21 110 | 194.14,190.75,1144.58,1698.56,30.2,55.77,68.93 111 | 191.17,187.53,1131.21,1681.12,30.01,55.56,68.13 112 | 191.35,188.81,1132.94,1681.51,30.13,55.79,68.92 113 | 191.385,192.17,1141.02,1693.0,30.17,55.64,68.26 114 | 192.42,192.74,1152.28,1702.81,29.98,55.39,67.82 115 | 191.55,193.1,1152.21,1713.48,30.04,55.33,67.56 116 | 190.03,195.79,1159.92,1714.0,29.25,54.55,66.32 117 | 187.88,194.8,1152.69,1706.26,28.94,54.51,65.75 118 | 185.14,196.2352,1170.11,1709.04,29.04,54.68,66.03 119 | 186.35,199.1,1183.3,1742.5,29.42,55.46,67.72 120 | 187.25,202.76,1185.51,1760.0,29.21,54.25,67.16 121 | 186.12,201.16,1171.49,1742.62,29.46,54.96,68.04 122 | 183.4,200.0,1155.0,1702.51,28.86,53.78,66.79 123 | 182.99,197.6,1144.14,1672.37,28.52,53.9,65.51 124 | 185.2278,199.18,1136.0,1708.11,28.51,53.24,66.22 125 | 184.1,195.18,1112.39,1672.54,28.4,53.55,65.98 126 | 186.29,197.32,1132.31,1717.0,29.09,55.62,68.0 127 | 183.82,193.37,1115.35,1682.7,28.08,55.2,66.21 128 | 187.79,194.55,1149.42,1723.96,28.33,56.43,67.45 129 | 185.26,194.74,1124.6,1705.38,27.95,55.92,66.44 130 | 185.42,198.45,1141.77,1696.0,27.81,55.3,66.22 131 | 189.5,204.93,1160.0,1724.05,28.23,56.19,67.5 132 | 190.71,204.5,1169.99,1738.53,29.22,57.06,69.23 133 | 188.5,202.22,1155.62,1737.99,28.67,56.4,67.97 134 | 189.53,203.43,1174.86,1764.51,28.9,56.46,68.36 135 | 191.08,207.81,1202.8,1803.93,28.62,54.74,67.99 136 | 191.52,207.5,1203.81,1821.95,28.78,55.74,67.36 137 | 189.75,204.9,1182.74,1811.56,29.89,56.95,69.5 138 | 191.78,209.82,1208.53,1848.0,29.92,56.26,69.22 139 | 189.69,208.77,1206.65,1829.46,29.93,56.26,69.5 140 | 191.78,208.85,1199.24,1825.01,29.62,56.07,68.79 141 | 190.68,210.58,1195.66,1812.21,30.14,56.39,69.16 142 | 192.45,215.11,1271.0,1829.01,30.85,58.0,70.4 143 | 193.0602,215.715,1252.62,1829.3,30.7,58.1,70.82 144 | 194.61,174.89,1267.18,1839.0,31.19,58.47,71.75 145 | 194.99,179.87,1289.12,1876.05,30.99,58.38,71.36 146 | 191.9,175.3,1245.05,1827.33,31.14,58.83,71.86 147 | 190.3,170.67,1231.71,1786.49,31.36,58.61,72.2 148 | 199.13,173.93,1239.11,1784.0,31.22,57.96,72.5 149 | 200.58,170.68,1218.5,1788.77,30.98,57.08,71.34 150 | 207.03,177.69,1245.18,1837.74,31.18,58.06,71.64 151 | 208.0,178.97,1241.61,1825.81,31.45,58.58,72.16 152 | 209.32,186.5,1252.01,1854.53,31.55,58.94,72.57 153 | 206.05,184.75,1256.72,1861.0,31.51,58.76,72.14 154 | 207.28,185.8492,1262.73,1882.0,31.73,58.65,72.44 155 | 207.36,182.04,1259.18,1888.51,31.23,57.38,70.63 156 | 207.7,180.1,1251.2,1898.5,31.08,57.66,70.2 157 | 210.155,180.71,1252.5,1919.39,30.61,57.67,69.3 158 | 209.22,179.34,1244.28,1909.55,30.48,57.68,69.23 159 | 211.75,180.42,1241.99,1903.94,30.56,58.14,69.16 160 | 213.44,174.5,1222.04,1885.8,30.66,58.51,69.42 161 | 218.1,174.04,1220.66,1890.57,30.69,58.81,69.46 162 | 216.8,172.81,1223.02,1880.0,30.91,59.0,70.63 163 | 214.1,172.21,1214.22,1876.64,30.92,59.04,71.16 164 | 214.65,173.09,1219.88,1907.17,30.94,59.0,71.03 165 | 216.6,173.7,1226.0,1910.51,30.94,58.9,70.6 166 | 217.15,175.99,1244.14,1915.0,31.03,58.87,71.07 167 | 219.01,178.1,1255.9,1937.73,31.36,59.19,72.57 168 | 220.15,176.295,1255.0,1953.45,31.24,59.05,72.3 169 | 223.25,175.9,1263.4,1997.42,31.02,58.67,72.09 170 | 226.51,177.15,1252.21,2007.0,30.88,58.27,71.05 171 | 228.41,173.5,1222.52,2026.5,30.91,58.45,70.99 172 | 228.99,169.49,1209.22,2038.11,31.07,58.88,71.2 173 | 226.23,166.98,1198.57,2006.51,31.05,58.91,70.93 174 | 221.85,160.31,1172.0,1938.71,31.0,57.92,70.34 175 | 220.95,163.51,1184.2,1971.0,30.88,57.62,69.98 176 | 218.01,163.94,1171.1,1928.27,30.72,57.4,69.26 177 | 224.94,163.25,1182.0,1994.0,30.88,57.33,70.96 178 | 223.52,162.0,1179.7,2000.0,30.53,56.01,70.9 179 | 225.75,161.715,1188.0,1992.93,30.13,55.19,70.18 180 | 222.15,161.92,1177.77,1954.73,30.34,54.75,70.79 181 | 217.79,159.39,1162.66,1918.65,30.33,54.25,71.3 182 | 218.5,160.08,1168.96,1940.5,30.13,54.49,71.54 183 | 220.24,164.5,1180.67,1938.58,31.3,55.63,74.5 184 | 220.78,166.64,1194.92,1954.22,31.34,55.75,74.98 185 | 216.82,161.03,1159.41,1903.79,30.98,54.8,74.08 186 | 219.75,161.99,1184.25,1942.9,30.85,54.3,74.22 187 | 221.0,164.3,1193.69,1968.5,30.71,54.38,73.52 188 | 223.82,167.55,1200.0,1993.24,30.24,53.28,72.9 189 | 224.79,168.33,1204.09,2004.41,29.65,52.69,72.06 190 | 227.95,163.03,1213.0,2021.99,29.68,52.74,72.24 191 | 227.25,161.58,1206.67,1999.99,29.58,52.24,71.51 192 | 230.05,160.0,1212.0,1981.7,29.81,52.5,72.54 193 | 230.78,161.46,1205.03,1949.0,30.17,52.73,72.78 194 | 227.96,159.21,1176.0,1917.99,30.6,53.7,72.95 195 | 222.21,155.54,1160.0,1874.0,30.05,52.98,71.69 196 | 223.64,157.69,1151.31,1859.99,30.03,53.38,72.06 197 | 225.46,156.82,1136.4,1857.89,29.98,53.7,72.07 198 | 214.52,150.13,1079.04,1724.0,28.89,52.38,69.14 199 | 220.42,156.73,1119.64,1808.0,28.99,52.1,70.45 200 | 221.16,153.32,1118.0,1795.0,28.49,52.51,70.13 201 | 218.93,155.4,1113.48,1783.5,28.24,53.31,69.89 202 | 222.3,159.56,1140.0,1842.79,28.37,53.56,69.35 203 | 217.86,158.51,1130.0,1821.49,28.67,54.17,69.54 204 | 218.06,155.86,1103.71,1785.16,28.14,53.08,68.54 205 | 219.79,154.76,1112.51,1784.0,28.32,53.07,68.89 206 | 215.83,151.22,1091.29,1742.24,26.39,50.84,64.15 207 | 222.6,154.28,1115.0,1773.7,26.86,51.31,65.41 208 | 217.71,147.73,1080.3,1703.34,26.38,50.53,64.77 209 | 215.9,145.82,1048.33,1649.59,26.33,51.315,64.17 210 | 219.19,148.5,1096.54,1660.0,26.69,51.88,65.06 211 | 211.15,139.935,1020.01,1486.16,26.76,52.45,64.6 212 | 216.88,155.0,1068.2,1569.99,27.1,53.04,65.01 213 | 219.05,151.52,1091.4,1623.53,27.77,53.43,66.09 214 | 209.55,151.8,1089.0,1678.59,28.01,54.21,66.07 215 | 204.3,150.1,1072.53,1657.57,27.95,53.73,66.3 216 | 201.92,149.31,1055.02,1618.35,28.02,53.36,67.31 217 | 205.97,151.57,1083.49,1673.0,28.415,53.82,67.29 218 | 209.98,150.49,1107.3,1755.0,28.5,52.9,67.94 219 | 205.55,146.75,1084.02,1732.5,28.75,53.085,67.2 220 | 199.0,144.48,1073.13,1698.24,28.38,53.13,65.56 221 | 191.63,142.0,1054.81,1649.29,27.75,52.62,64.38 222 | 193.9,143.7,1060.0,1656.32,27.95,53.14,65.39 223 | 188.39,142.33,1051.46,1581.01,27.14,51.54,62.97 224 | 190.5,141.07,1065.23,1587.5,27.69,52.65,64.49 225 | 190.0,137.61,1063.39,1577.01,27.78,52.97,65.08 226 | 178.37,127.03,1007.29,1437.5,27.42,53.05,63.82 227 | 179.73,134.4,1045.31,1542.99,27.59,52.62,62.76 228 | 174.94,133.65,1033.5,1517.0,27.08,51.97,62.33 229 | 174.24,133.0,1044.0,1539.0,27.18,52.33,62.67 230 | 171.51,135.75,1051.34,1575.99,27.43,52.69,63.35 231 | 176.73,136.28,1057.46,1613.92,27.88,53.15,63.64 232 | 182.66,135.92,1083.53,1674.99,28.18,53.97,65.09 233 | 180.29,138.26,1095.63,1679.5,27.94,54.08,64.37 234 | 184.46,143.0,1132.16,1769.46,28.99,54.78,66.04 235 | 180.95,140.73,1112.99,1756.0,28.32,53.95,64.58 236 | 171.76,133.82,1045.0,1614.87,26.23,50.65,59.36 237 | 173.49,139.25,1072.23,1705.07,26.15,50.89,59.72 238 | 165.0,139.6,1042.94,1623.84,25.18,50.07,57.95 239 | 171.66,143.88,1066.94,1678.0,25.21,49.41,58.17 240 | 170.4,143.08,1077.08,1669.0,24.87,48.29,57.13 241 | 170.49,145.57,1075.67,1680.0,24.66,47.97,56.38 242 | 169.0,143.34,1060.02,1638.0,24.18,46.55,54.73 243 | 165.45,143.08,1047.01,1566.0,24.26,46.22,54.85 244 | 165.38,141.08,1034.0,1540.0,24.67,46.94,54.72 245 | 166.0,141.21,1047.29,1543.05,24.45,46.39,53.9 246 | 160.4,130.7,1026.0,1484.0,23.85,45.4,52.21 247 | 156.86,133.39,1032.04,1464.99,23.91,45.59,51.9 248 | 148.15,123.1,984.32,1346.0,22.76,44.47,49.4 249 | 148.3,126.0,997.99,1368.89,22.91,43.84,49.65 250 | 155.84,132.44,1026.2,1454.2,23.78,44.72,50.62 251 | 157.5,135.34,1059.5,1473.35,24.59,45.68,51.93 252 | 158.53,134.45,1057.83,1510.8,24.57,45.98,51.98 253 | 154.89,128.99,1027.2,1465.2,24.08,45.525,50.68 254 | 143.98,134.69,1050.67,1520.01,24.94,46.65,53.41 255 | 144.53,134.01,1042.56,1530.0,25.1,47.42,54.01 256 | 148.7,137.56,1080.97,1602.31,25.56,47.78,55.33 257 | 149.56,139.89,1086.0,1664.69,25.72,47.9,56.03 258 | 151.29,142.95,1087.99,1652.98,25.67,47.51,55.71 259 | 152.5,143.08,1074.94,1641.01,25.56,47.5,55.99 260 | 152.88,143.15,1069.9,1640.55,25.53,47.5,56.46 261 | 150.85,142.0,1053.34,1615.0,25.77,47.51,56.1 262 | 150.27,146.01,1058.01,1632.0,26.21,47.6,59.89 263 | 153.08,149.0,1090.0,1684.22,28.13,48.15,61.95 264 | 154.2,146.95,1087.99,1680.0,28.34,48.64,61.63 265 | 157.5,149.75,1108.59,1712.0,29.02,49.1,62.86 266 | 156.41,149.2,1096.0,1681.0,28.98,49.77,62.67 267 | 154.15,148.28,1086.86,1656.0,29.22,50.0,62.27 268 | 154.11,144.64,1082.51,1641.07,28.75,49.79,61.74 269 | 155.48,147.48,1094.23,1670.5,29.28,50.4,63.33 270 | 155.79,148.05,1090.07,1643.59,29.32,49.77,63.19 271 | 156.25,148.09,1081.04,1631.27,29.54,49.78,63.71 272 | 163.25,146.22,1077.36,1623.0,29.42,49.84,63.46 273 | 166.11,165.6,1112.24,1692.85,28.75,49.28,63.65 274 | 166.96,165.84,1122.29,1638.88,28.6,49.0,64.25 275 | 167.41,165.7,1119.01,1623.0,28.47,48.85,63.5 276 | 172.86,169.15,1129.63,1643.34,28.85,49.27,64.19 277 | 174.65,171.2,1149.27,1670.75,28.67,49.09,63.83 278 | 172.4,168.2,1111.82,1625.0,28.54,49.09,63.61 279 | 168.99,164.47,1094.88,1586.0,28.15,47.94,62.13 280 | 171.05,167.9,1103.75,1600.98,28.34,47.87,62.41 281 | 170.1,166.86,1111.01,1604.0,28.62,48.01,62.15 282 | 171.39,165.38,1133.04,1647.0,28.87,49.24,63.3 283 | 169.71,163.19,1125.0,1624.5,28.36,48.63,62.35 284 | 171.25,164.51,1139.3,1627.86,28.76,49.09,63.14 285 | 169.71,160.5,1116.64,1601.0,28.93,49.03,63.73 286 | 171.19,162.25,1128.88,1630.0,29.1,49.43,64.5 287 | 171.8,161.93,1118.78,1619.85,29.33,49.76,64.55 288 | 171.58,160.58,1109.7,1623.5,29.05,49.6,64.63 289 | 174.16,163.07,1121.93,1641.45,29.12,49.26,64.59 290 | 173.71,164.335,1114.37,1625.98,29.02,49.49,64.09 291 | 173.21,162.9,1114.01,1628.18,29.3,49.59,64.24 292 | 174.32,162.37,1119.0,1635.25,29.5,49.98,64.46 293 | 174.28,162.6,1131.0,1655.13,29.33,50.23,64.78 294 | 175.69,163.9,1154.56,1685.0,29.33,50.29,64.53 295 | 175.94,167.37,1156.0,1702.95,29.02,50.03,63.66 296 | 174.67,172.9,1171.76,1695.97,29.01,49.97,62.7 297 | 173.87,171.5,1160.5,1667.37,28.67,49.71,62.03 298 | 170.32,166.2,1133.9,1604.01,28.06,49.26,60.96 299 | 175.49,171.6,1152.0,1626.12,28.7,50.0,62.53 300 | 180.0,172.09,1182.3,1669.0,29.0,49.78,62.3 301 | 182.25,172.32,1205.93,1683.0,29.05,49.87,63.14 302 | 183.9,169.76,1199.02,1691.2,29.31,50.07,63.69 303 | 184.85,167.16,1198.0,1703.0,29.44,50.22,64.16 304 | 185.8,163.57,1189.69,1712.7,29.38,50.84,65.21 305 | 188.35,161.48,1191.72,1753.51,30.01,52.42,66.46 306 | 186.23,161.5,1201.4,1769.94,29.62,51.19,65.26 307 | 190.02,164.89,1220.0,1796.26,28.45,49.98,63.67 308 | 195.34,165.65,1228.85,1810.17,27.91,49.25,62.98 309 | 191.51,163.0,1199.56,1757.79,26.91,48.42,60.98 310 | 191.664,167.35,1205.19,1793.0,27.15,48.52,60.99 311 | 188.75,167.85,1191.92,1784.13,27.09,48.94,61.38 312 | 188.95,164.57,1175.5,1770.0,27.2,48.73,60.95 313 | 189.83,166.39,1180.18,1786.58,27.55,49.8,62.33 314 | 191.64,167.83,1187.54,1800.11,27.9,48.43,62.85 315 | 191.09,170.14,1200.05,1811.02,28.47,48.57,64.24 316 | 193.25,174.5,1212.7,1826.72,29.07,48.47,64.99 317 | 194.79,176.02,1211.29,1820.65,28.85,48.95,64.81 318 | 196.45,176.88,1219.3,1829.0,29.32,49.26,65.93 319 | 196.42,175.21,1211.11,1833.23,28.89,48.64,65.13 320 | 200.32,175.62,1201.89,1845.49,29.0,48.68,65.6 321 | 198.68,178.18,1205.09,1841.0,28.9,48.08,65.48 322 | 200.85,178.24,1208.9,1848.7,29.25,47.97,65.8 323 | 199.2,178.0,1215.62,1848.4,29.56,47.75,67.08 324 | 198.58,178.5,1224.09,1842.0,30.1,45.87,67.39 325 | 199.46,179.0,1230.0,1851.35,29.3,46.88,67.5 326 | 199.54,179.6,1237.0,1872.99,29.83,47.96,69.95 327 | 203.12,178.8,1245.0,1868.79,29.95,47.44,70.01 328 | 202.83,178.25,1236.67,1855.4,29.93,47.41,69.58 329 | 204.43,182.74,1256.64,1891.2,29.99,47.2,68.84 330 | 207.36,184.49,1270.59,1925.0,29.95,47.06,68.99 331 | 206.83,196.98,1270.3,1917.0,29.91,47.17,68.56 332 | 204.9,192.5,1273.38,1929.0,30.17,47.52,68.9 333 | 204.4,190.95,1280.51,1949.0,30.43,47.97,69.69 334 | 203.06,194.19,1190.63,1930.1,30.8,48.35,71.02 335 | 209.88,194.78,1197.5,1933.09,30.56,48.32,70.68 336 | 209.84,193.0,1172.6,1913.33,30.26,48.35,69.74 337 | 210.89,194.38,1177.41,1949.0,30.66,48.5194,70.39 338 | 204.29,191.24,1172.0,1917.98,30.11,47.99,69.05 339 | 205.88,192.54,1185.81,1939.99,30.16,48.05,69.35 340 | 201.9,189.39,1177.29,1918.87,29.73,47.06,67.88 341 | 200.4,187.2,1162.6,1900.0,29.42,46.0,66.83 342 | 197.419,188.25,1168.84,1898.0,29.46,46.41,67.27 343 | 187.71,183.5,1145.24,1836.56,28.96,46.28,66.13 344 | 186.41,182.52,1142.32,1839.5,28.36,46.38,64.62 345 | 186.27,180.42,1122.55,1827.95,28.1,46.02,64.46 346 | 189.91,185.05,1171.84,1885.94,28.46,45.99,65.31 347 | 186.93,184.84,1175.83,1893.05,28.25,45.44,64.97 348 | 183.52,181.88,1153.0,1852.69,28.4,45.62,64.75 349 | 185.22,184.57,1154.48,1874.79,28.57,45.62,65.5 350 | 184.66,184.73,1151.25,1851.78,28.51,46.07,65.84 351 | 179.8,182.42,1146.07,1836.59,28.13,45.83,63.82 352 | 180.2,182.33,1152.0,1835.89,27.93,45.76,64.29 353 | 178.92,181.54,1141.48,1832.75,28.05,46.07,64.09 354 | 176.42,183.5,1132.7,1823.12,27.53,45.39,63.15 355 | 177.95,183.08,1120.15,1825.49,27.77,45.69,64.77 356 | 176.23,180.28,1105.64,1790.01,26.5,44.53,62.39 357 | 175.6,175.0,1066.93,1760.01,26.59,44.24,62.1 358 | 175.44,163.71,1044.49,1699.24,27.18,45.16,63.79 359 | 184.28,167.48,1055.0,1749.6,27.84,45.87,65.45 360 | 183.08,168.3,1046.21,1737.71,27.76,45.74,65.82 361 | 186.51,170.17,1054.28,1763.7,27.75,45.9,66.16 362 | 191.81,174.75,1077.0,1822.0,27.86,46.06,66.88 363 | 194.86,178.48,1096.99,1883.25,28.38,45.84,67.58 364 | 193.95,178.38,1079.95,1853.98,28.2,46.01,67.65 365 | 194.7,175.53,1084.71,1866.72,27.97,45.1,66.83 366 | 191.545,180.51,1089.74,1864.0,27.91,45.31,67.15 367 | 192.9,185.01,1089.1,1876.5,28.01,45.58,67.5 368 | 196.05,194.0,1111.5,1901.35,28.02,45.25,66.81 369 | 199.68,187.0,1107.24,1907.84,28.7,46.48,68.16 370 | 200.37,190.95,1121.7,1933.33,28.59,45.85,68.56 371 | 198.8,188.75,1109.86,1916.1,28.19,45.84,68.03 372 | 198.54,192.42,1120.0,1912.66,28.08,46.55,67.82 373 | 198.43,192.88,1115.08,1911.84,28.02,46.13,67.46 374 | 197.77,189.54,1091.0,1892.48,27.94,46.21,67.04 375 | 200.29,189.88,1086.75,1902.0,28.08,46.22,67.62 376 | 198.68,190.55,1077.23,1909.1,29.01,46.81,69.28 377 | 203.17,195.21,1101.04,1922.98,29.48,47.88,70.86 378 | 201.41,193.0,1104.83,1919.38,29.34,47.52,70.36 379 | 203.28,194.16,1118.5,1935.89,29.19,47.3,70.71 380 | 203.35,196.18,1119.37,1928.6,29.36,48.1,71.27 381 | 200.81,195.19,1125.87,1934.12,29.01,47.4,70.76 382 | 199.2,194.97,1110.32,1947.8,28.97,47.3,70.53 383 | 201.85,200.0,1132.32,1996.51,29.17,47.67,71.5 384 | 203.31,203.26,1146.16,2025.62,29.1,47.21,71.4298 385 | 202.45,199.68,1142.93,2008.27,29.44,47.12,71.89 386 | 204.09,204.25,1145.34,2021.4,29.5,47.4,71.73 387 | 204.59,203.89,1146.73,2010.58,29.17,46.72,71.7 388 | 204.05,204.18,1150.92,2007.05,29.1,45.2,71.19 389 | 204.0,200.15,1142.0,1980.01,29.28,45.22,70.54 390 | 205.79,202.18,1149.32,1991.21,29.54,45.96,71.73 391 | 203.65,199.91,1135.47,1971.14,29.29,45.89,70.72 392 | 208.46,202.84,1143.45,1995.99,29.75,46.68,71.33 393 | 207.67,197.63,1132.62,1969.3,30.07,47.3,71.66 394 | 208.89,206.7,1138.95,2001.0,30.74,48.52,72.96 395 | 207.48,200.19,1228.0,1942.0,30.57,48.15,72.15 396 | 208.46,199.0,1242.5,1930.0,30.72,49.38,71.91 397 | 208.76,195.39,1227.0,1891.12,30.21,48.01,71.03 398 | 216.42,196.95,1224.87,1898.11,30.83,48.26,71.4 399 | 213.9,194.17,1217.63,1871.72,30.55,48.41,70.67 400 | 205.53,191.1,1203.0,1845.0699,29.36,47.11,67.61 401 | 197.99,184.69,1172.97,1770.22,28.53,46.5,65.6 402 | 196.31,183.69,1165.52,1792.23,28.41,46.61,66.0 403 | 195.41,183.6,1157.8,1773.99,27.61,46.07,64.63 404 | 200.2,186.62,1186.4301,1806.0,28.11,45.69,65.85 405 | 201.3,190.0,1199.995,1828.95,28.27,46.02,66.33 406 | 199.62,186.85,1180.0,1795.99,27.83,45.51,64.61 407 | 201.02,185.52,1174.35,1783.0,27.57,45.61,63.9 408 | 203.16,185.8,1176.07,1793.01,26.94,45.02,62.88 409 | 203.46,180.9619,1168.43,1781.99,26.58,44.3,61.98 410 | 204.28,183.75,1180.79,1792.89,26.53,43.74,61.93 411 | 210.62,186.01,1191.83,1818.08,27.64,45.13,64.82 412 | 210.88,185.45,1195.35,1814.5,27.02,45.04,63.72 413 | 212.99,185.0,1195.8199,1819.39,26.93,44.96,64.13 414 | 213.19,183.43,1193.8,1828.0,27.09,45.33,63.81 415 | 209.43,180.84,1185.17,1793.03,26.95,45.2,63.28 416 | 205.86,179.4,1159.45,1766.91,26.7,44.71,62.57 417 | 207.86,181.93,1183.0,1775.73,26.86,45.23,63.01 418 | 204.1,180.53,1164.87,1755.0,26.22,44.64,61.3 419 | 208.5,183.77,1186.42,1783.0,27.0,46.0,63.31 420 | 210.16,186.78,1200.35,1797.49,27.5,46.54,64.34 421 | 206.43,184.0,1181.85,1770.0,27.21,46.31,64.0 422 | 208.39,184.65,1179.45,1805.0,27.32,46.54,64.0 423 | 212.0,188.53,1193.66,1821.95,27.85,47.31,65.65 424 | 214.05,190.21,1209.14,1838.22,28.02,47.58,66.19 425 | 214.84,187.73,1207.08,1841.0,28.0,47.32,66.76 426 | 213.86,187.44,1196.09,1822.75,28.89,48.07,69.18 427 | 218.07,186.46,1203.89,1812.14,29.37,48.29,68.72 428 | 224.8,189.86,1223.47,1837.63,29.22,48.52,68.11 429 | 220.0,187.325,1232.11,1842.01,30.0,49.01,70.34 430 | 217.73,186.93,1230.44,1824.02,29.68,48.46,68.96 431 | 219.96,186.66,1231.63,1807.08,29.85,48.7,69.78 432 | 221.06,188.09,1230.24,1817.04,29.77,48.63,69.21 433 | 222.01,188.66,1232.32,1821.02,30.0,48.99,70.06 434 | 221.38,190.66,1233.64,1821.71,29.97,49.07,70.15 435 | 218.95,189.34,1226.57,1777.0,29.31,48.32,68.62 436 | 221.055,187.98,1240.0,1790.61,29.6,48.98,69.5 437 | 218.55,181.45,1216.01,1747.36,28.78,48.73,67.91 438 | 220.0,181.33,1242.14,1762.79,29.21,49.25,69.32 439 | 220.54,178.16,1242.83,1748.0,29.41,50.36,69.82 440 | 220.9,177.87,1220.6,1726.99,29.49,50.77,69.7965 441 | 225.07,179.15,1222.49,1746.0,29.44,50.65,69.57 442 | 223.06,174.84,1196.5,1727.74,28.2,48.72,67.76 443 | 218.43,175.57,1183.34,1713.0,27.76,48.3,66.08 444 | 225.64,179.55,1194.29,1726.02,27.85,48.66,66.76 445 | 226.27,180.0,1207.0,1731.63,28.33,48.95,67.87 446 | 225.82,178.26,1198.77,1722.49,27.87,48.25,67.03 447 | 227.03,179.16,1201.33,1719.61,27.83,48.17,67.0 448 | 227.93,180.32,1198.6,1725.24,28.04,48.33,67.77 449 | 232.95,182.15,1224.03,1742.92,28.99,49.46,69.99 450 | 234.9,184.2,1213.89,1728.91,28.74,49.09,69.53 451 | 236.39,183.8,1221.5,1742.14,29.34,49.28,70.17 452 | 233.37,188.32,1241.81,1773.33,30.37,50.04,70.82 453 | 235.09,190.3,1251.4,1796.49,30.3,49.98,70.03 454 | 234.59,190.23,1254.69,1787.8,30.13,49.35,69.32 455 | 237.52,187.04,1248.7,1769.66,30.67,50.28,70.52 456 | 241.16,190.0,1244.48,1788.15,30.97,50.44,71.72 457 | 242.1,182.01,1240.21,1761.3,31.13,50.72,71.9 458 | 244.51,184.62,1259.11,1771.09,31.45,50.66,72.55 459 | 243.16,185.83,1252.0,1697.55,31.36,51.08,71.97 460 | 247.42,187.2,1275.0,1748.06,31.97,51.94,73.73 461 | 248.97,191.69,1276.0,1774.81,31.94,51.5,73.14 462 | 244.76,189.56,1255.15,1759.12,32.01,51.88,72.75 463 | 247.24,196.7,1260.0,1775.99,31.41,51.77,72.36 464 | 249.54,192.85,1265.8,1788.01,31.7,52.13,72.25 465 | 257.33,194.55,1276.11,1801.01,32.24,52.58,74.94 466 | 257.05,195.37,1291.2,1809.16,32.51,52.74,75.19 467 | 256.77,194.03,1290.09,1801.0,32.76,53.29,74.42 468 | 258.74,191.91,1294.28,1803.76,33.11,53.78,75.41 469 | 258.69,190.0,1301.52,1787.89,33.07,53.83,75.48 470 | 258.3,189.93,1304.0,1778.0,33.03,53.79,75.45 471 | 261.55,190.0,1298.57,1774.66,33.13,53.75,75.55 472 | 261.13,194.7,1293.18,1773.39,32.72,53.81,74.25 473 | 263.75,192.93,1295.0,1751.43,32.69,53.22,73.61 474 | 263.68,194.26,1315.05,1760.05,32.92,53.78,74.34 475 | 265.8,194.56,1332.34,1738.3,32.86,53.84,74.29 476 | 267.9,197.4,1326.63,1756.99,33.12,54.1,74.97 477 | 265.54,198.58,1311.36,1749.14,32.79,53.73,74.63 478 | 263.69,197.42,1299.25,1743.0,32.78,53.84,74.08 479 | 262.59,198.38,1303.0,1739.02,32.96,53.9,74.1 480 | 262.71,199.515,1296.26,1753.25,33.33,54.53,74.93 481 | 266.94,200.0,1309.91,1779.92,33.38,54.11,75.84 482 | 265.58,199.9,1315.42,1801.0,33.49,54.15,75.84 483 | 266.6,201.6,1306.6,1817.78,33.37,54.07,75.43 484 | 267.27,202.13,1302.56,1804.4,33.45,54.31,75.42 485 | 258.31,197.6,1278.66,1760.0,32.87,53.01,73.38 486 | 261.07,200.0,1306.1,1774.01,33.08,52.6,73.83 487 | 263.79,199.86,1327.0,1763.5,33.16,53.41,74.65 488 | 267.48,200.5,1332.75,1751.2,33.68,53.86,75.93 489 | 270.0,200.65,1338.86,1750.66,33.5,54.17,75.18 490 | 268.6,201.66,1339.94,1747.4,33.4,53.49,75.07 491 | 268.81,200.28,1348.3,1741.67,33.48,53.74,76.12 492 | 267.78,202.35,1343.21,1750.0,33.78,53.37,75.81 493 | 271.46,196.4,1347.9,1765.0,34.69,54.05,77.05 494 | 277.0,195.27,1355.0,1767.0,34.95,54.24,77.36 495 | 279.57,198.84,1362.41,1778.01,34.85,54.11,77.29 496 | 279.8,200.09,1357.0,1795.02,35.11,54.46,77.7 497 | 279.5,202.78,1351.91,1780.5,35.15,53.74,78.0 498 | 282.23,207.48,1363.1,1799.62,35.24,54.05,78.94 499 | 280.53,206.7,1358.73,1788.26,35.04,53.34,78.67 500 | 284.69,206.3,1350.21,1793.81,35.19,53.95,78.7 501 | 284.82,205.5665,1346.55,1801.01,35.32,54.05,78.75 502 | 291.12,208.6697,1364.0,1882.92,35.7,54.29,80.0 503 | 289.46,207.86,1356.81,1874.0,35.57,54.17,80.15 504 | 289.93,203.995,1335.79,1842.0,35.03,53.53,79.32 505 | 296.24,206.75,1348.41,1875.0,35.35,53.85,80.13 506 | 297.15,207.2135,1348.0,1864.5,34.98,53.11,79.8 507 | 293.79,206.92,1351.63,1860.0,34.41,52.74,78.72 508 | 299.84,212.82,1400.46,1904.5,34.7,53.05,79.29 509 | 297.16,213.0,1394.82,1898.04,34.57,52.84,78.77 510 | 307.235,217.54,1421.93,1909.89,35.3,53.2,80.98 511 | 310.6,219.2,1429.47,1905.37,35.0,52.79,80.0 512 | 311.64,219.5965,1435.25,1891.31,34.84,52.5,79.5 513 | 316.7,221.61,1440.0,1885.88,35.3,50.23,81.2 514 | 311.85,220.61,1433.02,1872.25,34.765,48.83,81.73 515 | 313.59,222.57,1445.45,1882.99,34.9,48.46,81.96 516 | 316.27,222.03,1462.54,1885.89,34.92,49.37,81.16 517 | 317.19,222.155,1479.0,1865.0,34.42,49.09,80.48 518 | 318.58,222.31,1489.73,1896.09,34.37,49.03,80.2 519 | 317.92,220.75,1487.55,1885.11,34.09,48.39,79.58 520 | 320.25,220.8,1492.82,1891.37,34.12,48.19,79.62 521 | 310.06,213.1,1431.54,1820.0,32.85,46.89,76.26 522 | 312.6,216.14,1441.74,1840.5,33.02,47.51,77.21 523 | 324.45,221.44,1458.46,1864.0,33.33,47.42,77.54 524 | 320.5435,206.53,1438.1,1858.0,32.7,47.17,75.74 525 | 320.93,208.43,1467.86,2051.47,33.07,47.42,75.96 526 | 304.3,203.44,1461.65,2010.6,33.0,47.24,74.7 527 | 315.31,206.6165,1454.49,2029.88,33.56,47.7,76.75 528 | 323.52,212.51,1463.61,2071.02,34.1,47.9,77.92 529 | 322.57,210.47,1451.98,2041.02,34.94,48.44,79.8 530 | 322.37,210.3,1467.38,2041.99,34.32,47.73,78.13 531 | 314.18,211.52,1477.23,2085.01,34.44,47.67,78.08 532 | 323.6,210.19,1513.27,2150.9,34.85,48.08,78.75 533 | 321.47,207.85,1515.86,2163.2,35.08,48.22,79.9 534 | 324.19,209.5235,1510.0,2144.99,34.76,47.62,78.89 535 | 324.73,214.0,1514.53,2155.68,34.88,48.14,78.89 536 | 315.36,213.55,1514.34,2125.02,34.77,47.97,78.61 537 | 320.0,217.99,1527.2,2167.8,34.45,47.25,77.87 538 | 322.63,216.52,1519.01,2173.07,34.46,47.0,77.66 539 | 318.62,213.48,1504.58,2142.15,34.53,47.48,77.56 540 | 297.26,201.8,1423.05,2003.18,33.18,46.58,73.38 541 | 300.95,202.27,1431.0,2026.42,32.69,46.29,72.63 542 | 286.53,197.19,1394.98,1970.28,31.39,45.48,70.02 543 | 281.1,191.82,1359.14,1934.38,29.55,43.32,66.1 544 | 257.26,182.695,1274.31,1814.63,28.0,41.31,62.0 545 | 282.28,194.03,1351.39,1906.49,28.35,40.98,63.85 546 | 303.67,196.22,1397.68,1975.37,29.37,42.06,67.86 547 | 296.44,189.17,1358.96,1946.57,28.25,41.01,66.05 548 | 295.52,186.78,1345.55,1933.0,27.24,40.0,64.8 549 | 282.0,178.33,1269.95,1875.0,25.47,37.0,60.46 550 | 263.75,169.6,1204.96,1773.86,22.23,33.72,54.31 551 | 277.14,174.67,1254.39,1870.88,23.25,34.63,54.73 552 | 277.39,174.01,1248.27,1857.85,22.84,34.05,53.95 553 | 255.94,159.54,1122.62,1721.98,20.59,29.81,45.3 554 | 264.89,163.53,1174.99,1755.0,22.19,30.15,47.82 555 | 241.95,152.32,1089.61,1641.51,19.85,26.34,40.49 556 | 247.51,150.74,1090.64,1775.47,21.06,27.63,42.0 557 | 239.77,139.75,1059.94,1750.0,20.27,27.7,37.06 558 | 247.385,146.62,1088.22,1860.0,20.26,27.66,35.47 559 | 247.18,156.02,1130.9,1926.31,21.76,28.68,40.46 560 | 228.08,149.66,1056.37,1827.75,19.26,26.26,37.55 561 | 236.36,155.205,1103.98,1952.01,19.42,26.96,37.65 562 | 250.75,158.92,1124.58,1920.69,21.71,29.0,42.31 563 | 246.52,158.25,1114.72,1902.0,21.31,29.33,43.68 564 | 252.75,158.2,1127.47,1930.86,21.69,30.02,44.13 565 | 250.74,159.18,1132.64,1922.83,21.65,30.36,44.05 566 | 255.6,165.48,1148.73,1964.35,21.82,29.41,43.63 567 | 246.5,161.615,1124.0,1932.97,19.93,27.0197,39.14 568 | 240.34,159.1,1100.0,1901.64,19.65,26.47,38.36 569 | 242.8,157.15,1114.71,1911.15,20.4,27.0,39.0 570 | 250.9,160.15,1133.0,1936.0,21.03,27.41,39.8 571 | 270.8,171.79,1217.01,2017.11,22.8,30.75,44.74 572 | 262.74,171.25,1203.1,2021.0,22.41,29.34,42.85 573 | 268.7,175.9,1218.18,2044.3,24.07,31.12,46.94 574 | 268.31,173.67,1201.5,2040.0,24.78,33.22,47.5 575 | 280.0,178.9765,1239.97,2200.47,24.5,32.11,47.66 576 | 282.4,175.1865,1246.51,2257.68,22.16,28.77,43.37 577 | 287.38,177.95,1267.14,2346.0,22.15,28.28,42.71 578 | 284.69,179.2,1281.7,2372.33,22.34,28.15,43.15 579 | 277.95,177.41,1269.89,2389.95,22.57,27.325,43.395 580 | 276.28,175.25,1242.71,2416.61,21.9,26.9,42.35 581 | 273.61,178.45,1241.11,2369.0,22.16,27.5,43.0 582 | 275.87,184.08,1265.74,2399.98,21.96,26.95,42.65 583 | 277.2,183.23,1255.0,2417.0,22.11,26.8,43.0 584 | 281.8,192.99,1292.0,2443.2,22.52,27.37,44.17 585 | 285.08,188.66,1283.2,2372.1,24.45,29.6,48.94 586 | 284.73,190.93,1345.0,2330.01,24.91,30.1,49.7 587 | 289.96,206.92,1331.36,2419.84,24.21,29.32,49.07 588 | 286.25,201.6,1324.09,2336.8,23.38,28.5,46.31 589 | 289.17,200.2,1308.13,2256.38,22.7,26.96,45.03 590 | 295.06,207.02,1337.5,2340.0,23.48,27.52,46.3 591 | 300.46,208.12,1358.0,2329.44,23.0,26.46,44.49 592 | 303.22,211.16,1361.31,2374.78,22.66,25.59,43.52 593 | 305.64,212.24,1381.82,2372.14,23.47,25.64,45.32 594 | 308.1,210.8865,1376.79,2374.7,23.11,25.11,45.21 595 | 317.83,213.29,1408.22,2411.85,22.71,24.96,44.39 596 | 312.15,209.43,1376.16,2366.8,21.66,23.95,42.0 597 | 304.51,202.56,1333.52,2361.01,20.29,22.07,39.99 598 | 300.35,205.27,1348.26,2368.52,21.4,23.69,41.58 599 | 313.17,212.15,1361.58,2402.48,22.42,24.55,44.05 600 | 315.03,213.27,1385.48,2429.83,22.66,25.0,45.27 601 | 316.68,223.5,1389.16,2477.87,22.68,24.49,45.57 602 | 318.66,234.72,1410.99,2500.0,22.81,24.39,45.17 603 | 315.77,231.51,1403.9,2455.01,22.93,24.44,44.77 604 | 323.5,239.77,1441.96,2458.0,23.75,25.05,46.2 605 | 316.14,229.07,1420.0,2404.99,26.07,27.8,51.68 606 | 316.77,224.3,1400.0,2384.33,26.08,28.32,52.99 607 | 319.25,225.2,1420.43,2415.94,24.42,26.5,48.02 608 | 317.75,224.59,1425.7,2448.0,24.28,26.61,48.14 609 | 320.745,230.94,1435.0,2467.0,24.96,27.58,50.71 610 | 324.66,232.11,1442.7,2468.01,25.5,28.39,52.56 611 | 324.39,229.56,1436.78,2477.43,25.71,29.48,53.3 612 | 323.35,226.71,1415.64,2444.51,28.95,33.89,61.57 613 | 330.25,229.03,1426.28,2500.2,28.59,32.9,60.66 614 | 332.14,231.52,1445.24,2529.44,27.72,32.44,59.0 615 | 347.9,240.96,1461.51,2645.0,27.97,32.3,58.9 616 | 349.31,229.94,1441.03,2603.5,24.98,27.2,51.67 617 | 344.72,229.9,1425.86,2601.21,25.03,28.0,51.4 618 | 333.25,225.09,1389.49,2526.6,23.76,26.86,49.96 619 | 351.46,237.14,1449.0,2620.0,26.18,29.43,56.3 620 | 355.15,235.0,1452.94,2647.5,25.9,28.5,54.7 621 | 351.41,234.99,1449.85,2647.01,24.7,27.1,52.67 622 | --------------------------------------------------------------------------------