mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-26 13:18:31 +08:00
TEST: Add test for demean example.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
Tests for Factor terms.
|
||||
"""
|
||||
from itertools import product
|
||||
from six import iteritems
|
||||
from nose_parameterized import parameterized
|
||||
|
||||
from numpy import (
|
||||
@@ -32,6 +33,7 @@ from zipline.testing import (
|
||||
check_arrays,
|
||||
parameter_space,
|
||||
)
|
||||
from zipline.utils.functional import dzip_exact
|
||||
from zipline.utils.numpy_utils import (
|
||||
datetime64ns_dtype,
|
||||
float64_dtype,
|
||||
@@ -423,6 +425,91 @@ class FactorTestCase(BasePipelineTestCase):
|
||||
|
||||
check_arrays(float_result, datetime_result)
|
||||
|
||||
def test_normalizations_hand_computed(self):
|
||||
"""
|
||||
Test the hand-computed example in factor.demean.
|
||||
"""
|
||||
f = self.f
|
||||
m = Mask()
|
||||
c = C()
|
||||
|
||||
factor_data = array(
|
||||
[[1.0, 2.0, 3.0, 4.0],
|
||||
[1.5, 2.5, 3.5, 1.0],
|
||||
[2.0, 3.0, 4.0, 1.5],
|
||||
[2.5, 3.5, 1.0, 2.0]],
|
||||
)
|
||||
filter_data = array(
|
||||
[[False, True, True, True],
|
||||
[True, False, True, True],
|
||||
[True, True, False, True],
|
||||
[True, True, True, False]],
|
||||
dtype=bool,
|
||||
)
|
||||
classifier_data = array(
|
||||
[[1, 1, 2, 2],
|
||||
[1, 1, 2, 2],
|
||||
[1, 1, 2, 2],
|
||||
[1, 1, 2, 2]],
|
||||
dtype=int,
|
||||
)
|
||||
|
||||
terms = {
|
||||
'vanilla': f.demean(),
|
||||
'masked': f.demean(mask=m),
|
||||
'grouped': f.demean(groupby=c),
|
||||
'grouped_masked': f.demean(mask=m, groupby=c),
|
||||
}
|
||||
expected = {
|
||||
'vanilla': array(
|
||||
[[-1.500, -0.500, 0.500, 1.500],
|
||||
[-0.625, 0.375, 1.375, -1.125],
|
||||
[-0.625, 0.375, 1.375, -1.125],
|
||||
[0.250, 1.250, -1.250, -0.250]],
|
||||
),
|
||||
'masked': array(
|
||||
[[nan, -1.000, 0.000, 1.000],
|
||||
[-0.500, nan, 1.500, -1.000],
|
||||
[-0.166, 0.833, nan, -0.666],
|
||||
[0.166, 1.166, -1.333, nan]],
|
||||
),
|
||||
'grouped': array(
|
||||
[[-0.500, 0.500, -0.500, 0.500],
|
||||
[-0.500, 0.500, 1.250, -1.250],
|
||||
[-0.500, 0.500, 1.250, -1.250],
|
||||
[-0.500, 0.500, -0.500, 0.500]],
|
||||
),
|
||||
'grouped_masked': array(
|
||||
[[nan, 0.000, -0.500, 0.500],
|
||||
[0.000, nan, 1.250, -1.250],
|
||||
[-0.500, 0.500, nan, 0.000],
|
||||
[-0.500, 0.500, 0.000, nan]]
|
||||
)
|
||||
}
|
||||
|
||||
graph = TermGraph(terms)
|
||||
results = self.run_graph(
|
||||
graph,
|
||||
initial_workspace={
|
||||
f: factor_data,
|
||||
c: classifier_data,
|
||||
m: filter_data,
|
||||
},
|
||||
mask=self.build_mask(self.ones_mask(shape=factor_data.shape)),
|
||||
)
|
||||
|
||||
for key, (res, exp) in iteritems(dzip_exact(results, expected)):
|
||||
check_allclose(
|
||||
res,
|
||||
exp,
|
||||
# The hand-computed values aren't very precise (in particular,
|
||||
# we truncate repeating decimals at 3 places) This is just
|
||||
# asserting that the example isn't misleading by being totally
|
||||
# wrong.
|
||||
atol=0.001,
|
||||
err_msg="Mismatch for %r" % key
|
||||
)
|
||||
|
||||
@parameter_space(
|
||||
seed_value=range(1, 2),
|
||||
normalizer_name_and_func=[
|
||||
@@ -431,10 +518,10 @@ class FactorTestCase(BasePipelineTestCase):
|
||||
],
|
||||
add_nulls_to_factor=(False, True,)
|
||||
)
|
||||
def test_normalizations(self,
|
||||
seed_value,
|
||||
normalizer_name_and_func,
|
||||
add_nulls_to_factor):
|
||||
def test_normalizations_randomized(self,
|
||||
seed_value,
|
||||
normalizer_name_and_func,
|
||||
add_nulls_to_factor):
|
||||
|
||||
name, func = normalizer_name_and_func
|
||||
|
||||
|
||||
@@ -568,8 +568,14 @@ def check_allclose(actual,
|
||||
"""
|
||||
if type(actual) != type(desired):
|
||||
raise AssertionError("%s != %s" % (type(actual), type(desired)))
|
||||
return assert_allclose(actual, desired, rtol=rtol, atol=atol,
|
||||
err_msg=err_msg, verbose=verbose)
|
||||
return assert_allclose(
|
||||
actual,
|
||||
desired,
|
||||
atol=atol,
|
||||
rtol=rtol,
|
||||
err_msg=err_msg,
|
||||
verbose=verbose,
|
||||
)
|
||||
|
||||
|
||||
def check_arrays(x, y, err_msg='', verbose=True):
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
from operator import methodcaller
|
||||
from six.moves import map
|
||||
from pprint import pformat
|
||||
|
||||
|
||||
def mapall(funcs, seq):
|
||||
"""
|
||||
Parameters
|
||||
@@ -20,3 +25,59 @@ def mapall(funcs, seq):
|
||||
for func in funcs:
|
||||
for elem in seq:
|
||||
yield func(elem)
|
||||
|
||||
|
||||
def same(*values):
|
||||
"""
|
||||
Check if all values in a sequence are equal.
|
||||
|
||||
Returns True on empty sequences.
|
||||
|
||||
Example
|
||||
-------
|
||||
>>> same(1, 1, 1, 1)
|
||||
True
|
||||
>>> same(1, 2, 1)
|
||||
False
|
||||
>>> same()
|
||||
True
|
||||
"""
|
||||
if not values:
|
||||
return True
|
||||
first, rest = values[0], values[1:]
|
||||
return all(value == first for value in rest)
|
||||
|
||||
|
||||
def _format_unequal_keys(dicts):
|
||||
return pformat([sorted(d.keys()) for d in dicts])
|
||||
|
||||
|
||||
def dzip_exact(*dicts):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
*dicts : iterable[dict]
|
||||
A sequence of dicts all sharing the same keys.
|
||||
|
||||
Returns
|
||||
-------
|
||||
zipped : dict
|
||||
A dict whose keys are the union of all keys in *dicts, and whose values
|
||||
are tuples of length len(dicts) containing the result of looking up
|
||||
each key in each dict.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If dicts don't all have the same keys.
|
||||
|
||||
Example
|
||||
-------
|
||||
>>> dzip_exact({'a': 1, 'b': 2}, {'a': 3, 'b': 4})
|
||||
{'a': (1, 3), 'b': (2, 4)}
|
||||
"""
|
||||
if not same(*map(methodcaller('viewkeys'), dicts)):
|
||||
raise ValueError(
|
||||
"dict keys not all equal:\n\n%s" % _format_unequal_keys(dicts)
|
||||
)
|
||||
return {k: tuple(d[k] for d in dicts) for k in dicts[0]}
|
||||
|
||||
@@ -321,6 +321,19 @@ def optional(type_):
|
||||
return (type_, type(None))
|
||||
|
||||
|
||||
def _expect_element(collection):
|
||||
template = (
|
||||
"%(funcname)s() expected a value in {collection} "
|
||||
"for argument '%(argname)s', but got %(actual)s instead."
|
||||
).format(collection=collection)
|
||||
return make_check(
|
||||
ValueError,
|
||||
template,
|
||||
complement(op.contains(collection)),
|
||||
repr,
|
||||
)
|
||||
|
||||
|
||||
def expect_element(*_pos, **named):
|
||||
"""
|
||||
Preprocessing decorator that verifies inputs are elements of some
|
||||
@@ -391,16 +404,3 @@ def coerce(from_, to, **to_kwargs):
|
||||
|
||||
|
||||
coerce_string = partial(coerce, string_types)
|
||||
|
||||
|
||||
def _expect_element(collection):
|
||||
template = (
|
||||
"%(funcname)s() expected a value in {collection} "
|
||||
"for argument '%(argname)s', but got %(actual)s instead."
|
||||
).format(collection=collection)
|
||||
return make_check(
|
||||
ValueError,
|
||||
template,
|
||||
complement(op.contains(collection)),
|
||||
repr,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user