2D 배열에서 대각선 당 최대 값


9

배열이 있고 동적 창과 최대 롤링 차이가 필요합니다.

a = np.array([8, 18, 5,15,12])
print (a)
[ 8 18  5 15 12]

먼저 자체적으로 차이를 만듭니다.

b = a - a[:, None]
print (b)
[[  0  10  -3   7   4]
 [-10   0 -13  -3  -6]
 [  3  13   0  10   7]
 [ -7   3 -10   0  -3]
 [ -4   6  -7   3   0]]

그런 다음 상단 삼각형 행렬을 0으로 바꿉니다.

c = np.tril(b)
print (c)
[[  0   0   0   0   0]
 [-10   0   0   0   0]
 [  3  13   0   0   0]
 [ -7   3 -10   0   0]
 [ -4   6  -7   3   0]]

마지막으로 대각선 당 최대 값이 필요하므로 다음을 의미합니다.

max([0,0,0,0,0]) = 0  
max([-10,13,-10,3]) = 13
max([3,3,-7]) = 3
max([-7,6]) = 6
max([-4]) = -4

예상되는 결과는 다음과 같습니다.

[0, 13, 3, 6, -4]

좋은 벡터화 솔루션은 무엇입니까? 아니면 예상되는 출력을위한 다른 방법이 있습니까?

답변:


3

이것이 고급 인덱싱을 고려하는 것이 얼마나 효율적인지 확실하지는 않지만 그렇게하는 한 가지 방법입니다.

import numpy as np

a = np.array([8, 18, 5, 15, 12])
b = a[:, None] - a
# Fill lower triangle with largest negative
b[np.tril_indices(len(a))] = np.iinfo(b.dtype).min  # np.finfo for float
# Put diagonals as rows
s = b.strides[1]
diags = np.ndarray((len(a) - 1, len(a) - 1), b.dtype, b, offset=s, strides=(s, (len(a) + 1) * s))
# Get maximum from each row and add initial zero
c = np.r_[0, diags.max(1)]
print(c)
# [ 0 13  3  6 -4]

편집하다:

당신이 찾고있는 것이 아닐 수도있는 다른 대안은 다음과 같이 Numba를 사용하는 것입니다.

import numpy as np
import numba as nb

def max_window_diffs_jdehesa(a):
    a = np.asarray(a)
    dtinf = np.iinfo(b.dtype) if np.issubdtype(b.dtype, np.integer) else np.finfo(b.dtype)
    out = np.full_like(a, dtinf.min)
    _pwise_diffs(a, out)
    return out

@nb.njit(parallel=True)
def _pwise_diffs(a, out):
    out[0] = 0
    for w in nb.prange(1, len(a)):
        for i in range(len(a) - w):
            out[w] = max(a[i] - a[i + w], out[w])

a = np.array([8, 18, 5, 15, 12])
print(max_window_diffs(a))
# [ 0 13  3  6 -4]

이 방법들을 원본과 비교하기 :

import numpy as np
import numba as nb

def max_window_diffs_orig(a):
    a = np.asarray(a)
    b = a - a[:, None]
    out = np.zeros(len(a), b.dtype)
    out[-1] = b[-1, 0]
    for i in range(1, len(a) - 1):
        out[i] = np.diag(b, -i).max()
    return out

def max_window_diffs_jdehesa_np(a):
    a = np.asarray(a)
    b = a[:, None] - a
    dtinf = np.iinfo(b.dtype) if np.issubdtype(b.dtype, np.integer) else np.finfo(b.dtype)
    b[np.tril_indices(len(a))] = dtinf.min
    s = b.strides[1]
    diags = np.ndarray((len(a) - 1, len(a) - 1), b.dtype, b, offset=s, strides=(s, (len(a) + 1) * s))
    return np.concatenate([[0], diags.max(1)])

def max_window_diffs_jdehesa_nb(a):
    a = np.asarray(a)
    dtinf = np.iinfo(b.dtype) if np.issubdtype(b.dtype, np.integer) else np.finfo(b.dtype)
    out = np.full_like(a, dtinf.min)
    _pwise_diffs(a, out)
    return out

@nb.njit(parallel=True)
def _pwise_diffs(a, out):
    out[0] = 0
    for w in nb.prange(1, len(a)):
        for i in range(len(a) - w):
            out[w] = max(a[i] - a[i + w], out[w])

np.random.seed(0)
a = np.random.randint(0, 100, size=100)
r = max_window_diffs_orig(a)
print((max_window_diffs_jdehesa_np(a) == r).all())
# True
print((max_window_diffs_jdehesa_nb(a) == r).all())
# True

%timeit max_window_diffs_orig(a)
# 348 µs ± 986 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit max_window_diffs_jdehesa_np(a)
# 91.7 µs ± 1.3 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit max_window_diffs_jdehesa_nb(a)
# 19.7 µs ± 88.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

np.random.seed(0)
a = np.random.randint(0, 100, size=10000)
%timeit max_window_diffs_orig(a)
# 651 ms ± 26 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit max_window_diffs_jdehesa_np(a)
# 1.61 s ± 6.19 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit max_window_diffs_jdehesa_nb(a)
# 22 ms ± 967 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

첫 번째 배열은 작은 배열에서는 조금 나을 수 있지만 큰 배열에서는 잘 작동하지 않습니다. 반면에 Numba는 모든 경우에 매우 좋습니다.


응답 할 타이밍을 추가 할 수 있습니까 (예 : 10, 100, 1000) a?
jezrael

1
@jezrael Numba 솔루션과 시간 측정을 추가했습니다. 내 NumPy 솔루션은 실제로 잘 확장되지 않지만 Numba는 유용하지만 도움이되는지 확실하지 않습니다.
jdehesa

4

사용하다 ndarray.diagonal

v = [max(c.diagonal(-i)) for i in range(b.shape[0])]
print(v) # [0, 13, 3, 6, -4]

1

당신은 사용할 수 있습니다 numpy.diagonal:

a = np.array([8, 18, 5,15,12])
b = a - a[:, None]
c = np.tril(b)
for i in range(b.shape[0]):
    print(max(c.diagonal(-i)))

산출:

0
13
3
6
-4

나는 더 루프 벡터화없는 생각
jezrael

1

다음은 벡터화 된 솔루션입니다 strides.

from skimage.util import view_as_windows

n = len(a)
z = np.zeros(n-1,dtype=a.dtype)
p = np.concatenate((a,z))

s = view_as_windows(p,n)
mask = np.tri(n,k=-1,dtype=bool)[:,::-1]
v = s[0]-s
out = np.where(mask,v.min()-1,v).max(1)

메모리 효율성을위한 1 루프

n = len(a)
out = [max(a[:-i+n]-a[i:]) for i in range(n)]

사용 np.max대신에 max배열의 메모리를보다 효율적으로 사용합니다.


1
@jezrael 내가 생각하는 데이터 크기에 따라 다릅니다. 큰 크기의 경우 슬라이싱 + 최대를 가진 루프가있는 것이 mem-efficiency 때문에 이길 수 있다고 생각합니다.
Divakar

1

당신은 모양의 정사각형이 아닌 배열을 재편한다는 사실을 악용 할 수 있습니다 (N+1, N)로하는 (N, N+1)대각선이 열로 표시 할 것

from scipy.linalg import toeplitz
a = toeplitz([1,2,3,4], [1,4,3])
# array([[1, 4, 3],
#        [2, 1, 4],
#        [3, 2, 1],
#        [4, 3, 2]])
a.reshape(3, 4)
# array([[1, 4, 3, 2],
#        [1, 4, 3, 2],
#        [1, 4, 3, 2]])

그런 다음 사용할 수 있습니다 (기호를 바꾸고 아래쪽 삼각형을 0으로 설정했습니다)

smallv = -10000  # replace this with np.nan if you have floats

a = np.array([8, 18, 5,15,12])
b = a[:, None] - a

b[np.tril_indices(len(b), -1)] = smallv
d = np.vstack((b, np.full(len(b), smallv)))

d.reshape(len(d) - 1, -1).max(0)[:-1]
# array([ 0, 13,  3,  6, -4])
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.