列出理解以创建两两不同

2024-10-06 11:56:42 发布

您现在位置:Python中文网/ 问答频道 /正文

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

如果没有成功,我就搞不懂怎么了!此外,在第一个实现中,没有对所有矩阵行值执行第二个循环以节省计算时间,如何能够使用列表理解来执行它?你知道吗

谢谢!你知道吗


Tags: inl1列表fordefnpdiffrange
1条回答
网友
1楼 · 发布于 2024-10-06 11:56:42

你不会从列表中得到任何东西。。。除了更好的理解列表! 你必须理解的是,列表理解是一个功能概念。我不会详细介绍函数式编程, 但是你必须记住函数式编程禁止副作用。举个例子:

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

那更干净。下一步是什么?在上面的代码中,您选择迭代大于ij,并同时设置两个值。但是在列表理解中,你不能选择单元格:列表理解为每个单元格提供坐标,你必须声明值。你知道吗

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

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

这样更好。第二,我们需要给矩阵的每个单元格赋值,而不仅仅是在单元格前加上零并选择不想更新的单元格:

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

一个简短的版本是,使用Python的“伪三元条件运算符”:

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

如果我没说错的话,更简单的是(最终版本):

def bray(x):
    n = x.shape[0]
    def diss(i,j):
        l1_diff = abs(x[i,:] - x[j,:])
        l1_sum = x[i,:] + x[j,:] + 1
        return l1_diff.sum() / l1_sum.sum()

    bray_diss_top_right = np.array([[diss(i,j) if j>i else 0. for j in range(n)] for i in range(n)])
    return bray_diss_top_right + bray_diss_top_right.transpose()

请注意,这个版本可能比你的版本慢,但在我看来,矩阵的构建方式更容易掌握。你知道吗

相关问题 更多 >