Skip to content

Commit 2d10e8b

Browse files
committed
Fix #22903 for lib.is_scalar missing PEP 3141 numbers
1 parent ce1f81f commit 2d10e8b

File tree

3 files changed

+45
-6
lines changed

3 files changed

+45
-6
lines changed

doc/source/whatsnew/v0.24.0.txt

+3
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ For situations where you need an ``ndarray`` of ``Interval`` objects, use
239239
np.asarray(idx)
240240
idx.values.astype(object)
241241

242+
242243
.. _whatsnew_0240.api.timezone_offset_parsing:
243244

244245
Parsing Datetime Strings with Timezone Offsets
@@ -900,3 +901,5 @@ Other
900901
- :meth:`DataFrame.nlargest` and :meth:`DataFrame.nsmallest` now returns the correct n values when keep != 'all' also when tied on the first columns (:issue:`22752`)
901902
- :meth:`~pandas.io.formats.style.Styler.bar` now also supports tablewise application (in addition to rowwise and columnwise) with ``axis=None`` and setting clipping range with ``vmin`` and ``vmax`` (:issue:`21548` and :issue:`21526`). ``NaN`` values are also handled properly.
902903
- Logical operations ``&, |, ^`` between :class:`Series` and :class:`Index` will no longer raise ``ValueError`` (:issue:`22092`)
904+
- Checking PEP 3141 numbers in :func:`~pandas.api.types.is_scalar` function returns ``True`` (:issue:`22903`)
905+
-

pandas/_libs/lib.pyx

+38-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# -*- coding: utf-8 -*-
22
from decimal import Decimal
3+
from fractions import Fraction
4+
from numbers import Number
5+
36
import sys
47

58
import cython
@@ -15,7 +18,6 @@ from cpython.datetime cimport (PyDateTime_Check, PyDate_Check,
1518
PyDateTime_IMPORT)
1619
PyDateTime_IMPORT
1720

18-
1921
import numpy as np
2022
cimport numpy as cnp
2123
from numpy cimport (ndarray, PyArray_NDIM, PyArray_GETITEM,
@@ -107,7 +109,10 @@ def memory_usage_of_objects(arr: object[:]) -> int64_t:
107109
def is_scalar(val: object) -> bint:
108110
"""
109111
Return True if given value is scalar.
110-
112+
113+
Parameters
114+
----------
115+
val : input argument of any type
111116
This includes:
112117
- numpy array scalar (e.g. np.int64)
113118
- Python builtin numerics
@@ -119,12 +124,42 @@ def is_scalar(val: object) -> bint:
119124
- instances of decimal.Decimal
120125
- Interval
121126
- DateOffset
127+
- Fraction
128+
- Number
122129

130+
Returns
131+
-------
132+
True if the given value is scalar, False otherwise.
133+
134+
Examples
135+
--------
136+
>>> dt = pd.datetime.datetime(2018, 10, 3)
137+
>>> pd.is_scalar(dt)
138+
True
139+
140+
>>> pd.api.types.is_scalar([2, 3])
141+
False
142+
143+
>>> pd.api.types.is_scalar({0:1, 2:3})
144+
False
145+
146+
>>> pd.api.types.is_scalar((0, 2))
147+
False
148+
149+
pandas supports PEP 3141 numbers:
150+
151+
>>> from fractions import Fraction
152+
>>> pd.api.types.is_scalar(Fraction(3, 5))
153+
True
154+
155+
>>> from numbers import Number
156+
>>> pd.api.types.is_scalar(Number())
157+
True
123158
"""
124159

125160
return (cnp.PyArray_IsAnyScalar(val)
126161
# As of numpy-1.9, PyArray_IsAnyScalar misses bytearrays on Py3.
127-
or isinstance(val, bytes)
162+
or isinstance(val,(bytes, Fraction, Number))
128163
# We differ from numpy (as of 1.10), which claims that None is
129164
# not scalar in np.isscalar().
130165
or val is None
@@ -136,7 +171,6 @@ def is_scalar(val: object) -> bint:
136171
or is_interval(val)
137172
or util.is_offset_object(val))
138173

139-
140174
def item_from_zerodim(val: object) -> object:
141175
"""
142176
If the value is a zerodim array, return the item it contains.

pandas/tests/dtypes/test_inference.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
import re
1111
from datetime import datetime, date, timedelta, time
1212
from decimal import Decimal
13+
from numbers import Number
14+
from fractions import Fraction
1315
import numpy as np
1416
import pytz
1517
import pytest
16-
1718
import pandas as pd
1819
from pandas._libs import tslib, lib, missing as libmissing
1920
from pandas import (Series, Index, DataFrame, Timedelta,
@@ -1120,6 +1121,8 @@ def test_is_scalar_builtin_scalars(self):
11201121
assert is_scalar(None)
11211122
assert is_scalar(True)
11221123
assert is_scalar(False)
1124+
assert is_scalar(Number())
1125+
assert is_scalar(Fraction())
11231126
assert is_scalar(0.)
11241127
assert is_scalar(np.nan)
11251128
assert is_scalar('foobar')
@@ -1184,7 +1187,6 @@ def test_is_scalar_pandas_containers(self):
11841187
assert not is_scalar(Index([]))
11851188
assert not is_scalar(Index([1]))
11861189

1187-
11881190
def test_datetimeindex_from_empty_datetime64_array():
11891191
for unit in ['ms', 'us', 'ns']:
11901192
idx = DatetimeIndex(np.array([], dtype='datetime64[%s]' % unit))

0 commit comments

Comments
 (0)