Arrays 针对唯一值的高效数据筛选(Python)

Arrays 针对唯一值的高效数据筛选(Python),arrays,python-2.7,numpy,unique,Arrays,Python 2.7,Numpy,Unique,我有一个由(X,Y,Z,a)值组成的二维Numpy数组,其中(X,Y,Z)是三维空间中的笛卡尔坐标,a是该位置的某个值。例如 __X__|__Y__|__Z__|__A_ 13 | 7 | 21 | 1.5 9 | 2 | 7 | 0.5 15 | 3 | 9 | 1.1 13 | 7 | 21 | 0.9 13 | 7 | 21 | 1.7 15 | 3 | 9 | 1.1 有没有一种有效的方法可以找到(X,Y)的所有唯一组

我有一个由(X,Y,Z,a)值组成的二维Numpy数组,其中(X,Y,Z)是三维空间中的笛卡尔坐标,a是该位置的某个值。例如

__X__|__Y__|__Z__|__A_
  13 |  7  |  21 | 1.5
  9  |  2  |  7  | 0.5
  15 |  3  |  9  | 1.1
  13 |  7  |  21 | 0.9
  13 |  7  |  21 | 1.7
  15 |  3  |  9  | 1.1

有没有一种有效的方法可以找到(X,Y)的所有唯一组合并添加它们的值?例如,(13,7)的总数将是(1.5+0.9+1.7)或4.1。

scipy。稀疏矩阵采用此类信息,但仅用于二维

sparse.coo_matrix((data, (row, col)))
其中
是类似于
X
Y
Z
的索引。它是重复的

实现这一点的第一步是对索引进行
词法排序。这使得具有匹配坐标的点彼此相邻

实际上,分组和求和是在编译代码中完成的。在
numpy
术语中,这样做的部分困难在于每组中的元素数量可变。有些是唯一的,其他可能有3个或更多

Python
itertools
有一个
groupby
工具。熊猫还有分组功能。我还可以想象使用
default\u dict
对值进行分组和求和

ufunc
reduceat
也可以工作,尽管它在1d中比在2或3中更容易使用

如果忽略
Z
,则稀疏
coo_矩阵
方法可能最简单

In [2]: X=np.array([13,9,15,13,13,15])
In [3]: Y=np.array([7,2,3,7,7,3])
In [4]: A=np.array([1.5,0.5,1.1,0.9,1.7,1.1])
In [5]: M=sparse.coo_matrix((A,(X,Y)))
In [15]: M.sum_duplicates()
In [16]: M.data
Out[16]: array([ 0.5,  2.2,  4.1])
In [17]: M.row
Out[17]: array([ 9, 15, 13])
In [18]: M.col
Out[18]: array([2, 3, 7])
In [19]: M
Out[19]: 
<16x8 sparse matrix of type '<class 'numpy.float64'>'
    with 3 stored elements in COOrdinate format>
当按这种方式排序时,
Z
坐标是“冗余的”,至少在这个目的上是这样的

使用
reduceat
对组进行求和:

In [40]: np.add.reduceat(A[idx],[0,1,3])  
Out[40]: array([ 0.5,  2.2,  4.1])
(目前我只是浏览了[0,1,3]列表)

方法#1

获取每一行作为视图,从而将每一行转换为标量,然后使用
np.unique
将每一行标记为从
(0……n)开始的最小标量,使用
n
作为基于唯一性的唯一标量的数量,最后使用
np.bincount`根据先前获得的唯一标量对最后一列进行求和

下面是实现-

def get_row_view(a):
    void_dt = np.dtype((np.void, a.dtype.itemsize * np.prod(a.shape[1:])))
    a = np.ascontiguousarray(a)
    return a.reshape(a.shape[0], -1).view(void_dt).ravel()

def groupby_cols_view(x):
    a = x[:,:2].astype(int)   
    a1D = get_row_view(a)     
    _, indx, IDs = np.unique(a1D, return_index=1, return_inverse=1)
    return np.c_[x[indx,:2],np.bincount(IDs, x[:,-1])]
def groupby_cols_linearindex(x):
    a = x[:,:2].astype(int)   
    a1D = a[:,0] + a[:,1]*(a[:,0].max() - a[:,1].min() + 1)    
    _, indx, IDs = np.unique(a1D, return_index=1, return_inverse=1)
    return np.c_[x[indx,:2],np.bincount(IDs, x[:,-1])]
方法#2

与方法#1相同,但不是使用
视图
,而是为每行生成等效的线性索引,从而将每行减少为标量。工作流的其余部分与第一种方法相同

实施-

def get_row_view(a):
    void_dt = np.dtype((np.void, a.dtype.itemsize * np.prod(a.shape[1:])))
    a = np.ascontiguousarray(a)
    return a.reshape(a.shape[0], -1).view(void_dt).ravel()

def groupby_cols_view(x):
    a = x[:,:2].astype(int)   
    a1D = get_row_view(a)     
    _, indx, IDs = np.unique(a1D, return_index=1, return_inverse=1)
    return np.c_[x[indx,:2],np.bincount(IDs, x[:,-1])]
def groupby_cols_linearindex(x):
    a = x[:,:2].astype(int)   
    a1D = a[:,0] + a[:,1]*(a[:,0].max() - a[:,1].min() + 1)    
    _, indx, IDs = np.unique(a1D, return_index=1, return_inverse=1)
    return np.c_[x[indx,:2],np.bincount(IDs, x[:,-1])]
样本运行

In [80]: data
Out[80]: 
array([[ 2.        ,  5.        ,  1.        ,  0.40756048],
       [ 3.        ,  4.        ,  6.        ,  0.78945661],
       [ 1.        ,  3.        ,  0.        ,  0.03943097],
       [ 2.        ,  5.        ,  7.        ,  0.43663582],
       [ 4.        ,  5.        ,  0.        ,  0.14919507],
       [ 1.        ,  3.        ,  3.        ,  0.03680583],
       [ 1.        ,  4.        ,  8.        ,  0.36504428],
       [ 3.        ,  4.        ,  2.        ,  0.8598825 ]])

In [81]: groupby_cols_view(data)
Out[81]: 
array([[ 1.        ,  3.        ,  0.0762368 ],
       [ 1.        ,  4.        ,  0.36504428],
       [ 2.        ,  5.        ,  0.8441963 ],
       [ 3.        ,  4.        ,  1.64933911],
       [ 4.        ,  5.        ,  0.14919507]])

In [82]: groupby_cols_linearindex(data)
Out[82]: 
array([[ 1.        ,  3.        ,  0.0762368 ],
       [ 1.        ,  4.        ,  0.36504428],
       [ 3.        ,  4.        ,  1.64933911],
       [ 2.        ,  5.        ,  0.8441963 ],
       [ 4.        ,  5.        ,  0.14919507]])

这些很好,谢谢!我认为
lexsort
是我计划“艰难地”做的事情——我不知道它已经有了一个函数,因此我提出了问题。我喜欢将数据保留在
lexsort
中的方式,因此我将继续。我实际上使用了您的答案——添加“示例运行”非常有用,可以再次检查函数。