numpy:采取log(矩阵)时有效避免0

时间:2014-02-13 11:31:43

标签: numpy

from numpy import *

m = array([[1,0],
           [2,3]])

我想计算元素log2(m),但仅限于m不为0的地方。在这些地方,我希望得到0。

我现在正在反对:

RuntimeWarning: divide by zero encountered in log2

尝试1:使用where

res = where(m != 0, log2(m), 0)

计算出正确的结果,但我仍然记录了RuntimeWarning: divide by zero encountered in log2。看起来(在语法上它很明显)numpy仍然在完整矩阵上计算log2(m),然后where选择要保留的值。

我想避免这种警告。


尝试2:使用面具

from numpy import ma

res = ma.filled(log2(ma.masked_equal(m, 0)), 0)

确保屏蔽零会阻止log2应用于它们,不是吗?不幸的是:我们仍然得到RuntimeWarning: divide by zero encountered in log2

即使矩阵被屏蔽,log2似乎仍然适用于每个元素。


如何在不获得被零除警告的情况下有效地计算numpy数组的逐元素日志?

  • 当然,我可以使用seterr暂时禁用这些警告的记录,但这看起来不是一个干净的解决方案。
  • 确定一个双 for 循环可以帮助特别处理0,但是会破坏numpy的效率。

有什么想法吗?

7 个答案:

答案 0 :(得分:22)

我们可以使用蒙面数组:

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

答案 1 :(得分:15)

只需禁用该计算的警告:

from numpy import errstate,isneginf,array

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

然后,如果需要,您可以对-inf进行后处理:

res[isneginf(res)]=0
编辑:我在这里放了一些关于另一个选项的评论,其中使用了掩码数组,在另一个答案中发布了。您应该选择禁用该错误有两个原因:

1)使用屏蔽数组的效率远远低于错误的暂时禁用,并且您要求效率。

2)禁用特定的“除以零”警告不会禁用计算数字日志的其他问题,即负输入。负输入被捕获为“无效值”警告,您将不得不处理它。

另一方面,使用蒙板阵列会将两个错误捕获为相同,并且会导致您不会注意到输入中的负数。换句话说,输入中的负数被视为零,并且结果将给出零。这不是你问的。

3)作为最后一点和个人观点,禁用警告是非常易读的,很明显代码正在做什么并使其更具可持续性。在这方面,我发现这个解决方案更清洁,然后使用蒙面数组。

答案 2 :(得分:5)

屏蔽数组解决方案和禁用警告的解决方案都很好。对于多样性,这是使用scipy.special.xlogy的另一个。 np.sign(m)作为x参数给出,因此xlogynp.sign(m)为0时返回0。 结果除以np.log(2),得到基数为2的对数。

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]])

答案 3 :(得分:4)

另一种选择是使用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))

不引发RuntimeWarning,在不计算日志的地方引入零。

答案 4 :(得分:2)

以下

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

答案 5 :(得分:1)

您可以使用类似- m = np.clip(m, 1e-12, None)以避免log(0)错误。这会将下界设置为1e-12

答案 6 :(得分:1)

问题

问题:Feb 2014May 2012

对于包含zerosnegatives的数组,我们会得到相应的错误。

y = np.log(x)
# RuntimeWarning: divide by zero encountered in log
# RuntimeWarning: invalid value encountered in log

解决方案

markroxor建议np.clip,在我的示例中,这将创建一个水平地板。 gg349和其他人使用np.errstatenp.seterr,我认为它们比较笨拙,不能解决问题。注意,np.complex不适用于零。 user3315095使用索引p=0<xNumPy.log内置了where / out此功能。 mdeff证明了这一点,但是用-inf代替了0,这对我来说是不够的,并且不能解决负面问题。

我建议使用0<xnp.nan(或在需要时使用np.NINF / -np.inf)。

y = np.log(x, where=0<x, out=np.nan*x)

John Zwinck使用掩码矩阵np.ma.log可以工作,但计算速度较慢,请尝试App:timeit。

示例

import numpy as np
x = np.linspace(-10, 10, 300)

# y = np.log(x)                         # Old
y = np.log(x, where=0<x, out=np.nan*x)  # New

import matplotlib.pyplot as plt
plt.plot(x, y)
plt.show()

App:timeit

maskwhere的时间比较

import numpy as np
import time
def timeit(fun, xs):
    t = time.time()
    for i in range(len(xs)):
        fun(xs[i])
    print(time.time() - t)

xs = np.random.randint(-10,+10, (1000,10000))
timeit(lambda x: np.ma.log(x).filled(np.nan), xs)
timeit(lambda x: np.log(x, where=0<x, out=np.nan*x), xs)