Numpy数组条件匹配

2024-06-28 21:00:16 发布

您现在位置:Python中文网/ 问答频道 /正文

我需要匹配两个非常大的Numpy数组(一个是20000行,另一个是大约100000行),我正试图构建一个脚本来高效地完成它。简单地在数组上循环是非常慢的,有人能建议一个更好的方法吗?下面是我要做的:数组datesSecondDict和数组pwfs2Dates包含日期时间值,我需要从数组pwfs2Dates(更小的数组)中获取每个datetime值,看看数组datesSecondDict(可能不止1个)中有没有这样的datetime值(加上-5分钟)。如果有一个(或多个)数组,我用数组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

谢谢, 艾娜。在


Tags: thetoindatadatetimeiftimevalue
3条回答

建立在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]))

收益率

^{pr2}$

数组日期是否排序?在

  • 如果是的话,你可以通过打破内心世界来加速你的比较 如果其日期大于 外环。通过这种方式,您将只进行一次比较,而不是 循环dimValslen(pwfs2Vals)
  • 如果不是,也许您应该将当前的pwfs2Dates数组转换为,例如, 一个成对的数组[(date, array_index),...],然后可以按 为所有数组添加日期,以便进行上面和 同时能够获得设置data[i]所需的原始索引

例如,如果数组已经排序(我在这里使用列表,不确定是否需要数组): (已编辑:现在使用and iterator不从每个步骤的开始循环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()

否则,如果它们没有被排序,并且您创建了如下排序的索引列表:

^{pr2}$

代码应该是:
已编辑:现在使用and iterator不从每个步骤的开始循环pwfs2Dates):

^{3}$ 太好了!在

。。在

  1. 请注意,dimVals:

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

    不在代码中使用,可以消除。

  2. 请注意,通过循环遍历 数组本身而不是使用xrange

编辑:来自unutbu的答案解决了上面代码中的一些薄弱部分。 我在这里指出它们的完整性:

  1. 使用next:next(iterator)优于iterator.next()iterator.next()是传统命名规则的一个例外 已在py3k中修复,将此方法重命名为 iterator.__next__()。在
  2. 使用try/except检查迭代器的结尾。毕竟 迭代器中的项在下一次调用next()时完成 生成StopIteration异常。使用try/except来表示善意 当这种情况发生时,跳出循环。对于 问题是两条射线不一样 使for循环与迭代器同时结束。所以没有 例外情况出现了。但是,可能有dict1和dict2 大小不一样。在这种情况下 异常正在上升。 问题是:使用try/except还是准备数组更好 在循环之前,将它们与较短的相等。在

我想你可以少做一个循环:

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

当然有更好的方法来收集和合并火柴,但是你知道。。。{{你也可以使用新的索引,而不是使用新的索引。在

相关问题 更多 >