Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/pandas/core/ops/missing.py: 9%
56 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1"""
2Missing data handling for arithmetic operations.
4In particular, pandas conventions regarding division by zero differ
5from numpy in the following ways:
6 1) np.array([-1, 0, 1], dtype=dtype1) // np.array([0, 0, 0], dtype=dtype2)
7 gives [nan, nan, nan] for most dtype combinations, and [0, 0, 0] for
8 the remaining pairs
9 (the remaining being dtype1==dtype2==intN and dtype==dtype2==uintN).
11 pandas convention is to return [-inf, nan, inf] for all dtype
12 combinations.
14 Note: the numpy behavior described here is py3-specific.
16 2) np.array([-1, 0, 1], dtype=dtype1) % np.array([0, 0, 0], dtype=dtype2)
17 gives precisely the same results as the // operation.
19 pandas convention is to return [nan, nan, nan] for all dtype
20 combinations.
22 3) divmod behavior consistent with 1) and 2).
23"""
24from __future__ import annotations
26import operator
28import numpy as np
30from pandas.core.dtypes.common import (
31 is_float_dtype,
32 is_integer_dtype,
33 is_scalar,
34)
36from pandas.core.ops import roperator
39def _fill_zeros(result, x, y):
40 """
41 If this is a reversed op, then flip x,y
43 If we have an integer value (or array in y)
44 and we have 0's, fill them with np.nan,
45 return the result.
47 Mask the nan's from x.
48 """
49 if is_float_dtype(result.dtype):
50 return result
52 is_variable_type = hasattr(y, "dtype")
53 is_scalar_type = is_scalar(y)
55 if not is_variable_type and not is_scalar_type:
56 return result
58 if is_scalar_type:
59 y = np.array(y)
61 if is_integer_dtype(y.dtype):
63 ymask = y == 0
64 if ymask.any():
66 # GH#7325, mask and nans must be broadcastable
67 mask = ymask & ~np.isnan(result)
69 # GH#9308 doing ravel on result and mask can improve putmask perf,
70 # but can also make unwanted copies.
71 result = result.astype("float64", copy=False)
73 np.putmask(result, mask, np.nan)
75 return result
78def mask_zero_div_zero(x, y, result: np.ndarray) -> np.ndarray:
79 """
80 Set results of 0 // 0 to np.nan, regardless of the dtypes
81 of the numerator or the denominator.
83 Parameters
84 ----------
85 x : ndarray
86 y : ndarray
87 result : ndarray
89 Returns
90 -------
91 ndarray
92 The filled result.
94 Examples
95 --------
96 >>> x = np.array([1, 0, -1], dtype=np.int64)
97 >>> x
98 array([ 1, 0, -1])
99 >>> y = 0 # int 0; numpy behavior is different with float
100 >>> result = x // y
101 >>> result # raw numpy result does not fill division by zero
102 array([0, 0, 0])
103 >>> mask_zero_div_zero(x, y, result)
104 array([ inf, nan, -inf])
105 """
107 if not hasattr(y, "dtype"):
108 # e.g. scalar, tuple
109 y = np.array(y)
110 if not hasattr(x, "dtype"):
111 # e.g scalar, tuple
112 x = np.array(x)
114 zmask = y == 0
116 if zmask.any():
118 # Flip sign if necessary for -0.0
119 zneg_mask = zmask & np.signbit(y)
120 zpos_mask = zmask & ~zneg_mask
122 x_lt0 = x < 0
123 x_gt0 = x > 0
124 nan_mask = zmask & (x == 0)
125 with np.errstate(invalid="ignore"):
126 neginf_mask = (zpos_mask & x_lt0) | (zneg_mask & x_gt0)
127 posinf_mask = (zpos_mask & x_gt0) | (zneg_mask & x_lt0)
129 if nan_mask.any() or neginf_mask.any() or posinf_mask.any():
130 # Fill negative/0 with -inf, positive/0 with +inf, 0/0 with NaN
131 result = result.astype("float64", copy=False)
133 result[nan_mask] = np.nan
134 result[posinf_mask] = np.inf
135 result[neginf_mask] = -np.inf
137 return result
140def dispatch_fill_zeros(op, left, right, result):
141 """
142 Call _fill_zeros with the appropriate fill value depending on the operation,
143 with special logic for divmod and rdivmod.
145 Parameters
146 ----------
147 op : function (operator.add, operator.div, ...)
148 left : object (np.ndarray for non-reversed ops)
149 right : object (np.ndarray for reversed ops)
150 result : ndarray
152 Returns
153 -------
154 result : np.ndarray
156 Notes
157 -----
158 For divmod and rdivmod, the `result` parameter and returned `result`
159 is a 2-tuple of ndarray objects.
160 """
161 if op is divmod:
162 result = (
163 mask_zero_div_zero(left, right, result[0]),
164 _fill_zeros(result[1], left, right),
165 )
166 elif op is roperator.rdivmod:
167 result = (
168 mask_zero_div_zero(right, left, result[0]),
169 _fill_zeros(result[1], right, left),
170 )
171 elif op is operator.floordiv:
172 # Note: no need to do this for truediv; in py3 numpy behaves the way
173 # we want.
174 result = mask_zero_div_zero(left, right, result)
175 elif op is roperator.rfloordiv:
176 # Note: no need to do this for rtruediv; in py3 numpy behaves the way
177 # we want.
178 result = mask_zero_div_zero(right, left, result)
179 elif op is operator.mod:
180 result = _fill_zeros(result, left, right)
181 elif op is roperator.rmod:
182 result = _fill_zeros(result, right, left)
183 return result