Python numpy中的有效bin分配
我有一个非常大的1D python数组Python numpy中的有效bin分配,python,numpy,indexing,binning,set-operations,Python,Numpy,Indexing,Binning,Set Operations,我有一个非常大的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 我想把它们放进垃圾箱。这应该通
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
我想把它们放进垃圾箱。这应该通过创建两个对象来完成。一个是矩阵缓冲区,b
是从d中提取的数据项。另一个对象是每个缓冲列引用的唯一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的所有列,并通过标识x==v
的所有位置来提取索引idx
但是,我不喜欢这个解决方案,因为for循环相当慢(比unique命令长50倍)。我宁愿把手术矢量化
因此,一种矢量化方法是创建一个索引矩阵,其中
x==v
,然后沿列对其运行nonzero()
命令。但是,该矩阵需要150k x 15k范围内的内存,因此在32位系统上大约需要8GB
对我来说,np.unique
-操作甚至可以有效地返回反向索引,以便x=v[inv_index]
但无法获得v中每个bin的v-To-x分配列表,这听起来相当愚蠢。当函数扫描x时,这几乎是免费的。在实现方面,唯一的挑战是结果索引矩阵的未知大小
假设np.unique-command是用于装箱的方法,则该问题的另一种表述方式是: 考虑到三个数组
x,v,inv\u索引
,其中v
是x
和x=v[inv\u索引]
中的唯一元素,是否有一种有效的方法来生成索引向量v\u到x[i]
,从而所有箱子的all(v[i]==x[v\u到x[i]]
我不应该花费比np.unique-command本身更多的时间。我很乐意为每个箱子中的物品数量提供一个上限(例如50)。根据@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毫秒的时间内运行,这导致了一些相当大的加速,与上面发布的原始代码相比
它首先枚举x
中的值,并添加相应的索引信息。然后,枚举按实际的x
值分组,该值实际上是enumerate()
生成的元组的第二个值
for循环遍历所有组,将元组g
的迭代器转换为大小(大小x 2)
的组矩阵,然后丢弃第二列,即仅保留索引的x
值。这导致组
只是一个1D数组
groupby()
仅适用于排序数组
干得好。我只是想知道我们是否能做得更好?仍然有很多不合理的数据复制似乎在发生。创建一个元组列表,然后将其转换为2D矩阵,仅仅扔掉其中的一半,仍然感觉有点不太理想。我通过重新表述问题得到了我想要的答案,请参见此处:
通过对np.unique()
返回的inv\u索引进行“累积计数”,我们接收稀疏矩阵的数组索引,以便
c = cumcount(inv_indices)
b[inv_indices, c] = d
上面链接的线程中建议的累积计数非常有效。低于20ms的运行时间非常现实。itertools.groupby是否有帮助?我不确定你在寻找什么…如果你在Pandas中把numpy数组定义为df=pd.DataFrame({“x”:x,“d”:d})
,那么你就可以用unik=df.groupby([“x”])[“d”].unique().reset_index()
来整理每个唯一值的数据。这不是您想要的数组,而是一列包含每个唯一x值的所有d值的numpy数组。Idk,您接下来想对数据做什么,但可能不需要一个包含所有NaN值的完整b数组。@user202729我研究了groupby业务,问题似乎是groupby只对值本身进行分组,而不是列表中的索引,因此如果您为键调用,则groupby(x)中的值
您可以将组转换为列表l=list(value)
。这将为l中的所有li留下True==(li==key)。我需要索引,即'True==(x[li]==key)来表示l中的所有li`enumerate
?@piintesky,即使您的建议适用于分组命令unik=df.groupby([“x”])[“d”])。unique().reset_index()
需要约900毫秒来计算上述问题。除非可以加快速度,否则这不是一个选择。