mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-12 20:41:12 +08:00
TST: Add tests for winsorize factor
This commit is contained in:
+169
-10
@@ -24,7 +24,7 @@ from numpy.random import randn, seed
|
||||
import pandas as pd
|
||||
from scipy.stats.mstats import winsorize as scipy_winsorize
|
||||
|
||||
from zipline.errors import UnknownRankMethod
|
||||
from zipline.errors import BadPercentileBounds, 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
|
||||
@@ -710,12 +710,157 @@ class FactorTestCase(BasePipelineTestCase):
|
||||
check=partial(check_allclose, atol=0.001),
|
||||
)
|
||||
|
||||
def test_winsorize_hand_computed(self):
|
||||
"""
|
||||
Test the hand-computed example in factor.winsorize.
|
||||
"""
|
||||
f = self.f
|
||||
m = Mask()
|
||||
c = C()
|
||||
str_c = C(dtype=categorical_dtype, missing_value=None)
|
||||
|
||||
factor_data = array([
|
||||
[1., 2., 3., 4., 5., 6.],
|
||||
[1., 8., 27., 64., 125., 216.],
|
||||
[6., 5., 4., 3., 2., 1.]
|
||||
])
|
||||
filter_data = array(
|
||||
[[False, True, True, True, True, True],
|
||||
[True, False, True, True, True, True],
|
||||
[True, True, False, True, True, True]],
|
||||
dtype=bool,
|
||||
)
|
||||
classifier_data = array(
|
||||
[[1, 1, 1, 2, 2, 2],
|
||||
[1, 1, 1, 2, 2, 2],
|
||||
[1, 1, 1, 2, 2, 2]],
|
||||
dtype=int64_dtype,
|
||||
)
|
||||
string_classifier_data = LabelArray(
|
||||
classifier_data.astype(str).astype(object),
|
||||
missing_value=None,
|
||||
)
|
||||
|
||||
terms = {
|
||||
'winsor_1': f.winsorize(
|
||||
min_percentile=0.33,
|
||||
max_percentile=0.67
|
||||
),
|
||||
'winsor_2': f.winsorize(
|
||||
min_percentile=0.49,
|
||||
max_percentile=1
|
||||
),
|
||||
'winsor_3': f.winsorize(
|
||||
min_percentile=0,
|
||||
max_percentile=.67
|
||||
),
|
||||
'masked': f.winsorize(
|
||||
min_percentile=0.33,
|
||||
max_percentile=0.67,
|
||||
mask=m
|
||||
),
|
||||
'grouped': f.winsorize(
|
||||
min_percentile=0.34,
|
||||
max_percentile=0.66,
|
||||
groupby=c
|
||||
),
|
||||
'grouped_str': f.winsorize(
|
||||
min_percentile=0.34,
|
||||
max_percentile=0.66,
|
||||
groupby=str_c
|
||||
),
|
||||
'grouped_masked': f.winsorize(
|
||||
min_percentile=0.34,
|
||||
max_percentile=0.66,
|
||||
mask=m,
|
||||
groupby=c
|
||||
),
|
||||
'grouped_masked_str': f.winsorize(
|
||||
min_percentile=0.34,
|
||||
max_percentile=0.66,
|
||||
mask=m,
|
||||
groupby=str_c
|
||||
),
|
||||
}
|
||||
expected = {
|
||||
'winsor_1': array([
|
||||
[2., 2., 3., 4., 5., 5.],
|
||||
[8., 8., 27., 64., 125., 125.],
|
||||
[5., 5., 4., 3., 2., 2.]
|
||||
]),
|
||||
'winsor_2': array([
|
||||
[3.0, 3., 3., 4., 5., 6.],
|
||||
[27., 27., 27., 64., 125., 216.],
|
||||
[6.0, 5., 4., 3., 3., 3.]
|
||||
]),
|
||||
'winsor_3': array([
|
||||
[1., 2., 3., 4., 5., 5.],
|
||||
[1., 8., 27., 64., 125., 125.],
|
||||
[5., 5., 4., 3., 2., 1.]
|
||||
]),
|
||||
'masked': array([
|
||||
[nan, 3., 3., 4., 5., 5.],
|
||||
[27., nan, 27., 64., 125., 125.],
|
||||
[5.0, 5., nan, 3., 2., 2.]
|
||||
]),
|
||||
'grouped': array([
|
||||
[2., 2., 2., 5., 5., 5.],
|
||||
[8., 8., 8., 125., 125., 125.],
|
||||
[5., 5., 5., 2., 2., 2.]
|
||||
]),
|
||||
'grouped_masked': array([
|
||||
[nan, 2., 3., 5., 5., 5.],
|
||||
[1.0, nan, 27., 125., 125., 125.],
|
||||
[6.0, 5., nan, 2., 2., 2.]
|
||||
]),
|
||||
}
|
||||
# Changing the classifier dtype shouldn't affect anything.
|
||||
expected['grouped_str'] = expected['grouped']
|
||||
expected['grouped_masked_str'] = expected['grouped_masked']
|
||||
|
||||
self.check_terms(
|
||||
terms,
|
||||
expected,
|
||||
initial_workspace={
|
||||
f: factor_data,
|
||||
c: classifier_data,
|
||||
str_c: string_classifier_data,
|
||||
m: filter_data,
|
||||
},
|
||||
mask=self.build_mask(self.ones_mask(shape=factor_data.shape)),
|
||||
check=partial(check_allclose, atol=0.001),
|
||||
)
|
||||
|
||||
def test_winsorize_bad_bounds(self):
|
||||
"""
|
||||
Test out of bounds input for factor.winsorize.
|
||||
"""
|
||||
f = self.f
|
||||
|
||||
bad_percentiles = [
|
||||
(-.1, 1),
|
||||
(0, 95),
|
||||
(5, 95),
|
||||
(5, 5),
|
||||
(.6, .4)
|
||||
]
|
||||
for min_, max_ in bad_percentiles:
|
||||
with self.assertRaises(BadPercentileBounds):
|
||||
f.winsorize(min_percentile=min_, max_percentile=max_)
|
||||
|
||||
@parameter_space(
|
||||
seed_value=range(1, 2),
|
||||
normalizer_name_and_func=[
|
||||
('demean', lambda row: row - nanmean(row)),
|
||||
('zscore', lambda row: (row - nanmean(row)) / nanstd(row)),
|
||||
('winsorize', lambda row: scipy_winsorize(row, limits=0.05)),
|
||||
('demean', {}, lambda row: row - nanmean(row)),
|
||||
('zscore', {}, lambda row: (row - nanmean(row)) / nanstd(row)),
|
||||
(
|
||||
'winsorize',
|
||||
{"min_percentile": 0.25, "max_percentile": 0.75},
|
||||
lambda row: scipy_winsorize(
|
||||
row,
|
||||
limits=0.25,
|
||||
)
|
||||
),
|
||||
],
|
||||
add_nulls_to_factor=(False, True,),
|
||||
)
|
||||
@@ -724,9 +869,9 @@ class FactorTestCase(BasePipelineTestCase):
|
||||
normalizer_name_and_func,
|
||||
add_nulls_to_factor):
|
||||
|
||||
name, func = normalizer_name_and_func
|
||||
name, kwargs, func = normalizer_name_and_func
|
||||
|
||||
shape = (7, 7)
|
||||
shape = (20, 20)
|
||||
|
||||
# All Trues.
|
||||
nomask = self.ones_mask(shape=shape)
|
||||
@@ -757,7 +902,7 @@ class FactorTestCase(BasePipelineTestCase):
|
||||
c = C()
|
||||
c_with_nulls = OtherC()
|
||||
m = Mask()
|
||||
method = getattr(f, name)
|
||||
method = partial(getattr(f, name), **kwargs)
|
||||
terms = {
|
||||
'vanilla': method(),
|
||||
'masked': method(mask=m),
|
||||
@@ -1054,7 +1199,7 @@ class ShortReprTestCase(TestCase):
|
||||
self.assertEqual(r, "GroupedRowTransform('zscore')")
|
||||
|
||||
def test_winsorize(self):
|
||||
r = F().winsorize().short_repr()
|
||||
r = F().winsorize(min_percentile=.05, max_percentile=.95).short_repr()
|
||||
self.assertEqual(r, "GroupedRowTransform('winsorize')")
|
||||
|
||||
|
||||
@@ -1068,8 +1213,22 @@ class TestWindowSafety(TestCase):
|
||||
self.assertFalse(F(window_safe=False).demean().window_safe)
|
||||
self.assertTrue(F(window_safe=True).demean().window_safe)
|
||||
|
||||
def test_winsorize_is_window_safe(self):
|
||||
self.assertTrue(F().winsorize().window_safe)
|
||||
def test_winsorize_is_window_safe_if_input_is_window_safe(self):
|
||||
self.assertFalse(
|
||||
F().winsorize(min_percentile=.05, max_percentile=.95).window_safe
|
||||
)
|
||||
self.assertFalse(
|
||||
F(window_safe=False).winsorize(
|
||||
min_percentile=.05,
|
||||
max_percentile=.95
|
||||
).window_safe
|
||||
)
|
||||
self.assertTrue(
|
||||
F(window_safe=True).winsorize(
|
||||
min_percentile=.05,
|
||||
max_percentile=.95
|
||||
).window_safe
|
||||
)
|
||||
|
||||
|
||||
class TestPostProcessAndToWorkSpaceValue(ZiplineTestCase):
|
||||
|
||||
Reference in New Issue
Block a user