Python:添加包含列元素成对乘积的列的最快方法
Python: fastest way of adding columns containing pairwise products of column elements
假设我有一个 numpy 数组
X = np.array([[1,2,3],
[4,5,6],
[7,8,9]])
我想通过添加(在左侧)将所有可能的列对相乘得到的列来扩展该矩阵。在此示例中,它将变为
X = np.array([[1, 2, 3, 2, 6],
[4,5,6,20,24,30],
[7,8,9,56,63,72]])
第四列是X[:,0]
和X[:,1]
的乘积,第五列是X[:,0]
和X[:,2]
的乘积,第六列是X[:,0]
和X[:,2]
的乘积X[:,1]
和 X[:,2]
.
我的尝试
我想为此使用 np.hstack
。但是我也知道使用循环会减慢一切,但我不知道如何在没有循环的情况下正确地做到这一点。
for i in range(matrix.shape[1]-1):
for j in range(matrix.shape[1])[i:]:
matrix2 = np.hstack((matrix, (matrix[:,i]*matrix[:,j]).reshape(-1,1))).copy()
问题是它很慢,而且我必须使用不同的矩阵,否则它会继续添加列。有更好的主意吗?
方法 #1
使用 np.triu_indices
获取成对列索引。将它们用于 select 两组块,这些块是从输入数组的列索引中获得的。使用这些块执行逐元素乘法,最后将它们作为新列与输入数组一起堆叠 np.concatenate
.
因此,实施 -
n = X.shape[1]
r,c = np.triu_indices(n,1)
out0 = X[:,r] * X[:,c]
out = np.concatenate(( X, out0), axis=1)
方法 #2
为了内存效率和性能,尤其是对于大型数组,另一个灵感来自 通过循环遍历配对组 -
m,n = X.shape
N = n*(n-1)//2
idx = np.concatenate(( [0], np.arange(n-1,0,-1).cumsum() ))+n
start, stop = idx[:-1], idx[1:]
out = np.empty((m,n+N),dtype=X.dtype)
out[:,:n] = X
for j,i in enumerate(range(n-1)):
out[:, start[j]:stop[j]] = X[:,i,None]*X[:,i+1:]
运行时测试
In [403]: X = np.random.randint(0,9,(10,100))
In [404]: %timeit app1(X)
...: %timeit app2(X)
...:
1000 loops, best of 3: 277 µs per loop
1000 loops, best of 3: 350 µs per loop
In [405]: X = np.random.randint(0,9,(10,1000))
In [406]: %timeit app1(X)
...: %timeit app2(X)
...:
10 loops, best of 3: 68.6 ms per loop
100 loops, best of 3: 12.5 ms per loop
In [407]: X = np.random.randint(0,9,(10,2000))
In [408]: %timeit app1(X)
...: %timeit app2(X)
...:
1 loop, best of 3: 311 ms per loop
10 loops, best of 3: 44.8 ms per loop
假设我有一个 numpy 数组
X = np.array([[1,2,3],
[4,5,6],
[7,8,9]])
我想通过添加(在左侧)将所有可能的列对相乘得到的列来扩展该矩阵。在此示例中,它将变为
X = np.array([[1, 2, 3, 2, 6],
[4,5,6,20,24,30],
[7,8,9,56,63,72]])
第四列是X[:,0]
和X[:,1]
的乘积,第五列是X[:,0]
和X[:,2]
的乘积,第六列是X[:,0]
和X[:,2]
的乘积X[:,1]
和 X[:,2]
.
我的尝试
我想为此使用 np.hstack
。但是我也知道使用循环会减慢一切,但我不知道如何在没有循环的情况下正确地做到这一点。
for i in range(matrix.shape[1]-1):
for j in range(matrix.shape[1])[i:]:
matrix2 = np.hstack((matrix, (matrix[:,i]*matrix[:,j]).reshape(-1,1))).copy()
问题是它很慢,而且我必须使用不同的矩阵,否则它会继续添加列。有更好的主意吗?
方法 #1
使用 np.triu_indices
获取成对列索引。将它们用于 select 两组块,这些块是从输入数组的列索引中获得的。使用这些块执行逐元素乘法,最后将它们作为新列与输入数组一起堆叠 np.concatenate
.
因此,实施 -
n = X.shape[1]
r,c = np.triu_indices(n,1)
out0 = X[:,r] * X[:,c]
out = np.concatenate(( X, out0), axis=1)
方法 #2
为了内存效率和性能,尤其是对于大型数组,另一个灵感来自
m,n = X.shape
N = n*(n-1)//2
idx = np.concatenate(( [0], np.arange(n-1,0,-1).cumsum() ))+n
start, stop = idx[:-1], idx[1:]
out = np.empty((m,n+N),dtype=X.dtype)
out[:,:n] = X
for j,i in enumerate(range(n-1)):
out[:, start[j]:stop[j]] = X[:,i,None]*X[:,i+1:]
运行时测试
In [403]: X = np.random.randint(0,9,(10,100))
In [404]: %timeit app1(X)
...: %timeit app2(X)
...:
1000 loops, best of 3: 277 µs per loop
1000 loops, best of 3: 350 µs per loop
In [405]: X = np.random.randint(0,9,(10,1000))
In [406]: %timeit app1(X)
...: %timeit app2(X)
...:
10 loops, best of 3: 68.6 ms per loop
100 loops, best of 3: 12.5 ms per loop
In [407]: X = np.random.randint(0,9,(10,2000))
In [408]: %timeit app1(X)
...: %timeit app2(X)
...:
1 loop, best of 3: 311 ms per loop
10 loops, best of 3: 44.8 ms per loop