Numpy sorted array for loop error but original works fine

Viewed 115

After I sort this numpy array and remove all duplicate (y) values and the corresponding (x) value for the duplicate (y) value, I use a for loop to draw rectangles at the remaining coordinates. yet I get the error : ValueError: too many values to unpack (expected 2), but its the same shape as the original just the duplicates have been removed.

from graphics import *
import numpy as np

def main():
    win = GraphWin("A Window", 500, 500)

# starting array
startArray = np.array([[2, 1, 2, 3, 4, 7],
              [5, 4, 8, 3, 7, 8]])

# the following reshapes the from all x's in one row and y's in second row
# to x,y rows pairing the x with corresponding y value.
# then it searches for duplicate (y) values and removes both the duplicate (y) and
# its corresponding (x) value by removing the row.
# then the unique [x,y]'s array is reshaped back to a [[x,....],[y,....]] array to be used to draw rectangles.
d = startArray.reshape((-1), order='F')

# reshape to [x,y] matching the proper x&y's together
e = d.reshape((-1, 2), order='C')

# searching for duplicate (y) values and removing that row so the corresponding (x) is removed too.
f = e[np.unique(e[:, 1], return_index=True)[1]]

# converting unique array back to original shape
almostdone = f.reshape((-1), order='C')

# final reshape to return to original starting shape but is only unique values
done = almostdone.reshape((2, -1), order='F')

# print all the shapes and elements
print("this is d reshape of original/start array:", d)
print("this is e reshape of d:\n", e)
print("this is f unique of e:\n", f)
print("this is almost done:\n", almostdone)
print("this is done:\n", done)
print("this is original array:\n",startArray)

# loop to draw a rectangle with each x,y value being pulled from the x and y rows
# says too many values to unpack?
for x,y in np.nditer(done,flags = ['external_loop'], order = 'F'):
    print("this is x,y:", x,y)
    print("this is y:", y)
    rect = Rectangle(Point(x,y),Point(x+4,y+4))
    rect.draw(win)

win.getMouse()
win.close()

main()

here is the output:

line 42, in main
for x,y in np.nditer(done,flags = ['external_loop'], order = 'F'):
ValueError: too many values to unpack (expected 2)
this is d reshape of original/start array: [2 5 1 4 2 8 3 3 4 7 7 8]
this is e reshape of d:
 [[2 5]
 [1 4]
 [2 8]
 [3 3]
 [4 7]
 [7 8]]
this is f unique of e:
 [[3 3]
 [1 4]
 [2 5]
 [4 7]
 [2 8]]
this is almost done:
 [3 3 1 4 2 5 4 7 2 8]
this is done:
 [[3 1 2 4 2]
 [3 4 5 7 8]]
this is original array:
 [[2 1 2 3 4 7]
 [5 4 8 3 7 8]]

why would the for loop work for the original array but not this sorted one? or what loop could I use to just use (f) since it is sorted but shape(-1,2)?

I also tried a different loop:

for x,y in done[np.nditer(done,flags = ['external_loop'], order = 'F')]:

Which seems to fix the too many values error but I get:

IndexError: index 3 is out of bounds for axis 0 with size 2

and

FutureWarning: Using a non-tuple sequence for multidimensional indexing is 
deprecated; use `arr[tuple(seq)]` instead of `arr[seq]`. In the future this 
will be interpreted as an array index, `arr[np.array(seq)]`, which will 
result either in an error or a different result.
  for x,y in done[np.nditer(done,flags = ['external_loop'], order = 'F')]:

which I've looked up on stackexchange to fix but keep getting the error regardless of how I do the syntax.

any help would be great thanks!

1 Answers

I don't have the graphics package (it might be a windows specific thing?), but I do know that you're making this waaaaay too complicated. Here is a much simpler version that produces the same done array:

from graphics import *
import numpy as np

# starting array
startArray = np.array([[2, 1, 2, 3, 4, 7],
                       [5, 4, 8, 3, 7, 8]])

# searching for duplicate (y) values and removing that row so the corresponding (x) is removed too.
done = startArray.T[np.unique(startArray[1,:], return_index=True)[1]]

for x,y in done:
    print("this is x,y:", x, y)
    print("this is y:", y)
    rect = Rectangle(Point(x,y),Point(x+4,y+4))
    rect.draw(win)

To note, in the above version done.shape==(5, 2) instead of (2, 5), but you can always change that back after the for loop with done = done.T.

Here's some notes on your original code for future reference:

  • The order flag in reshape is completely superfluous to what your code is trying to do, and just makes it more confusing/potentially more buggy. You can do all of the reshapes you wanted to do without it.

  • The use case for nditer is to iterate over the individual elements of one (or more) array one at a time. It cannot in general be used to iterate over the rows or columns of a 2D array. If you try to use it this way, you're likely to get buggy results that are highly dependent on the layout of the array in memory (as you saw).

  • To iterate over rows or columns of a 2D array, just use simple iteration. If you just iterate over an array (eg for row in arr:), you get each row, one at a time. If you want the columns instead you can transpose the array first (like I did in the code above with .T).

Note about .T

.T takes the transpose of an array. For example, if you start with

arr = np.array([[0, 1, 2, 3],
                [4, 5, 6, 7]])

then the transpose is:

arr.T==np.array([[0, 4],
                 [1, 5],
                 [2, 6],
                 [3, 7]])
Related