填充索引的列/行数组中的出现矩阵



我正在寻找一种有效的方法来从包含索引的两个数组中创建出现矩阵,一个表示此矩阵中的行索引,另一个表示列索引。

例如。我有:

#matrix will be size 4x3 in this example
#array of rows idxs, with values from 0 to 3
[0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3]
#array of columns idxs, with values from 0 to 2
[0, 1, 1, 1, 2, 2, 0, 1, 2, 0, 2, 2, 2, 2]

并且需要创建一个实例矩阵,例如:

[[1  0  0]
[0  2  0]
[0  1  2]
[2  1  5]]

我可以以简单的形式创建一个热向量的数组,但是当出现多次时无法使其工作:

n_rows    = 4
n_columns = 3
#data
rows    = np.array([0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3])
columns = np.array([0, 1, 1, 1, 2, 2, 0, 1, 2, 0, 2, 2, 2, 2])
#empty matrix
new_matrix = np.zeros([n_rows, n_columns])
#adding 1 for each [row, column] occurrence:
new_matrix[rows, columns] += 1
print(new_matrix)

其中返回:

[[ 1.  0.  0.]
[ 0.  1.  0.]
[ 0.  1.  1.]
[ 1.  1.  1.]]

当有多个出现/索引时,似乎索引和添加这样的值不起作用,除了打印它似乎工作得很好:

print(new_matrix[rows, :])

[[ 1.  0.  0.]
[ 0.  1.  0.]
[ 0.  1.  0.]
[ 0.  1.  1.]
[ 0.  1.  1.]
[ 0.  1.  1.]
[ 1.  1.  1.]
[ 1.  1.  1.]
[ 1.  1.  1.]
[ 1.  1.  1.]
[ 1.  1.  1.]
[ 1.  1.  1.]
[ 1.  1.  1.]
[ 1.  1.  1.]]

所以也许我在那里错过了什么?或者这做不到,我需要寻找另一种方法来做到这一点?

使用np.add.at,指定索引元组:

>>> np.add.at(new_matrix, (rows, columns), 1)
>>> new_matrix
array([[ 1.,  0.,  0.],
[ 0.,  2.,  0.],
[ 0.,  1.,  2.],
[ 2.,  1.,  5.]])

np.add.at就地对数组进行操作,向(row, columns)元组指定的索引添加1次数。

方法 #1

我们可以将这些货币对转换为线性指数,然后使用np.bincount-

def bincount_app(rows, columns, n_rows, n_columns):
# Get linear index equivalent
lidx = (columns.max()+1)*rows + columns
# Use binned count on the linear indices
return np.bincount(lidx, minlength=n_rows*n_columns).reshape(n_rows,n_columns)

示例运行 -

In [242]: n_rows    = 4
...: n_columns = 3
...: 
...: rows    = np.array([0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3])
...: columns = np.array([0, 1, 1, 1, 2, 2, 0, 1, 2, 0, 2, 2, 2, 2])
In [243]: bincount_app(rows, columns, n_rows, n_columns)
Out[243]: 
array([[1, 0, 0],
[0, 2, 0],
[0, 1, 2],
[2, 1, 5]])

方法#2

或者,我们可以对线性指数进行排序并使用slicing获取计数,以获得第二种方法,如下所示 -

def mask_diff_app(rows, columns, n_rows, n_columns):
lidx = (columns.max()+1)*rows + columns
lidx.sort()
mask = np.concatenate(([True],lidx[1:] != lidx[:-1],[True]))
count = np.diff(np.flatnonzero(mask))
new_matrix = np.zeros([n_rows, n_columns],dtype=int)
new_matrix.flat[lidx[mask[:-1]]] = count
return new_matrix

方法#3

这似乎也是一个具有稀疏矩阵csr_matrix的直截了当的方法,因为它确实会自行累积重复索引。好处是内存效率,因为它是一个稀疏矩阵,如果你在输出中填充少量位置并且稀疏矩阵输出是可以的,这将很明显。

实现看起来像这样 -

from scipy.sparse import csr_matrix
def sparse_matrix_app(rows, columns, n_rows, n_columns):
out_shp = (n_rows, n_columns)
data = np.ones(len(rows),dtype=int)
return csr_matrix((data, (rows, columns)), shape=out_shp)

如果你需要一个常规/密集的数组,只需做 -

sparse_matrix_app(rows, columns, n_rows, n_columns).toarray()

示例输出 -

In [319]: sparse_matrix_app(rows, columns, n_rows, n_columns).toarray()
Out[319]: 
array([[1, 0, 0],
[0, 2, 0],
[0, 1, 2],
[2, 1, 5]])
<小时 />

基准测试

其他方法 -

# @cᴏʟᴅsᴘᴇᴇᴅ's soln
def add_at_app(rows, columns, n_rows, n_columns):
new_matrix = np.zeros([n_rows, n_columns],dtype=int)
np.add.at(new_matrix, (rows, columns), 1)

计时

情况 #1:形状(1000, 1000)和索引编号的输出数组 =10k

In [307]: # Setup
...: n_rows = 1000
...: n_columns = 1000
...: rows = np.random.randint(0,1000,(10000))
...: columns = np.random.randint(0,1000,(10000))
In [308]: %timeit add_at_app(rows, columns, n_rows, n_columns)
...: %timeit bincount_app(rows, columns, n_rows, n_columns)
...: %timeit mask_diff_app(rows, columns, n_rows, n_columns)
...: %timeit sparse_matrix_app(rows, columns, n_rows, n_columns)
1000 loops, best of 3: 1.05 ms per loop
1000 loops, best of 3: 424 µs per loop
1000 loops, best of 3: 1.05 ms per loop
1000 loops, best of 3: 1.41 ms per loop

情况 #2:形状(1000, 1000)和索引编号的输出数组 =100k

In [309]: # Setup
...: n_rows = 1000
...: n_columns = 1000
...: rows = np.random.randint(0,1000,(100000))
...: columns = np.random.randint(0,1000,(100000))
In [310]: %timeit add_at_app(rows, columns, n_rows, n_columns)
...: %timeit bincount_app(rows, columns, n_rows, n_columns)
...: %timeit mask_diff_app(rows, columns, n_rows, n_columns)
...: %timeit sparse_matrix_app(rows, columns, n_rows, n_columns)
100 loops, best of 3: 11.4 ms per loop
1000 loops, best of 3: 1.27 ms per loop
100 loops, best of 3: 7.44 ms per loop
10 loops, best of 3: 20.4 ms per loop

情况#3:输出稀疏

如前所述,为了使稀疏方法更好地工作,我们需要稀疏性。这样的情况是这样的——

In [314]: # Setup
...: n_rows = 5000
...: n_columns = 5000
...: rows = np.random.randint(0,5000,(1000))
...: columns = np.random.randint(0,5000,(1000))
In [315]: %timeit add_at_app(rows, columns, n_rows, n_columns)
...: %timeit bincount_app(rows, columns, n_rows, n_columns)
...: %timeit mask_diff_app(rows, columns, n_rows, n_columns)
...: %timeit sparse_matrix_app(rows, columns, n_rows, n_columns)
100 loops, best of 3: 11.7 ms per loop
100 loops, best of 3: 11.1 ms per loop
100 loops, best of 3: 11.1 ms per loop
1000 loops, best of 3: 269 µs per loop

如果你需要一个密集的数组,我们也会失去内存效率,从而失去性能 -

In [317]: %timeit sparse_matrix_app(rows, columns, n_rows, n_columns).toarray()
100 loops, best of 3: 11.7 ms per loop

最新更新