2011-12-19 38 views
5

Tôi cần khớp hai mảng Numpy rất lớn (một là 20000 hàng, một hàng khoảng 100000 hàng) và tôi đang cố gắng xây dựng tập lệnh để thực hiện hiệu quả. Looping đơn giản trên mảng là cực kỳ chậm, ai đó có thể đề xuất một cách tốt hơn? Dưới đây là những gì tôi đang cố gắng làm: mảng datesSecondDict và mảng pwfs2Dates chứa giá trị datetime, tôi cần lấy từng giá trị datetime từ mảng pwfs2Dates (mảng nhỏ hơn) và xem liệu có giá trị datetime như thế (cộng trừ 5 phút) trong mảng hay không datesSecondDict (có thể có nhiều hơn 1). Nếu có một (hoặc nhiều hơn) tôi điền một mảng mới (có cùng kích thước với mảng pwfs2Dates) với giá trị (một trong các giá trị) từ mảng valsSecondDict (chỉ là mảng có các giá trị số tương ứng là datesSecondDict). Dưới đây là một giải pháp bởi @unutbu và @joaquin mà làm việc cho tôi (nhờ chàng trai!):Kết hợp điều kiện mảng Numpy

import time 
import datetime as dt 
import numpy as np 

def combineArs(dict1, dict2): 
    """Combine data from 2 dictionaries into a list. 
    dict1 contains primary data (e.g. seeing parameter). 
    The function compares each timestamp in dict1 to dict2 
    to see if there is a matching timestamp record(s) 
    in dict2 (plus/minus 5 minutes). 
    ==If yes: a list called data gets appended with the 
    corresponding parameter value from dict2. 
    (Note that if there are more than 1 record matching, 
    the first occuring value gets appended to the list). 
    ==If no: a list called data gets appended with 0.""" 
    # Specify the keys to use  
    pwfs2Key = 'pwfs2:dc:seeing' 
    dimmKey = 'ws:seeFwhm' 

    # Create an iterator for primary dict 
    datesPrimDictIter = iter(dict1[pwfs2Key]['datetimes']) 

    # Take the first timestamp value in primary dict 
    nextDatePrimDict = next(datesPrimDictIter) 

    # Split the second dictionary into lists 
    datesSecondDict = dict2[dimmKey]['datetime'] 
    valsSecondDict = dict2[dimmKey]['values'] 

    # Define time window 
    fiveMins = dt.timedelta(minutes = 5) 
    data = [] 
    #st = time.time() 
    for i, nextDateSecondDict in enumerate(datesSecondDict): 
     try: 
      while nextDatePrimDict < nextDateSecondDict - fiveMins: 
       # If there is no match: append zero and move on 
       data.append(0) 
       nextDatePrimDict = next(datesPrimDictIter) 
      while nextDatePrimDict < nextDateSecondDict + fiveMins: 
       # If there is a match: append the value of second dict 
       data.append(valsSecondDict[i]) 
       nextDatePrimDict = next(datesPrimDictIter) 
     except StopIteration: 
      break 
    data = np.array(data) 
    #st = time.time() - st  
    return data 

Cảm ơn, Aina.

Trả lời

6

Các ngày của mảng có được sắp xếp không?

  • Nếu có, bạn có thể tăng tốc độ so sánh của bạn bằng cách phá vỡ từ bên trong so sánh vòng lặp khi ngày của nó là lớn hơn so với ngày được đưa ra bởi vòng ngoài . Bằng cách này bạn sẽ làm một sự so sánh một-pass thay vì looping dimVals mục len(pwfs2Vals) lần
  • Nếu không, có lẽ bạn nên chuyển đổi pwfs2Dates mảng hiện tại, ví dụ, một mảng của các cặp [(date, array_index),...] và sau đó bạn có thể sắp xếp theo ngày tất cả các mảng của bạn để làm cho việc so sánh một-pass nêu trên và đồng thời để có thể có được các chỉ số ban đầu cần thiết để thiết lập data[i]

ví dụ nếu các mảng đã được sắp xếp (tôi sử dụng danh sách ở đây, không chắc chắn bạn cần mảng cho điều đó): (Sửa: bây giờ sử dụng và iterator không pwfs2Dates vòng lặp từ đầu trên mỗi bước):

pdates = iter(enumerate(pwfs2Dates)) 
i, datei = pdates.next() 

for datej, valuej in zip(dimmDates, dimvals): 
    while datei < datej - fiveMinutes: 
     i, datei = pdates.next() 
    while datei < datej + fiveMinutes: 
     data[i] = valuej 
     i, datei = pdates.next() 

Ngược lại, nếu họ không ra lệnh và bạn tạo ra các sắp xếp, danh sách được lập chỉ mục như thế này:

pwfs2Dates = sorted([(date, idx) for idx, date in enumerate(pwfs2Dates)]) 
dimmDates = sorted([(date, idx) for idx, date in enumerate(dimmDates)]) 

mã sẽ là:
(Sửa: bây giờ sử dụng và iterator không để lặp pwfs2Dates ngay từ đầu trên mỗi bước):

pdates = iter(pwfs2Dates) 
datei, i = pdates.next() 

for datej, j in dimmDates: 
    while datei < datej - fiveMinutes: 
     datei, i = pdates.next() 
    while datei < datej + fiveMinutes: 
     data[i] = dimVals[j] 
     datei, i = pdates.next() 

tuyệt vời!

..

  1. Lưu ý rằng dimVals:

    dimVals = np.array(dict1[dimmKey]['values']) 
    

    không được sử dụng trong mã của bạn và có thể được loại bỏ.

  2. Lưu ý rằng mã của bạn được đơn giản hóa rất nhiều bởi Looping qua mảng chính nó thay vì sử dụng xrange

Edit: Câu trả lời từ unutbu địa chỉ một số bộ phận yếu trong đoạn code trên. tôi thấy họ vào đây để completness:

  1. Sử dụng next: next(iterator) được ưa thích để iterator.next(). iterator.next() là ngoại lệ đối với quy tắc đặt tên thông thường là đã được sửa trong py3k đổi tên phương thức này là iterator.__next__().
  2. Kiểm tra kết thúc của trình lặp với một try/except. Sau khi tất cả các mục trong trình lặp được hoàn thành, lệnh gọi tiếp theo là next() sẽ tạo ra Ngoại lệ Dừng lại. Sử dụng try/except để vui lòng thoát khỏi vòng lặp khi điều đó xảy ra. Đối với trường hợp cụ thể của câu hỏi OP, đây không phải là một vấn đề, bởi vì hai arrrays là cùng kích thước do đó vòng lặp for kết thúc cùng một lúc so với vòng lặp. Vì vậy, không có ngoại lệ được tăng lên. Tuy nhiên, có thể có trường hợp là dict1 và dict2 không cùng kích thước. Và trong trường hợp này có khả năng xảy ra một ngoại lệ được tăng lên. Câu hỏi là: những gì là tốt hơn, để sử dụng thử/trừ hoặc để chuẩn bị các mảng trước khi lặp bằng cách cân bằng chúng với giá trị ngắn hơn.
+0

cảm ơn rất nhiều, nó hoàn toàn làm việc! – Aina

0

Tôi nghĩ bạn có thể làm điều đó với một vòng ít:

import datetime 
import numpy 

# Test data 

# Create an array of dates spaced at 1 minute intervals 
m = range(1, 21) 
n = datetime.datetime.now() 
a = numpy.array([n + datetime.timedelta(minutes=i) for i in m]) 

# A smaller array with three of those dates 
m = [5, 10, 15] 
b = numpy.array([n + datetime.timedelta(minutes=i) for i in m]) 

# End of test data 

def date_range(date_array, single_date, delta): 
    plus = single_date + datetime.timedelta(minutes=delta) 
    minus = single_date - datetime.timedelta(minutes=delta) 
    return date_array[(date_array < plus) * (date_array > minus)] 

dates = [] 
for i in b: 
    dates.append(date_range(a, i, 5)) 

all_matches = numpy.unique(numpy.array(dates).flatten()) 

Có chắc chắn là một cách tốt hơn để thu thập và hợp nhất các trận đấu, nhưng bạn sẽ có được ý tưởng ... Bạn cũng có thể sử dụng numpy.argwhere((a < plus) * (a > minus)) để trả về chỉ mục thay vì ngày và sử dụng chỉ mục để lấy toàn bộ hàng và đặt nó vào mảng mới của bạn.

4

xây dựng trên joaquin's idea:

import datetime as dt 
import itertools 

def combineArs(dict1, dict2, delta = dt.timedelta(minutes = 5)): 
    marks = dict1['datetime'] 
    values = dict1['values'] 
    pdates = iter(dict2['datetime']) 

    data = [] 
    datei = next(pdates) 
    for datej, val in itertools.izip(marks, values): 
     try: 
      while datei < datej - delta: 
       data.append(0) 
       datei = next(pdates) 
      while datei < datej + delta: 
       data.append(val) 
       datei = next(pdates) 
     except StopIteration: 
      break 
    return data 

dict1 = { 'ws:seeFwhm': 
      {'datetime': [dt.datetime(2011, 12, 19, 12, 0, 0), 
         dt.datetime(2011, 12, 19, 12, 1, 0), 
         dt.datetime(2011, 12, 19, 12, 20, 0), 
         dt.datetime(2011, 12, 19, 12, 22, 0), 
         dt.datetime(2011, 12, 19, 12, 40, 0), ], 
      'values': [1, 2, 3, 4, 5] } } 
dict2 = { 'pwfs2:dc:seeing': 
      {'datetime': [dt.datetime(2011, 12, 19, 12, 9), 
         dt.datetime(2011, 12, 19, 12, 19), 
         dt.datetime(2011, 12, 19, 12, 29), 
         dt.datetime(2011, 12, 19, 12, 39), 
         ], } } 

if __name__ == '__main__': 
    dimmKey = 'ws:seeFwhm' 
    pwfs2Key = 'pwfs2:dc:seeing'  
    print(combineArs(dict1[dimmKey], dict2[pwfs2Key])) 

mang

[0, 3, 0, 5] 
+0

+1 để làm cho nó thực sự hoạt động – joaquin

Các vấn đề liên quan