Given the list,
end = [0.0, 12.99, 5.85, 10.22, 3.95, 0.0, 5.12, 3.45, 4.10, 0.0, 5.05, 8.10, 0.0, 15.45, 0.0]
you could build the desired 2-dimensional array using
import numpy as np
result = np.zeros((s.shape[0],)*2) # 1
result[np.triu_indices(s.shape[0], 0)] = end # 2
result += result.T # 3
print(result)
which yields
[[ 0. 12.99 5.85 10.22 3.95]
[ 12.99 0. 5.12 3.45 4.1 ]
[ 5.85 5.12 0. 5.05 8.1 ]
[ 10.22 3.45 5.05 0. 15.45]
[ 3.95 4.1 8.1 15.45 0. ]]
- make an array filled with zeros
np.triu_indices(s.shape[0], 0) returns the indices for the upper-triangle of an array of shape (s.shape[0], s.shape[0]).
In [95]: np.triu_indices(5, 0)
Out[95]:
(array([0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4]),
array([0, 1, 2, 3, 4, 1, 2, 3, 4, 2, 3, 4, 3, 4, 4]))
result[...] = end fills the upper-triangle with the values from end.
Take the transpose of result and add that to result, thus making result symmetric.
This allows you to obtain the result without calling both mahalanobis(s[i], s[j]) and mahalanobis(s[j], s[i]) which is unnecessary since mahalanbis distance is symmetric.
Note that the diagonal is always zero since mahalanobis(x,x) equals zero for
any x. So for a little added efficiency, you could exclude the diagonal:
end =[]
for i in range(len(s)):
for j in range(i+1, len(s)): # <-- note i+1
out = mahalanobis(s[i], s[j], invcov)
end.append(out)
and then build result with the same code as before except that now we can use
result[np.triu_indices(s.shape[0], 1)] = end
instead of
result[np.triu_indices(s.shape[0], 0)] = end
The second argument to np.triu_indices controls the diagonal offset. When the offset is 1, the indices corresponding the the main diagonal are omitted.
In [96]: np.triu_indices(5, 1)
Out[96]: (array([0, 0, 0, 0, 1, 1, 1, 2, 2, 3]), array([1, 2, 3, 4, 2, 3, 4, 3, 4, 4]))