使用条件和函数向量化嵌套循环 [英] Vectorizing nested loop with conditionals and functions

查看:41
本文介绍了使用条件和函数向量化嵌套循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下功能:

def F(x):    #F receives a numpy vector (x) with size (xsize*ysize)

    ff = np.zeros(xsize*ysize)

    count=0 

    for i in range(xsize):
       for j in range(ysize):

           a=function(i,j,xsize,ysize)

           if (a>xsize):
               ff[count] = x[count]*a
           else
               ff[count] = x[count]*i*j

           count = count +1

    return ff      

这里有一个细微差别,即(例如 xsize =4, ysize=3)

There is one nuance here which is the fact that (example for xsize =4, ysize=3)

c=count
x[c=0] corresponds to x00 i=0,j=0
x[c=1]   x01   i=0, j=1
x[c=2]   x02   i=0, j=2   (i=0,  j = ysize-1)
x[c=3]   x10   i=1, j=0
...   ...  ...
x[c=n]   x32    i=3 j=2  (i=xsize-1, j=ysize-1)  

我的代码很简单

ff[c] = F[x[c]*a (condition 1)
ff[c] = F[x[c]*i*j (condition 2)

我可以使用广播避免嵌套循环,如此链接中所述:

I could avoid the nested loop using broadcasting as explained in this link:

Python3:向量化嵌套循环

但在这种情况下,我必须调用函数(i,j,xsize,ysize) 然后我有条件.我真的需要知道 i 和 j 的值.

But in this case I have to call the function(i,j,xsize,ysize) and then I have conditionals. I really need to know the value of i and j.

是否可以向量化这个函数?

Is it possible to vectorize this function?

function(i,j,xsize,ysize) 将使用 sympy 执行符号计算以返回浮点数.所以 a 是一个浮点数,而不是一个符号表达式.

the function(i,j,xsize,ysize) will use sympy to perform symbolic calculations to return a float. So a is a float, not a symbolic expression.

推荐答案

首先要注意的是你的函数 F(x) 可以描述为 x(idx) * weight(idx) 对于每个索引,其中权重仅取决于 x 的维度.因此,让我们根据函数 get_weights_for_shape 构建我们的代码,这样 F 就相当简单.为了简单起见,weights 将是一个 (xsize by size) 矩阵,但我们也可以让 F 用于平面输入:

The first thing to note is that your function F(x) can be described as x(idx) * weight(idx) for each index, where weight is only dependent on the dimensions of x. So lets structure our code in terms of a function get_weights_for_shape so that F is fairly simple. For sake of simplicity weights will be a (xsize by size) matrix but we can let F work for flat inputs too:

def F(x, xsize=None, ysize=None):
    if len(x.shape) == 2:
        # based on how you have put together your question this seems like the most reasonable representation.
        weights = get_weights_for_shape(*x.shape)
        return x * weights
    elif len(x.shape) == 1 and xsize * ysize == x.shape[0]:
        # single dimensional input with explicit size, use flattened weights.
        weights = get_weights_for_shape(xsize, ysize)
        return x * weights.flatten()
    else:
        raise TypeError("must take 2D input or 1d input with valid xsize and ysize")


# note that get_one_weight=function can be replaced with your actual function.
def get_weights_for_shape(xsize, ysize, get_one_weight=function):
    """returns weights matrix for F for given input shape"""
    # will use (xsize, ysize) shape for these calculations.
    weights = np.zeros((xsize,ysize))
    #TODO: will fill in calculations here
    return weights

所以首先我们要为每个元素运行您的 function(我在参数中将其别名为 get_one_weight),您已经说过这个函数不能被向量化所以我们可以只使用列表理解.我们想要一个具有相同形状的矩阵 a (xsize,ysize) 所以对于嵌套列表的理解有点倒退:

So first we want to run your function (which I have aliased get_one_weight inside the parameters) for each element, you have said that this function can't be vectorized so we can just use list comprehension. We want a matrix a that has the same shape (xsize,ysize) so the comprehension is a bit backwards for a nested list:

# notice that the nested list makes the loops in opposite order:
# [ROW for i in Xs]
#  ROW = [f() for j in Ys]
a = np.array([[get_one_weight(i,j,xsize,ysize)
                    for j in range(ysize)
              ] for i in range(xsize)])

有了这个矩阵 a >xsize 将为条件赋值提供一个布尔数组:

With this matrix a > xsize will give a boolean array for conditional assignment:

case1 = a > xsize
weights[case1] = a[case1]

对于另一种情况,我们使用索引 ij.要矢量化二维索引,我们可以使用 np.meshgrid

For the other case, we use the index i and j. To vectorize a 2D index we can use np.meshgrid

[i,j] = np.meshgrid(range(xsize), range(ysize), indexing='ij')
case2 = ~case1 # could have other cases, in this case it's just the rest.
weights[case2] = i[case2] * j[case2]

return weights #that covers all the calculations

将它们放在一起给出了完全矢量化的函数:

Putting it all together gives this as the fully vectorized function:

# note that get_one_weight=function can be replaced with your actual function.
def get_weights_for_shape(xsize, ysize, get_one_weight=function):
    """returns weights matrix for F for given input shape"""
    # will use (xsize, ysize) shape for these calculations.
    weights = np.zeros((xsize,ysize))

    # notice that the nested list makes the loop order confusing:
    # [ROW for i in Xs]
    #  ROW = [f() for j in Ys]
    a = np.array([[get_one_weight(i,j,xsize,ysize)
                        for j in range(ysize)
                  ] for i in range(xsize)])

    case1 = (a > xsize)
    weights[case1] = a[case1]

    # meshgrid lets us use indices i and j as vectorized matrices.
    [i,j] = np.meshgrid(range(xsize), range(ysize), indexing='ij')
    case2 = ~case1
    weights[case2] = i[case2] * j[case2]
    #could have more than 2 cases if applicable.

    return weights

这涵盖了大部分内容.对于您的特定情况,因为这种繁重的计算仅依赖于输入的形状,如果您希望使用类似大小的输入重复调用此函数,则可以缓存所有先前计算的权重:

And that covers most of it. For your specific case since this heavy calculation only relies on the shape of the input, if you expect to call this function repeatedly with similarly sized input you can cache all previously calculated weights:

def get_weights_for_shape(xsize, ysize, _cached_weights={}):
    if (xsize, ysize) not in _cached_weights:
        #assume we added an underscore to real function written above
        _cached_weights[xsize,ysize] = _get_weights_for_shape(xsize, ysize)
    return _cached_weights[xsize,ysize]

据我所知,这似乎是您将要获得的最优化的.唯一的改进是矢量化 function(即使这意味着只是在多个线程中并行调用它)或者如果 .flatten() 制作一个昂贵的副本,可能改进了,但我不完全确定如何.

As far as I can tell this seems like the most optimized you are going to get. The only improvements would be to vectorize function (even if that means just calling it in several threads in parallel) or possibly if .flatten() makes an expensive copy that could be improved but I'm not totally sure how.

这篇关于使用条件和函数向量化嵌套循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆