DEV: Kill compute_from_{arrays,windows}.

All terms just implement `_compute` now. (We reserve `compute` for the
public API of `CustomFactor`.)

Also removed `TestingTermMixin` and its subclasses in favor of just
using `CustomFactor.`
This commit is contained in:
Scott Sanderson
2015-09-16 01:28:15 -04:00
parent 58ceb7b7bb
commit 5730de25a4
11 changed files with 42 additions and 137 deletions
+4 -4
View File
@@ -40,7 +40,7 @@ from zipline.data.ffc.loaders.us_equity_pricing import (
)
from zipline.finance.trading import TradingEnvironment
from zipline.modelling.engine import SimpleFFCEngine
from zipline.modelling.factor import TestingFactor
from zipline.modelling.factor import CustomFactor
from zipline.modelling.factor.technical import (
MaxDrawdown,
SimpleMovingAverage,
@@ -54,12 +54,12 @@ from zipline.utils.test_utils import (
)
class RollingSumDifference(TestingFactor):
class RollingSumDifference(CustomFactor):
window_length = 3
inputs = [USEquityPricing.open, USEquityPricing.close]
def from_windows(self, open, close):
return (open - close).sum(axis=0)
def compute(self, today, assets, out, open, close):
out[:] = (open - close).sum(axis=0)
def assert_product(case, index, *levels):
+2 -2
View File
@@ -7,13 +7,13 @@ from numpy import (
)
from zipline.errors import UnknownRankMethod
from zipline.modelling.factor import TestingFactor
from zipline.modelling.factor import Factor
from zipline.utils.test_utils import check_arrays
from .base import BaseFFCTestCase
class F(TestingFactor):
class F(Factor):
inputs = ()
window_length = 0
+3 -3
View File
@@ -17,7 +17,7 @@ from numpy import (
)
from zipline.errors import BadPercentileBounds
from zipline.modelling.factor import TestingFactor
from zipline.modelling.factor import Factor
from zipline.utils.test_utils import check_arrays
from .base import BaseFFCTestCase
@@ -51,12 +51,12 @@ def rowwise_rank(array):
return argsort(argsort(array))
class SomeFactor(TestingFactor):
class SomeFactor(Factor):
inputs = ()
window_length = 0
class SomeOtherFactor(TestingFactor):
class SomeOtherFactor(Factor):
inputs = ()
window_length = 0
+5 -5
View File
@@ -28,21 +28,21 @@ from zipline.modelling.expression import (
NumericalExpression,
NUMEXPR_MATH_FUNCS,
)
from zipline.modelling.factor import TestingFactor
from zipline.modelling.factor import Factor
from zipline.utils.test_utils import check_arrays
class F(TestingFactor):
class F(Factor):
inputs = ()
window_length = 0
class G(TestingFactor):
class G(Factor):
inputs = ()
window_length = 0
class H(TestingFactor):
class H(Factor):
inputs = ()
window_length = 0
@@ -63,7 +63,7 @@ class NumericalExpressionTestCase(TestCase):
self.mask = DataFrame(True, index=self.dates, columns=self.assets)
def check_output(self, expr, expected):
result = expr.compute_from_arrays(
result = expr._compute(
[self.fake_raw_data[input_] for input_ in expr.inputs],
self.mask,
)
+1 -5
View File
@@ -317,11 +317,7 @@ class SimpleFFCEngine(object):
for loaded_term, adj_array in zip_longest(to_load, loaded):
workspace[loaded_term] = adj_array
else:
if term.windowed:
compute = term.compute_from_windows
else:
compute = term.compute_from_arrays
workspace[term] = compute(
workspace[term] = term._compute(
self._inputs_for_term(term, workspace, extra_rows),
base_mask_for_term,
)
+1 -1
View File
@@ -228,7 +228,7 @@ class NumericalExpression(Term):
)
return super(NumericalExpression, self)._validate()
def compute_from_arrays(self, arrays, mask):
def _compute(self, arrays, mask):
"""
Compute our stored expression string with numexpr.
"""
+15 -3
View File
@@ -1,11 +1,23 @@
from .factor import (
Factor,
TestingFactor,
CustomFactor,
)
from .latest import Latest
from .technical import (
MaxDrawdown,
RSI,
SimpleMovingAverage,
VWAP,
WeightedAverageValue,
)
__all__ = [
'Factor',
'TestingFactor',
'CustomFactor',
'Factor',
'Latest',
'MaxDrawdown',
'RSI',
'SimpleMovingAverage',
'VWAP',
'WeightedAverageValue',
]
+1 -10
View File
@@ -19,7 +19,6 @@ from zipline.modelling.term import (
RequiredWindowLengthMixin,
SingleInputMixin,
Term,
TestingTermMixin,
)
from zipline.modelling.expression import (
BadBinaryOperator,
@@ -393,7 +392,7 @@ class Rank(SingleInputMixin, Factor):
)
return super(Rank, self)._validate()
def compute_from_arrays(self, arrays, mask):
def _compute(self, arrays, mask):
"""
For each row in the input, compute a like-shaped array of per-row
ranks.
@@ -441,11 +440,3 @@ class CustomFactor(RequiredWindowLengthMixin, CustomTermMixin, Factor):
if self.dtype != float64:
raise UnsupportedDataType(self.dtype)
return super(CustomFactor, self)._validate()
class TestingFactor(TestingTermMixin, Factor):
"""
Base class for testing engines that asserts all inputs are correctly
shaped.
"""
pass
+5 -14
View File
@@ -16,7 +16,6 @@ from zipline.errors import (
from zipline.modelling.term import (
SingleInputMixin,
Term,
TestingTermMixin,
)
from zipline.modelling.expression import (
BadBinaryOperator,
@@ -124,11 +123,11 @@ class NumExprFilter(NumericalExpression, Filter):
A Filter computed from a numexpr expression.
"""
def compute_from_arrays(self, arrays, mask):
def _compute(self, arrays, mask):
"""
Compute our result with numexpr, then apply `mask`.
"""
return super(NumExprFilter, self).compute_from_arrays(
return super(NumExprFilter, self)._compute(
arrays,
mask,
) & mask.values
@@ -181,7 +180,7 @@ class PercentileFilter(SingleInputMixin, Filter):
)
return super(PercentileFilter, self)._validate()
def compute_from_arrays(self, arrays, mask):
def _compute(self, arrays, mask):
"""
For each row in the input, compute a mask of all values falling between
the given percentiles.
@@ -261,21 +260,13 @@ class SequencedFilter(Filter):
then,
)
def compute_from_arrays(self, arrays, mask):
def _compute(self, arrays, mask):
"""
Call our second filter on its inputs, masking out any inputs rejected
by our first filter.
"""
first_result, then_inputs = arrays[0], arrays[1:]
return self._then.compute_from_arrays(
return self._then._compute(
then_inputs,
mask & first_result,
)
class TestingFilter(TestingTermMixin, Filter):
"""
Base class for testing engines that asserts all inputs are correctly
shaped.
"""
pass
+5 -44
View File
@@ -172,17 +172,12 @@ class Term(object):
"""
return max(0, self.window_length - 1)
def compute_from_windows(self, windows, mask):
def _compute(self, inputs, mask):
"""
Subclasses should implement this for computations requiring moving
windows of continually-adjusting data.
"""
raise NotImplementedError()
Subclasses should implement this to perform actual computation.
def compute_from_arrays(self, arrays, mask):
"""
Subclasses should implement this for computations that can be expressed
directly as array computations.
This is `_compute` rather than just `compute` because `compute` is
reserved for user-supplied functions in CustomFactor.
"""
raise NotImplementedError()
@@ -235,7 +230,7 @@ class CustomTermMixin(object):
"""
raise NotImplementedError()
def compute_from_windows(self, windows, mask):
def _compute(self, windows, mask):
"""
Call the user's `compute` function on each window with a pre-built
output array.
@@ -256,37 +251,3 @@ class CustomTermMixin(object):
)
out[~mask.values] = nan
return out
class TestingTermMixin(object):
"""
Mixin for Term subclasses testing engines that asserts all inputs are
correctly shaped.
Used by TestingTerm, TestingFilter, TestingClassifier, etc.
"""
def compute_from_windows(self, windows, mask):
assert self.window_length > 0
dates, assets = mask.index, mask.columns
outbuf = empty(mask.shape, dtype=self.dtype)
for idx, _ in enumerate(dates):
result = self.from_windows(*(next(w) for w in windows))
assert result.shape == (len(assets),)
outbuf[idx] = result
for window in windows:
try:
next(window)
except StopIteration:
pass
else:
raise AssertionError("window %s was not exhausted" % window)
return outbuf
def compute_from_arrays(self, arrays, mask):
assert self.window_length == 0
outbuf = empty(mask.shape, dtype=self.dtype)
for array in arrays:
assert array.shape == outbuf.shape
outbuf[:] = self.from_arrays(*arrays)
return outbuf
-46
View File
@@ -1,46 +0,0 @@
"""
An immutable, lazily loaded value descriptor.
"""
from weakref import WeakKeyDictionary
class lazyval(object):
"""
Decorator that marks that an attribute should not be computed until
needed, and that the value should be memoized.
Example
-------
>>> from zipline.utils.lazyval import lazyval
>>> class C(object):
... def __init__(self):
... self.count = 0
... @lazyval
... def val(self):
... self.count += 1
... return "val"
...
>>> c = C()
>>> c.count
0
>>> c.val, c.count
('val', 1)
>>> c.val, c.count
('val', 1)
"""
def __init__(self, get):
self._get = get
self._cache = WeakKeyDictionary()
def __get__(self, instance, owner):
if instance is None:
return self
try:
return self._cache[instance]
except KeyError:
self._cache[instance] = val = self._get(instance)
return val