diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst
index 3829306f77a0b..4a11028950966 100644
--- a/doc/source/whatsnew/v1.5.0.rst
+++ b/doc/source/whatsnew/v1.5.0.rst
@@ -37,6 +37,7 @@ Other enhancements
- :meth:`to_numeric` now preserves float64 arrays when downcasting would generate values not representable in float32 (:issue:`43693`)
- :meth:`Series.reset_index` and :meth:`DataFrame.reset_index` now support the argument ``allow_duplicates`` (:issue:`44410`)
- :meth:`.GroupBy.min` and :meth:`.GroupBy.max` now supports `Numba `_ execution with the ``engine`` keyword (:issue:`45428`)
+- Implemented a ``bool``-dtype :class:`Index`, passing a bool-dtype arraylike to ``pd.Index`` will now retain ``bool`` dtype instead of casting to ``object`` (:issue:`45061`)
-
.. ---------------------------------------------------------------------------
diff --git a/pandas/_libs/index.pyi b/pandas/_libs/index.pyi
index 86f2429575ebb..184bad1c080b3 100644
--- a/pandas/_libs/index.pyi
+++ b/pandas/_libs/index.pyi
@@ -42,6 +42,7 @@ class ObjectEngine(IndexEngine): ...
class DatetimeEngine(Int64Engine): ...
class TimedeltaEngine(DatetimeEngine): ...
class PeriodEngine(Int64Engine): ...
+class BoolEngine(UInt8Engine): ...
class BaseMultiIndexCodesEngine:
levels: list[np.ndarray]
diff --git a/pandas/_libs/index.pyx b/pandas/_libs/index.pyx
index d8311282d1193..0e9a330587f07 100644
--- a/pandas/_libs/index.pyx
+++ b/pandas/_libs/index.pyx
@@ -802,6 +802,13 @@ cdef class BaseMultiIndexCodesEngine:
include "index_class_helper.pxi"
+cdef class BoolEngine(UInt8Engine):
+ cdef _check_type(self, object val):
+ if not util.is_bool_object(val):
+ raise KeyError(val)
+ return val
+
+
@cython.internal
@cython.freelist(32)
cdef class SharedEngine:
diff --git a/pandas/conftest.py b/pandas/conftest.py
index ba90c9eedb53c..a9c8ab833b40f 100644
--- a/pandas/conftest.py
+++ b/pandas/conftest.py
@@ -555,7 +555,8 @@ def _create_mi_with_dt64tz_level():
"num_uint8": tm.makeNumericIndex(100, dtype="uint8"),
"num_float64": tm.makeNumericIndex(100, dtype="float64"),
"num_float32": tm.makeNumericIndex(100, dtype="float32"),
- "bool": tm.makeBoolIndex(10),
+ "bool-object": tm.makeBoolIndex(10).astype(object),
+ "bool-dtype": Index(np.random.randn(10) < 0),
"categorical": tm.makeCategoricalIndex(100),
"interval": tm.makeIntervalIndex(100),
"empty": Index([]),
@@ -630,7 +631,7 @@ def index_flat_unique(request):
key
for key in indices_dict
if not (
- key in ["int", "uint", "range", "empty", "repeats"]
+ key in ["int", "uint", "range", "empty", "repeats", "bool-dtype"]
or key.startswith("num_")
)
and not isinstance(indices_dict[key], MultiIndex)
diff --git a/pandas/core/algorithms.py b/pandas/core/algorithms.py
index 36eabe93dbd7e..ddb8e19d1c558 100644
--- a/pandas/core/algorithms.py
+++ b/pandas/core/algorithms.py
@@ -220,9 +220,6 @@ def _reconstruct_data(
elif is_bool_dtype(dtype):
values = values.astype(dtype, copy=False)
- # we only support object dtypes bool Index
- if isinstance(original, ABCIndex):
- values = values.astype(object, copy=False)
elif dtype is not None:
if is_datetime64_dtype(dtype):
dtype = np.dtype("datetime64[ns]")
@@ -830,7 +827,10 @@ def value_counts(
-------
Series
"""
- from pandas.core.series import Series
+ from pandas import (
+ Index,
+ Series,
+ )
name = getattr(values, "name", None)
@@ -868,7 +868,13 @@ def value_counts(
else:
keys, counts = value_counts_arraylike(values, dropna)
- result = Series(counts, index=keys, name=name)
+ # For backwards compatibility, we let Index do its normal type
+ # inference, _except_ for if if infers from object to bool.
+ idx = Index._with_infer(keys)
+ if idx.dtype == bool and keys.dtype == object:
+ idx = idx.astype(object)
+
+ result = Series(counts, index=idx, name=name)
if sort:
result = result.sort_values(ascending=ascending)
diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py
index f6feec5fd97a6..847c5a607c086 100644
--- a/pandas/core/indexes/base.py
+++ b/pandas/core/indexes/base.py
@@ -505,6 +505,10 @@ def __new__(
if data.dtype.kind in ["i", "u", "f"]:
# maybe coerce to a sub-class
arr = data
+ elif data.dtype.kind == "b":
+ # No special subclass, and Index._ensure_array won't do this
+ # for us.
+ arr = np.asarray(data)
else:
arr = com.asarray_tuplesafe(data, dtype=_dtype_obj)
@@ -702,7 +706,7 @@ def _with_infer(cls, *args, **kwargs):
# "Union[ExtensionArray, ndarray[Any, Any]]"; expected
# "ndarray[Any, Any]"
values = lib.maybe_convert_objects(result._values) # type: ignore[arg-type]
- if values.dtype.kind in ["i", "u", "f"]:
+ if values.dtype.kind in ["i", "u", "f", "b"]:
return Index(values, name=result.name)
return result
@@ -872,9 +876,12 @@ def _engine(
):
return libindex.ExtensionEngine(target_values)
+ target_values = cast(np.ndarray, target_values)
# to avoid a reference cycle, bind `target_values` to a local variable, so
# `self` is not passed into the lambda.
- target_values = cast(np.ndarray, target_values)
+ if target_values.dtype == bool:
+ return libindex.BoolEngine(target_values)
+
# error: Argument 1 to "ExtensionEngine" has incompatible type
# "ndarray[Any, Any]"; expected "ExtensionArray"
return self._engine_type(target_values) # type:ignore[arg-type]
@@ -2680,7 +2687,6 @@ def _is_all_dates(self) -> bool:
"""
Whether or not the index values only consist of dates.
"""
-
if needs_i8_conversion(self.dtype):
return True
elif self.dtype != _dtype_obj:
@@ -7302,7 +7308,7 @@ def _maybe_cast_data_without_dtype(
FutureWarning,
stacklevel=3,
)
- if result.dtype.kind in ["b", "c"]:
+ if result.dtype.kind in ["c"]:
return subarr
result = ensure_wrapped_if_datetimelike(result)
return result
diff --git a/pandas/core/tools/datetimes.py b/pandas/core/tools/datetimes.py
index 4d9420fc0510d..3f0a5deb97548 100644
--- a/pandas/core/tools/datetimes.py
+++ b/pandas/core/tools/datetimes.py
@@ -1076,6 +1076,8 @@ def to_datetime(
result = convert_listlike(arg, format)
else:
result = convert_listlike(np.array([arg]), format)[0]
+ if isinstance(arg, bool) and isinstance(result, np.bool_):
+ result = bool(result) # TODO: avoid this kludge.
# error: Incompatible return value type (got "Union[Timestamp, NaTType,
# Series, Index]", expected "Union[DatetimeIndex, Series, float, str,
diff --git a/pandas/core/util/hashing.py b/pandas/core/util/hashing.py
index 892fa83f98755..79db60ab5a7ce 100644
--- a/pandas/core/util/hashing.py
+++ b/pandas/core/util/hashing.py
@@ -319,7 +319,7 @@ def _hash_ndarray(
# First, turn whatever array this is into unsigned 64-bit ints, if we can
# manage it.
- elif isinstance(dtype, bool):
+ elif dtype == bool:
vals = vals.astype("u8")
elif issubclass(dtype.type, (np.datetime64, np.timedelta64)):
vals = vals.view("i8").astype("u8", copy=False)
diff --git a/pandas/tests/base/test_value_counts.py b/pandas/tests/base/test_value_counts.py
index 13bf096cfe167..c46f1b036dbee 100644
--- a/pandas/tests/base/test_value_counts.py
+++ b/pandas/tests/base/test_value_counts.py
@@ -284,7 +284,7 @@ def test_value_counts_with_nan(dropna, index_or_series):
obj = klass(values)
res = obj.value_counts(dropna=dropna)
if dropna is True:
- expected = Series([1], index=[True])
+ expected = Series([1], index=Index([True], dtype=obj.dtype))
else:
expected = Series([1, 1, 1], index=[True, pd.NA, np.nan])
tm.assert_series_equal(res, expected)
diff --git a/pandas/tests/indexes/common.py b/pandas/tests/indexes/common.py
index 0cf66c0814293..3f8c679c6162f 100644
--- a/pandas/tests/indexes/common.py
+++ b/pandas/tests/indexes/common.py
@@ -216,6 +216,8 @@ def test_ensure_copied_data(self, index):
# RangeIndex cannot be initialized from data
# MultiIndex and CategoricalIndex are tested separately
return
+ elif index.dtype == object and index.inferred_type == "boolean":
+ init_kwargs["dtype"] = index.dtype
index_type = type(index)
result = index_type(index.values, copy=True, **init_kwargs)
@@ -522,6 +524,9 @@ def test_fillna(self, index):
# GH 11343
if len(index) == 0:
return
+ elif index.dtype == bool:
+ # can't hold NAs
+ return
elif isinstance(index, NumericIndex) and is_integer_dtype(index.dtype):
return
elif isinstance(index, MultiIndex):
diff --git a/pandas/tests/indexes/multi/test_indexing.py b/pandas/tests/indexes/multi/test_indexing.py
index 599455b2d2ba1..9626352ac7e36 100644
--- a/pandas/tests/indexes/multi/test_indexing.py
+++ b/pandas/tests/indexes/multi/test_indexing.py
@@ -621,13 +621,22 @@ def test_get_loc_implicit_cast(self, level, dtypes):
idx = MultiIndex.from_product(levels)
assert idx.get_loc(tuple(key)) == 3
- def test_get_loc_cast_bool(self):
- # GH 19086 : int is casted to bool, but not vice-versa
- levels = [[False, True], np.arange(2, dtype="int64")]
+ @pytest.mark.parametrize("dtype", [bool, object])
+ def test_get_loc_cast_bool(self, dtype):
+ # GH 19086 : int is casted to bool, but not vice-versa (for object dtype)
+ # With bool dtype, we don't cast in either direction.
+ levels = [Index([False, True], dtype=dtype), np.arange(2, dtype="int64")]
idx = MultiIndex.from_product(levels)
- assert idx.get_loc((0, 1)) == 1
- assert idx.get_loc((1, 0)) == 2
+ if dtype is bool:
+ with pytest.raises(KeyError, match=r"^\(0, 1\)$"):
+ assert idx.get_loc((0, 1)) == 1
+ with pytest.raises(KeyError, match=r"^\(1, 0\)$"):
+ assert idx.get_loc((1, 0)) == 2
+ else:
+ # We use python object comparisons, which treat 0 == False and 1 == True
+ assert idx.get_loc((0, 1)) == 1
+ assert idx.get_loc((1, 0)) == 2
with pytest.raises(KeyError, match=r"^\(False, True\)$"):
idx.get_loc((False, True))
diff --git a/pandas/tests/indexes/test_any_index.py b/pandas/tests/indexes/test_any_index.py
index c7aae5d69b8e3..8f0d1179a99c1 100644
--- a/pandas/tests/indexes/test_any_index.py
+++ b/pandas/tests/indexes/test_any_index.py
@@ -49,6 +49,10 @@ def test_mutability(index):
def test_map_identity_mapping(index):
# GH#12766
result = index.map(lambda x: x)
+ if index.dtype == object and result.dtype == bool:
+ assert (index == result).all()
+ # TODO: could work that into the 'exact="equiv"'?
+ return # FIXME: doesn't belong in this file anymore!
tm.assert_index_equal(result, index, exact="equiv")
diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py
index b2b902bd816c8..9df759588033f 100644
--- a/pandas/tests/indexes/test_base.py
+++ b/pandas/tests/indexes/test_base.py
@@ -321,15 +321,21 @@ def test_view_with_args(self, index):
"unicode",
"string",
pytest.param("categorical", marks=pytest.mark.xfail(reason="gh-25464")),
- "bool",
+ "bool-object",
+ "bool-dtype",
"empty",
],
indirect=True,
)
def test_view_with_args_object_array_raises(self, index):
- msg = "Cannot change data-type for object array"
- with pytest.raises(TypeError, match=msg):
- index.view("i8")
+ if index.dtype == bool:
+ msg = "When changing to a larger dtype"
+ with pytest.raises(ValueError, match=msg):
+ index.view("i8")
+ else:
+ msg = "Cannot change data-type for object array"
+ with pytest.raises(TypeError, match=msg):
+ index.view("i8")
@pytest.mark.parametrize("index", ["int", "range"], indirect=True)
def test_astype(self, index):
@@ -397,9 +403,9 @@ def test_is_(self):
def test_asof_numeric_vs_bool_raises(self):
left = Index([1, 2, 3])
- right = Index([True, False])
+ right = Index([True, False], dtype=object)
- msg = "Cannot compare dtypes int64 and object"
+ msg = "Cannot compare dtypes int64 and bool"
with pytest.raises(TypeError, match=msg):
left.asof(right[0])
# TODO: should right.asof(left[0]) also raise?
@@ -591,7 +597,8 @@ def test_append_empty_preserve_name(self, name, expected):
"index, expected",
[
("string", False),
- ("bool", False),
+ ("bool-object", False),
+ ("bool-dtype", False),
("categorical", False),
("int", True),
("datetime", False),
@@ -606,7 +613,8 @@ def test_is_numeric(self, index, expected):
"index, expected",
[
("string", True),
- ("bool", True),
+ ("bool-object", True),
+ ("bool-dtype", False),
("categorical", False),
("int", False),
("datetime", False),
@@ -621,7 +629,8 @@ def test_is_object(self, index, expected):
"index, expected",
[
("string", False),
- ("bool", False),
+ ("bool-object", False),
+ ("bool-dtype", False),
("categorical", False),
("int", False),
("datetime", True),
diff --git a/pandas/tests/indexes/test_common.py b/pandas/tests/indexes/test_common.py
index 6159c53ea5bf4..ce5166aa8cf0b 100644
--- a/pandas/tests/indexes/test_common.py
+++ b/pandas/tests/indexes/test_common.py
@@ -88,7 +88,9 @@ def test_constructor_non_hashable_name(self, index_flat):
def test_constructor_unwraps_index(self, index_flat):
a = index_flat
- b = type(a)(a)
+ # Passing dtype is necessary for Index([True, False], dtype=object)
+ # case.
+ b = type(a)(a, dtype=a.dtype)
tm.assert_equal(a._data, b._data)
def test_to_flat_index(self, index_flat):
@@ -426,6 +428,9 @@ def test_hasnans_isnans(self, index_flat):
return
elif isinstance(index, NumericIndex) and is_integer_dtype(index.dtype):
return
+ elif index.dtype == bool:
+ # values[1] = np.nan below casts to True!
+ return
values[1] = np.nan
diff --git a/pandas/tests/indexes/test_index_new.py b/pandas/tests/indexes/test_index_new.py
index 5f57e03ea9444..3052c9d7ee69b 100644
--- a/pandas/tests/indexes/test_index_new.py
+++ b/pandas/tests/indexes/test_index_new.py
@@ -74,7 +74,7 @@ def test_constructor_dtypes_to_object(self, cast_index, vals):
index = Index(vals)
assert type(index) is Index
- assert index.dtype == object
+ assert index.dtype == bool
def test_constructor_categorical_to_object(self):
# GH#32167 Categorical data and dtype=object should return object-dtype
diff --git a/pandas/tests/indexes/test_numpy_compat.py b/pandas/tests/indexes/test_numpy_compat.py
index 8f3ecce223afa..7d46f6d18a318 100644
--- a/pandas/tests/indexes/test_numpy_compat.py
+++ b/pandas/tests/indexes/test_numpy_compat.py
@@ -68,8 +68,10 @@ def test_numpy_ufuncs_basic(index, func):
with tm.external_error_raised((TypeError, AttributeError)):
with np.errstate(all="ignore"):
func(index)
- elif isinstance(index, NumericIndex) or (
- not isinstance(index.dtype, np.dtype) and index.dtype._is_numeric
+ elif (
+ isinstance(index, NumericIndex)
+ or (not isinstance(index.dtype, np.dtype) and index.dtype._is_numeric)
+ or index.dtype == bool
):
# coerces to float (e.g. np.sin)
with np.errstate(all="ignore"):
@@ -77,7 +79,7 @@ def test_numpy_ufuncs_basic(index, func):
exp = Index(func(index.values), name=index.name)
tm.assert_index_equal(result, exp)
- if type(index) is not Index:
+ if type(index) is not Index or index.dtype == bool:
# i.e NumericIndex
assert isinstance(result, Float64Index)
else:
@@ -117,8 +119,10 @@ def test_numpy_ufuncs_other(index, func):
with tm.external_error_raised(TypeError):
func(index)
- elif isinstance(index, NumericIndex) or (
- not isinstance(index.dtype, np.dtype) and index.dtype._is_numeric
+ elif (
+ isinstance(index, NumericIndex)
+ or (not isinstance(index.dtype, np.dtype) and index.dtype._is_numeric)
+ or index.dtype == bool
):
# Results in bool array
result = func(index)
diff --git a/pandas/tests/indexes/test_setops.py b/pandas/tests/indexes/test_setops.py
index bad75b7429efb..f4f572d8f79fc 100644
--- a/pandas/tests/indexes/test_setops.py
+++ b/pandas/tests/indexes/test_setops.py
@@ -9,7 +9,6 @@
import pytest
from pandas.core.dtypes.cast import find_common_type
-from pandas.core.dtypes.common import is_dtype_equal
from pandas import (
CategoricalIndex,
@@ -55,14 +54,20 @@ def test_union_different_types(index_flat, index_flat2, request):
if (
not idx1.is_unique
+ and not idx2.is_unique
+ and not idx2.is_monotonic_decreasing
and idx1.dtype.kind == "i"
- and is_dtype_equal(idx2.dtype, "boolean")
+ and idx2.dtype.kind == "b"
) or (
not idx2.is_unique
+ and not idx1.is_unique
+ and not idx1.is_monotonic_decreasing
and idx2.dtype.kind == "i"
- and is_dtype_equal(idx1.dtype, "boolean")
+ and idx1.dtype.kind == "b"
):
- mark = pytest.mark.xfail(reason="GH#44000 True==1", raises=ValueError)
+ mark = pytest.mark.xfail(
+ reason="GH#44000 True==1", raises=ValueError, strict=False
+ )
request.node.add_marker(mark)
common_dtype = find_common_type([idx1.dtype, idx2.dtype])
@@ -231,7 +236,11 @@ def test_union_base(self, index):
def test_difference_base(self, sort, index):
first = index[2:]
second = index[:4]
- if isinstance(index, CategoricalIndex) or index.is_boolean():
+ if index.is_boolean():
+ # i think (TODO: be sure) there assumptions baked in about
+ # the index fixture that don't hold here?
+ answer = set(first).difference(set(second))
+ elif isinstance(index, CategoricalIndex):
answer = []
else:
answer = index[4:]
diff --git a/pandas/tests/reshape/concat/test_append_common.py b/pandas/tests/reshape/concat/test_append_common.py
index 36bca1c2b654e..0a330fd12d76d 100644
--- a/pandas/tests/reshape/concat/test_append_common.py
+++ b/pandas/tests/reshape/concat/test_append_common.py
@@ -61,10 +61,7 @@ def _check_expected_dtype(self, obj, label):
considering not-supported dtypes
"""
if isinstance(obj, Index):
- if label == "bool":
- assert obj.dtype == "object"
- else:
- assert obj.dtype == label
+ assert obj.dtype == label
elif isinstance(obj, Series):
if label.startswith("period"):
assert obj.dtype == "Period[M]"
@@ -185,7 +182,7 @@ def test_concatlike_same_dtypes(self, item):
with pytest.raises(TypeError, match=msg):
pd.concat([Series(vals1), Series(vals2), vals3])
- def test_concatlike_dtypes_coercion(self, item, item2):
+ def test_concatlike_dtypes_coercion(self, item, item2, request):
# GH 13660
typ1, vals1 = item
typ2, vals2 = item2
@@ -210,9 +207,13 @@ def test_concatlike_dtypes_coercion(self, item, item2):
# series coerces to numeric based on numpy rule
# index doesn't because bool is object dtype
exp_series_dtype = typ2
+ mark = pytest.mark.xfail(reason="GH#39187 casting to object")
+ request.node.add_marker(mark)
warn = FutureWarning
elif typ2 == "bool" and typ1 in ("int64", "float64"):
exp_series_dtype = typ1
+ mark = pytest.mark.xfail(reason="GH#39187 casting to object")
+ request.node.add_marker(mark)
warn = FutureWarning
elif (
typ1 == "datetime64[ns, US/Eastern]"
@@ -229,7 +230,9 @@ def test_concatlike_dtypes_coercion(self, item, item2):
# ----- Index ----- #
# index.append
- res = Index(vals1).append(Index(vals2))
+ with tm.assert_produces_warning(warn, match="concatenating bool-dtype"):
+ # GH#39817
+ res = Index(vals1).append(Index(vals2))
exp = Index(exp_data, dtype=exp_index_dtype)
tm.assert_index_equal(res, exp)
diff --git a/pandas/tests/series/indexing/test_setitem.py b/pandas/tests/series/indexing/test_setitem.py
index a994c902f0b16..6a48d452f9624 100644
--- a/pandas/tests/series/indexing/test_setitem.py
+++ b/pandas/tests/series/indexing/test_setitem.py
@@ -758,26 +758,26 @@ def test_series_where(self, obj, key, expected, val, is_inplace):
self._check_inplace(is_inplace, orig, arr, obj)
def test_index_where(self, obj, key, expected, val):
- if obj.dtype == bool or obj.dtype.kind == "c" or expected.dtype.kind == "c":
- # TODO(GH#45061): Should become unreachable (at least the bool part)
+ if obj.dtype.kind == "c" or expected.dtype.kind == "c":
+ # TODO(Index[complex]): Should become unreachable
pytest.skip("test not applicable for this dtype")
mask = np.zeros(obj.shape, dtype=bool)
mask[key] = True
res = Index(obj).where(~mask, val)
- tm.assert_index_equal(res, Index(expected))
+ tm.assert_index_equal(res, Index(expected, dtype=expected.dtype))
def test_index_putmask(self, obj, key, expected, val):
- if obj.dtype == bool or obj.dtype.kind == "c" or expected.dtype.kind == "c":
- # TODO(GH#45061): Should become unreachable (at least the bool part)
+ if obj.dtype.kind == "c" or expected.dtype.kind == "c":
+ # TODO(Index[complex]): Should become unreachable
pytest.skip("test not applicable for this dtype")
mask = np.zeros(obj.shape, dtype=bool)
mask[key] = True
res = Index(obj).putmask(mask, val)
- tm.assert_index_equal(res, Index(expected))
+ tm.assert_index_equal(res, Index(expected, dtype=expected.dtype))
@pytest.mark.parametrize(
diff --git a/pandas/tests/series/methods/test_drop.py b/pandas/tests/series/methods/test_drop.py
index 59a60019bb1c1..a625e890393a6 100644
--- a/pandas/tests/series/methods/test_drop.py
+++ b/pandas/tests/series/methods/test_drop.py
@@ -54,7 +54,8 @@ def test_drop_with_ignore_errors():
# GH 8522
ser = Series([2, 3], index=[True, False])
- assert ser.index.is_object()
+ assert not ser.index.is_object()
+ assert ser.index.dtype == bool
result = ser.drop(True)
expected = Series([3], index=[False])
tm.assert_series_equal(result, expected)
diff --git a/pandas/tests/series/methods/test_value_counts.py b/pandas/tests/series/methods/test_value_counts.py
index c914dba75dc35..1977bf88481a5 100644
--- a/pandas/tests/series/methods/test_value_counts.py
+++ b/pandas/tests/series/methods/test_value_counts.py
@@ -194,7 +194,7 @@ def test_value_counts_categorical_with_nan(self):
(
Series([False, True, True, pd.NA]),
True,
- Series([2, 1], index=[True, False]),
+ Series([2, 1], index=pd.Index([True, False], dtype=object)),
),
(
Series(range(3), index=[True, False, np.nan]).index,
diff --git a/pandas/tests/series/test_logical_ops.py b/pandas/tests/series/test_logical_ops.py
index 9648b01492e02..38e3c5ec8a6f2 100644
--- a/pandas/tests/series/test_logical_ops.py
+++ b/pandas/tests/series/test_logical_ops.py
@@ -268,7 +268,9 @@ def test_logical_ops_with_index(self, op):
def test_reversed_xor_with_index_returns_index(self):
# GH#22092, GH#19792
ser = Series([True, True, False, False])
- idx1 = Index([True, False, True, False])
+ idx1 = Index(
+ [True, False, True, False], dtype=object
+ ) # TODO: raises if bool-dtype
idx2 = Index([1, 0, 1, 0])
msg = "operating as a set operation"
@@ -325,7 +327,7 @@ def test_reversed_logical_op_with_index_returns_series(self, op):
[
(ops.rand_, Index([False, True])),
(ops.ror_, Index([False, True])),
- (ops.rxor, Index([])),
+ (ops.rxor, Index([], dtype=bool)),
],
)
def test_reverse_ops_with_index(self, op, expected):
diff --git a/pandas/tests/test_algos.py b/pandas/tests/test_algos.py
index 749ed1bb979e9..84e8d72711305 100644
--- a/pandas/tests/test_algos.py
+++ b/pandas/tests/test_algos.py
@@ -56,6 +56,12 @@ def test_factorize(self, index_or_series_obj, sort):
if isinstance(obj, MultiIndex):
constructor = MultiIndex.from_tuples
expected_uniques = constructor(obj.unique())
+ if (
+ isinstance(obj, Index)
+ and expected_uniques.dtype == bool
+ and obj.dtype == object
+ ):
+ expected_uniques = expected_uniques.astype(object)
if sort:
expected_uniques = expected_uniques.sort_values()
@@ -1240,7 +1246,7 @@ def test_dropna(self):
tm.assert_series_equal(
Series([True] * 3 + [False] * 2 + [None] * 5).value_counts(dropna=True),
- Series([3, 2], index=[True, False]),
+ Series([3, 2], index=Index([True, False], dtype=object)),
)
tm.assert_series_equal(
Series([True] * 5 + [False] * 3 + [None] * 2).value_counts(dropna=False),