将Numpy 2D数组裁剪为非NaN值

3

我有一个二维Numpy数组,其中包含被NaN包围的数据岛。

我希望裁剪数组,使其仅包含边界框和非NaN区域的内容。

我还想知道相对于原始数组的该框的索引和大小。

这个可能吗?

2个回答

9
import numpy as np
a = np.empty((15,10))
a.fill(np.nan)
a[7,6] = 76
a[8,5] = 85
a[9,5] = 95
a[9,7] = 97

现在我们有一个15行10列的数组,包含一片数据海洋中的一座小岛:

nan,  76.,  nan,
85.,  nan,  nan,
95.,  nan,  97.,

接下来:

nans = np.isnan(a)
nancols = np.all(nans, axis=0) # 10 booleans, True where col is all NAN
nanrows = np.all(nans, axis=1) # 15 booleans

firstcol = nancols.argmin() # 5, the first index where not NAN
firstrow = nanrows.argmin() # 7

lastcol = len(nancols) - nancols[::-1].argmin() # 8, last index where not NAN
lastrow = len(nanrows) - nanrows[::-1].argmin() # 10

最后,需要注意的是:
a[firstrow:lastrow,firstcol:lastcol]

展示给我们这个岛屿:
array([[ nan,  76.,  nan],
       [ 85.,  nan,  nan],
       [ 95.,  nan,  97.]])

0
我遇到了类似的问题,并以@John Zwinck的答案为灵感。我提供了一种替代方案,可以从数组中删除所有的NaN值。它假设非NaN数据是“完整的”,即被NaN包围,但不包含NaN值。就像这个数据集一样:
import numpy as np
image = np.empty((15,10))
image.fill(np.nan)
image[1,5] = 1
image[2,3:6] = 1
image[3,3:7] = 1
image[4,3:8] = 1
image[5,2:9] = 1
image[6,1:9] = 1
image[7,1:8] = 1
image[8,1:8] = 1
image[9,1:9] = 1
image[10,2:7] = 1
image[11,2:] = 1
image[12,4:6] = 1

print(image)
[[nan nan nan nan nan nan nan nan nan nan]
[nan nan nan nan nan  1. nan nan nan nan]
[nan nan nan  1.  1.  1. nan nan nan nan]
[nan nan nan  1.  1.  1.  1. nan nan nan]
[nan nan nan  1.  1.  1.  1.  1. nan nan]
[nan nan  1.  1.  1.  1.  1.  1.  1. nan]
[nan  1.  1.  1.  1.  1.  1.  1.  1. nan]
[nan  1.  1.  1.  1.  1.  1.  1. nan nan]
[nan  1.  1.  1.  1.  1.  1.  1. nan nan]
[nan  1.  1.  1.  1.  1.  1.  1.  1. nan]
[nan nan  1.  1.  1.  1.  1. nan nan nan]
[nan nan  1.  1.  1.  1.  1. nan nan nan]
[nan nan nan nan  1.  1. nan nan nan nan]
[nan nan nan nan nan nan nan nan nan nan]
[nan nan nan nan nan nan nan nan nan nan]]

首先,剪裁掉所有包含NaN的列和行,记录新数组相对于旧数组的左上角的索引值。
nans = np.isnan(image) #Find position al all the NaNs
nancols = np.all(nans, axis=0) # Find all the columns that have only NaNs
nanrows = np.all(nans, axis=1) # Find all the columns that have only NaNs
top_left_x = nancols.argmin() # position of the left most column that does not contain all NaNs
top_left_y = nanrows.argmin() # position of the top most column that does not contain all NaNs
cropped_image = image[:,~nancols][~nanrows] #remove all the rows and columns that are all NaNs
print(cropped_image)
[[nan nan nan nan  1. nan nan nan nan]
[nan nan  1.  1.  1. nan nan nan nan]
[nan nan  1.  1.  1.  1. nan nan nan]
[nan nan  1.  1.  1.  1.  1. nan nan]
[nan  1.  1.  1.  1.  1.  1.  1. nan]
[ 1.  1.  1.  1.  1.  1.  1.  1. nan]
[ 1.  1.  1.  1.  1.  1.  1. nan nan]
[ 1.  1.  1.  1.  1.  1.  1. nan nan]
[ 1.  1.  1.  1.  1.  1.  1.  1. nan]
[nan  1.  1.  1.  1.  1. nan nan nan]
[nan  1.  1.  1.  1.  1.  1.  1.  1.]
[nan nan nan  1.  1. nan nan nan nan]]

接下来,通过删除具有最多NaN的行或列,迭代地处理图像,直到图像中不再存在NaN。
while np.any(np.isnan(cropped_image)): #Loop over the image until there a no NaNs left
    nans = np.isnan(cropped_image) # Locate all NaNs
    nans_in_cols = np.sum(nans,axis=0) # Figure out how many NaNs are in each column
    nans_in_rows = np.sum(nans,axis=1) # Figure out how many NaNs are in each row
    if np.max(nans_in_cols) > np.max(nans_in_rows): # Remove the column or Row with the most NaNs, if it first row or column of the image, add 1 to the top left x or y coordinate
        cropped_image = np.delete(cropped_image, np.argmax(nans_in_cols), 1)
        if np.argmax(nans_in_cols) == 0: top_left_x += 1
    else:
        cropped_image = np.delete(cropped_image, np.argmax(nans_in_rows), 0)
        if np.argmax(nans_in_rows) == 0: top_left_y += 1
print(cropped_image, top_left_x,top_left_y)
[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]] 3 3

这是新的数组,其中左上角对应于原始数组中的位置[3,3]。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接