Skip to content

Commit 4001a5d

Browse files
Bug fix for Rank and WMA operators (#1228)
* bug fix: 1) 100 should be used to scale down percentileofscore return to 0-1, not length of array; 2) for (linear) weighted MA(n), weight should be n, n-1, ..., 1 instead of n-1, ..., 0 * use native pandas fucntion for rank * remove useless import * require pandas 1.4+ * rank for py37+pandas 1.3.5 compatibility * lint improvement * lint black fix * use hasattr instead of version to check whether rolling.rank is implemented
1 parent ff2154c commit 4001a5d

File tree

1 file changed

+9
-15
lines changed

1 file changed

+9
-15
lines changed

qlib/data/ops.py

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@
3434

3535

3636
#################### Element-Wise Operator ####################
37-
38-
3937
class ElemOperator(ExpressionOps):
4038
"""Element-wise Operator
4139
@@ -216,9 +214,7 @@ class Not(NpElemOperator):
216214
217215
Parameters
218216
----------
219-
feature_left : Expression
220-
feature instance
221-
feature_right : Expression
217+
feature : Expression
222218
feature instance
223219
224220
Returns
@@ -241,8 +237,6 @@ class PairOperator(ExpressionOps):
241237
feature instance or numeric value
242238
feature_right : Expression
243239
feature instance or numeric value
244-
func : str
245-
operator function
246240
247241
Returns
248242
----------
@@ -1155,23 +1149,23 @@ class Rank(Rolling):
11551149
def __init__(self, feature, N):
11561150
super(Rank, self).__init__(feature, N, "rank")
11571151

1152+
# for compatiblity of python 3.7, which doesn't support pandas 1.4.0+ which implements Rolling.rank
11581153
def _load_internal(self, instrument, start_index, end_index, *args):
11591154
series = self.feature.load(instrument, start_index, end_index, *args)
1160-
# TODO: implement in Cython
1155+
1156+
rolling_or_expending = series.expanding(min_periods=1) if self.N == 0 else series.rolling(self.N, min_periods=1)
1157+
if hasattr(rolling_or_expending, "rank"):
1158+
return rolling_or_expending.rank(pct=True)
11611159

11621160
def rank(x):
11631161
if np.isnan(x[-1]):
11641162
return np.nan
11651163
x1 = x[~np.isnan(x)]
11661164
if x1.shape[0] == 0:
11671165
return np.nan
1168-
return percentileofscore(x1, x1[-1]) / len(x1)
1166+
return percentileofscore(x1, x1[-1]) / 100
11691167

1170-
if self.N == 0:
1171-
series = series.expanding(min_periods=1).apply(rank, raw=True)
1172-
else:
1173-
series = series.rolling(self.N, min_periods=1).apply(rank, raw=True)
1174-
return series
1168+
return rolling_or_expending.apply(rank, raw=True)
11751169

11761170

11771171
class Count(Rolling):
@@ -1341,7 +1335,7 @@ def _load_internal(self, instrument, start_index, end_index, *args):
13411335
# TODO: implement in Cython
13421336

13431337
def weighted_mean(x):
1344-
w = np.arange(len(x))
1338+
w = np.arange(len(x)) + 1
13451339
w = w / w.sum()
13461340
return np.nanmean(w * x)
13471341

0 commit comments

Comments
 (0)