59
59
deprecate_nonkeyword_arguments ,
60
60
doc ,
61
61
)
62
- from pandas .util ._exceptions import find_stack_level
62
+ from pandas .util ._exceptions import (
63
+ find_stack_level ,
64
+ rewrite_exception ,
65
+ )
63
66
64
67
from pandas .core .dtypes .cast import (
65
68
can_hold_element ,
@@ -985,20 +988,40 @@ def astype(self, dtype, copy=True):
985
988
dtype = pandas_dtype (dtype )
986
989
987
990
if is_dtype_equal (self .dtype , dtype ):
991
+ # Ensure that self.astype(self.dtype) is self
988
992
return self .copy () if copy else self
989
993
994
+ if (
995
+ self .dtype == np .dtype ("M8[ns]" )
996
+ and isinstance (dtype , np .dtype )
997
+ and dtype .kind == "M"
998
+ and dtype != np .dtype ("M8[ns]" )
999
+ ):
1000
+ # For now DatetimeArray supports this by unwrapping ndarray,
1001
+ # but DatetimeIndex doesn't
1002
+ raise TypeError (f"Cannot cast { type (self ).__name__ } to dtype" )
1003
+
1004
+ values = self ._data
1005
+ if isinstance (values , ExtensionArray ):
1006
+ with rewrite_exception (type (values ).__name__ , type (self ).__name__ ):
1007
+ new_values = values .astype (dtype , copy = copy )
1008
+
990
1009
elif isinstance (dtype , ExtensionDtype ):
991
1010
cls = dtype .construct_array_type ()
992
- new_values = cls ._from_sequence (self , dtype = dtype , copy = False )
993
- return Index (new_values , dtype = dtype , copy = copy , name = self .name )
1011
+ # Note: for RangeIndex and CategoricalDtype self vs self._values
1012
+ # behaves differently here.
1013
+ new_values = cls ._from_sequence (self , dtype = dtype , copy = copy )
994
1014
995
- try :
996
- casted = self ._values .astype (dtype , copy = copy )
997
- except (TypeError , ValueError ) as err :
998
- raise TypeError (
999
- f"Cannot cast { type (self ).__name__ } to dtype { dtype } "
1000
- ) from err
1001
- return Index (casted , name = self .name , dtype = dtype )
1015
+ else :
1016
+ try :
1017
+ new_values = values .astype (dtype , copy = copy )
1018
+ except (TypeError , ValueError ) as err :
1019
+ raise TypeError (
1020
+ f"Cannot cast { type (self ).__name__ } to dtype { dtype } "
1021
+ ) from err
1022
+
1023
+ # pass copy=False because any copying will be done in the astype above
1024
+ return Index (new_values , name = self .name , dtype = new_values .dtype , copy = False )
1002
1025
1003
1026
_index_shared_docs [
1004
1027
"take"
@@ -4875,8 +4898,6 @@ def __getitem__(self, key):
4875
4898
corresponding `Index` subclass.
4876
4899
4877
4900
"""
4878
- # There's no custom logic to be implemented in __getslice__, so it's
4879
- # not overloaded intentionally.
4880
4901
getitem = self ._data .__getitem__
4881
4902
4882
4903
if is_scalar (key ):
@@ -4885,25 +4906,32 @@ def __getitem__(self, key):
4885
4906
4886
4907
if isinstance (key , slice ):
4887
4908
# This case is separated from the conditional above to avoid
4888
- # pessimization of basic indexing .
4909
+ # pessimization com.is_bool_indexer and ndim checks .
4889
4910
result = getitem (key )
4890
4911
# Going through simple_new for performance.
4891
4912
return type (self )._simple_new (result , name = self ._name )
4892
4913
4893
4914
if com .is_bool_indexer (key ):
4915
+ # if we have list[bools, length=1e5] then doing this check+convert
4916
+ # takes 166 µs + 2.1 ms and cuts the ndarray.__getitem__
4917
+ # time below from 3.8 ms to 496 µs
4918
+ # if we already have ndarray[bool], the overhead is 1.4 µs or .25%
4894
4919
key = np .asarray (key , dtype = bool )
4895
4920
4896
4921
result = getitem (key )
4897
- if not is_scalar (result ):
4898
- if np .ndim (result ) > 1 :
4899
- deprecate_ndim_indexing (result )
4900
- return result
4901
- # NB: Using _constructor._simple_new would break if MultiIndex
4902
- # didn't override __getitem__
4903
- return self ._constructor ._simple_new (result , name = self ._name )
4904
- else :
4922
+ # Because we ruled out integer above, we always get an arraylike here
4923
+ if result .ndim > 1 :
4924
+ deprecate_ndim_indexing (result )
4925
+ if hasattr (result , "_ndarray" ):
4926
+ # i.e. NDArrayBackedExtensionArray
4927
+ # Unpack to ndarray for MPL compat
4928
+ return result ._ndarray
4905
4929
return result
4906
4930
4931
+ # NB: Using _constructor._simple_new would break if MultiIndex
4932
+ # didn't override __getitem__
4933
+ return self ._constructor ._simple_new (result , name = self ._name )
4934
+
4907
4935
def _getitem_slice (self : _IndexT , slobj : slice ) -> _IndexT :
4908
4936
"""
4909
4937
Fastpath for __getitem__ when we know we have a slice.
0 commit comments