Tìm chỉ số của điểm gần nhất trong mảng vô số các tọa độ x và y


82

Tôi có hai mảng numpy 2d: x_array chứa thông tin vị trí theo hướng x, y_array chứa các vị trí theo hướng y.

Sau đó tôi có một danh sách dài các điểm x, y.

Đối với mỗi điểm trong danh sách, tôi cần tìm chỉ số mảng của vị trí (được chỉ định trong các mảng) gần điểm đó nhất.

Tôi đã tạo ra một số mã hoạt động một cách ngây thơ, dựa trên câu hỏi này: Tìm giá trị gần nhất trong mảng numpy

I E

import time
import numpy

def find_index_of_nearest_xy(y_array, x_array, y_point, x_point):
    distance = (y_array-y_point)**2 + (x_array-x_point)**2
    idy,idx = numpy.where(distance==distance.min())
    return idy[0],idx[0]

def do_all(y_array, x_array, points):
    store = []
    for i in xrange(points.shape[1]):
        store.append(find_index_of_nearest_xy(y_array,x_array,points[0,i],points[1,i]))
    return store


# Create some dummy data
y_array = numpy.random.random(10000).reshape(100,100)
x_array = numpy.random.random(10000).reshape(100,100)

points = numpy.random.random(10000).reshape(2,5000)

# Time how long it takes to run
start = time.time()
results = do_all(y_array, x_array, points)
end = time.time()
print 'Completed in: ',end-start

Tôi đang làm điều này trên một tập dữ liệu lớn và thực sự muốn tăng tốc nó lên một chút. Bất cứ ai có thể tối ưu hóa điều này?

Cảm ơn.


CẬP NHẬT: GIẢI PHÁP theo đề xuất của @silvado và @justin (bên dưới)

# Shoe-horn existing data for entry into KDTree routines
combined_x_y_arrays = numpy.dstack([y_array.ravel(),x_array.ravel()])[0]
points_list = list(points.transpose())


def do_kdtree(combined_x_y_arrays,points):
    mytree = scipy.spatial.cKDTree(combined_x_y_arrays)
    dist, indexes = mytree.query(points)
    return indexes

start = time.time()
results2 = do_kdtree(combined_x_y_arrays,points_list)
end = time.time()
print 'Completed in: ',end-start

Đoạn mã trên đã tăng tốc mã của tôi (tìm kiếm 5000 điểm trong ma trận 100x100) lên 100 lần. Thật thú vị, việc sử dụng scipy.spatial.KDTree (thay vì scipy.spatial.cKDTree ) đã đưa ra thời gian tương đương với giải pháp ngây thơ của tôi, vì vậy, chắc chắn đáng để sử dụng phiên bản cKDTree ...


1
Chỉ là một phỏng đoán nhưng có thể một cây kd sẽ giúp ích. Tôi không biết liệu Python có triển khai hay không.
Justin

Không cần tạo danh sách và chuyển đổi 'điểm'. Thay vào đó, hãy sử dụng một mảng và chia nhỏ các chỉ mục.
Théo Simier

Câu trả lời:


48

scipy.spatialcũng có một kd thực hiện cây: scipy.spatial.KDTree.

Cách tiếp cận thường là trước tiên sử dụng dữ liệu điểm để xây dựng cây kd. Độ phức tạp tính toán của điều đó theo thứ tự N log N, trong đó N là số điểm dữ liệu. Sau đó có thể thực hiện các truy vấn phạm vi và tìm kiếm hàng xóm gần nhất với độ phức tạp của nhật ký N. Điều này hiệu quả hơn nhiều so với việc đơn giản là đạp xe qua tất cả các điểm (độ phức tạp N).

Vì vậy, nếu bạn có phạm vi lặp lại hoặc các truy vấn hàng xóm gần nhất, cây kd được khuyến khích.


1
Điều này có vẻ rất hứa hẹn. Tôi sẽ bắt đầu đọc về nó và xem liệu tôi có thể làm được thứ gì đó hoạt động hay không ...
Pete W

1
Tôi vẫn đang kiểm tra mã của mình, nhưng các dấu hiệu ban đầu cho thấy việc sử dụng scipy.spatial.cKDTree nhanh hơn khoảng 100 lần so với cách tiếp cận ngây thơ của tôi. Khi tôi có thêm thời gian vào ngày mai, tôi sẽ đăng mã cuối cùng của mình và rất có thể sẽ chấp nhận câu trả lời này (trừ khi có một phương pháp nhanh hơn trước đó!). Cảm ơn bạn đã giúp đỡ.
Pete W

OK, sử dụng scipy.spatial.cKDTree có vẻ là một cách tốt nhất. Thử nghiệm với dữ liệu thử nghiệm của tôi cho thấy rằng tiêu chuẩn scipy.spatial.KDTree không mang lại nhiều / bất kỳ cải tiến nào so với giải pháp ngây thơ của tôi.
Pete W,

74

Đây là một scipy.spatial.KDTreeví dụ

In [1]: from scipy import spatial

In [2]: import numpy as np

In [3]: A = np.random.random((10,2))*100

In [4]: A
Out[4]:
array([[ 68.83402637,  38.07632221],
       [ 76.84704074,  24.9395109 ],
       [ 16.26715795,  98.52763827],
       [ 70.99411985,  67.31740151],
       [ 71.72452181,  24.13516764],
       [ 17.22707611,  20.65425362],
       [ 43.85122458,  21.50624882],
       [ 76.71987125,  44.95031274],
       [ 63.77341073,  78.87417774],
       [  8.45828909,  30.18426696]])

In [5]: pt = [6, 30]  # <-- the point to find

In [6]: A[spatial.KDTree(A).query(pt)[1]] # <-- the nearest point 
Out[6]: array([  8.45828909,  30.18426696])

#how it works!
In [7]: distance,index = spatial.KDTree(A).query(pt)

In [8]: distance # <-- The distances to the nearest neighbors
Out[8]: 2.4651855048258393

In [9]: index # <-- The locations of the neighbors
Out[9]: 9

#then 
In [10]: A[index]
Out[10]: array([  8.45828909,  30.18426696])

5
Cảm ơn bạn đã trả lời đầy đủ với một ví dụ làm việc (đơn giản), đánh giá cao nó!
johndodo

@lostCrotchet Tôi nghĩ vậy .. Tôi cũng đã sử dụng nó với nhiều hơn một cặp dữ liệu. ví dụ: (x, y, z, i)
efirvida

5

Nếu bạn có thể đưa dữ liệu của mình vào đúng định dạng, thì một cách nhanh chóng là sử dụng các phương pháp scipy.spatial.distancesau:

http://docs.scipy.org/doc/scipy/reference/spatial.distance.html

Đặc biệt pdistcdistcung cấp các cách nhanh chóng để tính toán khoảng cách theo cặp.


Tôi cũng gọi đó là xoa bóp, nó mô tả khá nhiều những gì chúng ta làm với dữ liệu. : D
Lorinc Nyitrai

1
Scipy.spatil.distance là một công cụ tuyệt vời nhưng hãy lưu ý rằng nếu bạn có nhiều khoảng cách để tính toán cKdtree sẽ nhanh hơn rất nhiều so với cdist.
Losbaltica

1
Nếu tôi không hiểu lầm, sử dụng cdist () hoặc phương pháp NumPy khác được thể hiện trong câu trả lời này codereview.stackexchange.com/a/134918/156228
Alex F
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.