裁剪矩阵的 nan 行和列,但保持正方形
Posted
技术标签:
【中文标题】裁剪矩阵的 nan 行和列,但保持正方形【英文标题】:Crop nan rows and columns of a matrix, but keep it square 【发布时间】:2013-12-30 21:49:55 【问题描述】:我有一个超过 1,000 行和列的方阵。在“边界”的许多字段中都有nan
,例如:
grid = [[nan, nan, nan, nan, nan],
[nan, nan, nan, nan, nan],
[nan, nan, 1, nan, nan],
[nan, 2, 3, 2, nan],
[ 1, 2, 2, 1, nan]]
现在我想删除我只有nan
的所有行和列。这将是 1. 和 2. 行和最后一列。但我也想收到一个方阵,所以淘汰的行数必须等于淘汰的列数。在这个例子中,我想得到这个:
grid = [[nan, nan, nan, nan],
[nan, nan, 1, nan],
[nan, 2, 3, 2],
[ 1, 2, 2, 1]]
我确定我可以通过循环解决这个问题:检查每一列和每一行,如果里面只有 nan
,最后我使用 numpy.delete 删除我找到的行和列(但只有最小的数字,因为得到一个正方形)。
但我希望任何人都可以帮助我提供更好的解决方案或一个好的库。
【问题讨论】:
np.isnan()
会给你一个布尔矩阵,你可以使用一些np.all()
和np.any()
来更进一步
尝试g = np.isnan(grid)
然后grid[:, ~np.all(g, axis=0)][~np.all(g, axis=1)]
。不过不确定你的方形情况。似乎在某些情况下这可能是模棱两可的。
如果你想保持一个正方形,你有时还会有行完全被nan填满?
【参考方案1】:
这可行,压缩 rows\cols 的索引是关键,因此它们始终具有相同的长度,从而保持矩阵的方形。
nans_in_grid = np.isnan(grid)
nan_rows = np.all(nans_in_grid, axis=0)
nan_cols = np.all(nans_in_grid, axis=1)
indicies_to_remove = zip(np.nonzero(nan_rows)[0], np.nonzero(nan_cols)[0])
y_indice_to_remove, x_indice_to_remove = zip(*indicies_to_remove)
tmp = grid[[x for x in range(grid.shape[0]) if x not in x_indice_to_remove], :]
grid = tmp[:, [y for y in range(grid.shape[1]) if y not in y_indice_to_remove]]
继续 E 先生的解决方案,然后填充结果也可以。
def pad_to_square(a, pad_value=np.nan):
m = a.reshape((a.shape[0], -1))
padded = pad_value * np.ones(2 * [max(m.shape)], dtype=m.dtype)
padded[0:m.shape[0], 0:m.shape[1]] = m
return padded
g = np.isnan(grid)
grid = pad_to_square(grid[:, ~np.all(g, axis=0)][~np.all(g, axis=1)])
另一个解决方案,建立在另一个答案的基础上。对于较大的矩阵,速度明显更快。
shape = grid.shape[0]
first_col = (i for i,col in enumerate(grid.T) if np.isfinite(col).any() == True).next()
last_col = (shape-i-1 for i,col in enumerate(grid.T[::-1]) if np.isfinite(col).any() == True).next()
first_row = (i for i,row in enumerate(grid) if np.isfinite(row).any() == True).next()
last_row = (shape-i-1 for i,row in enumerate(grid[::-1]) if np.isfinite(row).any() == True).next()
row_len = last_row - first_row
col_len = last_col - first_col
delta_len = row_len - col_len
if delta_len == 0:
pass
elif delta_len < 0:
first_row = first_row - abs(delta_len)
if first_row < 0:
delta_len = first_row
first_row = 0
last_row += abs(delta_len)
elif delta_len > 0:
first_col -= abs(delta_len)
if first_col < 0:
delta_len = first_col
first_col = 0
last_col += abs(delta_len)
grid = grid[first_row:last_row+1, first_col:last_col+1]
【讨论】:
添加了另一个解决方案 如果nan
行/列不在结果数组的边界,而是在内部,我认为填充解决方案不起作用。
啊,没错。只有当 nans 总是在边界时才能使用它。我认为问题中暗示了什么?他甚至可能不在乎 nan 放在哪里
另一种解决方案,对于更大的阵列更快。【参考方案2】:
import numpy as np
nan = np.nan
grid = [[nan, nan, nan, nan, nan],
[nan, nan, nan, nan, nan],
[nan, nan, 1, nan, nan],
[nan, 2, 3, 2, nan],
[ 1, 2, 2, 1, nan]]
g = np.array(grid)
cols = np.isnan(g).all(axis=0)
rows = np.isnan(g).all(axis=1)
first_col = np.where(cols==False)[0][0]
last_col = len(cols) - np.where(cols[::-1]==False)[0][0] -1
first_row = np.where(rows==False)[0][0]
last_row = len(rows) - np.where(rows[::-1]==False)[0][0] -1
row_len = last_row - first_row
col_len = last_col - first_col
delta_len = row_len - col_len
if delta_len == 0:
pass
elif delta_len < 0:
first_row = first_row - abs(delta_len)
if first_row < 0:
delta_len = first_row
first_row = 0
last_row += abs(delta_len)
elif delta_len > 0:
first_col -= abs(delta_len)
if first_col < 0:
delta_len = first_col
first_col = 0
last_col += abs(delta_len)
print g[first_row:last_row+1, first_col:last_col+1]
输出:
[[ nan nan nan nan]
[ nan nan 1. nan]
[ nan 2. 3. 2.]
[ 1. 2. 2. 1.]]
【讨论】:
【参考方案3】:这是一个更短的方法。它的工作原理是扫描主对角线,删除所有为 nan 的行+列,然后对辅助对角线执行相同操作:
import numpy as np
nan = np.nan
grid = [[nan, nan, nan, nan, nan],
[nan, nan, nan, nan, nan],
[nan, nan, 1, nan, nan],
[nan, 2, 3, 2, nan],
[ 1, 2, 2, 1, nan]]
g = np.array(grid)
for i in [1, 2]:
cols = np.isnan(g).all(axis=0)
rows = np.isnan(g).all(axis=1)
main_diagonal = np.logical_not(cols & rows)
ind = np.nonzero(main_diagonal)[0]
main_diagonal[ind[0]:ind[-1]+1] = True # do not remove inner row/col
removed_main_diag = g[main_diagonal][:, main_diagonal]
g = removed_main_diag[:][::-1]
print g
输出:
[[ nan nan nan nan]
[ nan nan 1. nan]
[ nan 2. 3. 2.]
[ 1. 2. 2. 1.]]
【讨论】:
以上是关于裁剪矩阵的 nan 行和列,但保持正方形的主要内容,如果未能解决你的问题,请参考以下文章