【问题标题】:numpy.fromfunction with specified non-arange-like arraynumpy.fromfunction 具有指定的非范围类数组
【发布时间】:2020-07-15 21:52:25
【问题描述】:

我已经定义了一个函数;

def f(x,y):
    return (1+x)**3.2 * np.cos(y**2.31+x)

并有以下两个数组:

x = np.linspace(10,20,10)
y = np.linspace(5,8,5)

现在我希望创建一个 10 x 5 矩阵,其中每个 (i,j) 元素由 f(x[i],x[j]) 给出。 这可以通过 for 循环轻松实现:

result = np.zeros(len(x)*len(y)).reshape((len(x),len(y)))
for i in range(len(x)):
    for j in range(len(y)):
        result[i][j] = f(x[i],y[j])

但是,我想显着增加计算时间,所以我正在寻找一种非 for 循环方法。 似乎np.fromfunction 部分地把我带到了那里:

result = np.fromfunction(lambda x, y: f(x,y), (10, 5), dtype=int)

但是,这需要 x 和 y 类似于范围,即这需要元素 x = 0,1,...,9 和 y = 0,1,...,4。 有没有办法告诉这个函数我希望它采用我之前定义的 x 和 y 数组。 如果 np.fromfunction 无法做到这一点,是否有另一种方法可以实现与我的 for 循环相同的结果,但计算时间更少?

【问题讨论】:

  • 你能定义你的函数f 吗?
  • 当然可以。请参阅已编辑的问题。这是一个具有余弦和幂的复杂函数。这只是一个示例,因为我使用的实际功能更加复杂。
  • fromfunction 不能替代循环!它仅在您的函数已经适用于整个数组时才有效。我知道文档可能会令人困惑,尤其是如果您有先入为主的概念,但如果有疑问,也请查看代码。

标签: python numpy for-loop optimization numpy-ndarray


【解决方案1】:

如果您的函数被编写为接受整个数组,您可以只传递xy,以便将它们broadcast 一起传递。

In [472]: x = np.linspace(10,20,10) 
     ...: y = np.linspace(5,8,5)                                                               
In [473]: def f(x,y): 
     ...:     return (1-x)**3.2 * np.cos(y**2.31+x) 
     ...:                                                                                      

制作x (10,1) 数组,然后与 (5,) y 一起广播以给出 (10,5) 结果:

In [474]: f(x[:,None], y)                                                                      
/usr/local/bin/ipython3:2: RuntimeWarning: invalid value encountered in power

Out[474]: 
array([[nan, nan, nan, nan, nan],
       [nan, nan, nan, nan, nan],
       [nan, nan, nan, nan, nan],
       [nan, nan, nan, nan, nan],
       [nan, nan, nan, nan, nan],
       [nan, nan, nan, nan, nan],
       [nan, nan, nan, nan, nan],
       [nan, nan, nan, nan, nan],
       [nan, nan, nan, nan, nan],
       [nan, nan, nan, nan, nan]])

所有fromfunction 所做的只是生成indices 并将它们传递给您的函数:

In [478]: np.indices((3,4))                                                                    
Out[478]: 
array([[[0, 0, 0, 0],
        [1, 1, 1, 1],
        [2, 2, 2, 2]],

       [[0, 1, 2, 3],
        [0, 1, 2, 3],
        [0, 1, 2, 3]]])
#function(*indices...)

如果您的函数需要值,则不是i,j 索引fromfunction 对您没有任何帮助!它只是迫使您使用额外的索引层 f(x[i],y[j])。如果您的函数处理数组,fromfunction 不会加快计算速度。如果你的函数只能接受标量,fromfunction 根本不起作用。

为什么是nan

In [489]: f(x[0],y[0])                                                                         
/usr/local/bin/ipython3:2: RuntimeWarning: invalid value encountered in double_scalars

Out[489]: nan

===

使用更正后的f

In [494]: def f(x,y): 
     ...:     return (1+x)**3.2 * np.cos(y**2.31+x) 
     ...:                                                                                      
In [495]: x,y                                                                                  
Out[495]: 
(array([10.        , 11.11111111, 12.22222222, 13.33333333, 14.44444444,
        15.55555556, 16.66666667, 17.77777778, 18.88888889, 20.        ]),
 array([5.  , 5.75, 6.5 , 7.25, 8.  ]))

ix_可以用来给x添加广播维度:

In [496]: np.ix_(x,y)                                                                          
Out[496]: 
(array([[10.        ],
        [11.11111111],
        [12.22222222],
        [13.33333333],
        [14.44444444],
        [15.55555556],
        [16.66666667],
        [17.77777778],
        [18.88888889],
        [20.        ]]), 
 array([[5.  , 5.75, 6.5 , 7.25, 8.  ]]))

然后可以将这些数组传递给f(就像我在 [474] 中所做的那样):

In [497]: f(*np.ix_(x,y))                                                                      
Out[497]: 
array([[  1322.50123614,  -1353.39115671,  -1702.96842965,
          2039.59858593,   2149.99822839],
       [ -1268.79441897,   1220.20345147,    572.47038806,
           401.57578676,   1322.04810644],
       [ -3873.89288421,   3872.45293387,   3741.19176848,
         -3203.15416244,  -2320.43820171],
       [ -2274.83634272,   2356.4885057 ,   3316.20257628,
         -4368.07920439,  -4932.15975126],
       [  3805.32723978,  -3710.95441159,  -2413.75855025,
           343.98082438,  -1742.79381001],
       [  7825.16462628,  -7850.07869311,  -7934.59775682,
          7309.08041012,   5891.07144208],
       [  2696.9980712 ,  -2869.31389573,  -4956.11491324,
          7455.17663401,   9114.69519122],
       [ -8800.47513177,   8651.89940176,   6527.58767232,
         -2896.13626079,   1015.66697767],
       [-13326.47899275,  13419.77502789,  14202.98227539,
        -13981.08503081, -12233.58338808],
       [ -1484.09855587,   1795.12675239,   5660.63236478,
        -10620.54747276, -14370.53381538]])

应用于3个数组:

In [503]: np.ix_([100,200],[10,20,30],[1,2,3,4])                                               
Out[503]: 
(array([[[100]],

        [[200]]]), array([[[10],
         [20],
         [30]]]), array([[[1, 2, 3, 4]]]))

产生一个 (2,1,1)、(1,3,1) 和 (1,1,4) 数组,它们将一起广播以产生 (2,3,4) 结果。

def ff(x,y,z): return x+y+z  
In [504]: ff(*_)                                                                               
Out[504]: 
array([[[111, 112, 113, 114],
        [121, 122, 123, 124],
        [131, 132, 133, 134]],

       [[211, 212, 213, 214],
        [221, 222, 223, 224],
        [231, 232, 233, 234]]])

【讨论】:

  • nan 在我的示例中是由于 1-x 为负数,使得 (1-x)**3.2 复杂。将此更改为 (1+x)**3.2 可以解决此问题。我将为此更新我的帖子帐户。
  • 另外,这是我的问题的简化版本。事实上,我的函数看起来比这更复杂(例如,它实际上需要 4 个输入值而不是 2 个),从而导致更多的复杂性,使 np.fromfunction 可能不那么吸引人。而且我如何将您描述的广播用于两个以上的输入值并不明显。我想我最好在新帖子中解释这些复杂情况。
  • @TheOne。您仍然应该弄清楚如何正确矢量化您的代码。如果需要,请发布实际功能。调用 fromfunction 将在后台为您运行一个美化的 for 循环。
  • 我添加了一个np.ix_ 示例。
【解决方案2】:

你可以使用:

np.fromfunction(lambda i, j:  f(x[i],y[j]), (len(x),len(y)), dtype=int)

您必须指定dtype=int,否则坐标的数据类型将是float,它不能用作f(x[i],y[j])中的索引

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-13
    • 2018-09-24
    • 1970-01-01
    • 2012-01-13
    • 2016-06-23
    • 1970-01-01
    • 2018-08-25
    相关资源
    最近更新 更多