针对唯一值的高效数据筛选 (Python)
Efficient data sifting for unique values (Python)
我有一个由 (X,Y,Z,A) 值组成的 2D Numpy 数组,其中 (X,Y,Z) 是 3D space 中的笛卡尔坐标,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
矩阵采用这种信息,但只有 2d
sparse.coo_matrix((data, (row, col)))
其中 row
和 col
是类似于您的 X
、Y
和 Z
的索引。它对重复项求和。
这样做的第一步是对索引进行 lexical
排序。这会将具有匹配坐标的点彼此相邻。
我相信实际的分组和求和是在编译代码中完成的。用 numpy
术语快速完成的部分困难在于每个组中的元素数量是可变的。有些是独一无二的,有些可能有 3 个或更多。
Python itertools
有一个 groupby
工具。 Pandas也有分组功能。我还可以想象使用 default_dict
对值进行分组和求和。
ufunc
reduceat
也可能有效,尽管它在 1d 中比在 2 或 3 中更容易使用。
如果您忽略 Z
,稀疏 coo_matrix
方法可能是最简单的方法。
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>
这是我对 lexsort 的想法
In [32]: Z=np.array([21,7,9,21,21,9])
In [33]: xyz=np.stack([X,Y,Z],1)
In [34]: idx=np.lexsort([X,Y,Z])
In [35]: idx
Out[35]: array([1, 2, 5, 0, 3, 4], dtype=int32)
In [36]: xyz[idx,:]
Out[36]:
array([[ 9, 2, 7],
[15, 3, 9],
[15, 3, 9],
[13, 7, 21],
[13, 7, 21],
[13, 7, 21]])
In [37]: A[idx]
Out[37]: array([ 0.5, 1.1, 1.1, 1.5, 0.9, 1.7])
当像这样排序时,Z
坐标变得更加明显 'redundant',至少为了这个目的。
使用 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), with
nas no. of unique scalars based on the uniqueness among others and finally use
[= 开始的最小标量37=]` 根据之前获得的唯一标量对最后一列进行求和。
这是实现 -
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])]
方法 #2
与方法 #1 相同,但不是使用 view
,我们将为每一行生成等价的线性索引,从而将每一行缩减为一个标量。其余工作流程与第一种方法相同。
实施-
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]])
我有一个由 (X,Y,Z,A) 值组成的 2D Numpy 数组,其中 (X,Y,Z) 是 3D space 中的笛卡尔坐标,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
矩阵采用这种信息,但只有 2d
sparse.coo_matrix((data, (row, col)))
其中 row
和 col
是类似于您的 X
、Y
和 Z
的索引。它对重复项求和。
这样做的第一步是对索引进行 lexical
排序。这会将具有匹配坐标的点彼此相邻。
我相信实际的分组和求和是在编译代码中完成的。用 numpy
术语快速完成的部分困难在于每个组中的元素数量是可变的。有些是独一无二的,有些可能有 3 个或更多。
Python itertools
有一个 groupby
工具。 Pandas也有分组功能。我还可以想象使用 default_dict
对值进行分组和求和。
ufunc
reduceat
也可能有效,尽管它在 1d 中比在 2 或 3 中更容易使用。
如果您忽略 Z
,稀疏 coo_matrix
方法可能是最简单的方法。
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>
这是我对 lexsort 的想法
In [32]: Z=np.array([21,7,9,21,21,9])
In [33]: xyz=np.stack([X,Y,Z],1)
In [34]: idx=np.lexsort([X,Y,Z])
In [35]: idx
Out[35]: array([1, 2, 5, 0, 3, 4], dtype=int32)
In [36]: xyz[idx,:]
Out[36]:
array([[ 9, 2, 7],
[15, 3, 9],
[15, 3, 9],
[13, 7, 21],
[13, 7, 21],
[13, 7, 21]])
In [37]: A[idx]
Out[37]: array([ 0.5, 1.1, 1.1, 1.5, 0.9, 1.7])
当像这样排序时,Z
坐标变得更加明显 'redundant',至少为了这个目的。
使用 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), with
nas no. of unique scalars based on the uniqueness among others and finally use
[= 开始的最小标量37=]` 根据之前获得的唯一标量对最后一列进行求和。
这是实现 -
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])]
方法 #2
与方法 #1 相同,但不是使用 view
,我们将为每一行生成等价的线性索引,从而将每一行缩减为一个标量。其余工作流程与第一种方法相同。
实施-
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]])