1"""basic inference routines"""
2
3from __future__ import annotations
4
5from collections import abc
6from numbers import Number
7import re
8from re import Pattern
9from typing import (
10 TYPE_CHECKING,
11 Any,
12 TypeGuard,
13)
14
15import numpy as np
16
17from pandas._libs import lib
18from pandas.util._decorators import set_module
19
20if TYPE_CHECKING:
21 from collections.abc import Hashable
22
23is_bool = lib.is_bool
24
25is_integer = lib.is_integer
26
27is_float = lib.is_float
28
29is_complex = lib.is_complex
30
31is_scalar = lib.is_scalar
32
33is_decimal = lib.is_decimal
34
35is_list_like = lib.is_list_like
36
37is_iterator = lib.is_iterator
38
39
40@set_module("pandas.api.types")
41def is_number(obj: object) -> TypeGuard[Number | np.number]:
42 """
43 Check if the object is a number.
44
45 Returns True when the object is a number, and False if is not.
46
47 Parameters
48 ----------
49 obj : any type
50 The object to check if is a number.
51
52 Returns
53 -------
54 bool
55 Whether `obj` is a number or not.
56
57 See Also
58 --------
59 api.types.is_integer: Checks a subgroup of numbers.
60
61 Examples
62 --------
63 >>> from pandas.api.types import is_number
64 >>> is_number(1)
65 True
66 >>> is_number(7.15)
67 True
68
69 Booleans are valid because they are int subclass.
70
71 >>> is_number(False)
72 True
73
74 >>> is_number("foo")
75 False
76 >>> is_number("5")
77 False
78 """
79 return isinstance(obj, (Number, np.number))
80
81
82def iterable_not_string(obj: object) -> bool:
83 """
84 Check if the object is an iterable but not a string.
85
86 Parameters
87 ----------
88 obj : The object to check.
89
90 Returns
91 -------
92 is_iter_not_string : bool
93 Whether `obj` is a non-string iterable.
94
95 Examples
96 --------
97 >>> iterable_not_string([1, 2, 3])
98 True
99 >>> iterable_not_string("foo")
100 False
101 >>> iterable_not_string(1)
102 False
103 """
104 return isinstance(obj, abc.Iterable) and not isinstance(obj, str)
105
106
107@set_module("pandas.api.types")
108def is_file_like(obj: object) -> bool:
109 """
110 Check if the object is a file-like object.
111
112 For objects to be considered file-like, they must have
113 a `read` and/or `write` method as an attribute.
114
115 Parameters
116 ----------
117 obj : object
118 The object to check for file-like properties.
119 This can be any Python object, and the function will
120 check if it has attributes typically associated with
121 file-like objects (e.g., `read`, `write`).
122
123 Returns
124 -------
125 bool
126 Whether `obj` has file-like properties.
127
128 See Also
129 --------
130 api.types.is_dict_like : Check if the object is dict-like.
131 api.types.is_hashable : Return True if hash(obj) will succeed, False otherwise.
132 api.types.is_named_tuple : Check if the object is a named tuple.
133 api.types.is_iterator : Check if the object is an iterator.
134
135 Examples
136 --------
137 >>> import io
138 >>> from pandas.api.types import is_file_like
139 >>> buffer = io.StringIO("data")
140 >>> is_file_like(buffer)
141 True
142 >>> is_file_like([1, 2, 3])
143 False
144 """
145 return bool(hasattr(obj, "read") or hasattr(obj, "write"))
146
147
148@set_module("pandas.api.types")
149def is_re(obj: object) -> TypeGuard[Pattern[Any]]:
150 """
151 Check if the object is a regex pattern instance.
152
153 This function tests whether ``obj`` is an instance of a compiled
154 regular expression pattern, as created by :func:`re.compile`.
155
156 Parameters
157 ----------
158 obj : object
159 The object to check for being a regex pattern. Typically,
160 this would be an object that you expect to be a compiled
161 pattern from the `re` module.
162
163 Returns
164 -------
165 bool
166 Whether `obj` is a regex pattern.
167
168 See Also
169 --------
170 api.types.is_float : Return True if given object is float.
171 api.types.is_iterator : Check if the object is an iterator.
172 api.types.is_integer : Return True if given object is integer.
173 api.types.is_re_compilable : Check if the object can be compiled
174 into a regex pattern instance.
175
176 Examples
177 --------
178 >>> from pandas.api.types import is_re
179 >>> import re
180 >>> is_re(re.compile(".*"))
181 True
182 >>> is_re("foo")
183 False
184 """
185 return isinstance(obj, Pattern)
186
187
188@set_module("pandas.api.types")
189def is_re_compilable(obj: object) -> bool:
190 """
191 Check if the object can be compiled into a regex pattern instance.
192
193 This function attempts to compile ``obj`` as a regular expression
194 using :func:`re.compile` and returns whether the compilation succeeds.
195
196 Parameters
197 ----------
198 obj : The object to check
199 The object to check if the object can be compiled into a regex pattern instance.
200
201 Returns
202 -------
203 bool
204 Whether `obj` can be compiled as a regex pattern.
205
206 See Also
207 --------
208 api.types.is_re : Check if the object is a regex pattern instance.
209
210 Examples
211 --------
212 >>> from pandas.api.types import is_re_compilable
213 >>> is_re_compilable(".*")
214 True
215 >>> is_re_compilable(1)
216 False
217 """
218 try:
219 re.compile(obj) # type: ignore[call-overload]
220 except TypeError:
221 return False
222 else:
223 return True
224
225
226@set_module("pandas.api.types")
227def is_array_like(obj: object) -> bool:
228 """
229 Check if the object is array-like.
230
231 For an object to be considered array-like, it must be list-like and
232 have a `dtype` attribute.
233
234 Parameters
235 ----------
236 obj : The object to check
237
238 Returns
239 -------
240 is_array_like : bool
241 Whether `obj` has array-like properties.
242
243 Examples
244 --------
245 >>> is_array_like(np.array([1, 2, 3]))
246 True
247 >>> is_array_like(pd.Series(["a", "b"]))
248 True
249 >>> is_array_like(pd.Index(["2016-01-01"]))
250 True
251 >>> is_array_like([1, 2, 3])
252 False
253 >>> is_array_like(("a", "b"))
254 False
255 """
256 return is_list_like(obj) and hasattr(obj, "dtype")
257
258
259def is_nested_list_like(obj: object) -> bool:
260 """
261 Check if the object is list-like, and that all of its elements
262 are also list-like.
263
264 Parameters
265 ----------
266 obj : The object to check
267
268 Returns
269 -------
270 is_list_like : bool
271 Whether `obj` has list-like properties.
272
273 Examples
274 --------
275 >>> is_nested_list_like([[1, 2, 3]])
276 True
277 >>> is_nested_list_like([{1, 2, 3}, {1, 2, 3}])
278 True
279 >>> is_nested_list_like(["foo"])
280 False
281 >>> is_nested_list_like([])
282 False
283 >>> is_nested_list_like([[1, 2, 3], 1])
284 False
285
286 Notes
287 -----
288 This won't reliably detect whether a consumable iterator (e. g.
289 a generator) is a nested-list-like without consuming the iterator.
290 To avoid consuming it, we always return False if the outer container
291 doesn't define `__len__`.
292
293 See Also
294 --------
295 is_list_like
296 """
297 return (
298 is_list_like(obj)
299 and hasattr(obj, "__len__")
300 # need PEP 724 to handle these typing errors
301 and len(obj) > 0 # pyright: ignore[reportArgumentType]
302 and all(is_list_like(item) for item in obj) # type: ignore[attr-defined]
303 )
304
305
306@set_module("pandas.api.types")
307def is_dict_like(obj: object) -> bool:
308 """
309 Check if the object is dict-like.
310
311 An object is considered dict-like if it has the ``__getitem__``,
312 ``keys``, and ``__contains__`` attributes but is not a type itself.
313
314 Parameters
315 ----------
316 obj : object
317 The object to check. This can be any Python object,
318 and the function will determine whether it
319 behaves like a dictionary.
320
321 Returns
322 -------
323 bool
324 Whether `obj` has dict-like properties.
325
326 See Also
327 --------
328 api.types.is_list_like : Check if the object is list-like.
329 api.types.is_file_like : Check if the object is a file-like.
330 api.types.is_named_tuple : Check if the object is a named tuple.
331
332 Examples
333 --------
334 >>> from pandas.api.types import is_dict_like
335 >>> is_dict_like({1: 2})
336 True
337 >>> is_dict_like([1, 2, 3])
338 False
339 >>> is_dict_like(dict)
340 False
341 >>> is_dict_like(dict())
342 True
343 """
344 dict_like_attrs = ("__getitem__", "keys", "__contains__")
345 return (
346 all(hasattr(obj, attr) for attr in dict_like_attrs)
347 # [GH 25196] exclude classes
348 and not isinstance(obj, type)
349 )
350
351
352@set_module("pandas.api.types")
353def is_named_tuple(obj: object) -> bool:
354 """
355 Check if the object is a named tuple.
356
357 A named tuple is a subclass of :class:`tuple` that has named fields,
358 as created by :func:`collections.namedtuple`.
359
360 Parameters
361 ----------
362 obj : object
363 The object that will be checked to determine
364 whether it is a named tuple.
365
366 Returns
367 -------
368 bool
369 Whether `obj` is a named tuple.
370
371 See Also
372 --------
373 api.types.is_dict_like: Check if the object is dict-like.
374 api.types.is_hashable: Return True if hash(obj)
375 will succeed, False otherwise.
376 api.types.is_categorical_dtype : Check if the dtype is categorical.
377
378 Examples
379 --------
380 >>> from collections import namedtuple
381 >>> from pandas.api.types import is_named_tuple
382 >>> Point = namedtuple("Point", ["x", "y"])
383 >>> p = Point(1, 2)
384 >>>
385 >>> is_named_tuple(p)
386 True
387 >>> is_named_tuple((1, 2))
388 False
389 """
390 return isinstance(obj, abc.Sequence) and hasattr(obj, "_fields")
391
392
393@set_module("pandas.api.types")
394def is_hashable(obj: object, allow_slice: bool = True) -> TypeGuard[Hashable]:
395 """
396 Return True if hash(obj) will succeed, False otherwise.
397
398 Some types will pass a test against collections.abc.Hashable but fail when
399 they are actually hashed with hash().
400
401 Distinguish between these and other types by trying the call to hash() and
402 seeing if they raise TypeError.
403
404 Parameters
405 ----------
406 obj : object
407 The object to check for hashability. Any Python object can be passed here.
408 allow_slice : bool
409 If True, return True if the object is hashable (including slices).
410 If False, return True if the object is hashable and not a slice.
411
412 Returns
413 -------
414 bool
415 True if object can be hashed (i.e., does not raise TypeError when
416 passed to hash()) and passes the slice check according to 'allow_slice'.
417 False otherwise (e.g., if object is mutable like a list or dictionary
418 or if allow_slice is False and object is a slice or contains a slice).
419
420 See Also
421 --------
422 api.types.is_float : Return True if given object is float.
423 api.types.is_iterator : Check if the object is an iterator.
424 api.types.is_list_like : Check if the object is list-like.
425 api.types.is_dict_like : Check if the object is dict-like.
426
427 Examples
428 --------
429 >>> import collections
430 >>> from pandas.api.types import is_hashable
431 >>> a = ([],)
432 >>> isinstance(a, collections.abc.Hashable)
433 True
434 >>> is_hashable(a)
435 False
436 """
437 # We can't use isinstance(obj, collections.abc.Hashable) because it
438 # returns True for tuples containing unhashable elements (e.g. ([],)).
439 # Calling hash() is the only reliable check.
440 if allow_slice is False:
441 if isinstance(obj, tuple) and any(isinstance(v, slice) for v in obj):
442 return False
443 elif isinstance(obj, slice):
444 return False
445
446 try:
447 hash(obj)
448 except TypeError:
449 return False
450 else:
451 return True
452
453
454def is_sequence(obj: object) -> bool:
455 """
456 Check if the object is a sequence of objects.
457 String types are not included as sequences here.
458
459 Parameters
460 ----------
461 obj : The object to check
462
463 Returns
464 -------
465 is_sequence : bool
466 Whether `obj` is a sequence of objects.
467
468 Examples
469 --------
470 >>> l = [1, 2, 3]
471 >>>
472 >>> is_sequence(l)
473 True
474 >>> is_sequence(iter(l))
475 False
476 """
477 try:
478 # Can iterate over it.
479 iter(obj) # type: ignore[call-overload]
480 # Has a length associated with it.
481 len(obj) # type: ignore[arg-type]
482 return not isinstance(obj, (str, bytes))
483 except (TypeError, AttributeError):
484 return False
485
486
487def is_dataclass(item: object) -> bool:
488 """
489 Checks if the object is a data-class instance
490
491 Parameters
492 ----------
493 item : object
494
495 Returns
496 --------
497 is_dataclass : bool
498 True if the item is an instance of a data-class,
499 will return false if you pass the data class itself
500
501 Examples
502 --------
503 >>> from dataclasses import dataclass
504 >>> @dataclass
505 ... class Point:
506 ... x: int
507 ... y: int
508
509 >>> is_dataclass(Point)
510 False
511 >>> is_dataclass(Point(0, 2))
512 True
513
514 """
515 try:
516 import dataclasses
517
518 return dataclasses.is_dataclass(item) and not isinstance(item, type)
519 except ImportError:
520 return False