mirror of
https://github.com/wassname/catalyst.git
synced 2026-06-30 22:01:30 +08:00
ENH: Make aliases filters, factors, and classifiers to give them their methods
This commit is contained in:
@@ -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
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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
@@ -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__.
|
||||
|
||||
@@ -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."""
|
||||
|
||||
Reference in New Issue
Block a user