Python 如何有效地求和数组X中每个不同值c的所有元素Y[i],其中X[i]=k?
给定一个1D数组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]也是一个解决方案
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]))