numpy:在获取日志(矩阵)时有效避免0

numpy:在获取日志(矩阵)时有效避免0,numpy,Numpy,我想计算元素方面的log2(m),但只在m不是0的地方。在这些地方,我希望结果是0 我现在正在反对: from numpy import * m = array([[1,0], [2,3]]) 尝试1:使用where RuntimeWarning: divide by zero encountered in log2 这会计算出正确的结果,但我仍然会记录一个运行时警告:在log2中遇到被零除的情况。看起来(从语法上看,这是很明显的)numpy仍然在完整的矩阵上计算l

我想计算元素方面的
log2(m)
,但只在
m
不是0的地方。在这些地方,我希望结果是0

我现在正在反对:

from numpy import *

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

尝试1:使用
where

RuntimeWarning: divide by zero encountered in log2
这会计算出正确的结果,但我仍然会记录一个
运行时警告:在log2中遇到被零除的情况。看起来(从语法上看,这是很明显的)numpy仍然在完整的矩阵上计算
log2(m)
,并且只有在
之后,
才选择要保留的值

我想避免这个警告


尝试2:使用遮罩

res = where(m != 0, log2(m), 0)
当然,屏蔽掉零会阻止
log2
应用于它们,不是吗?不幸的是,我们仍然得到
运行时警告:在log2中遇到被零除的情况

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


如何有效地计算numpy数组的元素日志,而不得到零除警告?

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

有什么想法吗?

我们可以使用掩码数组来实现这一点:

from numpy import ma

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

只需禁用该计算的警告:

>>> from numpy import *
>>> m = array([[1,0], [2,3]])
>>> x = ma.log(m)
>>> print x.filled(0)
[[ 0.          0.        ]
 [ 0.69314718  1.09861229]]
然后,如果需要,可以对
-inf
进行后处理:

from numpy import errstate,isneginf,array

m = array([[1,0],[2,3]])
with errstate(divide='ignore'):
    res = log2(m)
编辑:我在这里发表了一些关于另一个选项的评论,这是在另一个答案中发布的,使用屏蔽数组。出于两个原因,您应该选择禁用错误:

1) 使用屏蔽阵列的效率远远低于暂时禁用错误的效率,您要求提高效率

2) 禁用特定的“除以零”警告不会禁用计算数字日志的另一个问题,这是一个负输入。负输入被捕获为“无效值”警告,您必须处理它


另一方面,使用掩码数组将两个错误捕获为相同的错误,并将导致您注意不到输入中的负数。换句话说,输入中的负数被视为零,结果为零。这不是你要的


3) 作为最后一点和个人观点,禁用警告是非常可读的,很明显代码在做什么,并使其更易于维护。在这方面,我发现这个解决方案比使用屏蔽阵列更干净。

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

res[isneginf(res)]=0

下面呢

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

你可以使用类似于-
m=np.剪裁(m,1e-12,无)
以避免日志(0)错误。这会将下限设置为
1e-12

另一个选项是使用:

不会引发运行时警告,并且在未计算日志的地方引入零。

问题 问题:

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

m = np.array([[1., 0], [2, 3]])
res = np.log2(m, out=np.zeros_like(m), where=(m!=0))
解决方案
建议使用
np.clip
,在我的示例中,这将创建一个水平地板。还有一些人使用
np.errstate
np.seterr
,我认为它们很笨重,不能解决问题。请注意,
np.complex
不适用于零。使用索引
p=0
q=array([0.0,1.0])
log(q.astype(float))
在我的机器上引发运行时警告。同样,在ipython中的第一次警告之后,numpy停止警告我有关按错误除法的问题。也许你被误导了?@flebool:哈哈,你是对的,我被误导了。我使用了常规Python(2.7.6),第一次尝试后它也停止了警告。哦,好吧,我很快会编辑我的答案。啊,
ma
有自己的
log2
!太好了,随你的便。但是,请注意,使用屏蔽数组的效率不如暂时禁用错误。禁用特定的“除以零”警告不会禁用计算数字日志的另一个问题,即负输入。这种情况被捕获为“无效值”警告。另一方面,使用掩码数组将两个错误捕获为相同的错误,并可能导致您注意不到输入中的错误。换句话说,输入中的负数被视为零,结果将为零。这同样有效!我更喜欢
ma
解决方案,因为它允许我编写一个无副作用的表达式,并且它与
ma
中的其他函数很好地组合。使用
和np.errstate(divide='ignore'):res=log2(m)
而不是调用
seterr
两次:您不能忘记在计算后禁用忽略,之后它也会重置为以前的设置(这是@gg349没有的):(是的,我很晚才做这个:)@azrdev,绝对!我已经实施了你上面的建议。
out=np。像(m)这样的零非常重要。当您忘记这一点时,输出可能看起来不错,但您将使用未初始化的内存。
m = np.array([[1., 0], [2, 3]])
res = np.log2(m, out=np.zeros_like(m), where=(m!=0))
y = np.log(x)
# RuntimeWarning: divide by zero encountered in log
# RuntimeWarning: invalid value encountered in log
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()
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)