├── 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 | [](https://pypi.python.org/pypi/twopiece/)[](https://pepy.tech/project/twopiece)
4 |
5 | 
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 |
--------------------------------------------------------------------------------