Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pandas/compat/numpy/function.py: 64%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

152 statements  

1""" 

2For compatibility with numpy libraries, pandas functions or methods have to 

3accept '*args' and '**kwargs' parameters to accommodate numpy arguments that 

4are not actually used or respected in the pandas implementation. 

5 

6To ensure that users do not abuse these parameters, validation is performed in 

7'validators.py' to make sure that any extra parameters passed correspond ONLY 

8to those in the numpy signature. Part of that validation includes whether or 

9not the user attempted to pass in non-default values for these extraneous 

10parameters. As we want to discourage users from relying on these parameters 

11when calling the pandas implementation, we want them only to pass in the 

12default values for these parameters. 

13 

14This module provides a set of commonly used default arguments for functions and 

15methods that are spread throughout the codebase. This module will make it 

16easier to adjust to future upstream changes in the analogous numpy signatures. 

17""" 

18 

19from __future__ import annotations 

20 

21from typing import ( 

22 TYPE_CHECKING, 

23 Any, 

24 TypeVar, 

25 cast, 

26 overload, 

27) 

28 

29import numpy as np 

30from numpy import ndarray 

31 

32from pandas._libs.lib import ( 

33 is_bool, 

34 is_integer, 

35) 

36from pandas.errors import UnsupportedFunctionCall 

37from pandas.util._validators import ( 

38 validate_args, 

39 validate_args_and_kwargs, 

40 validate_kwargs, 

41) 

42 

43if TYPE_CHECKING: 

44 from pandas._typing import ( 

45 Axis, 

46 AxisInt, 

47 ) 

48 

49 AxisNoneT = TypeVar("AxisNoneT", Axis, None) 

50 

51 

52class CompatValidator: 

53 def __init__( 

54 self, 

55 defaults, 

56 fname=None, 

57 method: str | None = None, 

58 max_fname_arg_count=None, 

59 ) -> None: 

60 self.fname = fname 

61 self.method = method 

62 self.defaults = defaults 

63 self.max_fname_arg_count = max_fname_arg_count 

64 

65 def __call__( 

66 self, 

67 args, 

68 kwargs, 

69 fname=None, 

70 max_fname_arg_count=None, 

71 method: str | None = None, 

72 ) -> None: 

73 if not args and not kwargs: 

74 return None 

75 

76 fname = self.fname if fname is None else fname 

77 max_fname_arg_count = ( 

78 self.max_fname_arg_count 

79 if max_fname_arg_count is None 

80 else max_fname_arg_count 

81 ) 

82 method = self.method if method is None else method 

83 

84 if method == "args": 

85 validate_args(fname, args, max_fname_arg_count, self.defaults) 

86 elif method == "kwargs": 

87 validate_kwargs(fname, kwargs, self.defaults) 

88 elif method == "both": 

89 validate_args_and_kwargs( 

90 fname, args, kwargs, max_fname_arg_count, self.defaults 

91 ) 

92 else: 

93 raise ValueError(f"invalid validation method '{method}'") 

94 

95 

96ARGMINMAX_DEFAULTS = {"out": None} 

97validate_argmin = CompatValidator( 

98 ARGMINMAX_DEFAULTS, fname="argmin", method="both", max_fname_arg_count=1 

99) 

100validate_argmax = CompatValidator( 

101 ARGMINMAX_DEFAULTS, fname="argmax", method="both", max_fname_arg_count=1 

102) 

103 

104 

105def process_skipna(skipna: bool | ndarray | None, args) -> tuple[bool, Any]: 

106 if isinstance(skipna, ndarray) or skipna is None: 

107 args = (skipna, *args) 

108 skipna = True 

109 

110 return skipna, args 

111 

112 

113def validate_argmin_with_skipna(skipna: bool | ndarray | None, args, kwargs) -> bool: 

114 """ 

115 If 'Series.argmin' is called via the 'numpy' library, the third parameter 

116 in its signature is 'out', which takes either an ndarray or 'None', so 

117 check if the 'skipna' parameter is either an instance of ndarray or is 

118 None, since 'skipna' itself should be a boolean 

119 """ 

120 skipna, args = process_skipna(skipna, args) 

121 validate_argmin(args, kwargs) 

122 return skipna 

123 

124 

125def validate_argmax_with_skipna(skipna: bool | ndarray | None, args, kwargs) -> bool: 

126 """ 

127 If 'Series.argmax' is called via the 'numpy' library, the third parameter 

128 in its signature is 'out', which takes either an ndarray or 'None', so 

129 check if the 'skipna' parameter is either an instance of ndarray or is 

130 None, since 'skipna' itself should be a boolean 

131 """ 

132 skipna, args = process_skipna(skipna, args) 

133 validate_argmax(args, kwargs) 

134 return skipna 

135 

136 

137ARGSORT_DEFAULTS: dict[str, int | str | None] = {} 

138ARGSORT_DEFAULTS["axis"] = -1 

139ARGSORT_DEFAULTS["kind"] = "quicksort" 

140ARGSORT_DEFAULTS["order"] = None 

141ARGSORT_DEFAULTS["kind"] = None 

142ARGSORT_DEFAULTS["stable"] = None 

143 

144 

145validate_argsort = CompatValidator( 

146 ARGSORT_DEFAULTS, fname="argsort", max_fname_arg_count=0, method="both" 

147) 

148 

149# two different signatures of argsort, this second validation for when the 

150# `kind` param is supported 

151ARGSORT_DEFAULTS_KIND: dict[str, int | None] = {} 

152ARGSORT_DEFAULTS_KIND["axis"] = -1 

153ARGSORT_DEFAULTS_KIND["order"] = None 

154ARGSORT_DEFAULTS_KIND["stable"] = None 

155validate_argsort_kind = CompatValidator( 

156 ARGSORT_DEFAULTS_KIND, fname="argsort", max_fname_arg_count=0, method="both" 

157) 

158 

159 

160def validate_argsort_with_ascending(ascending: bool | int | None, args, kwargs) -> bool: 

161 """ 

162 If 'Categorical.argsort' is called via the 'numpy' library, the first 

163 parameter in its signature is 'axis', which takes either an integer or 

164 'None', so check if the 'ascending' parameter has either integer type or is 

165 None, since 'ascending' itself should be a boolean 

166 """ 

167 if is_integer(ascending) or ascending is None: 

168 args = (ascending, *args) 

169 ascending = True 

170 

171 validate_argsort_kind(args, kwargs, max_fname_arg_count=3) 

172 ascending = cast(bool, ascending) 

173 return ascending 

174 

175 

176CLIP_DEFAULTS: dict[str, Any] = {"out": None} 

177validate_clip = CompatValidator( 

178 CLIP_DEFAULTS, fname="clip", method="both", max_fname_arg_count=3 

179) 

180 

181 

182@overload 

183def validate_clip_with_axis(axis: ndarray, args, kwargs) -> None: ... 

184 

185 

186@overload 

187def validate_clip_with_axis(axis: AxisNoneT, args, kwargs) -> AxisNoneT: ... 

188 

189 

190def validate_clip_with_axis( 

191 axis: ndarray | AxisNoneT, args, kwargs 

192) -> AxisNoneT | None: 

193 """ 

194 If 'NDFrame.clip' is called via the numpy library, the third parameter in 

195 its signature is 'out', which can takes an ndarray, so check if the 'axis' 

196 parameter is an instance of ndarray, since 'axis' itself should either be 

197 an integer or None 

198 """ 

199 if isinstance(axis, ndarray): 

200 args = (axis, *args) 

201 # error: Incompatible types in assignment (expression has type "None", 

202 # variable has type "Union[ndarray[Any, Any], str, int]") 

203 axis = None # type: ignore[assignment] 

204 

205 validate_clip(args, kwargs) 

206 # error: Incompatible return value type (got "Union[ndarray[Any, Any], 

207 # str, int]", expected "Union[str, int, None]") 

208 return axis # type: ignore[return-value] 

209 

210 

211CUM_FUNC_DEFAULTS: dict[str, Any] = {} 

212CUM_FUNC_DEFAULTS["dtype"] = None 

213CUM_FUNC_DEFAULTS["out"] = None 

214validate_cum_func = CompatValidator( 

215 CUM_FUNC_DEFAULTS, method="both", max_fname_arg_count=1 

216) 

217validate_cumsum = CompatValidator( 

218 CUM_FUNC_DEFAULTS, fname="cumsum", method="both", max_fname_arg_count=1 

219) 

220 

221 

222def validate_cum_func_with_skipna(skipna: bool, args, kwargs, name) -> bool: 

223 """ 

224 If this function is called via the 'numpy' library, the third parameter in 

225 its signature is 'dtype', which takes either a 'numpy' dtype or 'None', so 

226 check if the 'skipna' parameter is a boolean or not 

227 """ 

228 if not is_bool(skipna): 

229 args = (skipna, *args) 

230 skipna = True 

231 elif isinstance(skipna, np.bool_): 

232 skipna = bool(skipna) 

233 

234 validate_cum_func(args, kwargs, fname=name) 

235 return skipna 

236 

237 

238ALLANY_DEFAULTS: dict[str, bool | None] = {} 

239ALLANY_DEFAULTS["dtype"] = None 

240ALLANY_DEFAULTS["out"] = None 

241ALLANY_DEFAULTS["keepdims"] = False 

242ALLANY_DEFAULTS["axis"] = None 

243validate_all = CompatValidator( 

244 ALLANY_DEFAULTS, fname="all", method="both", max_fname_arg_count=1 

245) 

246validate_any = CompatValidator( 

247 ALLANY_DEFAULTS, fname="any", method="both", max_fname_arg_count=1 

248) 

249 

250LOGICAL_FUNC_DEFAULTS = {"out": None, "keepdims": False} 

251validate_logical_func = CompatValidator(LOGICAL_FUNC_DEFAULTS, method="kwargs") 

252 

253MINMAX_DEFAULTS = {"axis": None, "dtype": None, "out": None, "keepdims": False} 

254validate_min = CompatValidator( 

255 MINMAX_DEFAULTS, fname="min", method="both", max_fname_arg_count=1 

256) 

257validate_max = CompatValidator( 

258 MINMAX_DEFAULTS, fname="max", method="both", max_fname_arg_count=1 

259) 

260 

261 

262REPEAT_DEFAULTS: dict[str, Any] = {"axis": None} 

263validate_repeat = CompatValidator( 

264 REPEAT_DEFAULTS, fname="repeat", method="both", max_fname_arg_count=1 

265) 

266 

267ROUND_DEFAULTS: dict[str, Any] = {"out": None} 

268validate_round = CompatValidator( 

269 ROUND_DEFAULTS, fname="round", method="both", max_fname_arg_count=1 

270) 

271 

272STAT_FUNC_DEFAULTS: dict[str, Any | None] = {} 

273STAT_FUNC_DEFAULTS["dtype"] = None 

274STAT_FUNC_DEFAULTS["out"] = None 

275 

276SUM_DEFAULTS = STAT_FUNC_DEFAULTS.copy() 

277SUM_DEFAULTS["axis"] = None 

278SUM_DEFAULTS["keepdims"] = False 

279SUM_DEFAULTS["initial"] = None 

280 

281PROD_DEFAULTS = SUM_DEFAULTS.copy() 

282 

283MEAN_DEFAULTS = SUM_DEFAULTS.copy() 

284 

285MEDIAN_DEFAULTS = STAT_FUNC_DEFAULTS.copy() 

286MEDIAN_DEFAULTS["overwrite_input"] = False 

287MEDIAN_DEFAULTS["keepdims"] = False 

288 

289STAT_FUNC_DEFAULTS["keepdims"] = False 

290 

291validate_stat_func = CompatValidator(STAT_FUNC_DEFAULTS, method="kwargs") 

292validate_sum = CompatValidator( 

293 SUM_DEFAULTS, fname="sum", method="both", max_fname_arg_count=1 

294) 

295validate_prod = CompatValidator( 

296 PROD_DEFAULTS, fname="prod", method="both", max_fname_arg_count=1 

297) 

298validate_mean = CompatValidator( 

299 MEAN_DEFAULTS, fname="mean", method="both", max_fname_arg_count=1 

300) 

301validate_median = CompatValidator( 

302 MEDIAN_DEFAULTS, fname="median", method="both", max_fname_arg_count=1 

303) 

304 

305STAT_DDOF_FUNC_DEFAULTS: dict[str, bool | None] = {} 

306STAT_DDOF_FUNC_DEFAULTS["dtype"] = None 

307STAT_DDOF_FUNC_DEFAULTS["out"] = None 

308STAT_DDOF_FUNC_DEFAULTS["keepdims"] = False 

309validate_stat_ddof_func = CompatValidator(STAT_DDOF_FUNC_DEFAULTS, method="kwargs") 

310 

311TAKE_DEFAULTS: dict[str, str | None] = {} 

312TAKE_DEFAULTS["out"] = None 

313TAKE_DEFAULTS["mode"] = "raise" 

314validate_take = CompatValidator(TAKE_DEFAULTS, fname="take", method="kwargs") 

315 

316 

317TRANSPOSE_DEFAULTS = {"axes": None} 

318validate_transpose = CompatValidator( 

319 TRANSPOSE_DEFAULTS, fname="transpose", method="both", max_fname_arg_count=0 

320) 

321 

322 

323def validate_groupby_func(name: str, args, kwargs, allowed=None) -> None: 

324 """ 

325 'args' and 'kwargs' should be empty, except for allowed kwargs because all 

326 of their necessary parameters are explicitly listed in the function 

327 signature 

328 """ 

329 if allowed is None: 

330 allowed = [] 

331 

332 kwargs = set(kwargs) - set(allowed) 

333 

334 if len(args) + len(kwargs) > 0: 

335 raise UnsupportedFunctionCall( 

336 "numpy operations are not valid with groupby. " 

337 f"Use .groupby(...).{name}() instead" 

338 ) 

339 

340 

341def validate_minmax_axis(axis: AxisInt | None, ndim: int = 1) -> None: 

342 """ 

343 Ensure that the axis argument passed to min, max, argmin, or argmax is zero 

344 or None, as otherwise it will be incorrectly ignored. 

345 

346 Parameters 

347 ---------- 

348 axis : int or None 

349 ndim : int, default 1 

350 

351 Raises 

352 ------ 

353 ValueError 

354 """ 

355 if axis is None: 

356 return 

357 if axis >= ndim or (axis < 0 and ndim + axis < 0): 

358 raise ValueError(f"`axis` must be fewer than the number of dimensions ({ndim})") 

359 

360 

361_validation_funcs = { 

362 "median": validate_median, 

363 "mean": validate_mean, 

364 "min": validate_min, 

365 "max": validate_max, 

366 "sum": validate_sum, 

367 "prod": validate_prod, 

368} 

369 

370 

371def validate_func(fname, args, kwargs) -> None: 

372 if fname not in _validation_funcs: 

373 return validate_stat_func(args, kwargs, fname=fname) 

374 

375 validation_func = _validation_funcs[fname] 

376 return validation_func(args, kwargs)