Python numpy中的有效bin分配

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 我想把它们放进垃圾箱。这应该通

我有一个非常大的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

我想把它们放进垃圾箱。这应该通过创建两个对象来完成。一个是矩阵缓冲区,
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毫秒来计算上述问题。除非可以加快速度,否则这不是一个选择。