2011-12-19 3 views
5

두 개의 매우 큰 Numpy 배열 (하나는 20000 개의 행, 다른 하나는 약 100000 개의 행)과 일치해야하며 효율적으로 스크립트를 작성하려고합니다. 배열에 대한 단순 루핑은 매우 느리며 누군가가 더 좋은 방법을 제안 할 수 있습니까? 여기에 내가 뭘 하려는지 : 배열 datesSecondDict 및 배열 pwfs2Dates datetime 값이 들어, 배열 pwfs2Dates (더 작은 배열) 배열에서 각 datetime 값을 가져와 배열에 (더하기 마이너스 5 분) 같은 datetime 값이 있는지 확인해야합니다. datesSecondDict (1이 넘을 수도 있음) 배열이 하나 이상 있으면 배열 valsSecondDict (값이 datesSecondDict 인 배열)의 값 (값 중 하나)을 가진 배열 (배열 pwfs2Dates과 동일한 크기 임)을 채 웁니다. 여기에 나를 위해 일한 @unutbu 및 @joaquin하여 솔루션 (감사들!) :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 

감사합니다, 아이나는.

답변

6

배열 날짜가 정렬되어 있습니까?

  • 의 날짜가 외부 루프에 의해 지정된 날짜보다 더 큰 한 번 그래, 당신은 내부 루프 비교 파괴하여 비교를 가속화 할 수 있습니다. 이러한 방법으로 당신이 dimVals 항목을 반복 한 패스 비교 대신 을 만든 것이다 len(pwfs2Vals) 시간
  • 아니, 어쩌면 당신은, 예를 들어, 쌍 [(date, array_index),...]의 배열 한 다음에 의해 정렬 할 수 있습니다 현재 pwfs2Dates 배열을 변환해야하는 경우 배열이 이미 정렬 된 경우 모든 배열 예를 들어 일자가 data[i]

을 설정하는 데 필요한 원래의 인덱스를 얻을 수 있기를 위하고 동시에 표시된 하나의 패스 비교를 위해 (나는 목록을 사용하여 여기에 배열이 필요하지는 않은지 확인하십시오) :이 같은 색인 목록을 주문한되지 않은 경우,

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

그렇지 않으면 당신은 분류 만든 :210 (지금 각 단계에 처음부터하지에 루프 pwfs2Dates를 사용하여 반복자 편집)

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

코드가 될 것이다 :
(를 편집 : 현재 사용되지 각 공정의 처음부터 루프를 pwfs2Dates에 반복자)

위대한!

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

코드에서 사용되지 않으며 제거 할 수 있습니다 dimVals

..

  1. 참고.코드가 크게

xrange을 배열 자체를 통해 루프 대신 사용하여 단순화됩니다


  • 편집 : 대답 코드의 일부 약한 부분 위의 주소 unutbu 에서. 나는 completness 여기를 나타냅니다 next(iterator)iterator.next()에 선호된다

    1. 사용 next의. iterator.next()은 py35k에서 이 수정되어이 메서드의 이름을 iterator.__next__()으로 바꾸는 일반적인 명명 규칙의 예외입니다.
    2. try/except으로 이터레이터의 끝을 확인하십시오. 이터레이터의 모든 항목이 완료되면 next() 에 대한 다음 호출은 StopIteration 예외를 생성합니다. 그런 경우에는 try/except을 사용하여 을 수행하십시오. OP 질문의 경우에는 두 개의 arrrays가 같은 크기이므로 반복문과 동시에 for 루프가 완료되기 때문에 이는 문제가되지 않습니다. 따라서 예외가 발생하지 않습니다. 그러나 dict1과 dict2가있는 경우가있을 수 있습니다. 은 (는) 같은 크기가 아닙니다. 이 경우에는 예외가 제기 될 가능성이 있습니다. 질문 : try/except를 사용하거나 배열을 준비 할 때 더 좋은 점이 무엇입니까? 을 짧게 반올림하여 루프하기 전에.
  • +0

    감사를 산출 너무, 그것은 완전히 일! – Aina

    0

    내가 하나 개 더 적은 루프와 함께 할 수 있다고 생각 : 확실히 수집하고 경기를 병합 할 수있는 더 좋은 방법이있다

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

    ,하지만 당신은 아이디어를 얻을 ... 당신은 또한 numpy.argwhere((a < plus) * (a > minus))을 사용할 수 있습니다 날짜 대신 색인을 리턴하고 색인을 사용하여 전체 행을 가져 와서 새 배열에 놓으십시오.

    4

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

    [0, 3, 0, 5] 
    
    +0

    +1 실제로 작동하도록하기 위해 – joaquin