Вы ничего не получите с пониманием списка ... кроме лучшего понимания понимания списка!Что вы должны понять, так это то, что понимание списка - это функциональная концепция.Я не буду вдаваться в детали функционального программирования, но вы должны иметь в виду, что функциональное программирование запрещает побочные эффекты.Пример:
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
, которая больше 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
Это лучше.Во-вторых, нам нужно присвоить значение каждой ячейке матрицы, а не просто заполнить нулями и выбрать ячейки, которые мы не хотим обновлять:
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()
Обратите внимание, что эта версия на , вероятно, (я не измерял) медленнее, чем ваша, но способ построения матрицыНа мой взгляд, легче понять.