我有这个丑陋的,非pythonic的野兽:
def crop(dat, clp=True):
'''Crops zero-edges of an array and (optionally) clips it to [0,1].
Example:
>>> crop( np.array(
... [[0,0,0,0,0,0],
... [0,0,0,0,0,0],
... [0,1,0,2,9,0],
... [0,0,0,0,0,0],
... [0,7,4,1,0,0],
... [0,0,0,0,0,0]]
... ))
array([[1, 0, 1, 1],
[0, 0, 0, 0],
[1, 1, 1, 0]])
'''
if clp: np.clip( dat, 0, 1, out=dat )
while np.all( dat[0,:]==0 ):
dat = dat[1:,:]
while np.all( dat[:,0]==0 ):
dat = dat[:,1:]
while np.all( dat[-1,:]==0 ):
dat = dat[:-1,:]
while np.all( dat[:,-1]==0 ):
dat = dat[:,:-1]
return dat
# Below gets rid of zero-lines/columns in the middle
#+so not usable.
#dat = dat[~np.all(dat==0, axis=1)]
#dat = dat[:, ~np.all(dat == 0, axis=0)]
我如何驯服它,让它美丽?
答案 0 :(得分:4)
尝试合并这样的内容:
# argwhere will give you the coordinates of every non-zero point
true_points = np.argwhere(dat)
# take the smallest points and use them as the top left of your crop
top_left = true_points.min(axis=0)
# take the largest points and use them as the bottom right of your crop
bottom_right = true_points.max(axis=0)
out = dat[top_left[0]:bottom_right[0]+1, # plus 1 because slice isn't
top_left[1]:bottom_right[1]+1] # inclusive
对于一般n-d
案例,这可以毫无困难地扩展。
答案 1 :(得分:1)
这应该适用于任何数量的维度。我相信它也非常有效,因为交换轴和切片只创建数组上的视图,而不是复制(排除了take()
或compress()
可能会被诱惑使用的函数)或任何临时函数。然而,它并不比你自己的解决方案明显“好”。
def crop2(dat, clp=True):
if clp: np.clip( dat, 0, 1, out=dat )
for i in range(dat.ndim):
dat = np.swapaxes(dat, 0, i) # send i-th axis to front
while np.all( dat[0]==0 ):
dat = dat[1:]
while np.all( dat[-1]==0 ):
dat = dat[:-1]
dat = np.swapaxes(dat, 0, i) # send i-th axis to its original position
return dat
答案 2 :(得分:1)
绝对不是最漂亮的方法,但想尝试别的东西。
def _fill_gap(a):
"""
a = 1D array of `True`s and `False`s.
Fill the gap between first and last `True` with `True`s.
Doesn't do a copy of `a` but in this case it isn't really needed.
"""
a[slice(*a.nonzero()[0].take([0,-1]))] = True
return a
def crop3(d, clip=True):
dat = np.array(d)
if clip: np.clip(dat, 0, 1, out=dat)
dat = np.compress(_fill_gap(dat.any(axis=0)), dat, axis=1)
dat = np.compress(_fill_gap(dat.any(axis=1)), dat, axis=0)
return dat
但它有效。
In [639]: crop3(np.array(
...: [[0,0,0,0,0,0],
...: [0,0,0,0,0,0],
...: [0,1,0,2,9,0],
...: [0,0,0,0,0,0],
...: [0,7,4,1,0,0],
...: [0,0,0,0,0,0]]))
Out[639]:
array([[1, 0, 1, 1],
[0, 0, 0, 0],
[1, 1, 1, 0]])
答案 3 :(得分:0)
实现此目标的另一种方法(对于密集数组而言更快)利用了argmax
属性:
def get_last_nz(vec):
"""Get last nonzero element position of a vector
:param vec: the vector
:type vec: iterable
"""
if not isinstance(vec, np.ndarray) or vec.dtype != 'bool':
vec = np.array(vec) > 0
return vec.size - 1 - np.argmax(vec[::-1])
def get_first_nz(vec):
"""Get the first nonzero element position of a vector
:param vec: the vector
:type vec: iterable
"""
if not isinstance(vec, np.ndarray) or vec.dtype != 'bool':
vec = np.array(vec) > 0
return np.argmax(vec)
def crop(array):
y_sum = array.sum(axis=1) > 0
x_sum = array.sum(axis=0) > 0
x_min = get_first_nz(x_sum)
x_max = get_last_nz(x_sum)
y_min = get_first_nz(y_sum)
y_max = get_last_nz(y_sum)
return array[y_min: y_max + 1, x_min: x_max + 1]