numerai-tools 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- numerai_tools-0.0.1/LICENSE +21 -0
- numerai_tools-0.0.1/PKG-INFO +21 -0
- numerai_tools-0.0.1/README.md +2 -0
- numerai_tools-0.0.1/numerai_tools/__init__.py +0 -0
- numerai_tools-0.0.1/numerai_tools/scoring.py +228 -0
- numerai_tools-0.0.1/numerai_tools.egg-info/PKG-INFO +21 -0
- numerai_tools-0.0.1/numerai_tools.egg-info/SOURCES.txt +11 -0
- numerai_tools-0.0.1/numerai_tools.egg-info/dependency_links.txt +1 -0
- numerai_tools-0.0.1/numerai_tools.egg-info/requires.txt +4 -0
- numerai_tools-0.0.1/numerai_tools.egg-info/top_level.txt +1 -0
- numerai_tools-0.0.1/setup.cfg +4 -0
- numerai_tools-0.0.1/setup.py +45 -0
- numerai_tools-0.0.1/tests/test_scoring.py +142 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Numerai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: numerai_tools
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A collection of open-source tools to help interact with Numerai, model data, and automate submissions.
|
|
5
|
+
Home-page: https://github.com/numerai/numerai-tools
|
|
6
|
+
Maintainer: Numerai
|
|
7
|
+
Maintainer-email: support@numer.ai
|
|
8
|
+
License: MIT License
|
|
9
|
+
Description: # numerai-tools
|
|
10
|
+
A collection of open-source tools to help interact with Numerai, model data, and automate submissions.
|
|
11
|
+
|
|
12
|
+
Platform: OS Independent
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
File without changes
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
from typing import List, Tuple, Union
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from scipy import stats
|
|
6
|
+
from sklearn.preprocessing import OneHotEncoder
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# this is primarily used b/c round 326 had too many stocks,
|
|
10
|
+
# so we need to filter out the unnecessary ids here just in case
|
|
11
|
+
# it's also just convenient way to ensure everything is sorted/matching
|
|
12
|
+
def filter_sort_index(
|
|
13
|
+
s1: Union[pd.DataFrame, pd.Series],
|
|
14
|
+
s2: Union[pd.DataFrame, pd.Series],
|
|
15
|
+
max_filtered_ratio: float = 0.2,
|
|
16
|
+
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
|
17
|
+
ids = s1.dropna().index.intersection(s2.dropna().index)
|
|
18
|
+
# ensure we didn't filter too many ids
|
|
19
|
+
assert len(ids) / len(s1) >= (1 - max_filtered_ratio)
|
|
20
|
+
assert len(ids) / len(s2) >= (1 - max_filtered_ratio)
|
|
21
|
+
return s1.loc[ids].sort_index(), s2.loc[ids].sort_index()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def rank(df: pd.DataFrame, method: str = 'average') -> pd.DataFrame:
|
|
25
|
+
"""Percentile rank each column of a pandas DataFrame, centering values around 0.5
|
|
26
|
+
|
|
27
|
+
Arguments:
|
|
28
|
+
df: pd.DataFrame - the data to rank
|
|
29
|
+
method: str - the pandas ranking method to use, options:
|
|
30
|
+
'average' (default) - keeps ties
|
|
31
|
+
'first' - breaks ties by index
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
pd.DataFrame - the ranked DataFrame
|
|
35
|
+
"""
|
|
36
|
+
assert np.array_equal(df.index.sort_values(), df.index), "unsorted index found"
|
|
37
|
+
return df.apply(
|
|
38
|
+
lambda series: (series.rank(method=method).values - 0.5) / series.count()
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def tie_broken_rank(df: pd.DataFrame) -> pd.DataFrame:
|
|
43
|
+
# rank columns, breaking ties by index
|
|
44
|
+
return rank(df, "first")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def tie_kept_rank(df: pd.DataFrame) -> pd.DataFrame:
|
|
48
|
+
# rank columns, but keep ties
|
|
49
|
+
return rank(df, "average")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def min_max_normalize(s: pd.Series) -> pd.Series:
|
|
53
|
+
# scale a series to be between 0 and 1
|
|
54
|
+
return (s - s.min()) / (s.max() - s.min())
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def validate_indices(live_targets: pd.Series, predictions: pd.Series) -> None:
|
|
58
|
+
# ensure the ids are equivalent and sorted
|
|
59
|
+
assert np.array_equal(predictions.index, live_targets.index.sort_values())
|
|
60
|
+
assert np.array_equal(live_targets.index, live_targets.index.sort_values())
|
|
61
|
+
assert np.array_equal(predictions.index, predictions.index.sort_values())
|
|
62
|
+
# ensure no nans
|
|
63
|
+
assert not predictions.isna().any()
|
|
64
|
+
assert not live_targets.isna().any()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def correlation(live_targets: pd.Series, predictions: pd.Series) -> float:
|
|
68
|
+
validate_indices(live_targets, predictions)
|
|
69
|
+
# calculate correlation coefficient
|
|
70
|
+
return np.corrcoef(live_targets, predictions)[0, 1]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def tie_broken_rank_correlation(
|
|
74
|
+
live_targets: pd.Series, predictions: pd.Series
|
|
75
|
+
) -> float:
|
|
76
|
+
# percentile rank the predictions and get the correlation with live_targets
|
|
77
|
+
ranked_predictions = tie_broken_rank(predictions.to_frame())[predictions.name]
|
|
78
|
+
return correlation(live_targets, ranked_predictions)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def spearman_correlation(live_targets: pd.Series, predictions: pd.Series) -> float:
|
|
82
|
+
validate_indices(live_targets, predictions)
|
|
83
|
+
# calculate corr
|
|
84
|
+
return live_targets.corr(predictions, method="spearman")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def pearson_correlation(live_targets: pd.Series, predictions: pd.Series) -> float:
|
|
88
|
+
validate_indices(live_targets, predictions)
|
|
89
|
+
# calculate corr
|
|
90
|
+
return live_targets.corr(predictions, method="pearson")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def power(df: pd.DataFrame, p: float) -> pd.DataFrame:
|
|
94
|
+
"""Raise given predictions series to the given power.
|
|
95
|
+
|
|
96
|
+
Arguments:
|
|
97
|
+
df: pd.DataFrame - the data to raise to the given power
|
|
98
|
+
p: float - the power to which we exponentiate the data
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
pd.DataFrame - the predictions raised to the given power,
|
|
102
|
+
each column should be at least 90% correlated with the original data
|
|
103
|
+
"""
|
|
104
|
+
assert not df.isna().any().any(), "Data contains NaNs"
|
|
105
|
+
assert np.array_equal(df.index.sort_values(), df.index), "Index is not sorted"
|
|
106
|
+
result = np.sign(df) * np.abs(df) ** p
|
|
107
|
+
assert ((result.std() == 0) | (result.corrwith(df) >= 0.9)).all()
|
|
108
|
+
return result
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def gaussian(df: pd.DataFrame) -> pd.DataFrame:
|
|
112
|
+
"""Gaussianize each column of a pandas DataFrame using a normal percent point func
|
|
113
|
+
|
|
114
|
+
Arguments:
|
|
115
|
+
df: pd.DataFrame - the data to gaussianize
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
pd.DataFrame - the gaussianized data
|
|
119
|
+
"""
|
|
120
|
+
assert np.array_equal(df.index.sort_values(), df.index)
|
|
121
|
+
return df.apply(lambda series: stats.norm.ppf(series))
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def neutralize(
|
|
125
|
+
df: pd.DataFrame, neutralizers: np.ndarray, proportion: float = 1.0
|
|
126
|
+
) -> pd.DataFrame:
|
|
127
|
+
"""Neutralize each column of a given DataFrame by each feature in a given
|
|
128
|
+
neutralizers DataFrame.
|
|
129
|
+
|
|
130
|
+
Arguments:
|
|
131
|
+
df: pd.DataFrame - the data with columns to neutralize
|
|
132
|
+
neutralizers: pd.DataFrame - the neutralizer data with features as columns
|
|
133
|
+
proportion: float - the degree to which neutralization occurs
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
pd.DataFrame - the neutralized data
|
|
137
|
+
|
|
138
|
+
"""
|
|
139
|
+
assert not neutralizers.isna().any().any(), "Neutralizers contain NaNs"
|
|
140
|
+
assert len(df.index) == len(neutralizers.index), "Indices don't match"
|
|
141
|
+
assert (df.index == neutralizers.index).all(), "Indices don't match"
|
|
142
|
+
df[df.columns[df.std() == 0]] = np.nan
|
|
143
|
+
df_arr = df.values
|
|
144
|
+
neutralizer_arr = neutralizers.values
|
|
145
|
+
inverse_neutralizers = np.linalg.pinv(neutralizer_arr, rcond=1e-6)
|
|
146
|
+
adjustments = proportion * neutralizer_arr.dot(inverse_neutralizers.dot(df_arr))
|
|
147
|
+
neutral = df_arr - adjustments
|
|
148
|
+
neutral /= np.std(neutral, axis=0)
|
|
149
|
+
return pd.DataFrame(neutral, index=df.index, columns=df.columns)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def one_hot_encode(
|
|
153
|
+
df: pd.DataFrame, columns: List[str], dtype: type = np.float64
|
|
154
|
+
) -> pd.DataFrame:
|
|
155
|
+
"""One-hot encodes specified columns in a pandas dataframe.
|
|
156
|
+
Each column i should have x_i discrete values (eg. categories, bucket values, etc.)
|
|
157
|
+
and will be converted to x_i columns that each have 0s for rows that don't have
|
|
158
|
+
the associated value and 1s for rows that do have that value.
|
|
159
|
+
|
|
160
|
+
Arguments:
|
|
161
|
+
df: pd.DataFrame - the data with columns to one-hot encode
|
|
162
|
+
columns: List[str] - list of columns names to replace w/ one-hot encoding
|
|
163
|
+
dtype: type = np.float64 - the target datatype for the resulting columns
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
pd.DataFrame - original data, but specified cols replaced w/ one-hot encoding
|
|
167
|
+
|
|
168
|
+
"""
|
|
169
|
+
for col in columns:
|
|
170
|
+
encoder = OneHotEncoder(dtype=dtype)
|
|
171
|
+
one_hot = encoder.fit_transform(df[[col]])
|
|
172
|
+
one_hot = pd.DataFrame(
|
|
173
|
+
one_hot.toarray(),
|
|
174
|
+
columns=encoder.get_feature_names(),
|
|
175
|
+
index=df.index,
|
|
176
|
+
)
|
|
177
|
+
df = df.join(one_hot).drop(columns=col)
|
|
178
|
+
return df
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def tie_kept_rank__gaussianize__pow_1_5(df: pd.DataFrame) -> pd.DataFrame:
|
|
182
|
+
"""Perform the 3 functions in order on the given pandas DataFrame.
|
|
183
|
+
Will tie-kept rank then gaussianize then exponentiate to the 1.5 power.
|
|
184
|
+
|
|
185
|
+
Arguments:
|
|
186
|
+
df: pd.DataFrame - the data to transform
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
pd.DataFrame - the resulting data after applying the 3 functions
|
|
190
|
+
|
|
191
|
+
"""
|
|
192
|
+
return power(gaussian(tie_kept_rank(df)), 1.5)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def tie_kept_rank__gaussianize__neutralize(
|
|
196
|
+
df: pd.DataFrame, neutralizers: pd.DataFrame
|
|
197
|
+
) -> pd.DataFrame:
|
|
198
|
+
"""Perform the 3 functions in order on the given pandas DataFrame.
|
|
199
|
+
Will tie-kept rank then gaussianize then neutralize the df to the neutralizers.
|
|
200
|
+
|
|
201
|
+
Arguments:
|
|
202
|
+
df: pd.DataFrame - the data to transform
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
pd.DataFrame - the resulting data after applying the 3 functions
|
|
206
|
+
"""
|
|
207
|
+
return neutralize(gaussian(tie_kept_rank(df)), neutralizers)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def numerai_corr(predictions: pd.DataFrame, targets: pd.Series) -> pd.Series:
|
|
211
|
+
"""Recenter the target on 0, filter and sort indices, apply tie_kept_rank__gaussianize__pow_1_5
|
|
212
|
+
to the predictions, raise the targets to the 1.5 power, then calculate the
|
|
213
|
+
pearson correlation between the predictions and targets.
|
|
214
|
+
|
|
215
|
+
Arguments:
|
|
216
|
+
predictions: pd.DataFrame - the predictions to evaluate
|
|
217
|
+
targets: pd.Series - the live targets to evaluate against
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
pd.Series - the resulting correlation scores for each column in predictions
|
|
221
|
+
|
|
222
|
+
"""
|
|
223
|
+
targets -= targets.mean()
|
|
224
|
+
targets, predictions = filter_sort_index(targets, predictions)
|
|
225
|
+
predictions = tie_kept_rank__gaussianize__pow_1_5(predictions)
|
|
226
|
+
targets = power(targets.to_frame(), 1.5)[targets.name]
|
|
227
|
+
scores = predictions.apply(lambda sub: pearson_correlation(targets, sub))
|
|
228
|
+
return scores
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: numerai-tools
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A collection of open-source tools to help interact with Numerai, model data, and automate submissions.
|
|
5
|
+
Home-page: https://github.com/numerai/numerai-tools
|
|
6
|
+
Maintainer: Numerai
|
|
7
|
+
Maintainer-email: support@numer.ai
|
|
8
|
+
License: MIT License
|
|
9
|
+
Description: # numerai-tools
|
|
10
|
+
A collection of open-source tools to help interact with Numerai, model data, and automate submissions.
|
|
11
|
+
|
|
12
|
+
Platform: OS Independent
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
setup.py
|
|
4
|
+
numerai_tools/__init__.py
|
|
5
|
+
numerai_tools/scoring.py
|
|
6
|
+
numerai_tools.egg-info/PKG-INFO
|
|
7
|
+
numerai_tools.egg-info/SOURCES.txt
|
|
8
|
+
numerai_tools.egg-info/dependency_links.txt
|
|
9
|
+
numerai_tools.egg-info/requires.txt
|
|
10
|
+
numerai_tools.egg-info/top_level.txt
|
|
11
|
+
tests/test_scoring.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
numerai_tools
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from setuptools import setup
|
|
2
|
+
from setuptools import find_packages
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
VERSION = '0.0.1'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def load(path):
|
|
9
|
+
return open(path, 'r').read()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 5 - Production/Stable",
|
|
14
|
+
"Environment :: Console",
|
|
15
|
+
"Intended Audience :: Science/Research",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Programming Language :: Python",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Topic :: Scientific/Engineering",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
setup(
|
|
26
|
+
name="numerai_tools",
|
|
27
|
+
version=VERSION,
|
|
28
|
+
maintainer="Numerai",
|
|
29
|
+
maintainer_email="support@numer.ai",
|
|
30
|
+
description="A collection of open-source tools to help interact with Numerai, model data, and automate submissions.",
|
|
31
|
+
long_description=load('README.md'),
|
|
32
|
+
long_description_content_type='text/markdown',
|
|
33
|
+
url='https://github.com/numerai/numerai-tools',
|
|
34
|
+
platforms="OS Independent",
|
|
35
|
+
classifiers=classifiers,
|
|
36
|
+
license='MIT License',
|
|
37
|
+
package_data={'numerai': ['LICENSE', 'README.md']},
|
|
38
|
+
packages=find_packages(exclude=['tests']),
|
|
39
|
+
install_requires=[
|
|
40
|
+
"pandas==1.2.4",
|
|
41
|
+
"numpy==1.20.3",
|
|
42
|
+
"scipy==1.2.1",
|
|
43
|
+
"sklearn==0.0",
|
|
44
|
+
],
|
|
45
|
+
)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
from numerai_tools.scoring import (
|
|
7
|
+
correlation,
|
|
8
|
+
tie_broken_rank_correlation,
|
|
9
|
+
spearman_correlation,
|
|
10
|
+
pearson_correlation,
|
|
11
|
+
tie_broken_rank,
|
|
12
|
+
tie_kept_rank,
|
|
13
|
+
gaussian,
|
|
14
|
+
neutralize,
|
|
15
|
+
one_hot_encode,
|
|
16
|
+
power,
|
|
17
|
+
tie_kept_rank__gaussianize__pow_1_5,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TestScoring(unittest.TestCase):
|
|
22
|
+
def setUp(self):
|
|
23
|
+
print(f'\n running {type(self).__name__}')
|
|
24
|
+
|
|
25
|
+
self.up = pd.Series(list(range(5))).rename('up')
|
|
26
|
+
self.down = pd.Series(list(reversed(range(5)))).rename('down')
|
|
27
|
+
self.up_down = pd.Series([1, 0, 1, 0, 1]).rename('up_down')
|
|
28
|
+
self.down_up = (1 - self.up_down).rename('down_up')
|
|
29
|
+
self.up_float = (self.up / self.up.max()).rename('up_float')
|
|
30
|
+
self.pos_neg = pd.Series([0, -0, 0.5, -0.5, 1.0, -1.0, 2.0, -2.0]).rename(
|
|
31
|
+
'pos_neg'
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def test_correlation(self):
|
|
35
|
+
assert np.isclose(correlation(self.up, self.up), 1)
|
|
36
|
+
assert np.isclose(correlation(self.up, self.down), -1)
|
|
37
|
+
assert np.isclose(correlation(self.up, self.up_down), 0)
|
|
38
|
+
assert np.isclose(correlation(self.up, self.down_up), 0)
|
|
39
|
+
|
|
40
|
+
def test_tie_broken_rank_correlation(self):
|
|
41
|
+
assert np.isclose(tie_broken_rank_correlation(self.up, self.up), 1)
|
|
42
|
+
assert np.isclose(tie_broken_rank_correlation(self.up, self.down), -1)
|
|
43
|
+
# tie_broken_rank_correlation ranks the submission not the targets
|
|
44
|
+
assert np.isclose(tie_broken_rank_correlation(self.up, self.up_down), 0.5)
|
|
45
|
+
assert np.isclose(tie_broken_rank_correlation(self.up, self.down_up), 0.5)
|
|
46
|
+
assert np.isclose(tie_broken_rank_correlation(self.up_down, self.up), 0)
|
|
47
|
+
assert np.isclose(tie_broken_rank_correlation(self.down_up, self.up), 0)
|
|
48
|
+
|
|
49
|
+
def test_spearman_correlation(self):
|
|
50
|
+
assert np.isclose(spearman_correlation(self.up, self.up), 1)
|
|
51
|
+
assert np.isclose(spearman_correlation(self.up, self.down), -1)
|
|
52
|
+
assert np.isclose(spearman_correlation(self.up, self.up_down), 0)
|
|
53
|
+
assert np.isclose(spearman_correlation(self.up, self.down_up), 0)
|
|
54
|
+
assert np.isclose(spearman_correlation(self.up_down, self.up), 0)
|
|
55
|
+
assert np.isclose(spearman_correlation(self.down_up, self.up), 0)
|
|
56
|
+
|
|
57
|
+
def test_pearson_correlation(self):
|
|
58
|
+
assert np.isclose(pearson_correlation(self.up, self.up), 1)
|
|
59
|
+
assert np.isclose(pearson_correlation(self.up, self.down), -1)
|
|
60
|
+
assert np.isclose(pearson_correlation(self.up, self.up_down), 0)
|
|
61
|
+
assert np.isclose(pearson_correlation(self.up, self.down_up), 0)
|
|
62
|
+
assert np.isclose(pearson_correlation(self.up_down, self.up), 0)
|
|
63
|
+
assert np.isclose(pearson_correlation(self.down_up, self.up), 0)
|
|
64
|
+
|
|
65
|
+
def test_tie_broken_rank(self):
|
|
66
|
+
assert np.isclose(
|
|
67
|
+
tie_broken_rank(self.up.to_frame()).T, [0.1, 0.3, 0.5, 0.7, 0.9]
|
|
68
|
+
).all()
|
|
69
|
+
assert np.isclose(
|
|
70
|
+
tie_broken_rank(self.up_down.to_frame()).T, [0.5, 0.1, 0.7, 0.3, 0.9]
|
|
71
|
+
).all()
|
|
72
|
+
|
|
73
|
+
def test_tie_kept_rank(self):
|
|
74
|
+
assert np.isclose(
|
|
75
|
+
tie_kept_rank(self.up.to_frame()).T, [0.1, 0.3, 0.5, 0.7, 0.9]
|
|
76
|
+
).all()
|
|
77
|
+
assert np.isclose(
|
|
78
|
+
tie_kept_rank(self.up_down.to_frame()).T, [0.7, 0.2, 0.7, 0.2, 0.7]
|
|
79
|
+
).all()
|
|
80
|
+
|
|
81
|
+
def test_gaussian(self):
|
|
82
|
+
assert np.isclose(
|
|
83
|
+
gaussian(self.up_float).values.T,
|
|
84
|
+
[-np.inf, -0.6744897501960817, 0, 0.6744897501960817, np.inf],
|
|
85
|
+
).all()
|
|
86
|
+
|
|
87
|
+
def test_neutralize(self):
|
|
88
|
+
reciprocal_std_dev = 1 / self.up_down.values.std()
|
|
89
|
+
assert np.isclose(
|
|
90
|
+
neutralize(self.up_down.to_frame(), self.down_up.to_frame()).values.T,
|
|
91
|
+
[reciprocal_std_dev, 0, reciprocal_std_dev, 0, reciprocal_std_dev],
|
|
92
|
+
).all()
|
|
93
|
+
# ensure it works for multiple submissions/neutralizers
|
|
94
|
+
assert np.isclose(
|
|
95
|
+
neutralize(
|
|
96
|
+
pd.concat([self.up_down, self.up_down], axis=1),
|
|
97
|
+
pd.concat([self.down_up, self.down_up], axis=1),
|
|
98
|
+
).values.T,
|
|
99
|
+
[
|
|
100
|
+
[reciprocal_std_dev, 0, reciprocal_std_dev, 0, reciprocal_std_dev],
|
|
101
|
+
[reciprocal_std_dev, 0, reciprocal_std_dev, 0, reciprocal_std_dev],
|
|
102
|
+
],
|
|
103
|
+
).all()
|
|
104
|
+
|
|
105
|
+
def test_one_hot_encode(self):
|
|
106
|
+
assert np.isclose(
|
|
107
|
+
one_hot_encode(self.up.to_frame(), ['up']).values.T,
|
|
108
|
+
[
|
|
109
|
+
[1.0, 0.0, 0.0, 0.0, 0.0],
|
|
110
|
+
[0.0, 1.0, 0.0, 0.0, 0.0],
|
|
111
|
+
[0.0, 0.0, 1.0, 0.0, 0.0],
|
|
112
|
+
[0.0, 0.0, 0.0, 1.0, 0.0],
|
|
113
|
+
[0.0, 0.0, 0.0, 0.0, 1.0],
|
|
114
|
+
],
|
|
115
|
+
).all()
|
|
116
|
+
|
|
117
|
+
def test_power(self):
|
|
118
|
+
assert np.isclose(
|
|
119
|
+
power(self.pos_neg.to_frame(), 1.5),
|
|
120
|
+
[
|
|
121
|
+
[0.0],
|
|
122
|
+
[0.0],
|
|
123
|
+
[0.3535533905932738],
|
|
124
|
+
[-0.3535533905932738],
|
|
125
|
+
[1.0000000000000000],
|
|
126
|
+
[-1.0000000000000000],
|
|
127
|
+
[2.8284271247461903],
|
|
128
|
+
[-2.8284271247461903],
|
|
129
|
+
],
|
|
130
|
+
).all()
|
|
131
|
+
|
|
132
|
+
def test_tie_kept_rank__gaussianize__pow_1_5(self):
|
|
133
|
+
assert np.isclose(
|
|
134
|
+
tie_kept_rank__gaussianize__pow_1_5(self.up_float.to_frame()),
|
|
135
|
+
[
|
|
136
|
+
[-1.4507885796854221],
|
|
137
|
+
[-0.3797472709071263],
|
|
138
|
+
[0.0000000000000000],
|
|
139
|
+
[0.3797472709071261],
|
|
140
|
+
[1.4507885796854221],
|
|
141
|
+
],
|
|
142
|
+
).all()
|