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

1""" 

2Missing data handling for arithmetic operations. 

3 

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). 

10 

11 pandas convention is to return [-inf, nan, inf] for all dtype 

12 combinations. 

13 

14 Note: the numpy behavior described here is py3-specific. 

15 

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. 

18 

19 pandas convention is to return [nan, nan, nan] for all dtype 

20 combinations. 

21 

22 3) divmod behavior consistent with 1) and 2). 

23""" 

24from __future__ import annotations 

25 

26import operator 

27 

28import numpy as np 

29 

30from pandas.core.dtypes.common import ( 

31 is_float_dtype, 

32 is_integer_dtype, 

33 is_scalar, 

34) 

35 

36from pandas.core.ops import roperator 

37 

38 

39def _fill_zeros(result, x, y): 

40 """ 

41 If this is a reversed op, then flip x,y 

42 

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. 

46 

47 Mask the nan's from x. 

48 """ 

49 if is_float_dtype(result.dtype): 

50 return result 

51 

52 is_variable_type = hasattr(y, "dtype") 

53 is_scalar_type = is_scalar(y) 

54 

55 if not is_variable_type and not is_scalar_type: 

56 return result 

57 

58 if is_scalar_type: 

59 y = np.array(y) 

60 

61 if is_integer_dtype(y.dtype): 

62 

63 ymask = y == 0 

64 if ymask.any(): 

65 

66 # GH#7325, mask and nans must be broadcastable 

67 mask = ymask & ~np.isnan(result) 

68 

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) 

72 

73 np.putmask(result, mask, np.nan) 

74 

75 return result 

76 

77 

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. 

82 

83 Parameters 

84 ---------- 

85 x : ndarray 

86 y : ndarray 

87 result : ndarray 

88 

89 Returns 

90 ------- 

91 ndarray 

92 The filled result. 

93 

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 """ 

106 

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) 

113 

114 zmask = y == 0 

115 

116 if zmask.any(): 

117 

118 # Flip sign if necessary for -0.0 

119 zneg_mask = zmask & np.signbit(y) 

120 zpos_mask = zmask & ~zneg_mask 

121 

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) 

128 

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) 

132 

133 result[nan_mask] = np.nan 

134 result[posinf_mask] = np.inf 

135 result[neginf_mask] = -np.inf 

136 

137 return result 

138 

139 

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. 

144 

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 

151 

152 Returns 

153 ------- 

154 result : np.ndarray 

155 

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