2011-12-19 122 views
5

我需要匹配两个非常大的Numpy数组(一个是20000行,另一个大约100000行),我试图构建一个脚本来有效地完成它。简单的循环遍历数组非常慢,有人可以提出更好的方法吗?这是我想要做的:数组datesSecondDict和数组pwfs2Dates包含日期时间值,我需要从数组pwfs2Dates(较小的数组)中获取每个日期时间值,并查看数组中是否有类似的日期时间值(加上减去5分钟) datesSecondDict(可能有1个以上)。如果有一个(或多个)I使用数组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)
  • 如果没有,也许你应该改变目前的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从开始):

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:

使用 next
  1. next(iterator)是首选到iterator.next()iterator.next()是传统命名规则的一个例外, 已在py3k中修复,将此方法重命名为 iterator.__next__()
  2. 检查迭代器的末尾是否有try/except。在迭代器中的所有 项完成后,下一个对next() 的调用会产生StopIteration异常。当发生这种情况时,请使用try/except友善地打出 。对于 OP问题的具体情况,这不是一个问题,因为两个数组的大小相同,所以for循环与迭代器同时完成。所以没有 异常上升。但是,可能有一些情况是dict1和dict2 的大小不一样。在这种情况下, 例外的可能性正在上升。 问题是:什么是更好的,使用尝试/除了或准备数组 循环之前通过均衡他们到较短的一个。
+0

感谢这么多,它完全成功了! – Aina 2011-12-20 20:48:33

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 2011-12-20 21:03:12