Giá trị tối đa trên mỗi đường chéo trong mảng 2d


9

Tôi có mảng và cần tối đa sự khác biệt với cửa sổ động.

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

Vì vậy, trước tiên tôi tự tạo ra sự khác biệt:

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]]

Sau đó thay thế ma trận tam giác trên thành 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]]

Cuối cùng cần giá trị tối đa trên mỗi đường chéo, vì vậy nó có nghĩa là:

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

Vì vậy, sản lượng dự kiến ​​là:

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

Một số giải pháp vectorized tốt đẹp là gì? Hoặc có thể một số cách khác cho đầu ra dự kiến?

Câu trả lời:


3

Không chắc chắn chính xác mức độ hiệu quả của việc xem xét lập chỉ mục nâng cao có liên quan, nhưng đây là một cách để làm điều đó:

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]

BIÊN TẬP:

Một lựa chọn khác, có thể không phải là những gì bạn đang tìm kiếm, chỉ là sử dụng Numba, ví dụ như thế này:

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]

So sánh các phương pháp này với bản gốc:

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)

Cái đầu tiên có thể tốt hơn một chút cho các mảng nhỏ hơn, nhưng không hoạt động tốt cho các mảng lớn hơn. Numba mặt khác là khá tốt trong mọi trường hợp.


Bạn có thể thêm một số thời gian để trả lời, ví dụ cho 10, 100, 1000 giá trị trong akhông?
jezrael

1
@jezrael Đã thêm giải pháp Numba có thể và một số biện pháp thời gian. Giải pháp NumPy của tôi không thực sự mở rộng quy mô, Numba rất tốt, mặc dù tôi không chắc nó có hữu ích cho bạn không.
jdehesa

4

Sử dụng ndarray.diagonal

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

1

Bạn có thể sử dụng 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)))

Đầu ra:

0
13
3
6
-4

Tôi nghĩ véc tơ, không có vòng lặp
jezrael

1

Đây là một giải pháp véc tơ với 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)

Với một vòng lặp cho hiệu quả bộ nhớ -

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

Sử dụng np.maxthay thế maxcho việc sử dụng bộ nhớ mảng tốt hơn.


1
@jezrael Phụ thuộc vào dữ liệu tôi sẽ nghĩ. Đối với các kích thước lớn, tôi sẽ nghĩ rằng một cái vòng với cắt + max có thể chiến thắng vì hiệu quả của mem.
Divakar

1

Bạn có thể lạm dụng thực tế là định hình lại các mảng không vuông hình dạng (N+1, N)để (N, N+1)sẽ làm cho đường chéo xuất hiện như cột

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]])

Mà sau đó bạn có thể sử dụng như thế nào (lưu ý rằng tôi đã hoán đổi dấu hiệu và đặt tam giác dưới thành không)

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])
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.