161

Note: This is asking for the reverse of the usual tuple-to-array conversion.

I have to pass an argument to a (wrapped c++) function as a nested tuple. For example, the following works

X = MyFunction( ((2,2),(2,-2)) )

whereas the following do not

X = MyFunction( numpy.array(((2,2),(2,-2))) )
X = MyFunction( [[2,2],[2,-2]] )

Unfortunately, the argument I would like to use comes to me as a numpy array. That array always has dimensions 2xN for some N, which may be quite large.

Is there an easy way to convert that to a tuple? I know that I could just loop through, creating a new tuple, but would prefer if there's some nice access the numpy array provides.

If it's not possible to do this as nicely as I hope, what's the prettiest way to do it by looping, or whatever?

Mike
  • 19,114
  • 12
  • 59
  • 91

5 Answers5

230
>>> arr = numpy.array(((2,2),(2,-2)))
>>> tuple(map(tuple, arr))
((2, 2), (2, -2))
Niklas B.
  • 92,950
  • 18
  • 194
  • 224
37

Here's a function that'll do it:

def totuple(a):
    try:
        return tuple(totuple(i) for i in a)
    except TypeError:
        return a

And an example:

>>> array = numpy.array(((2,2),(2,-2)))
>>> totuple(array)
((2, 2), (2, -2))
Bi Rico
  • 25,283
  • 3
  • 52
  • 75
  • 3
    Nice generalization. As a python newbie, though, I wonder if it's considered good style to use exceptions for a condition that is almost as common as the non-exceptional state. At least in c++, flow control by exceptions is usually frowned upon. Would it be better to test if `type(a)==numpy.ndarray`? – Mike Apr 05 '12 at 15:36
  • 12
    This is pretty common in python because of the concept of "duck-typing" and EAFT, more here: http://docs.python.org/glossary.html#term-duck-typing. The advantage of this approach is that it'll convert any nested sequence into nested tuples, not just an array. One thing I should have done that I've fixed is specify which errors I want handled by the except block. – Bi Rico Apr 05 '12 at 16:53
  • 4
    In C++, exceptions are slow relative to conditionals for a variety of reasons. In Python, they are approximately the same in terms of performance - this is one of the places that we have to check our C++ intuitions at the door. – dbn Nov 19 '15 at 23:02
  • 1
    This solution is 100% what I needed. Thanks! – Antoine Neidecker Feb 26 '21 at 18:38
16

I was not satisfied, so I finally used this:

>>> a=numpy.array([[1,2,3],[4,5,6]])
>>> a
array([[1, 2, 3],
       [4, 5, 6]])

>>> tuple(a.reshape(1, -1)[0])
(1, 2, 3, 4, 5, 6)

I don't know if it's quicker, but it looks more effective ;)

MegaIng
  • 7,361
  • 1
  • 22
  • 35
hoping it helps
  • 179
  • 1
  • 2
  • 8
    That was not the shape that I wanted for the tuple. – Mike Aug 12 '13 at 18:32
  • This looks like it can be a nice 0-copy way to hash arrays – Ludecan Jan 25 '23 at 22:09
  • Nah. It takes the same time (pardon the ugly formatting): # %% import numpy as np n = 10000 a = np.array([list(range(n)) for i in range(n)]) # %% %timeit hash(tuple(map(tuple, a))) >>> 3.59 s ± 70.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) # %% %timeit hash(tuple(map(tuple, a))) >>> 3.53 s ± 47.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) – Ludecan Jan 25 '23 at 22:22
9

Another option

tuple([tuple(row) for row in myarray])

If you are passing NumPy arrays to C++ functions, you may also wish to look at using Cython or SWIG.

Greg von Winckel
  • 2,261
  • 2
  • 16
  • 14
  • That doesn't convert to tuple. Converts to a list? – Peter Sep 23 '15 at 04:54
  • Did you try it? It makes a tuple when I run in. Note that the last function called is tuple, which returns a tuple. If you have only the [...] part without the outer tuple, you will get a list of tuples. – Greg von Winckel Sep 24 '15 at 14:26
  • is there a faster method? – Vicrobot Jul 15 '19 at 18:02
  • 6
    You could avoid creating the intermediate `list` by omitting the square brackets, i.e. using `tuple(tuple(row) for row in myarray)` – norok2 Dec 14 '19 at 13:52
5

If you like long cuts, here is another way tuple(tuple(a_m.tolist()) for a_m in a )

from numpy import array
a = array([[1, 2],
           [3, 4]])
tuple(tuple(a_m.tolist()) for a_m in a )

The output is ((1, 2), (3, 4))

Note just (tuple(a_m.tolist()) for a_m in a ) will give a generator expresssion. Sort of inspired by @norok2's comment to Greg von Winckel's answer

Tejas Shetty
  • 685
  • 6
  • 30