Hide keyboard shortcuts

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

2For compatibility with numpy libraries, pandas functions or 

3methods have to accept '*args' and '**kwargs' parameters to 

4accommodate numpy arguments that are not actually used or 

5respected in the pandas implementation. 

6 

7To ensure that users do not abuse these parameters, validation 

8is performed in 'validators.py' to make sure that any extra 

9parameters passed correspond ONLY to those in the numpy signature. 

10Part of that validation includes whether or not the user attempted 

11to pass in non-default values for these extraneous parameters. As we 

12want to discourage users from relying on these parameters when calling 

13the pandas implementation, we want them only to pass in the default values 

14for these parameters. 

15 

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

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

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

19""" 

20from collections import OrderedDict 

21from distutils.version import LooseVersion 

22from typing import Any, Dict, Optional, Union 

23 

24from numpy import __version__ as _np_version, ndarray 

25 

26from pandas._libs.lib import is_bool, is_integer 

27from pandas.errors import UnsupportedFunctionCall 

28from pandas.util._validators import ( 

29 validate_args, 

30 validate_args_and_kwargs, 

31 validate_kwargs, 

32) 

33 

34 

35class CompatValidator: 

36 def __init__(self, defaults, fname=None, method=None, max_fname_arg_count=None): 

37 self.fname = fname 

38 self.method = method 

39 self.defaults = defaults 

40 self.max_fname_arg_count = max_fname_arg_count 

41 

42 def __call__(self, args, kwargs, fname=None, max_fname_arg_count=None, method=None): 

43 if args or kwargs: 

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

45 max_fname_arg_count = ( 

46 self.max_fname_arg_count 

47 if max_fname_arg_count is None 

48 else max_fname_arg_count 

49 ) 

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

51 

52 if method == "args": 

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

54 elif method == "kwargs": 

55 validate_kwargs(fname, kwargs, self.defaults) 

56 elif method == "both": 

57 validate_args_and_kwargs( 

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

59 ) 

60 else: 

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

62 

63 

64ARGMINMAX_DEFAULTS = dict(out=None) 

65validate_argmin = CompatValidator( 

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

67) 

68validate_argmax = CompatValidator( 

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

70) 

71 

72 

73def process_skipna(skipna, args): 

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

75 args = (skipna,) + args 

76 skipna = True 

77 

78 return skipna, args 

79 

80 

81def validate_argmin_with_skipna(skipna, args, kwargs): 

82 """ 

83 If 'Series.argmin' is called via the 'numpy' library, 

84 the third parameter in its signature is 'out', which 

85 takes either an ndarray or 'None', so check if the 

86 'skipna' parameter is either an instance of ndarray or 

87 is None, since 'skipna' itself should be a boolean 

88 """ 

89 

90 skipna, args = process_skipna(skipna, args) 

91 validate_argmin(args, kwargs) 

92 return skipna 

93 

94 

95def validate_argmax_with_skipna(skipna, args, kwargs): 

96 """ 

97 If 'Series.argmax' is called via the 'numpy' library, 

98 the third parameter in its signature is 'out', which 

99 takes either an ndarray or 'None', so check if the 

100 'skipna' parameter is either an instance of ndarray or 

101 is None, since 'skipna' itself should be a boolean 

102 """ 

103 

104 skipna, args = process_skipna(skipna, args) 

105 validate_argmax(args, kwargs) 

106 return skipna 

107 

108 

109ARGSORT_DEFAULTS: "OrderedDict[str, Optional[Union[int, str]]]" = OrderedDict() 

110ARGSORT_DEFAULTS["axis"] = -1 

111ARGSORT_DEFAULTS["kind"] = "quicksort" 

112ARGSORT_DEFAULTS["order"] = None 

113 

114if LooseVersion(_np_version) >= LooseVersion("1.17.0"): 

115 # GH-26361. NumPy added radix sort and changed default to None. 

116 ARGSORT_DEFAULTS["kind"] = None 

117 

118 

119validate_argsort = CompatValidator( 

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

121) 

122 

123# two different signatures of argsort, this second validation 

124# for when the `kind` param is supported 

125ARGSORT_DEFAULTS_KIND: "OrderedDict[str, Optional[int]]" = OrderedDict() 

126ARGSORT_DEFAULTS_KIND["axis"] = -1 

127ARGSORT_DEFAULTS_KIND["order"] = None 

128validate_argsort_kind = CompatValidator( 

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

130) 

131 

132 

133def validate_argsort_with_ascending(ascending, args, kwargs): 

134 """ 

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

136 first parameter in its signature is 'axis', which takes either 

137 an integer or 'None', so check if the 'ascending' parameter has 

138 either integer type or is None, since 'ascending' itself should 

139 be a boolean 

140 """ 

141 

142 if is_integer(ascending) or ascending is None: 

143 args = (ascending,) + args 

144 ascending = True 

145 

146 validate_argsort_kind(args, kwargs, max_fname_arg_count=3) 

147 return ascending 

148 

149 

150CLIP_DEFAULTS = dict(out=None) # type Dict[str, Any] 

151validate_clip = CompatValidator( 

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

153) 

154 

155 

156def validate_clip_with_axis(axis, args, kwargs): 

157 """ 

158 If 'NDFrame.clip' is called via the numpy library, the third 

159 parameter in its signature is 'out', which can takes an ndarray, 

160 so check if the 'axis' parameter is an instance of ndarray, since 

161 'axis' itself should either be an integer or None 

162 """ 

163 

164 if isinstance(axis, ndarray): 

165 args = (axis,) + args 

166 axis = None 

167 

168 validate_clip(args, kwargs) 

169 return axis 

170 

171 

172CUM_FUNC_DEFAULTS: "OrderedDict[str, Any]" = OrderedDict() 

173CUM_FUNC_DEFAULTS["dtype"] = None 

174CUM_FUNC_DEFAULTS["out"] = None 

175validate_cum_func = CompatValidator( 

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

177) 

178validate_cumsum = CompatValidator( 

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

180) 

181 

182 

183def validate_cum_func_with_skipna(skipna, args, kwargs, name): 

184 """ 

185 If this function is called via the 'numpy' library, the third 

186 parameter in its signature is 'dtype', which takes either a 

187 'numpy' dtype or 'None', so check if the 'skipna' parameter is 

188 a boolean or not 

189 """ 

190 if not is_bool(skipna): 

191 args = (skipna,) + args 

192 skipna = True 

193 

194 validate_cum_func(args, kwargs, fname=name) 

195 return skipna 

196 

197 

198ALLANY_DEFAULTS: "OrderedDict[str, Optional[bool]]" = OrderedDict() 

199ALLANY_DEFAULTS["dtype"] = None 

200ALLANY_DEFAULTS["out"] = None 

201ALLANY_DEFAULTS["keepdims"] = False 

202validate_all = CompatValidator( 

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

204) 

205validate_any = CompatValidator( 

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

207) 

208 

209LOGICAL_FUNC_DEFAULTS = dict(out=None, keepdims=False) 

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

211 

212MINMAX_DEFAULTS = dict(axis=None, out=None, keepdims=False) 

213validate_min = CompatValidator( 

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

215) 

216validate_max = CompatValidator( 

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

218) 

219 

220RESHAPE_DEFAULTS: Dict[str, str] = dict(order="C") 

221validate_reshape = CompatValidator( 

222 RESHAPE_DEFAULTS, fname="reshape", method="both", max_fname_arg_count=1 

223) 

224 

225REPEAT_DEFAULTS: Dict[str, Any] = dict(axis=None) 

226validate_repeat = CompatValidator( 

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

228) 

229 

230ROUND_DEFAULTS: Dict[str, Any] = dict(out=None) 

231validate_round = CompatValidator( 

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

233) 

234 

235SORT_DEFAULTS: "OrderedDict[str, Optional[Union[int, str]]]" = OrderedDict() 

236SORT_DEFAULTS["axis"] = -1 

237SORT_DEFAULTS["kind"] = "quicksort" 

238SORT_DEFAULTS["order"] = None 

239validate_sort = CompatValidator(SORT_DEFAULTS, fname="sort", method="kwargs") 

240 

241STAT_FUNC_DEFAULTS: "OrderedDict[str, Optional[Any]]" = OrderedDict() 

242STAT_FUNC_DEFAULTS["dtype"] = None 

243STAT_FUNC_DEFAULTS["out"] = None 

244 

245PROD_DEFAULTS = SUM_DEFAULTS = STAT_FUNC_DEFAULTS.copy() 

246SUM_DEFAULTS["keepdims"] = False 

247SUM_DEFAULTS["initial"] = None 

248 

249MEDIAN_DEFAULTS = STAT_FUNC_DEFAULTS.copy() 

250MEDIAN_DEFAULTS["overwrite_input"] = False 

251MEDIAN_DEFAULTS["keepdims"] = False 

252 

253STAT_FUNC_DEFAULTS["keepdims"] = False 

254 

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

256validate_sum = CompatValidator( 

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

258) 

259validate_prod = CompatValidator( 

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

261) 

262validate_mean = CompatValidator( 

263 STAT_FUNC_DEFAULTS, fname="mean", method="both", max_fname_arg_count=1 

264) 

265validate_median = CompatValidator( 

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

267) 

268 

269STAT_DDOF_FUNC_DEFAULTS: "OrderedDict[str, Optional[bool]]" = OrderedDict() 

270STAT_DDOF_FUNC_DEFAULTS["dtype"] = None 

271STAT_DDOF_FUNC_DEFAULTS["out"] = None 

272STAT_DDOF_FUNC_DEFAULTS["keepdims"] = False 

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

274 

275TAKE_DEFAULTS: "OrderedDict[str, Optional[str]]" = OrderedDict() 

276TAKE_DEFAULTS["out"] = None 

277TAKE_DEFAULTS["mode"] = "raise" 

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

279 

280 

281def validate_take_with_convert(convert, args, kwargs): 

282 """ 

283 If this function is called via the 'numpy' library, the third 

284 parameter in its signature is 'axis', which takes either an 

285 ndarray or 'None', so check if the 'convert' parameter is either 

286 an instance of ndarray or is None 

287 """ 

288 

289 if isinstance(convert, ndarray) or convert is None: 

290 args = (convert,) + args 

291 convert = True 

292 

293 validate_take(args, kwargs, max_fname_arg_count=3, method="both") 

294 return convert 

295 

296 

297TRANSPOSE_DEFAULTS = dict(axes=None) 

298validate_transpose = CompatValidator( 

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

300) 

301 

302 

303def validate_window_func(name, args, kwargs): 

304 numpy_args = ("axis", "dtype", "out") 

305 msg = ( 

306 f"numpy operations are not valid with window objects. " 

307 f"Use .{name}() directly instead " 

308 ) 

309 

310 if len(args) > 0: 

311 raise UnsupportedFunctionCall(msg) 

312 

313 for arg in numpy_args: 

314 if arg in kwargs: 

315 raise UnsupportedFunctionCall(msg) 

316 

317 

318def validate_rolling_func(name, args, kwargs): 

319 numpy_args = ("axis", "dtype", "out") 

320 msg = ( 

321 f"numpy operations are not valid with window objects. " 

322 f"Use .rolling(...).{name}() instead " 

323 ) 

324 

325 if len(args) > 0: 

326 raise UnsupportedFunctionCall(msg) 

327 

328 for arg in numpy_args: 

329 if arg in kwargs: 

330 raise UnsupportedFunctionCall(msg) 

331 

332 

333def validate_expanding_func(name, args, kwargs): 

334 numpy_args = ("axis", "dtype", "out") 

335 msg = ( 

336 f"numpy operations are not valid with window objects. " 

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

338 ) 

339 

340 if len(args) > 0: 

341 raise UnsupportedFunctionCall(msg) 

342 

343 for arg in numpy_args: 

344 if arg in kwargs: 

345 raise UnsupportedFunctionCall(msg) 

346 

347 

348def validate_groupby_func(name, args, kwargs, allowed=None): 

349 """ 

350 'args' and 'kwargs' should be empty, except for allowed 

351 kwargs because all of 

352 their necessary parameters are explicitly listed in 

353 the function signature 

354 """ 

355 if allowed is None: 

356 allowed = [] 

357 

358 kwargs = set(kwargs) - set(allowed) 

359 

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

361 raise UnsupportedFunctionCall( 

362 f"numpy operations are not valid with " 

363 f"groupby. Use .groupby(...).{name}() " 

364 f"instead" 

365 ) 

366 

367 

368RESAMPLER_NUMPY_OPS = ("min", "max", "sum", "prod", "mean", "std", "var") 

369 

370 

371def validate_resampler_func(method, args, kwargs): 

372 """ 

373 'args' and 'kwargs' should be empty because all of 

374 their necessary parameters are explicitly listed in 

375 the function signature 

376 """ 

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

378 if method in RESAMPLER_NUMPY_OPS: 

379 raise UnsupportedFunctionCall( 

380 f"numpy operations are not " 

381 f"valid with resample. Use " 

382 f".resample(...).{method}() instead" 

383 ) 

384 else: 

385 raise TypeError("too many arguments passed in") 

386 

387 

388def validate_minmax_axis(axis): 

389 """ 

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

391 zero or None, as otherwise it will be incorrectly ignored. 

392 

393 Parameters 

394 ---------- 

395 axis : int or None 

396 

397 Raises 

398 ------ 

399 ValueError 

400 """ 

401 ndim = 1 # hard-coded for Index 

402 if axis is None: 

403 return 

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

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