Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/pandas/core/ops/missing.py : 12%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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"""
24import operator
26import numpy as np
28from pandas.core.dtypes.common import is_float_dtype, is_integer_dtype, is_scalar
30from pandas.core.ops.roperator import rdivmod, rfloordiv, rmod
33def fill_zeros(result, x, y):
34 """
35 If this is a reversed op, then flip x,y
37 If we have an integer value (or array in y)
38 and we have 0's, fill them with np.nan,
39 return the result.
41 Mask the nan's from x.
42 """
43 if is_float_dtype(result.dtype):
44 return result
46 is_variable_type = hasattr(y, "dtype") or hasattr(y, "type")
47 is_scalar_type = is_scalar(y)
49 if not is_variable_type and not is_scalar_type:
50 return result
52 if is_scalar_type:
53 y = np.array(y)
55 if is_integer_dtype(y.dtype):
57 if (y == 0).any():
59 # GH#7325, mask and nans must be broadcastable (also: GH#9308)
60 # Raveling and then reshaping makes np.putmask faster
61 mask = ((y == 0) & ~np.isnan(result)).ravel()
63 shape = result.shape
64 result = result.astype("float64", copy=False).ravel()
66 np.putmask(result, mask, np.nan)
68 result = result.reshape(shape)
70 return result
73def mask_zero_div_zero(x, y, result):
74 """
75 Set results of 0 / 0 or 0 // 0 to np.nan, regardless of the dtypes
76 of the numerator or the denominator.
78 Parameters
79 ----------
80 x : ndarray
81 y : ndarray
82 result : ndarray
84 Returns
85 -------
86 filled_result : ndarray
88 Examples
89 --------
90 >>> x = np.array([1, 0, -1], dtype=np.int64)
91 >>> y = 0 # int 0; numpy behavior is different with float
92 >>> result = x / y
93 >>> result # raw numpy result does not fill division by zero
94 array([0, 0, 0])
95 >>> mask_zero_div_zero(x, y, result)
96 array([ inf, nan, -inf])
97 """
98 if not isinstance(result, np.ndarray):
99 # FIXME: SparseArray would raise TypeError with np.putmask
100 return result
102 if is_scalar(y):
103 y = np.array(y)
105 zmask = y == 0
107 if isinstance(zmask, bool):
108 # FIXME: numpy did not evaluate pointwise, seen in docs build
109 return result
111 if zmask.any():
112 shape = result.shape
114 # Flip sign if necessary for -0.0
115 zneg_mask = zmask & np.signbit(y)
116 zpos_mask = zmask & ~zneg_mask
118 nan_mask = (zmask & (x == 0)).ravel()
119 with np.errstate(invalid="ignore"):
120 neginf_mask = ((zpos_mask & (x < 0)) | (zneg_mask & (x > 0))).ravel()
121 posinf_mask = ((zpos_mask & (x > 0)) | (zneg_mask & (x < 0))).ravel()
123 if nan_mask.any() or neginf_mask.any() or posinf_mask.any():
124 # Fill negative/0 with -inf, positive/0 with +inf, 0/0 with NaN
125 result = result.astype("float64", copy=False).ravel()
127 np.putmask(result, nan_mask, np.nan)
128 np.putmask(result, posinf_mask, np.inf)
129 np.putmask(result, neginf_mask, -np.inf)
131 result = result.reshape(shape)
133 return result
136def dispatch_fill_zeros(op, left, right, result):
137 """
138 Call fill_zeros with the appropriate fill value depending on the operation,
139 with special logic for divmod and rdivmod.
141 Parameters
142 ----------
143 op : function (operator.add, operator.div, ...)
144 left : object (np.ndarray for non-reversed ops)
145 right : object (np.ndarray for reversed ops)
146 result : ndarray
148 Returns
149 -------
150 result : np.ndarray
152 Notes
153 -----
154 For divmod and rdivmod, the `result` parameter and returned `result`
155 is a 2-tuple of ndarray objects.
156 """
157 if op is divmod:
158 result = (
159 mask_zero_div_zero(left, right, result[0]),
160 fill_zeros(result[1], left, right),
161 )
162 elif op is rdivmod:
163 result = (
164 mask_zero_div_zero(right, left, result[0]),
165 fill_zeros(result[1], right, left),
166 )
167 elif op is operator.floordiv:
168 # Note: no need to do this for truediv; in py3 numpy behaves the way
169 # we want.
170 result = mask_zero_div_zero(left, right, result)
171 elif op is rfloordiv:
172 # Note: no need to do this for rtruediv; in py3 numpy behaves the way
173 # we want.
174 result = mask_zero_div_zero(right, left, result)
175 elif op is operator.mod:
176 result = fill_zeros(result, left, right)
177 elif op is rmod:
178 result = fill_zeros(result, right, left)
179 return result