numpy: Efficiently avoid 0s when taking log(matrix)

Solution 1:

We can use masked arrays for this:

>>> from numpy import *
>>> m = array([[1,0], [2,3]])
>>> x = ma.log(m)
>>> print x.filled(0)
[[ 0.          0.        ]
 [ 0.69314718  1.09861229]]

Solution 2:

Another option is to use the where parameter of numpy's ufuncs:

m = np.array([[1., 0], [2, 3]])
res = np.log2(m, out=np.zeros_like(m), where=(m!=0))

No RuntimeWarning is raised, and zeros are introduced where the log is not computed.

Solution 3:

Simply disable the warning for that computation:

from numpy import errstate,isneginf,array

m = array([[1,0],[2,3]])
with errstate(divide='ignore'):
    res = log2(m)

And then you can postprocess the -inf if you want:

res[isneginf(res)]=0

EDIT: I put here some comments about the other option, which is using masked arrays, posted in the other answer. You should opt for disabling the error for two reasons:

1) Using masked arrays is by far less efficient then disabling momentarily the error, and you asked for efficiency.

2) Disabling the specific 'divide by zero' warning does NOT disable the other problem with calculating the log of a number, which is negative input. Negative input is captured as an 'invalid value' warning, and you will have to deal with it.

On the other hand, using masked arrays captures the two errors as the same, and will lead you to not notice a negative number in the input. In other words, a negative number in the input is treated like a zero, and will give zero as a result. This is not what you asked.

3) As a last point and as a personal opinion, disabling the warning is very readable, it is obvious what the code is doing and makes it more mantainable. In that respect, I find this solution cleaner then using masked arrays.

Solution 4:

The masked array solution and the solution that disables the warning are both fine. For variety, here's another that uses scipy.special.xlogy. np.sign(m) is given as the x argument, so xlogy returns 0 wherever np.sign(m) is 0. The result is divided by np.log(2) to give the base-2 logarithm.

In [4]: from scipy.special import xlogy

In [5]: m = np.array([[1, 0], [2, 3]])

In [6]: xlogy(np.sign(m), m) / np.log(2)
Out[6]: 
array([[ 0.       ,  0.       ],
       [ 1.       ,  1.5849625]])

Solution 5:

What about the following

from numpy import *
m=array((-1.0,0.0,2.0))
p=m > 0.0
print 'positive=',p
print m[p]
res=zeros_like(m)
res[p]=log(m[p])
print res