Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

from this array

s = np.array([[35788, 41715, ... 34964], 
          [5047, 23529, ... 5165], 
          [12104, 33899, ... 11914], 
          [3646, 21031, ... 3814], 
          [8704, 7906, ... 8705]])

I have a loop like this

end =[]
for i in range(len(s)):
    for j in range(i, len(s)):
        out = mahalanobis(s[i], s[j], invcov)       
        end.append(out)
print end

and i take output :

[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]

but I want the output like this :

[[0.0, 12.99, 5.85, 10.22, 3.95], 
[12.99, 0.0, 5.12, 3.45, 4.10], 
[5.85, 5.12, 0.0, 5.05, 8.10], 
[10.22, 3.45, 5.05, 0.0, 15.45], 
[3.95, 4.10, 8.10, 15.45, 0.0]]
share|improve this question
1  
To clarify, you want a list containing lists of exactly 5 items each? Or will the size of the inside lists vary? –  Banjer 1 hour ago
    
I want a list containing lists of exactly 5 items each, like a distance matrix –  Majesty Eksa Permana 1 hour ago

4 Answers 4

You need to loop differently in at least two ways:

end =[]
for s1 in s:
    end.append([mahalanobis(s1, s2, invcov) for s2 in s])

The most important thing is that the inner loop needs to be on the whole s again, else you will never get a square but 1 + 2 + ... + len(s) items (15 in this case as len(s) is 5).

Next, the inner loop must be enclosed in a list, since you want a list of lists.

Less important but nice: I've changed the inner loop to a list comprehension; and I've changed both loops to be directly on s since there's really no reason to go over the indirection of looping over indices then using those indices to get the s items you care about.

So I made four changes in all, but the first two are what you really need to get the result you desire, the other two are just nice improvements:-).

share|improve this answer
    
nice and compact, but how would you assure there that you have 5 elements in the inner array? when i run your code i get different array length depending on input, and i thought Majesty wanted 5 fix? –  hexerei software 1 hour ago
    
You'll have just as many items in each sub-list as you have sub-lists -- "like a distance matrix" (the distance matrix among N points is of course always a square matrix, N by N). –  Alex Martelli 1 hour ago

your loop is fine, if you just add a counter and a second array, then you can group results in groups of 5 elements in one array (if this is the desired result)

end =[]
tmp =[]
for i in range(len(s)):
    for j in range(i, len(s)):
        out = mahalanobis(s[i], s[j], invcov)
        if k % 5 == 0 and k != 0:
            end.append(tmp)
            tmp =[]       
        k += 1
        tmp.append(out)
if len(tmp) > 0:
    end.append(tmp)
print end

No matter what your input then, your output will be an array with n arrays each with 5 members [[1,2,3,4,5],[...],[...], ... ]

share|improve this answer
    
You missed the most important error in that range(i, len(s)) -- generating fewer and fewer items each time through. With len(s) at 5 this makes a three by five matrix, not the 5 by 5 declared as the desired output in the body of the question. –  Alex Martelli 58 mins ago
    
@AlexMartelli yupp, i just concentrated on the 5 array business, as Majesty had not posted the sample data until i had posted my answer... i prefer yours anyhow from style and if the result is what was desired, then perfect! –  hexerei software 48 mins ago

Another way is to use your code, and underneath it:

end2 = []
for repeat in range(len(end)/5):
    end2.append(end[0:4])
    end = end[5:]
share|improve this answer
    
end as generated by the Q's posted code is only 15 items long (not 25) so this will have plenty of empties at the tail of end2. –  Alex Martelli 1 hour ago

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)

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.  ]]
  1. make an array filled with zeros
  2. 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.

  3. 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.

share

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.