|
57 | 57 | is_integer_dtype, |
58 | 58 | is_list_like, |
59 | 59 | is_object_dtype, |
60 | | - is_signed_integer_dtype, |
61 | 60 | needs_i8_conversion, |
62 | 61 | ) |
63 | 62 | from pandas.core.dtypes.concat import concat_compat |
@@ -518,23 +517,10 @@ def isin(comps: ListLike, values: ListLike) -> npt.NDArray[np.bool_]: |
518 | 517 | if not isinstance(values, (ABCIndex, ABCSeries, ABCExtensionArray, np.ndarray)): |
519 | 518 | orig_values = list(values) |
520 | 519 | values = _ensure_arraylike(orig_values, func_name="isin-targets") |
521 | | - |
522 | | - if ( |
523 | | - len(values) > 0 |
524 | | - and values.dtype.kind in "iufcb" |
525 | | - # If the dtypes differ and either side is unsigned integer, |
526 | | - # prefer object dtype to avoid unsafe upcast to float64 that |
527 | | - # can lose precision for large 64-bit integers. |
528 | | - and (not is_dtype_equal(values, comps)) |
529 | | - and ( |
530 | | - (not is_signed_integer_dtype(comps)) |
531 | | - or (not is_signed_integer_dtype(values)) |
532 | | - ) |
533 | | - ): |
534 | | - # GH#46485: Use object to avoid upcast to float64 later |
535 | | - # Ensure symmetric behavior when mixing signed and unsigned |
536 | | - # integer dtypes. |
537 | | - values = construct_1d_object_array_from_listlike(orig_values) |
| 520 | + # Keep values as a numeric ndarray where possible; we handle |
| 521 | + # signed/unsigned integer mixes with a fast-path later (after |
| 522 | + # comps_array extraction) to avoid object-dtype conversions that |
| 523 | + # harm performance for large numeric arrays. |
538 | 524 |
|
539 | 525 | elif isinstance(values, ABCMultiIndex): |
540 | 526 | # Avoid raising in extract_array |
@@ -586,6 +572,28 @@ def f(c, v): |
586 | 572 | f = lambda a, b: np.isin(a, b).ravel() |
587 | 573 |
|
588 | 574 | else: |
| 575 | + # Fast-path: handle integer-kind mixes without upcasting to float64. |
| 576 | + if ( |
| 577 | + values.dtype.kind in "iu" |
| 578 | + and comps_array.dtype.kind in "iu" |
| 579 | + and not is_dtype_equal(values.dtype, comps_array.dtype) |
| 580 | + ): |
| 581 | + try: |
| 582 | + if values.size > 0 and comps_array.size > 0: |
| 583 | + signed_negative = False |
| 584 | + if values.dtype.kind == "i": |
| 585 | + signed_negative = values.min() < 0 |
| 586 | + if comps_array.dtype.kind == "i": |
| 587 | + signed_negative = signed_negative or (comps_array.min() < 0) |
| 588 | + |
| 589 | + if not signed_negative: |
| 590 | + values_u = values.astype("uint64", copy=False) |
| 591 | + comps_u = comps_array.astype("uint64", copy=False) |
| 592 | + return htable.ismember(comps_u, values_u) |
| 593 | + except Exception: |
| 594 | + # fall back to generic path on error |
| 595 | + pass |
| 596 | + |
589 | 597 | common = np_find_common_type(values.dtype, comps_array.dtype) |
590 | 598 | values = values.astype(common, copy=False) |
591 | 599 | comps_array = comps_array.astype(common, copy=False) |
|
0 commit comments