Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Behavior of np.c_ with list and tuple arguments

Tags:

python

numpy

The output of np.c_ differs when its arguments are lists or tuples. Consider the output of the three following lines

np.c_[[1,2]]
np.c_[(1,2)]
np.c_[(1,2),]

With a list argument, np.c_ returns a column array, as expected. When the argument is a tuple instead (second line), it returns a 2D row. Adding a comma after the tuple (third line) returns a column array as for the first call.

Can somebody explain the rationale behind this behavior?

like image 801
P-Gn Avatar asked Jul 28 '17 14:07

P-Gn


2 Answers

There are 2 common use cases for np.c_:

  • np.c_ can accept a sequence of 1D array-likes:

    In [98]: np.c_[[1,2],[3,4]]
    Out[98]: 
    array([[1, 3],
           [2, 4]])
    
  • or, np.c_ can accept a sequence of 2D array-likes:

    In [96]: np.c_[[[1,2],[3,4]], [[5,6],[7,8]]]
    Out[96]: 
    array([[1, 2, 5, 6],
           [3, 4, 7, 8]])
    

So np.c_ can be passed 1D array-likes or 2D array-likes. But that raises the question how is np.c_ supposed to recognize if the input is a single 2D array-like (e.g. [[1,2],[3,4]]) or a sequence of 1D array-likes (e.g. [1,2], [3,4])?

The developers made a design decision: If np.c_ is passed a tuple, the argument will be treated as a sequence of separate array-likes. If it is passed a non-tuple (such as a list), then that object will be consider a single array-like.

Thus, np.c_[[1,2], [3,4]] (which is equivalent to np.c_[([1,2], [3,4])]) will treat ([1,2], [3,4]) as two separate 1D arrays.

In [99]: np.c_[[1,2], [3,4]]
Out[99]: 
array([[1, 3],
       [2, 4]])

In contrast, np.c_[[[1,2], [3,4]]] will treat [[1,2], [3,4]] as a single 2D array.

In [100]: np.c_[[[1,2], [3,4]]]
Out[100]: 
array([[1, 2],
       [3, 4]])

So, for the examples you posted:

np.c_[[1,2]] treats [1,2] as a single 1D array-like, so it makes [1,2] into a column of a 2D array:

In [101]: np.c_[[1,2]]
Out[101]: 
array([[1],
       [2]])

np.c_[(1,2)] treats (1,2) as 2 separate array-likes, so it places each value into its own column:

In [102]: np.c_[(1,2)]
Out[102]: array([[1, 2]])

np.c_[(1,2),] treats the tuple (1,2), (which is equivalent to ((1,2),)) as a sequence of one array-like, so that array-like is treated as a column:

In [103]: np.c_[(1,2),]
Out[103]: 
array([[1],
       [2]])

PS. Perhaps more than most packages, NumPy has a history of treating lists and tuples differently. That link discusses how lists and tuples are treated differenty when passed to np.array.

like image 86
unutbu Avatar answered Nov 03 '22 14:11

unutbu


The first level on handling the argument comes from the Python interpreter, which translates a [...] into a call to __getitem__:

In [442]: class Foo():
     ...:     def __getitem__(self,args):
     ...:         print(args)
     ...:        
In [443]: Foo()['str']
str
In [444]: Foo()[[1,2]]
[1, 2]
In [445]: Foo()[[1,2],]
([1, 2],)
In [446]: Foo()[(1,2)]
(1, 2)
In [447]: Foo()[(1,2),]
((1, 2),)

np.c_ is an instance of np.lib.index_tricks.AxisConcatenator. It's __getitem__

    # handle matrix builder syntax
    if isinstance(key, str):
        ....
        mymat = matrixlib.bmat(...)
        return mymat

    if not isinstance(key, tuple):
        key = (key,)

     ....
    for k, item in enumerate(key):
        ....

So except for the np.bmat compatible string, it turns all inputs into a tuple, and then iterates over the elements.

Any of the variations containing [1,2] is the same as ([1,2],), a single element tuple. (1,2) is two elements that will be concatenated. So is ([1,2],[3,4]).

Note that numpy indexing also distinguishes between lists and tuples (though with a few inconsistencies).

In [455]: x=np.arange(24).reshape(2,3,4)
In [456]: x[0,1]               # tuple - index for each dim
Out[456]: array([4, 5, 6, 7])
In [457]: x[(0,1)]             # same tuple
Out[457]: array([4, 5, 6, 7])
In [458]: x[[0,1]]             # list - index for one dim
Out[458]: 
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])
In [459]: x[([0,1],)]          # same
     ....
like image 22
hpaulj Avatar answered Nov 03 '22 15:11

hpaulj