如何优化多维数组中的循环?

时间:2019-04-18 02:25:42

标签: python

在Python 3上收集了一些3D netCDF数据后,我正在循环遍历每个x,y数据点以计算另一个变量。对于给定的x,y点,此变量的计算取决于z。该代码似乎可以正常运行,但是速度很慢。我想知道是否有人对如何优化代码以使其运行更快提出建议。

我已经从一个较长的代码定义了许多中间变量,变成了一个比较裸露的骨骼,如下所示。即使在修剪代码后,它仍然运行缓慢(即,外部for循环中的每个i需要几分钟的时间)。

for i in range(0,217):
    print(i)
    for j in range(0,301):
        for k in range(10,30):
            if (data.variables[longvars[v][2]][0][k][i][j]-data.variables[longvars[v][3]][0][i][j]) <= 3000.0:
                break

        if (abs(data.variables[longvars[v][2]][0][k][i][j]-data.variables[longvars[v][3]][0][i][j])-3000.) \
         < (abs(data.variables[longvars[v][2]][0][k-1][i][j]-data.variables[longvars[v][3]][0][i][j])-3000.):
            lev = k
        else:
            lev = k-1

        newd[i][j] = np.sqrt(((data.variables[longvars[v][0]][0][lev][i][j]-data.variables[longvars[v][4]][0][0][i][j])**2)+((data.variables[longvars[v][1]][0][lev][i][j]-data.variables[longvars[v][5]][0][0][i][j])**2))

我想象有一种方法可以对另一个存储每个x,y(i,j)点正确的z(k)级别的数组执行此操作,然后对整个数据数组运行计算。但是,我不知道它会更快。我感谢大家能提供的任何帮助!

1 个答案:

答案 0 :(得分:0)

逻辑看起来不错,但是我们可以使用生成器和理解功能对其进行进一步优化。

让我们将内部逻辑隔离到一个名为findZValue的函数中。

def findZValue(v, i, j, variables, longvars, np):

如果我读错了,请原谅我,但是您似乎正在寻找最接近3000的值的索引?如果是这样,首先我们将使生成器返回一个包含索引和“ variable-variable-3000”的绝对值的元组:

def findZValue(v, i, j, variables, longvars, np):
    lev = ((k, abs(variables[longvars[v][2]][0][k][i][j] - variables[longvars[v][3]][0][i][j] - 3000)) for k in range(10, 30))

为了获得我们想要的值,我们将整个内容包装在min函数中(键说我们希望它按第二个值排序),并指定我们要获取索引(即min返回的元组中的第一个值:

def findZValue(v, i, j, variables, longvars, np):
    lev = min(((k, abs(variables[longvars[v][2]][0][k][i][j] - variables[longvars[v][3]][0][i][j] - 3000)) for k in range(10, 30)), key = lambda t: t[1])[0]

对于“ newd”中输入的值,您似乎是在求平方和(即其归一化或幅值)的平方根。幸运的是,numpy(我假设“ np”就是)具有用于查找数组的大小/规格化的内置方法:np.linalg.norm。我们要做的就是将其他值放入np.array中,然后对其进行调用:

def findZValue(v, i, j, variables, longvars, np):
    lev = min(((k, abs(variables[longvars[v][2]][0][k][i][j] - variables[longvars[v][3]][0][i][j] - 3000)) for k in range(10, 30)), key = lambda t: t[1])[0]
    return np.linalg.norm(np.array(variables[longvars[v][0]][0][lev][i][j]-variables[longvars[v][4]][0][0][i][j], variables[longvars[v][1]][0][lev][i][j]-variables[longvars[v][5]][0][0][i][j]))

现在,我们可以将整个循环放入嵌套的理解中:

newd = [[findZValue(v, i, j, data.variables, longvars, np) for j in range(301)] for i in range(217)]

def findZValue(v, i, j, variables, longvars, np):
    lev = min(((k, abs(variables[longvars[v][2]][0][k][i][j] - variables[longvars[v][3]][0][i][j] - 3000)) for k in range(10, 30)), key = lambda t: t[1])[0]

    return np.linalg.norm(np.array(variables[longvars[v][0]][0][lev][i][j]-variables[longvars[v][4]][0][0][i][j], variables[longvars[v][1]][0][lev][i][j]-variables[longvars[v][5]][0][0][i][j]))

使用生成器和理解应该可以加快使用for循环的速度。但是,如果您真的想加快处理速度,我们可以使用“多处理”。具体来说,是一个多处理池。为此,我们将需要创建另一个函数来处理每个向量(这是由于多处理池的工作方式受到限制):

from multiprocessing import Pool

def findZValue(v, i, j, variables, longvars, np):
    lev = min(((k, abs(variables[longvars[v][2]][0][k][i][j] - variables[longvars[v][3]][0][i][j] - 3000)) for k in range(10, 30)), key = lambda t: t[1])[0]

    return np.linalg.norm(np.array(variables[longvars[v][0]][0][lev][i][j]-variables[longvars[v][4]][0][0][i][j], variables[longvars[v][1]][0][lev][i][j]-variables[longvars[v][5]][0][0][i][j]))

def findZValuesForVector(vector):
    return [findZValue(*values) for values in vector]

with Pool(processes=4) as pool:
    newd = pool.map(findZValuesForVector, [[[v, i, j, data.variables, longvars, np] for j in range(301)] for i in range(217)])

您可以更改为池创建的“进程”数,以查看获得最佳效果的结果。