2011-12-19 21 views
5

İki çok büyük Numpy dizisini eşleştirmem gerekiyor (biri 20000 satır, başka bir 100000 satır) ve bunu verimli bir şekilde yapmak için bir komut dosyası oluşturmaya çalışıyorum. Diziler üzerinde basit döngü inanılmaz derecede yavaş, birisi daha iyi bir yol önerebilir mi? İşte yapmaya çalıştığım şey: dizi datesSecondDict ve dizi pwfs2Dates datetime değerlerini içerir, her bir datetime değerini dizi pwfs2Dates'dan (daha küçük bir dizi) almam ve dizide bir datetime değeri var mı (artı eksi 5 dakika) olup olmadığını görmem gerekir datesSecondDict (1'den fazla olabilir). Bir (veya daha fazla) varsa, valsSecondDict dizisinden gelen değerle (değerlerden biri) (pwfs2Dates dizilişiyle aynı boyutta) yeni bir dizi dolduruyorum (bu yalnızca datesSecondDict'a karşılık gelen sayısal değerlere sahip dizidir). İşte benim için çalıştı @unutbu ve @joaquin bir çözümdür (teşekkürler çocuklar!):Numpy dizisi koşullu eşleştirmesi

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 

sayesinde Aina.

cevap

6

Dizi tarihleri ​​sıralanıyor mu?

  • onun tarih dış döngü tarafından verilen tarihte daha büyük bir kez evet, iç döngü karşılaştırılmasından kırarak karşılaştırmalar hızlandırabilir edin. Bu şekilde dimVals öğeleri döngü bir tek geçişli karşılaştırma yerine yapılmış olacaktır len(pwfs2Vals) kez
  • hayır, belki, örneğin, bir çifti [(date, array_index),...] dizisi ve sonra tarafından sıralayabilirsiniz mevcut pwfs2Dates dizi dönüşümü olursa diziler zaten sıralanmış olsaydı tüm diziler tarih örneğin data[i]

ayarlamak için gerekli orijinal dizinleri elde edebilmek için yukarıda ve aynı anda gösterilen tek geçişli bir karşılaştırma yapmak için (I listeleri kullanmak Burada, bunun için dizilere ihtiyacınız olduğundan emin değilsiniz):Bu gibi endeksli listeleri onlar sipariş olmasaydı,

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

Aksi ve sıraladığınız oluşturulan: 210 (: şimdi her adım baştan değil döngü pwfs2Dates kullanarak ve yineleyici Düzenlendi)

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

kodu olacaktır:
( düzenlenmiştir: Şu anda kullanılarak olup her adım baştan döngü pwfs2Dates Yineleyiciyi):

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

great!

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

kodunuzda kullanılmaz ve elimine edilebilir: dimVals

..

  1. Not.kodunuzu ölçüde

xrange dizinin kendisi döngü yerine kullanarak basitleştirilmiş olur

  • Not

    Düzenleme: cevap kodunda bazı zayıf kısımları yukarıda adresi unutbu dan. Ben tamlanışını için onları burada belirtmek: next(iterator)iterator.next() için tercih edilir:

    1. Kullanım next arasında. iterator.next(), 'un bu yöntemi iterator.__next__() olarak yeniden adlandırdığı geleneksel bir kurallandırma kuralının istisnasıdır.
    2. Yineleyicinin sonunu try/except ile kontrol edin. Yineleyicideki öğesinin tümü bittikten sonra next() için bir Durma Özel Durumu oluşturur. Bu durumda try/except kullanın, bu durumda döngüsünden kurtulun. OP sorusunun özel durumu için bu bir sorun değildir, çünkü iki arrrays aynı boyutundadır, dolayısıyla for döngüsü yineleyici ile aynı anda sona erer. Yani istisnası artırıldı. Bununla birlikte, durumlar dict1 ve dict2 aynı boyutta olmayabilir. Ve bu durumda, bir istisnasının yükselmesi olasılığı vardır. Soru şudur: daha iyisi, deneyin/istisnayı kullanmak veya dizilerini daha kısa olana eşitleyerek döngüden önce hazırlamaktır.
  • +0

    sayesinde verir o kadar, tamamen işe yaradı! Aslında çalışmak için – Aina

    0

    Sana bir daha az döngüler ile yapabilirsiniz düşünüyorum: mutlaka toplamak ve maçları birleştirmek için daha iyi bir yol yoktur

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

    , ama fikir olsun ... Ayrıca numpy.argwhere((a < plus) * (a > minus)) kullanabilirsiniz dizini tarih yerine döndürmek ve dizinin tamamını almak için dizini kullanın ve yeni dizininize yerleştirin.

    4

    joaquin's idea Bina:

    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

    İlgili konular