#!/usr/bin/env python # coding: utf-8 # # numpy Testing # ### Miki Tebeka .:. [353solutions](http://353solutions.com) .:. Highly effective Python, Scientific Python and Go workshops # # We'll explore certain caveats while testing [numpy](http://docs.scipy.org/doc/numpy/reference/) code. # # #### TL;DR # Use [np.allclose](http://docs.scipy.org/doc/numpy/reference/generated/numpy.allclose.html) when comparing numpy arrays. Beware of `nan`. # In[1]: import numpy as np # ## The Naive Approach # In[2]: def test_mul(): arr = np.array([0.0, 1.0, 1.1]) v, expected = 1.1, np.array([0.0, 1.1, 1.21]) assert arr * v == expected, 'bad multiplication' test_mul() # This is due to the fact that when we compare two numpy arrays with `==` we'll get an array of boolean values comparing each element. # In[3]: np.array([1,2,3]) == np.array([1, 1, 3]) # And the truch value of an array (as the error says) is ambiguous. # In[4]: bool(np.array([1, 2, 3])) # We need to use [np.all](http://docs.scipy.org/doc/numpy/reference/generated/numpy.all.html) to check that all elements are equal. # In[5]: np.all([True, True, True]) # ## Using np.all # In[6]: def test_mul(): arr = np.array([0.0, 1.0, 1.1]) v, expected = 1.1, np.array([0.0, 1.1, 1.21]) assert np.all(arr * v == expected), 'bad multiplication' test_mul() # This is due to the fact that floating points are not exact. # In[7]: 1.1 * 1.1 # This is *not* a bug in Python but how floating points are implemented. You'll get the same result in C, Java, Go ... # To overcome this we're going to use [np.allclose](http://docs.scipy.org/doc/numpy/reference/generated/numpy.allclose.html). # # BTW: If you're really intersted in floating points, read [this article](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). # ## Using np.allclose # In[8]: def test_mul(): arr = np.array([0.0, 1.0, 1.1]) v, expected = 1.1, np.array([0.0, 1.1, 1.21]) assert np.allclose(arr * v, expected), 'bad multiplication' test_mul() # ## Oh nan, Let Me Count the Ways ... # In[9]: def test_div(): arr1, arr2 = np.array([1.0, np.inf, 2.0]), np.array([2.0, np.inf, 2.0]) expected = np.array([0.5, np.nan, 1.0]) assert np.allclose(arr1 / arr2, expected), 'bad nan' test_div() # This is due to the fact the `nan` does not equal itself. # In[10]: np.nan == np.nan # To check is a number is `nan` we need to use [np.isnan](http://docs.scipy.org/doc/numpy/reference/generated/numpy.isnan.html) # In[11]: np.isnan(np.inf/np.inf) # We have two options to solve this: # # 1. Convert all `nan` to numbers # 2. Use `equal_nan` argument to `np.allclose` # ## Option 1: Convert `nan` to Numbers # In[12]: def test_div(): arr1, arr2 = np.array([1.0, np.inf, 2.0]), np.array([2.0, np.inf, 2.0]) expected = np.array([0.5, np.nan, 1.0]) result = arr1 / arr2 result[np.isnan(result)] = 0.0 expected[np.isnan(expected)] = 0.0 assert np.allclose(result, expected), 'bad nan' test_div() # ## Option 2: Use `equal_nan` in `np.allclose` # In[13]: def test_div(): arr1, arr2 = np.array([1.0, np.inf, 2.0]), np.array([2.0, np.inf, 2.0]) expected = np.array([0.5, np.nan, 1.0]) assert np.allclose(arr1 / arr2, expected, equal_nan=True), 'bad nan' test_div() # In[ ]: