@@ -471,7 +471,9 @@ def __new__(
471
471
arr = com .asarray_tuplesafe (data , dtype = np .dtype ("object" ))
472
472
473
473
if dtype is None :
474
- arr = _maybe_cast_data_without_dtype (arr )
474
+ arr = _maybe_cast_data_without_dtype (
475
+ arr , cast_numeric_deprecated = True
476
+ )
475
477
dtype = arr .dtype
476
478
477
479
if kwargs :
@@ -504,6 +506,15 @@ def __new__(
504
506
# other iterable of some kind
505
507
506
508
subarr = com .asarray_tuplesafe (data , dtype = np .dtype ("object" ))
509
+ if dtype is None :
510
+ # with e.g. a list [1, 2, 3] casting to numeric is _not_ deprecated
511
+ # error: Incompatible types in assignment (expression has type
512
+ # "Union[ExtensionArray, ndarray[Any, Any]]", variable has type
513
+ # "ndarray[Any, Any]")
514
+ subarr = _maybe_cast_data_without_dtype ( # type: ignore[assignment]
515
+ subarr , cast_numeric_deprecated = False
516
+ )
517
+ dtype = subarr .dtype
507
518
return Index (subarr , dtype = dtype , copy = copy , name = name , ** kwargs )
508
519
509
520
@classmethod
@@ -637,6 +648,26 @@ def _simple_new(cls: type[_IndexT], values, name: Hashable = None) -> _IndexT:
637
648
638
649
return result
639
650
651
+ @classmethod
652
+ def _with_infer (cls , * args , ** kwargs ):
653
+ """
654
+ Constructor that uses the 1.0.x behavior inferring numeric dtypes
655
+ for ndarray[object] inputs.
656
+ """
657
+ with warnings .catch_warnings ():
658
+ warnings .filterwarnings ("ignore" , ".*the Index constructor" , FutureWarning )
659
+ result = cls (* args , ** kwargs )
660
+
661
+ if result .dtype == object and not result ._is_multi :
662
+ # error: Argument 1 to "maybe_convert_objects" has incompatible type
663
+ # "Union[ExtensionArray, ndarray[Any, Any]]"; expected
664
+ # "ndarray[Any, Any]"
665
+ values = lib .maybe_convert_objects (result ._values ) # type: ignore[arg-type]
666
+ if values .dtype .kind in ["i" , "u" , "f" ]:
667
+ return Index (values , name = result .name )
668
+
669
+ return result
670
+
640
671
@cache_readonly
641
672
def _constructor (self : _IndexT ) -> type [_IndexT ]:
642
673
return type (self )
@@ -2609,7 +2640,7 @@ def fillna(self, value=None, downcast=None):
2609
2640
if downcast is None :
2610
2641
# no need to care metadata other than name
2611
2642
# because it can't have freq if
2612
- return Index (result , name = self .name )
2643
+ return Index . _with_infer (result , name = self .name )
2613
2644
return self ._view ()
2614
2645
2615
2646
def dropna (self : _IndexT , how : str_t = "any" ) -> _IndexT :
@@ -4009,7 +4040,7 @@ def _reindex_non_unique(
4009
4040
if isinstance (self , ABCMultiIndex ):
4010
4041
new_index = type (self ).from_tuples (new_labels , names = self .names )
4011
4042
else :
4012
- new_index = Index (new_labels , name = self .name )
4043
+ new_index = Index . _with_infer (new_labels , name = self .name )
4013
4044
return new_index , indexer , new_indexer
4014
4045
4015
4046
# --------------------------------------------------------------------
@@ -4450,9 +4481,12 @@ def _wrap_joined_index(self: _IndexT, joined: ArrayLike, other: _IndexT) -> _Ind
4450
4481
4451
4482
if isinstance (self , ABCMultiIndex ):
4452
4483
name = self .names if self .names == other .names else None
4484
+ # error: Incompatible return value type (got "MultiIndex",
4485
+ # expected "_IndexT")
4486
+ return self ._constructor (joined , name = name ) # type: ignore[return-value]
4453
4487
else :
4454
4488
name = get_op_result_name (self , other )
4455
- return self ._constructor (joined , name = name )
4489
+ return self ._constructor . _with_infer (joined , name = name )
4456
4490
4457
4491
# --------------------------------------------------------------------
4458
4492
# Uncategorized Methods
@@ -4805,7 +4839,7 @@ def _concat(self, to_concat: list[Index], name: Hashable) -> Index:
4805
4839
to_concat_vals = [x ._values for x in to_concat ]
4806
4840
4807
4841
result = concat_compat (to_concat_vals )
4808
- return Index (result , name = name )
4842
+ return Index . _with_infer (result , name = name )
4809
4843
4810
4844
def putmask (self , mask , value ) -> Index :
4811
4845
"""
@@ -5752,7 +5786,7 @@ def map(self, mapper, na_action=None):
5752
5786
):
5753
5787
return self ._constructor (new_values , ** attributes )
5754
5788
5755
- return Index (new_values , ** attributes )
5789
+ return Index . _with_infer (new_values , ** attributes )
5756
5790
5757
5791
# TODO: De-duplicate with map, xref GH#32349
5758
5792
@final
@@ -6228,7 +6262,7 @@ def insert(self, loc: int, item) -> Index:
6228
6262
# Use Index constructor to ensure we get tuples cast correctly.
6229
6263
item = Index ([item ], dtype = self .dtype )._values
6230
6264
idx = np .concatenate ((arr [:loc ], item , arr [loc :]))
6231
- return Index (idx , name = self .name )
6265
+ return Index . _with_infer (idx , name = self .name )
6232
6266
6233
6267
def drop (self , labels , errors : str_t = "raise" ) -> Index :
6234
6268
"""
@@ -6313,8 +6347,8 @@ def _arith_method(self, other, op):
6313
6347
6314
6348
result = op (Series (self ), other )
6315
6349
if isinstance (result , tuple ):
6316
- return (Index (result [0 ]), Index (result [1 ]))
6317
- return Index (result )
6350
+ return (Index . _with_infer (result [0 ]), Index (result [1 ]))
6351
+ return Index . _with_infer (result )
6318
6352
6319
6353
@final
6320
6354
def _unary_method (self , op ):
@@ -6637,7 +6671,7 @@ def ensure_index(index_like: AnyArrayLike | Sequence, copy: bool = False) -> Ind
6637
6671
6638
6672
if isinstance (index_like , ABCSeries ):
6639
6673
name = index_like .name
6640
- return Index (index_like , name = name , copy = copy )
6674
+ return Index . _with_infer (index_like , name = name , copy = copy )
6641
6675
6642
6676
if is_iterator (index_like ):
6643
6677
index_like = list (index_like )
@@ -6653,10 +6687,9 @@ def ensure_index(index_like: AnyArrayLike | Sequence, copy: bool = False) -> Ind
6653
6687
6654
6688
return MultiIndex .from_arrays (index_like )
6655
6689
else :
6656
- return Index (index_like , copy = copy , tupleize_cols = False )
6690
+ return Index . _with_infer (index_like , copy = copy , tupleize_cols = False )
6657
6691
else :
6658
-
6659
- return Index (index_like , copy = copy )
6692
+ return Index ._with_infer (index_like , copy = copy )
6660
6693
6661
6694
6662
6695
def ensure_has_len (seq ):
@@ -6717,14 +6750,26 @@ def maybe_extract_name(name, obj, cls) -> Hashable:
6717
6750
return name
6718
6751
6719
6752
6720
- def _maybe_cast_data_without_dtype (subarr : np .ndarray ) -> ArrayLike :
6753
+ _cast_depr_msg = (
6754
+ "In a future version, passing an object-dtype arraylike to pd.Index will "
6755
+ "not infer numeric values to numeric dtype (matching the Series behavior). "
6756
+ "To retain the old behavior, explicitly pass the desired dtype or use the "
6757
+ "desired Index subclass"
6758
+ )
6759
+
6760
+
6761
+ def _maybe_cast_data_without_dtype (
6762
+ subarr : np .ndarray , cast_numeric_deprecated : bool = True
6763
+ ) -> ArrayLike :
6721
6764
"""
6722
6765
If we have an arraylike input but no passed dtype, try to infer
6723
6766
a supported dtype.
6724
6767
6725
6768
Parameters
6726
6769
----------
6727
6770
subarr : np.ndarray[object]
6771
+ cast_numeric_deprecated : bool, default True
6772
+ Whether to issue a FutureWarning when inferring numeric dtypes.
6728
6773
6729
6774
Returns
6730
6775
-------
@@ -6739,6 +6784,17 @@ def _maybe_cast_data_without_dtype(subarr: np.ndarray) -> ArrayLike:
6739
6784
convert_interval = True ,
6740
6785
dtype_if_all_nat = np .dtype ("datetime64[ns]" ),
6741
6786
)
6787
+ if result .dtype .kind in ["i" , "u" , "f" ]:
6788
+ if not cast_numeric_deprecated :
6789
+ # i.e. we started with a list, not an ndarray[object]
6790
+ return result
6791
+
6792
+ warnings .warn (
6793
+ "In a future version, the Index constructor will not infer numeric "
6794
+ "dtypes when passed object-dtype sequences (matching Series behavior)" ,
6795
+ FutureWarning ,
6796
+ stacklevel = 3 ,
6797
+ )
6742
6798
if result .dtype .kind in ["b" , "c" ]:
6743
6799
return subarr
6744
6800
result = ensure_wrapped_if_datetimelike (result )
0 commit comments