列表理解会产生成对的差异

时间:2018-11-27 09:37:29

标签: python list-comprehension

我对列表推导不熟悉,但是我想使用列表推导来计算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])]

没有成功,我不知道出什么问题了!而且,在第一种实现方式中,没有对所有矩阵行值都执行第二次循环以节省计算时间,如何通过列表理解来实现呢?

谢谢!

1 个答案:

答案 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

请注意,此版本可能比您的版本慢(我没有测量),但我认为矩阵的构建方式更容易掌握。