我有一个索引列表
a = [
[1,2,4],
[0,2,3],
[1,3,4],
[0,2]]
将其转换为numpy数组的最快方法是什么,其中每个索引都显示1的位置?
即我想要的是:
output = array([
[0,1,1,0,1],
[1,0,1,1,0],
[0,1,0,1,1],
[1,0,1,0,0]])
我事先知道数组的最大大小。我知道我可以遍历每个列表,并在每个索引位置插入1,但是有没有一种更快/矢量化的方法来做到这一点?
我的用例可能有成千上万的行/列,而我需要这样做数千次,所以速度越快越好。
答案 0 :(得分:10)
如何?
ncol = 5
nrow = len(a)
out = np.zeros((nrow, ncol), int)
out[np.arange(nrow).repeat([*map(len,a)]), np.concatenate(a)] = 1
out
# array([[0, 1, 1, 0, 1],
# [1, 0, 1, 1, 0],
# [0, 1, 0, 1, 1],
# [1, 0, 1, 0, 0]])
以下是1000x1000二进制数组的计时,请注意,我使用了上面的优化版本,请参见下面的函数pp
:
pp 21.717635259992676 ms
ts 37.10938713003998 ms
u9 37.32933565042913 ms
产生计时的代码:
import itertools as it
import numpy as np
def make_data(n,m):
I,J = np.where(np.random.random((n,m))<np.random.random((n,1)))
return [*map(np.ndarray.tolist, np.split(J, I.searchsorted(np.arange(1,n))))]
def pp():
sz = np.fromiter(map(len,a),int,nrow)
out = np.zeros((nrow,ncol),int)
out[np.arange(nrow).repeat(sz),np.fromiter(it.chain.from_iterable(a),int,sz.sum())] = 1
return out
def ts():
out = np.zeros((nrow,ncol),int)
for i, ix in enumerate(a):
out[i][ix] = 1
return out
def u9():
out = np.zeros((nrow,ncol),int)
for i, (x, y) in enumerate(zip(a, out)):
y[x] = 1
out[i] = y
return out
nrow,ncol = 1000,1000
a = make_data(nrow,ncol)
from timeit import timeit
assert (pp()==ts()).all()
assert (pp()==u9()).all()
print("pp", timeit(pp,number=100)*10, "ms")
print("ts", timeit(ts,number=100)*10, "ms")
print("u9", timeit(u9,number=100)*10, "ms")
答案 1 :(得分:6)
这可能不是最快的方法。您将需要使用大型数组比较这些答案的执行时间,以找出最快的方法。这是我的解决方法
CREATE TABLEinventry.users(
id INT NOT NULL AUTO_INCREMENT,
username VARCHAR(225) NOT NULL,
email VARCHAR(225) NOT NULL,
password VARCHAR(300) NOT NULL,
usertype ENUM(**'0'**) NOT NULL,
register_dateDATETIME NOT NULL,
last_loginDATETIME NOT NULL,
notesVARCHAR(225) NOT NULL,
PRIMARY KEY(id)
) ENGINE = InnoDB;
答案 2 :(得分:4)
可能不是最好的方法,但我能想到的唯一方法是
output = np.zeros((4,5))
for i, (x, y) in enumerate(zip(a, output)):
y[x] = 1
output[i] = y
print(output)
哪个输出:
[[ 0. 1. 1. 0. 1.]
[ 1. 0. 1. 1. 0.]
[ 0. 1. 0. 1. 1.]
[ 1. 0. 1. 0. 0.]]
答案 3 :(得分:3)
如果可以并且想使用Cython,则可以创建一种可读性强(至少在您不介意打字的情况下)的快速解决方案。
在这里,我正在使用Cython的IPython绑定在Jupyter笔记本中对其进行编译:
%load_ext cython
%%cython
cimport cython
cimport numpy as cnp
import numpy as np
@cython.boundscheck(False) # remove this if you cannot guarantee that nrow/ncol are correct
@cython.wraparound(False)
cpdef cnp.int_t[:, :] mseifert(list a, int nrow, int ncol):
cdef cnp.int_t[:, :] out = np.zeros([nrow, ncol], dtype=int)
cdef list subl
cdef int row_idx
cdef int col_idx
for row_idx, subl in enumerate(a):
for col_idx in subl:
out[row_idx, col_idx] = 1
return out
要比较此处介绍的解决方案的性能,我使用了我的库simple_benchmark
:
请注意,这使用对数轴来同时显示小型阵列和大型阵列的差异。根据我的基准,我的功能实际上是解决方案中最快的,但是也值得指出的是,所有解决方案之间的距离都不太远。
这是我用于基准测试的完整代码:
import numpy as np
from simple_benchmark import BenchmarkBuilder, MultiArgument
import itertools
b = BenchmarkBuilder()
@b.add_function()
def pp(a, nrow, ncol):
sz = np.fromiter(map(len, a), int, nrow)
out = np.zeros((nrow, ncol), int)
out[np.arange(nrow).repeat(sz), np.fromiter(itertools.chain.from_iterable(a), int, sz.sum())] = 1
return out
@b.add_function()
def ts(a, nrow, ncol):
out = np.zeros((nrow, ncol), int)
for i, ix in enumerate(a):
out[i][ix] = 1
return out
@b.add_function()
def u9(a, nrow, ncol):
out = np.zeros((nrow, ncol), int)
for i, (x, y) in enumerate(zip(a, out)):
y[x] = 1
out[i] = y
return out
b.add_functions([mseifert])
@b.add_arguments("number of rows/columns")
def argument_provider():
for n in range(2, 13):
ncols = 2**n
a = [
sorted(set(np.random.randint(0, ncols, size=np.random.randint(0, ncols))))
for _ in range(ncols)
]
yield ncols, MultiArgument([a, ncols, ncols])
r = b.run()
r.plot()
答案 4 :(得分:1)
根据您的用例,您可能会考虑使用稀疏矩阵。输入矩阵看起来像一个Compressed Sparse Row (CSR)矩阵。也许像
import numpy as np
from scipy.sparse import csr_matrix
from itertools import accumulate
def ragged2csr(inds):
offset = len(inds[0])
lens = [len(x) for x in inds]
indptr = list(accumulate(lens))
indptr = np.array([x - offset for x in indptr])
indices = np.array([val for sublist in inds for val in sublist])
n = indices.size
data = np.ones(n)
return csr_matrix((data, indices, indptr))
同样,如果适合您的用例,则稀疏矩阵将允许按元素/遮罩操作按非零数量而不是元素数量(行*列)缩放,这可能带来显着的加速(对于稀疏的矩阵)。
CSR矩阵的另一个很好的介绍是Iterative Methods的3.4节。在这种情况下,data
是aa
,indices
是ja
,而indptr
是ia
。这种格式还具有在不同的程序包/库中非常受欢迎的好处。
答案 5 :(得分:0)
如何使用数组索引?如果您对输入了解更多,则可以摆脱必须先转换为线性数组的代价。
import numpy as np
def main():
row_count = 4
col_count = 5
a = [[1,2,4],[0,2,3],[1,3,4],[0,2]]
# iterate through each row, concatenate all indices and convert them to linear
# numpy append performs copy even if you don't want it, list append is faster
b = []
for row_idx, row in enumerate(a):
b.append(np.array(row, dtype=np.int64) + (row_idx * col_count))
linear_idxs = np.hstack(b)
#could skip previous steps if given index inputs well before hand, or in linear index order.
c = np.zeros(row_count * col_count)
c[linear_idxs] = 1
c = c.reshape(row_count, col_count)
print(c)
if __name__ == "__main__":
main()
#output
# [[0. 1. 1. 0. 1.]
# [1. 0. 1. 1. 0.]
# [0. 1. 0. 1. 1.]
# [1. 0. 1. 0. 0.]]