@@ -1143,6 +1143,7 @@ def na_op(x, y):
1143
1143
result [mask ] = op (x [mask ], com .values_from_object (y [mask ]))
1144
1144
else :
1145
1145
assert isinstance (x , np .ndarray )
1146
+ assert is_scalar (y )
1146
1147
result = np .empty (len (x ), dtype = x .dtype )
1147
1148
mask = notna (x )
1148
1149
result [mask ] = op (x [mask ], y )
@@ -1189,6 +1190,7 @@ def wrapper(left, right):
1189
1190
1190
1191
elif (is_extension_array_dtype (left ) or
1191
1192
is_extension_array_dtype (right )):
1193
+ # TODO: should this include `not is_scalar(right)`?
1192
1194
return dispatch_to_extension_op (op , left , right )
1193
1195
1194
1196
elif is_datetime64_dtype (left ) or is_datetime64tz_dtype (left ):
@@ -1278,13 +1280,11 @@ def na_op(x, y):
1278
1280
# should have guarantess on what x, y can be type-wise
1279
1281
# Extension Dtypes are not called here
1280
1282
1281
- # dispatch to the categorical if we have a categorical
1282
- # in either operand
1283
- if is_categorical_dtype (y ) and not is_scalar (y ):
1284
- # The `not is_scalar(y)` check excludes the string "category"
1285
- return op (y , x )
1283
+ # Checking that cases that were once handled here are no longer
1284
+ # reachable.
1285
+ assert not (is_categorical_dtype (y ) and not is_scalar (y ))
1286
1286
1287
- elif is_object_dtype (x .dtype ):
1287
+ if is_object_dtype (x .dtype ):
1288
1288
result = _comp_method_OBJECT_ARRAY (op , x , y )
1289
1289
1290
1290
elif is_datetimelike_v_numeric (x , y ):
@@ -1342,7 +1342,7 @@ def wrapper(self, other, axis=None):
1342
1342
return self ._constructor (res_values , index = self .index ,
1343
1343
name = res_name )
1344
1344
1345
- if is_datetime64_dtype (self ) or is_datetime64tz_dtype (self ):
1345
+ elif is_datetime64_dtype (self ) or is_datetime64tz_dtype (self ):
1346
1346
# Dispatch to DatetimeIndex to ensure identical
1347
1347
# Series/Index behavior
1348
1348
if (isinstance (other , datetime .date ) and
@@ -1384,8 +1384,9 @@ def wrapper(self, other, axis=None):
1384
1384
name = res_name )
1385
1385
1386
1386
elif (is_extension_array_dtype (self ) or
1387
- (is_extension_array_dtype (other ) and
1388
- not is_scalar (other ))):
1387
+ (is_extension_array_dtype (other ) and not is_scalar (other ))):
1388
+ # Note: the `not is_scalar(other)` condition rules out
1389
+ # e.g. other == "category"
1389
1390
return dispatch_to_extension_op (op , self , other )
1390
1391
1391
1392
elif isinstance (other , ABCSeries ):
@@ -1408,13 +1409,6 @@ def wrapper(self, other, axis=None):
1408
1409
# is not.
1409
1410
return result .__finalize__ (self ).rename (res_name )
1410
1411
1411
- elif isinstance (other , pd .Categorical ):
1412
- # ordering of checks matters; by this point we know
1413
- # that not is_categorical_dtype(self)
1414
- res_values = op (self .values , other )
1415
- return self ._constructor (res_values , index = self .index ,
1416
- name = res_name )
1417
-
1418
1412
elif is_scalar (other ) and isna (other ):
1419
1413
# numpy does not like comparisons vs None
1420
1414
if op is operator .ne :
@@ -1544,6 +1538,41 @@ def flex_wrapper(self, other, level=None, fill_value=None, axis=0):
1544
1538
# -----------------------------------------------------------------------------
1545
1539
# DataFrame
1546
1540
1541
+ def dispatch_to_series (left , right , func ):
1542
+ """
1543
+ Evaluate the frame operation func(left, right) by evaluating
1544
+ column-by-column, dispatching to the Series implementation.
1545
+
1546
+ Parameters
1547
+ ----------
1548
+ left : DataFrame
1549
+ right : scalar or DataFrame
1550
+ func : arithmetic or comparison operator
1551
+
1552
+ Returns
1553
+ -------
1554
+ DataFrame
1555
+ """
1556
+ # Note: we use iloc to access columns for compat with cases
1557
+ # with non-unique columns.
1558
+ if lib .is_scalar (right ):
1559
+ new_data = {i : func (left .iloc [:, i ], right )
1560
+ for i in range (len (left .columns ))}
1561
+ elif isinstance (right , ABCDataFrame ):
1562
+ assert right ._indexed_same (left )
1563
+ new_data = {i : func (left .iloc [:, i ], right .iloc [:, i ])
1564
+ for i in range (len (left .columns ))}
1565
+ else :
1566
+ # Remaining cases have less-obvious dispatch rules
1567
+ raise NotImplementedError
1568
+
1569
+ result = left ._constructor (new_data , index = left .index , copy = False )
1570
+ # Pin columns instead of passing to constructor for compat with
1571
+ # non-unique columns case
1572
+ result .columns = left .columns
1573
+ return result
1574
+
1575
+
1547
1576
def _combine_series_frame (self , other , func , fill_value = None , axis = None ,
1548
1577
level = None , try_cast = True ):
1549
1578
"""
0 commit comments