├── twopiece ├── __init__.py ├── tests │ ├── __init__.py │ └── test_twopiece.py ├── data │ ├── README.md │ ├── fan_parameters.csv │ └── fan_history.csv ├── README.md ├── shape.py ├── sinharcsinh.py ├── utils.py ├── double.py └── scale.py ├── tpfamilies.png ├── .gitignore ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── setup.py ├── LICENSE ├── CODE_OF_CONDUCT.md ├── docs └── arxive │ └── README_v<=1.1.7.tex.md └── README.md /twopiece/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /twopiece/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tpfamilies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantgirluk/twopiece/HEAD/tpfamilies.png -------------------------------------------------------------------------------- /twopiece/data/README.md: -------------------------------------------------------------------------------- 1 | Data used in online post [Fan Charts](https://quantgirl.blog/fan-charts/) from my personal blog. 2 | 3 | Original Data Source: https://www.bankofengland.co.uk/monetary-policy-report/2019/november-2019 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | __pycache__/ 3 | .vscode 4 | .DS_store 5 | 6 | # Distribution / packaging 7 | .Python 8 | build/ 9 | develop-eggs/ 10 | dist/ 11 | downloads/ 12 | eggs/ 13 | .eggs/ 14 | lib/ 15 | lib64/ 16 | parts/ 17 | sdist/ 18 | var/ 19 | wheels/ 20 | share/python-wheels/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | MANIFEST 25 | 26 | -------------------------------------------------------------------------------- /twopiece/data/fan_parameters.csv: -------------------------------------------------------------------------------- 1 | Date,Mode,Uncertainty,Skewness 2 | 2019-10-01,1.43,0.61,0 3 | 2020-01-01,1.67,0.88,0 4 | 2020-04-01,1.2,1.11,0 5 | 2020-07-01,1.17,1.27,0 6 | 2020-10-01,1.51,1.34,0 7 | 2021-01-01,1.67,1.37,0 8 | 2021-04-01,1.95,1.39,0 9 | 2021-07-01,2,1.42,0 10 | 2021-10-01,2.03,1.48,0 11 | 2022-01-01,2.07,1.49,0 12 | 2022-04-01,2.14,1.5,0 13 | 2022-07-01,2.18,1.52,0 14 | 2022-10-01,2.25,1.52,0 15 | -------------------------------------------------------------------------------- /twopiece/README.md: -------------------------------------------------------------------------------- 1 | ### Read Me 2 | 3 | - The file **single.py** contains the two-piece scale distributions main classes. 4 | 5 | - The file **shape.py** contains the two-piece shape distributions main classes. 6 | 7 | - The file **double.py** contains the double two-piece distributions main classes. 8 | 9 | - The file **sinharcsinh.py** contains the implementation of the SinhASinh distribution. 10 | 11 | - The file **util.py** contains several utility functions used to visualisation and reparameterisation. 12 | 13 | #### Contact 14 | 15 | - https://github.com/quantgirluk 16 | - https://www.linkedin.com/in/dialidsantiago/ 17 | - @Quant_Girl -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name="twopiece", 8 | version="1.3.1", 9 | author="Dialid Santiago ", 10 | author_email="d.santiago@outlook.com", 11 | description="Two-Piece Distributions Implementation", 12 | long_description=long_description, 13 | long_description_content_type="text/markdown", 14 | url="https://github.com/quantgirluk/twopiece", 15 | packages=setuptools.find_packages(), 16 | classifiers=[ 17 | "Programming Language :: Python :: 3", 18 | "License :: OSI Approved :: MIT License", 19 | "Operating System :: OS Independent", 20 | ], 21 | python_requires='>=3.6', 22 | install_requires=['numpy>=1.13.1', 'scipy>=0.19.1', 'matplotlib>=2.2.2', 'seaborn>=0.8'], 23 | ) 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 The Python Packaging Authority 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /twopiece/tests/test_twopiece.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from twopiece.scale import tpnorm, get_sigma1_sigma2 3 | import numpy as np 4 | from parameterized import parameterized 5 | 6 | 7 | class TestTwoPiece(unittest.TestCase): 8 | 9 | @parameterized.expand([ 10 | [0.0, 1.0, 0.0], 11 | [0.0, 1.0, 1.0], [0.0, 1.0, -1.0], 12 | [0.0, 1.0, 2.0], [0.0, 1.0, -2.0], ]) 13 | def test_boe_parametrisation(self, mu, sigma, gamma): 14 | 15 | sigma1, sigma2 = get_sigma1_sigma2(sigma, gamma, kind='boe') 16 | dist = tpnorm(mu, sigma1=sigma1, sigma2=sigma2) 17 | dist_boe = tpnorm(loc=mu, sigma=sigma, gamma=gamma, kind='boe') 18 | 19 | x = np.arange(-12, 12, 0.1) 20 | pdf = dist.pdf(x) 21 | pdf_boe = dist_boe.pdf(x) 22 | 23 | for y, y_boe in zip(pdf, pdf_boe): 24 | self.assertEqual(y, y_boe) # add assertion here 25 | 26 | mean_theoretical = mu + np.sqrt(2/np.pi)*(sigma2-sigma1) 27 | gamma_theoretical = mean_theoretical-mu 28 | self.assertAlmostEqual(gamma, gamma_theoretical) 29 | 30 | def test_parameters(self): 31 | self.assertRaises(TypeError, tpnorm) 32 | self.assertRaises(TypeError, tpnorm, loc=0.0) 33 | self.assertRaises(TypeError, tpnorm, loc=0.0, sigma=1.0, gamma=0.5) 34 | self.assertRaises(TypeError, tpnorm, loc=0.0, sigma=1.0, gamma=0.5, kind=None) 35 | self.assertRaises(ValueError, tpnorm, loc=0.0, sigma=1.0, gamma=0.5, kind='my_new_type') 36 | self.assertRaises(ValueError, tpnorm, loc=0.0, sigma=1.0, gamma=0.0, kind='inverse_scale') 37 | self.assertRaises(ValueError, tpnorm, loc=0.0, sigma=1.0, gamma=-1.0, kind='epsilon_skew') 38 | self.assertRaises(ValueError, tpnorm, loc=0.0, sigma=1.0, gamma=0.0, kind='percentile') 39 | 40 | 41 | if __name__ == '__main__': 42 | unittest.main(verbosity=2) 43 | -------------------------------------------------------------------------------- /twopiece/shape.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # name: twopiece.shape.py 3 | # author: D.Santiago 4 | # https://www.linkedin.com/in/dialidsantiago/ 5 | # @Quant_Girl 6 | # -- 7 | # coding: utf-8 8 | 9 | 10 | import scipy.stats 11 | 12 | from twopiece.double import tpd_continuous 13 | from twopiece.sinharcsinh import ssas 14 | from twopiece.utils import display_dist 15 | 16 | 17 | class TwoPieceShape(tpd_continuous): 18 | 19 | def __init__(self, f, loc=0.0, sigma=1.0, shape1=None, shape2=None): 20 | tpd_continuous.__init__(self, f, loc, sigma, sigma, None, None, shape1, shape2, None) 21 | 22 | 23 | class tpshastudent(TwoPieceShape): 24 | 25 | def __init__(self, loc=0.0, sigma=1.0, shape1=3.0, shape2=3.0): 26 | TwoPieceShape.__init__(self, scipy.stats.t, loc, sigma, shape1, shape2) 27 | 28 | 29 | class tpshagennorm(TwoPieceShape): 30 | 31 | def __init__(self, loc=0.0, sigma=1.0, shape1=3.0, shape2=3.0): 32 | TwoPieceShape.__init__(self, scipy.stats.gennorm, loc, sigma, shape1, shape2) 33 | 34 | 35 | class tpshasas(TwoPieceShape): 36 | 37 | def __init__(self, loc=0.0, sigma=None, shape1=3.0, shape2=3.0): 38 | TwoPieceShape.__init__(self, ssas, loc, sigma, shape1, shape2) 39 | 40 | 41 | def display_tpshape(tpd='All', loc=0.0, sigma=1.0, shape1=2.0, shape2=6.0, show='random_sample', xlim=None): 42 | if tpd in ['All', 'student']: 43 | z = tpshastudent(loc=loc, sigma=sigma, shape1=shape1, shape2=shape2) 44 | display_dist(dist=z, color='dodgerblue', bound=True, name='dtpshstudent', show=show, xlim=xlim) 45 | 46 | if tpd in ['All', 'gennorm']: 47 | z = tpshagennorm(loc=loc, sigma=sigma, shape1=shape1, shape2=shape2) 48 | display_dist(dist=z, color='gold', name='tpshagennorm', show=show, xlim=xlim) 49 | 50 | if tpd in ['All', 'Sinhasinh']: 51 | z = tpshasas(loc=loc, sigma=sigma, shape1=shape1, shape2=shape2) 52 | display_dist(dist=z, color='deeppink', name='dtshapsas', show=show, xlim=xlim) 53 | 54 | return None 55 | -------------------------------------------------------------------------------- /twopiece/sinharcsinh.py: -------------------------------------------------------------------------------- 1 | from math import asinh, cosh, sqrt, sinh 2 | 3 | import scipy.stats 4 | from numpy import random, vectorize 5 | 6 | 7 | def _pdf_instance(x, pdf, loc, scale, delta, epsilon): 8 | z = (x - loc) / scale 9 | output = (delta / scale) * pdf(sinh(delta * asinh(z) - epsilon)) * cosh(delta * asinh(z) - epsilon) / sqrt( 10 | 1 + z * z) 11 | return output 12 | 13 | 14 | def _cdf_instance(x, cdf, loc, scale, delta, epsilon): 15 | z = (x - loc) / scale 16 | output = cdf(sinh(delta * asinh(z) - epsilon)) 17 | 18 | return output 19 | 20 | 21 | def _qqf_instance(q, qqf, loc, scale, delta, epsilon): 22 | output = loc + scale * sinh((asinh(qqf(q)) + epsilon) / delta) 23 | return output 24 | 25 | 26 | def _random_sample(size, qqf, loc, scale, delta, epsilon): 27 | alpha = random.rand(size) 28 | qqfv = vectorize(_qqf_instance) 29 | 30 | return qqfv(alpha, qqf, loc, scale, delta, epsilon) 31 | 32 | 33 | class SinhArcsinh: 34 | 35 | def __init__(self, f, loc, scale, delta, epsilon): 36 | self.f = f 37 | self.loc = loc 38 | self.scale = scale 39 | self.delta = delta 40 | self.epsilon = epsilon 41 | 42 | def pdf(self, x): 43 | _pdf_vector = vectorize(_pdf_instance) 44 | s = _pdf_vector(x, self.f.pdf, self.loc, self.scale, self.delta, self.epsilon) 45 | return s 46 | 47 | def cdf(self, x): 48 | _cdf_vector = vectorize(_cdf_instance) 49 | s = _cdf_vector(x, self.f.cdf, self.loc, self.scale, self.delta, self.epsilon) 50 | return s 51 | 52 | def ppf(self, q): 53 | _qqf_vector = vectorize(_qqf_instance) 54 | x = _qqf_vector(q, self.f.ppf, self.loc, self.scale, self.delta, self.epsilon) 55 | 56 | return x 57 | 58 | def random_sample(self, size): 59 | sample = _random_sample(size, self.f.ppf, self.loc, self.scale, self.delta, self.epsilon) 60 | return sample 61 | 62 | 63 | class sinhasinh(SinhArcsinh): 64 | 65 | def __init__(self, loc=0.0, scale=1.0, delta=1.0, epsilon=0.0): 66 | SinhArcsinh.__init__(self, scipy.stats.norm, loc, scale, delta, epsilon) 67 | 68 | 69 | class ssas(SinhArcsinh): 70 | 71 | def __init__(self, delta=1.0): 72 | SinhArcsinh.__init__(self, f=scipy.stats.norm, loc=0.0, scale=1.0, delta=delta, epsilon=0.0) 73 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at dialidstgo@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /twopiece/data/fan_history.csv: -------------------------------------------------------------------------------- 1 | Date,Inflation 2 | 2004-01-31,1.4 3 | 2004-02-29,1.3 4 | 2004-03-31,1.1 5 | 2004-04-30,1.1 6 | 2004-05-31,1.5 7 | 2004-06-30,1.6 8 | 2004-07-31,1.4 9 | 2004-08-31,1.3 10 | 2004-09-30,1.1 11 | 2004-10-31,1.2 12 | 2004-11-30,1.5 13 | 2004-12-31,1.7 14 | 2005-01-31,1.6 15 | 2005-02-28,1.7 16 | 2005-03-31,1.9 17 | 2005-04-30,1.9 18 | 2005-05-31,1.9 19 | 2005-06-30,2.0 20 | 2005-07-31,2.3 21 | 2005-08-31,2.4 22 | 2005-09-30,2.5 23 | 2005-10-31,2.3 24 | 2005-11-30,2.1 25 | 2005-12-31,1.9 26 | 2006-01-31,1.9 27 | 2006-02-28,2.0 28 | 2006-03-31,1.8 29 | 2006-04-30,2.0 30 | 2006-05-31,2.2 31 | 2006-06-30,2.5 32 | 2006-07-31,2.4 33 | 2006-08-31,2.5 34 | 2006-09-30,2.4 35 | 2006-10-31,2.4 36 | 2006-11-30,2.7 37 | 2006-12-31,3.0 38 | 2007-01-31,2.7 39 | 2007-02-28,2.8 40 | 2007-03-31,3.1 41 | 2007-04-30,2.8 42 | 2007-05-31,2.5 43 | 2007-06-30,2.4 44 | 2007-07-31,1.9 45 | 2007-08-31,1.8 46 | 2007-09-30,1.8 47 | 2007-10-31,2.1 48 | 2007-11-30,2.1 49 | 2007-12-31,2.1 50 | 2008-01-31,2.2 51 | 2008-02-29,2.5 52 | 2008-03-31,2.5 53 | 2008-04-30,3.0 54 | 2008-05-31,3.3 55 | 2008-06-30,3.8 56 | 2008-07-31,4.4 57 | 2008-08-31,4.7 58 | 2008-09-30,5.2 59 | 2008-10-31,4.5 60 | 2008-11-30,4.1 61 | 2008-12-31,3.1 62 | 2009-01-31,3.0 63 | 2009-02-28,3.2 64 | 2009-03-31,2.9 65 | 2009-04-30,2.3 66 | 2009-05-31,2.2 67 | 2009-06-30,1.8 68 | 2009-07-31,1.8 69 | 2009-08-31,1.6 70 | 2009-09-30,1.1 71 | 2009-10-31,1.5 72 | 2009-11-30,1.9 73 | 2009-12-31,2.9 74 | 2010-01-31,3.5 75 | 2010-02-28,3.0 76 | 2010-03-31,3.4 77 | 2010-04-30,3.7 78 | 2010-05-31,3.4 79 | 2010-06-30,3.2 80 | 2010-07-31,3.1 81 | 2010-08-31,3.1 82 | 2010-09-30,3.1 83 | 2010-10-31,3.2 84 | 2010-11-30,3.3 85 | 2010-12-31,3.7 86 | 2011-01-31,4.0 87 | 2011-02-28,4.4 88 | 2011-03-31,4.0 89 | 2011-04-30,4.5 90 | 2011-05-31,4.5 91 | 2011-06-30,4.2 92 | 2011-07-31,4.4 93 | 2011-08-31,4.5 94 | 2011-09-30,5.2 95 | 2011-10-31,5.0 96 | 2011-11-30,4.8 97 | 2011-12-31,4.2 98 | 2012-01-31,3.6 99 | 2012-02-29,3.4 100 | 2012-03-31,3.5 101 | 2012-04-30,3.0 102 | 2012-05-31,2.8 103 | 2012-06-30,2.4 104 | 2012-07-31,2.6 105 | 2012-08-31,2.5 106 | 2012-09-30,2.2 107 | 2012-10-31,2.7 108 | 2012-11-30,2.7 109 | 2012-12-31,2.7 110 | 2013-01-31,2.7 111 | 2013-02-28,2.8 112 | 2013-03-31,2.8 113 | 2013-04-30,2.4 114 | 2013-05-31,2.7 115 | 2013-06-30,2.9 116 | 2013-07-31,2.8 117 | 2013-08-31,2.7 118 | 2013-09-30,2.7 119 | 2013-10-31,2.2 120 | 2013-11-30,2.1 121 | 2013-12-31,2.0 122 | 2014-01-31,1.9 123 | 2014-02-28,1.7 124 | 2014-03-31,1.6 125 | 2014-04-30,1.8 126 | 2014-05-31,1.5 127 | 2014-06-30,1.9 128 | 2014-07-31,1.6 129 | 2014-08-31,1.5 130 | 2014-09-30,1.2 131 | 2014-10-31,1.3 132 | 2014-11-30,1.0 133 | 2014-12-31,0.5 134 | 2015-01-31,0.3 135 | 2015-02-28,0.0 136 | 2015-03-31,0.0 137 | 2015-04-30,0.0 138 | 2015-05-31,0.1 139 | 2015-06-30,0.0 140 | 2015-07-31,0.1 141 | 2015-08-31,0.0 142 | 2015-09-30,-0.1 143 | 2015-10-31,-0.1 144 | 2015-11-30,0.1 145 | 2015-12-31,0.2 146 | 2016-01-31,0.3 147 | 2016-02-29,0.3 148 | 2016-03-31,0.5 149 | 2016-04-30,0.3 150 | 2016-05-31,0.3 151 | 2016-06-30,0.5 152 | 2016-07-31,0.6 153 | 2016-08-31,0.6 154 | 2016-09-30,1.0 155 | 2016-10-31,0.9 156 | 2016-11-30,1.2 157 | 2016-12-31,1.6 158 | 2017-01-31,1.8 159 | 2017-02-28,2.3 160 | 2017-03-31,2.3 161 | 2017-04-30,2.7 162 | 2017-05-31,2.9 163 | 2017-06-30,2.6 164 | 2017-07-31,2.6 165 | 2017-08-31,2.9 166 | 2017-09-30,3.0 167 | 2017-10-31,3.0 168 | 2017-11-30,3.1 169 | 2017-12-31,3.0 170 | 2018-01-31,3.0 171 | 2018-02-28,2.7 172 | 2018-03-31,2.5 173 | 2018-04-30,2.4 174 | 2018-05-31,2.4 175 | 2018-06-30,2.4 176 | 2018-07-31,2.5 177 | 2018-08-31,2.7 178 | 2018-09-30,2.4 179 | 2018-10-31,2.4 180 | 2018-11-30,2.3 181 | 2018-12-31,2.1 182 | 2019-01-31,1.8 183 | 2019-02-28,1.9 184 | 2019-03-31,1.9 185 | 2019-04-30,2.1 186 | 2019-05-31,2.0 187 | 2019-06-30,2.0 188 | 2019-07-01,2.1 189 | 2019-08-01,1.7 190 | 2019-09-01,1.7 191 | -------------------------------------------------------------------------------- /docs/arxive/README_v<=1.1.7.tex.md: -------------------------------------------------------------------------------- 1 | # twopiece: Two Piece Distributions 2 | 3 | - **Homepage:** https://github.com/quantgirluk/twopiece 4 | - **Free software:** MIT license 5 | 6 | ### Overview 7 | 8 | The **twopiece** library provides a Python implementation of the family of Two Piece distributions. 9 | 10 | The family of univariate two–piece distributions is a family of univariate three-parameter location-scale models, where skewness is introduced by differing scale parameters either side of the location. 11 | 12 | 13 | **Definition.** Let $f: \mathbb{R} \mapsto \mathbb{R}_{+}$ be a unimodal symmetric (about 0) probability density function (pdf) from the [location-scale family](https://en.wikipedia.org/wiki/Location%E2%80%93scale_family), possibly including a shape parameter $\delta$. Then, the pdf of a member of the two-piece family of distributions is given by 14 | 15 | $$ 16 | s\left(x; \mu,\sigma_1,\sigma_2, \delta\right) = 17 | \begin{cases} 18 | \dfrac{2}{\sigma_1+\sigma_2}f\left(\dfrac{x-\mu}{\sigma_1};\delta\right), \mbox{if } x < \mu, \\ 19 | \dfrac{2}{\sigma_1+\sigma_2}f\left(\dfrac{x-\mu}{\sigma_2};\delta\right), \mbox{if } x \geq \mu. \\ 20 | \end{cases} 21 | $$ 22 | 23 | **Example** If $f$ corresponds to the normal pdf, then $s$ corresponds to the pdf of the Two-Piece Normal distribution as proposed by [Gustav Fechner](https://en.wikipedia.org/wiki/Gustav_Fechner). 24 | 25 | For details on this family of distributions we refer to 26 | [Inference in Two-Piece Location-Scale Models with Jeffreys Priors](https://projecteuclid.org/euclid.ba/1393251764) 27 | published in Bayesian Anal. 28 | Volume 9, Number 1 (2014), 1-22 and the references therein. 29 | 30 | 31 | ### Supported Distributions 32 | Implementation is provided for the following distributions 33 | 34 | #### Three Parameters 35 | 36 | - two-piece normal [[+ info]](https://en.wikipedia.org/wiki/Split_normal_distribution) 37 | - two-piece Laplace 38 | - two-piece Cauchy 39 | - two-piece logistic 40 | 41 | #### Four Parameters 42 | 43 | - two-piece t 44 | - two-piece exponential power 45 | 46 | 47 | ### Main Features 48 | We provide the following functionality: 49 | 50 | - probability density function ***pdf*** 51 | - cumulative distribution function ***cdf*** 52 | - quantile function ***ppf*** 53 | - random generation ***random_sample*** 54 | 55 | for all the supported distributions. 56 | 57 | 58 | ### Quick Start 59 | 60 | To illustrate usage of the features for the 3 and 4 parameters distributions we will use 61 | the two-piece normal, and two-piece t, respectively. The behaviour is analogous for the rest of the supported distributions. 62 | 63 | ``` python 64 | from twopiece.single import * 65 | ``` 66 | 67 | 68 | #### 1. Create a twopiece instance 69 | To create an instance we need to specify either 3 or 4 parameters: 70 | 71 | For the **two-piece normal** we require: 72 | 73 | - *loc*: which is the location parameter 74 | - *sigma1*, *sigma2* : which are both scale parameters 75 | 76 | ```python 77 | loc=0.0 78 | sigma1=1.0 79 | sigma2=1.0 80 | dist = tpnorm(loc=loc, sigma1=sigma1, sigma2=sigma2) 81 | ``` 82 | 83 | For the **two-piece t** we require: 84 | 85 | - *loc*: which is the location parameter 86 | - *sigma1*, *sigma2* : which are both scale parameters 87 | - *shape* : which defines the degrees of freedom for the t-Student distribution 88 | 89 | ```python 90 | loc=0.0 91 | sigma1=1.0 92 | sigma2=1.0 93 | shape=3.0 94 | dist = tpstudent(loc=loc, sigma1=sigma1, sigma2=sigma2, shape=shape) 95 | ``` 96 | 97 | Hereafter we assume that there is a twopiece instance called *dist*. 98 | 99 | #### 2. Evaluate and visualise the probability density function (pdf) 100 | We can evaluate the pdf on a single point or an array type object 101 | 102 | ```python 103 | dist.pdf(0) 104 | ``` 105 | 106 | ```python 107 | dist.pdf([0.0,0.25,0.5]) 108 | ``` 109 | 110 | To visualise the pdf use 111 | ```python 112 | x = arange(-12, 12, 0.1) 113 | y = dist.pdf(x) 114 | plt.plot(x, y) 115 | plt.show() 116 | ``` 117 | 118 | #### 3. Evaluate the cumulative distribution function (cdf) 119 | We can evaluate the cdf on a single point or an array type object 120 | ```python 121 | dist.cdf(0) 122 | ``` 123 | 124 | ```python 125 | dist.cdf([0.0,0.25,0.5]) 126 | ``` 127 | 128 | To visualise the cdf use 129 | 130 | ```python 131 | x = arange(-12, 12, 0.1) 132 | y = dist.cdf(x) 133 | plt.plot(x, y) 134 | plt.show() 135 | ``` 136 | 137 | #### 4. Evaluate the quantile function (ppf) 138 | We can evaluate the ppf on a single point or an array type object. Note that the ppf has support on [0,1]. 139 | ```python 140 | dist.ppf(0.95) 141 | ``` 142 | 143 | ```python 144 | dist.ppf([0.5, 0.9, 0.95]) 145 | ``` 146 | 147 | To visualise the ppf use 148 | ```python 149 | x = arange(0.001, 0.999, 0.01) 150 | y = dist.ppf(x) 151 | plt.plot(x, y) 152 | plt.show() 153 | ``` 154 | 155 | #### 5. Generate a random sample 156 | 157 | To generate a random sample we require: 158 | - *size*: which is simply the size of the sample 159 | 160 | ```python 161 | sample = dist.random_sample(size = 100) 162 | ``` 163 | 164 | ### Install 165 | 166 | #### Requirements 167 | 168 | **twopiece** has been developed and tested on [Python 3.6, and 3.7](https://www.python.org/downloads/) 169 | -------------------------------------------------------------------------------- /twopiece/utils.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | import matplotlib.pyplot as plt 4 | from numpy import min, max, arange, pi 5 | from seaborn import distplot 6 | from seaborn import set 7 | 8 | set(style='whitegrid', rc={"grid.linewidth": 0.75, "figure.figsize": (9, 6)}) 9 | 10 | 11 | def get_sigma1_sigma2(sigma, gamma, kind): 12 | """ 13 | Gets the scale parameters sigma1, sigma2 from sigma and gamma 14 | :param sigma: scale parameter 15 | :param gamma: skewness or asymmetry parameter 16 | :param kind: Parametrisation name 17 | :return: sigma1 and sigma2 scale parameters 18 | """ 19 | if kind == 'inverse_scale': 20 | if gamma <= 0: 21 | raise ValueError(f'Gamma parameter must be positive under {kind} parametrisation') 22 | sigma1 = sigma / gamma 23 | sigma2 = sigma * gamma 24 | elif kind == 'epsilon_skew': 25 | if gamma >= 1 or gamma <= -1: 26 | raise ValueError(f'Gamma parameter must be in (-1, 1) under {kind} parametrisation') 27 | sigma1 = sigma * (1 + gamma) 28 | sigma2 = sigma * (1 - gamma) 29 | elif kind == 'percentile': 30 | if gamma >= 1 or gamma <= 0: 31 | raise ValueError(f'Gamma parameter must be in (0,1) under {kind} parametrisation') 32 | sigma1 = sigma * gamma 33 | sigma2 = sigma * (1 - gamma) 34 | elif kind == 'boe': 35 | if gamma == 0: 36 | actual_gamma = 0 37 | else: 38 | s = gamma / sigma 39 | actual_gamma_unsigned = math.sqrt(1 - 4 * ((math.sqrt(1 + pi * s ** 2) - 1) / (pi * s ** 2)) ** 2) 40 | actual_gamma = actual_gamma_unsigned if gamma > 0 else -actual_gamma_unsigned 41 | sigma1 = sigma / math.sqrt(1 + actual_gamma) 42 | sigma2 = sigma / math.sqrt(1 - actual_gamma) 43 | else: 44 | raise ValueError('Invalid value of kind provided. Valid values ' 45 | 'are boe, inverse_scale, epsilon_skew, percentile.') 46 | 47 | return sigma1, sigma2 48 | 49 | 50 | def display_dist(dist, name='', color='dodgerblue', bound=False, show='random_sample', xlim=None): 51 | """ 52 | Shows graphs for a given two piece distribution 53 | :param dist: distribution instance 54 | :param name: string, name 55 | :param color: string, color 56 | :param bound: boolean, bounding the x axis for better display 57 | :param show: string, All, pdf, cds, ppf, sample 58 | :param xlim: overwrites the xlim for the plots 59 | :return: 1 60 | """ 61 | 62 | if show in ['All', 'pdf']: 63 | 64 | x = arange(-10, 10, 0.01) 65 | y = dist.pdf(x) 66 | plt.figure('Probability Density Function') 67 | plt.plot(x, y, marker='', linestyle='solid', color=color) 68 | if xlim: 69 | plt.xlim(xlim) 70 | plt.title(name + ' pdf') 71 | plt.show() 72 | 73 | if show in ['All', 'cdf']: 74 | x = arange(-10, 10, 0.01) 75 | y = dist.cdf(x) 76 | plt.figure('Cumulative Distribution Function') 77 | plt.plot(x, y, marker='', linestyle='solid', color=color) 78 | if xlim: 79 | plt.xlim(xlim) 80 | plt.title(name + ' cdf') 81 | plt.show() 82 | 83 | if show in ['All', 'ppf']: 84 | x = arange(0.001, 0.999, 0.001) 85 | y = dist.ppf(x) 86 | plt.figure('Quantile Function') 87 | plt.plot(x, y, marker='', linestyle='solid', color=color) 88 | plt.title(name + ' ppf') 89 | plt.show() 90 | 91 | if show in ['All', 'random_sample']: 92 | 93 | sample = dist.random_sample(1000) 94 | 95 | if bound: 96 | sample = sample[abs(sample) < 25] 97 | 98 | plt.figure('Random Sample') 99 | distplot(sample, bins=50, kde=False, norm_hist=True, 100 | hist_kws=dict(edgecolor="white", color=color, linewidth=1.0, alpha=0.60)) 101 | x = arange(min(sample) - 2, max(sample) + 2, 0.01) 102 | y = dist.pdf(x) 103 | plt.plot(x, y, marker='', linestyle='solid', color=color) 104 | if xlim: 105 | plt.xlim(xlim) 106 | plt.title(name + ' random sample') 107 | plt.show() 108 | 109 | return None 110 | 111 | 112 | def display_parameterisations(dist=None, loc=0.0, sigma1=1.0, sigma2=1.0, sigma=1.0, gamma=0.5, show='random_sample'): 113 | z = dist(loc=loc, sigma1=sigma1, sigma2=sigma2) 114 | display_dist(dist=z, color='blue', name='standard', show=show) 115 | 116 | z = dist(loc=loc, sigma=sigma, gamma=gamma, kind='boe') 117 | display_dist(dist=z, color='coral', name='boe', show=show) 118 | 119 | z = dist(loc=loc, sigma=sigma, gamma=gamma, kind='inverse_scale') 120 | display_dist(dist=z, color='red', name='inverse_scale', show=show) 121 | 122 | z = dist(loc=loc, sigma=sigma, gamma=gamma, kind='percentile') 123 | display_dist(dist=z, color='black', name='percentile', show=show) 124 | 125 | return None 126 | 127 | 128 | def display_parameterisations_shape(dist=None, loc=0.0, sigma1=1.0, sigma2=1.0, 129 | sigma=1.0, gamma=0.5, shape=2.0, show='random_sample'): 130 | z = dist(loc=loc, sigma1=sigma1, sigma2=sigma2, shape=shape) 131 | display_dist(dist=z, color='dodgerblue', name='standard', show=show) 132 | 133 | z = dist(loc=loc, sigma=sigma, gamma=gamma, shape=shape, kind='boe') 134 | display_dist(dist=z, color='coral', name='boe', show=show) 135 | 136 | z = dist(loc=loc, sigma=sigma, gamma=gamma, shape=shape, kind='inverse_scale') 137 | display_dist(dist=z, color='red', name='inverse_scale', show=show) 138 | 139 | z = dist(loc=loc, sigma=sigma, gamma=gamma, shape=shape, kind='percentile') 140 | display_dist(dist=z, color='black', name='percentile', show=show) 141 | 142 | return None 143 | -------------------------------------------------------------------------------- /twopiece/double.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # name: twopiece.double.py 3 | # author: D.Santiago 4 | # https://www.linkedin.com/in/dialidsantiago/ 5 | # @Quant_Girl 6 | # -- 7 | # coding: utf-8 8 | 9 | 10 | import scipy.stats 11 | from numpy import isscalar, asarray, random, sum, empty 12 | 13 | from twopiece.sinharcsinh import ssas 14 | from twopiece.utils import get_sigma1_sigma2, display_dist 15 | 16 | 17 | def pdf_tpd_generic(x, pdf1, pdf2, loc, sigma1, sigma2, epsilon): 18 | """ 19 | Probability density function at x of the defined two piece distribution. 20 | :param x: array like 21 | :param pdf1: 22 | :param pdf2: 23 | :param loc: location parameter 24 | :param sigma1: scale parameter 25 | :param sigma2: scale parameter 26 | :param epsilon: shape parameter 27 | :return: pdf of the defined two piece in x 28 | """ 29 | 30 | if sigma1 * sigma2 <= 0: 31 | raise ValueError('Scale parameters must be positive.') 32 | 33 | aux1 = 2 * epsilon / sigma1 34 | aux2 = 2 * (1 - epsilon) / sigma2 35 | 36 | if isscalar(x): 37 | if x < loc: 38 | output = aux1 * pdf1((x - loc) / sigma1) 39 | else: 40 | output = aux2 * pdf2((x - loc) / sigma2) 41 | else: 42 | x = asarray(x) 43 | output = empty(x.size) 44 | index = x < loc 45 | output[index] = aux1 * pdf1((x[index] - loc) / sigma1) 46 | index = x >= loc 47 | output[index] = aux2 * pdf2((x[index] - loc) / sigma2) 48 | 49 | return output 50 | 51 | 52 | def cdf_tpd_generic(x, cdf1, cdf2, loc, sigma1, sigma2, epsilon): 53 | """ 54 | Cumulative Density Function at x of the defined two piece distribution. 55 | 56 | :param x: array like 57 | :param cdf1: a cumulative density function from a symmetric distribution defined on R. 58 | :param cdf2: a cumulative density function from a symmetric distribution defined on R. 59 | :param loc: location parameter 60 | :param sigma1: scale parameter 61 | :param sigma2: scale parameter 62 | :param epsilon: shape parameter 63 | :return: 64 | 65 | """ 66 | 67 | if sigma1 * sigma2 <= 0: 68 | raise ValueError('Scale parameters must be positive.') 69 | if isscalar(x): 70 | if x < loc: 71 | output = 2 * epsilon * cdf1((x - loc) / sigma1) 72 | else: 73 | output = epsilon + (1 - epsilon) * (2 * cdf2((x - loc) / sigma2) - 1) 74 | else: 75 | x = asarray(x) 76 | output = empty(x.size) 77 | index = x < loc 78 | output[index] = 2 * epsilon * cdf1((x[index] - loc) / sigma1) 79 | index = x >= loc 80 | output[index] = epsilon + (1 - epsilon) * (2 * cdf2((x[index] - loc) / sigma2) - 1) 81 | 82 | return output 83 | 84 | 85 | def qqf_tpd_generic(q, qqf1, qqf2, loc, sigma1, sigma2, epsilon): 86 | """ 87 | Quantile Function at q of the defined two piece distribution. 88 | 89 | :param q: array like 90 | :param qqf1: a quantile function (ppf) from a symmetric distribution defined on R. 91 | :param qqf2: a quantile function (ppf) from a symmetric distribution defined on R. 92 | :param loc: location parameter 93 | :param sigma1: scale parameter 94 | :param sigma2: scale parameter 95 | :param epsilon: shape parameter 96 | :return: 97 | """ 98 | 99 | if sigma1 * sigma2 <= 0: 100 | raise ValueError('Scale parameters must be positive.') 101 | 102 | if isscalar(q): 103 | if q > 1 or q < 0: 104 | raise ValueError('Quantile Function is defined on (0,1).') 105 | if q <= epsilon: 106 | output = loc + sigma1 * qqf1((0.5 / epsilon) * q) 107 | else: 108 | aux = 0.5 * ((q - epsilon) / (1 - epsilon) + 1) 109 | output = loc + sigma2 * qqf2(aux) 110 | else: 111 | q = asarray(q) 112 | if sum((q > 1) | (q < 0)) > 0: 113 | raise ValueError('Quantile Function is defined on (0,1).') 114 | output = empty(q.size) 115 | index = q <= epsilon 116 | output[index] = loc + sigma1 * qqf1((0.5 / epsilon) * q[index]) 117 | index = q > epsilon 118 | aux = 0.5 * ((q[index] - epsilon) / (1 - epsilon) + 1) 119 | output[index] = loc + sigma2 * qqf2(aux) 120 | 121 | return output 122 | 123 | 124 | def random_tpd_sample(size, qqf1, qqf2, loc, sigma1, sigma2, epsilon): 125 | """ 126 | Random Sample Generation 127 | 128 | :param size: integer, sample size 129 | :param qqf1: a quantile function (qqf) from a symmetric distribution defined on R. 130 | :param qqf2: a quantile function (qqf) from a symmetric distribution defined on R. 131 | :param loc: location parameter 132 | :param sigma1: scale parameter 133 | :param sigma2: scale parameter 134 | :param epsilon: shape parameter 135 | :return: 136 | """ 137 | if not isinstance(size, int): 138 | raise TypeError('Sample size must be of type integer.') 139 | 140 | if sigma1 * sigma2 <= 0: 141 | raise ValueError('Scale parameters must be positive.') 142 | 143 | alpha = random.rand(size) 144 | 145 | if isscalar(alpha): 146 | if alpha <= epsilon: 147 | qq = loc + sigma1 * qqf1(0.5 * alpha / epsilon) 148 | else: 149 | qq = loc + sigma2 * qqf2(0.5 * ((alpha - epsilon) / (1 - epsilon) + 1)) 150 | else: 151 | alpha = asarray(alpha) 152 | qq = empty(alpha.size) 153 | index = alpha <= epsilon 154 | qq[index] = loc + sigma1 * qqf1(0.5 * alpha[index] * (1 / epsilon)) 155 | index = alpha > epsilon 156 | qq[index] = loc + sigma2 * qqf2(0.5 * ((alpha[index] - epsilon) / (1 - epsilon) + 1)) 157 | 158 | return qq 159 | 160 | 161 | class TwoPieceDouble: 162 | 163 | def __init__(self, f, loc, sigma1, sigma2, sigma, gamma, shape1, shape2, kind): 164 | 165 | """ 166 | 167 | :param f: continuous symmetric distribution with support on R 168 | :param loc: location parameter 169 | :param sigma1: scale parameter 170 | :param sigma2: scale parameter 171 | :param sigma: scale parameter 172 | :param gamma: skewness or asymmetry parameter 173 | :param shape1: shape parameter 174 | :param shape2: shape parameter 175 | :param kind: Parametrisation 176 | """ 177 | 178 | if all(v is None for v in {sigma1, sigma2, sigma, gamma, kind}): 179 | raise AssertionError('Expected either (sigma1, sigma2) or (sigma, gamma, kind).') 180 | 181 | if kind: 182 | if kind not in {'inverse_scale', 'epsilon_skew', 'percentile', 'boe'}: 183 | raise ValueError('Invalid value of kind provided. Valid values are boe, ' 184 | 'inverse_scale, epsilon_skew, percentile.') 185 | 186 | self.f = f 187 | self.loc = loc 188 | self.sigma = sigma 189 | self.gamma = gamma 190 | self.shape1 = shape1 191 | self.shape2 = shape2 192 | self.kind = kind 193 | 194 | if sigma1 and sigma2: 195 | self.sigma1 = sigma1 196 | self.sigma2 = sigma2 197 | else: 198 | sigma1, sigma2 = get_sigma1_sigma2(self.sigma, self.gamma, self.kind) 199 | self.sigma1 = sigma1 200 | self.sigma2 = sigma2 201 | 202 | 203 | class tpd_continuous(TwoPieceDouble): 204 | 205 | def __init__(self, f, loc, sigma1, sigma2, sigma, gamma, shape1, shape2, kind): 206 | super().__init__(f, loc, sigma1, sigma2, sigma, gamma, shape1, shape2, kind) 207 | 208 | self.f1 = self.f(self.shape1) 209 | self.f2 = self.f(self.shape2) 210 | self.epsilon = self.sigma1 * self.f2.pdf(0) / (self.sigma1 * self.f2.pdf(0) + self.sigma2 * self.f1.pdf(0)) 211 | 212 | def pdf(self, x): 213 | s = pdf_tpd_generic(x, self.f1.pdf, self.f2.pdf, self.loc, self.sigma1, self.sigma2, self.epsilon) 214 | return s 215 | 216 | def cdf(self, x): 217 | s = cdf_tpd_generic(x, self.f1.cdf, self.f2.cdf, self.loc, self.sigma1, self.sigma2, self.epsilon) 218 | return s 219 | 220 | def ppf(self, x): 221 | qq = qqf_tpd_generic(x, self.f1.ppf, self.f2.ppf, self.loc, self.sigma1, self.sigma2, self.epsilon) 222 | return qq 223 | 224 | def random_sample(self, size): 225 | sample = random_tpd_sample(size, self.f1.ppf, self.f2.ppf, self.loc, self.sigma1, self.sigma2, self.epsilon) 226 | return sample 227 | 228 | 229 | class dtpstudent(tpd_continuous): 230 | 231 | def __init__(self, loc=0.0, sigma1=None, sigma2=None, sigma=None, gamma=None, shape1=None, shape2=None, kind=None): 232 | tpd_continuous.__init__(self, scipy.stats.t, loc, sigma1, sigma2, sigma, gamma, shape1, shape2, kind) 233 | 234 | 235 | class dtpgennorm(tpd_continuous): 236 | 237 | def __init__(self, loc=0.0, sigma1=None, sigma2=None, sigma=None, gamma=None, shape1=None, shape2=None, kind=None): 238 | tpd_continuous.__init__(self, scipy.stats.gennorm, loc, sigma1, sigma2, sigma, gamma, shape1, shape2, kind) 239 | 240 | 241 | class dtpsas(tpd_continuous): 242 | 243 | def __init__(self, loc=0.0, sigma1=None, sigma2=None, sigma=None, gamma=None, shape1=None, shape2=None, kind=None): 244 | tpd_continuous.__init__(self, ssas, loc, sigma1, sigma2, sigma, gamma, shape1, shape2, kind) 245 | 246 | 247 | def display_dtp(tpd='All', loc=0.0, sigma1=1.0, sigma2=0.5, shape1=2.0, shape2=6.0, show='random_sample', xlim=None): 248 | if tpd in ['All', 'dtpstudent']: 249 | z = dtpstudent(loc=loc, sigma1=sigma1, sigma2=sigma2, shape1=shape1, shape2=shape2) 250 | display_dist(dist=z, color='dodgerblue', bound=True, name='dtpstudent', show=show, xlim=xlim) 251 | 252 | if tpd in ['All', 'dtpgennorm']: 253 | z = dtpgennorm(loc=loc, sigma1=sigma1, sigma2=sigma2, shape1=shape1, shape2=shape2) 254 | display_dist(dist=z, color='gold', name='dtpgennorm', show=show, xlim=xlim) 255 | 256 | if tpd in ['All', 'dtpsas']: 257 | z = dtpsas(loc=loc, sigma1=sigma1, sigma2=sigma2, shape1=shape1, shape2=shape2) 258 | display_dist(dist=z, color='deeppink', name='dtpsas', show=show, xlim=xlim) 259 | 260 | return None 261 | -------------------------------------------------------------------------------- /twopiece/scale.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # name: twopiece.scale.py 3 | # author: D.Santiago 4 | # https://www.linkedin.com/in/dialidsantiago/ 5 | # @Quant_Girl 6 | # -- 7 | # coding: utf-8 8 | 9 | import scipy.stats 10 | from numpy import isscalar, asarray, random, sum, empty 11 | 12 | from twopiece.sinharcsinh import ssas 13 | from twopiece.utils import display_dist, get_sigma1_sigma2 14 | 15 | 16 | def pdf_tp_generic(x, pdf, loc, sigma1, sigma2): 17 | """ 18 | Probability density function at x of the defined two piece distribution. 19 | 20 | :param x: array like 21 | :param pdf: a probability density function from a symmetric distribution defined on R. 22 | :param loc: location parameter 23 | :param sigma1: shape parameter 24 | :param sigma2: shape parameter 25 | :return: pdf of the defined two piece in x 26 | 27 | """ 28 | if sigma1 * sigma2 <= 0: 29 | raise AssertionError('Scale parameters must be positive.') 30 | aux = 2 / (sigma1 + sigma2) 31 | if isscalar(x): 32 | if x < loc: 33 | output = aux * pdf((x - loc) / sigma1) 34 | else: 35 | output = aux * pdf((x - loc) / sigma2) 36 | else: 37 | x = asarray(x) 38 | output = empty(x.size) 39 | index = x < loc 40 | output[index] = aux * pdf((x[index] - loc) / sigma1) 41 | index = x >= loc 42 | output[index] = aux * pdf((x[index] - loc) / sigma2) 43 | return output 44 | 45 | 46 | def cdf_tp_generic(x, cdf, loc, sigma1, sigma2): 47 | """ 48 | Cumulative Density Function at x of the defined two piece distribution. 49 | :param x: array like 50 | :param cdf: a cumulative density function from a symmetric distribution defined on R. 51 | :param loc: location parameter 52 | :param sigma1: scale parameter 53 | :param sigma2: scale parameter 54 | :return: 55 | """ 56 | 57 | if sigma1 * sigma2 <= 0: 58 | raise AssertionError('Scale parameters must be positive.') 59 | aux = 2 / (sigma1 + sigma2) 60 | if isscalar(x): 61 | if x < loc: 62 | output = aux * sigma1 * cdf((x - loc) / sigma1) 63 | else: 64 | output = 1 - aux * sigma2 * (1 - cdf((x - loc) / sigma2)) 65 | else: 66 | x = asarray(x) 67 | output = empty(x.size) 68 | index = x < loc 69 | output[index] = aux * sigma1 * cdf((x[index] - loc) / sigma1) 70 | index = x >= loc 71 | output[index] = 1 - aux * sigma2 * (1 - cdf((x[index] - loc) / sigma2)) 72 | 73 | return output 74 | 75 | 76 | def qqf_tp_generic(q, qqf, loc, sigma1, sigma2): 77 | """ 78 | Quantile Function at q of the defined two piece distribution. 79 | :param q: array like 80 | :param qqf: a quantile function (ppf) from a symmetric distribution defined on R. 81 | :param loc: location parameter 82 | :param sigma1: scale parameter 83 | :param sigma2: scale parameter 84 | :return: 85 | """ 86 | 87 | if sigma1 * sigma2 <= 0: 88 | raise AssertionError('Scale parameters must be positive.') 89 | 90 | p = sigma1 / (sigma1 + sigma2) 91 | 92 | if isscalar(q): 93 | if q > 1 or q < 0: 94 | raise AssertionError('Quantile Function is defined on (0,1).') 95 | if q <= p: 96 | output = loc + sigma1 * qqf(0.5 * (sigma1 + sigma2) * q / sigma1) 97 | else: 98 | output = loc + sigma2 * qqf(0.5 * ((sigma1 + sigma2) * (1 + q) - 2 * sigma1) / sigma2) 99 | else: 100 | q = asarray(q) 101 | if sum((q > 1) | (q < 0)) > 0: 102 | raise AssertionError('Quantile Function is defined on (0,1).') 103 | output = empty(q.size) 104 | index = q <= p 105 | output[index] = loc + sigma1 * qqf(0.5 * (sigma1 + sigma2) * q[index] / sigma1) 106 | index = q > p 107 | output[index] = loc + sigma2 * qqf(0.5 * ((sigma1 + sigma2) * (1 + q[index]) - 2 * sigma1) / sigma2) 108 | 109 | return output 110 | 111 | 112 | def random_tp_sample(size, qqf, loc, sigma1, sigma2): 113 | """ 114 | Random Sample Generation 115 | :param size: integer, sample size 116 | :param qqf: a quantile function (ppf) from a symmetric distribution defined on R 117 | :param loc: location parameter 118 | :param sigma1: scale parameter 119 | :param sigma2: scale parameter 120 | :return: 121 | """ 122 | if not isinstance(size, int): 123 | raise TypeError('Sample size must be of type integer.') 124 | 125 | if sigma1 * sigma2 <= 0: 126 | raise AssertionError('Scale parameters must be positive.') 127 | 128 | alpha = random.rand(size) 129 | p = sigma1 / (sigma1 + sigma2) 130 | 131 | if isscalar(alpha): 132 | if alpha <= p: 133 | qq = loc + sigma1 * qqf(0.5 * (sigma1 + sigma2) * alpha / sigma1) 134 | else: 135 | qq = loc + sigma2 * qqf(0.5 * ((sigma1 + sigma2) * (1 + alpha) - 2 * sigma1) / sigma2) 136 | else: 137 | alpha = asarray(alpha) 138 | qq = empty(alpha.size) 139 | index = alpha <= p 140 | qq[index] = loc + sigma1 * qqf(0.5 * (sigma1 + sigma2) * alpha[index] / sigma1) 141 | index = alpha > p 142 | qq[index] = loc + sigma2 * qqf(0.5 * ((sigma1 + sigma2) * (1 + alpha[index]) - 2 * sigma1) / sigma2) 143 | 144 | return qq 145 | 146 | 147 | class TwoPiece: 148 | 149 | def __init__(self, f, loc, sigma1, sigma2, sigma, gamma, kind): 150 | 151 | """ 152 | :param f: continuous symmetric distribution with support on R 153 | :param loc: location parameter 154 | :param sigma1: scale parameter 155 | :param sigma2: scale parameter 156 | :param sigma: scale parameter 157 | :param gamma: skewness or asymmetry parameter 158 | :param kind: Parametrisation 159 | """ 160 | 161 | if sigma1 is None or sigma2 is None: 162 | if sigma is None or gamma is None or kind is None: 163 | raise TypeError('Missing parameters.Expected either ' 164 | '(sigma1, sigma2) or (sigma, gamma, kind).') 165 | if kind and kind not in {'inverse_scale', 'epsilon_skew', 'percentile', 'boe'}: 166 | raise ValueError('Invalid value of kind provided. Valid values ' 167 | 'are boe, inverse_scale, epsilon_skew, percentile. ') 168 | self.f = f 169 | self.loc = loc 170 | self.sigma = sigma 171 | self.gamma = gamma 172 | self.kind = kind 173 | 174 | if sigma1 and sigma2: 175 | self.sigma1 = sigma1 176 | self.sigma2 = sigma2 177 | else: 178 | sigma1, sigma2 = get_sigma1_sigma2(self.sigma, self.gamma, self.kind) 179 | self.sigma1 = sigma1 180 | self.sigma2 = sigma2 181 | 182 | 183 | class TwoPieceScale(TwoPiece): 184 | 185 | def pdf(self, x): 186 | s = pdf_tp_generic(x, self.f.pdf, self.loc, self.sigma1, self.sigma2) 187 | return s 188 | 189 | def cdf(self, x): 190 | s = cdf_tp_generic(x, self.f.cdf, self.loc, self.sigma1, self.sigma2) 191 | return s 192 | 193 | def ppf(self, x): 194 | qq = qqf_tp_generic(x, self.f.ppf, self.loc, self.sigma1, self.sigma2) 195 | return qq 196 | 197 | def random_sample(self, size): 198 | sample = random_tp_sample(size, self.f.ppf, self.loc, self.sigma1, self.sigma2) 199 | return sample 200 | 201 | 202 | class tpnorm(TwoPieceScale): 203 | 204 | def __init__(self, loc=0.0, sigma1=None, sigma2=None, sigma=None, gamma=None, kind=None): 205 | TwoPieceScale.__init__(self, scipy.stats.norm, loc, sigma1, sigma2, sigma, gamma, kind) 206 | 207 | 208 | class tplaplace(TwoPieceScale): 209 | 210 | def __init__(self, loc=0.0, sigma1=None, sigma2=None, sigma=None, gamma=None, kind=None): 211 | TwoPieceScale.__init__(self, scipy.stats.laplace, loc, sigma1, sigma2, sigma, gamma, kind) 212 | 213 | 214 | class tpcauchy(TwoPieceScale): 215 | 216 | def __init__(self, loc=0.0, sigma1=None, sigma2=None, sigma=None, gamma=None, kind=None): 217 | TwoPieceScale.__init__(self, scipy.stats.cauchy, loc, sigma1, sigma2, sigma, gamma, kind) 218 | 219 | 220 | class tplogistic(TwoPieceScale): 221 | 222 | def __init__(self, loc=0.0, sigma1=None, sigma2=None, sigma=None, gamma=None, kind=None): 223 | TwoPieceScale.__init__(self, scipy.stats.logistic, loc, sigma1, sigma2, sigma, gamma, kind) 224 | 225 | 226 | class TwoPieceScalewithShape: 227 | 228 | def __init__(self, f, loc, sigma1, sigma2, sigma, gamma, shape, kind): 229 | 230 | if sigma1 is None or sigma2 is None: 231 | if sigma is None or gamma is None or kind is None: 232 | raise TypeError('Missing parameters.Expected either ' 233 | '(sigma1, sigma2) or (sigma, gamma, kind).') 234 | if kind and kind not in {'inverse_scale', 'epsilon_skew', 'percentile', 'boe'}: 235 | raise ValueError('Invalid value of kind provided. Valid values ' 236 | 'are boe, inverse_scale, epsilon_skew, percentile. ') 237 | 238 | self.loc = loc 239 | self.sigma = sigma 240 | self.gamma = gamma 241 | self.f = f(shape) 242 | self.kind = kind 243 | 244 | if sigma1 and sigma2: 245 | self.sigma1 = sigma1 246 | self.sigma2 = sigma2 247 | else: 248 | sigma1, sigma2 = get_sigma1_sigma2(self.sigma, self.gamma, self.kind) 249 | self.sigma1 = sigma1 250 | self.sigma2 = sigma2 251 | 252 | 253 | class tp_scalesh(TwoPieceScalewithShape): 254 | 255 | def pdf(self, x): 256 | s = pdf_tp_generic(x, self.f.pdf, self.loc, self.sigma1, self.sigma2) 257 | return s 258 | 259 | def cdf(self, x): 260 | s = cdf_tp_generic(x, self.f.cdf, self.loc, self.sigma1, self.sigma2) 261 | return s 262 | 263 | def ppf(self, x): 264 | qq = qqf_tp_generic(x, self.f.ppf, self.loc, self.sigma1, self.sigma2) 265 | return qq 266 | 267 | def random_sample(self, size): 268 | sample = random_tp_sample(size, self.f.ppf, self.loc, self.sigma1, self.sigma2) 269 | return sample 270 | 271 | 272 | class tpstudent(tp_scalesh): 273 | 274 | def __init__(self, loc=0.0, sigma1=None, sigma2=None, sigma=None, gamma=None, shape=None, kind=None): 275 | tp_scalesh.__init__(self, scipy.stats.t, loc, sigma1, sigma2, sigma, gamma, shape, kind) 276 | 277 | 278 | class tpgennorm(tp_scalesh): 279 | 280 | def __init__(self, loc=0.0, sigma1=None, sigma2=None, sigma=None, gamma=None, shape=None, kind=None): 281 | tp_scalesh.__init__(self, scipy.stats.gennorm, loc, sigma1, sigma2, sigma, gamma, shape, kind) 282 | 283 | 284 | class tpsas(tp_scalesh): 285 | 286 | def __init__(self, loc=0.0, sigma1=None, sigma2=None, sigma=None, gamma=None, shape=None, kind=None): 287 | tp_scalesh.__init__(self, ssas, loc, sigma1, sigma2, sigma, gamma, shape, kind) 288 | 289 | 290 | def display_tpscale(tpdist='All', loc=0.0, sigma1=1.0, sigma2=1.0, shape=3.0, show='random_sample'): 291 | if tpdist in ['All', 'tpnorm']: 292 | z = tpnorm(loc=loc, sigma1=sigma1, sigma2=sigma2) 293 | display_dist(dist=z, color='dodgerblue', name='tpnorm', show=show) 294 | 295 | if tpdist in ['All', 'tplaplace']: 296 | z = tplaplace(loc=loc, sigma1=sigma1, sigma2=sigma2) 297 | display_dist(dist=z, color='greenyellow', name='tplaplace', show=show) 298 | 299 | if tpdist in ['All', 'tpcauchy']: 300 | z = tpcauchy(loc=loc, sigma1=sigma1, sigma2=sigma2) 301 | display_dist(dist=z, color='orange', bound=True, name='tpcauchy', show=show) 302 | 303 | if tpdist in ['All', 'tplogistic']: 304 | z = tplogistic(loc=loc, sigma1=sigma1, sigma2=sigma2) 305 | display_dist(dist=z, color='aquamarine', name='tplogistic', show=show) 306 | 307 | if tpdist in ['All', 'tpstudent']: 308 | z = tpstudent(loc=loc, sigma1=sigma1, sigma2=sigma2, shape=shape) 309 | display_dist(dist=z, color='gold', name='tpstudent', show=show) 310 | 311 | if tpdist in ['All', 'tpgennorm']: 312 | z = tpgennorm(loc=loc, sigma1=sigma1, sigma2=sigma2, shape=shape) 313 | display_dist(dist=z, color='cyan', name='tpgen_norm', show=show) 314 | 315 | if tpdist in ['All', 'tpsas']: 316 | z = tpsas(loc=loc, sigma1=sigma1, sigma2=sigma2, shape=shape) 317 | display_dist(dist=z, color='deeppink', name='tpsas', show=show) 318 | 319 | return None 320 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # *twopiece*: Two-Piece Distributions 2 | 3 | [![PyPI version fury.io](https://badge.fury.io/py/twopiece.svg)](https://pypi.python.org/pypi/twopiece/)[![Downloads](https://static.pepy.tech/personalized-badge/twopiece?period=total&units=international_system&left_color=black&right_color=blue&left_text=Downloads)](https://pepy.tech/project/twopiece) 4 | 5 | ![PyPI license](https://img.shields.io/pypi/l/twopiece.svg) 6 | 7 | - Homepage: https://github.com/quantgirluk/twopiece 8 | - Pip Repository: [twopiece](https://pypi.org/project/twopiece/) 9 | - Demo: [Python Notebook](https://github.com/quantgirluk/Quant-Girl-Blog/blob/master/Twopiece_Demo.ipynb) 10 | 11 | 12 | --- 13 | 14 | 15 | 16 | - [Overview](#overview) 17 | - [Supported Distributions](#supported-distributions) 18 | - [Main Features](#main-features) 19 | - [Quick Start](#quick-start) 20 | - [Install](#install) 21 | 22 | --- 23 | ## Overview 24 | 25 | The **_twopiece_** library provides a [Python](https://www.python.org/) implementation of the family of Two Piece 26 | distributions. It covers three subfamilies [Two-Piece Scale](#two-piece-scale), [Two-Piece Shape](#two-piece-shape), 27 | and [Double Two-Piece](#double-two-piece). The following diagram shows how these families relate. 28 | 29 |
30 |

32 |

33 | 34 | --- 35 | ### Two-Piece Scale 36 | 37 | The family of **two–piece scale distributions** is a family of univariate three parameter location-scale models, 38 | where **_skewness_** is introduced by differing [**_scale_**](https://en.wikipedia.org/wiki/Scale_parameter) 39 | parameters either side of the [mode](). 40 | 41 | **Definition.** Let $f: \mathbb{R} \mapsto \mathbb{R}_{+}$ be a unimodal symmetric (around zero) probability 42 | density function (pdf) from the [location-scale family](https://en.wikipedia.org/wiki/Location%E2%80%93scale_family), 43 | possibly including a [shape parameter](https://en.wikipedia.org/wiki/Shape_parameter) $\delta$. Then, the 44 | pdf of a member of the two-piece family of distributions is given by 45 | 46 | $$ 47 | s\left(x; \mu,\sigma_1,\sigma_2, \delta\right) = 48 | \begin{cases} 49 | \dfrac{2}{\sigma_1+\sigma_2}f\left(\dfrac{x-\mu}{\sigma_1};\delta\right), \mbox{if } x < \mu, \\ 50 | \dfrac{2}{\sigma_1+\sigma_2}f\left(\dfrac{x-\mu}{\sigma_2};\delta\right), \mbox{if } x \geq \mu. \\ 51 | \end{cases} 52 | $$ 53 | 54 | **Example.** If $f$ corresponds to the normal pdf, then $s$ corresponds to the pdf of the 55 | [Two-Piece Normal](https://quantgirl.blog/two-piece-normal/) distribution 56 | as proposed by [Gustav Fechner](https://en.wikipedia.org/wiki/Gustav_Fechner) in 1887. 57 | 58 | --- 59 | 60 | ### Two-Piece Shape 61 | 62 | The family of **two–piece shape distributions** is a family of univariate three parameter location-scale 63 | models, where **_skewness_** is introduced by differing **_shape_** parameters either side of the mode. 64 | 65 | **Definition.** Let $f: \mathbb{R} \mapsto \mathbb{R}_{+}$ be a unimodal symmetric (around 0) probability 66 | density function (pdf) from the [location-scale family](https://en.wikipedia.org/wiki/Location%E2%80%93scale_family) 67 | which includes a [shape parameter](https://en.wikipedia.org/wiki/Shape_parameter) $\delta$. Then, the pdf of a member 68 | of the two-piece family of distributions is given by 69 | 70 | $$ 71 | s\left(x; \mu,\sigma,\delta_1 \delta_2\right) = 72 | \begin{cases} 73 | \dfrac{2\epsilon}{\sigma}f\left(\dfrac{x-\mu}{\sigma};\delta_1\right), \mbox{if } x < \mu, \\ 74 | \dfrac{2(1 -\epsilon)}{\sigma}f\left(\dfrac{x-\mu}{\sigma};\delta_2\right), \mbox{if } x \geq \mu. \\ 75 | \end{cases} 76 | $$ 77 | 78 | where 79 | 80 | $$ 81 | \epsilon = \dfrac{f(0;\delta_2)}{f(0;\delta_1)+f(0;\delta_2)}. 82 | $$ 83 | 84 | 85 | **Example.** If $f$ corresponds to the Student-t pdf, then $s$ corresponds to the pdf of the 86 | Two-Piece Shape Student-t distribution. Note that $s$ has different shape parameter on each side mode but 87 | the same scale. 88 | 89 | --- 90 | 91 | ### Double Two-Piece 92 | 93 | The family of **double two–piece distributions** is obtained by using 94 | a density–based transformation of unimodal symmetric continuous distributions with a shape parameter. The 95 | resulting distributions contain five interpretable parameters that control the mode, as well as both **scale 96 | and shape** in each direction. 97 | 98 | **Definition.** Let $f: \mathbb{R} \mapsto \mathbb{R}_{+}$ be a unimodal symmetric (around 0) probability density function (pdf) from the [location-scale family](https://en.wikipedia.org/wiki/Location%E2%80%93scale_family) which includes a [shape parameter](https://en.wikipedia.org/wiki/Shape_parameter) $\delta$. Then, the pdf of a member of the two-piece family of distributions is given by 99 | 100 | $$ 101 | s\left(x; \mu,\sigma_1,\sigma_2, \delta_1, \delta_2 \right) = 102 | \begin{cases} 103 | \dfrac{2\epsilon}{\sigma_1}f\left(\dfrac{x-\mu}{\sigma_1};\delta_1\right), \mbox{if } x < \mu, \\ 104 | \dfrac{2(1 -\epsilon)}{\sigma_2}f\left(\dfrac{x-\mu}{\sigma_2};\delta_2\right), \mbox{if } x \geq \mu. \\ 105 | \end{cases} 106 | $$ 107 | 108 | where 109 | 110 | $$ 111 | \epsilon = \dfrac{\sigma_1f(0;\delta_2)}{\sigma_2f(0;\delta_1)+\sigma_1 f(0;\delta_2)}. 112 | $$ 113 | 114 | **Example.** If $f$ corresponds to the Student-t pdf then $s$ corresponds to the pdf of the Double Two-Piece Student-t distribution. Note that $s$ has different scale and shape on each side of the mode. 115 | 116 | 117 | ------------------------------------------------------------- 118 | ### Notes 119 | 120 | For technical details on this families of distributions we refer to the following two publications which serve as reference for our implementation. 121 | 122 | - [Inference in Two-Piece Location-Scale Models with Jeffreys Priors](https://projecteuclid.org/euclid.ba/1393251764) published in [Bayesian Anal.](https://projecteuclid.org/euclid.ba) Volume 9, Number 1 (2014), 1-22. 123 | 124 | 125 | - [Bayesian modelling of skewness and kurtosis with Two-Piece Scale and shape distributions](https://projecteuclid.org/euclid.ejs/1440680330) 126 | published in [Electron. J. Statist.](https://projecteuclid.org/euclid.ejs), Volume 9, Number 2 (2015), 1884-1912. 127 | 128 | For the [R](https://www.r-project.org/) implementation we refer to the following packages. 129 | 130 | - [twopiece, DTP, and TPSAS](https://sites.google.com/site/fjavierrubio67/resources) 131 | 132 | 133 | _twopiece_ has been developed and tested on [Python 3.6, and 3.7](https://www.python.org/downloads/). 134 | 135 | 136 | 137 | --- 138 | ## Supported Distributions 139 | Implementation is provided for the following distributions. 140 | 141 | ### Two-Piece Scale 142 | 143 | | Name | Function | Parameters | 144 | |-------------|-------------|----------| 145 | | [Two-Piece Normal](https://quantgirl.blog/two-piece-normal/) | tpnorm | loc, sigma1, sigma2 | 146 | | Two-Piece Laplace | tplaplace | loc, sigma1, sigma2 | 147 | | Two-Piece Cauchy | tpcauchy | loc, sigma1, sigma2 | 148 | | Two-Piece Logistic | tplogistic | loc, sigma1, sigma2 | 149 | | Two-Piece Student-t | tpstudent | loc, sigma1, sigma2, shape | 150 | | Two-Piece Exponential Power | tpgennorm | loc, sigma1, sigma2, shape | 151 | | Two-Piece SinhArcSinh | tpsas |loc, sigma1, sigma2, shape | 152 | 153 | ### Two-Piece Shape 154 | 155 | | Name | Function | Parameters | 156 | |-------------|-------------|----------| 157 | | Two-Piece-Shape Student-t | tpshastudent | loc, sigma, shape1, shape2 | 158 | | Two-Piece-Shape Exponential Power | tpshagennorm | loc, sigma, shape1, shape2 | 159 | | Two-Piece-Shape SinhArcSinh | tpshasas |loc, sigma, shape1, shape2 | 160 | 161 | 162 | 163 | ### Double Two-Piece 164 | 165 | | Name | Function | Parameters | 166 | |-------------|-------------|-------------| 167 | | Double Two-Piece Student-t | dtpstudent | loc, sigma1, sigma2, shape1, shape2 | 168 | | Double Two-Piece Exponential Power | dtpgennorm | loc, sigma1, sigma2, shape1, shape2 | 169 | | Double Two-Piece SinhArcSinh | dtpsinhasinh | loc, sigma1, sigma2, shape1, shape2 | 170 | 171 | 172 | --- 173 | ## Main Features 174 | We provide the following functionality for all the supported distributions. 175 | 176 | | Function | Method | Parameters | 177 | |----------------------------------|---------------|------------| 178 | | Probability Density Function | pdf | x | 179 | | Cumulative Distribution Function | cdf | x | 180 | | Quantile Function | ppf | q | 181 | | Random Sample Generation | random_sample | size | 182 | 183 | --- 184 | ## Quick Start 185 | 186 | 187 | #### Install 188 | 189 | We recommend install _twopiece_ using [pip](https://pip.pypa.io/en/stable/) as follows. 190 | 191 | 192 | ``` 193 | pip install twopiece 194 | ``` 195 | 196 | To illustrate usage two-piece scale distributions we will use 197 | the two-piece Normal, and two-piece Student-t. The behaviour is analogous for the rest of 198 | the supported distributions. 199 | 200 | --- 201 | 202 | #### 1. Create a twopiece instance 203 | 204 | First, we load the family (scale, shape and double) of two-piece distributions that we want to use. 205 | 206 | ``` python 207 | from twopiece.scale import * 208 | from twopiece.shape import * 209 | from twopiece.double import * 210 | ``` 211 | 212 | To create an instance we need to specify either 3, 4, or 5 parameters: 213 | 214 | For the **Two-Piece Normal** we require: 215 | 216 | - *loc*: which is the location parameter 217 | - *sigma1*, *sigma2* : which are both scale parameters 218 | 219 | ```python 220 | loc=0.0 221 | sigma1=1.0 222 | sigma2=1.0 223 | dist = tpnorm(loc=loc, sigma1=sigma1, sigma2=sigma2) 224 | ``` 225 | 226 | For the **Two-Piece Student-t** we require: 227 | 228 | - *loc*: which is the location parameter 229 | - *sigma1*, *sigma2* : which are both scale parameters 230 | - *shape* : which defines the degrees of freedom for the t-Student distribution 231 | 232 | ```python 233 | loc=0.0 234 | sigma1=1.0 235 | sigma2=2.0 236 | shape=3.0 237 | dist = tpstudent(loc=loc, sigma1=sigma1, sigma2=sigma2, shape=shape) 238 | ``` 239 | 240 | 241 | For the **Double Two-Piece Student-t** we require: 242 | 243 | - *loc*: which is the location parameter 244 | - *sigma1, sigma2* : which are both scale parameters 245 | - *shape1, shape2* : which define the degrees of freedom for the t-Student distribution on each side of 246 | the mode. 247 | 248 | ```python 249 | loc=0.0 250 | sigma1=1.0 251 | sigma2=2.0 252 | shape1=3.0 253 | shape2=10.0 254 | dist = dtpstudent(loc=loc, sigma1=sigma1, sigma2=sigma2, shape1=shape1, shape2=shape2) 255 | ``` 256 | 257 | Hereafter we assume that there is a twopiece instance called *dist*. 258 | 259 | 260 | #### 2. Evaluate and visualise the probability density function (pdf) 261 | We can evaluate the pdf on a single point or an array type object 262 | 263 | ```python 264 | dist.pdf(0) 265 | ``` 266 | 267 | ```python 268 | dist.pdf([0.0,0.25,0.5]) 269 | ``` 270 | 271 | To visualise the pdf use 272 | ```python 273 | x = arange(-12, 12, 0.1) 274 | y = dist.pdf(x) 275 | plt.plot(x, y) 276 | plt.show() 277 | ``` 278 | 279 | #### 3. Evaluate the cumulative distribution function (cdf) 280 | We can evaluate the cdf on a single point or an array type object 281 | ```python 282 | dist.cdf(0) 283 | ``` 284 | 285 | ```python 286 | dist.cdf([0.0,0.25,0.5]) 287 | ``` 288 | 289 | To visualise the cdf use 290 | 291 | ```python 292 | x = arange(-12, 12, 0.1) 293 | y = dist.cdf(x) 294 | plt.plot(x, y) 295 | plt.show() 296 | ``` 297 | 298 | #### 4. Evaluate the quantile function (ppf) 299 | We can evaluate the ppf on a single point or an array type object. Note that the ppf has support on [0,1]. 300 | ```python 301 | dist.ppf(0.95) 302 | ``` 303 | 304 | ```python 305 | dist.ppf([0.5, 0.9, 0.95]) 306 | ``` 307 | 308 | To visualise the ppf use 309 | ```python 310 | x = arange(0.001, 0.999, 0.01) 311 | y = dist.ppf(x) 312 | plt.plot(x, y) 313 | plt.show() 314 | ``` 315 | 316 | #### 5. Generate a random sample 317 | 318 | To generate a random sample we require: 319 | - *size*: which is simply the size of the sample 320 | 321 | ```python 322 | sample = dist.random_sample(size = 100) 323 | ``` 324 | 325 | --- 326 | 327 | ## Thanks for Visiting! ✨ 328 | 329 | Connect with me via: 330 | 331 | - 🦜 [Twitter](https://twitter.com/Quant_Girl) 332 | - 👩🏽‍💼 [Linkedin](https://www.linkedin.com/in/dialidsantiago/) 333 | - 📸 [Instagram](https://www.instagram.com/quant_girl/) 334 | - 👾 [Personal Website](https://quantgirl.blog) 335 | 336 | 337 | ⭐️ **If you like this repository, please give it a star!** ⭐️ 338 | 339 | --------------------------------------------------------------------------------