我遇到的实际问题是我想在RAM中存储一个长(float, str)
个元组的排序列表。普通列表不适合我的4Gb RAM,所以我想我可以使用两个numpy.ndarray
。
数据源是可重复的2元组。 numpy
具有fromiter
功能,但我该如何使用它?迭代中的项目数是未知的。由于内存限制,我不能首先将它消耗到列表中。我想到了itertools.tee
,但它似乎在这里增加了很多内存开销。
我想我能做的就是以块的形式使用迭代器并将它们添加到数组中。然后我的问题是,如何有效地做到这一点?我应该制作2个2D阵列并为它们添加行吗? (然后我需要将它们转换为1D)。
或许还有更好的方法?我真正需要的是通过对数时间内相应数字的值来搜索字符串数组(这就是为什么我要按浮点值排序)并保持尽可能紧凑。
P.S。迭代没有排序。
答案 0 :(得分:8)
也许使用np.fromiter
构建单个结构化数组:
import numpy as np
def gendata():
# You, of course, have a different gendata...
for i in xrange(N):
yield (np.random.random(), str(i))
N = 100
arr = np.fromiter(gendata(), dtype='<f8,|S20')
按第一列排序,使用第二列打破连接器需要O(N log N)时间:
arr.sort(order=['f0','f1'])
在O(log N)时间内,可以使用searchsorted
按第一列中的值查找行:
# Some pseudo-random value in arr['f0']
val = arr['f0'][10]
print(arr[10])
# (0.049875262239617246, '46')
idx = arr['f0'].searchsorted(val)
print(arr[idx])
# (0.049875262239617246, '46')
你在评论中提出了许多重要问题;让我试着在这里回答:
基本dtypes在numpybook中有解释。可能有一个或
两个额外的dtypes(如float16
之后已添加
这本书是写的,但基础知识都在那里解释。)
也许在online documentation进行更彻底的讨论。对于您提到的示例here,这是一个很好的补充。
Dtypes可用于定义具有列名称的结构化数组,或
使用默认列名称。 'f0'
,'f1'
等是默认列
名。由于我将dtype定义为'<f8,|S20'
,因此无法提供
列名,因此NumPy命名第一列'f0'
,第二列
'f1'
。如果我们使用过
dtype='[('fval','<f8'), ('text','|S20')]
然后结构化数组arr
将具有列名'fval'
和
'text'
。
np.fromiter
时修复dtype。您
可以想象,可以迭代gendata
一次来发现
最大字符串长度,构建你的dtype然后调用
np.fromiter
(并且第二次迭代gendata
),但是
这是相当繁重的。如果你知道,那当然会更好
提高字符串的最大大小。 (|S20
定义字符串
字段的固定长度为20个字节。)dtype
设置)来快速计算,从而获得大部分速度访问数组中元素所需的偏移量。如果字符串具有可变大小,那么它
NumPy很难找到合适的补偿。我的意思是,很难
NumPy需要索引或以某种方式重新设计。 NumPy根本就不是
以这种方式建造。object
dtype,允许你放置一个4字节
指向您想要的任何Python对象的指针。这样,你可以拥有NumPy
具有任意Python数据的数组。不幸的是,np.fromiter
函数不允许您创建dtype object
的数组。我不确定为什么会有这种限制...... np.fromiter
具有更好的效果count
指定。通过了解count
(行数)和。{
dtype
(以及每行的大小)NumPy可以预先分配
为结果数组准确的内存。如果你没有指定
count
,然后NumPy将猜测初始大小
数组,如果太小,它会尝试调整数组的大小。如果
原始的记忆块可以延长你的运气。但如果
NumPy必须分配一个全新的内存块,然后才能分配旧内存
必须将数据复制到新位置,这将减慢速度
性能显着。答案 1 :(得分:1)
这是一种从N
的生成器构建N
个独立数组的方法 - 元组:
import numpy as np
import itertools as IT
def gendata():
# You, of course, have a different gendata...
N = 100
for i in xrange(N):
yield (np.random.random(), str(i))
def fromiter(iterable, dtype, chunksize=7):
chunk = np.fromiter(IT.islice(iterable, chunksize), dtype=dtype)
result = [chunk[name].copy() for name in chunk.dtype.names]
size = len(chunk)
while True:
chunk = np.fromiter(IT.islice(iterable, chunksize), dtype=dtype)
N = len(chunk)
if N == 0:
break
newsize = size + N
for arr, name in zip(result, chunk.dtype.names):
col = chunk[name]
arr.resize(newsize, refcheck=0)
arr[size:] = col
size = newsize
return result
x, y = fromiter(gendata(), '<f8,|S20')
order = np.argsort(x)
x = x[order]
y = y[order]
# Some pseudo-random value in x
N = 10
val = x[N]
print(x[N], y[N])
# (0.049875262239617246, '46')
idx = x.searchsorted(val)
print(x[idx], y[idx])
# (0.049875262239617246, '46')
上面的fromiter
函数读取块中的可迭代(大小为chunksize
)。它调用NumPy数组方法resize
以根据需要扩展结果数组。
我使用了一个小的默认chunksize
,因为我在小数据上测试了这段代码。当然,您需要更改默认的chunksize或传递具有更大值的chunksize
参数。