用大多数周围值填充孔 (Python)
Fill Holes with Majority of Surrounding Values (Python)
我使用 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) 视为陆地(红色)。
如果有一个被陆地包围的 "little" 海洋(海洋的大小可以再次为 1-5 像素),它将得到陆地,正如您在结果图像中成功看到的那样。如果被包围的海域大于5px或者在陆地之外,海域就不会获得陆地(这在图像中是看不到的,因为事实并非如此)。
如果有 1px "nan" 的海洋比陆地多,它仍然会变成陆地(在这个例子中它有 50/50)。
下图是我需要的。在海洋(值=0)和陆地(值>0)的边界处,"nan"-像素需要获得大部分陆地值的值。
听起来很难,希望我能解释得形象一点。
使用 scipy.ndimage
中的 label()
and 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
我没有找到任何库,所以我写了一个函数,如果数组中间的所有 None 你可以使用这些
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()
在 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
我使用 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) 视为陆地(红色)。
如果有一个被陆地包围的 "little" 海洋(海洋的大小可以再次为 1-5 像素),它将得到陆地,正如您在结果图像中成功看到的那样。如果被包围的海域大于5px或者在陆地之外,海域就不会获得陆地(这在图像中是看不到的,因为事实并非如此)。
如果有 1px "nan" 的海洋比陆地多,它仍然会变成陆地(在这个例子中它有 50/50)。
下图是我需要的。在海洋(值=0)和陆地(值>0)的边界处,"nan"-像素需要获得大部分陆地值的值。
听起来很难,希望我能解释得形象一点。
使用 scipy.ndimage
中的 label()
and 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
我没有找到任何库,所以我写了一个函数,如果数组中间的所有 None 你可以使用这些
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()
在 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