与许多其他人一样,我的情况是我有一个收集大量数据的类,并提供了一种将数据作为numpy数组返回的方法。 (即使在返回数组后,其他数据也可以继续流入)。由于创建数组是一项昂贵的操作,我只想在必要时创建它,并尽可能高效地进行创建(特别是,在可能的情况下就地附加数据)。
为此,我一直在阅读有关ndarray.resize()方法和refcheck参数的一些内容。我知道只有在“您确定没有与另一个Python对象共享此数组的内存”时才应将refcheck设置为False。
事情是我不确定。有时我有,有时我没有。如果refcehck失败(我可以捕获它然后创建一个新的副本),我很好,它会引发错误,但我希望它只有在存在“真正的”外部引用时才会失败,忽略我认为安全的那些。
这是一个简化的插图:
import numpy as np
def array_append(arr, values, refcheck = True):
added_len = len(values)
if added_len == 0:
return arr
old_len = len(arr)
new_len = old_len + added_len
arr.resize(new_len, refcheck = refcheck)
arr[old_len:] = values
return arr
class DataCollector(object):
def __init__(self):
self._new_data = []
self._arr = np.array([])
def add_data(self, data):
self._new_data.append(data)
def get_data_as_array(self):
self._flush()
return self._arr
def _flush(self):
if not self._new_data:
return
# self._arr = self._append1()
# self._arr = self._append2()
self._arr = self._append3()
self._new_data = []
def _append1(self):
# always raises an error, because there are at least 2 refs:
# self._arr and local variable 'arr' in array_append()
return array_append(self._arr, self._new_data, refcheck = True)
def _append2(self):
# Does not raise an error, but unsafe in case there are other
# references to self._arr
return array_append(self._arr, self._new_data, refcheck = False)
def _append3(self):
# "inline" version: works if there are no other references
# to self._arr, but raises an error if there are.
added_len = len(self._new_data)
old_len = len(self._arr)
self._arr.resize(old_len + added_len, refcheck = True)
self._arr[old_len:] = self._new_data
return self._arr
dc = DataCollector()
dc.add_data(0)
dc.add_data(1)
print dc.get_data_as_array()
dc.add_data(2)
print dc.get_data_as_array()
x = dc.get_data_as_array() # create an external reference
print x.shape
for i in xrange(5000):
dc.add_data(999)
print dc.get_data_as_array()
print x.shape
问题:
答案 0 :(得分:1)
我将使用array.array()
进行数据收集:
import array
a = array.array("d")
for i in xrange(100):
a.append(i*2)
每当您想要对收集的数据进行一些计算时,请按numpy.ndarray
将其转换为numpy.frombuffer
:
b = np.frombuffer(a, dtype=float)
print np.mean(b)
b
将与a
共享数据内存,因此转换速度非常快。
答案 1 :(得分:1)
resize
方法有两个主要问题。第一个是当用户调用get_data_as_array
时返回对self._arr的引用。现在调整大小将根据您的实现执行两项操作之一。它会修改你给用户的数组,即用户将采用a.shape
并且形状将无法预测地改变。或者它会破坏那个阵列,让它指向糟糕的记忆。您可以通过始终get_data_as_array
返回self._arr.copy()
来解决该问题,但这会让我想到第二个问题。 resize
实际上效率不高。我相信一般来说,resize必须分配新的内存并在每次调用它时生成一个数组。此外,您现在需要在每次要将其返回给用户时复制数组。
另一种方法是设计自己的dynamic array,看起来像:
class DynamicArray(object):
_data = np.empty(1)
data = _data[:0]
len = 0
scale_factor = 2
def append(self, values):
old_data = len(self.data)
total_data = len(values) + old_data
total_storage = len(self._data)
if total_storage < total_data:
while total_storage < total_data:
total_storage = np.ceil(total_storage * self.scale_factor)
self._data = np.empty(total_storage)
self._data[:old_data] = self.data
self._data[old_data:total_data] = values
self.data = self._data[:total_data]
这应该非常快,因为您只需要增加数组日志(N)次,并且最多使用2 * N-1个存储,其中N是数组的最大大小。除了增长数组之外,你只是制作_data
的视图,它不涉及任何复制,应该是恒定的时间。
希望这很有用。