我需要匹配两个非常大的Numpy数组(一个是20000行,另一个是大约100000行),我正在尝试构建一个脚本来高效地完成它。简单地在数组上循环是非常慢的,有人可以提出更好的方法吗?这是我想要做的:数组datesSecondDict
和数组pwfs2Dates
包含日期时间值,我需要从数组pwfs2Dates
(较小的数组)中获取每个日期时间值,看看是否有数组datesSecondDict
中的日期时间值(加上减去5分钟)(可能超过1)。如果有一个(或多个)我使用数组pwfs2Dates
中的值(其中一个值)填充一个新数组(与数组valsSecondDict
大小相同)(这只是带有数组的数组)相应的数值为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
谢谢, 艾娜。
答案 0 :(得分: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()
太棒了!
..
请注意dimVals:
dimVals = np.array(dict1[dimmKey]['values'])
未在您的代码中使用,可以删除。
编辑 unutbu 的答案解决了上述代码中的一些弱部分。 我在这里指出它们的完整性:
next
:next(iterator)
优先于iterator.next()
。
iterator.next()
是传统命名规则的一个例外
已在py3k中修复此方法重命名为
iterator.__next__()
。try/except
检查迭代器的结尾。毕竟
迭代器中的项目完成了对next()
的下一次调用
产生一个StopIteration异常。善意使用try/except
当发生这种情况时突然退出。对于具体案例
OP问题这不是问题,因为两个arrray是相同的
大小,因此for循环与迭代器同时完成。所以不行
异常上升。但是,可能有dict1和dict2的情况
大小不一样。在这种情况下,有一个可能性
异常上升。
问题是:什么是更好的,使用try / except或准备数组
在循环之前,将它们均衡为较短的一个。答案 1 :(得分:4)
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]
答案 2 :(得分: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))
返回索引而不是日期并使用索引来抓取整行并将它放入你的新阵列。