Skip to content

Commit ef5c259

Browse files
committed
API: Make Series.searchsorted return a scalar, when supplied a scalar
1 parent 0ab8eb2 commit ef5c259

File tree

6 files changed

+31
-17
lines changed

6 files changed

+31
-17
lines changed

doc/source/whatsnew/v0.24.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,7 @@ Other API Changes
10081008
- Slicing a single row of a DataFrame with multiple ExtensionArrays of the same type now preserves the dtype, rather than coercing to object (:issue:`22784`)
10091009
- :class:`DateOffset` attribute `_cacheable` and method `_should_cache` have been removed (:issue:`23118`)
10101010
- Comparing :class:`Timedelta` to be less or greater than unknown types now raises a ``TypeError`` instead of returning ``False`` (:issue:`20829`)
1011+
- :meth:`Series.searchsorted`, when supplied a scalar value to search for, now returns a scalar instead of an array (:issue:`xxxxx`).
10111012
- :meth:`Categorical.searchsorted`, when supplied a scalar value to search for, now returns a scalar instead of an array (:issue:`23466`).
10121013
- :meth:`Categorical.searchsorted` now raises a ``KeyError`` rather that a ``ValueError``, if a searched for key is not found in its categories (:issue:`23466`).
10131014
- :meth:`Index.hasnans` and :meth:`Series.hasnans` now always return a python boolean. Previously, a python or a numpy boolean could be returned, depending on circumstances (:issue:`23294`).

pandas/core/base.py

+12-6
Original file line numberDiff line numberDiff line change
@@ -1195,8 +1195,8 @@ def factorize(self, sort=False, na_sentinel=-1):
11951195
11961196
Parameters
11971197
----------
1198-
value : array_like
1199-
Values to insert into `self`.
1198+
value : scalar or array_like
1199+
Value(s) to insert into `self`.
12001200
side : {'left', 'right'}, optional
12011201
If 'left', the index of the first suitable location found is given.
12021202
If 'right', return the last such index. If there is no suitable
@@ -1207,8 +1207,14 @@ def factorize(self, sort=False, na_sentinel=-1):
12071207
12081208
Returns
12091209
-------
1210-
indices : array of ints
1211-
Array of insertion points with the same shape as `value`.
1210+
int or array of ints
1211+
A scalar or array of insertion points with the
1212+
same shape as `value`.
1213+
1214+
.. versionchanged :: 0.24.0
1215+
Ìf `value`is a scalar, an int is now always returned.
1216+
Previously, scalar inputs returned an 1-item array for
1217+
:class:`Series` and :class:`Categorical`.
12121218
12131219
See Also
12141220
--------
@@ -1229,7 +1235,7 @@ def factorize(self, sort=False, na_sentinel=-1):
12291235
dtype: int64
12301236
12311237
>>> x.searchsorted(4)
1232-
array([3])
1238+
3
12331239
12341240
>>> x.searchsorted([0, 4])
12351241
array([0, 3])
@@ -1246,7 +1252,7 @@ def factorize(self, sort=False, na_sentinel=-1):
12461252
Categories (4, object): [apple < bread < cheese < milk]
12471253
12481254
>>> x.searchsorted('bread')
1249-
array([1]) # Note: an array, not a scalar
1255+
1
12501256
12511257
>>> x.searchsorted(['bread'], side='right')
12521258
array([3])

pandas/core/series.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -2149,8 +2149,10 @@ def __rmatmul__(self, other):
21492149
def searchsorted(self, value, side='left', sorter=None):
21502150
if sorter is not None:
21512151
sorter = ensure_platform_int(sorter)
2152-
return self._values.searchsorted(Series(value)._values,
2153-
side=side, sorter=sorter)
2152+
result = self._values.searchsorted(Series(value)._values,
2153+
side=side, sorter=sorter)
2154+
2155+
return result[0] if is_scalar(value) else result
21542156

21552157
# -------------------------------------------------------------------
21562158
# Combination

pandas/tests/arrays/categorical/test_analytics.py

+2
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,11 @@ def test_searchsorted(self):
8686
# Searching for single item argument, side='left' (default)
8787
res_cat = c1.searchsorted('apple')
8888
assert res_cat == 2
89+
assert tm.is_scalar(res_cat)
8990

9091
res_ser = s1.searchsorted('apple')
9192
assert res_ser == 2
93+
assert tm.is_scalar(res_ser)
9294

9395
# Searching for single item array, side='left' (default)
9496
res_cat = c1.searchsorted(['bread'])

pandas/tests/series/test_analytics.py

+10-8
Original file line numberDiff line numberDiff line change
@@ -1398,17 +1398,19 @@ def test_numpy_repeat(self):
13981398
def test_searchsorted(self):
13991399
s = Series([1, 2, 3])
14001400

1401-
idx = s.searchsorted(1, side='left')
1402-
tm.assert_numpy_array_equal(idx, np.array([0], dtype=np.intp))
1401+
result = s.searchsorted(1, side='left')
1402+
assert tm.is_scalar(result)
1403+
assert result == 0
14031404

1404-
idx = s.searchsorted(1, side='right')
1405-
tm.assert_numpy_array_equal(idx, np.array([1], dtype=np.intp))
1405+
result = s.searchsorted(1, side='right')
1406+
assert tm.is_scalar(result)
1407+
assert result == 1
14061408

14071409
def test_searchsorted_numeric_dtypes_scalar(self):
14081410
s = Series([1, 2, 90, 1000, 3e9])
14091411
r = s.searchsorted(30)
1410-
e = 2
1411-
assert r == e
1412+
assert tm.is_scalar(r)
1413+
assert r == 2
14121414

14131415
r = s.searchsorted([30])
14141416
e = np.array([2], dtype=np.intp)
@@ -1424,8 +1426,8 @@ def test_search_sorted_datetime64_scalar(self):
14241426
s = Series(pd.date_range('20120101', periods=10, freq='2D'))
14251427
v = pd.Timestamp('20120102')
14261428
r = s.searchsorted(v)
1427-
e = 1
1428-
assert r == e
1429+
assert tm.is_scalar(r)
1430+
assert r == 1
14291431

14301432
def test_search_sorted_datetime64_list(self):
14311433
s = Series(pd.date_range('20120101', periods=10, freq='2D'))

pandas/util/testing.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
is_bool, is_categorical_dtype, is_datetime64_dtype, is_datetime64tz_dtype,
2828
is_datetimelike_v_numeric, is_datetimelike_v_object,
2929
is_extension_array_dtype, is_interval_dtype, is_list_like, is_number,
30-
is_period_dtype, is_sequence, is_timedelta64_dtype, needs_i8_conversion)
30+
is_period_dtype, is_scalar, is_sequence, is_timedelta64_dtype,
31+
needs_i8_conversion) # noqa
3132
from pandas.core.dtypes.missing import array_equivalent
3233

3334
import pandas as pd

0 commit comments

Comments
 (0)