rangekeeper 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rangekeeper/__init__.py +40 -0
- rangekeeper/api.py +88 -0
- rangekeeper/distribution.py +269 -0
- rangekeeper/dynamics/__init__.py +6 -0
- rangekeeper/dynamics/black_swan.py +77 -0
- rangekeeper/dynamics/cyclicality.py +414 -0
- rangekeeper/dynamics/market.py +328 -0
- rangekeeper/dynamics/noise.py +34 -0
- rangekeeper/dynamics/trend.py +79 -0
- rangekeeper/dynamics/volatility.py +140 -0
- rangekeeper/extrapolation.py +100 -0
- rangekeeper/flux.py +733 -0
- rangekeeper/graph.py +144 -0
- rangekeeper/measure.py +138 -0
- rangekeeper/models/__init__.py +5 -0
- rangekeeper/models/deterministic.py +143 -0
- rangekeeper/models/flexible.py +177 -0
- rangekeeper/models/linear.py +144 -0
- rangekeeper/models/linear_graph.py +171 -0
- rangekeeper/models/probabilistic.py +157 -0
- rangekeeper/periodicity.py +236 -0
- rangekeeper/policy.py +20 -0
- rangekeeper/projection.py +248 -0
- rangekeeper/segmentation.py +220 -0
- rangekeeper/setup.py +14 -0
- rangekeeper/space.py +42 -0
- rangekeeper/span.py +226 -0
- rangekeeper-0.1.0.dist-info/LICENSE +373 -0
- rangekeeper-0.1.0.dist-info/METADATA +32 -0
- rangekeeper-0.1.0.dist-info/RECORD +31 -0
- rangekeeper-0.1.0.dist-info/WHEEL +4 -0
rangekeeper/__init__.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from . import api as api
|
|
2
|
+
from . import distribution as distribution
|
|
3
|
+
from . import extrapolation as extrapolation
|
|
4
|
+
from . import flux as flux
|
|
5
|
+
from . import graph as graph
|
|
6
|
+
from . import measure as measure
|
|
7
|
+
from . import periodicity as periodicity
|
|
8
|
+
from . import policy as policy
|
|
9
|
+
from . import projection as projection
|
|
10
|
+
from . import span as span
|
|
11
|
+
from . import segmentation as segmentation
|
|
12
|
+
from . import space as space
|
|
13
|
+
|
|
14
|
+
from . import dynamics as dynamics
|
|
15
|
+
|
|
16
|
+
from . import models as models
|
|
17
|
+
|
|
18
|
+
# Helper Methods:
|
|
19
|
+
import functools
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def update_class(
|
|
23
|
+
main_class=None, exclude=("__module__", "__name__", "__dict__", "__weakref__")
|
|
24
|
+
):
|
|
25
|
+
"""Class decorator. Adds all methods and members from the wrapped class to main_class
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
- main_class: class to which to append members. Defaults to the class with the same name as the wrapped class
|
|
29
|
+
- exclude: black-list of members which should not be copied
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def decorates(main_class, exclude, appended_class):
|
|
33
|
+
if main_class is None:
|
|
34
|
+
main_class = locals()[appended_class.__name__]
|
|
35
|
+
for k, v in appended_class.__dict__.items():
|
|
36
|
+
if k not in exclude:
|
|
37
|
+
setattr(main_class, k, v)
|
|
38
|
+
return main_class
|
|
39
|
+
|
|
40
|
+
return functools.partial(decorates, main_class, exclude)
|
rangekeeper/api.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import specklepy
|
|
2
|
+
import specklepy.api.credentials
|
|
3
|
+
import specklepy.api.client
|
|
4
|
+
from specklepy.transports.server import ServerTransport
|
|
5
|
+
from specklepy.api import operations
|
|
6
|
+
from specklepy import objects
|
|
7
|
+
from specklepy.api.wrapper import StreamWrapper
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Speckle:
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
host="speckle.xyz",
|
|
14
|
+
token=None):
|
|
15
|
+
|
|
16
|
+
print("Connecting to {} with {}".format(host, token))
|
|
17
|
+
self.client = specklepy.api.client.SpeckleClient(host=host)
|
|
18
|
+
account = specklepy.api.credentials.get_account_from_token(token, host)
|
|
19
|
+
self.client.authenticate_with_account(account)
|
|
20
|
+
print(self.client)
|
|
21
|
+
|
|
22
|
+
def get_item(
|
|
23
|
+
self,
|
|
24
|
+
stream_id: str,
|
|
25
|
+
commit_id: str):
|
|
26
|
+
stream = self.client.stream.get(id=stream_id)
|
|
27
|
+
transport = ServerTransport(client=self.client, stream_id=stream_id)
|
|
28
|
+
# latest_commit = stream.branches.items[0].commits.items[0]
|
|
29
|
+
|
|
30
|
+
commit = self.client.commit.get(stream_id, commit_id)
|
|
31
|
+
received_base = operations.receive(obj_id=commit.referencedObject, remote_transport=transport)
|
|
32
|
+
data = received_base
|
|
33
|
+
return data
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# @staticmethod
|
|
37
|
+
# def query(
|
|
38
|
+
# stream_id: str,
|
|
39
|
+
# commit_id: str,
|
|
40
|
+
# host: str = 'https://speckle.xyz/',
|
|
41
|
+
# account: specklepy.api.credentials.Account = None):
|
|
42
|
+
# foo = 1
|
|
43
|
+
# account = get_default_account() if account is None else account
|
|
44
|
+
# client = SpeckleClient(host=host)
|
|
45
|
+
# client.authenticate_with_account(account)
|
|
46
|
+
#
|
|
47
|
+
# commit = client.commit.get(stream_id, commit_id)
|
|
48
|
+
# transport = ServerTransport(client=client, stream_id=stream_id)
|
|
49
|
+
#
|
|
50
|
+
# obj = operations.receive(commit.referencedObject, transport)
|
|
51
|
+
#
|
|
52
|
+
# data = obj['data']
|
|
53
|
+
#
|
|
54
|
+
# return data
|
|
55
|
+
#
|
|
56
|
+
# @staticmethod
|
|
57
|
+
# def query2(resource: str):
|
|
58
|
+
# wrapper = StreamWrapper(resource)
|
|
59
|
+
# client = wrapper.get_client()
|
|
60
|
+
# transport = wrapper.get_transport()
|
|
61
|
+
#
|
|
62
|
+
# commit_id = wrapper.commit_id
|
|
63
|
+
#
|
|
64
|
+
# commit = client.commit.get(wrapper.stream_id, commit_id)
|
|
65
|
+
#
|
|
66
|
+
# obj = client.object.get(wrapper.stream_id, wrapper.object_id)
|
|
67
|
+
#
|
|
68
|
+
# return client.object.get(wrapper.stream_id, obj.get_id())#[obj.get_dynamic_member_names()[0]].id
|
|
69
|
+
# # obj = operations.receive(commit.referencedObject, transport)
|
|
70
|
+
#
|
|
71
|
+
#
|
|
72
|
+
#
|
|
73
|
+
#
|
|
74
|
+
#
|
|
75
|
+
#
|
|
76
|
+
#
|
|
77
|
+
#
|
|
78
|
+
#
|
|
79
|
+
# # or whatever your host is
|
|
80
|
+
|
|
81
|
+
#
|
|
82
|
+
# class Conversion:
|
|
83
|
+
# @staticmethod
|
|
84
|
+
# def from_speckle(
|
|
85
|
+
# element: specklepy.objects.Base
|
|
86
|
+
# ):
|
|
87
|
+
# foo = specklepy.objects.base.Base()
|
|
88
|
+
# foo.totalChildrenCount
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import enum
|
|
4
|
+
import numpy as np
|
|
5
|
+
import scipy.stats as ss
|
|
6
|
+
from abc import abstractmethod
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Type(enum.Enum):
|
|
10
|
+
UNIFORM = 'Uniform' # Continuous uniform distribution or rectangular distribution
|
|
11
|
+
TRIANGULAR = 'Triangular' # Continuous linear distribution with lower limit a, upper limit b and mode c, where a < b and a ≤ c ≤ b.
|
|
12
|
+
NORMAL = 'Normal' # Continuous probability distribution defined as the limiting case of a discrete binomial distribution.
|
|
13
|
+
PERT = 'PERT' # Transformation of the four-parameter Beta distribution defined by the minimum, most likely, and maximum values.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Form:
|
|
17
|
+
type: Type
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
generator: Optional[np.random.Generator] = None):
|
|
22
|
+
self.generator = generator
|
|
23
|
+
self.dist = ss.rv_continuous()
|
|
24
|
+
|
|
25
|
+
def sample(
|
|
26
|
+
self,
|
|
27
|
+
size: int = 1):
|
|
28
|
+
|
|
29
|
+
if hasattr(self.dist, 'b'):
|
|
30
|
+
if self.dist.b == 0.:
|
|
31
|
+
return self.dist.a # If the distribution is a point mass, return the value of the point mass
|
|
32
|
+
|
|
33
|
+
if self.generator is None:
|
|
34
|
+
generator = np.random.default_rng()
|
|
35
|
+
else:
|
|
36
|
+
generator = self.generator
|
|
37
|
+
return self.dist.rvs(size=size, random_state=generator)
|
|
38
|
+
|
|
39
|
+
# if size == 1:
|
|
40
|
+
# return variates[0]
|
|
41
|
+
# else:
|
|
42
|
+
# return variates
|
|
43
|
+
|
|
44
|
+
@abstractmethod
|
|
45
|
+
def interval_density(
|
|
46
|
+
self,
|
|
47
|
+
parameters: [float]):
|
|
48
|
+
"""
|
|
49
|
+
Returns the cumulative density (integral of the interpolated curve)
|
|
50
|
+
between n parameter pairs as intervals (i.e. returns n-1 results)
|
|
51
|
+
|
|
52
|
+
:param parameters: Any set of floats between 0 and 1
|
|
53
|
+
:return: List of floats representing the cumulative density of that interval.
|
|
54
|
+
If the input parameters span 0 to 1, the sum of the interval densities will reach 1.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
if (all(parameters) >= 0) & (all(parameters) <= 1):
|
|
58
|
+
return [(self.dist.cdf(parameters[i + 1]) - self.dist.cdf(parameters[i]))
|
|
59
|
+
for i in (range(0, len(parameters) - 1))]
|
|
60
|
+
else:
|
|
61
|
+
raise ValueError("Error: Parameter must be between 0 and 1 inclusive")
|
|
62
|
+
|
|
63
|
+
@abstractmethod
|
|
64
|
+
def cumulative_density(
|
|
65
|
+
self,
|
|
66
|
+
parameters: [float]):
|
|
67
|
+
"""
|
|
68
|
+
Returns the cumulative distribution at parameters between 0 and 1.
|
|
69
|
+
"""
|
|
70
|
+
if (all(parameters) >= 0) & (all(parameters) <= 1):
|
|
71
|
+
return [self.dist.cdf(parameter) for parameter in parameters]
|
|
72
|
+
else:
|
|
73
|
+
raise ValueError("Error: Parameter must be between 0 and 1 inclusive")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class Symmetric(Form):
|
|
77
|
+
def __init__(
|
|
78
|
+
self,
|
|
79
|
+
type: Type,
|
|
80
|
+
residual: float,
|
|
81
|
+
mean: float = 0,
|
|
82
|
+
generator: Optional[np.random.Generator] = None):
|
|
83
|
+
"""
|
|
84
|
+
Parameters that define the mean and residual (maximum deviation)
|
|
85
|
+
of a symmetric distribution
|
|
86
|
+
|
|
87
|
+
:param type: Type of symmetric distribution
|
|
88
|
+
:param residual: Maximum deviation from the mean
|
|
89
|
+
:param mean: Mean of the distribution. Defaults to 0.
|
|
90
|
+
"""
|
|
91
|
+
super().__init__(generator=generator)
|
|
92
|
+
if type is Type.UNIFORM or type is Type.PERT or type is Type.TRIANGULAR:
|
|
93
|
+
self.type = type
|
|
94
|
+
else:
|
|
95
|
+
raise ValueError("Distribution type must be able to be symmetrical about its mean")
|
|
96
|
+
self.mean = mean
|
|
97
|
+
self.residual = residual
|
|
98
|
+
self.generator = generator
|
|
99
|
+
self.dist = self._distribution().dist
|
|
100
|
+
|
|
101
|
+
def _distribution(self):
|
|
102
|
+
if self.type == Type.UNIFORM:
|
|
103
|
+
return Uniform(
|
|
104
|
+
lower=self.mean - self.residual,
|
|
105
|
+
range=self.residual * 2,
|
|
106
|
+
generator=self.generator)
|
|
107
|
+
elif self.type == Type.PERT:
|
|
108
|
+
return PERT.symmetric(
|
|
109
|
+
peak=self.mean,
|
|
110
|
+
residual=self.residual)
|
|
111
|
+
elif self.type == Type.TRIANGULAR:
|
|
112
|
+
return Triangular.symmetric(
|
|
113
|
+
mode=self.mean,
|
|
114
|
+
residual=self.residual)
|
|
115
|
+
|
|
116
|
+
def interval_density(
|
|
117
|
+
self,
|
|
118
|
+
parameters: [float]) -> [float]:
|
|
119
|
+
return self._distribution().interval_density(parameters)
|
|
120
|
+
|
|
121
|
+
def cumulative_density(
|
|
122
|
+
self,
|
|
123
|
+
parameters: [float]) -> [float]:
|
|
124
|
+
return self._distribution().cumulative_density(parameters)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class Uniform(Form):
|
|
128
|
+
"""
|
|
129
|
+
For default arguments, this is a continuous, uniform distribution between 0 and 1,
|
|
130
|
+
such the cumulative distribution reaches 1. (For uniform distribution, this means the density is continuously 1).
|
|
131
|
+
|
|
132
|
+
For specified arguments, this distribution is constant between lower, and lower + range.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
def __init__(
|
|
136
|
+
self,
|
|
137
|
+
lower: float = 0.,
|
|
138
|
+
range: float = 1.,
|
|
139
|
+
generator: Optional[np.random.Generator] = None):
|
|
140
|
+
super().__init__(generator=generator)
|
|
141
|
+
self.dist = ss.uniform(loc=lower, scale=range)
|
|
142
|
+
self.type = Type.UNIFORM
|
|
143
|
+
|
|
144
|
+
@classmethod
|
|
145
|
+
def symmetric(
|
|
146
|
+
cls,
|
|
147
|
+
mean: float,
|
|
148
|
+
residual: float,
|
|
149
|
+
generator: Optional[np.random.Generator] = None):
|
|
150
|
+
return cls(
|
|
151
|
+
lower=mean - residual,
|
|
152
|
+
range=residual * 2,
|
|
153
|
+
generator=generator)
|
|
154
|
+
|
|
155
|
+
def interval_density(
|
|
156
|
+
self,
|
|
157
|
+
parameters: [float]) -> [float]:
|
|
158
|
+
return super().interval_density(parameters)
|
|
159
|
+
|
|
160
|
+
def cumulative_density(
|
|
161
|
+
self,
|
|
162
|
+
parameters: [float]) -> [float]:
|
|
163
|
+
return super().cumulative_density(parameters)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class Triangular(Form):
|
|
167
|
+
"""
|
|
168
|
+
A continuous probability distribution with lower limit a, upper limit b and mode c, where a < b and a ≤ c ≤ b.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
def __init__(
|
|
172
|
+
self,
|
|
173
|
+
lower: float = 0.,
|
|
174
|
+
upper: float = 1.,
|
|
175
|
+
mode: float = 0.5,
|
|
176
|
+
generator: Optional[np.random.Generator] = None):
|
|
177
|
+
super().__init__(generator=generator)
|
|
178
|
+
loc = lower
|
|
179
|
+
scale = upper - lower
|
|
180
|
+
if scale == 0:
|
|
181
|
+
c = lower
|
|
182
|
+
else:
|
|
183
|
+
c = (mode - lower) / (upper - lower)
|
|
184
|
+
self.dist = ss.triang(loc=loc, scale=scale, c=c)
|
|
185
|
+
self.type = Type.TRIANGULAR
|
|
186
|
+
|
|
187
|
+
@classmethod
|
|
188
|
+
def symmetric(
|
|
189
|
+
cls,
|
|
190
|
+
mode: float,
|
|
191
|
+
residual: float):
|
|
192
|
+
return cls(
|
|
193
|
+
lower=mode - residual,
|
|
194
|
+
upper=mode + residual,
|
|
195
|
+
mode=mode)
|
|
196
|
+
|
|
197
|
+
def interval_density(
|
|
198
|
+
self,
|
|
199
|
+
parameters: [float]) -> [float]:
|
|
200
|
+
return super().interval_density(parameters)
|
|
201
|
+
|
|
202
|
+
def cumulative_density(
|
|
203
|
+
self,
|
|
204
|
+
parameters: [float]) -> [float]:
|
|
205
|
+
return super().cumulative_density(parameters)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class PERT(Form):
|
|
209
|
+
"""
|
|
210
|
+
A continuous distribution that is controlled by the placement and weighting of the mode (peak) value.
|
|
211
|
+
It produces similar distributions to the Normal (Gaussian) distribution within a specified domain.
|
|
212
|
+
Its cumulative distribution function (CDF) curve is a classic 's-curve'.
|
|
213
|
+
|
|
214
|
+
Peak must be between 0 and 1. Weighting must be >= 0. Standard weighting is 4.
|
|
215
|
+
|
|
216
|
+
Specifically, it is a Beta Distribution extended to specified domain,
|
|
217
|
+
with the mean (μ) constrained so that μ = (a + Ɣ*b + c) / (Ɣ + 2),
|
|
218
|
+
where a and c are the limits (0 and 1 in this formulation), b is the mode (peak value),
|
|
219
|
+
and Ɣ is a weighting factor for the mode.
|
|
220
|
+
|
|
221
|
+
This function is built from the formulation in Tensorflow:
|
|
222
|
+
https://github.com/tensorflow/probability/blob/master/tensorflow_probability/python/distributions/pert.py
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
def __init__(
|
|
226
|
+
self,
|
|
227
|
+
peak: float = 0.5,
|
|
228
|
+
weighting: float = 4.0,
|
|
229
|
+
minimum: float = 0.0,
|
|
230
|
+
maximum: float = 1.0,
|
|
231
|
+
generator: Optional[np.random.Generator] = None):
|
|
232
|
+
super().__init__(generator=generator)
|
|
233
|
+
self.type = Type.PERT
|
|
234
|
+
self.peak = peak
|
|
235
|
+
self.weighting = weighting
|
|
236
|
+
self.minimum = minimum
|
|
237
|
+
self.maximum = maximum
|
|
238
|
+
self.scale = maximum - minimum
|
|
239
|
+
if (self.weighting >= 0) & (self.peak >= minimum) & (self.peak <= maximum):
|
|
240
|
+
a = (1. + self.weighting * (self.peak - minimum) / (maximum - minimum))
|
|
241
|
+
b = (1. + self.weighting * (maximum - self.peak) / (maximum - minimum))
|
|
242
|
+
self.dist = ss.beta(
|
|
243
|
+
loc=minimum,
|
|
244
|
+
scale=self.scale,
|
|
245
|
+
a=a,
|
|
246
|
+
b=b)
|
|
247
|
+
else:
|
|
248
|
+
raise ValueError("Error: Weighting must be greater than 0 and Peak must be between 0 and 1 inclusive")
|
|
249
|
+
|
|
250
|
+
@classmethod
|
|
251
|
+
def symmetric(
|
|
252
|
+
cls,
|
|
253
|
+
peak: float,
|
|
254
|
+
residual: float):
|
|
255
|
+
return cls(
|
|
256
|
+
peak=peak,
|
|
257
|
+
weighting=4.,
|
|
258
|
+
minimum=peak - residual,
|
|
259
|
+
maximum=peak + residual)
|
|
260
|
+
|
|
261
|
+
def interval_density(
|
|
262
|
+
self,
|
|
263
|
+
parameters: [float]) -> [float]:
|
|
264
|
+
return super().interval_density(parameters)
|
|
265
|
+
|
|
266
|
+
def cumulative_density(
|
|
267
|
+
self,
|
|
268
|
+
parameters: [float]) -> [float]:
|
|
269
|
+
return super().cumulative_density(parameters)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from numba import jit
|
|
6
|
+
|
|
7
|
+
import rangekeeper as rk
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BlackSwan:
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
sequence: pd.PeriodIndex,
|
|
14
|
+
likelihood: float,
|
|
15
|
+
dissipation_rate: float,
|
|
16
|
+
probability: rk.distribution.Form,
|
|
17
|
+
impact: float):
|
|
18
|
+
"""
|
|
19
|
+
This random variable will determine whether a "black swan" event
|
|
20
|
+
occurs in any given year. We ensure that no more than one black
|
|
21
|
+
swan will occur in the 24-yr history, as black swans are by definition
|
|
22
|
+
rare events.
|
|
23
|
+
|
|
24
|
+
This column causes the effect of the Black Swan event to dissipate
|
|
25
|
+
over time, geometrically, at the same mean reversion rate as is
|
|
26
|
+
applied in general to the rents (entered in co.F).
|
|
27
|
+
|
|
28
|
+
This is another source or type of uncertainty in real estate pricing.
|
|
29
|
+
This will apply the given "black swan" result, but then reduces
|
|
30
|
+
the subsequent impact of the event gradually as mean-reversion
|
|
31
|
+
takes effect.
|
|
32
|
+
"""
|
|
33
|
+
self.sequence = sequence
|
|
34
|
+
self.likelihood = likelihood
|
|
35
|
+
self.dissipation_rate = dissipation_rate
|
|
36
|
+
self.probability = probability
|
|
37
|
+
self.impact = impact
|
|
38
|
+
|
|
39
|
+
def generate(self) -> rk.flux.Flow:
|
|
40
|
+
return rk.flux.Flow(
|
|
41
|
+
name='Black Swan Effect',
|
|
42
|
+
movements=pd.Series(
|
|
43
|
+
data=self.calculate_black_swan_effects(
|
|
44
|
+
likelihood=self.likelihood,
|
|
45
|
+
dissipation_rate=self.dissipation_rate,
|
|
46
|
+
events=self.probability.sample(self.sequence.size)),
|
|
47
|
+
index=rk.periodicity.to_datestamps(period_index=self.sequence)))
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
@jit(nopython=True)
|
|
51
|
+
def calculate_black_swan_effects(
|
|
52
|
+
likelihood: float,
|
|
53
|
+
dissipation_rate: float,
|
|
54
|
+
events: [float]):
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
:param likelihood:
|
|
58
|
+
:type likelihood:
|
|
59
|
+
:param dissipation_rate:
|
|
60
|
+
:type dissipation_rate:
|
|
61
|
+
:param events:
|
|
62
|
+
:type events:
|
|
63
|
+
:return:
|
|
64
|
+
:rtype:
|
|
65
|
+
"""
|
|
66
|
+
idx = -1
|
|
67
|
+
impacts = []
|
|
68
|
+
for i in range(len(events)):
|
|
69
|
+
if idx == -1:
|
|
70
|
+
if events[i] < likelihood:
|
|
71
|
+
idx = i
|
|
72
|
+
impacts.append(1)
|
|
73
|
+
else:
|
|
74
|
+
impacts.append(0)
|
|
75
|
+
else:
|
|
76
|
+
impacts.append(np.power((1 - dissipation_rate), (i - idx)))
|
|
77
|
+
return impacts
|