2011-12-19 21 views
5

मुझे दो बहुत बड़े नम्पी सरणी (एक 20000 पंक्तियां, एक और 100000 पंक्तियों) से मेल खाना चाहिए और मैं इसे कुशलतापूर्वक करने के लिए एक स्क्रिप्ट बनाने की कोशिश कर रहा हूं। सरणी पर सरल लूपिंग अविश्वसनीय रूप से धीमी है, क्या कोई बेहतर तरीका सुझा सकता है? यहां मैं जो करने की कोशिश कर रहा हूं वह है: सर datesSecondDict और सरणी pwfs2Dates में डेटाटाइम मान होते हैं, मुझे सरणी pwfs2Dates (छोटी सरणी) से प्रत्येक डेटाटाइम मान लेने की आवश्यकता है और देखें कि उस तरह का डेटाटाइम वैल्यू है (प्लस माइनस 5 मिनट) सरणी में datesSecondDict (1 से अधिक हो सकता है)। यदि कोई (या अधिक) है, तो मैं valsSecondDict से मान (मूल्यों में से एक) के साथ एक नई सरणी (सरणी pwfs2Dates के समान आकार के) को पॉप्युलेट करता हूं (जो कि datesSecondDict पर संबंधित संख्यात्मक मानों के साथ सरणी है)। यहाँ @unutbu और @joaquin द्वारा एक समाधान है कि मेरे लिए काम किया है (धन्यवाद लोग!):न्यूम्पी सरणी सशर्त मिलान

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 

धन्यवाद, Aina।

उत्तर

6

क्या सरणी तिथियां क्रमबद्ध हैं?

  • यदि हाँ, आप आंतरिक पाश तुलना से विभाजित करके अपने तुलना में तेजी लाने के कर सकते हैं एक बार अपने दिनांकों बाहरी पाश द्वारा दी गई तारीख से भी बड़ा है। इस तरह आप एक पास तुलना बजाय dimVals आइटम पाशन बनाया जाएगा len(pwfs2Vals) बार
  • यदि नहीं, तो हो सकता है आप वर्तमान pwfs2Dates सरणी के लिए, उदाहरण के लिए, जोड़े [(date, array_index),...] की एक सरणी और फिर आप द्वारा सॉर्ट कर सकते हैं बदलने चाहिए अपने सभी सरणियों तारीख एक-पास तुलना ऊपर और एक ही समय में संकेत दिया बनाने के लिए उदाहरण के लिए data[i]

सेट करने के लिए आवश्यक मूल अनुक्रमित पाने के लिए सक्षम होने के लिए है, तो सरणियों पहले से ही अलग कर लिए जाते (मैं सूचियों का उपयोग यहां, सुनिश्चित नहीं है कि आपको इसके लिए सरणी की आवश्यकता है): ( संपादित: अब हर कदम पर शुरू से ही उपयोग करते हुए और इटरेटर के लिए नहीं पाश pwfs2Dates):

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

अन्यथा, यदि वे आदेश दिया नहीं कर रहे थे और आप हल कर बनाया जाता था अनुक्रमित सूचियों इस तरह:

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

कोड होगा:
(संपादित: अब का उपयोग कर और प्रत्येक चरण पर शुरू से ही पाश pwfs2Dates के लिए नहीं iterator):

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

बढ़िया!

..

  1. ध्यान दें कि dimVals:

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

    अपने कोड में नहीं किया जाता है और समाप्त किया जा सकता।

  2. ध्यान रखें कि आपके कोड बहुत सरणी खुद के माध्यम से पाशन के बजाय xrange
का उपयोग करके सरलीकृत हो जाता है

संपादित करें: जवाब उपरोक्त कोड में कुछ कमजोर हिस्सों से unutbu पता। मैं उन्हें यहाँ completness के लिए संकेत मिलता है:

  1. next का प्रयोग: next(iterator)iterator.next() लिए पसंदीदा है। iterator.next() एक परंपरागत नामकरण नियम का अपवाद है कि को इस विधि को iterator.__next__() के रूप में नामित करने के लिए py3k में तय किया गया है।
  2. try/except के साथ इटरेटर के अंत की जांच करें। इटरेटर में सभी आइटमों के बाद next() पर अगली कॉल समाप्त हो गई है, जो स्टॉपइटरेशन अपवाद उत्पन्न करती है। try/except का उपयोग लूप से बाहर होने पर करें जब ऐसा होता है। ओपी प्रश्न के विशिष्ट मामले के लिए यह कोई मुद्दा नहीं है, क्योंकि दो arrays एक ही आकार हैं, इसलिए लूप के लिए एक ही समय में iterator की तुलना में समाप्त होता है। तो अपवाद बढ़ गया है। हालांकि, मामले dict1 थे और dict2 समान आकार नहीं हैं। और इस मामले में अपवाद बढ़ने की संभावना है। प्रश्न यह है: कम से कम बराबर करके लूपिंग से पहले सरणी को निकालने/छोड़ने या तैयार करने के लिए बेहतर क्या है।
+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 इसे वास्तव में काम करने के लिए +1 – joaquin

संबंधित मुद्दे