Python 如何有效地求和数组X中每个不同值c的所有元素Y[i],其中X[i]=k?

Python 如何有效地求和数组X中每个不同值c的所有元素Y[i],其中X[i]=k?,python,numpy,Python,Numpy,给定一个1D数组X的长度n在Numpy中具有k不同的值,我想以最有效的方式对另一个1D数组Y中的每个不同值c进行汇总,所有这些元素Y[idx]其中X[idx]==c 例如: X=[1,3,2,1,2]和Y=[0.1,0.2,0.5,2.0,0.3]。长度n为5,我们在X中有k=3不同的值。这意味着我们的运算结果是X中k=3不同元素[1,3,2]的向量,以及Y元素的相应和,即[2.1,0.2,0.8]。如果不同的元素是有序的,也可以。因此,[1,2,3]和[2.1,0.8,0.2]也是一个解决方案

给定一个1D数组
X
的长度
n
在Numpy中具有
k
不同的值,我想以最有效的方式对另一个1D数组
Y
中的每个不同值
c
进行汇总,所有这些元素
Y[idx]
其中
X[idx]==c

例如:

X=[1,3,2,1,2]
Y=[0.1,0.2,0.5,2.0,0.3]
。长度
n
为5,我们在
X
中有
k=3
不同的值。这意味着我们的运算结果是
X
k=3
不同元素
[1,3,2]
的向量,以及
Y
元素的相应和,即
[2.1,0.2,0.8]
。如果不同的元素是有序的,也可以。因此,
[1,2,3]
[2.1,0.8,0.2]
也是一个解决方案

我已经在Numpy中查找了各种函数,最接近我想要的是
np.unique(X,return\u counts=True)
但它返回的是计数,而不是Y中的和

当然,我们可以用一个讨厌的循环来解决整个问题,比如:

将numpy导入为np
X=np.数组([1,3,2,1,2])
Y=np.数组([0.1,0.2,0.5,2.0,0.3])
定义唯一_和(x,y):
独特的x=np.唯一的(x)
y_和=np.空(不同的x.形状)
对于idx,枚举中的val(不同的x):
y_和[idx]=np.sum(y[x==val])
返回不同的x,y和
唯一和(X,Y)
导致有序结果:

(array([1, 2, 3]), array([2.1, 0.8, 0.2]))

在Numpy或任何其他公共Python库中是否有类似的矢量化操作?如果没有,Cython中最有效的实现是什么?

我想您应该使用哈希表。Python的dict对于小型数据集来说足够有效。你肯定要用你自己的算法来解决这个问题

def唯一和(x,y):
xd={}
对于i,枚举中的数字(y):
xd[x[i]]=xd.get(x[i],0)+数字
返回xd.keys(),xd.values()
我认为你的解是O(n^2),因为
np.sum(y[x==val])
但我上面的解是O(n)。

给你:

In [21]: u, inv = np.unique(X, return_inverse=True)                                                                                                            

In [22]: sums = np.zeros(len(u), dtype=Y.dtype)                                                                                                                               

In [23]: np.add.at(sums, inv, Y)                                                                                                                               

In [24]: sums                                                                                                                                                  
Out[24]: array([2.1, 0.8, 0.2])
这将用漂亮的方法
numpy.add.at
替换您的
for
-循环


请注意,
np.unique
X
进行排序,因此此方法是O(n*log(n))。这不是这个问题的最佳时间复杂度。

我们可以在这里使用
scipy.sparse.csr\u矩阵来获得更有效的解决方案


设置


这是来自
0->k
的总和列表,其中
k
X
数组的最大值。
X
中不存在键的任何条目显然都将是
0
。要获得更好的映射,可以使用
np.unique
和一些索引:

u = np.unique(X)
np.column_stack((u, res[u]))


计时


我们将尝试利用
pandas.factorize
有效地获得唯一的基于int的ID,然后使用
numpy.bincount
获得基于ID的求和。所以,解决方案看起来是这样的-

import pandas as pd

def unique_sums_factorize_bincount(X, Y):
    ids,unq = pd.factorize(X)
    return unq, np.bincount(ids,Y)
样本运行-

In [24]: X = np.array([ 1,   3,   2,   1,   2]).astype(float)
    ...: Y = np.array([0.1, 0.2, 0.5, 2.0, 0.3])

In [25]: unique_sums_factorize_bincount(X,Y)
Out[25]: (array([1., 3., 2.]), array([2.1, 0.2, 0.8]))

谢谢你的建议,我应该提到我的用例。这将是通过样本权重加权的分类朴素贝叶斯的一部分。因此,我处理相当大的阵列,如~10k到~100k及以上。这还不错,我认为dict应该适合您。至少你不会是O(len(Y)),如果dict对你来说不够快,你应该能够用C语言的自定义哈希表实现这个算法。虽然理论上,dict是O(1),但我认为由于哈希,非连续内存,会有很多开销,等等,我猜Cython中的一个循环应该只通过X和Y一次就可以完成这件事。我只是想知道Cython是否是唯一的解决方案,或者是否存在
np的智能应用程序。其中
np。选择
或一些我不知道的函数。您要求的是“最有效的实现”。您没有指定运行时或内存,因此我假设为运行时。最有效的是O(n),这是一个解决方案。如果你不想使用哈希,你应该编辑你的问题,规定“没有哈希的最有效的实现”。@fwilhelm,考虑到你将有10k到100k个元素,花一些时间测试这种大小的数组的性能可能是值得的。对于小数组,使用
np.unique
可能会赢,但随着数组大小的增加,最终时间复杂度较低的方法会赢。测试会让你知道你是否达到了那个程度。我真的很感动!正是我想要的。我不知道Numpy中有这些
ufunc.at
函数。谢谢,聪明的主意。我是否也可以在您的解决方案中得到相应的不同值列表?不,您没有。如果您需要这些,您必须使用
np.unique
。但是,根据我的时间安排,使用
np.unique
不会增加足够的开销,使任何其他解决方案都比这个解决方案更快。例如,使用
n=100_000
,它会使总时间增加约3ms,因此仍然比其他解决方案快3倍。好的,明白了。你的解决方案唯一的缺点就是我只能处理X中的整数,对吗?如果我将其应用于大多数矩阵X为浮点的Scikit学习上下文中,这可能是一个问题。这是正确的。尽管这会把这里所有的解决方案都搞糟。我在回答btwThanks,nice中添加了一个将唯一值链接回结果的示例。我刚刚尝试了@WarrenWeckesser的解决方案,在X中使用浮点值,它仍然有效。为什么会出现故障?你能在某个地方发布发布,在你的10万到10万大小的暗示数据集上发布的方法是如何公平的吗?我猜性能(即运行时)是您的首要任务,所以最好看看这些性能是如何叠加的。
u = np.unique(X)
np.column_stack((u, res[u]))
array([[1. , 2.1],
       [2. , 0.8],
       [3. , 0.2]])
X = np.random.randint(0, 100, 100_000)
Y = np.random.rand(100_000)

In [11]: %%timeit
    ...: sparse.csr_matrix(
    ...:     (Y, X, np.arange(Y.shape[0]+1)),
    ...:     (Y.shape[0], X.max()+1)
    ...: ).sum(0).A1
    ...:
1.15 ms ± 17.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [13]: %%timeit
    ...: u, inv = np.unique(X, return_inverse=True)
    ...: sums = np.zeros(len(u), dtype=Y.dtype)
    ...: np.add.at(sums, inv, Y)
    ...:
16.5 ms ± 161 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [16]: %timeit unique_sums(X, Y)
16.6 ms ± 169 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
import pandas as pd

def unique_sums_factorize_bincount(X, Y):
    ids,unq = pd.factorize(X)
    return unq, np.bincount(ids,Y)
In [24]: X = np.array([ 1,   3,   2,   1,   2]).astype(float)
    ...: Y = np.array([0.1, 0.2, 0.5, 2.0, 0.3])

In [25]: unique_sums_factorize_bincount(X,Y)
Out[25]: (array([1., 3., 2.]), array([2.1, 0.2, 0.8]))