2013-04-07 9 views
16

Sau nhiều lần thử tối ưu hóa mã, có vẻ như một tài nguyên cuối cùng sẽ cố gắng chạy mã dưới đây bằng cách sử dụng nhiều lõi. Tôi không biết chính xác làm thế nào để chuyển đổi/tái cấu trúc mã của tôi để nó có thể chạy nhanh hơn nhiều bằng cách sử dụng nhiều lõi. Tôi sẽ đánh giá cao nếu tôi có thể nhận được hướng dẫn để đạt được mục tiêu cuối cùng. Mục tiêu cuối cùng là có thể chạy mã này càng nhanh càng tốt cho các mảng A và B trong đó mỗi mảng chứa khoảng 700.000 phần tử. Đây là mã sử dụng các mảng nhỏ. Các mảng phần tử 700k được nhận xét.Python tương đương với hàm "ismember" của MATLAB

import numpy as np 

def ismember(a,b): 
    for i in a: 
     index = np.where(b==i)[0] 
     if index.size == 0: 
      yield 0 
     else: 
      yield index 


def f(A, gen_obj): 
    my_array = np.arange(len(A)) 
    for i in my_array: 
     my_array[i] = gen_obj.next() 
    return my_array 


#A = np.arange(700000) 
#B = np.arange(700000) 
A = np.array([3,4,4,3,6]) 
B = np.array([2,5,2,6,3]) 

gen_obj = ismember(A,B) 

f(A, gen_obj) 

print 'done' 
# if we print f(A, gen_obj) the output will be: [4 0 0 4 3] 
# notice that the output array needs to be kept the same size as array A. 

Những gì tôi đang cố gắng làm là để bắt chước một hàm MATLAB gọi ismember [2] (Một trong đó được định dạng như:. [Lia,Locb] = ismember(A,B) Tôi chỉ cố gắng để có được chỉ là phần Locb

.

Từ Matlab: Locb, chứa chỉ số thấp nhất trong B cho mỗi giá trị trong A mà là thành viên của B. mảng đầu ra, Locb, chứa 0 bất cứ nơi nào A không là thành viên của B

một trong những chính vấn đề là tôi cần phải là abl e để thực hiện thao tác này hiệu quả nhất có thể. Để thử nghiệm, tôi có hai mảng 700k phần tử. Tạo ra một máy phát điện và đi qua các giá trị của máy phát điện dường như không hoàn thành công việc một cách nhanh chóng.

Trả lời

13

Trước khi lo lắng về nhiều lõi, tôi sẽ loại bỏ quá trình quét tuyến tính trong chức năng ismember của bạn bằng cách sử dụng một từ điển:

def ismember(a, b): 
    bind = {} 
    for i, elt in enumerate(b): 
     if elt not in bind: 
      bind[elt] = i 
    return [bind.get(itm, None) for itm in a] # None can be replaced by any other "not in b" value 

thực hiện ban đầu của bạn đòi hỏi một quét toàn bộ các yếu tố trong B cho mỗi phần tử trong A, làm cho nó O(len(A)*len(B)). Đoạn mã trên yêu cầu quét toàn bộ B để tạo ra Bset dict. Bằng cách sử dụng một dict, bạn có hiệu quả làm cho việc tra cứu của mỗi phần tử trong hằng số B cho mỗi phần tử của A, làm cho hoạt động O(len(A)+len(B)). Nếu điều này vẫn còn quá chậm, thì hãy lo lắng về việc làm cho hàm trên chạy trên nhiều lõi.

Chỉnh sửa: Tôi cũng đã sửa đổi chỉ mục của bạn một chút. Matlab sử dụng 0 bởi vì tất cả các mảng của nó bắt đầu từ chỉ số 1. Python/NumPy bắt đầu mảng ở mức 0, vì vậy nếu bạn đang tập hợp dữ liệu trông như thế này

A = [2378, 2378, 2378, 2378] 
B = [2378, 2379] 

và bạn quay lại 0 cho không có yếu tố, sau đó kết quả của bạn sẽ loại trừ tất cả các yếu tố của A. Các thói quen trên trả về None không có chỉ mục thay vì 0. Trả về -1 là một tùy chọn, nhưng Python sẽ giải thích đó là phần tử cuối cùng trong mảng. None sẽ tăng ngoại lệ nếu nó được sử dụng làm chỉ mục vào mảng. Nếu bạn muốn có hành vi khác, hãy thay đổi đối số thứ hai trong biểu thức Bind.get(item,None) thành giá trị bạn muốn trả về.

+0

Chà, điều này thực sự rất nhanh! Bạn không có ý tưởng bao nhiêu tôi đánh giá cao giải pháp của bạn. Cảm ơn nhiều ! Bạn có sử dụng một công cụ cụ thể để xuất cấu hình hiệu suất không? – zd5151

+5

@ z5151 Không, phân tích thuật toán đơn giản. Sử dụng [ký hiệu Big-O] (http://en.wikipedia.org/wiki/Big_O_notation): 'np.where' phải thực hiện quét tuyến tính' B', yêu cầu 'O (len (B))' hoạt động. Sau đó bạn sử dụng một vòng lặp bên ngoài yêu cầu các hoạt động 'O (len (A))', làm cho thuật toán ban đầu của bạn xấp xỉ các hoạt động 'O (len (A) * len (B))'. Việc tạo 'Bind' yêu cầu các hoạt động' len (B) '. Từ điển được triển khai dưới dạng [bảng băm] (http://en.wikipedia.org/wiki/Hash_table), có tra cứu 'O (1)' liên tục, do đó, quét A là 'O (len (A))'; độ phức tạp tổng thể là 'O (len (A) + len (B))'. – sfstewman

+0

OK. Cảm ơn bạn đã tham khảo wikipedia. – zd5151

1

Thử sử dụng danh sách hiểu;

In [1]: import numpy as np 

In [2]: A = np.array([3,4,4,3,6]) 

In [3]: B = np.array([2,5,2,6,3]) 

In [4]: [x for x in A if not x in B] 
Out[4]: [4, 4] 

Nói chung, việc hiểu danh sách nhanh hơn nhiều so với vòng lặp.

Để có được danh sách dài bằng nhau;

In [19]: map(lambda x: x if x not in B else False, A) 
Out[19]: [False, 4, 4, False, False] 

này khá nhanh cho tập dữ liệu nhỏ:

In [20]: C = np.arange(10000) 

In [21]: D = np.arange(15000, 25000) 

In [22]: %timeit map(lambda x: x if x not in D else False, C) 
1 loops, best of 3: 756 ms per loop 

Đối với các tập dữ liệu lớn, bạn có thể thử sử dụng một multiprocessing.Pool.map() để tăng tốc độ hoạt động.

+0

Sản lượng cần phải được giữ cùng kích thước. – zd5151

+0

@ z5151: Xem câu trả lời nâng cao. Bạn có thể thay đổi biểu thức 'lambda' để trả về 0 thay vì False nếu bạn muốn, nhưng điều đó sẽ che dấu 0 thực trong kết quả. –

+0

Điều này rất hữu ích cho các mảng có số lượng phần tử nhỏ. Cảm ơn bạn đã nhấn mạnh rằng việc hiểu danh sách nhanh hơn nhiều so với vòng lặp. – zd5151

10

Câu trả lời hay nhất của sfstewman có khả năng giải quyết được vấn đề cho bạn.

Tôi chỉ muốn thêm làm thế nào bạn có thể đạt được cùng một độc quyền trong gumpy.

Tôi sử dụng chức năng unique của numpy an in1d.

B_unique_sorted, B_idx = np.unique(B, return_index=True) 
B_in_A_bool = np.in1d(B_unique_sorted, A, assume_unique=True) 
  • B_unique_sorted chứa các giá trị duy nhất trong B sắp xếp.
  • B_idx giữ cho các giá trị này chỉ số vào số B gốc.
  • B_in_A_bool là mảng boolean có kích thước là B_unique_sorted rằng lưu trữ xem giá trị trong B_unique_sorted có nằm trong A hay không.
    Lưu ý: tôi cần phải tìm kiếm (Vals độc đáo từ B) A bởi vì tôi cần đầu ra để được trả lại liên quan đến B_idx
    Lưu ý: tôi cho rằng A là đã độc đáo.

Bây giờ bạn có thể sử dụng B_in_A_bool hoặc là có được Vals chung

B_unique_sorted[B_in_A_bool] 

và các chỉ số tương ứng của họ trong bản gốc B

B_idx[B_in_A_bool] 

Cuối cùng, tôi cho rằng đây là nhanh hơn so với cách đáng kể tinh khiết Python cho vòng lặp mặc dù tôi đã không kiểm tra nó.

+0

+1 chỉ cần sử dụng numpy bất cứ khi nào có thể, tăng tốc lớn có thể đạt được theo cách này (như tôi đã học được cách cứng> _ <) –

+1

Xem ra! Điều này không bảo vệ thứ tự của các chỉ số! thử nó với phạm vi (1,6) và [5,1]. Trong trường hợp thứ tự của các chỉ số là không cần tôi nghĩ rằng bạn chỉ có thể sử dụng np.in1d ​​() và sau đó np.nonzero() [0] – aless80

+1

Xem câu trả lời ở đây: https://stackoverflow.com/questions/33678543/finding -indices-of-matches-of-one-array-trong-một mảng khác để nhận các chỉ mục theo đúng thứ tự – aless80

0

Đây là tương đương MATLAB chính xác trả về cả hai đối số đầu ra [Lia, Locb] khớp với MATLAB ngoại trừ trong Python 0 cũng là một chỉ mục hợp lệ. Vì vậy, hàm này không trả về số 0. Về cơ bản nó trả về Locb (Locb> 0). Hiệu suất cũng tương đương với MATLAB.

def ismember(a_vec, b_vec): 
    """ MATLAB equivalent ismember function """ 

    bool_ind = np.isin(a_vec,b_vec) 
    common = a[bool_ind] 
    common_unique, common_inv = np.unique(common, return_inverse=True)  # common = common_unique[common_inv] 
    b_unique, b_ind = np.unique(b_vec, return_index=True) # b_unique = b_vec[b_ind] 
    common_ind = b_ind[np.isin(b_unique, common_unique, assume_unique=True)] 
    return bool_ind, common_ind[common_inv] 

An thực hiện xen kẽ đó là một chút (~ 5x) chậm hơn nhưng không sử dụng chức năng duy nhất là ở đây:

mảng
def ismember(a_vec, b_vec): 
    ''' MATLAB equivalent ismember function. Slower than above implementation''' 
    b_dict = {b_vec[i]: i for i in range(0, len(b_vec))} 
    indices = [b_dict.get(x) for x in a_vec if b_dict.get(x) is not None] 
    booleans = np.in1d(a_vec, b_vec) 
    return booleans, np.array(indices, dtype=int)