test_arithmetics.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. import operator
  2. import numpy as np
  3. import pytest
  4. import pandas as pd
  5. import pandas._testing as tm
  6. from pandas.core.arrays.sparse import (
  7. SparseArray,
  8. SparseDtype,
  9. )
  10. @pytest.fixture(params=["integer", "block"])
  11. def kind(request):
  12. """kind kwarg to pass to SparseArray"""
  13. return request.param
  14. @pytest.fixture(params=[True, False])
  15. def mix(request):
  16. """
  17. Fixture returning True or False, determining whether to operate
  18. op(sparse, dense) instead of op(sparse, sparse)
  19. """
  20. return request.param
  21. class TestSparseArrayArithmetics:
  22. def _assert(self, a, b):
  23. # We have to use tm.assert_sp_array_equal. See GH #45126
  24. tm.assert_numpy_array_equal(a, b)
  25. def _check_numeric_ops(self, a, b, a_dense, b_dense, mix: bool, op):
  26. # Check that arithmetic behavior matches non-Sparse Series arithmetic
  27. if isinstance(a_dense, np.ndarray):
  28. expected = op(pd.Series(a_dense), b_dense).values
  29. elif isinstance(b_dense, np.ndarray):
  30. expected = op(a_dense, pd.Series(b_dense)).values
  31. else:
  32. raise NotImplementedError
  33. with np.errstate(invalid="ignore", divide="ignore"):
  34. if mix:
  35. result = op(a, b_dense).to_dense()
  36. else:
  37. result = op(a, b).to_dense()
  38. self._assert(result, expected)
  39. def _check_bool_result(self, res):
  40. assert isinstance(res, SparseArray)
  41. assert isinstance(res.dtype, SparseDtype)
  42. assert res.dtype.subtype == np.bool_
  43. assert isinstance(res.fill_value, bool)
  44. def _check_comparison_ops(self, a, b, a_dense, b_dense):
  45. with np.errstate(invalid="ignore"):
  46. # Unfortunately, trying to wrap the computation of each expected
  47. # value is with np.errstate() is too tedious.
  48. #
  49. # sparse & sparse
  50. self._check_bool_result(a == b)
  51. self._assert((a == b).to_dense(), a_dense == b_dense)
  52. self._check_bool_result(a != b)
  53. self._assert((a != b).to_dense(), a_dense != b_dense)
  54. self._check_bool_result(a >= b)
  55. self._assert((a >= b).to_dense(), a_dense >= b_dense)
  56. self._check_bool_result(a <= b)
  57. self._assert((a <= b).to_dense(), a_dense <= b_dense)
  58. self._check_bool_result(a > b)
  59. self._assert((a > b).to_dense(), a_dense > b_dense)
  60. self._check_bool_result(a < b)
  61. self._assert((a < b).to_dense(), a_dense < b_dense)
  62. # sparse & dense
  63. self._check_bool_result(a == b_dense)
  64. self._assert((a == b_dense).to_dense(), a_dense == b_dense)
  65. self._check_bool_result(a != b_dense)
  66. self._assert((a != b_dense).to_dense(), a_dense != b_dense)
  67. self._check_bool_result(a >= b_dense)
  68. self._assert((a >= b_dense).to_dense(), a_dense >= b_dense)
  69. self._check_bool_result(a <= b_dense)
  70. self._assert((a <= b_dense).to_dense(), a_dense <= b_dense)
  71. self._check_bool_result(a > b_dense)
  72. self._assert((a > b_dense).to_dense(), a_dense > b_dense)
  73. self._check_bool_result(a < b_dense)
  74. self._assert((a < b_dense).to_dense(), a_dense < b_dense)
  75. def _check_logical_ops(self, a, b, a_dense, b_dense):
  76. # sparse & sparse
  77. self._check_bool_result(a & b)
  78. self._assert((a & b).to_dense(), a_dense & b_dense)
  79. self._check_bool_result(a | b)
  80. self._assert((a | b).to_dense(), a_dense | b_dense)
  81. # sparse & dense
  82. self._check_bool_result(a & b_dense)
  83. self._assert((a & b_dense).to_dense(), a_dense & b_dense)
  84. self._check_bool_result(a | b_dense)
  85. self._assert((a | b_dense).to_dense(), a_dense | b_dense)
  86. @pytest.mark.parametrize("scalar", [0, 1, 3])
  87. @pytest.mark.parametrize("fill_value", [None, 0, 2])
  88. def test_float_scalar(
  89. self, kind, mix, all_arithmetic_functions, fill_value, scalar, request
  90. ):
  91. op = all_arithmetic_functions
  92. values = np.array([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan])
  93. a = SparseArray(values, kind=kind, fill_value=fill_value)
  94. self._check_numeric_ops(a, scalar, values, scalar, mix, op)
  95. def test_float_scalar_comparison(self, kind):
  96. values = np.array([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan])
  97. a = SparseArray(values, kind=kind)
  98. self._check_comparison_ops(a, 1, values, 1)
  99. self._check_comparison_ops(a, 0, values, 0)
  100. self._check_comparison_ops(a, 3, values, 3)
  101. a = SparseArray(values, kind=kind, fill_value=0)
  102. self._check_comparison_ops(a, 1, values, 1)
  103. self._check_comparison_ops(a, 0, values, 0)
  104. self._check_comparison_ops(a, 3, values, 3)
  105. a = SparseArray(values, kind=kind, fill_value=2)
  106. self._check_comparison_ops(a, 1, values, 1)
  107. self._check_comparison_ops(a, 0, values, 0)
  108. self._check_comparison_ops(a, 3, values, 3)
  109. def test_float_same_index_without_nans(self, kind, mix, all_arithmetic_functions):
  110. # when sp_index are the same
  111. op = all_arithmetic_functions
  112. values = np.array([0.0, 1.0, 2.0, 6.0, 0.0, 0.0, 1.0, 2.0, 1.0, 0.0])
  113. rvalues = np.array([0.0, 2.0, 3.0, 4.0, 0.0, 0.0, 1.0, 3.0, 2.0, 0.0])
  114. a = SparseArray(values, kind=kind, fill_value=0)
  115. b = SparseArray(rvalues, kind=kind, fill_value=0)
  116. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  117. def test_float_same_index_with_nans(
  118. self, kind, mix, all_arithmetic_functions, request
  119. ):
  120. # when sp_index are the same
  121. op = all_arithmetic_functions
  122. values = np.array([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan])
  123. rvalues = np.array([np.nan, 2, 3, 4, np.nan, 0, 1, 3, 2, np.nan])
  124. a = SparseArray(values, kind=kind)
  125. b = SparseArray(rvalues, kind=kind)
  126. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  127. def test_float_same_index_comparison(self, kind):
  128. # when sp_index are the same
  129. values = np.array([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan])
  130. rvalues = np.array([np.nan, 2, 3, 4, np.nan, 0, 1, 3, 2, np.nan])
  131. a = SparseArray(values, kind=kind)
  132. b = SparseArray(rvalues, kind=kind)
  133. self._check_comparison_ops(a, b, values, rvalues)
  134. values = np.array([0.0, 1.0, 2.0, 6.0, 0.0, 0.0, 1.0, 2.0, 1.0, 0.0])
  135. rvalues = np.array([0.0, 2.0, 3.0, 4.0, 0.0, 0.0, 1.0, 3.0, 2.0, 0.0])
  136. a = SparseArray(values, kind=kind, fill_value=0)
  137. b = SparseArray(rvalues, kind=kind, fill_value=0)
  138. self._check_comparison_ops(a, b, values, rvalues)
  139. def test_float_array(self, kind, mix, all_arithmetic_functions):
  140. op = all_arithmetic_functions
  141. values = np.array([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan])
  142. rvalues = np.array([2, np.nan, 2, 3, np.nan, 0, 1, 5, 2, np.nan])
  143. a = SparseArray(values, kind=kind)
  144. b = SparseArray(rvalues, kind=kind)
  145. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  146. self._check_numeric_ops(a, b * 0, values, rvalues * 0, mix, op)
  147. a = SparseArray(values, kind=kind, fill_value=0)
  148. b = SparseArray(rvalues, kind=kind)
  149. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  150. a = SparseArray(values, kind=kind, fill_value=0)
  151. b = SparseArray(rvalues, kind=kind, fill_value=0)
  152. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  153. a = SparseArray(values, kind=kind, fill_value=1)
  154. b = SparseArray(rvalues, kind=kind, fill_value=2)
  155. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  156. def test_float_array_different_kind(self, mix, all_arithmetic_functions):
  157. op = all_arithmetic_functions
  158. values = np.array([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan])
  159. rvalues = np.array([2, np.nan, 2, 3, np.nan, 0, 1, 5, 2, np.nan])
  160. a = SparseArray(values, kind="integer")
  161. b = SparseArray(rvalues, kind="block")
  162. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  163. self._check_numeric_ops(a, b * 0, values, rvalues * 0, mix, op)
  164. a = SparseArray(values, kind="integer", fill_value=0)
  165. b = SparseArray(rvalues, kind="block")
  166. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  167. a = SparseArray(values, kind="integer", fill_value=0)
  168. b = SparseArray(rvalues, kind="block", fill_value=0)
  169. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  170. a = SparseArray(values, kind="integer", fill_value=1)
  171. b = SparseArray(rvalues, kind="block", fill_value=2)
  172. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  173. def test_float_array_comparison(self, kind):
  174. values = np.array([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan])
  175. rvalues = np.array([2, np.nan, 2, 3, np.nan, 0, 1, 5, 2, np.nan])
  176. a = SparseArray(values, kind=kind)
  177. b = SparseArray(rvalues, kind=kind)
  178. self._check_comparison_ops(a, b, values, rvalues)
  179. self._check_comparison_ops(a, b * 0, values, rvalues * 0)
  180. a = SparseArray(values, kind=kind, fill_value=0)
  181. b = SparseArray(rvalues, kind=kind)
  182. self._check_comparison_ops(a, b, values, rvalues)
  183. a = SparseArray(values, kind=kind, fill_value=0)
  184. b = SparseArray(rvalues, kind=kind, fill_value=0)
  185. self._check_comparison_ops(a, b, values, rvalues)
  186. a = SparseArray(values, kind=kind, fill_value=1)
  187. b = SparseArray(rvalues, kind=kind, fill_value=2)
  188. self._check_comparison_ops(a, b, values, rvalues)
  189. def test_int_array(self, kind, mix, all_arithmetic_functions):
  190. op = all_arithmetic_functions
  191. # have to specify dtype explicitly until fixing GH 667
  192. dtype = np.int64
  193. values = np.array([0, 1, 2, 0, 0, 0, 1, 2, 1, 0], dtype=dtype)
  194. rvalues = np.array([2, 0, 2, 3, 0, 0, 1, 5, 2, 0], dtype=dtype)
  195. a = SparseArray(values, dtype=dtype, kind=kind)
  196. assert a.dtype == SparseDtype(dtype)
  197. b = SparseArray(rvalues, dtype=dtype, kind=kind)
  198. assert b.dtype == SparseDtype(dtype)
  199. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  200. self._check_numeric_ops(a, b * 0, values, rvalues * 0, mix, op)
  201. a = SparseArray(values, fill_value=0, dtype=dtype, kind=kind)
  202. assert a.dtype == SparseDtype(dtype)
  203. b = SparseArray(rvalues, dtype=dtype, kind=kind)
  204. assert b.dtype == SparseDtype(dtype)
  205. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  206. a = SparseArray(values, fill_value=0, dtype=dtype, kind=kind)
  207. assert a.dtype == SparseDtype(dtype)
  208. b = SparseArray(rvalues, fill_value=0, dtype=dtype, kind=kind)
  209. assert b.dtype == SparseDtype(dtype)
  210. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  211. a = SparseArray(values, fill_value=1, dtype=dtype, kind=kind)
  212. assert a.dtype == SparseDtype(dtype, fill_value=1)
  213. b = SparseArray(rvalues, fill_value=2, dtype=dtype, kind=kind)
  214. assert b.dtype == SparseDtype(dtype, fill_value=2)
  215. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  216. def test_int_array_comparison(self, kind):
  217. dtype = "int64"
  218. # int32 NI ATM
  219. values = np.array([0, 1, 2, 0, 0, 0, 1, 2, 1, 0], dtype=dtype)
  220. rvalues = np.array([2, 0, 2, 3, 0, 0, 1, 5, 2, 0], dtype=dtype)
  221. a = SparseArray(values, dtype=dtype, kind=kind)
  222. b = SparseArray(rvalues, dtype=dtype, kind=kind)
  223. self._check_comparison_ops(a, b, values, rvalues)
  224. self._check_comparison_ops(a, b * 0, values, rvalues * 0)
  225. a = SparseArray(values, dtype=dtype, kind=kind, fill_value=0)
  226. b = SparseArray(rvalues, dtype=dtype, kind=kind)
  227. self._check_comparison_ops(a, b, values, rvalues)
  228. a = SparseArray(values, dtype=dtype, kind=kind, fill_value=0)
  229. b = SparseArray(rvalues, dtype=dtype, kind=kind, fill_value=0)
  230. self._check_comparison_ops(a, b, values, rvalues)
  231. a = SparseArray(values, dtype=dtype, kind=kind, fill_value=1)
  232. b = SparseArray(rvalues, dtype=dtype, kind=kind, fill_value=2)
  233. self._check_comparison_ops(a, b, values, rvalues)
  234. @pytest.mark.parametrize("fill_value", [True, False, np.nan])
  235. def test_bool_same_index(self, kind, fill_value):
  236. # GH 14000
  237. # when sp_index are the same
  238. values = np.array([True, False, True, True], dtype=np.bool_)
  239. rvalues = np.array([True, False, True, True], dtype=np.bool_)
  240. a = SparseArray(values, kind=kind, dtype=np.bool_, fill_value=fill_value)
  241. b = SparseArray(rvalues, kind=kind, dtype=np.bool_, fill_value=fill_value)
  242. self._check_logical_ops(a, b, values, rvalues)
  243. @pytest.mark.parametrize("fill_value", [True, False, np.nan])
  244. def test_bool_array_logical(self, kind, fill_value):
  245. # GH 14000
  246. # when sp_index are the same
  247. values = np.array([True, False, True, False, True, True], dtype=np.bool_)
  248. rvalues = np.array([True, False, False, True, False, True], dtype=np.bool_)
  249. a = SparseArray(values, kind=kind, dtype=np.bool_, fill_value=fill_value)
  250. b = SparseArray(rvalues, kind=kind, dtype=np.bool_, fill_value=fill_value)
  251. self._check_logical_ops(a, b, values, rvalues)
  252. def test_mixed_array_float_int(self, kind, mix, all_arithmetic_functions, request):
  253. op = all_arithmetic_functions
  254. rdtype = "int64"
  255. values = np.array([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan])
  256. rvalues = np.array([2, 0, 2, 3, 0, 0, 1, 5, 2, 0], dtype=rdtype)
  257. a = SparseArray(values, kind=kind)
  258. b = SparseArray(rvalues, kind=kind)
  259. assert b.dtype == SparseDtype(rdtype)
  260. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  261. self._check_numeric_ops(a, b * 0, values, rvalues * 0, mix, op)
  262. a = SparseArray(values, kind=kind, fill_value=0)
  263. b = SparseArray(rvalues, kind=kind)
  264. assert b.dtype == SparseDtype(rdtype)
  265. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  266. a = SparseArray(values, kind=kind, fill_value=0)
  267. b = SparseArray(rvalues, kind=kind, fill_value=0)
  268. assert b.dtype == SparseDtype(rdtype)
  269. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  270. a = SparseArray(values, kind=kind, fill_value=1)
  271. b = SparseArray(rvalues, kind=kind, fill_value=2)
  272. assert b.dtype == SparseDtype(rdtype, fill_value=2)
  273. self._check_numeric_ops(a, b, values, rvalues, mix, op)
  274. def test_mixed_array_comparison(self, kind):
  275. rdtype = "int64"
  276. # int32 NI ATM
  277. values = np.array([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan])
  278. rvalues = np.array([2, 0, 2, 3, 0, 0, 1, 5, 2, 0], dtype=rdtype)
  279. a = SparseArray(values, kind=kind)
  280. b = SparseArray(rvalues, kind=kind)
  281. assert b.dtype == SparseDtype(rdtype)
  282. self._check_comparison_ops(a, b, values, rvalues)
  283. self._check_comparison_ops(a, b * 0, values, rvalues * 0)
  284. a = SparseArray(values, kind=kind, fill_value=0)
  285. b = SparseArray(rvalues, kind=kind)
  286. assert b.dtype == SparseDtype(rdtype)
  287. self._check_comparison_ops(a, b, values, rvalues)
  288. a = SparseArray(values, kind=kind, fill_value=0)
  289. b = SparseArray(rvalues, kind=kind, fill_value=0)
  290. assert b.dtype == SparseDtype(rdtype)
  291. self._check_comparison_ops(a, b, values, rvalues)
  292. a = SparseArray(values, kind=kind, fill_value=1)
  293. b = SparseArray(rvalues, kind=kind, fill_value=2)
  294. assert b.dtype == SparseDtype(rdtype, fill_value=2)
  295. self._check_comparison_ops(a, b, values, rvalues)
  296. def test_xor(self):
  297. s = SparseArray([True, True, False, False])
  298. t = SparseArray([True, False, True, False])
  299. result = s ^ t
  300. sp_index = pd.core.arrays.sparse.IntIndex(4, np.array([0, 1, 2], dtype="int32"))
  301. expected = SparseArray([False, True, True], sparse_index=sp_index)
  302. tm.assert_sp_array_equal(result, expected)
  303. @pytest.mark.parametrize("op", [operator.eq, operator.add])
  304. def test_with_list(op):
  305. arr = SparseArray([0, 1], fill_value=0)
  306. result = op(arr, [0, 1])
  307. expected = op(arr, SparseArray([0, 1]))
  308. tm.assert_sp_array_equal(result, expected)
  309. def test_with_dataframe():
  310. # GH#27910
  311. arr = SparseArray([0, 1], fill_value=0)
  312. df = pd.DataFrame([[1, 2], [3, 4]])
  313. result = arr.__add__(df)
  314. assert result is NotImplemented
  315. def test_with_zerodim_ndarray():
  316. # GH#27910
  317. arr = SparseArray([0, 1], fill_value=0)
  318. result = arr * np.array(2)
  319. expected = arr * 2
  320. tm.assert_sp_array_equal(result, expected)
  321. @pytest.mark.parametrize("ufunc", [np.abs, np.exp])
  322. @pytest.mark.parametrize(
  323. "arr", [SparseArray([0, 0, -1, 1]), SparseArray([None, None, -1, 1])]
  324. )
  325. def test_ufuncs(ufunc, arr):
  326. result = ufunc(arr)
  327. fill_value = ufunc(arr.fill_value)
  328. expected = SparseArray(ufunc(np.asarray(arr)), fill_value=fill_value)
  329. tm.assert_sp_array_equal(result, expected)
  330. @pytest.mark.parametrize(
  331. "a, b",
  332. [
  333. (SparseArray([0, 0, 0]), np.array([0, 1, 2])),
  334. (SparseArray([0, 0, 0], fill_value=1), np.array([0, 1, 2])),
  335. (SparseArray([0, 0, 0], fill_value=1), np.array([0, 1, 2])),
  336. (SparseArray([0, 0, 0], fill_value=1), np.array([0, 1, 2])),
  337. (SparseArray([0, 0, 0], fill_value=1), np.array([0, 1, 2])),
  338. ],
  339. )
  340. @pytest.mark.parametrize("ufunc", [np.add, np.greater])
  341. def test_binary_ufuncs(ufunc, a, b):
  342. # can't say anything about fill value here.
  343. result = ufunc(a, b)
  344. expected = ufunc(np.asarray(a), np.asarray(b))
  345. assert isinstance(result, SparseArray)
  346. tm.assert_numpy_array_equal(np.asarray(result), expected)
  347. def test_ndarray_inplace():
  348. sparray = SparseArray([0, 2, 0, 0])
  349. ndarray = np.array([0, 1, 2, 3])
  350. ndarray += sparray
  351. expected = np.array([0, 3, 2, 3])
  352. tm.assert_numpy_array_equal(ndarray, expected)
  353. def test_sparray_inplace():
  354. sparray = SparseArray([0, 2, 0, 0])
  355. ndarray = np.array([0, 1, 2, 3])
  356. sparray += ndarray
  357. expected = SparseArray([0, 3, 2, 3], fill_value=0)
  358. tm.assert_sp_array_equal(sparray, expected)
  359. @pytest.mark.parametrize("cons", [list, np.array, SparseArray])
  360. def test_mismatched_length_cmp_op(cons):
  361. left = SparseArray([True, True])
  362. right = cons([True, True, True])
  363. with pytest.raises(ValueError, match="operands have mismatched length"):
  364. left & right
  365. @pytest.mark.parametrize("op", ["add", "sub", "mul", "truediv", "floordiv", "pow"])
  366. @pytest.mark.parametrize("fill_value", [np.nan, 3])
  367. def test_binary_operators(op, fill_value):
  368. op = getattr(operator, op)
  369. data1 = np.random.randn(20)
  370. data2 = np.random.randn(20)
  371. data1[::2] = fill_value
  372. data2[::3] = fill_value
  373. first = SparseArray(data1, fill_value=fill_value)
  374. second = SparseArray(data2, fill_value=fill_value)
  375. with np.errstate(all="ignore"):
  376. res = op(first, second)
  377. exp = SparseArray(
  378. op(first.to_dense(), second.to_dense()), fill_value=first.fill_value
  379. )
  380. assert isinstance(res, SparseArray)
  381. tm.assert_almost_equal(res.to_dense(), exp.to_dense())
  382. res2 = op(first, second.to_dense())
  383. assert isinstance(res2, SparseArray)
  384. tm.assert_sp_array_equal(res, res2)
  385. res3 = op(first.to_dense(), second)
  386. assert isinstance(res3, SparseArray)
  387. tm.assert_sp_array_equal(res, res3)
  388. res4 = op(first, 4)
  389. assert isinstance(res4, SparseArray)
  390. # Ignore this if the actual op raises (e.g. pow).
  391. try:
  392. exp = op(first.to_dense(), 4)
  393. exp_fv = op(first.fill_value, 4)
  394. except ValueError:
  395. pass
  396. else:
  397. tm.assert_almost_equal(res4.fill_value, exp_fv)
  398. tm.assert_almost_equal(res4.to_dense(), exp)