有效获取numpy平均面积的方法

时间:2019-01-10 05:27:03

标签: python numpy vectorization

在确定给定的numpy数组中某个区域的平均值时,是否存在更有效的方法?为了简单起见,假设我有一个5x5数组:

values = np.array([[0, 1, 2, 3, 4],
                   [1, 2, 3, 4, 5],
                   [2, 3, 4, 5, 6],
                   [3, 4, 5, 6, 7],
                   [4, 5, 6, 7, 8]])

我想获得指定坐标区域大小的每个坐标的平均值,假设数组环绕。假设某个区域的大小为2,因此将考虑距离2内某个点周围的任何东西。例如,要从坐标(2,2)获得面积的平均值,我们需要考虑

      2,
   2, 3, 4,
2, 3, 4, 5, 6
   4, 5, 6,
      6,

因此,平均值将为4.

对于坐标(4,4),我们需要考虑:

      6,
   6, 7, 3,
6, 7, 8, 4, 5
   3, 4, 0,
      5,

因此平均值将为4.92.

当前,我在下面有以下代码。但是由于我有一个for循环,所以我觉得它可以改进。有没有办法只使用numpy内置函数?

有没有一种方法可以使用np.vectorize收集子数组(区域),将它们全部放置在数组中,然后使用np.einsum或其他东西。

def get_average(matrix, loc, dist):
    sum = 0
    num = 0
    size, size = matrix.shape
    for y in range(-dist, dist + 1):
        for x in range(-dist + abs(y), dist - abs(y) + 1):
            y_ = (y + loc.y) % size
            x_ = (x + loc.x) % size

            sum += matrix[y_, x_]
            num += 1

    return sum/num

class Coord():
    def __init__(self, x, y):
        self.x = x
        self.y = y

values = np.array([[0, 1, 2, 3, 4],
                     [1, 2, 3, 4, 5],
                     [2, 3, 4, 5, 6],
                     [3, 4, 5, 6, 7],
                     [4, 5, 6, 7, 8]])

height, width = values.shape

averages = np.zeros((height, width), dtype=np.float16)

for r in range(height):
    for c in range(width):
        loc = Coord(c, r)
        averages[r][c] = get_average(values, loc, 2)

print(averages)

输出:

[[ 3.07617188  2.92382812  3.5390625   4.15234375  4.        ]
 [ 2.92382812  2.76953125  3.38476562  4.          3.84570312]
 [ 3.5390625   3.38476562  4.          4.6171875   4.4609375 ]
 [ 4.15234375  4.          4.6171875   5.23046875  5.078125  ]
 [ 4.          3.84570312  4.4609375   5.078125    4.921875  ]]

1 个答案:

答案 0 :(得分:0)

此解决方案的效率(慢)不如您,但仅是使用docker exec模块的示例。

所需的库:

import numpy as np
import numpy.ma as ma

定义完成这项工作的方法:

# build the shape of the area as a rhomboid
def rhomboid2(dim):
    size = 2*dim + 1
    matrix = np.ones((size,size))
    for y in range(-dim, dim + 1):
      for x in range(-dim + abs(y), dim - abs(y) + 1):
        matrix[(y + dim) % size, (x + dim) % size] = 0
    return matrix

# build a mask using the area shaped
def mask(matrix_shape, rhom_dim):
  mask = np.zeros(matrix_shape)
  bound = 2*rhom_dim+1
  rhom = rhomboid2(rhom_dim)
  mask[0:bound, 0:bound] = rhom
  # roll to set the position of the rhomboid to 0,0
  mask = np.roll(mask,-rhom_dim, axis = 0)
  mask = np.roll(mask,-rhom_dim, axis = 1)
  return mask

然后,迭代生成结果:

mask_ = mask((5,5), 2) # call the mask sized as values array with a rhomboid area of size 2
averages = np.zeros_like(values, dtype=np.float16) # initialize the recipient
# iterate over the mask to calculate the average
for y in range(len(mask_)):
  for x in range(len(mask_)):
    masked = ma.array(values, mask = mask_)
    averages[y,x] = np.mean(masked)
    mask_ = np.roll(mask_, 1, axis = 1)
  mask_ = np.roll(mask_, 1, axis = 0)

返回哪个

# [[3.076 2.924 3.54  4.152 4.   ]
#  [2.924 2.77  3.385 4.    3.846]
#  [3.54  3.385 4.    4.617 4.46 ]
#  [4.152 4.    4.617 5.23  5.08 ]
#  [4.    3.846 4.46  5.08  4.92 ]]