@@ -575,7 +575,14 @@ def _is_builtin_func(self, arg):
575
575
576
576
@final
577
577
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 ,
579
586
) -> ArrayLike :
580
587
"""
581
588
If we have an ExtensionArray, unwrap, call _cython_operation, and
@@ -589,7 +596,7 @@ def _ea_wrap_cython_operation(
589
596
# operate on the tz-naive equivalents
590
597
values = values .view ("M8[ns]" )
591
598
res_values = self ._cython_operation (
592
- kind , values , how , axis , min_count , ** kwargs
599
+ kind , values , how , axis , min_count , mask = mask , ** kwargs
593
600
)
594
601
if how in ["rank" ]:
595
602
# preserve float64 dtype
@@ -603,7 +610,7 @@ def _ea_wrap_cython_operation(
603
610
# IntegerArray or BooleanArray
604
611
values = ensure_int_or_float (values )
605
612
res_values = self ._cython_operation (
606
- kind , values , how , axis , min_count , ** kwargs
613
+ kind , values , how , axis , min_count , mask = mask , ** kwargs
607
614
)
608
615
dtype = maybe_cast_result_dtype (orig_values .dtype , how )
609
616
if isinstance (dtype , ExtensionDtype ):
@@ -616,7 +623,7 @@ def _ea_wrap_cython_operation(
616
623
# FloatingArray
617
624
values = values .to_numpy (values .dtype .numpy_dtype , na_value = np .nan )
618
625
res_values = self ._cython_operation (
619
- kind , values , how , axis , min_count , ** kwargs
626
+ kind , values , how , axis , min_count , mask = mask , ** kwargs
620
627
)
621
628
result = type (orig_values )._from_sequence (res_values )
622
629
return result
@@ -632,7 +639,8 @@ def _masked_ea_wrap_cython_operation(
632
639
values : BaseMaskedArray ,
633
640
how : str ,
634
641
axis : int ,
635
- min_count : int = - 1 ,
642
+ min_count : int ,
643
+ mask : np .ndarray ,
636
644
** kwargs ,
637
645
) -> BaseMaskedArray :
638
646
"""
@@ -641,9 +649,6 @@ def _masked_ea_wrap_cython_operation(
641
649
"""
642
650
orig_values = values
643
651
644
- # isna just directly returns self._mask, so copy here to prevent
645
- # modifying the original
646
- mask = isna (values ).copy ()
647
652
arr = values ._data
648
653
649
654
if is_integer_dtype (values .dtype ) or is_bool_dtype (values .dtype ):
@@ -658,7 +663,7 @@ def _masked_ea_wrap_cython_operation(
658
663
cls = dtype .construct_array_type ()
659
664
660
665
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 )
662
667
)
663
668
664
669
@final
@@ -695,14 +700,20 @@ def _cython_operation(
695
700
cy_op .disallow_invalid_ops (dtype , is_numeric )
696
701
697
702
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
+
698
708
if is_extension_array_dtype (dtype ):
699
709
if isinstance (values , BaseMaskedArray ) and func_uses_mask :
710
+ assert mask is not None
700
711
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
702
713
)
703
714
else :
704
715
return self ._ea_wrap_cython_operation (
705
- kind , values , how , axis , min_count , ** kwargs
716
+ kind , values , how , axis , min_count , mask = mask , ** kwargs
706
717
)
707
718
708
719
elif values .ndim == 1 :
0 commit comments