使用fromfunction和数组填充numpy矩阵

4 投票
2 回答
2015 浏览
提问于 2025-04-18 13:16

我有一个叫做 phases 的数组,假设它看起来是这样的:

phases = numpy.random.uniform(0,1,10)

现在我想创建一个矩阵,这个矩阵的每一行都是某个函数 f 应用到 phases 中的每个元素上,而每一列则是这个元素的倍数,最终的样子大概是这样的:

[[ f(phases[0]) f(2*phases[0]) f(3*phases[0]) ]
 [ f(phases[1]) f(2*phases[1]) f(3*phases[1]) ]
       ...            ...           ...      
 [ f(phases[9]) f(2*phases[9]) f(3*phases[9]) ]]

为了简单起见,我们可以把 f 定义为一个简单的函数,比如 f(x) = x+1

所以我想我可以用 numpy.fromfunction 来实现,代码如下:

numpy.fromfunction(lambda i,j: (j+1)*phases[i]+1,
                   (phases.size, 3), dtype=float)

但是这样做却出现了错误:

IndexError: arrays used as indices must be of integer (or boolean) type

我该如何在 fromfunction 中访问 phases 的第 i 个元素呢?

或者说,这样做是不是不对的方式呢?

2 个回答

1

我觉得最简单的方法是先创建你想要的矩阵,然后再把你的函数 f 应用到这个矩阵上,这样做符合 NumPy 的使用习惯,也能很好地进行向量化处理。

>>> phases = numpy.random.uniform(0,1,10)
>>> phases = phases.reshape((10, 1))
>>> phases = np.tile(phases, (1, 3))

这样你就得到了一个矩阵(实际上是一个 ndarray),它的形式是

[[ phases[0] 2*phases[0] 3*phases[0] ]
 [ phases[1] 2*phases[1] 3*phases[1] ]
    ...        ...        ...      
 [ phases[9] 2*phases[9] 3*phases[9] ]]

接下来你可以把你的函数应用到这个矩阵上。

>>> def f(x):
...     return numpy.sin(x)
>>> f(phases)
array([[ 0.56551297,  0.93280166,  0.97312359],
       [ 0.38704365,  0.71375602,  0.92921009],
       [ 0.62778184,  0.97731738,  0.89368501],
       [ 0.0806512 ,  0.16077695,  0.23985519],
       [ 0.4140241 ,  0.75374405,  0.95819095],
       [ 0.25929821,  0.50085902,  0.70815838],
       [ 0.25399811,  0.49133634,  0.69644753],
       [ 0.7754078 ,  0.97927926,  0.46134512],
       [ 0.53301912,  0.90197836,  0.99331443],
       [ 0.44019133,  0.79049912,  0.9793933 ]])

不过,这样做的前提是你的函数 f 必须是“向量化”的,也就是说,它需要接受一个 ndarray 并对这个数组中的每个元素进行操作。如果你的函数不是这样的话,你可以使用 numpy.vectorize 来得到一个可以这样工作的函数版本。

>>> import math
>>> def f(x):
...     return math.sin(x)
>>> f(phases)
TypeError: only length-1 arrays can be converted to Python scalars
>>> f = numpy.vectorize(f)
>>> f(phases)
array([[ 0.56551297,  0.93280166,  0.97312359],
       [ 0.38704365,  0.71375602,  0.92921009],
       [ 0.62778184,  0.97731738,  0.89368501],
       [ 0.0806512 ,  0.16077695,  0.23985519],
       [ 0.4140241 ,  0.75374405,  0.95819095],
       [ 0.25929821,  0.50085902,  0.70815838],
       [ 0.25399811,  0.49133634,  0.69644753],
       [ 0.7754078 ,  0.97927926,  0.46134512],
       [ 0.53301912,  0.90197836,  0.99331443],
       [ 0.44019133,  0.79049912,  0.9793933 ]])
4

numpy.fromfunction 这个函数的表现和预期的不太一样,它的说明文档也有点误导。这个函数并不是对每一个单元格都调用一次,而是一次性用所有的索引调用。

def fromfunction(function, shape, **kwargs):
    dtype = kwargs.pop('dtype', float)
    args = indices(shape, dtype=dtype)
    return function(*args,**kwargs)

所以,现在要得到你想要的结果,你可以这样做:

In [57]: vf = numpy.vectorize(f)

In [58]: vf(numpy.outer(phases, numpy.arange(1,4)))
Out[58]:
array([[ 1.87176928,  2.74353857,  3.61530785],
       [ 1.23090955,  1.4618191 ,  1.69272866],
       [ 1.29294723,  1.58589445,  1.87884168],
       [ 1.05863891,  1.11727783,  1.17591674],
       [ 1.28370397,  1.56740794,  1.85111191],
       [ 1.87210286,  2.74420573,  3.61630859],
       [ 1.08652975,  1.1730595 ,  1.25958925],
       [ 1.33835545,  1.6767109 ,  2.01506634],
       [ 1.74479635,  2.48959269,  3.23438904],
       [ 1.76381301,  2.52762602,  3.29143903]])

outer 函数会计算两个向量的外积,正好是你想要的效果,只不过是用不同的方式。

你的函数必须能够处理数组。对于比较复杂的操作,你需要将函数“向量化”,这样它才能逐个单元格地应用。在你的例子中,你不需要太担心这个问题。

撰写回答