ENH: Make aliases filters, factors, and classifiers to give them their methods

This commit is contained in:
Joe Jevnik
2016-10-03 20:26:06 -04:00
parent 13c8139d45
commit c8e40a3736
8 changed files with 175 additions and 43 deletions
+54
View File
@@ -0,0 +1,54 @@
import numpy as np
from zipline.testing.predicates import assert_equal
from zipline.pipeline import Classifier, Factor, Filter
from zipline.utils.numpy_utils import float64_dtype, int64_dtype
from .base import BasePipelineTestCase
class WithAlias(object):
def test_alias(self):
f = self.Term()
alias = f.alias('ayy lmao')
f_values = np.random.RandomState(5).randn(5, 5)
self.check_terms(
terms={
'f_alias': alias,
},
expected={
'f_alias': f_values,
},
initial_workspace={f: f_values},
mask=self.build_mask(np.ones((5, 5))),
)
def test_repr(self):
assert_equal(
repr(self.Term().alias('ayy lmao')),
"Aliased%s(..., name='ayy lmao')" % self.Term.__base__.__name__,
)
class TestFactorAlias(WithAlias, BasePipelineTestCase):
class Term(Factor):
dtype = float64_dtype
inputs = ()
window_length = 0
class TestFilterAlias(WithAlias, BasePipelineTestCase):
class Term(Filter):
inputs = ()
window_length = 0
class TestClassifierAlias(WithAlias, BasePipelineTestCase):
class Term(Classifier):
dtype = int64_dtype
inputs = ()
window_length = 0
missing_value = -1
+1 -22
View File
@@ -20,14 +20,13 @@ from numpy import (
rot90,
where,
)
from numpy.random import randn, RandomState, seed
from numpy.random import randn, seed
from zipline.errors import UnknownRankMethod
from zipline.lib.labelarray import LabelArray
from zipline.lib.rank import masked_rankdata_2d
from zipline.lib.normalize import naive_grouped_rowwise_apply as grouped_apply
from zipline.pipeline import Classifier, Factor, Filter
from zipline.pipeline.term import Alias
from zipline.pipeline.factors import (
Returns,
RSI,
@@ -1059,23 +1058,3 @@ class TestWindowSafety(TestCase):
self.assertFalse(F().demean().window_safe)
self.assertFalse(F(window_safe=False).demean().window_safe)
self.assertTrue(F(window_safe=True).demean().window_safe)
class TestAlias(BasePipelineTestCase):
def test_alias_factor(self):
f = F()
a = Alias(f)
f_values = RandomState(5).randn(5, 5)
self.check_terms(
terms={
'f_alias': a,
},
expected={
'f_alias': f_values,
},
initial_workspace={f: f_values},
mask=self.build_mask(ones((5, 5))),
)
@@ -24,6 +24,7 @@ from zipline.utils.numpy_utils import (
from ..filters import ArrayPredicate, NotNullFilter, NullFilter, NumExprFilter
from ..mixins import (
AliasedMixin,
CustomTermMixin,
DownsampledMixin,
LatestMixin,
@@ -323,6 +324,10 @@ class Classifier(RestrictedDTypeMixin, ComputableTerm):
def _downsampled_type(self):
return DownsampledMixin.make_downsampled_type(Classifier)
@classlazyval
def _aliased_type(self):
return AliasedMixin.make_aliased_type(Classifier)
class Everything(Classifier):
"""
+5
View File
@@ -32,6 +32,7 @@ from zipline.pipeline.filters import (
NullFilter,
)
from zipline.pipeline.mixins import (
AliasedMixin,
CustomTermMixin,
DownsampledMixin,
LatestMixin,
@@ -1078,6 +1079,10 @@ class Factor(RestrictedDTypeMixin, ComputableTerm):
def _downsampled_type(self):
return DownsampledMixin.make_downsampled_type(Factor)
@classlazyval
def _aliased_type(self):
return AliasedMixin.make_aliased_type(Factor)
class NumExprFactor(NumericalExpression, Factor):
"""
+5
View File
@@ -24,6 +24,7 @@ from zipline.pipeline.expression import (
NumericalExpression,
)
from zipline.pipeline.mixins import (
AliasedMixin,
CustomTermMixin,
DownsampledMixin,
LatestMixin,
@@ -207,6 +208,10 @@ class Filter(RestrictedDTypeMixin, ComputableTerm):
def _downsampled_type(self):
return DownsampledMixin.make_downsampled_type(Filter)
@classlazyval
def _aliased_type(self):
return AliasedMixin.make_aliased_type(Filter)
class NumExprFilter(NumericalExpression, Filter):
"""
+67
View File
@@ -20,6 +20,7 @@ from zipline.utils.control_flow import nullctx
from zipline.utils.input_validation import expect_types
from zipline.utils.sharedoc import (
format_docstring,
PIPELINE_ALIAS_DOC,
PIPELINE_DOWNSAMPLING_FREQUENCY_DOC,
)
from zipline.utils.pandas_utils import nearest_unequal_elements
@@ -240,6 +241,72 @@ class LatestMixin(SingleInputMixin):
)
class AliasedMixin(SingleInputMixin):
"""
Mixin for aliased terms.
"""
def __new__(cls, term, name):
return super(AliasedMixin, cls).__new__(
cls,
inputs=(term,),
outputs=term.outputs,
window_length=0,
name=name,
dtype=term.dtype,
missing_value=term.missing_value,
ndim=term.ndim,
)
def _init(self, name, *args, **kwargs):
self.name = name
return super(AliasedMixin, self)._init(*args, **kwargs)
@classmethod
def _static_identity(cls, name, *args, **kwargs):
return (
super(AliasedMixin, cls)._static_identity(*args, **kwargs),
name,
)
def _compute(self, inputs, dates, assets, mask):
return inputs[0]
def __repr__(self):
return '{type}(..., name={name!r})'.format(
type=type(self).__name__,
name=self.name,
)
@classmethod
def make_aliased_type(cls, other_base):
"""
Factory for making Aliased{Filter,Factor,Classifier}.
"""
docstring = dedent(
"""
A {t} that names another {t}.
Parameters
----------
term : {t}
{{name}}
"""
).format(t=other_base.__name__)
doc = format_docstring(
owner_name=other_base.__name__,
docstring=docstring,
formatters={'name': PIPELINE_ALIAS_DOC},
)
return type(
'Aliased' + other_base.__name__,
(cls, other_base,),
{'__doc__': doc,
'__module__': other_base.__module__},
)
class DownsampledMixin(StandardOutputs):
"""
Mixin for behavior shared by Downsampled{Factor,Filter,Classifier}
+31 -21
View File
@@ -39,6 +39,7 @@ from zipline.utils.numpy_utils import (
)
from zipline.utils.sharedoc import (
templated_docstring,
PIPELINE_ALIAS_DOC,
PIPELINE_DOWNSAMPLING_FREQUENCY_DOC,
)
@@ -602,7 +603,7 @@ class ComputableTerm(Term):
fill_value=self.missing_value,
).values
def _downsampled_type(self):
def _downsampled_type(self, *args, **kwargs):
"""
The expression type to return from self.downsample().
"""
@@ -623,6 +624,35 @@ class ComputableTerm(Term):
"""
return self._downsampled_type(term=self, frequency=frequency)
def _aliased_type(self, *args, **kwargs):
"""
The expression type to return from self.alias().
"""
raise NotImplementedError(
"alias is not yet implemented "
"for instances of %s." % type(self).__name__
)
@templated_docstring(name=PIPELINE_ALIAS_DOC)
def alias(self, name):
"""
Make a term from ``self`` that names the expression.
Parameters
----------
{name}
Returns
-------
aliased : Aliased
``self`` with a name.
Notes
-----
This is useful for giving a name to a numerical or boolean expression.
"""
return self._aliased_type(term=self, name=name)
def __repr__(self):
return (
"{type}({inputs}, window_length={window_length})"
@@ -696,26 +726,6 @@ class Slice(ComputableTerm):
)
class Alias(ComputableTerm):
"""An alias for another computed term."""
@expect_types(term=ComputableTerm)
def __new__(cls, term):
return super(Alias, cls).__new__(
cls,
window_length=0,
dtype=term.dtype,
missing_value=term.missing_value,
window_safe=term.window_safe,
ndim=term.ndim,
domain=term.domain,
inputs=(term,),
)
def _compute(self, inputs, dates, assets, mask):
return inputs[0]
def validate_dtype(termname, dtype, missing_value):
"""
Validate a `dtype` and `missing_value` passed to Term.__new__.
+7
View File
@@ -18,6 +18,13 @@ PIPELINE_DOWNSAMPLING_FREQUENCY_DOC = dedent(
"""
)
PIPELINE_ALIAS_DOC = dedent(
"""\
name : str
The name to alias this term as.
""",
)
def pad_lines_after_first(prefix, s):
"""Apply a prefix to each line in s after the first."""