ค่าสูงสุดต่อเส้นทแยงมุมในอาร์เรย์ 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]

vectorized solution ที่ดีคืออะไร? หรือเป็นไปได้อีกวิธีหนึ่งสำหรับการส่งออกที่คาดหวัง

คำตอบ:


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, 1,000 ได้aหรือไม่
jezrael

1
@jezrael เพิ่มโซลูชัน Numba ที่เป็นไปได้และมาตรการเวลา วิธีแก้ปัญหา NumPy ของฉันไม่ค่อยดีนัก Numba นั้นดี แต่ฉันก็ไม่แน่ใจว่ามันมีประโยชน์สำหรับคุณหรือไม่
jdehesa



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)

ด้วยหนึ่งลูปเพื่อประสิทธิภาพหน่วยความจำ -

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

ใช้np.maxแทนmaxการใช้หน่วยความจำอาเรย์ที่ดีขึ้น


1
@jezrael ขึ้นอยู่กับชุดข้อมูลที่ฉันคิดว่า สำหรับขนาดใหญ่ฉันคิดว่าวงแหวนที่มีการแบ่ง + สูงสุดอาจชนะเพราะประสิทธิภาพในการใช้งาน
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]])

ซึ่งคุณสามารถใช้เช่น (โปรดทราบว่าฉันสลับเครื่องหมายและตั้งสามเหลี่ยมล่างเป็นศูนย์)

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.