1

I've got a list with row matrices:

rows = [ matrix([[1, 0, 0]]), matrix([[0, 1, 0]]), matrix([[0, 0, 1]]) ]

and I attempted to loop over these using for (a, b, c) in rows:, but instead of this working, I got an error:

ValueError: not enough values to unpack (expected 3, got 1)

The expected behaviour would be to unpack the three elements in the row to a, b, c:

for (a, b, c) in rows:
    print(f"{a} {b} {c}")
> 1 0 0
> 0 1 0
> 0 0 1

Unfortunately, this would work on [1, 0, 0], but not on [[1, 0, 0]].

I realized this is because they're [[doubly packed]], but I was wondering if there was a simple solution to this issue?

Frank Vel
  • 1,202
  • 1
  • 13
  • 27
  • 1
    Please also include the "expected behavior" (see [mcve]). Otherwise it's hard to accurately answer your question.. – MSeifert Sep 07 '17 at 20:49
  • Is there a reason you're using a `for` loop on numpy arrays/matrices? Are you creating this list in the first place? – roganjosh Sep 07 '17 at 20:51
  • @MSeifert I apologize, I thought it was obvious, but I guess I should have made it clearer, so I've added an example! – Frank Vel Sep 07 '17 at 21:03
  • @roganjosh I'm iterating over the values in the row matrix; These are all of length 3, so it would be ncie to simply unpack the values to `a, b, c`. I have create the list of the matrices. – Frank Vel Sep 07 '17 at 21:05
  • Thanks for the edit. Now it's indeed clearer. :) – MSeifert Sep 07 '17 at 21:16

3 Answers3

2

It seems like you want to unpack each matrix itself. In that case I would advise: Don't use matrix or NumPy. There is also no reason to use NumPy matrix at all. Even the NumPy documentation states that you should prefer arrays over matrices. But in this case you should use plain lists, they are faster if you iterate over them or unpack them.

So you first want to iterate over each matrix and then you want to unpack each one of these. But you have to do it in two steps:

for mat in rows:      # this iterates over the list and gives you each matrix
    # this unpacks the matrix
    # to make it work faster I converted it to a list first using "tolist"
    # note that I needed to index it because it's a 2D matrix!
    a, b, c = mat.tolist()[0]
    print(a, b, c)  

It's also possible to do the two steps in one line:

for a, b, c in (mat.tolist()[0] for mat in rows):
    print(a, b, c)
MSeifert
  • 145,886
  • 38
  • 333
  • 352
  • I appreciate your concern, but the list is the result of matrix multiplication. The speed of your solution is impressive though, and I like the oneline solution, even if it's a bit unintuitive. – Frank Vel Sep 07 '17 at 21:47
2

A problem is that indexing a row of a matrix still gives a matrix. A way around that is to turn the matrix into a 1d array:

In [368]: for r in rows:
     ...:     a,b,c=r.A1
     ...:     print(a,b,c)
     ...:     
1 0 0
0 1 0
0 0 1

Numpy matrix to array


In [370]: for r in rows:
     ...:     print(r.shape,r[0].shape)
     ...:     
(1, 3) (1, 3)
(1, 3) (1, 3)
(1, 3) (1, 3)

I don't usually use or recommend unpacking values from an array. That works against the generality of arrays. unpacking (unless using '*' terms) fixes the dimensions. But in general array can have several dimensions, and large sizes.

Also np.matrix is discouraged. If these elements were np.array instead there wouldn't be this persistent 2d problem.

hpaulj
  • 221,503
  • 14
  • 230
  • 353
2

Since, we are using a,b,c to extract elements from each matrix's first row, it seems we are guaranteed to have 3 elements per row and it would be one per row for each of those matrices. So, one simple solution (as asked) would be to use the array constructor when using the loop and squeeze out the singleton nested level, like so -

for a,b,c in np.array(rows)[:,0]:

Or simpler np.squeeze() -

for a,b,c in np.squeeze(rows):

Note that this won't be the most memory efficient way but works as a simple way to extract those required scalar values.

Here's a sample run -

In [375]: for a,b,c in np.squeeze(rows):
     ...:     print(a,b,c)
     ...:     
(1, 0, 0)
(0, 1, 0)
(0, 0, 1)
Divakar
  • 218,885
  • 19
  • 262
  • 358
  • While I admire the elegance this is terrible. You convert a **list** of matrices to an **array** just to iterate over it with a `for`-loop? – MSeifert Sep 07 '17 at 21:25
  • Also note the question uses `f`-strings so the answer should at least have `()` for the print :) – MSeifert Sep 07 '17 at 21:27
  • 1
    @MSeifert As I said, I was going more with the `simple solution` part as stated in the question. Agreed, not very memory efficient this one :) – Divakar Sep 07 '17 at 21:28
  • Inefficient in every respect (but simple - I grant you that). Unpacking an array or iterating over it is much slower. Just timing the 3 approaches; mine: 20us, hpaulj: 35us, yours: 67us. – MSeifert Sep 07 '17 at 21:35
  • 1
    `np.squeeze` seems to be the simplest way of turning the original code to a working solution. Memory is of no concern, as the list is relatively small, but that's indeed a valid point. But since there's no optimization tag, that shouldn't be too important. – Frank Vel Sep 07 '17 at 21:39