diff --git a/zipline/pipeline/classifiers/classifier.py b/zipline/pipeline/classifiers/classifier.py index 81f08e7d..1ae66dbc 100644 --- a/zipline/pipeline/classifiers/classifier.py +++ b/zipline/pipeline/classifiers/classifier.py @@ -20,7 +20,7 @@ from zipline.utils.numpy_utils import ( vectorized_is_element, ) -from ..filters import ArrayPredicate, NullFilter, NumExprFilter +from ..filters import ArrayPredicate, NotNullFilter, NullFilter, NumExprFilter from ..mixins import ( CustomTermMixin, LatestMixin, @@ -64,7 +64,7 @@ class Classifier(RestrictedDTypeMixin, ComputableTerm): """ A Filter producing True for values where this term has complete data. """ - return ~self.isnull() + return NotNullFilter(self) # We explicitly don't support classifier to classifier comparisons, since # the stored values likely don't mean the same thing. This may be relaxed diff --git a/zipline/pipeline/factors/factor.py b/zipline/pipeline/factors/factor.py index 7f4d0a63..52403bcc 100644 --- a/zipline/pipeline/factors/factor.py +++ b/zipline/pipeline/factors/factor.py @@ -27,6 +27,7 @@ from zipline.pipeline.filters import ( Filter, NumExprFilter, PercentileFilter, + NotNullFilter, NullFilter, ) from zipline.pipeline.mixins import ( @@ -1013,7 +1014,7 @@ class Factor(RestrictedDTypeMixin, ComputableTerm): Equivalent to ``~self.isnan()` when ``self.dtype`` is float64. Otherwise equivalent to ``(self != self.missing_value)``. """ - return ~self.isnull() + return NotNullFilter(self) @if_not_float64_tell_caller_to_use_isnull def isnan(self): diff --git a/zipline/pipeline/filters/__init__.py b/zipline/pipeline/filters/__init__.py index c3589d99..b1c06ec4 100644 --- a/zipline/pipeline/filters/__init__.py +++ b/zipline/pipeline/filters/__init__.py @@ -3,6 +3,7 @@ from .filter import ( CustomFilter, Filter, Latest, + NotNullFilter, NullFilter, NumExprFilter, PercentileFilter, @@ -14,6 +15,7 @@ __all__ = [ 'CustomFilter', 'Filter', 'Latest', + 'NotNullFilter', 'NullFilter', 'NumExprFilter', 'PercentileFilter', diff --git a/zipline/pipeline/filters/filter.py b/zipline/pipeline/filters/filter.py index c184e0d6..4d261b32 100644 --- a/zipline/pipeline/filters/filter.py +++ b/zipline/pipeline/filters/filter.py @@ -250,6 +250,30 @@ class NullFilter(SingleInputMixin, Filter): return is_missing(arrays[0], self.inputs[0].missing_value) +class NotNullFilter(SingleInputMixin, Filter): + """ + A Filter indicating whether input values are **not** missing from an input. + + Parameters + ---------- + factor : zipline.pipeline.Term + The factor to compare against its missing_value. + """ + window_length = 0 + + def __new__(cls, term): + return super(NotNullFilter, cls).__new__( + cls, + inputs=(term,), + ) + + def _compute(self, arrays, dates, assets, mask): + data = arrays[0] + if isinstance(data, LabelArray): + return ~data.is_missing() + return ~is_missing(arrays[0], self.inputs[0].missing_value) + + class PercentileFilter(SingleInputMixin, Filter): """ A Filter representing assets falling between percentile bounds of a Factor.