How can I have two for loops in the same list but the second one to start from the next value of the first loop?

Viewed 50

I am trying to write a code where i have a list of vectors and Ι have to find the angle between every vector and the rest of them.(I am working on mediapipe's hand landmarks). My code so far is this one:

vectors = [thumb_cmc_vec, thumb_mcp_vec, thumb_ip_vec, thumb_tip_vec, index_mcp_vec, index_pip_vec,
                           index_dip_vec, index_tip_vec, middle_mcp_vec, middle_pip_vec, middle_dip_vec, middle_tip_vec,
                           ring_mcp_vec, ring_pip_vec, ring_dip_vec, ring_tip_vec, pinky_mcp_vec, pinky_pip_vec,
                           pinky_dip_vec, pinky_tip_vec]

                for vector in vectors:
                    next_vector = vector + 1
                    print(vector)
                    for next_vector in vectors:
                        print(next_vector)
                        M = (np.linalg.norm(vector) * np.linalg.norm(next_vector))
                        ES = np.dot(vector, next_vector)
                        th = math.acos(ES / M)
                        list.append(th)
                        print(list)

where M = the multiplication of the norms of the current sets of vectors, ES = the scalar product of the vectors and th = the angle of the vectors. My problem is that the variable next_vector always starts the for loop from the first vector of the list even though I want it to start from the previous loop's next vector in order not to have duplicate results. Also when both of the loops are on the 3rd vector (thumb_ip_vec) I am getting this error th = math.acos(ES / M) ValueError: math domain error . Is there any way to solve this? Thank you!

3 Answers

I think you can iterate through the list indices (using range(len(vectors) - 1)) and access the elements through their indices instead of looping through each element

for i in range(len(vectors) - 1):
    # Iterate from 0 to len(vectors) -1
    vector = vectors[i]
    for j in range(i + 1, len(vectors)):
        # Iterate from index i + 1 to len(vectors)
        next_vector = vectors[j]
        M = (np.linalg.norm(vector) * np.linalg.norm(next_vector))
        ES = np.dot(vector, next_vector)
        th = math.acos(ES / M)
        list.append(th)
        print(list)

The efficient solution here is to iterate over combinations of vectors:

from itertools import combinations  # At top of file

for vector, next_vector in itertools.combinations(vectors, 2):
    M = (np.linalg.norm(vector) * np.linalg.norm(next_vector))
    ES = np.dot(vector, next_vector)
    th = math.acos(ES / M)
    list.append(th)

It's significantly faster than looping over indices and indexing, reduces the level of loop nesting, and makes it more clear what you're trying to do (working with every unique pairing of the input).

I'm not sure I understand your question, but consider using ranges instead. Ranges allow you to iterate, but without calling the exact value only, but by calling it's address. Which means you can manipulate that index to access neighboring values.

for i in range(len(iterables)-1):
    ii = i+1
    initial_value = iterables[i]
    next_value = iterables[ii]

    for ii in range(len(iterables)):
        # do_rest_of_code
                               

Sort of like the mailman, you can reach someone's neighbor without knowing the neighbor's address.
The structure above generally works, but you will need to tweak it to meet your needs.

Related