【问题标题】:Fill Holes with Majority of Surrounding Values (Python)用大多数周围值填充孔(Python)
【发布时间】:2017-01-10 01:36:57
【问题描述】:

我使用 Python 并且有一个值为 1.0 、 2.0 、 3.0 、 4.0 、 5.0 、 6.0 和 np.nan 作为 NoData 的数组。

我想用一个值填充所有“nan”。该值应该是周围值的大部分。

例如:

1 1 1 1 1
1 n 1 2 2
1 3 3 2 1
1 3 2 3 1

“n”在本例中应表示“nan”。它的大多数邻居的值为 1。因此,“nan”将被值 1 替换。

注意,由“nan”组成的孔的大小可以是 1 到 5。例如(最大大小为 5 nan):

1 1 1 1 1
1 n n n 2
1 n n 2 1
1 3 2 3 1

这里的“nan”的洞有以下周围的值:

surrounding_values = [1,1,1,1,1,2,1,2,3,2,3,1,1,1] -> Majority = 1

我尝试了以下代码:

from sklearn.preprocessing import Imputer

array = np.array(.......)   #consisting of 1.0-6.0 & np.nan
imp = Imputer(strategy="most_frequent")
fill = imp.fit_transform(array)

这很好用。但是,它只使用一个轴(0 = 列,1 = 行)。默认值为 0(列),因此它使用同一列的大部分周围值。例如:

Array
2 1 2 1 1
2 n 2 2 2
2 1 2 2 1
1 3 2 3 1

Filled Array
2 1 2 1 1
2 1 2 2 2
2 1 2 2 1
1 3 2 3 1

所以你在这里看到,虽然大多数是 2,但周围的列值的大部分是 1,因此它变成了 1 而不是 2。

因此,我需要找到另一种使用 python 的方法。有什么建议或想法吗?


补充:

在我添加了 Martin Valgur 的非常有用的改进之后,您可以看到结果。

将“0”视为海洋(蓝色),将其他值(> 0)视为陆地(红色)。

如果有一个被陆地包围的“小”海(海的大小也可以是 1-5 像素),它会获得陆地,正如您在结果图像中成功看到的那样。如果被包围的海大于5px或在陆地之外,海不会获得陆地(这在图像中不可见,因为不是这样)。

如果有 1px 的“nan”,其中大部分是海洋而不是陆地,它仍然会变成陆地(在这个例子中它有 50/50)。

下图显示了我需要的东西。在海(value=0)和陆地(value>0)的边界处,“nan”-pixel需要获取大部分land-value的值。

这听起来很难,我希望我能生动地解释它。

【问题讨论】:

    标签: python arrays raster fill


    【解决方案1】:

    使用来自scipy.ndimagelabel()binary_dilation() 的可能解决方案:

    import numpy as np
    from scipy.ndimage import label, binary_dilation
    from collections import Counter
    
    def impute(arr):
        imputed_array = np.copy(arr)
    
        mask = np.isnan(arr)
        labels, count = label(mask)
        for idx in range(1, count + 1):
            hole = labels == idx
            surrounding_values = arr[binary_dilation(hole) & ~hole]
            most_frequent = Counter(surrounding_values).most_common(1)[0][0]
            imputed_array[hole] = most_frequent
    
        return imputed_array
    

    编辑:关于您松散相关的后续问题,您可以扩展上述代码以实现您所追求的:

    import numpy as np
    from scipy.ndimage import label, binary_dilation, binary_closing
    
    def fill_land(arr):
        output = np.copy(arr)
    
        # Fill NaN-s
        mask = np.isnan(arr)
        labels, count = label(mask)
        for idx in range(1, count + 1):
            hole = labels == idx
            surrounding_values = arr[binary_dilation(hole) & ~hole]
            output[hole] = any(surrounding_values)
    
        # Fill lakes
        land = output.astype(bool)
        lakes = binary_closing(land) & ~land
        labels, count = label(lakes)
        for idx in range(1, count + 1):
            lake = labels == idx
            output[lake] = lake.sum() < 6
    
        return output
    

    【讨论】:

    • 行:imputed_array[hole] = mode(surrounding_values).mode[0] 给出 AttributeError: 'tuple' object has no attribute 'mode'
    • 好吧,看来你可能有不同版本的 SciPy。您可以使用collections.Counter 或任何其他方法来查找最常见的值(请参阅stackoverflow.com/a/28528632/2997179)。我修改了代码以使用Counter
    • 我为您的后续问题添加了示例代码。在某些您未描述的边缘情况下,它可能不会按照您想要的方式运行,因此请根据您认为合适的方式对其进行调整。
    • 我将其添加为对我的问题的补充,并对其进行了更好的描述。我已经解决了小海的问题,因为这是之前代码中的问题。然而,剩下的问题是海与湖的边界,海无法获得像素。
    【解决方案2】:

    我没有找到任何库,所以我写了一个函数,如果数组中间的所有情况都没有,你可以使用这些

    import numpy as np
    from collections import Counter
    
    
    def getModulusSurround(data):
    
        tempdata = list(filter(lambda x: x, data))
        c = Counter(tempdata)
        if c.most_common(1)[0][0]:
            return(c.most_common(1)[0][0])
    
    
    def main():
    
        array = [[1, 2, 2, 4, 5],
                 [2, 3, 4, 5, 6],
                 [3, 4, None, 6, 7],
                 [1, 4, 2, 3, 4],
                 [4, 6, 2, 2, 4]]
    
        array = np.array(array)
    
        for i in range(5):
            for j in range(5):
                if array[i,j] == None:
    
                    temparray = array[i-1:i+2,j-1:j+2]
                    array[i,j] = getModulusSurround(temparray.flatten())
    
        print(array)
    
    main()
    

    【讨论】:

      【解决方案3】:

      在 Martin Valgur 难以置信的帮助下,我得到了我需要的结果。

      因此,我在 Martins 代码中添加了以下几行:

      from scipy.ndimage import label, binary_dilation
      from scipy.stats import mode
      
      def impute(arr):
          imputed_array = np.copy(arr)
      
          mask = np.isnan(arr)
          labels, count = label(mask)
          for idx in range(1, count + 1):
              hole = labels == idx
              surrounding_values = arr[binary_dilation(hole) & ~hole]
      
              sv_list = np.ndarray.tolist(surrounding_values) #!
              for sv in sv_list:  #!
                  if sv == 0:
                      sv_list.remove(sv)
              surrounding_values = np.array(sv_list)
      
              imputed_array[hole] = mode(surrounding_values).mode[0]
      
          return imputed_array
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-29
        • 2012-06-14
        • 1970-01-01
        • 2022-01-23
        相关资源
        最近更新 更多