【问题标题】:Numpy matrix row comparisonNumpy矩阵行比较
【发布时间】:2015-06-12 09:21:48
【问题描述】:

这个问题更侧重于计算的性能。

我有 2 个列数相同但行数不同的矩阵。一个矩阵是“模式”,其行必须分别与其他矩阵行(所有行)进行比较,然后才能提取平均值的统计值等于模式、标准、... 所以,我有以下矩阵,计算如下:

numCols = 10
pattern = np.random.randint(0,2,size=(7,numCols))
matrix = np.random.randint(0,2,size=(5,numCols))

comp_mean = np.zeros(pattern.shape[0])
for i in range(pattern.shape[0]):
    comp_mean[i] = np.mean(np.sum(pattern[i,:] == matrix, axis=1))

print comp_mean # Output example: [ 1.6  1.   1.6  2.2  2.   2.   1.6]

这很清楚。问题是两者的矩阵行数都大得多(~1.000.000)。所以这段代码很慢。我尝试实现 numpy 语法,因为有时它会令我惊讶地改进计算时间。所以我做了以下代码(它可能很奇怪,但它有效!):

comp_mean = np.mean( np.sum( (pattern[np.repeat(np.arange(pattern.shape[0]), matrix.shape[0])].ravel() == np.tile(matrix.ravel(),pattern.shape[0])).reshape(pattern.shape[0],matrix.shape[0],matrix.shape[1]), axis=2 ),axis=1)
print comp_mean

但是,此代码比使用“for”扣环的前一个代码要慢。所以我想知道是否有可能加快计算速度。

编辑

我检查了实矩阵的不同方法的运行时间,结果如下:

  • 我 - 方法 1:18.04
  • 我 - 方法 2:303.10
  • Divakar - 方法 1:18.79
  • Divakar - 方法 2:65.11
  • Divakar - 方法 3.1:137.78
  • Divakar - 方法 3.2:59.59
  • Divakar - 方法 4:6.06

编辑(2)

以前在笔记本电脑上执行的运行。我已经在桌面上运行了代码。我已经避免了最坏的结果,并且新的运行时现在不同了:

  • 我 - 方法 1:6.25
  • Divakar - 方法 1:4.01
  • Divakar - 方法 2:3.66
  • Divakar - 方法 4:3.12

【问题讨论】:

  • pattern 有多大?
  • @Divakar,对于暴露的运行时测试,模式编号行 100.000 和矩阵 1.000 行。
  • 查看添加的方法#4?
  • @Divakar 您的最后一种方法具有最佳性能。您是否认为还有可能走得更远,或者正如您所说,矩阵太大最好坚持使用 for 循环?我将在内存大的机器上尝试相同的代码,以检查这是否会成为计算瓶颈
  • 看看它在高端机器上的表现肯定会很有趣。对于矢量化代码和庞大的数据量,总是存在这种争斗,特别是当操作是归约时。稍作修改以接近 #4,尽管我怀疑不会产生巨大影响。

标签: python performance numpy matrix comparison


【解决方案1】:

这里可以建议使用broadcasting 的几种方法。

方法#1

out = np.mean(np.sum(pattern[:,None,:] == matrix[None,:,:],2),1)

方法 #2

mrows = matrix.shape[0]
prows = pattern.shape[0]
out = (pattern[:,None,:] == matrix[None,:,:]).reshape(prows,-1).sum(1)/mrows

方法#3

mrows = matrix.shape[0]
prows = pattern.shape[0]
out = np.einsum('ijk->i',(pattern[:,None,:] == matrix[None,:,:]).astype(int))/mrows
# OR out = np.einsum('ijk->i',(pattern[:,None,:] == matrix[None,:,:])+0)/mrows

方法#4

如果 matrix 中的行数很大,最好坚持使用 for 循环以避免这种情况下的巨大内存需求,这也可能导致运行速度变慢。相反,我们可以在每次循环迭代中进行一些优化。这是显示的一种可能的优化 -

mrows = matrix.shape[0]
comp_mean = np.zeros(pattern.shape[0])
for i in range(pattern.shape[0]):
    comp_mean[i] = (pattern[i,:] == matrix).sum()
comp_mean = comp_mean/mrows

【讨论】:

  • 方法#4 非常令人惊讶……而且np.einsum……很有趣。 +1。
  • @rayryeng 只是这种巨大的归约操作的内存需求是最大的原因matrixpattern 大得多可能是这里的原因。
  • @rayryeng,我同意你的观点,np.einsum 很有趣。我什至不知道那个功能:S
  • @isal 我发现这个页面更详细:ajcr.github.io/Basic-guide-to-einsum - numpy 文档非常不准确。
【解决方案2】:

你可以试试这个:

import scipy.ndimage.measurements

comp_mean = np.zeros(pattern.shape[0])
for i in range(pattern.shape[0]):
    m = scipy.ndimage.measurements.histogram(matrix,0,1,2,pattern[i],[0,1])
    comp_mean[i] = m[0][0]+m[1][1]
comp_mean /= matrix.shape[0]

问候。

【讨论】:

  • 这段代码的问题是模式行只能包含零或一,这使得 m[0][0] 或 m[1][1] 不存在并返回 TypeError。我尝试使用以下 comp_mean[i] = int(0 if np.size(m[0]) == 1 else m[0][0]) + int(0 if np.size(m[1]) == 1 else m[1][1]) 更改您的行,这可能不是最优化的行,因为运行时间是 250.35 秒。如果您找到另一种更好的方法,请告诉我检查运行时间。
  • 3 个标签。我修改了您的代码,但正如我所解释的,当其中一个标签不在您在循环中比较的模式行中时,问题就出现了,因为 m[n][n] 不存在给出 TypeError 并停止执行。
  • @isai 尝试类似 m = scipy.ndimage.measurements.histogram(matrix,0,2,3,pattern[i],[0,1,2]) 然后 m[0] [0]+m[1][1]+m[2][2] 问候。
  • 当我提到我修改了你的代码时,这就是我所尝试的。但是,正如我所说,当您从模式中有一行没有一个标签的值时(假设一行全为零),m[1][1] 不存在。它是空的。所以这给出了一个 TypeError 停止执行。尝试使用以下矩阵来理解我的意思:pattern = np.array([[1,1,1,1,1,1,1],[1,1,0,0,1,1,0],[0,1,1,1,1,0,1]]) matrix = np.array([[0,1,1,0,0,0,0],[0,1,0,0,1,1,0]])
  • 好的,我明白了;这个想法可能不是很好。我建议这样做是因为 scipy.ndimage 对于与数组上的“标签”相关的一些计算非常快,但您自己的问题似乎有点不同。
猜你喜欢
  • 2017-08-13
  • 2019-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-31
  • 1970-01-01
  • 2019-10-08
相关资源
最近更新 更多