@@ -575,7 +575,14 @@ def _is_builtin_func(self, arg):
575575
576576 @final
577577 def _ea_wrap_cython_operation (
578- self , kind : str , values , how : str , axis : int , min_count : int = - 1 , ** kwargs
578+ self ,
579+ kind : str ,
580+ values ,
581+ how : str ,
582+ axis : int ,
583+ min_count : int = - 1 ,
584+ mask : np .ndarray | None = None ,
585+ ** kwargs ,
579586 ) -> ArrayLike :
580587 """
581588 If we have an ExtensionArray, unwrap, call _cython_operation, and
@@ -589,7 +596,7 @@ def _ea_wrap_cython_operation(
589596 # operate on the tz-naive equivalents
590597 values = values .view ("M8[ns]" )
591598 res_values = self ._cython_operation (
592- kind , values , how , axis , min_count , ** kwargs
599+ kind , values , how , axis , min_count , mask = mask , ** kwargs
593600 )
594601 if how in ["rank" ]:
595602 # preserve float64 dtype
@@ -603,7 +610,7 @@ def _ea_wrap_cython_operation(
603610 # IntegerArray or BooleanArray
604611 values = ensure_int_or_float (values )
605612 res_values = self ._cython_operation (
606- kind , values , how , axis , min_count , ** kwargs
613+ kind , values , how , axis , min_count , mask = mask , ** kwargs
607614 )
608615 dtype = maybe_cast_result_dtype (orig_values .dtype , how )
609616 if isinstance (dtype , ExtensionDtype ):
@@ -616,7 +623,7 @@ def _ea_wrap_cython_operation(
616623 # FloatingArray
617624 values = values .to_numpy (values .dtype .numpy_dtype , na_value = np .nan )
618625 res_values = self ._cython_operation (
619- kind , values , how , axis , min_count , ** kwargs
626+ kind , values , how , axis , min_count , mask = mask , ** kwargs
620627 )
621628 result = type (orig_values )._from_sequence (res_values )
622629 return result
@@ -632,7 +639,8 @@ def _masked_ea_wrap_cython_operation(
632639 values : BaseMaskedArray ,
633640 how : str ,
634641 axis : int ,
635- min_count : int = - 1 ,
642+ min_count : int ,
643+ mask : np .ndarray ,
636644 ** kwargs ,
637645 ) -> BaseMaskedArray :
638646 """
@@ -641,9 +649,6 @@ def _masked_ea_wrap_cython_operation(
641649 """
642650 orig_values = values
643651
644- # isna just directly returns self._mask, so copy here to prevent
645- # modifying the original
646- mask = isna (values ).copy ()
647652 arr = values ._data
648653
649654 if is_integer_dtype (values .dtype ) or is_bool_dtype (values .dtype ):
@@ -658,7 +663,7 @@ def _masked_ea_wrap_cython_operation(
658663 cls = dtype .construct_array_type ()
659664
660665 return cls (
661- res_values .astype (dtype .type , copy = False ), mask .astype (bool , copy = False )
666+ res_values .astype (dtype .type , copy = False ), mask .astype (bool , copy = True )
662667 )
663668
664669 @final
@@ -695,14 +700,20 @@ def _cython_operation(
695700 cy_op .disallow_invalid_ops (dtype , is_numeric )
696701
697702 func_uses_mask = cy_op .uses_mask ()
703+
704+ # Only compute the mask if we haven't yet
705+ if func_uses_mask and mask is None :
706+ mask = isna (values )
707+
698708 if is_extension_array_dtype (dtype ):
699709 if isinstance (values , BaseMaskedArray ) and func_uses_mask :
710+ assert mask is not None
700711 return self ._masked_ea_wrap_cython_operation (
701- kind , values , how , axis , min_count , ** kwargs
712+ kind , values , how , axis , min_count , mask = mask , ** kwargs
702713 )
703714 else :
704715 return self ._ea_wrap_cython_operation (
705- kind , values , how , axis , min_count , ** kwargs
716+ kind , values , how , axis , min_count , mask = mask , ** kwargs
706717 )
707718
708719 elif values .ndim == 1 :
0 commit comments