正确编写numpy meshgrid

时间:2017-11-08 14:00:01

标签: python numpy matplotlib

以下代码有效。但是,计算rez的行很可能不是按照numpy开发人员的预期方式编写的。如何在不转换到列表和从列表转换的情况下以高效的方式重写它?

另外,如果你碰巧知道如何在python中编写数学函数的好指南,这对数字和np.arrays同样有用,请分享:)

import numpy as np
from matplotlib import pyplot as plt

def L2normSquared(X, Y, x, y):
    return sum((X-x)**2 + (Y-y)**2)

X = np.random.normal(0, 1, 10)
Y = np.random.normal(0, 1, 10)

PX = np.arange(-1, 1, 0.1)
PY = np.arange(-1, 1, 0.1)
PXm, PYm = np.meshgrid(PX, PY)

rez = np.array([[L2normSquared(X, Y, x, y) for y in PY] for x in PX])

print(rez.shape)

plt.imshow(rez, cmap='jet', interpolation='nearest', origin='lower', extent=[-1, 1, -1, 1])
plt.show()

修改:让我解释一下我要做的事情

我在2D中生成10个随机点。然后我为2D中的任意点(x,y)定义一个损失函数。该函数的结果是所有10个固定点与该任意点的欧几里德距离之和。最后,我想用2d imshow方法绘制这个损失函数

编辑2 :根据Nils Werner的回答,可以使用3D数组和广播,生成以下代码

import numpy as np
from matplotlib import pyplot as plt

def L2normSquared(X, Y, x, y):
    return np.sum((X-x)**2 + (Y-y)**2, axis=0)

X = np.random.normal(0, 1, 10)
Y = np.random.normal(0, 1, 10)

PX = np.arange(-1, 1, 0.1)
PY = np.arange(-1, 1, 0.1)
PXm, PYm = np.meshgrid(PX, PY)


rez = L2normSquared(X[:, None, None], Y[:, None, None], PXm, PYm)

print(rez.shape)

plt.imshow(rez, cmap='jet', interpolation='nearest', origin='lower', extent=[-1, 1, -1, 1])
plt.show()

然而,这段代码实际上比列表理解慢(对于10000随机坐标,步骤0.01,它大约慢2-3倍)。对于更大的输入,存在内存崩溃,这使我相信此方法在内部导致3D阵列动态编程,这在内存分配方面不能很好地扩展。

编辑3 : 我很抱歉,但我的最小例子太少了。在我面临的原始问题中,坐标X和Y不会解耦以允许它们单独计算。其中一个原始功能是

def gaussian(X, Y, x, y):
  return sum(np.exp(-(X-x)**2 -(Y-y)**2))

3 个答案:

答案 0 :(得分:1)

meshgrid背后的想法是你得到两个或更多的数组,你可以自己简单地传递给一个操作。理想情况下,我们根本不需要for循环。

但是,因为你正在做一个"外部差异"在XPX之间,在X轴上进行求和后,您还需要使用broadcasting来首先执行外部产品,最后总结正确的轴:

import numpy as np
from matplotlib import pyplot as plt

def L2normSquared(X, Y, x, y):
    return np.sum((X-x)**2 + (Y-y)**2, axis=0)

X = np.random.normal(0, 1, 10)
Y = np.random.normal(0, 1, 10)

PX = np.arange(-1, 1, 0.1)
PY = np.arange(-1, 1, 0.1)
PXm, PYm = np.meshgrid(PX, PY)

rez = L2normSquared(X[:, None, None], Y[:, None, None], PXm, PYm)

答案 1 :(得分:1)

更系统的方法是将总和与规范区分开来,因为你的 L2normSquared函数无法查看x vs X的特定角色。

def normSquared(X, Y, x, y):
    return (X-x)**2 + (Y-y)**2

X = np.random.normal(0, 1, 10)
Y = np.random.normal(0, 1, 10)
x = y = np.arange(-1, 1, 0.1)

rez=normSquared(*np.meshgrid(X,Y,x,y)).sum(axis=(0,1))

normedSquared了解数组和数字,sum这里含糊不清。

无需重新发明meshgrid

修改

要推迟记忆问题,你可以提前求和,以及稀疏网格:

n=1000000
X = np.random.normal(0, 1, n)
Y = np.random.normal(0, 1, n)
x = np.arange(-1, 1, 0.1)
y = np.arange(-1, 1, 0.1)

Xm,Ym,xm,ym = mesh = np.meshgrid(X,Y,x,y,sparse=True)

rez=((Xm-xm)**2).sum((0,1))+((Ym-ym)**2).sum((0,1))

这可以在1秒内解决问题,因为n = 1,000,000。

答案 2 :(得分:1)

使用Images.xcassets魔法与numpy.dot同时提高内存效率和性能而不会弄乱matrix-multiplication,就像这样 -

meshgrids

<强>基准

方法和相关功能 -

a = np.exp(-(PX[:,None]-X)**2)
b = np.exp(-(PY[:,None]-Y)**2)
out = a.dot(b.T)

计时和验证 -

def gaussian(X, Y, x, y):
  return sum(np.exp(-(X-x)**2 -(Y-y)**2))

def org_method(PX, X, PY, Y):
    PXm, PYm = np.meshgrid(PX, PY)
    return np.array([[gaussian(X, Y, x, y) for y in PY] for x in PX])

def matmult_method(PX, X, PY, Y):
    a = np.exp(-(PX[:,None]-X)**2)
    b = np.exp(-(PY[:,None]-Y)**2)
    return a.dot(b.T)

因此,我们正在加速 In [256]: X = np.random.normal(0, 1, 1000) ...: Y = np.random.normal(0, 1, 1000) ...: ...: PX = np.arange(-1, 1, 0.01) ...: PY = np.arange(-1, 1, 0.01) In [257]: np.allclose(org_method(PX, X, PY, Y), matmult_method(PX, X, PY, Y)) Out[257]: True In [258]: %timeit org_method(PX, X, PY, Y) 1 loop, best of 3: 3.76 s per loop In [259]: %timeit matmult_method(PX, X, PY, Y) 100 loops, best of 3: 12.2 ms per loop