1from __future__ import annotations
2
3import operator
4from typing import (
5 TYPE_CHECKING,
6 Any,
7 Literal,
8 NoReturn,
9 cast,
10)
11import warnings
12
13import numpy as np
14
15from pandas._libs import lib
16from pandas._libs.missing import is_matching_na
17from pandas._libs.sparse import SparseIndex
18import pandas._libs.testing as _testing
19from pandas._libs.tslibs.np_datetime import compare_mismatched_resolutions
20from pandas.errors import Pandas4Warning
21from pandas.util._decorators import (
22 deprecate_kwarg,
23 set_module,
24)
25from pandas.util._exceptions import find_stack_level
26
27from pandas.core.dtypes.cast import construct_1d_object_array_from_listlike
28from pandas.core.dtypes.common import (
29 is_bool,
30 is_float_dtype,
31 is_integer_dtype,
32 is_number,
33 is_numeric_dtype,
34 needs_i8_conversion,
35)
36from pandas.core.dtypes.dtypes import (
37 CategoricalDtype,
38 DatetimeTZDtype,
39 ExtensionDtype,
40 NumpyEADtype,
41)
42from pandas.core.dtypes.missing import array_equivalent
43
44import pandas as pd
45from pandas import (
46 Categorical,
47 CategoricalIndex,
48 DataFrame,
49 DatetimeIndex,
50 Index,
51 IntervalDtype,
52 IntervalIndex,
53 MultiIndex,
54 PeriodIndex,
55 RangeIndex,
56 Series,
57 TimedeltaIndex,
58)
59from pandas.core.arrays import (
60 DatetimeArray,
61 ExtensionArray,
62 IntervalArray,
63 PeriodArray,
64 TimedeltaArray,
65)
66from pandas.core.arrays.datetimelike import DatetimeLikeArrayMixin
67from pandas.core.arrays.string_ import StringDtype
68from pandas.core.indexes.api import safe_sort_index
69
70from pandas.io.formats.printing import pprint_thing
71
72if TYPE_CHECKING:
73 from collections.abc import (
74 Container,
75 Iterable,
76 )
77
78 from pandas._typing import DtypeObj
79
80
81def assert_almost_equal(
82 left: Any,
83 right: Any,
84 check_dtype: bool | Literal["equiv"] = "equiv",
85 rtol: float = 1.0e-5,
86 atol: float = 1.0e-8,
87 **kwargs: Any,
88) -> None:
89 """
90 Check that the left and right objects are approximately equal.
91
92 By approximately equal, we refer to objects that are numbers or that
93 contain numbers which may be equivalent to specific levels of precision.
94
95 Parameters
96 ----------
97 left : object
98 right : object
99 check_dtype : bool or {'equiv'}, default 'equiv'
100 Check dtype if both a and b are the same type. If 'equiv' is passed in,
101 then `RangeIndex` and `Index` with int64 dtype are also considered
102 equivalent when doing type checking.
103 rtol : float, default 1e-5
104 Relative tolerance. Only applied to numeric dtypes; values of other
105 dtypes, such as interval, are always compared exactly.
106 atol : float, default 1e-8
107 Absolute tolerance. Only applied to numeric dtypes; values of other
108 dtypes, such as interval, are always compared exactly.
109 """
110 if isinstance(left, Index):
111 assert_index_equal(
112 left,
113 right,
114 check_exact=False,
115 exact=check_dtype,
116 rtol=rtol,
117 atol=atol,
118 **kwargs,
119 )
120
121 elif isinstance(left, Series):
122 assert_series_equal(
123 left,
124 right,
125 check_exact=False,
126 check_dtype=check_dtype,
127 rtol=rtol,
128 atol=atol,
129 **kwargs,
130 )
131
132 elif isinstance(left, DataFrame):
133 assert_frame_equal(
134 left,
135 right,
136 check_exact=False,
137 check_dtype=check_dtype,
138 rtol=rtol,
139 atol=atol,
140 **kwargs,
141 )
142
143 else:
144 # Other sequences.
145 if check_dtype:
146 if is_number(left) and is_number(right):
147 # Do not compare numeric classes, like np.float64 and float.
148 pass
149 elif is_bool(left) and is_bool(right):
150 # Do not compare bool classes, like np.bool_ and bool.
151 pass
152 else:
153 if isinstance(left, np.ndarray) or isinstance(right, np.ndarray):
154 obj = "numpy array"
155 else:
156 obj = "Input"
157 assert_class_equal(left, right, obj=obj)
158
159 # if we have "equiv", this becomes True
160 _testing.assert_almost_equal(
161 left, right, check_dtype=bool(check_dtype), rtol=rtol, atol=atol, **kwargs
162 )
163
164
165def _check_isinstance(left: Any, right: Any, cls: type) -> None:
166 """
167 Helper method for our assert_* methods that ensures that
168 the two objects being compared have the right type before
169 proceeding with the comparison.
170
171 Parameters
172 ----------
173 left : The first object being compared.
174 right : The second object being compared.
175 cls : The class type to check against.
176
177 Raises
178 ------
179 AssertionError : Either `left` or `right` is not an instance of `cls`.
180 """
181 cls_name = cls.__name__
182
183 if not isinstance(left, cls):
184 raise AssertionError(
185 f"{cls_name} Expected type {cls}, found {type(left)} instead"
186 )
187 if not isinstance(right, cls):
188 raise AssertionError(
189 f"{cls_name} Expected type {cls}, found {type(right)} instead"
190 )
191
192
193def assert_dict_equal(
194 left: dict[Any, Any], right: dict[Any, Any], compare_keys: bool = True
195) -> None:
196 _check_isinstance(left, right, dict)
197 _testing.assert_dict_equal(left, right, compare_keys=compare_keys)
198
199
200def _resolve_check_freq(
201 index: Index, check_freq: bool | lib.NoDefault
202) -> bool | lib.NoDefault:
203 """
204 Resolve an unspecified check_freq for a Series/DataFrame axis.
205
206 The hard freq check has long applied only to a flat DatetimeIndex or
207 TimedeltaIndex, so preserve that. Freqs nested inside a MultiIndex level or
208 inside Categorical categories were not checked before, so those go through
209 the deprecation warning in assert_index_equal. GH#51920, GH#66761
210 """
211 if check_freq is lib.no_default and isinstance(
212 index, (DatetimeIndex, TimedeltaIndex)
213 ):
214 return True
215 return check_freq
216
217
218@set_module("pandas.testing")
219def assert_index_equal(
220 left: Index,
221 right: Index,
222 exact: bool | str | lib.NoDefault = lib.no_default,
223 check_names: bool = True,
224 check_exact: bool = True,
225 check_categorical: bool = True,
226 check_order: bool = True,
227 rtol: float = 1.0e-5,
228 atol: float = 1.0e-8,
229 obj: str | None = None,
230 *,
231 check_freq: bool | lib.NoDefault = lib.no_default,
232) -> None:
233 """
234 Check that left and right Index are equal.
235
236 This function compares two Index objects and raises an ``AssertionError``
237 if they are not equal. It provides fine-grained control over which
238 attributes to check, including dtype, names, and categorical properties.
239
240 Parameters
241 ----------
242 left : Index
243 The first index to compare.
244 right : Index
245 The second index to compare.
246 exact : bool or {'equiv'}, default 'equiv'
247 Whether to check the Index class, dtype and inferred_type
248 are identical. If 'equiv', then RangeIndex can be substituted for
249 Index with an int64 dtype as well.
250
251 .. deprecated:: 3.1.0
252 The default value of 'equiv' has been deprecated and will be changed to
253 True in the future.
254
255 check_names : bool, default True
256 Whether to check the names attribute.
257 check_exact : bool, default True
258 Whether to compare number exactly.
259 check_categorical : bool, default True
260 Whether to compare internal Categorical exactly.
261 check_order : bool, default True
262 Whether to compare the order of index entries as well as their values.
263 If True, both indexes must contain the same elements, in the same order.
264 If False, both indexes must contain the same elements, but in any order.
265 rtol : float, default 1e-5
266 Relative tolerance. Only used when check_exact is False, and only
267 applied to numeric dtypes; values of other dtypes, such as interval,
268 are always compared exactly.
269 atol : float, default 1e-8
270 Absolute tolerance. Only used when check_exact is False, and only
271 applied to numeric dtypes; values of other dtypes, such as interval,
272 are always compared exactly.
273 obj : str, default 'Index' or 'MultiIndex'
274 Specify object name being compared, internally used to show appropriate
275 assertion message.
276 check_freq : bool, default True
277 Whether to check the ``freq`` attribute on a DatetimeIndex or
278 TimedeltaIndex. This check is skipped when ``check_order=False``,
279 as sorting does not preserve ``freq``.
280
281 .. versionadded:: 3.1.0
282
283 .. deprecated:: 3.1.0
284 Currently a ``freq`` mismatch only warns; in a future version
285 ``check_freq=True`` will be the default and mismatches will
286 raise. Pass ``check_freq`` explicitly to silence the warning.
287
288 See Also
289 --------
290 testing.assert_series_equal : Check that two Series are equal.
291 testing.assert_frame_equal : Check that two DataFrames are equal.
292
293 Examples
294 --------
295 >>> from pandas import testing as tm
296 >>> a = pd.Index([1, 2, 3])
297 >>> b = pd.Index([1, 2, 3])
298 >>> tm.assert_index_equal(a, b)
299 """
300 __tracebackhide__ = True
301
302 if obj is None:
303 obj = "MultiIndex" if isinstance(left, MultiIndex) else "Index"
304
305 def _check_rangeindex_index_int(left: Index, right: Index) -> bool:
306 return (
307 isinstance(left, RangeIndex)
308 and isinstance(right, Index)
309 and not isinstance(right, RangeIndex)
310 and is_integer_dtype(right.dtype)
311 ) or (
312 isinstance(right, RangeIndex)
313 and isinstance(left, Index)
314 and not isinstance(left, RangeIndex)
315 and is_integer_dtype(left.dtype)
316 )
317
318 if exact is lib.no_default:
319 if _check_rangeindex_index_int(left, right):
320 warnings.warn(
321 "The default value of 'equiv' for the `exact` parameter is deprecated "
322 "and will be changed to 'True' in a future version. Please set exact "
323 "to the desired value to avoid seeing this warning",
324 Pandas4Warning,
325 stacklevel=find_stack_level(),
326 )
327 exact = "equiv"
328
329 def _check_types(left: Index, right: Index, obj: str = "Index") -> None:
330 if not exact:
331 return
332
333 assert_class_equal(left, right, exact=exact, obj=obj)
334 assert_attr_equal("inferred_type", left, right, obj=obj)
335
336 # Skip exact dtype checking when `check_categorical` is False
337 if isinstance(left.dtype, CategoricalDtype) and isinstance(
338 right.dtype, CategoricalDtype
339 ):
340 if check_categorical:
341 assert_attr_equal("dtype", left, right, obj=obj)
342 assert_index_equal(
343 cast("CategoricalIndex", left).categories,
344 cast("CategoricalIndex", right).categories,
345 exact=exact,
346 check_freq=check_freq,
347 )
348 return
349
350 assert_attr_equal("dtype", left, right, obj=obj)
351
352 # instance validation
353 _check_isinstance(left, right, Index)
354
355 # class / dtype comparison
356 _check_types(left, right, obj=obj)
357
358 # level comparison
359 if left.nlevels != right.nlevels:
360 msg1 = f"{obj} levels are different"
361 msg2 = f"{left.nlevels}, {left}"
362 msg3 = f"{right.nlevels}, {right}"
363 raise_assert_detail(obj, msg1, msg2, msg3)
364
365 # length comparison
366 if len(left) != len(right):
367 msg1 = f"{obj} length are different"
368 msg2 = f"{len(left)}, {left}"
369 msg3 = f"{len(right)}, {right}"
370 raise_assert_detail(obj, msg1, msg2, msg3)
371
372 # If order doesn't matter then sort the index entries
373 if not check_order:
374 left = safe_sort_index(left)
375 right = safe_sort_index(right)
376
377 # MultiIndex special comparison for little-friendly error messages
378 if isinstance(left, MultiIndex):
379 right = cast("MultiIndex", right)
380
381 # A freq mismatch in the levels is retried below on get_level_values,
382 # which usually drops freq, so the levels comparison must not be what
383 # emits the check_freq deprecation warning: it would warn in cases the
384 # eventual check_freq=True default accepts. Resolve the sentinel to a
385 # hard check here and let the retry decide. GH#66761
386 levels_check_freq = True if check_freq is lib.no_default else check_freq
387
388 for level in range(left.nlevels):
389 lobj = f"{obj} level [{level}]"
390 try:
391 # try comparison on levels/codes to avoid densifying MultiIndex
392 assert_index_equal(
393 left.levels[level],
394 right.levels[level],
395 exact=exact,
396 check_names=check_names,
397 check_exact=check_exact,
398 check_categorical=check_categorical,
399 check_freq=levels_check_freq,
400 rtol=rtol,
401 atol=atol,
402 obj=lobj,
403 )
404 assert_numpy_array_equal(left.codes[level], right.codes[level])
405 except AssertionError:
406 llevel = left.get_level_values(level)
407 rlevel = right.get_level_values(level)
408
409 assert_index_equal(
410 llevel,
411 rlevel,
412 exact=exact,
413 check_names=check_names,
414 check_exact=check_exact,
415 check_categorical=check_categorical,
416 check_freq=check_freq,
417 rtol=rtol,
418 atol=atol,
419 obj=lobj,
420 )
421 # get_level_values may change dtype
422 _check_types(left.levels[level], right.levels[level], obj=lobj)
423
424 # skip exact index checking when `check_categorical` is False
425 elif check_exact and check_categorical:
426 if not left.equals(right):
427 # _values compare can raise TypeError (non-comparable
428 # categoricals (GH#61935)
429 try:
430 mismatch = left._values != right._values
431 except TypeError:
432 raise_assert_detail(
433 obj,
434 "types are not comparable (non-matching categorical categories)",
435 left,
436 right,
437 )
438
439 if not isinstance(mismatch, np.ndarray):
440 mismatch = cast("ExtensionArray", mismatch).fillna(True)
441
442 diff = np.sum(mismatch.astype(int)) * 100.0 / len(left)
443 msg = f"{obj} values are different ({np.round(diff, 5)} %)"
444 raise_assert_detail(obj, msg, left, right)
445 else:
446 # if we have "equiv", this becomes True
447 exact_bool = bool(exact)
448 _testing.assert_almost_equal(
449 left._values,
450 right._values,
451 rtol=rtol,
452 atol=atol,
453 check_dtype=exact_bool,
454 obj=obj,
455 lobj=left,
456 robj=right,
457 )
458
459 # metadata comparison
460 if check_names:
461 assert_attr_equal("names", left, right, obj=obj)
462 if isinstance(left, PeriodIndex) or isinstance(right, PeriodIndex):
463 assert_attr_equal("dtype", left, right, obj=obj)
464 if isinstance(left, IntervalIndex) or isinstance(right, IntervalIndex):
465 assert_interval_array_equal(
466 cast("IntervalArray", left._values),
467 cast("IntervalArray", right._values),
468 )
469
470 # freq is not preserved by sorting, so when check_order=False the freqs
471 # of the sorted copies are meaningless and the check is skipped
472 if (
473 check_order
474 and isinstance(left, (DatetimeIndex, TimedeltaIndex))
475 and isinstance(right, (DatetimeIndex, TimedeltaIndex))
476 ):
477 if check_freq is lib.no_default:
478 if left.freq != right.freq:
479 warnings.warn(
480 "assert_index_equal will check the 'freq' attribute by "
481 f"default in a future version; these freqs {left.freq!r} "
482 f"and {right.freq!r} do not match. Pass check_freq=True "
483 "or check_freq=False to silence this warning",
484 Pandas4Warning,
485 stacklevel=find_stack_level(),
486 )
487 elif check_freq:
488 assert_attr_equal("freq", left, right, obj=obj)
489
490 if check_categorical:
491 if isinstance(left.dtype, CategoricalDtype) or isinstance(
492 right.dtype, CategoricalDtype
493 ):
494 assert_categorical_equal(
495 cast("Categorical", left._values),
496 cast("Categorical", right._values),
497 check_freq=check_freq,
498 obj=f"{obj} category",
499 )
500
501
502def assert_class_equal(
503 left: Any, right: Any, exact: bool | str = True, obj: str = "Input"
504) -> None:
505 """
506 Checks classes are equal.
507 """
508 __tracebackhide__ = True
509
510 def repr_class(x: Any) -> Any:
511 if isinstance(x, Index):
512 # return Index as it is to include values in the error message
513 return x
514
515 return type(x).__name__
516
517 def is_class_equiv(idx: Index) -> bool:
518 """Classes that are a RangeIndex (sub-)instance or exactly an `Index` .
519
520 This only checks class equivalence. There is a separate check that the
521 dtype is int64.
522 """
523 return type(idx) is Index or isinstance(idx, RangeIndex)
524
525 if type(left) == type(right):
526 return
527
528 if exact == "equiv":
529 if is_class_equiv(left) and is_class_equiv(right):
530 return
531
532 msg = f"{obj} classes are different"
533 raise_assert_detail(obj, msg, repr_class(left), repr_class(right))
534
535
536def assert_attr_equal(
537 attr: str, left: Any, right: Any, obj: str = "Attributes"
538) -> None:
539 """
540 Check attributes are equal. Both objects must have attribute.
541
542 Parameters
543 ----------
544 attr : str
545 Attribute name being compared.
546 left : object
547 right : object
548 obj : str, default 'Attributes'
549 Specify object name being compared, internally used to show appropriate
550 assertion message
551 """
552 __tracebackhide__ = True
553
554 left_attr = getattr(left, attr)
555 right_attr = getattr(right, attr)
556
557 if left_attr is right_attr or is_matching_na(left_attr, right_attr):
558 # e.g. both np.nan, both NaT, both pd.NA, ...
559 return None
560
561 try:
562 result = left_attr == right_attr
563 except TypeError:
564 # datetimetz on rhs may raise TypeError
565 result = False
566 if (left_attr is pd.NA) ^ (right_attr is pd.NA):
567 result = False
568 elif not isinstance(result, bool):
569 result = result.all()
570
571 if (
572 not result
573 and isinstance(left_attr, (tuple, list))
574 and type(left_attr) is type(right_attr)
575 ):
576 # MultiIndex labels (tuples) and Index.names (FrozenList) can contain
577 # NA values that == does not treat as equal.
578 try:
579 result = array_equivalent(
580 construct_1d_object_array_from_listlike(left_attr),
581 construct_1d_object_array_from_listlike(right_attr),
582 strict_nan=True,
583 )
584 except TypeError:
585 result = False
586
587 if not result:
588 msg = f'Attribute "{attr}" are different'
589 raise_assert_detail(obj, msg, left_attr, right_attr)
590 return None
591
592
593def assert_is_sorted(seq: Index | Series | np.ndarray | ExtensionArray) -> None:
594 """Assert that the sequence is sorted."""
595 if isinstance(seq, (Index, Series)):
596 seq = seq._values
597 # sorting does not change precisions
598 if isinstance(seq, np.ndarray):
599 assert_numpy_array_equal(seq, np.sort(np.array(seq)))
600 else:
601 assert_extension_array_equal(seq, seq[seq.argsort()])
602
603
604def assert_categorical_equal(
605 left: Categorical,
606 right: Categorical,
607 check_dtype: bool = True,
608 check_category_order: bool = True,
609 obj: str = "Categorical",
610 *,
611 check_freq: bool | lib.NoDefault = lib.no_default,
612) -> None:
613 """
614 Test that Categoricals are equivalent.
615
616 Parameters
617 ----------
618 left : Categorical
619 right : Categorical
620 check_dtype : bool, default True
621 Check that integer dtype of the codes are the same.
622 check_category_order : bool, default True
623 Whether the order of the categories should be compared, which
624 implies identical integer codes. If False, only the resulting
625 values are compared. The ordered attribute is
626 checked regardless.
627 obj : str, default 'Categorical'
628 Specify object name being compared, internally used to show appropriate
629 assertion message.
630 check_freq : bool, default True
631 Whether to check the ``freq`` attribute of DatetimeIndex or
632 TimedeltaIndex categories.
633
634 .. versionadded:: 3.1.0
635
636 .. deprecated:: 3.1.0
637 Currently a ``freq`` mismatch only warns; in a future version
638 ``check_freq=True`` will be the default and mismatches will
639 raise. Pass ``check_freq`` explicitly to silence the warning.
640 """
641 _check_isinstance(left, right, Categorical)
642
643 exact: bool | str
644 if isinstance(left.categories, RangeIndex) or isinstance(
645 right.categories, RangeIndex
646 ):
647 exact = "equiv"
648 else:
649 # We still want to require exact matches for Index
650 exact = True
651
652 if check_category_order:
653 assert_index_equal(
654 left.categories,
655 right.categories,
656 obj=f"{obj}.categories",
657 exact=exact,
658 check_freq=check_freq,
659 )
660 assert_numpy_array_equal(
661 left.codes, right.codes, check_dtype=check_dtype, obj=f"{obj}.codes"
662 )
663 else:
664 try:
665 lc = left.categories.sort_values()
666 rc = right.categories.sort_values()
667 except TypeError:
668 # e.g. '<' not supported between instances of 'int' and 'str'
669 lc, rc = left.categories, right.categories
670 assert_index_equal(
671 lc,
672 rc,
673 obj=f"{obj}.categories",
674 exact=exact,
675 check_freq=check_freq,
676 )
677 # GH#62008 Compare the values as objects. Taking the categories with
678 # the raw codes would treat the -1 code used for NA as a positional
679 # indexer for the last category, and filling instead would cast
680 # integer categories to float64.
681 assert_numpy_array_equal(
682 left.astype(object),
683 right.astype(object),
684 obj=f"{obj}.values",
685 )
686
687 assert_attr_equal("ordered", left, right, obj=obj)
688
689
690def assert_interval_array_equal(
691 left: IntervalArray,
692 right: IntervalArray,
693 obj: str = "IntervalArray",
694) -> None:
695 """
696 Test that two IntervalArrays are equivalent.
697
698 Parameters
699 ----------
700 left, right : IntervalArray
701 The IntervalArrays to compare.
702 obj : str, default 'IntervalArray'
703 Specify object name being compared, internally used to show appropriate
704 assertion message
705 """
706 _check_isinstance(left, right, IntervalArray)
707
708 assert_equal(left._left, right._left, obj=f"{obj}.left")
709 assert_equal(left._right, right._right, obj=f"{obj}.right")
710
711 assert_attr_equal("closed", left, right, obj=obj)
712
713
714def assert_period_array_equal(
715 left: PeriodArray, right: PeriodArray, obj: str = "PeriodArray"
716) -> None:
717 _check_isinstance(left, right, PeriodArray)
718
719 assert_numpy_array_equal(left._ndarray, right._ndarray, obj=f"{obj}._ndarray")
720 assert_attr_equal("dtype", left, right, obj=obj)
721
722
723def assert_datetime_array_equal(
724 left: DatetimeArray, right: DatetimeArray, obj: str = "DatetimeArray"
725) -> None:
726 __tracebackhide__ = True
727 _check_isinstance(left, right, DatetimeArray)
728
729 assert_numpy_array_equal(left._ndarray, right._ndarray, obj=f"{obj}._ndarray")
730 assert_attr_equal("tz", left, right, obj=obj)
731
732
733def assert_timedelta_array_equal(
734 left: TimedeltaArray, right: TimedeltaArray, obj: str = "TimedeltaArray"
735) -> None:
736 __tracebackhide__ = True
737 _check_isinstance(left, right, TimedeltaArray)
738 assert_numpy_array_equal(left._ndarray, right._ndarray, obj=f"{obj}._ndarray")
739
740
741def raise_assert_detail(
742 obj: Any,
743 message: str,
744 left: Any,
745 right: Any,
746 diff: Any = None,
747 first_diff: Any = None,
748 index_values: Index | np.ndarray | None = None,
749) -> NoReturn:
750 __tracebackhide__ = True
751
752 msg = f"""{obj} are different
753
754{message}"""
755
756 if isinstance(index_values, Index):
757 index_values = np.asarray(index_values)
758
759 if isinstance(index_values, np.ndarray):
760 msg += f"\n[index]: {pprint_thing(index_values)}"
761
762 if isinstance(left, np.ndarray):
763 left = pprint_thing(left)
764 elif isinstance(left, (CategoricalDtype, StringDtype, NumpyEADtype)):
765 left = repr(left)
766
767 if isinstance(right, np.ndarray):
768 right = pprint_thing(right)
769 elif isinstance(right, (CategoricalDtype, StringDtype, NumpyEADtype)):
770 right = repr(right)
771
772 msg += f"""
773[left]: {left}
774[right]: {right}"""
775
776 if diff is not None:
777 msg += f"\n[diff]: {diff}"
778
779 if first_diff is not None:
780 msg += f"\n{first_diff}"
781
782 raise AssertionError(msg)
783
784
785def assert_numpy_array_equal(
786 left: Any,
787 right: Any,
788 strict_nan: bool = False,
789 check_dtype: bool | Literal["equiv"] = True,
790 err_msg: str | None = None,
791 check_same: Literal["copy", "same"] | None = None,
792 obj: str = "numpy array",
793 index_values: Index | np.ndarray | None = None,
794 *,
795 class_obj: str | None = None,
796) -> None:
797 """
798 Check that 'np.ndarray' is equivalent.
799
800 Parameters
801 ----------
802 left, right : numpy.ndarray or iterable
803 The two arrays to be compared.
804 strict_nan : bool, default False
805 If True, consider NaN and None to be different.
806 check_dtype : bool, default True
807 Check dtype if both a and b are np.ndarray.
808 err_msg : str, default None
809 If provided, used as assertion message.
810 check_same : None|'copy'|'same', default None
811 Ensure left and right refer/do not refer to the same memory area.
812 obj : str, default 'numpy array'
813 Specify object name being compared, internally used to show appropriate
814 assertion message.
815 index_values : Index | numpy.ndarray, default None
816 optional index (shared by both left and right), used in output.
817 class_obj : str, default None
818 Specify object name for class comparison, internally used to show
819 appropriate assertion message.
820 """
821 __tracebackhide__ = True
822
823 # instance validation
824 # Show a detailed error message when classes are different
825 assert_class_equal(left, right, obj=class_obj or obj)
826 # both classes must be an np.ndarray
827 _check_isinstance(left, right, np.ndarray)
828
829 def _get_base(obj: np.ndarray) -> Any:
830 return obj.base if getattr(obj, "base", None) is not None else obj
831
832 left_base = _get_base(left)
833 right_base = _get_base(right)
834
835 if check_same == "same":
836 if left_base is not right_base:
837 raise AssertionError(f"{left_base!r} is not {right_base!r}")
838 elif check_same == "copy":
839 if left_base is right_base:
840 raise AssertionError(f"{left_base!r} is {right_base!r}")
841
842 def _raise(left: np.ndarray, right: np.ndarray, err_msg: str | None) -> NoReturn:
843 if err_msg is None:
844 if left.shape != right.shape:
845 raise_assert_detail(
846 obj, f"{obj} shapes are different", left.shape, right.shape
847 )
848
849 diff = 0.0
850 # ravel so the count is over values, matching the `left.size` total
851 for left_val, right_val in zip(left.ravel(), right.ravel(), strict=True):
852 # count up differences
853 if not array_equivalent(left_val, right_val, strict_nan=strict_nan):
854 diff += 1
855
856 diff = diff * 100.0 / left.size
857 msg = f"{obj} values are different ({np.round(diff, 5)} %)"
858 raise_assert_detail(obj, msg, left, right, index_values=index_values)
859
860 raise AssertionError(err_msg)
861
862 # compare shape and values
863 if not array_equivalent(left, right, strict_nan=strict_nan):
864 _raise(left, right, err_msg)
865
866 if check_dtype:
867 if isinstance(left, np.ndarray) and isinstance(right, np.ndarray):
868 assert_attr_equal("dtype", left, right, obj=obj)
869
870
871@set_module("pandas.testing")
872def assert_extension_array_equal(
873 left: ExtensionArray,
874 right: ExtensionArray,
875 check_dtype: bool | Literal["equiv"] = True,
876 index_values: Index | np.ndarray | None = None,
877 check_exact: bool | lib.NoDefault = lib.no_default,
878 rtol: float | lib.NoDefault = lib.no_default,
879 atol: float | lib.NoDefault = lib.no_default,
880 obj: str = "ExtensionArray",
881) -> None:
882 """
883 Check that left and right ExtensionArrays are equal.
884
885 This method compares two ``ExtensionArray`` instances for equality,
886 including checks for missing values, the dtype of the arrays, and
887 the exactness of the comparison (or tolerance when comparing floats).
888
889 Parameters
890 ----------
891 left, right : ExtensionArray
892 The two arrays to compare.
893 check_dtype : bool, default True
894 Whether to check if the ExtensionArray dtypes are identical.
895 index_values : Index | numpy.ndarray, default None
896 Optional index (shared by both left and right), used in output.
897 check_exact : bool, default False
898 Whether to compare number exactly.
899
900 .. versionchanged:: 2.2.0
901
902 Defaults to True for integer dtypes if none of
903 ``check_exact``, ``rtol`` and ``atol`` are specified.
904 rtol : float, default 1e-5
905 Relative tolerance. Only used when check_exact is False, and only
906 applied to numeric dtypes; values of other dtypes, such as interval,
907 are always compared exactly.
908 atol : float, default 1e-8
909 Absolute tolerance. Only used when check_exact is False, and only
910 applied to numeric dtypes; values of other dtypes, such as interval,
911 are always compared exactly.
912 obj : str, default 'ExtensionArray'
913 Specify object name being compared, internally used to show appropriate
914 assertion message.
915
916 .. versionadded:: 2.0.0
917
918 See Also
919 --------
920 testing.assert_series_equal : Check that left and right ``Series`` are equal.
921 testing.assert_frame_equal : Check that left and right ``DataFrame`` are equal.
922 testing.assert_index_equal : Check that left and right ``Index`` are equal.
923
924 Notes
925 -----
926 Missing values are checked separately from valid values.
927 A mask of missing values is computed for each and checked to match.
928 The remaining all-valid values are cast to object dtype and checked.
929
930 Examples
931 --------
932 >>> from pandas import testing as tm
933 >>> a = pd.Series([1, 2, 3, 4])
934 >>> b, c = a.array, a.array
935 >>> tm.assert_extension_array_equal(b, c)
936 """
937 if (
938 check_exact is lib.no_default
939 and rtol is lib.no_default
940 and atol is lib.no_default
941 ):
942 check_exact = (
943 is_numeric_dtype(left.dtype) and not is_float_dtype(left.dtype)
944 ) or (is_numeric_dtype(right.dtype) and not is_float_dtype(right.dtype))
945 elif check_exact is lib.no_default:
946 check_exact = False
947
948 rtol = rtol if rtol is not lib.no_default else 1.0e-5
949 atol = atol if atol is not lib.no_default else 1.0e-8
950
951 assert isinstance(left, ExtensionArray), "left is not an ExtensionArray"
952 assert isinstance(right, ExtensionArray), "right is not an ExtensionArray"
953 if check_dtype:
954 assert_attr_equal("dtype", left, right, obj=f"Attributes of {obj}")
955
956 if (
957 isinstance(left, DatetimeLikeArrayMixin)
958 and isinstance(right, DatetimeLikeArrayMixin)
959 and type(right) == type(left)
960 ):
961 # GH 52449
962 if not check_dtype and left.dtype.kind in "mM":
963 if not isinstance(left.dtype, np.dtype):
964 l_unit = cast("DatetimeTZDtype", left.dtype).unit
965 else:
966 l_unit = np.datetime_data(left.dtype)[0]
967 if not isinstance(right.dtype, np.dtype):
968 r_unit = cast("DatetimeTZDtype", right.dtype).unit
969 else:
970 r_unit = np.datetime_data(right.dtype)[0]
971 if (
972 l_unit != r_unit
973 and compare_mismatched_resolutions(
974 left._ndarray, right._ndarray, operator.eq
975 ).all()
976 ):
977 return
978 # Avoid slow object-dtype comparisons
979 # np.asarray for case where we have an np.MaskedArray
980 assert_numpy_array_equal(
981 np.asarray(left.asi8),
982 np.asarray(right.asi8),
983 index_values=index_values,
984 obj=obj,
985 )
986 return
987
988 left_na = np.asarray(left.isna())
989 right_na = np.asarray(right.isna())
990 assert_numpy_array_equal(
991 left_na, right_na, obj=f"{obj} NA mask", index_values=index_values
992 )
993
994 # Specifically for StringArrayNumpySemantics, validate here we have a valid array
995 if (
996 isinstance(left.dtype, StringDtype)
997 and left.dtype.storage == "python"
998 and left.dtype.na_value is np.nan
999 ):
1000 assert np.all(
1001 [np.isnan(val) for val in left._ndarray[left_na]] # type: ignore[attr-defined]
1002 ), "wrong missing value sentinels"
1003 if (
1004 isinstance(right.dtype, StringDtype)
1005 and right.dtype.storage == "python"
1006 and right.dtype.na_value is np.nan
1007 ):
1008 assert np.all(
1009 [np.isnan(val) for val in right._ndarray[right_na]] # type: ignore[attr-defined]
1010 ), "wrong missing value sentinels"
1011
1012 left_valid = left[~left_na].to_numpy(dtype=object)
1013 right_valid = right[~right_na].to_numpy(dtype=object)
1014 if check_exact:
1015 assert_numpy_array_equal(
1016 left_valid, right_valid, obj=obj, index_values=index_values
1017 )
1018 else:
1019 _testing.assert_almost_equal(
1020 left_valid,
1021 right_valid,
1022 check_dtype=bool(check_dtype),
1023 rtol=rtol,
1024 atol=atol,
1025 obj=obj,
1026 index_values=index_values,
1027 )
1028
1029
1030# This could be refactored to use the NDFrame.equals method
1031@set_module("pandas.testing")
1032@deprecate_kwarg(Pandas4Warning, "check_datetimelike_compat", new_arg_name=None)
1033def assert_series_equal(
1034 left: Series,
1035 right: Series,
1036 check_dtype: bool | Literal["equiv"] = True,
1037 check_index_type: bool | Literal["equiv"] = "equiv",
1038 check_series_type: bool = True,
1039 check_names: bool = True,
1040 check_exact: bool | lib.NoDefault = lib.no_default,
1041 check_datetimelike_compat: bool = False,
1042 check_categorical: bool = True,
1043 check_category_order: bool = True,
1044 check_freq: bool | lib.NoDefault = lib.no_default,
1045 check_flags: bool = True,
1046 rtol: float | lib.NoDefault = lib.no_default,
1047 atol: float | lib.NoDefault = lib.no_default,
1048 obj: str = "Series",
1049 *,
1050 check_index: bool = True,
1051 check_like: bool = False,
1052) -> None:
1053 """
1054 Check that left and right Series are equal.
1055
1056 This function compares two Series and raises an ``AssertionError`` if they
1057 are not equal. It provides fine-grained control over which attributes to
1058 check, including dtype, index, names, and categorical properties.
1059
1060 Parameters
1061 ----------
1062 left : Series
1063 First Series to compare.
1064 right : Series
1065 Second Series to compare.
1066 check_dtype : bool, default True
1067 Whether to check the Series dtype is identical.
1068 check_index_type : bool or {'equiv'}, default 'equiv'
1069 Whether to check the Index class, dtype and inferred_type
1070 are identical.
1071 check_series_type : bool, default True
1072 Whether to check the Series class is identical.
1073 check_names : bool, default True
1074 Whether to check the Series and Index names attribute.
1075 check_exact : bool, default False
1076 Whether to compare number exactly. This also applies when checking
1077 Index equivalence.
1078
1079 .. versionchanged:: 2.2.0
1080
1081 Defaults to True for integer dtypes if none of
1082 ``check_exact``, ``rtol`` and ``atol`` are specified.
1083
1084 .. versionchanged:: 3.0.0
1085
1086 check_exact for comparing the Indexes defaults to True by
1087 checking if an Index is of integer dtypes.
1088
1089 When ``check_exact=True``, the comparison of object-dtype values
1090 containing nested :class:`numpy.ndarray` objects takes the type of
1091 the nested object into account. In particular, a nested
1092 ``numpy.ndarray`` is not considered equivalent to a Python
1093 ``list``, Python ``tuple``, or a pandas
1094 :class:`~pandas.api.extensions.ExtensionArray` merely because the
1095 contained values are the same.
1096
1097 check_datetimelike_compat : bool, default False
1098 Compare datetime-like which is comparable ignoring dtype.
1099
1100 .. deprecated:: 3.0
1101
1102 check_categorical : bool, default True
1103 Whether to compare internal Categorical exactly.
1104 check_category_order : bool, default True
1105 Whether to compare category order of internal Categoricals.
1106 check_freq : bool, default True
1107 Whether to check the `freq` attribute on a DatetimeIndex or TimedeltaIndex.
1108 The index check is skipped if ``check_index=False`` or ``check_like=True``.
1109
1110 .. deprecated:: 3.1.0
1111 The ``freq`` attribute of a :class:`DatetimeIndex`/
1112 :class:`TimedeltaIndex` MultiIndex level or Categorical categories is
1113 not yet checked by default; a mismatch currently only warns and will
1114 raise in a future version. Pass ``check_freq`` explicitly to silence
1115 the warning.
1116 check_flags : bool, default True
1117 Whether to check the `flags` attribute.
1118 rtol : float, default 1e-5
1119 Relative tolerance. Only used when check_exact is False, and only
1120 applied to numeric dtypes; values of other dtypes, such as interval,
1121 are always compared exactly.
1122 atol : float, default 1e-8
1123 Absolute tolerance. Only used when check_exact is False, and only
1124 applied to numeric dtypes; values of other dtypes, such as interval,
1125 are always compared exactly.
1126 obj : str, default 'Series'
1127 Specify object name being compared, internally used to show appropriate
1128 assertion message.
1129 check_index : bool, default True
1130 Whether to check index equivalence. If False, then compare only values.
1131 check_like : bool, default False
1132 If True, ignore the order of the index. Must be False if check_index is False.
1133 Note: same labels must be with the same data.
1134
1135 See Also
1136 --------
1137 testing.assert_index_equal : Check that two Indexes are equal.
1138 testing.assert_frame_equal : Check that two DataFrames are equal.
1139
1140 Examples
1141 --------
1142 >>> from pandas import testing as tm
1143 >>> a = pd.Series([1, 2, 3, 4])
1144 >>> b = pd.Series([1, 2, 3, 4])
1145 >>> tm.assert_series_equal(a, b)
1146 """
1147 __tracebackhide__ = True
1148 if (
1149 check_exact is lib.no_default
1150 and rtol is lib.no_default
1151 and atol is lib.no_default
1152 ):
1153 check_exact = (
1154 is_numeric_dtype(left.dtype) and not is_float_dtype(left.dtype)
1155 ) or (is_numeric_dtype(right.dtype) and not is_float_dtype(right.dtype))
1156 left_index_dtypes = (
1157 [left.index.dtype]
1158 if left.index.nlevels == 1
1159 else cast("MultiIndex", left.index).dtypes
1160 )
1161 right_index_dtypes = (
1162 [right.index.dtype]
1163 if right.index.nlevels == 1
1164 else cast("MultiIndex", right.index).dtypes
1165 )
1166 check_exact_index = all(
1167 dtype.kind in "iu" for dtype in left_index_dtypes
1168 ) or all(dtype.kind in "iu" for dtype in right_index_dtypes)
1169 elif check_exact is lib.no_default:
1170 check_exact = False
1171 check_exact_index = False
1172 else:
1173 check_exact_index = check_exact
1174
1175 rtol = rtol if rtol is not lib.no_default else 1.0e-5
1176 atol = atol if atol is not lib.no_default else 1.0e-8
1177
1178 if not check_index and check_like:
1179 raise ValueError("check_like must be False if check_index is False")
1180
1181 # instance validation
1182 _check_isinstance(left, right, Series)
1183
1184 if check_series_type:
1185 assert_class_equal(left, right, obj=obj)
1186
1187 # length comparison
1188 if len(left) != len(right):
1189 msg1 = f"{len(left)}, {left.index}"
1190 msg2 = f"{len(right)}, {right.index}"
1191 raise_assert_detail(obj, "Series length are different", msg1, msg2)
1192
1193 if check_flags:
1194 assert left.flags == right.flags, f"{left.flags!r} != {right.flags!r}"
1195
1196 if check_index:
1197 # GH #38183
1198 assert_index_equal(
1199 left.index,
1200 right.index,
1201 exact=check_index_type,
1202 check_names=check_names,
1203 check_exact=check_exact_index,
1204 check_categorical=check_categorical,
1205 check_order=not check_like,
1206 check_freq=_resolve_check_freq(left.index, check_freq),
1207 rtol=rtol,
1208 atol=atol,
1209 obj=f"{obj}.index",
1210 )
1211
1212 if check_like:
1213 left = left.reindex_like(right)
1214
1215 if check_dtype:
1216 # We want to skip exact dtype checking when `check_categorical`
1217 # is False. We'll still raise if only one is a `Categorical`,
1218 # regardless of `check_categorical`
1219 if (
1220 isinstance(left.dtype, CategoricalDtype)
1221 and isinstance(right.dtype, CategoricalDtype)
1222 and not check_categorical
1223 ):
1224 pass
1225 else:
1226 assert_attr_equal("dtype", left, right, obj=f"Attributes of {obj}")
1227 if check_exact:
1228 left_values = left._values
1229 right_values = right._values
1230 # Only check exact if dtype is numeric
1231 if isinstance(left_values, ExtensionArray) and isinstance(
1232 right_values, ExtensionArray
1233 ):
1234 assert_extension_array_equal(
1235 left_values,
1236 right_values,
1237 check_dtype=check_dtype,
1238 index_values=left.index,
1239 obj=str(obj),
1240 )
1241 else:
1242 # convert both to NumPy if not, check_dtype would raise earlier
1243 lv, rv = left_values, right_values
1244 if isinstance(left_values, ExtensionArray):
1245 lv = left_values.to_numpy()
1246 if isinstance(right_values, ExtensionArray):
1247 rv = right_values.to_numpy()
1248 assert_numpy_array_equal(
1249 lv,
1250 rv,
1251 check_dtype=check_dtype,
1252 obj=str(obj),
1253 class_obj=f"{obj} values",
1254 index_values=left.index,
1255 )
1256 elif check_datetimelike_compat and (
1257 needs_i8_conversion(left.dtype) or needs_i8_conversion(right.dtype)
1258 ):
1259 # we want to check only if we have compat dtypes
1260 # e.g. integer and M|m are NOT compat, but we can simply check
1261 # the values in that case
1262
1263 # datetimelike may have different objects (e.g. datetime.datetime
1264 # vs Timestamp) but will compare equal
1265 if not Index(left._values).equals(Index(right._values)):
1266 msg = (
1267 f"[datetimelike_compat=True] {left._values} "
1268 f"is not equal to {right._values}."
1269 )
1270 raise AssertionError(msg)
1271 elif isinstance(left.dtype, IntervalDtype) and isinstance(
1272 right.dtype, IntervalDtype
1273 ):
1274 assert_interval_array_equal(
1275 cast("IntervalArray", left.array), cast("IntervalArray", right.array)
1276 )
1277 elif isinstance(left.dtype, CategoricalDtype) or isinstance(
1278 right.dtype, CategoricalDtype
1279 ):
1280 _testing.assert_almost_equal(
1281 left._values,
1282 right._values,
1283 rtol=rtol,
1284 atol=atol,
1285 check_dtype=bool(check_dtype),
1286 obj=str(obj),
1287 index_values=left.index,
1288 )
1289 elif isinstance(left.dtype, ExtensionDtype) and isinstance(
1290 right.dtype, ExtensionDtype
1291 ):
1292 assert_extension_array_equal(
1293 left._values,
1294 right._values,
1295 rtol=rtol,
1296 atol=atol,
1297 check_dtype=check_dtype,
1298 index_values=left.index,
1299 obj=str(obj),
1300 )
1301 elif is_extension_array_dtype_and_needs_i8_conversion(
1302 left.dtype, right.dtype
1303 ) or is_extension_array_dtype_and_needs_i8_conversion(right.dtype, left.dtype):
1304 assert_extension_array_equal(
1305 left._values,
1306 right._values,
1307 check_dtype=check_dtype,
1308 index_values=left.index,
1309 obj=str(obj),
1310 )
1311 elif needs_i8_conversion(left.dtype) and needs_i8_conversion(right.dtype):
1312 # DatetimeArray or TimedeltaArray
1313 assert_extension_array_equal(
1314 left._values,
1315 right._values,
1316 check_dtype=check_dtype,
1317 index_values=left.index,
1318 obj=str(obj),
1319 )
1320 else:
1321 _testing.assert_almost_equal(
1322 left._values,
1323 right._values,
1324 rtol=rtol,
1325 atol=atol,
1326 check_dtype=bool(check_dtype),
1327 obj=str(obj),
1328 index_values=left.index,
1329 )
1330
1331 # metadata comparison
1332 if check_names:
1333 assert_attr_equal("name", left, right, obj=obj)
1334
1335 if check_categorical:
1336 if isinstance(left.dtype, CategoricalDtype) or isinstance(
1337 right.dtype, CategoricalDtype
1338 ):
1339 assert_categorical_equal(
1340 left._values,
1341 right._values,
1342 obj=f"{obj} category",
1343 check_category_order=check_category_order,
1344 check_freq=check_freq,
1345 )
1346
1347
1348# This could be refactored to use the NDFrame.equals method
1349@set_module("pandas.testing")
1350@deprecate_kwarg(Pandas4Warning, "check_datetimelike_compat", new_arg_name=None)
1351# stacklevel=3 to account for the extra frame from the stacked decorator above
1352@deprecate_kwarg(Pandas4Warning, "by_blocks", new_arg_name=None, stacklevel=3)
1353def assert_frame_equal(
1354 left: DataFrame,
1355 right: DataFrame,
1356 check_dtype: bool | Literal["equiv"] = True,
1357 check_index_type: bool | Literal["equiv"] = "equiv",
1358 check_column_type: bool | Literal["equiv"] = "equiv",
1359 check_frame_type: bool = True,
1360 check_names: bool = True,
1361 by_blocks: bool = False,
1362 check_exact: bool | lib.NoDefault = lib.no_default,
1363 check_datetimelike_compat: bool = False,
1364 check_categorical: bool = True,
1365 check_like: bool = False,
1366 check_freq: bool | lib.NoDefault = lib.no_default,
1367 check_flags: bool = True,
1368 rtol: float | lib.NoDefault = lib.no_default,
1369 atol: float | lib.NoDefault = lib.no_default,
1370 obj: str = "DataFrame",
1371) -> None:
1372 """
1373 Check that left and right DataFrame are equal.
1374
1375 This function is intended to compare two DataFrames and output any
1376 differences. It is mostly intended for use in unit tests.
1377 Additional parameters allow varying the strictness of the
1378 equality checks performed.
1379
1380 Parameters
1381 ----------
1382 left : DataFrame
1383 First DataFrame to compare.
1384 right : DataFrame
1385 Second DataFrame to compare.
1386 check_dtype : bool, default True
1387 Whether to check the DataFrame dtype is identical.
1388 check_index_type : bool or {'equiv'}, default 'equiv'
1389 Whether to check the Index class, dtype and inferred_type
1390 are identical.
1391 check_column_type : bool or {'equiv'}, default 'equiv'
1392 Whether to check the columns class, dtype and inferred_type
1393 are identical. Is passed as the ``exact`` argument of
1394 :func:`assert_index_equal`.
1395 check_frame_type : bool, default True
1396 Whether to check the DataFrame class is identical.
1397 check_names : bool, default True
1398 Whether to check that the `names` attribute for both the `index`
1399 and `column` attributes of the DataFrame is identical.
1400 by_blocks : bool, default False
1401 Specify how to compare internal data. If False, compare by columns.
1402 If True, compare by blocks.
1403
1404 .. deprecated:: 3.1
1405 check_exact : bool, default False
1406 Whether to compare number exactly. If False, the comparison uses the
1407 relative tolerance (``rtol``) and absolute tolerance (``atol``)
1408 parameters to determine if two values are considered close,
1409 according to the formula: ``|a - b| <= (atol + rtol * |b|)``.
1410
1411 .. versionchanged:: 2.2.0
1412
1413 Defaults to True for integer dtypes if none of
1414 ``check_exact``, ``rtol`` and ``atol`` are specified.
1415
1416 .. versionchanged:: 3.0.0
1417
1418 When ``check_exact=True``, the comparison of object-dtype values
1419 containing nested :class:`numpy.ndarray` objects takes the type of
1420 the nested object into account. In particular, a nested
1421 ``numpy.ndarray`` is not considered equivalent to a Python
1422 ``list``, Python ``tuple``, or a pandas
1423 :class:`~pandas.api.extensions.ExtensionArray` merely because the
1424 contained values are the same.
1425
1426 check_datetimelike_compat : bool, default False
1427 Compare datetime-like which is comparable ignoring dtype.
1428
1429 .. deprecated:: 3.0
1430
1431 check_categorical : bool, default True
1432 Whether to compare internal Categorical exactly.
1433 check_like : bool, default False
1434 If True, ignore the order of index & columns.
1435 Note: index labels must match their respective rows
1436 (same as in columns) - same labels must be with the same data.
1437 check_freq : bool, default True
1438 Whether to check the `freq` attribute on a DatetimeIndex or TimedeltaIndex
1439 index or columns. The index and columns checks are skipped if
1440 ``check_like=True``.
1441
1442 .. deprecated:: 3.1.0
1443 The ``freq`` attribute of :class:`DatetimeIndex`/:class:`TimedeltaIndex`
1444 columns, MultiIndex levels, and Categorical categories is not yet
1445 checked by default; a mismatch currently only warns and will raise in
1446 a future version. Pass ``check_freq`` explicitly to silence the
1447 warning.
1448 check_flags : bool, default True
1449 Whether to check the `flags` attribute.
1450 rtol : float, default 1e-5
1451 Relative tolerance. Only used when check_exact is False, and only
1452 applied to numeric dtypes; values of other dtypes, such as interval,
1453 are always compared exactly.
1454 atol : float, default 1e-8
1455 Absolute tolerance. Only used when check_exact is False, and only
1456 applied to numeric dtypes; values of other dtypes, such as interval,
1457 are always compared exactly.
1458 obj : str, default 'DataFrame'
1459 Specify object name being compared, internally used to show appropriate
1460 assertion message.
1461
1462 See Also
1463 --------
1464 assert_series_equal : Equivalent method for asserting Series equality.
1465 DataFrame.equals : Check DataFrame equality.
1466
1467 Examples
1468 --------
1469 This example shows comparing two DataFrames that are equal
1470 but with columns of differing dtypes.
1471
1472 >>> from pandas.testing import assert_frame_equal
1473 >>> df1 = pd.DataFrame({"a": [1, 2], "b": [3, 4]})
1474 >>> df2 = pd.DataFrame({"a": [1, 2], "b": [3.0, 4.0]})
1475
1476 df1 equals itself.
1477
1478 >>> assert_frame_equal(df1, df1)
1479
1480 df1 differs from df2 as column 'b' is of a different type.
1481
1482 >>> assert_frame_equal(df1, df2)
1483 Traceback (most recent call last):
1484 ...
1485 AssertionError: Attributes of DataFrame.iloc[:, 1] (column name="b") are different
1486
1487 Attribute "dtype" are different
1488 [left]: int64
1489 [right]: float64
1490
1491 Ignore differing dtypes in columns with check_dtype.
1492
1493 >>> assert_frame_equal(df1, df2, check_dtype=False)
1494 """
1495 __tracebackhide__ = True
1496 _rtol = rtol if rtol is not lib.no_default else 1.0e-5
1497 _atol = atol if atol is not lib.no_default else 1.0e-8
1498 _check_exact = check_exact if check_exact is not lib.no_default else False
1499
1500 # instance validation
1501 _check_isinstance(left, right, DataFrame)
1502
1503 # The flat-index freq has long been checked by default, so preserve that
1504 # hard check; the columns freq check is new and goes through the deprecation
1505 # warning in assert_index_equal (passing check_freq unresolved). GH#51920
1506 _check_freq = _resolve_check_freq(left.index, check_freq)
1507
1508 if check_frame_type:
1509 assert isinstance(left, type(right))
1510 # assert_class_equal(left, right, obj=obj)
1511
1512 # shape comparison
1513 if left.shape != right.shape:
1514 raise_assert_detail(
1515 obj, f"{obj} shape mismatch", f"{left.shape!r}", f"{right.shape!r}"
1516 )
1517
1518 if check_flags:
1519 assert left.flags == right.flags, f"{left.flags!r} != {right.flags!r}"
1520
1521 # index comparison
1522 assert_index_equal(
1523 left.index,
1524 right.index,
1525 exact=check_index_type,
1526 check_names=check_names,
1527 check_exact=_check_exact,
1528 check_categorical=check_categorical,
1529 check_order=not check_like,
1530 check_freq=_check_freq,
1531 rtol=_rtol,
1532 atol=_atol,
1533 obj=f"{obj}.index",
1534 )
1535
1536 # column comparison
1537 assert_index_equal(
1538 left.columns,
1539 right.columns,
1540 exact=check_column_type,
1541 check_names=check_names,
1542 check_exact=_check_exact,
1543 check_categorical=check_categorical,
1544 check_order=not check_like,
1545 check_freq=check_freq,
1546 rtol=_rtol,
1547 atol=_atol,
1548 obj=f"{obj}.columns",
1549 )
1550
1551 if check_like:
1552 left = left.reindex_like(right)
1553
1554 # compare by blocks
1555 if by_blocks:
1556 rblocks = right._to_dict_of_blocks()
1557 lblocks = left._to_dict_of_blocks()
1558 for dtype in list(set(list(lblocks.keys()) + list(rblocks.keys()))):
1559 assert dtype in lblocks
1560 assert dtype in rblocks
1561 assert_frame_equal(
1562 lblocks[dtype],
1563 rblocks[dtype],
1564 check_dtype=check_dtype,
1565 check_freq=check_freq,
1566 obj=obj,
1567 )
1568
1569 # compare by columns
1570 else:
1571 for i, col in enumerate(left.columns):
1572 # We have already checked that columns match, so we can do
1573 # fast location-based lookups
1574 lcol = left._ixs(i, axis=1)
1575 rcol = right._ixs(i, axis=1)
1576
1577 # GH #38183
1578 # use check_index=False, because we do not want to run
1579 # assert_index_equal for each column,
1580 # as we already checked it for the whole dataframe before.
1581 with warnings.catch_warnings():
1582 warnings.filterwarnings(
1583 "ignore",
1584 message="the 'check_datetimelike_compat' keyword",
1585 category=Pandas4Warning,
1586 )
1587 assert_series_equal(
1588 lcol,
1589 rcol,
1590 check_dtype=check_dtype,
1591 check_index_type=check_index_type,
1592 check_exact=check_exact,
1593 check_names=check_names,
1594 check_datetimelike_compat=check_datetimelike_compat,
1595 check_categorical=check_categorical,
1596 # check_index=False above, so this governs only the
1597 # categorical categories, whose freq goes through the
1598 # deprecation rather than the index's hard check
1599 check_freq=check_freq,
1600 obj=f'{obj}.iloc[:, {i}] (column name="{col}")',
1601 rtol=rtol,
1602 atol=atol,
1603 check_index=False,
1604 check_flags=False,
1605 )
1606
1607
1608def assert_equal(left: Any, right: Any, **kwargs: Any) -> None:
1609 """
1610 Wrapper for tm.assert_*_equal to dispatch to the appropriate test function.
1611
1612 Parameters
1613 ----------
1614 left, right : Index, Series, DataFrame, ExtensionArray, or np.ndarray
1615 The two items to be compared.
1616 **kwargs
1617 All keyword arguments are passed through to the underlying assert method.
1618 """
1619 __tracebackhide__ = True
1620
1621 if isinstance(left, Index):
1622 # retain the long-standing hard freq check for datetimelike Index;
1623 # the check_freq deprecation in assert_index_equal only warns by
1624 # default. GH#51920
1625 if isinstance(left, (DatetimeIndex, TimedeltaIndex)):
1626 kwargs.setdefault("check_freq", True)
1627 assert_index_equal(left, right, **kwargs)
1628 elif isinstance(left, Series):
1629 assert_series_equal(left, right, **kwargs)
1630 elif isinstance(left, DataFrame):
1631 assert_frame_equal(left, right, **kwargs)
1632 elif isinstance(left, IntervalArray):
1633 assert_interval_array_equal(left, right, **kwargs)
1634 elif isinstance(left, PeriodArray):
1635 assert_period_array_equal(left, right, **kwargs)
1636 elif isinstance(left, DatetimeArray):
1637 assert_datetime_array_equal(left, right, **kwargs)
1638 elif isinstance(left, TimedeltaArray):
1639 assert_timedelta_array_equal(left, right, **kwargs)
1640 elif isinstance(left, ExtensionArray):
1641 assert_extension_array_equal(left, right, **kwargs)
1642 elif isinstance(left, np.ndarray):
1643 assert_numpy_array_equal(left, right, **kwargs)
1644 elif isinstance(left, str):
1645 assert kwargs == {}
1646 assert left == right
1647 else:
1648 assert kwargs == {}
1649 assert_almost_equal(left, right)
1650
1651
1652def assert_sp_array_equal(left: Any, right: Any) -> None:
1653 """
1654 Check that the left and right SparseArray are equal.
1655
1656 Parameters
1657 ----------
1658 left : SparseArray
1659 right : SparseArray
1660 """
1661 _check_isinstance(left, right, pd.arrays.SparseArray)
1662
1663 assert_numpy_array_equal(left.sp_values, right.sp_values)
1664
1665 # SparseIndex comparison
1666 assert isinstance(left.sp_index, SparseIndex)
1667 assert isinstance(right.sp_index, SparseIndex)
1668
1669 left_index = left.sp_index
1670 right_index = right.sp_index
1671
1672 if not left_index.equals(right_index):
1673 raise_assert_detail(
1674 "SparseArray.index", "index are not equal", left_index, right_index
1675 )
1676 else:
1677 # Just ensure a
1678 pass
1679
1680 assert_attr_equal("fill_value", left, right)
1681 assert_attr_equal("dtype", left, right)
1682 assert_numpy_array_equal(left.to_dense(), right.to_dense())
1683
1684
1685def assert_contains_all(iterable: Iterable[Any], dic: Container[Any]) -> None:
1686 for k in iterable:
1687 assert k in dic, f"Did not contain item: {k!r}"
1688
1689
1690def assert_copy(iter1: Iterable[Any], iter2: Iterable[Any], **eql_kwargs: Any) -> None:
1691 """
1692 iter1, iter2: iterables that produce elements
1693 comparable with assert_almost_equal
1694
1695 Checks that the elements are equal, but not
1696 the same object. (Does not check that items
1697 in sequences are also not the same object)
1698 """
1699 for elem1, elem2 in zip(iter1, iter2, strict=True):
1700 assert_almost_equal(elem1, elem2, **eql_kwargs)
1701 msg = (
1702 f"Expected object {type(elem1)!r} and object {type(elem2)!r} to be "
1703 "different objects, but they were the same object."
1704 )
1705 assert elem1 is not elem2, msg
1706
1707
1708def is_extension_array_dtype_and_needs_i8_conversion(
1709 left_dtype: DtypeObj, right_dtype: DtypeObj
1710) -> bool:
1711 """
1712 Checks that we have the combination of an ExtensionArraydtype and
1713 a dtype that should be converted to int64
1714
1715 Returns
1716 -------
1717 bool
1718
1719 Related to issue #37609
1720 """
1721 return isinstance(left_dtype, ExtensionDtype) and needs_i8_conversion(right_dtype)
1722
1723
1724def assert_indexing_slices_equivalent(ser: Series, l_slc: slice, i_slc: slice) -> None:
1725 """
1726 Check that ser.iloc[i_slc] matches ser.loc[l_slc] and, if applicable,
1727 ser[l_slc].
1728 """
1729 expected = ser.iloc[i_slc]
1730
1731 assert_series_equal(ser.loc[l_slc], expected)
1732
1733 if not is_integer_dtype(ser.index):
1734 # For integer indices, .loc and plain getitem are position-based.
1735 assert_series_equal(ser[l_slc], expected)
1736
1737
1738def assert_metadata_equivalent(
1739 left: DataFrame | Series, right: DataFrame | Series | None = None
1740) -> None:
1741 """
1742 Check that ._metadata attributes are equivalent.
1743 """
1744 for attr in left._metadata:
1745 val = getattr(left, attr, None)
1746 if right is None:
1747 assert val is None
1748 else:
1749 assert val == getattr(right, attr, None)