test_nanfunctions.py 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418
  1. import warnings
  2. import pytest
  3. import inspect
  4. from functools import partial
  5. import numpy as np
  6. from numpy._core.numeric import normalize_axis_tuple
  7. from numpy.exceptions import AxisError, ComplexWarning
  8. from numpy.lib._nanfunctions_impl import _nan_mask, _replace_nan
  9. from numpy.testing import (
  10. assert_, assert_equal, assert_almost_equal, assert_raises,
  11. assert_raises_regex, assert_array_equal, suppress_warnings
  12. )
  13. # Test data
  14. _ndat = np.array([[0.6244, np.nan, 0.2692, 0.0116, np.nan, 0.1170],
  15. [0.5351, -0.9403, np.nan, 0.2100, 0.4759, 0.2833],
  16. [np.nan, np.nan, np.nan, 0.1042, np.nan, -0.5954],
  17. [0.1610, np.nan, np.nan, 0.1859, 0.3146, np.nan]])
  18. # Rows of _ndat with nans removed
  19. _rdat = [np.array([0.6244, 0.2692, 0.0116, 0.1170]),
  20. np.array([0.5351, -0.9403, 0.2100, 0.4759, 0.2833]),
  21. np.array([0.1042, -0.5954]),
  22. np.array([0.1610, 0.1859, 0.3146])]
  23. # Rows of _ndat with nans converted to ones
  24. _ndat_ones = np.array([[0.6244, 1.0, 0.2692, 0.0116, 1.0, 0.1170],
  25. [0.5351, -0.9403, 1.0, 0.2100, 0.4759, 0.2833],
  26. [1.0, 1.0, 1.0, 0.1042, 1.0, -0.5954],
  27. [0.1610, 1.0, 1.0, 0.1859, 0.3146, 1.0]])
  28. # Rows of _ndat with nans converted to zeros
  29. _ndat_zeros = np.array([[0.6244, 0.0, 0.2692, 0.0116, 0.0, 0.1170],
  30. [0.5351, -0.9403, 0.0, 0.2100, 0.4759, 0.2833],
  31. [0.0, 0.0, 0.0, 0.1042, 0.0, -0.5954],
  32. [0.1610, 0.0, 0.0, 0.1859, 0.3146, 0.0]])
  33. class TestSignatureMatch:
  34. NANFUNCS = {
  35. np.nanmin: np.amin,
  36. np.nanmax: np.amax,
  37. np.nanargmin: np.argmin,
  38. np.nanargmax: np.argmax,
  39. np.nansum: np.sum,
  40. np.nanprod: np.prod,
  41. np.nancumsum: np.cumsum,
  42. np.nancumprod: np.cumprod,
  43. np.nanmean: np.mean,
  44. np.nanmedian: np.median,
  45. np.nanpercentile: np.percentile,
  46. np.nanquantile: np.quantile,
  47. np.nanvar: np.var,
  48. np.nanstd: np.std,
  49. }
  50. IDS = [k.__name__ for k in NANFUNCS]
  51. @staticmethod
  52. def get_signature(func, default="..."):
  53. """Construct a signature and replace all default parameter-values."""
  54. prm_list = []
  55. signature = inspect.signature(func)
  56. for prm in signature.parameters.values():
  57. if prm.default is inspect.Parameter.empty:
  58. prm_list.append(prm)
  59. else:
  60. prm_list.append(prm.replace(default=default))
  61. return inspect.Signature(prm_list)
  62. @pytest.mark.parametrize("nan_func,func", NANFUNCS.items(), ids=IDS)
  63. def test_signature_match(self, nan_func, func):
  64. # Ignore the default parameter-values as they can sometimes differ
  65. # between the two functions (*e.g.* one has `False` while the other
  66. # has `np._NoValue`)
  67. signature = self.get_signature(func)
  68. nan_signature = self.get_signature(nan_func)
  69. np.testing.assert_equal(signature, nan_signature)
  70. def test_exhaustiveness(self):
  71. """Validate that all nan functions are actually tested."""
  72. np.testing.assert_equal(
  73. set(self.IDS), set(np.lib._nanfunctions_impl.__all__)
  74. )
  75. class TestNanFunctions_MinMax:
  76. nanfuncs = [np.nanmin, np.nanmax]
  77. stdfuncs = [np.min, np.max]
  78. def test_mutation(self):
  79. # Check that passed array is not modified.
  80. ndat = _ndat.copy()
  81. for f in self.nanfuncs:
  82. f(ndat)
  83. assert_equal(ndat, _ndat)
  84. def test_keepdims(self):
  85. mat = np.eye(3)
  86. for nf, rf in zip(self.nanfuncs, self.stdfuncs):
  87. for axis in [None, 0, 1]:
  88. tgt = rf(mat, axis=axis, keepdims=True)
  89. res = nf(mat, axis=axis, keepdims=True)
  90. assert_(res.ndim == tgt.ndim)
  91. def test_out(self):
  92. mat = np.eye(3)
  93. for nf, rf in zip(self.nanfuncs, self.stdfuncs):
  94. resout = np.zeros(3)
  95. tgt = rf(mat, axis=1)
  96. res = nf(mat, axis=1, out=resout)
  97. assert_almost_equal(res, resout)
  98. assert_almost_equal(res, tgt)
  99. def test_dtype_from_input(self):
  100. codes = 'efdgFDG'
  101. for nf, rf in zip(self.nanfuncs, self.stdfuncs):
  102. for c in codes:
  103. mat = np.eye(3, dtype=c)
  104. tgt = rf(mat, axis=1).dtype.type
  105. res = nf(mat, axis=1).dtype.type
  106. assert_(res is tgt)
  107. # scalar case
  108. tgt = rf(mat, axis=None).dtype.type
  109. res = nf(mat, axis=None).dtype.type
  110. assert_(res is tgt)
  111. def test_result_values(self):
  112. for nf, rf in zip(self.nanfuncs, self.stdfuncs):
  113. tgt = [rf(d) for d in _rdat]
  114. res = nf(_ndat, axis=1)
  115. assert_almost_equal(res, tgt)
  116. @pytest.mark.parametrize("axis", [None, 0, 1])
  117. @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"])
  118. @pytest.mark.parametrize("array", [
  119. np.array(np.nan),
  120. np.full((3, 3), np.nan),
  121. ], ids=["0d", "2d"])
  122. def test_allnans(self, axis, dtype, array):
  123. if axis is not None and array.ndim == 0:
  124. pytest.skip("`axis != None` not supported for 0d arrays")
  125. array = array.astype(dtype)
  126. match = "All-NaN slice encountered"
  127. for func in self.nanfuncs:
  128. with pytest.warns(RuntimeWarning, match=match):
  129. out = func(array, axis=axis)
  130. assert np.isnan(out).all()
  131. assert out.dtype == array.dtype
  132. def test_masked(self):
  133. mat = np.ma.fix_invalid(_ndat)
  134. msk = mat._mask.copy()
  135. for f in [np.nanmin]:
  136. res = f(mat, axis=1)
  137. tgt = f(_ndat, axis=1)
  138. assert_equal(res, tgt)
  139. assert_equal(mat._mask, msk)
  140. assert_(not np.isinf(mat).any())
  141. def test_scalar(self):
  142. for f in self.nanfuncs:
  143. assert_(f(0.) == 0.)
  144. def test_subclass(self):
  145. class MyNDArray(np.ndarray):
  146. pass
  147. # Check that it works and that type and
  148. # shape are preserved
  149. mine = np.eye(3).view(MyNDArray)
  150. for f in self.nanfuncs:
  151. res = f(mine, axis=0)
  152. assert_(isinstance(res, MyNDArray))
  153. assert_(res.shape == (3,))
  154. res = f(mine, axis=1)
  155. assert_(isinstance(res, MyNDArray))
  156. assert_(res.shape == (3,))
  157. res = f(mine)
  158. assert_(res.shape == ())
  159. # check that rows of nan are dealt with for subclasses (#4628)
  160. mine[1] = np.nan
  161. for f in self.nanfuncs:
  162. with warnings.catch_warnings(record=True) as w:
  163. warnings.simplefilter('always')
  164. res = f(mine, axis=0)
  165. assert_(isinstance(res, MyNDArray))
  166. assert_(not np.any(np.isnan(res)))
  167. assert_(len(w) == 0)
  168. with warnings.catch_warnings(record=True) as w:
  169. warnings.simplefilter('always')
  170. res = f(mine, axis=1)
  171. assert_(isinstance(res, MyNDArray))
  172. assert_(np.isnan(res[1]) and not np.isnan(res[0])
  173. and not np.isnan(res[2]))
  174. assert_(len(w) == 1, 'no warning raised')
  175. assert_(issubclass(w[0].category, RuntimeWarning))
  176. with warnings.catch_warnings(record=True) as w:
  177. warnings.simplefilter('always')
  178. res = f(mine)
  179. assert_(res.shape == ())
  180. assert_(res != np.nan)
  181. assert_(len(w) == 0)
  182. def test_object_array(self):
  183. arr = np.array([[1.0, 2.0], [np.nan, 4.0], [np.nan, np.nan]], dtype=object)
  184. assert_equal(np.nanmin(arr), 1.0)
  185. assert_equal(np.nanmin(arr, axis=0), [1.0, 2.0])
  186. with warnings.catch_warnings(record=True) as w:
  187. warnings.simplefilter('always')
  188. # assert_equal does not work on object arrays of nan
  189. assert_equal(list(np.nanmin(arr, axis=1)), [1.0, 4.0, np.nan])
  190. assert_(len(w) == 1, 'no warning raised')
  191. assert_(issubclass(w[0].category, RuntimeWarning))
  192. @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"])
  193. def test_initial(self, dtype):
  194. class MyNDArray(np.ndarray):
  195. pass
  196. ar = np.arange(9).astype(dtype)
  197. ar[:5] = np.nan
  198. for f in self.nanfuncs:
  199. initial = 100 if f is np.nanmax else 0
  200. ret1 = f(ar, initial=initial)
  201. assert ret1.dtype == dtype
  202. assert ret1 == initial
  203. ret2 = f(ar.view(MyNDArray), initial=initial)
  204. assert ret2.dtype == dtype
  205. assert ret2 == initial
  206. @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"])
  207. def test_where(self, dtype):
  208. class MyNDArray(np.ndarray):
  209. pass
  210. ar = np.arange(9).reshape(3, 3).astype(dtype)
  211. ar[0, :] = np.nan
  212. where = np.ones_like(ar, dtype=np.bool)
  213. where[:, 0] = False
  214. for f in self.nanfuncs:
  215. reference = 4 if f is np.nanmin else 8
  216. ret1 = f(ar, where=where, initial=5)
  217. assert ret1.dtype == dtype
  218. assert ret1 == reference
  219. ret2 = f(ar.view(MyNDArray), where=where, initial=5)
  220. assert ret2.dtype == dtype
  221. assert ret2 == reference
  222. class TestNanFunctions_ArgminArgmax:
  223. nanfuncs = [np.nanargmin, np.nanargmax]
  224. def test_mutation(self):
  225. # Check that passed array is not modified.
  226. ndat = _ndat.copy()
  227. for f in self.nanfuncs:
  228. f(ndat)
  229. assert_equal(ndat, _ndat)
  230. def test_result_values(self):
  231. for f, fcmp in zip(self.nanfuncs, [np.greater, np.less]):
  232. for row in _ndat:
  233. with suppress_warnings() as sup:
  234. sup.filter(RuntimeWarning, "invalid value encountered in")
  235. ind = f(row)
  236. val = row[ind]
  237. # comparing with NaN is tricky as the result
  238. # is always false except for NaN != NaN
  239. assert_(not np.isnan(val))
  240. assert_(not fcmp(val, row).any())
  241. assert_(not np.equal(val, row[:ind]).any())
  242. @pytest.mark.parametrize("axis", [None, 0, 1])
  243. @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"])
  244. @pytest.mark.parametrize("array", [
  245. np.array(np.nan),
  246. np.full((3, 3), np.nan),
  247. ], ids=["0d", "2d"])
  248. def test_allnans(self, axis, dtype, array):
  249. if axis is not None and array.ndim == 0:
  250. pytest.skip("`axis != None` not supported for 0d arrays")
  251. array = array.astype(dtype)
  252. for func in self.nanfuncs:
  253. with pytest.raises(ValueError, match="All-NaN slice encountered"):
  254. func(array, axis=axis)
  255. def test_empty(self):
  256. mat = np.zeros((0, 3))
  257. for f in self.nanfuncs:
  258. for axis in [0, None]:
  259. assert_raises_regex(
  260. ValueError,
  261. "attempt to get argm.. of an empty sequence",
  262. f, mat, axis=axis)
  263. for axis in [1]:
  264. res = f(mat, axis=axis)
  265. assert_equal(res, np.zeros(0))
  266. def test_scalar(self):
  267. for f in self.nanfuncs:
  268. assert_(f(0.) == 0.)
  269. def test_subclass(self):
  270. class MyNDArray(np.ndarray):
  271. pass
  272. # Check that it works and that type and
  273. # shape are preserved
  274. mine = np.eye(3).view(MyNDArray)
  275. for f in self.nanfuncs:
  276. res = f(mine, axis=0)
  277. assert_(isinstance(res, MyNDArray))
  278. assert_(res.shape == (3,))
  279. res = f(mine, axis=1)
  280. assert_(isinstance(res, MyNDArray))
  281. assert_(res.shape == (3,))
  282. res = f(mine)
  283. assert_(res.shape == ())
  284. @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"])
  285. def test_keepdims(self, dtype):
  286. ar = np.arange(9).astype(dtype)
  287. ar[:5] = np.nan
  288. for f in self.nanfuncs:
  289. reference = 5 if f is np.nanargmin else 8
  290. ret = f(ar, keepdims=True)
  291. assert ret.ndim == ar.ndim
  292. assert ret == reference
  293. @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"])
  294. def test_out(self, dtype):
  295. ar = np.arange(9).astype(dtype)
  296. ar[:5] = np.nan
  297. for f in self.nanfuncs:
  298. out = np.zeros((), dtype=np.intp)
  299. reference = 5 if f is np.nanargmin else 8
  300. ret = f(ar, out=out)
  301. assert ret is out
  302. assert ret == reference
  303. _TEST_ARRAYS = {
  304. "0d": np.array(5),
  305. "1d": np.array([127, 39, 93, 87, 46])
  306. }
  307. for _v in _TEST_ARRAYS.values():
  308. _v.setflags(write=False)
  309. @pytest.mark.parametrize(
  310. "dtype",
  311. np.typecodes["AllInteger"] + np.typecodes["AllFloat"] + "O",
  312. )
  313. @pytest.mark.parametrize("mat", _TEST_ARRAYS.values(), ids=_TEST_ARRAYS.keys())
  314. class TestNanFunctions_NumberTypes:
  315. nanfuncs = {
  316. np.nanmin: np.min,
  317. np.nanmax: np.max,
  318. np.nanargmin: np.argmin,
  319. np.nanargmax: np.argmax,
  320. np.nansum: np.sum,
  321. np.nanprod: np.prod,
  322. np.nancumsum: np.cumsum,
  323. np.nancumprod: np.cumprod,
  324. np.nanmean: np.mean,
  325. np.nanmedian: np.median,
  326. np.nanvar: np.var,
  327. np.nanstd: np.std,
  328. }
  329. nanfunc_ids = [i.__name__ for i in nanfuncs]
  330. @pytest.mark.parametrize("nanfunc,func", nanfuncs.items(), ids=nanfunc_ids)
  331. @np.errstate(over="ignore")
  332. def test_nanfunc(self, mat, dtype, nanfunc, func):
  333. mat = mat.astype(dtype)
  334. tgt = func(mat)
  335. out = nanfunc(mat)
  336. assert_almost_equal(out, tgt)
  337. if dtype == "O":
  338. assert type(out) is type(tgt)
  339. else:
  340. assert out.dtype == tgt.dtype
  341. @pytest.mark.parametrize(
  342. "nanfunc,func",
  343. [(np.nanquantile, np.quantile), (np.nanpercentile, np.percentile)],
  344. ids=["nanquantile", "nanpercentile"],
  345. )
  346. def test_nanfunc_q(self, mat, dtype, nanfunc, func):
  347. mat = mat.astype(dtype)
  348. if mat.dtype.kind == "c":
  349. assert_raises(TypeError, func, mat, q=1)
  350. assert_raises(TypeError, nanfunc, mat, q=1)
  351. else:
  352. tgt = func(mat, q=1)
  353. out = nanfunc(mat, q=1)
  354. assert_almost_equal(out, tgt)
  355. if dtype == "O":
  356. assert type(out) is type(tgt)
  357. else:
  358. assert out.dtype == tgt.dtype
  359. @pytest.mark.parametrize(
  360. "nanfunc,func",
  361. [(np.nanvar, np.var), (np.nanstd, np.std)],
  362. ids=["nanvar", "nanstd"],
  363. )
  364. def test_nanfunc_ddof(self, mat, dtype, nanfunc, func):
  365. mat = mat.astype(dtype)
  366. tgt = func(mat, ddof=0.5)
  367. out = nanfunc(mat, ddof=0.5)
  368. assert_almost_equal(out, tgt)
  369. if dtype == "O":
  370. assert type(out) is type(tgt)
  371. else:
  372. assert out.dtype == tgt.dtype
  373. @pytest.mark.parametrize(
  374. "nanfunc", [np.nanvar, np.nanstd]
  375. )
  376. def test_nanfunc_correction(self, mat, dtype, nanfunc):
  377. mat = mat.astype(dtype)
  378. assert_almost_equal(
  379. nanfunc(mat, correction=0.5), nanfunc(mat, ddof=0.5)
  380. )
  381. err_msg = "ddof and correction can't be provided simultaneously."
  382. with assert_raises_regex(ValueError, err_msg):
  383. nanfunc(mat, ddof=0.5, correction=0.5)
  384. with assert_raises_regex(ValueError, err_msg):
  385. nanfunc(mat, ddof=1, correction=0)
  386. class SharedNanFunctionsTestsMixin:
  387. def test_mutation(self):
  388. # Check that passed array is not modified.
  389. ndat = _ndat.copy()
  390. for f in self.nanfuncs:
  391. f(ndat)
  392. assert_equal(ndat, _ndat)
  393. def test_keepdims(self):
  394. mat = np.eye(3)
  395. for nf, rf in zip(self.nanfuncs, self.stdfuncs):
  396. for axis in [None, 0, 1]:
  397. tgt = rf(mat, axis=axis, keepdims=True)
  398. res = nf(mat, axis=axis, keepdims=True)
  399. assert_(res.ndim == tgt.ndim)
  400. def test_out(self):
  401. mat = np.eye(3)
  402. for nf, rf in zip(self.nanfuncs, self.stdfuncs):
  403. resout = np.zeros(3)
  404. tgt = rf(mat, axis=1)
  405. res = nf(mat, axis=1, out=resout)
  406. assert_almost_equal(res, resout)
  407. assert_almost_equal(res, tgt)
  408. def test_dtype_from_dtype(self):
  409. mat = np.eye(3)
  410. codes = 'efdgFDG'
  411. for nf, rf in zip(self.nanfuncs, self.stdfuncs):
  412. for c in codes:
  413. with suppress_warnings() as sup:
  414. if nf in {np.nanstd, np.nanvar} and c in 'FDG':
  415. # Giving the warning is a small bug, see gh-8000
  416. sup.filter(ComplexWarning)
  417. tgt = rf(mat, dtype=np.dtype(c), axis=1).dtype.type
  418. res = nf(mat, dtype=np.dtype(c), axis=1).dtype.type
  419. assert_(res is tgt)
  420. # scalar case
  421. tgt = rf(mat, dtype=np.dtype(c), axis=None).dtype.type
  422. res = nf(mat, dtype=np.dtype(c), axis=None).dtype.type
  423. assert_(res is tgt)
  424. def test_dtype_from_char(self):
  425. mat = np.eye(3)
  426. codes = 'efdgFDG'
  427. for nf, rf in zip(self.nanfuncs, self.stdfuncs):
  428. for c in codes:
  429. with suppress_warnings() as sup:
  430. if nf in {np.nanstd, np.nanvar} and c in 'FDG':
  431. # Giving the warning is a small bug, see gh-8000
  432. sup.filter(ComplexWarning)
  433. tgt = rf(mat, dtype=c, axis=1).dtype.type
  434. res = nf(mat, dtype=c, axis=1).dtype.type
  435. assert_(res is tgt)
  436. # scalar case
  437. tgt = rf(mat, dtype=c, axis=None).dtype.type
  438. res = nf(mat, dtype=c, axis=None).dtype.type
  439. assert_(res is tgt)
  440. def test_dtype_from_input(self):
  441. codes = 'efdgFDG'
  442. for nf, rf in zip(self.nanfuncs, self.stdfuncs):
  443. for c in codes:
  444. mat = np.eye(3, dtype=c)
  445. tgt = rf(mat, axis=1).dtype.type
  446. res = nf(mat, axis=1).dtype.type
  447. assert_(res is tgt, "res %s, tgt %s" % (res, tgt))
  448. # scalar case
  449. tgt = rf(mat, axis=None).dtype.type
  450. res = nf(mat, axis=None).dtype.type
  451. assert_(res is tgt)
  452. def test_result_values(self):
  453. for nf, rf in zip(self.nanfuncs, self.stdfuncs):
  454. tgt = [rf(d) for d in _rdat]
  455. res = nf(_ndat, axis=1)
  456. assert_almost_equal(res, tgt)
  457. def test_scalar(self):
  458. for f in self.nanfuncs:
  459. assert_(f(0.) == 0.)
  460. def test_subclass(self):
  461. class MyNDArray(np.ndarray):
  462. pass
  463. # Check that it works and that type and
  464. # shape are preserved
  465. array = np.eye(3)
  466. mine = array.view(MyNDArray)
  467. for f in self.nanfuncs:
  468. expected_shape = f(array, axis=0).shape
  469. res = f(mine, axis=0)
  470. assert_(isinstance(res, MyNDArray))
  471. assert_(res.shape == expected_shape)
  472. expected_shape = f(array, axis=1).shape
  473. res = f(mine, axis=1)
  474. assert_(isinstance(res, MyNDArray))
  475. assert_(res.shape == expected_shape)
  476. expected_shape = f(array).shape
  477. res = f(mine)
  478. assert_(isinstance(res, MyNDArray))
  479. assert_(res.shape == expected_shape)
  480. class TestNanFunctions_SumProd(SharedNanFunctionsTestsMixin):
  481. nanfuncs = [np.nansum, np.nanprod]
  482. stdfuncs = [np.sum, np.prod]
  483. @pytest.mark.parametrize("axis", [None, 0, 1])
  484. @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"])
  485. @pytest.mark.parametrize("array", [
  486. np.array(np.nan),
  487. np.full((3, 3), np.nan),
  488. ], ids=["0d", "2d"])
  489. def test_allnans(self, axis, dtype, array):
  490. if axis is not None and array.ndim == 0:
  491. pytest.skip("`axis != None` not supported for 0d arrays")
  492. array = array.astype(dtype)
  493. for func, identity in zip(self.nanfuncs, [0, 1]):
  494. out = func(array, axis=axis)
  495. assert np.all(out == identity)
  496. assert out.dtype == array.dtype
  497. def test_empty(self):
  498. for f, tgt_value in zip([np.nansum, np.nanprod], [0, 1]):
  499. mat = np.zeros((0, 3))
  500. tgt = [tgt_value]*3
  501. res = f(mat, axis=0)
  502. assert_equal(res, tgt)
  503. tgt = []
  504. res = f(mat, axis=1)
  505. assert_equal(res, tgt)
  506. tgt = tgt_value
  507. res = f(mat, axis=None)
  508. assert_equal(res, tgt)
  509. @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"])
  510. def test_initial(self, dtype):
  511. ar = np.arange(9).astype(dtype)
  512. ar[:5] = np.nan
  513. for f in self.nanfuncs:
  514. reference = 28 if f is np.nansum else 3360
  515. ret = f(ar, initial=2)
  516. assert ret.dtype == dtype
  517. assert ret == reference
  518. @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"])
  519. def test_where(self, dtype):
  520. ar = np.arange(9).reshape(3, 3).astype(dtype)
  521. ar[0, :] = np.nan
  522. where = np.ones_like(ar, dtype=np.bool)
  523. where[:, 0] = False
  524. for f in self.nanfuncs:
  525. reference = 26 if f is np.nansum else 2240
  526. ret = f(ar, where=where, initial=2)
  527. assert ret.dtype == dtype
  528. assert ret == reference
  529. class TestNanFunctions_CumSumProd(SharedNanFunctionsTestsMixin):
  530. nanfuncs = [np.nancumsum, np.nancumprod]
  531. stdfuncs = [np.cumsum, np.cumprod]
  532. @pytest.mark.parametrize("axis", [None, 0, 1])
  533. @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"])
  534. @pytest.mark.parametrize("array", [
  535. np.array(np.nan),
  536. np.full((3, 3), np.nan)
  537. ], ids=["0d", "2d"])
  538. def test_allnans(self, axis, dtype, array):
  539. if axis is not None and array.ndim == 0:
  540. pytest.skip("`axis != None` not supported for 0d arrays")
  541. array = array.astype(dtype)
  542. for func, identity in zip(self.nanfuncs, [0, 1]):
  543. out = func(array)
  544. assert np.all(out == identity)
  545. assert out.dtype == array.dtype
  546. def test_empty(self):
  547. for f, tgt_value in zip(self.nanfuncs, [0, 1]):
  548. mat = np.zeros((0, 3))
  549. tgt = tgt_value*np.ones((0, 3))
  550. res = f(mat, axis=0)
  551. assert_equal(res, tgt)
  552. tgt = mat
  553. res = f(mat, axis=1)
  554. assert_equal(res, tgt)
  555. tgt = np.zeros(0)
  556. res = f(mat, axis=None)
  557. assert_equal(res, tgt)
  558. def test_keepdims(self):
  559. for f, g in zip(self.nanfuncs, self.stdfuncs):
  560. mat = np.eye(3)
  561. for axis in [None, 0, 1]:
  562. tgt = f(mat, axis=axis, out=None)
  563. res = g(mat, axis=axis, out=None)
  564. assert_(res.ndim == tgt.ndim)
  565. for f in self.nanfuncs:
  566. d = np.ones((3, 5, 7, 11))
  567. # Randomly set some elements to NaN:
  568. rs = np.random.RandomState(0)
  569. d[rs.rand(*d.shape) < 0.5] = np.nan
  570. res = f(d, axis=None)
  571. assert_equal(res.shape, (1155,))
  572. for axis in np.arange(4):
  573. res = f(d, axis=axis)
  574. assert_equal(res.shape, (3, 5, 7, 11))
  575. def test_result_values(self):
  576. for axis in (-2, -1, 0, 1, None):
  577. tgt = np.cumprod(_ndat_ones, axis=axis)
  578. res = np.nancumprod(_ndat, axis=axis)
  579. assert_almost_equal(res, tgt)
  580. tgt = np.cumsum(_ndat_zeros,axis=axis)
  581. res = np.nancumsum(_ndat, axis=axis)
  582. assert_almost_equal(res, tgt)
  583. def test_out(self):
  584. mat = np.eye(3)
  585. for nf, rf in zip(self.nanfuncs, self.stdfuncs):
  586. resout = np.eye(3)
  587. for axis in (-2, -1, 0, 1):
  588. tgt = rf(mat, axis=axis)
  589. res = nf(mat, axis=axis, out=resout)
  590. assert_almost_equal(res, resout)
  591. assert_almost_equal(res, tgt)
  592. class TestNanFunctions_MeanVarStd(SharedNanFunctionsTestsMixin):
  593. nanfuncs = [np.nanmean, np.nanvar, np.nanstd]
  594. stdfuncs = [np.mean, np.var, np.std]
  595. def test_dtype_error(self):
  596. for f in self.nanfuncs:
  597. for dtype in [np.bool, np.int_, np.object_]:
  598. assert_raises(TypeError, f, _ndat, axis=1, dtype=dtype)
  599. def test_out_dtype_error(self):
  600. for f in self.nanfuncs:
  601. for dtype in [np.bool, np.int_, np.object_]:
  602. out = np.empty(_ndat.shape[0], dtype=dtype)
  603. assert_raises(TypeError, f, _ndat, axis=1, out=out)
  604. def test_ddof(self):
  605. nanfuncs = [np.nanvar, np.nanstd]
  606. stdfuncs = [np.var, np.std]
  607. for nf, rf in zip(nanfuncs, stdfuncs):
  608. for ddof in [0, 1]:
  609. tgt = [rf(d, ddof=ddof) for d in _rdat]
  610. res = nf(_ndat, axis=1, ddof=ddof)
  611. assert_almost_equal(res, tgt)
  612. def test_ddof_too_big(self):
  613. nanfuncs = [np.nanvar, np.nanstd]
  614. stdfuncs = [np.var, np.std]
  615. dsize = [len(d) for d in _rdat]
  616. for nf, rf in zip(nanfuncs, stdfuncs):
  617. for ddof in range(5):
  618. with suppress_warnings() as sup:
  619. sup.record(RuntimeWarning)
  620. sup.filter(ComplexWarning)
  621. tgt = [ddof >= d for d in dsize]
  622. res = nf(_ndat, axis=1, ddof=ddof)
  623. assert_equal(np.isnan(res), tgt)
  624. if any(tgt):
  625. assert_(len(sup.log) == 1)
  626. else:
  627. assert_(len(sup.log) == 0)
  628. @pytest.mark.parametrize("axis", [None, 0, 1])
  629. @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"])
  630. @pytest.mark.parametrize("array", [
  631. np.array(np.nan),
  632. np.full((3, 3), np.nan),
  633. ], ids=["0d", "2d"])
  634. def test_allnans(self, axis, dtype, array):
  635. if axis is not None and array.ndim == 0:
  636. pytest.skip("`axis != None` not supported for 0d arrays")
  637. array = array.astype(dtype)
  638. match = "(Degrees of freedom <= 0 for slice.)|(Mean of empty slice)"
  639. for func in self.nanfuncs:
  640. with pytest.warns(RuntimeWarning, match=match):
  641. out = func(array, axis=axis)
  642. assert np.isnan(out).all()
  643. # `nanvar` and `nanstd` convert complex inputs to their
  644. # corresponding floating dtype
  645. if func is np.nanmean:
  646. assert out.dtype == array.dtype
  647. else:
  648. assert out.dtype == np.abs(array).dtype
  649. def test_empty(self):
  650. mat = np.zeros((0, 3))
  651. for f in self.nanfuncs:
  652. for axis in [0, None]:
  653. with warnings.catch_warnings(record=True) as w:
  654. warnings.simplefilter('always')
  655. assert_(np.isnan(f(mat, axis=axis)).all())
  656. assert_(len(w) == 1)
  657. assert_(issubclass(w[0].category, RuntimeWarning))
  658. for axis in [1]:
  659. with warnings.catch_warnings(record=True) as w:
  660. warnings.simplefilter('always')
  661. assert_equal(f(mat, axis=axis), np.zeros([]))
  662. assert_(len(w) == 0)
  663. @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"])
  664. def test_where(self, dtype):
  665. ar = np.arange(9).reshape(3, 3).astype(dtype)
  666. ar[0, :] = np.nan
  667. where = np.ones_like(ar, dtype=np.bool)
  668. where[:, 0] = False
  669. for f, f_std in zip(self.nanfuncs, self.stdfuncs):
  670. reference = f_std(ar[where][2:])
  671. dtype_reference = dtype if f is np.nanmean else ar.real.dtype
  672. ret = f(ar, where=where)
  673. assert ret.dtype == dtype_reference
  674. np.testing.assert_allclose(ret, reference)
  675. def test_nanstd_with_mean_keyword(self):
  676. # Setting the seed to make the test reproducible
  677. rng = np.random.RandomState(1234)
  678. A = rng.randn(10, 20, 5) + 0.5
  679. A[:, 5, :] = np.nan
  680. mean_out = np.zeros((10, 1, 5))
  681. std_out = np.zeros((10, 1, 5))
  682. mean = np.nanmean(A,
  683. out=mean_out,
  684. axis=1,
  685. keepdims=True)
  686. # The returned object should be the object specified during calling
  687. assert mean_out is mean
  688. std = np.nanstd(A,
  689. out=std_out,
  690. axis=1,
  691. keepdims=True,
  692. mean=mean)
  693. # The returned object should be the object specified during calling
  694. assert std_out is std
  695. # Shape of returned mean and std should be same
  696. assert std.shape == mean.shape
  697. assert std.shape == (10, 1, 5)
  698. # Output should be the same as from the individual algorithms
  699. std_old = np.nanstd(A, axis=1, keepdims=True)
  700. assert std_old.shape == mean.shape
  701. assert_almost_equal(std, std_old)
  702. _TIME_UNITS = (
  703. "Y", "M", "W", "D", "h", "m", "s", "ms", "us", "ns", "ps", "fs", "as"
  704. )
  705. # All `inexact` + `timdelta64` type codes
  706. _TYPE_CODES = list(np.typecodes["AllFloat"])
  707. _TYPE_CODES += [f"m8[{unit}]" for unit in _TIME_UNITS]
  708. class TestNanFunctions_Median:
  709. def test_mutation(self):
  710. # Check that passed array is not modified.
  711. ndat = _ndat.copy()
  712. np.nanmedian(ndat)
  713. assert_equal(ndat, _ndat)
  714. def test_keepdims(self):
  715. mat = np.eye(3)
  716. for axis in [None, 0, 1]:
  717. tgt = np.median(mat, axis=axis, out=None, overwrite_input=False)
  718. res = np.nanmedian(mat, axis=axis, out=None, overwrite_input=False)
  719. assert_(res.ndim == tgt.ndim)
  720. d = np.ones((3, 5, 7, 11))
  721. # Randomly set some elements to NaN:
  722. w = np.random.random((4, 200)) * np.array(d.shape)[:, None]
  723. w = w.astype(np.intp)
  724. d[tuple(w)] = np.nan
  725. with suppress_warnings() as sup:
  726. sup.filter(RuntimeWarning)
  727. res = np.nanmedian(d, axis=None, keepdims=True)
  728. assert_equal(res.shape, (1, 1, 1, 1))
  729. res = np.nanmedian(d, axis=(0, 1), keepdims=True)
  730. assert_equal(res.shape, (1, 1, 7, 11))
  731. res = np.nanmedian(d, axis=(0, 3), keepdims=True)
  732. assert_equal(res.shape, (1, 5, 7, 1))
  733. res = np.nanmedian(d, axis=(1,), keepdims=True)
  734. assert_equal(res.shape, (3, 1, 7, 11))
  735. res = np.nanmedian(d, axis=(0, 1, 2, 3), keepdims=True)
  736. assert_equal(res.shape, (1, 1, 1, 1))
  737. res = np.nanmedian(d, axis=(0, 1, 3), keepdims=True)
  738. assert_equal(res.shape, (1, 1, 7, 1))
  739. @pytest.mark.parametrize(
  740. argnames='axis',
  741. argvalues=[
  742. None,
  743. 1,
  744. (1, ),
  745. (0, 1),
  746. (-3, -1),
  747. ]
  748. )
  749. @pytest.mark.filterwarnings("ignore:All-NaN slice:RuntimeWarning")
  750. def test_keepdims_out(self, axis):
  751. d = np.ones((3, 5, 7, 11))
  752. # Randomly set some elements to NaN:
  753. w = np.random.random((4, 200)) * np.array(d.shape)[:, None]
  754. w = w.astype(np.intp)
  755. d[tuple(w)] = np.nan
  756. if axis is None:
  757. shape_out = (1,) * d.ndim
  758. else:
  759. axis_norm = normalize_axis_tuple(axis, d.ndim)
  760. shape_out = tuple(
  761. 1 if i in axis_norm else d.shape[i] for i in range(d.ndim))
  762. out = np.empty(shape_out)
  763. result = np.nanmedian(d, axis=axis, keepdims=True, out=out)
  764. assert result is out
  765. assert_equal(result.shape, shape_out)
  766. def test_out(self):
  767. mat = np.random.rand(3, 3)
  768. nan_mat = np.insert(mat, [0, 2], np.nan, axis=1)
  769. resout = np.zeros(3)
  770. tgt = np.median(mat, axis=1)
  771. res = np.nanmedian(nan_mat, axis=1, out=resout)
  772. assert_almost_equal(res, resout)
  773. assert_almost_equal(res, tgt)
  774. # 0-d output:
  775. resout = np.zeros(())
  776. tgt = np.median(mat, axis=None)
  777. res = np.nanmedian(nan_mat, axis=None, out=resout)
  778. assert_almost_equal(res, resout)
  779. assert_almost_equal(res, tgt)
  780. res = np.nanmedian(nan_mat, axis=(0, 1), out=resout)
  781. assert_almost_equal(res, resout)
  782. assert_almost_equal(res, tgt)
  783. def test_small_large(self):
  784. # test the small and large code paths, current cutoff 400 elements
  785. for s in [5, 20, 51, 200, 1000]:
  786. d = np.random.randn(4, s)
  787. # Randomly set some elements to NaN:
  788. w = np.random.randint(0, d.size, size=d.size // 5)
  789. d.ravel()[w] = np.nan
  790. d[:,0] = 1. # ensure at least one good value
  791. # use normal median without nans to compare
  792. tgt = []
  793. for x in d:
  794. nonan = np.compress(~np.isnan(x), x)
  795. tgt.append(np.median(nonan, overwrite_input=True))
  796. assert_array_equal(np.nanmedian(d, axis=-1), tgt)
  797. def test_result_values(self):
  798. tgt = [np.median(d) for d in _rdat]
  799. res = np.nanmedian(_ndat, axis=1)
  800. assert_almost_equal(res, tgt)
  801. @pytest.mark.parametrize("axis", [None, 0, 1])
  802. @pytest.mark.parametrize("dtype", _TYPE_CODES)
  803. def test_allnans(self, dtype, axis):
  804. mat = np.full((3, 3), np.nan).astype(dtype)
  805. with suppress_warnings() as sup:
  806. sup.record(RuntimeWarning)
  807. output = np.nanmedian(mat, axis=axis)
  808. assert output.dtype == mat.dtype
  809. assert np.isnan(output).all()
  810. if axis is None:
  811. assert_(len(sup.log) == 1)
  812. else:
  813. assert_(len(sup.log) == 3)
  814. # Check scalar
  815. scalar = np.array(np.nan).astype(dtype)[()]
  816. output_scalar = np.nanmedian(scalar)
  817. assert output_scalar.dtype == scalar.dtype
  818. assert np.isnan(output_scalar)
  819. if axis is None:
  820. assert_(len(sup.log) == 2)
  821. else:
  822. assert_(len(sup.log) == 4)
  823. def test_empty(self):
  824. mat = np.zeros((0, 3))
  825. for axis in [0, None]:
  826. with warnings.catch_warnings(record=True) as w:
  827. warnings.simplefilter('always')
  828. assert_(np.isnan(np.nanmedian(mat, axis=axis)).all())
  829. assert_(len(w) == 1)
  830. assert_(issubclass(w[0].category, RuntimeWarning))
  831. for axis in [1]:
  832. with warnings.catch_warnings(record=True) as w:
  833. warnings.simplefilter('always')
  834. assert_equal(np.nanmedian(mat, axis=axis), np.zeros([]))
  835. assert_(len(w) == 0)
  836. def test_scalar(self):
  837. assert_(np.nanmedian(0.) == 0.)
  838. def test_extended_axis_invalid(self):
  839. d = np.ones((3, 5, 7, 11))
  840. assert_raises(AxisError, np.nanmedian, d, axis=-5)
  841. assert_raises(AxisError, np.nanmedian, d, axis=(0, -5))
  842. assert_raises(AxisError, np.nanmedian, d, axis=4)
  843. assert_raises(AxisError, np.nanmedian, d, axis=(0, 4))
  844. assert_raises(ValueError, np.nanmedian, d, axis=(1, 1))
  845. def test_float_special(self):
  846. with suppress_warnings() as sup:
  847. sup.filter(RuntimeWarning)
  848. for inf in [np.inf, -np.inf]:
  849. a = np.array([[inf, np.nan], [np.nan, np.nan]])
  850. assert_equal(np.nanmedian(a, axis=0), [inf, np.nan])
  851. assert_equal(np.nanmedian(a, axis=1), [inf, np.nan])
  852. assert_equal(np.nanmedian(a), inf)
  853. # minimum fill value check
  854. a = np.array([[np.nan, np.nan, inf],
  855. [np.nan, np.nan, inf]])
  856. assert_equal(np.nanmedian(a), inf)
  857. assert_equal(np.nanmedian(a, axis=0), [np.nan, np.nan, inf])
  858. assert_equal(np.nanmedian(a, axis=1), inf)
  859. # no mask path
  860. a = np.array([[inf, inf], [inf, inf]])
  861. assert_equal(np.nanmedian(a, axis=1), inf)
  862. a = np.array([[inf, 7, -inf, -9],
  863. [-10, np.nan, np.nan, 5],
  864. [4, np.nan, np.nan, inf]],
  865. dtype=np.float32)
  866. if inf > 0:
  867. assert_equal(np.nanmedian(a, axis=0), [4., 7., -inf, 5.])
  868. assert_equal(np.nanmedian(a), 4.5)
  869. else:
  870. assert_equal(np.nanmedian(a, axis=0), [-10., 7., -inf, -9.])
  871. assert_equal(np.nanmedian(a), -2.5)
  872. assert_equal(np.nanmedian(a, axis=-1), [-1., -2.5, inf])
  873. for i in range(0, 10):
  874. for j in range(1, 10):
  875. a = np.array([([np.nan] * i) + ([inf] * j)] * 2)
  876. assert_equal(np.nanmedian(a), inf)
  877. assert_equal(np.nanmedian(a, axis=1), inf)
  878. assert_equal(np.nanmedian(a, axis=0),
  879. ([np.nan] * i) + [inf] * j)
  880. a = np.array([([np.nan] * i) + ([-inf] * j)] * 2)
  881. assert_equal(np.nanmedian(a), -inf)
  882. assert_equal(np.nanmedian(a, axis=1), -inf)
  883. assert_equal(np.nanmedian(a, axis=0),
  884. ([np.nan] * i) + [-inf] * j)
  885. class TestNanFunctions_Percentile:
  886. def test_mutation(self):
  887. # Check that passed array is not modified.
  888. ndat = _ndat.copy()
  889. np.nanpercentile(ndat, 30)
  890. assert_equal(ndat, _ndat)
  891. def test_keepdims(self):
  892. mat = np.eye(3)
  893. for axis in [None, 0, 1]:
  894. tgt = np.percentile(mat, 70, axis=axis, out=None,
  895. overwrite_input=False)
  896. res = np.nanpercentile(mat, 70, axis=axis, out=None,
  897. overwrite_input=False)
  898. assert_(res.ndim == tgt.ndim)
  899. d = np.ones((3, 5, 7, 11))
  900. # Randomly set some elements to NaN:
  901. w = np.random.random((4, 200)) * np.array(d.shape)[:, None]
  902. w = w.astype(np.intp)
  903. d[tuple(w)] = np.nan
  904. with suppress_warnings() as sup:
  905. sup.filter(RuntimeWarning)
  906. res = np.nanpercentile(d, 90, axis=None, keepdims=True)
  907. assert_equal(res.shape, (1, 1, 1, 1))
  908. res = np.nanpercentile(d, 90, axis=(0, 1), keepdims=True)
  909. assert_equal(res.shape, (1, 1, 7, 11))
  910. res = np.nanpercentile(d, 90, axis=(0, 3), keepdims=True)
  911. assert_equal(res.shape, (1, 5, 7, 1))
  912. res = np.nanpercentile(d, 90, axis=(1,), keepdims=True)
  913. assert_equal(res.shape, (3, 1, 7, 11))
  914. res = np.nanpercentile(d, 90, axis=(0, 1, 2, 3), keepdims=True)
  915. assert_equal(res.shape, (1, 1, 1, 1))
  916. res = np.nanpercentile(d, 90, axis=(0, 1, 3), keepdims=True)
  917. assert_equal(res.shape, (1, 1, 7, 1))
  918. @pytest.mark.parametrize('q', [7, [1, 7]])
  919. @pytest.mark.parametrize(
  920. argnames='axis',
  921. argvalues=[
  922. None,
  923. 1,
  924. (1,),
  925. (0, 1),
  926. (-3, -1),
  927. ]
  928. )
  929. @pytest.mark.filterwarnings("ignore:All-NaN slice:RuntimeWarning")
  930. def test_keepdims_out(self, q, axis):
  931. d = np.ones((3, 5, 7, 11))
  932. # Randomly set some elements to NaN:
  933. w = np.random.random((4, 200)) * np.array(d.shape)[:, None]
  934. w = w.astype(np.intp)
  935. d[tuple(w)] = np.nan
  936. if axis is None:
  937. shape_out = (1,) * d.ndim
  938. else:
  939. axis_norm = normalize_axis_tuple(axis, d.ndim)
  940. shape_out = tuple(
  941. 1 if i in axis_norm else d.shape[i] for i in range(d.ndim))
  942. shape_out = np.shape(q) + shape_out
  943. out = np.empty(shape_out)
  944. result = np.nanpercentile(d, q, axis=axis, keepdims=True, out=out)
  945. assert result is out
  946. assert_equal(result.shape, shape_out)
  947. @pytest.mark.parametrize("weighted", [False, True])
  948. def test_out(self, weighted):
  949. mat = np.random.rand(3, 3)
  950. nan_mat = np.insert(mat, [0, 2], np.nan, axis=1)
  951. resout = np.zeros(3)
  952. if weighted:
  953. w_args = {"weights": np.ones_like(mat), "method": "inverted_cdf"}
  954. nan_w_args = {
  955. "weights": np.ones_like(nan_mat), "method": "inverted_cdf"
  956. }
  957. else:
  958. w_args = dict()
  959. nan_w_args = dict()
  960. tgt = np.percentile(mat, 42, axis=1, **w_args)
  961. res = np.nanpercentile(nan_mat, 42, axis=1, out=resout, **nan_w_args)
  962. assert_almost_equal(res, resout)
  963. assert_almost_equal(res, tgt)
  964. # 0-d output:
  965. resout = np.zeros(())
  966. tgt = np.percentile(mat, 42, axis=None, **w_args)
  967. res = np.nanpercentile(
  968. nan_mat, 42, axis=None, out=resout, **nan_w_args
  969. )
  970. assert_almost_equal(res, resout)
  971. assert_almost_equal(res, tgt)
  972. res = np.nanpercentile(
  973. nan_mat, 42, axis=(0, 1), out=resout, **nan_w_args
  974. )
  975. assert_almost_equal(res, resout)
  976. assert_almost_equal(res, tgt)
  977. def test_complex(self):
  978. arr_c = np.array([0.5+3.0j, 2.1+0.5j, 1.6+2.3j], dtype='G')
  979. assert_raises(TypeError, np.nanpercentile, arr_c, 0.5)
  980. arr_c = np.array([0.5+3.0j, 2.1+0.5j, 1.6+2.3j], dtype='D')
  981. assert_raises(TypeError, np.nanpercentile, arr_c, 0.5)
  982. arr_c = np.array([0.5+3.0j, 2.1+0.5j, 1.6+2.3j], dtype='F')
  983. assert_raises(TypeError, np.nanpercentile, arr_c, 0.5)
  984. @pytest.mark.parametrize("weighted", [False, True])
  985. @pytest.mark.parametrize("use_out", [False, True])
  986. def test_result_values(self, weighted, use_out):
  987. if weighted:
  988. percentile = partial(np.percentile, method="inverted_cdf")
  989. nanpercentile = partial(np.nanpercentile, method="inverted_cdf")
  990. def gen_weights(d):
  991. return np.ones_like(d)
  992. else:
  993. percentile = np.percentile
  994. nanpercentile = np.nanpercentile
  995. def gen_weights(d):
  996. return None
  997. tgt = [percentile(d, 28, weights=gen_weights(d)) for d in _rdat]
  998. out = np.empty_like(tgt) if use_out else None
  999. res = nanpercentile(_ndat, 28, axis=1,
  1000. weights=gen_weights(_ndat), out=out)
  1001. assert_almost_equal(res, tgt)
  1002. # Transpose the array to fit the output convention of numpy.percentile
  1003. tgt = np.transpose([percentile(d, (28, 98), weights=gen_weights(d))
  1004. for d in _rdat])
  1005. out = np.empty_like(tgt) if use_out else None
  1006. res = nanpercentile(_ndat, (28, 98), axis=1,
  1007. weights=gen_weights(_ndat), out=out)
  1008. assert_almost_equal(res, tgt)
  1009. @pytest.mark.parametrize("axis", [None, 0, 1])
  1010. @pytest.mark.parametrize("dtype", np.typecodes["Float"])
  1011. @pytest.mark.parametrize("array", [
  1012. np.array(np.nan),
  1013. np.full((3, 3), np.nan),
  1014. ], ids=["0d", "2d"])
  1015. def test_allnans(self, axis, dtype, array):
  1016. if axis is not None and array.ndim == 0:
  1017. pytest.skip("`axis != None` not supported for 0d arrays")
  1018. array = array.astype(dtype)
  1019. with pytest.warns(RuntimeWarning, match="All-NaN slice encountered"):
  1020. out = np.nanpercentile(array, 60, axis=axis)
  1021. assert np.isnan(out).all()
  1022. assert out.dtype == array.dtype
  1023. def test_empty(self):
  1024. mat = np.zeros((0, 3))
  1025. for axis in [0, None]:
  1026. with warnings.catch_warnings(record=True) as w:
  1027. warnings.simplefilter('always')
  1028. assert_(np.isnan(np.nanpercentile(mat, 40, axis=axis)).all())
  1029. assert_(len(w) == 1)
  1030. assert_(issubclass(w[0].category, RuntimeWarning))
  1031. for axis in [1]:
  1032. with warnings.catch_warnings(record=True) as w:
  1033. warnings.simplefilter('always')
  1034. assert_equal(np.nanpercentile(mat, 40, axis=axis), np.zeros([]))
  1035. assert_(len(w) == 0)
  1036. def test_scalar(self):
  1037. assert_equal(np.nanpercentile(0., 100), 0.)
  1038. a = np.arange(6)
  1039. r = np.nanpercentile(a, 50, axis=0)
  1040. assert_equal(r, 2.5)
  1041. assert_(np.isscalar(r))
  1042. def test_extended_axis_invalid(self):
  1043. d = np.ones((3, 5, 7, 11))
  1044. assert_raises(AxisError, np.nanpercentile, d, q=5, axis=-5)
  1045. assert_raises(AxisError, np.nanpercentile, d, q=5, axis=(0, -5))
  1046. assert_raises(AxisError, np.nanpercentile, d, q=5, axis=4)
  1047. assert_raises(AxisError, np.nanpercentile, d, q=5, axis=(0, 4))
  1048. assert_raises(ValueError, np.nanpercentile, d, q=5, axis=(1, 1))
  1049. def test_multiple_percentiles(self):
  1050. perc = [50, 100]
  1051. mat = np.ones((4, 3))
  1052. nan_mat = np.nan * mat
  1053. # For checking consistency in higher dimensional case
  1054. large_mat = np.ones((3, 4, 5))
  1055. large_mat[:, 0:2:4, :] = 0
  1056. large_mat[:, :, 3:] *= 2
  1057. for axis in [None, 0, 1]:
  1058. for keepdim in [False, True]:
  1059. with suppress_warnings() as sup:
  1060. sup.filter(RuntimeWarning, "All-NaN slice encountered")
  1061. val = np.percentile(mat, perc, axis=axis, keepdims=keepdim)
  1062. nan_val = np.nanpercentile(nan_mat, perc, axis=axis,
  1063. keepdims=keepdim)
  1064. assert_equal(nan_val.shape, val.shape)
  1065. val = np.percentile(large_mat, perc, axis=axis,
  1066. keepdims=keepdim)
  1067. nan_val = np.nanpercentile(large_mat, perc, axis=axis,
  1068. keepdims=keepdim)
  1069. assert_equal(nan_val, val)
  1070. megamat = np.ones((3, 4, 5, 6))
  1071. assert_equal(
  1072. np.nanpercentile(megamat, perc, axis=(1, 2)).shape, (2, 3, 6)
  1073. )
  1074. @pytest.mark.parametrize("nan_weight", [0, 1, 2, 3, 1e200])
  1075. def test_nan_value_with_weight(self, nan_weight):
  1076. x = [1, np.nan, 2, 3]
  1077. result = np.float64(2.0)
  1078. q_unweighted = np.nanpercentile(x, 50, method="inverted_cdf")
  1079. assert_equal(q_unweighted, result)
  1080. # The weight value at the nan position should not matter.
  1081. w = [1.0, nan_weight, 1.0, 1.0]
  1082. q_weighted = np.nanpercentile(x, 50, weights=w, method="inverted_cdf")
  1083. assert_equal(q_weighted, result)
  1084. @pytest.mark.parametrize("axis", [0, 1, 2])
  1085. def test_nan_value_with_weight_ndim(self, axis):
  1086. # Create a multi-dimensional array to test
  1087. np.random.seed(1)
  1088. x_no_nan = np.random.random(size=(100, 99, 2))
  1089. # Set some places to NaN (not particularly smart) so there is always
  1090. # some non-Nan.
  1091. x = x_no_nan.copy()
  1092. x[np.arange(99), np.arange(99), 0] = np.nan
  1093. p = np.array([[20., 50., 30], [70, 33, 80]])
  1094. # We just use ones as weights, but replace it with 0 or 1e200 at the
  1095. # NaN positions below.
  1096. weights = np.ones_like(x)
  1097. # For comparison use weighted normal percentile with nan weights at
  1098. # 0 (and no NaNs); not sure this is strictly identical but should be
  1099. # sufficiently so (if a percentile lies exactly on a 0 value).
  1100. weights[np.isnan(x)] = 0
  1101. p_expected = np.percentile(
  1102. x_no_nan, p, axis=axis, weights=weights, method="inverted_cdf")
  1103. p_unweighted = np.nanpercentile(
  1104. x, p, axis=axis, method="inverted_cdf")
  1105. # The normal and unweighted versions should be identical:
  1106. assert_equal(p_unweighted, p_expected)
  1107. weights[np.isnan(x)] = 1e200 # huge value, shouldn't matter
  1108. p_weighted = np.nanpercentile(
  1109. x, p, axis=axis, weights=weights, method="inverted_cdf")
  1110. assert_equal(p_weighted, p_expected)
  1111. # Also check with out passed:
  1112. out = np.empty_like(p_weighted)
  1113. res = np.nanpercentile(
  1114. x, p, axis=axis, weights=weights, out=out, method="inverted_cdf")
  1115. assert res is out
  1116. assert_equal(out, p_expected)
  1117. class TestNanFunctions_Quantile:
  1118. # most of this is already tested by TestPercentile
  1119. @pytest.mark.parametrize("weighted", [False, True])
  1120. def test_regression(self, weighted):
  1121. ar = np.arange(24).reshape(2, 3, 4).astype(float)
  1122. ar[0][1] = np.nan
  1123. if weighted:
  1124. w_args = {"weights": np.ones_like(ar), "method": "inverted_cdf"}
  1125. else:
  1126. w_args = dict()
  1127. assert_equal(np.nanquantile(ar, q=0.5, **w_args),
  1128. np.nanpercentile(ar, q=50, **w_args))
  1129. assert_equal(np.nanquantile(ar, q=0.5, axis=0, **w_args),
  1130. np.nanpercentile(ar, q=50, axis=0, **w_args))
  1131. assert_equal(np.nanquantile(ar, q=0.5, axis=1, **w_args),
  1132. np.nanpercentile(ar, q=50, axis=1, **w_args))
  1133. assert_equal(np.nanquantile(ar, q=[0.5], axis=1, **w_args),
  1134. np.nanpercentile(ar, q=[50], axis=1, **w_args))
  1135. assert_equal(np.nanquantile(ar, q=[0.25, 0.5, 0.75], axis=1, **w_args),
  1136. np.nanpercentile(ar, q=[25, 50, 75], axis=1, **w_args))
  1137. def test_basic(self):
  1138. x = np.arange(8) * 0.5
  1139. assert_equal(np.nanquantile(x, 0), 0.)
  1140. assert_equal(np.nanquantile(x, 1), 3.5)
  1141. assert_equal(np.nanquantile(x, 0.5), 1.75)
  1142. def test_complex(self):
  1143. arr_c = np.array([0.5+3.0j, 2.1+0.5j, 1.6+2.3j], dtype='G')
  1144. assert_raises(TypeError, np.nanquantile, arr_c, 0.5)
  1145. arr_c = np.array([0.5+3.0j, 2.1+0.5j, 1.6+2.3j], dtype='D')
  1146. assert_raises(TypeError, np.nanquantile, arr_c, 0.5)
  1147. arr_c = np.array([0.5+3.0j, 2.1+0.5j, 1.6+2.3j], dtype='F')
  1148. assert_raises(TypeError, np.nanquantile, arr_c, 0.5)
  1149. def test_no_p_overwrite(self):
  1150. # this is worth retesting, because quantile does not make a copy
  1151. p0 = np.array([0, 0.75, 0.25, 0.5, 1.0])
  1152. p = p0.copy()
  1153. np.nanquantile(np.arange(100.), p, method="midpoint")
  1154. assert_array_equal(p, p0)
  1155. p0 = p0.tolist()
  1156. p = p.tolist()
  1157. np.nanquantile(np.arange(100.), p, method="midpoint")
  1158. assert_array_equal(p, p0)
  1159. @pytest.mark.parametrize("axis", [None, 0, 1])
  1160. @pytest.mark.parametrize("dtype", np.typecodes["Float"])
  1161. @pytest.mark.parametrize("array", [
  1162. np.array(np.nan),
  1163. np.full((3, 3), np.nan),
  1164. ], ids=["0d", "2d"])
  1165. def test_allnans(self, axis, dtype, array):
  1166. if axis is not None and array.ndim == 0:
  1167. pytest.skip("`axis != None` not supported for 0d arrays")
  1168. array = array.astype(dtype)
  1169. with pytest.warns(RuntimeWarning, match="All-NaN slice encountered"):
  1170. out = np.nanquantile(array, 1, axis=axis)
  1171. assert np.isnan(out).all()
  1172. assert out.dtype == array.dtype
  1173. @pytest.mark.parametrize("arr, expected", [
  1174. # array of floats with some nans
  1175. (np.array([np.nan, 5.0, np.nan, np.inf]),
  1176. np.array([False, True, False, True])),
  1177. # int64 array that can't possibly have nans
  1178. (np.array([1, 5, 7, 9], dtype=np.int64),
  1179. True),
  1180. # bool array that can't possibly have nans
  1181. (np.array([False, True, False, True]),
  1182. True),
  1183. # 2-D complex array with nans
  1184. (np.array([[np.nan, 5.0],
  1185. [np.nan, np.inf]], dtype=np.complex64),
  1186. np.array([[False, True],
  1187. [False, True]])),
  1188. ])
  1189. def test__nan_mask(arr, expected):
  1190. for out in [None, np.empty(arr.shape, dtype=np.bool)]:
  1191. actual = _nan_mask(arr, out=out)
  1192. assert_equal(actual, expected)
  1193. # the above won't distinguish between True proper
  1194. # and an array of True values; we want True proper
  1195. # for types that can't possibly contain NaN
  1196. if type(expected) is not np.ndarray:
  1197. assert actual is True
  1198. def test__replace_nan():
  1199. """ Test that _replace_nan returns the original array if there are no
  1200. NaNs, not a copy.
  1201. """
  1202. for dtype in [np.bool, np.int32, np.int64]:
  1203. arr = np.array([0, 1], dtype=dtype)
  1204. result, mask = _replace_nan(arr, 0)
  1205. assert mask is None
  1206. # do not make a copy if there are no nans
  1207. assert result is arr
  1208. for dtype in [np.float32, np.float64]:
  1209. arr = np.array([0, 1], dtype=dtype)
  1210. result, mask = _replace_nan(arr, 2)
  1211. assert (mask == False).all()
  1212. # mask is not None, so we make a copy
  1213. assert result is not arr
  1214. assert_equal(result, arr)
  1215. arr_nan = np.array([0, 1, np.nan], dtype=dtype)
  1216. result_nan, mask_nan = _replace_nan(arr_nan, 2)
  1217. assert_equal(mask_nan, np.array([False, False, True]))
  1218. assert result_nan is not arr_nan
  1219. assert_equal(result_nan, np.array([0, 1, 2]))
  1220. assert np.isnan(arr_nan[-1])