如何在Python中计算2D数组中的相邻单元

时间:2018-07-13 17:53:00

标签: python multidimensional-array

提示: 给定一个表示图像灰度的2D整数矩阵M,您需要设计一个更平滑的图像,以使每个单元的灰度变为周围所有8个单元及其自身的平均灰度(向下舍入)。如果一个单元格周围的单元格少于8个,则请尽量使用。

示例:

输入:

[[1,1,1]

[1,0,1]

[1,1,1]]

输出:

[[0,0,0],

[0,0,0],

[0,0,0]]

说明:

For the point (0,0), (0,2), (2,0), (2,2) -> floor(3/4) = floor(0.75) = 0
For the point (0,1), (1,0), (1,2), (2,1) ->  floor(5/6) = floor(0.83333333) = 0
For the point (1,1): floor(8/9) = floor(0.88888889) = 0

解决方案:

class Solution:
    def imageSmoother(self, grid):
        """
        :type M: List[List[int]]
        :rtype: List[List[int]]
        """
        rows, cols = len(grid), len(grid[0])

        #Go through each cell 
        for r in range(rows):
            for c in range(cols):

            #Metrics for calculating average, starting inputs are zero since the loop includes the current cell, grid[r][c]

            total = 0
            n = 0

            #Checking the neighbors
            for ri in [-1,0,1]:
                for ci in [-1,0,1]:
                    if (r + ri >= 0 and r + ri <= rows-1 and c + ci >=0 and c + ci <= cols-1):
                        total += grid[r+ri][c+ci]
                        n += 1 

            #Now we convert the cell value to the average 
            grid[r][c] = int(total/n)

        return grid                             

我的解决方案不正确。它通过了一些测试用例,但为此我失败了。

输入: [[2,3,4],[5,6,7],[8,9,10],[11,12,13],[14 ,15,16]]

输出: [[4,4,5],[6,6,6],[8,9,9],[11,11,12],[12 ,12,12]]

预期: [[4,4,5],[5,6,6],[8,9,9],[11,12,12],[13 ,13,14]]

如您所见,我的解决方案非常接近。我不确定自己在哪里搞砸,因为当我更改参数时,我开始在其他基本测试用例中失败。我在网上看到的解决方案使用了其他一些我不想使用的软件包,因为我想更直观地解决这个问题。

您如何检查2D阵列问题出了哪些问题?谢谢!

密码解决方案:

def imageSmoother(self, M):
        R,C=len(M),len(M[0])
        M2=[[0]*C for i in range(R)]
        for i in range(R):
            for j in range(C):
                temp=[M[i+x][j+y] for x,y in list(itertools.product([-1,0,1],[-1,0,1])) if 0<=i+x<R and 0<=j+y<C ]
                M2[i][j]=(sum(temp)//len(temp))
        return M2

1 个答案:

答案 0 :(得分:2)

您的代码存在问题,因为您正在修改grid。因此,对于每个单元格,您将使用下/右邻居的输入值,但使用上/左邻居的输出值。

因此,在给定的示例中,当您计算grid[1][0]的邻居时,您已经替换了grid[0][0]grid[0][1]这两个邻居,因此它们是现在4, 4,而不是2, 3。这意味着您正在平均4, 4, 5, 6, 8, 9而不是2, 3, 5, 6, 8, 9。因此,您得到的不是6.0而是四舍五入为5。


最简单的解决方法是在进行过程中建立一个新的输出网格,然后将其返回:

    rows, cols = len(grid), len(grid[0])
    outgrid = []
    #Go through each cell 
    for r in range(rows):
        outrow = []
        for c in range(cols):
            # … same code as before, but instead of the grid[r][c] =
            outrow.append(int(total/n))
        outgrid.append(outrow)
    return outgrid

如果需要在适当的位置修改网格,则可以复制原始网格,然后遍历该副本:

    rows, cols = len(grid), len(grid[0])
    ingrid = [list(row) for row in grid]
    #Go through each cell 
    for r in range(rows):
        for c in range(cols):
            # … same code as before, but instead of total += grid[r+ri][c+ci]
            total += ingrid[r+ri][c+ci]

如果使用2D NumPy数组而不是列表列表,则可以在更高层次上解决此问题。

NumPy可让您一次添加整个数组,将它们按标量除以此类推,这样您就可以摆脱rc上的那些循环,而只需在整个数组范围内工作即可。但是您仍然必须考虑自己的界限。您不能只添加arrarr[:-1]arr[1:],依此类推,您需要将它们填充到相同的大小。而且,如果只填充0,则最终将平均0, 4, 4, 0, 5, 6, 0, 8, 9,这是不好的。但是,如果您用NaN值填充它们,因此要对NaN, 4, 4, NaN, 5, 6, NaN, 8, 9求平均值,则可以使用nanmean函数,该函数将忽略那些NaN值并平均6个实数值。

因此,这仍然是几行代码,可以在9个方向上进行迭代,填充9个数组并nanmean结果。 (或者您也可以使用product将其塞入一个巨大的表达式中,例如leetcode答案,但这并没有更容易理解或理解。)

但是,如果您可以拖曳SciPy(几乎所有您想在NumPy之上构建的所有算法的集合),它的ndimage库中就有一个名为{{3 }}可以做所有可能的变体,例如“聚集N个邻居,像X那样填充,并在结果数组上运行函数Y”。

在我们的例子中,我们想要收集每轴3个邻居,使用常数NaN填充,并运行nanmean函数,所以generic_filter

scipy.ndimage.generic_filter(grid, function=np.nanmean, size=3, mode='constant', cval=np.NaN)