列表理解以创建成对不相似性

问题描述 投票:1回答:1

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

没有成功,我无法弄清楚出了什么问题!而且,在第一种实现中,不对所有矩阵行值执行第二循环以节省计算时间,如何利用列表理解来实现呢?

谢谢 !

python list-comprehension
1个回答
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)

最后一行是副作用:你修改qazxsw poi的状态。相比之下,副作用免费版本可以:

my_matrix

您没有“create-then-assign”序列:您通过声明每个位置的值来创建矩阵。更确切地说,要创建一个矩阵:

  • 你必须为每个细胞声明一个值;
  • 当你给对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并一次设置两个值。但是在列表理解中,你不选择单元格:列表理解为每个单元格提供坐标,你必须声明这些值。

首先,让我们尝试每次迭代只设置一个值,即使用两个循环:

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

请注意,这个版本可能(我没有测量)比你的版本慢,但在我看来,矩阵的构建方式更容易掌握。

© www.soinside.com 2019 - 2024. All rights reserved.