我有一个非常大的1D python数组x
有些重复数字,还有一些相同大小的数据d
。
x = np.array([48531, 62312, 23345, 62312, 1567, ..., 23345, 23345])
d = np.array([0 , 1 , 2 , 3 , 4 , ..., 99998, 99999])
在我的上下文中“非常大”是指10k ... 100k条目。其中一些是重复的,所以唯一条目的数量大约是5k ... 15k。
我想将它们分组到箱子里。这应该通过创建两个对象来完成。一个是矩阵缓冲区,{d}取自b
个数据项。另一个对象是每个缓冲区列引用的唯一x值的向量v
。这是一个例子:
v = [48531, 62312, 23345, 1567, ...]
b = [[0 , 1 , 2 , 4 , ...]
[X , 3 , ....., ...., ...]
[ ...., ....., ....., ...., ...]
[X , X , 99998, X , ...]
[X , X , 99999, X , ...] ]
由于x中每个唯一数字的出现次数不同,缓冲区b中的某些值无效(由大写X
表示,即“不关心”)。
在numpy中导出v非常容易:
v, n = np.unique(x, return_counts=True) # yay, just 5ms
我们甚至得到n
,这是b中每列中有效条目的数量。此外,(np.max(n), v.shape[0])
返回需要分配的矩阵b的形状。
但如何有效地生成b ? for循环可以帮助
b = np.zeros((np.max(n), v.shape[0]))
for i in range(v.shape[0]):
idx = np.flatnonzero(x == v[i])
b[0:n[i], i] = d[idx]
此循环遍历b的所有列,并通过识别idx
的所有位置来提取索引x == v
。
但是我不喜欢这个解决方案,因为循环速度相当慢(比独特命令长约50倍)。我宁愿让操作矢量化。
因此,一种向量化方法是创建索引矩阵x == v
,然后沿着列运行nonzero()
命令。但是,这个矩阵需要150k x 15k的内存,所以在32位系统上大约需要8GB。
对我而言,np.unique
- 操作甚至可以有效地返回反向索引以便x = v[inv_indices]
听起来相当愚蠢但是无法获得每个{-1}}的v-to-x赋值列表bin in v。当函数扫描x时,这几乎是免费的。在实施方面,唯一的挑战是结果索引矩阵的未知大小。
另一种表达此问题的方法,假设np.unique-command是用于分箱的方法:
给定三个数组x, v, inv_indices
其中v
是x
和x = v[inv_indices]
中的唯一元素,有一种生成索引向量v_to_x[i]
的有效方法所有箱子all(v[i] == x[v_to_x[i]])
都是i
?
我不应该花费更多时间而不是np.unique-command本身。我很高兴为每个箱子中的物品数量提供上限(例如50)。
答案 0 :(得分:0)
根据@ user202729的建议,我写了这段代码
x_sorted_args = np.argsort(x)
x_sorted = x[x_sorted_args]
i = 0
v = -np.ones(T)
b = np.zeros((K, T))
for k,g in groupby(enumerate(x_sorted), lambda tup: tup[1]):
groups = np.array(list(g))[:,0]
size = groups.shape[0]
v[i] = k
b[0:size, i] = d[x_sorted_args[groups]]
i += 1
在大约100毫秒的运行中导致一些相当大的加速w.r.t.上面张贴的原始代码。
它首先枚举x
中添加相应索引信息的值。然后枚举按实际x
值分组,实际上是enumerate()
生成的元组的第二个值。
for循环遍历所有组,将元组g
的迭代器转换为大小为groups
的{{1}}矩阵,然后抛弃第二列,即{{1}值只保留索引。这导致(size x 2)
只是一维数组。
x
仅适用于已排序的数组。
干得好。我只是想知道我们能做得更好吗?仍然有很多不合理的数据复制似乎发生了。创建一个元组列表然后将其转换为2D矩阵只是为了丢掉一半它仍然感觉有点不理想。