Skip to content

Commit a00b098

Browse files
0x0L0x0L
0x0L
authored and
0x0L
committed
initial support for rank and ffill
1 parent 4caae2e commit a00b098

File tree

3 files changed

+61
-2
lines changed

3 files changed

+61
-2
lines changed

xarray/core/duck_array_ops.py

+6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
try:
2424
import bottleneck as bn
2525
has_bottleneck = True
26+
# monkeypatch numpy with rankdata
27+
np.rankdata = bn.rankdata
2628
except ImportError:
2729
# use numpy methods instead
2830
bn = np
@@ -240,6 +242,10 @@ def f(values, axis=None, skipna=None, **kwargs):
240242
cumsum = _create_nan_agg_method('cumsum', numeric_only=True, np_compat=True,
241243
no_bottleneck=True, keep_dims=True)
242244

245+
if has_bottleneck:
246+
rank = _create_nan_agg_method('rankdata', numeric_only=False, np_compat=True,
247+
no_bottleneck=False, keep_dims=True)
248+
243249
_fail_on_dask_array_input_skipna = partial(
244250
fail_on_dask_array_input,
245251
msg='%r with skipna=True is not yet implemented on dask arrays')

xarray/core/ops.py

+39-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
'move_std': 'std', 'move_min': 'min',
5252
'move_max': 'max', 'move_var': 'var',
5353
'move_argmin': 'argmin', 'move_argmax': 'argmax',
54-
'move_median': 'median'}
54+
'move_median': 'median'} #, 'move_rank': 'rank'}
5555
# TODO: wrap take, dot, sort
5656

5757

@@ -121,6 +121,31 @@
121121
"""
122122

123123

124+
_BN_RANK_DOC = """\
125+
Ranks the data, dealing with ties and NaNs appropriately.
126+
127+
Equal values are assigned a rank that is the average of the ranks that
128+
would have been otherwise assigned to all of the values within that set.
129+
Ranks begin at 1, not 0.
130+
131+
NaNs in the input array are returned as NaNs.
132+
133+
Parameters
134+
----------
135+
dim : str, optional
136+
Dimension(s) over which to apply `{name}`.
137+
axis : int, optional
138+
Axis over which to apply `{name}`. Either the 'dim'
139+
or the 'axis' argument must be supplied.
140+
141+
Returns
142+
-------
143+
ranked: {cls}
144+
New {cls} object with `{name}` applied to its data along the
145+
indicated dimension. The dtype is 'float64'.
146+
"""
147+
148+
124149
def fillna(data, other, join="left", dataset_join="left"):
125150
"""Fill missing values in this object with data from the other object.
126151
Follows normal broadcasting and alignment rules.
@@ -305,6 +330,17 @@ def inject_binary_ops(cls, inplace=False):
305330
cls._inplace_binary_op(get_op('i' + name)))
306331

307332

333+
def inject_bottleneck_rank(cls):
334+
name = 'rank'
335+
f = getattr(duck_array_ops, name)
336+
include_skipna = False
337+
numeric_only = getattr(f, 'numeric_only', False)
338+
func = cls._reduce_method(f, include_skipna, numeric_only)
339+
func.__name__ = name
340+
func.__doc__ = _BN_RANK_DOC.format(name=name, cls=cls.__name__)
341+
setattr(cls, name, func)
342+
343+
308344
def inject_all_ops_and_reduce_methods(cls, priority=50, array_only=True):
309345
# prioritize our operations over those of numpy.ndarray (priority=1)
310346
# and numpy.matrix (priority=10)
@@ -334,6 +370,8 @@ def inject_all_ops_and_reduce_methods(cls, priority=50, array_only=True):
334370

335371
inject_reduce_methods(cls)
336372
inject_cum_methods(cls)
373+
if has_bottleneck:
374+
inject_bottleneck_rank(cls)
337375

338376

339377
def inject_bottleneck_rolling_methods(cls):

xarray/tests/test_dataarray.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from xarray.tests import (
2020
TestCase, ReturnItem, source_ndarray, unittest, requires_dask,
2121
assert_identical, assert_equal, assert_allclose, assert_array_equal,
22-
raises_regex, requires_scipy)
22+
raises_regex, requires_scipy, requires_bottleneck)
2323

2424

2525
class TestDataArray(TestCase):
@@ -2976,6 +2976,21 @@ def test_sortby(self):
29762976
actual = da.sortby(['x', 'y'])
29772977
self.assertDataArrayEqual(actual, expected)
29782978

2979+
@requires_bottleneck
2980+
def test_rank(self):
2981+
# floats
2982+
ar = DataArray([[3, 4, np.nan, 1]])
2983+
expect_0 = DataArray([[1, 1, np.nan, 1]])
2984+
expect_1 = DataArray([[2, 3, np.nan, 1]])
2985+
self.assertDataArrayEqual(ar.rank('dim_0'), expect_0)
2986+
self.assertDataArrayEqual(ar.rank('dim_1'), expect_1)
2987+
# int
2988+
x = DataArray([3,2,1])
2989+
self.assertDataArrayEqual(x.rank('dim_0'), x)
2990+
# str
2991+
y = DataArray(['c', 'b', 'a'])
2992+
self.assertDataArrayEqual(y.rank('dim_0'), x)
2993+
29792994

29802995
@pytest.fixture(params=[1])
29812996
def da(request):

0 commit comments

Comments
 (0)