我对列表推导不熟悉,但是我想使用列表推导来计算bray-curtis差异。差异由
给出def bray(x):
bray_diss = np.zeros((x.shape[0], x.shape[0]))
for i in range(0, bray_diss.shape[0]):
bray_diss[i,i] = 0
for j in range(i+1, bray_diss.shape[0]):
l1_diff = abs(x[i,:] - x[j,:])
l1_sum = x[i,:] + x[j,:] + 1
bray_diss[i,j] = l1_diff.sum() / l1_sum.sum()
bray_diss[j,i] = bray_diss[i,j]
return bray_diss
我尝试过类似的事情:
def bray(x):
[[((abs(x[i,:] - x[j,:])).sum() / (x[i,:] + x[j,:] + 1).sum()) for j in range(0, x.shape[0])] for i in range(0, x.shape[0])]
没有成功,我不知道出什么问题了!而且,在第一种实现方式中,没有对所有矩阵行值都执行第二次循环以节省计算时间,如何通过列表理解来实现呢?
谢谢!
答案 0 :(得分:0)
除了列表理解能力更好之外,您将不会获得任何列表理解能力! 您必须了解的是列表理解是一个功能概念。我不会详细介绍函数式编程, 但是您必须记住,函数式编程禁止产生副作用。一个例子:
my_matrix = np.zeros(n, n)
for i in range(n):
for j in range(n):
my_matrix[i,j] = value_of_cell(i,j)
最后一行是一个副作用:您修改my_matrix
的状态。相比之下,免费的副作用版本可以做到:
np.array([[value_of_cell(i,j) for j in range(n)] for i in range(n)])
您没有“创建-然后-分配”序列:通过声明每个位置的值来创建矩阵。更精确地讲,创建一个矩阵:
(i,j)
时,您不能使用它来声明另一个单元格的值(例如(j,i)
)(如果以后需要转换矩阵,则必须重新创建它。这就是为什么这种方法可能会花费大量时间和时间的原因。)
现在,看看您的代码。当您编写列表理解时,一个好的经验法则是使用辅助功能,因为它们有助于清理代码(我们在这里不尝试创建单一代码):
def bray(x):
n = x.shape[0] # cleaner than to repeat x.shape[0] everywhere
def diss(i,j): # I hope it's correct
l1_diff = abs(x[i,:] - x[j,:])
l1_sum = x[i,:] + x[j,:] + 1
return l1_diff.sum() / l1_sum.sum()
bray_diss = np.zeros((n, n))
for i in range(n): # range(n) = range(0,n)
# bray_diss[i,i] = 0 <-- you don't need to set it to zero here
for j in range(i+1, n):
bray_diss[i,j] = diss(i,j)
bray_diss[j,i] = bray_diss[i,j]
return bray_diss
那更干净。你下一步怎么做?在上面的代码中,您选择对大于j
的{{1}}进行迭代,并一次设置两个值。但是在列表推导中,您不必选择单元格:列表推导为您提供了每个单元格的坐标,并且您必须声明值。
首先,让我们尝试在每次迭代中仅设置一个值,即使用两个循环:
i
那更好。其次,我们需要给矩阵的每个单元分配一个值,而不仅仅是用零预填充并选择我们不想更新的单元:
def bray(x):
...
bray_diss = np.zeros((n, n))
for i in range(n):
for j in range(i+1, n):
bray_diss[i,j] = inner(i,j)
for i in range(n):
for j in range(i):
bray_diss[i,j] = bray_diss[j,i]
return bray_diss
一个简短的版本是使用Python的“伪三元条件运算符”:
def bray(x):
...
bray_diss = np.zeros((n, n))
for i in range(n):
for j in range(n):
if j>i: # j in range(i+1, n)
bray_diss[i,j] = inner(i,j) # top right corner
else # j in range(i+1)
bray_diss[i,j] = 0. # zeroes in the bottom left corner + diagonal
for i in range(n):
for j in range(n):
if j<i: # j in range(i)
bray_diss[i,j] = bray_diss[j,i] # fill the bottom left corner now
else # j in range(i, n)
bray_diss[i,j] = bray_diss[i,j] # top right corner + diagonal is already ok
return bray_diss
现在,我们可以将其转换为列表解析:
def bray(x):
...
bray_diss = np.zeros((n, n))
for i in range(n):
for j in range(n):
bray_diss[i,j] = inner(i,j) if j>i else 0.
for i in range(n):
for j in range(n):
bray_diss[i,j] = bray_diss[j,i] if j<i else bray_diss[i,j]
return bray_diss
而且,如果我没记错的话,它甚至更简单(最终版本):
def bray(x):
...
bray_diss_top_right = np.array([[diss(i,j) if j>i else 0. for j in range(n)] for i in range(n)])
bray_diss = np.array([[bray_diss_top_right[j,i] if j<i else bray_diss_top_right[i,j] for j in range(n)] for i in range(n)])
return bray_diss
请注意,此版本可能比您的版本慢(我没有测量),但我认为矩阵的构建方式更容易掌握。