Skip to content

Commit

Permalink
ENH: add Factor.clip method
Browse files Browse the repository at this point in the history
  • Loading branch information
Joe Jevnik committed May 14, 2020
1 parent bc469db commit bec7781
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 0 deletions.
30 changes: 30 additions & 0 deletions tests/pipeline/test_factor.py
Expand Up @@ -1328,6 +1328,36 @@ def test_quantile_helpers(self):
self.assertIs(f.deciles(mask=m), f.quantiles(bins=10, mask=m))
self.assertIsNot(f.deciles(), f.deciles(mask=m))

@parameter_space(seed=[1, 2, 3])
def test_clip(self, seed):
rand = np.random.RandomState(seed)
shape = (5, 5)
original_min = -10
original_max = +10
input_array = rand.uniform(
original_min,
original_max,
size=shape,
)
min_, max_ = np.percentile(input_array, [25, 75])
self.assertGreater(min_, original_min)
self.assertLess(max_, original_max)

f = F()

self.check_terms(
terms={
'clip': f.clip(min_, max_)
},
initial_workspace={
f: input_array,
},
expected={
'clip': np.clip(input_array, min_, max_),
},
mask=self.build_mask(self.ones_mask(shape=shape)),
)


class ReprTestCase(TestCase):
"""
Expand Down
38 changes: 38 additions & 0 deletions zipline/pipeline/factors/basic.py
Expand Up @@ -4,6 +4,7 @@
from numpy import (
arange,
average,
clip,
copyto,
exp,
fmax,
Expand Down Expand Up @@ -526,3 +527,40 @@ def compute(self, today, assets, out, classifier_values):
# Convenience aliases
EWMA = ExponentialWeightedMovingAverage
EWMSTD = ExponentialWeightedMovingStdDev


class Clip(CustomFactor):
"""
Clip (limit) the values in a factor.
Given an interval, values outside the interval are clipped to the interval
edges. For example, if an interval of ``[0, 1]`` is specified, values
smaller than 0 become 0, and values larger than 1 become 1.
**Default Window Length:** 1
Parameters
----------
min_bound : float
The minimum value to use.
max_bound : float
The maximum value to use.
Notes
-----
To only clip values on one side, ``-np.inf` and ``np.inf`` may be passed.
For example, to only clip the maximum value but not clip a minimum value:
.. code-block::
Clip(inputs=[factor], min_bound=-np.inf, max_bound=user_provided_max)
See Also
--------
numpy.clip
"""
window_length = 1
params = ('min_bound', 'max_bound')

def compute(self, today, assets, out, values, min_bound, max_bound):
clip(values[-1], min_bound, max_bound, out=out)
39 changes: 39 additions & 0 deletions zipline/pipeline/factors/factor.py
Expand Up @@ -1301,6 +1301,45 @@ def isfinite(self):
"""
return (-inf < self) & (self < inf)

def clip(self, min_bound, max_bound, mask=NotSpecified):
"""
Clip (limit) the values in a factor.
Given an interval, values outside the interval are clipped to the
interval edges. For example, if an interval of ``[0, 1]`` is specified,
values smaller than 0 become 0, and values larger than 1 become 1.
Parameters
----------
min_bound : float
The minimum value to use.
max_bound : float
The maximum value to use.
mask : zipline.pipeline.Filter, optional
A Filter representing assets to consider when clipping.
Notes
-----
To only clip values on one side, ``-np.inf` and ``np.inf`` may be
passed. For example, to only clip the maximum value but not clip a
minimum value:
.. code-block::
factor.clip(min_bound=-np.inf, max_bound=user_provided_max)
See Also
--------
numpy.clip
"""
from .basic import Clip

return Clip(
inputs=[self],
min_bound=min_bound,
max_bound=max_bound,
)

@classmethod
def _principal_computable_term_type(cls):
return Factor
Expand Down

0 comments on commit bec7781

Please sign in to comment.