Problem: extracting a row from an array, returns a column

In Julia 1.4.0, attempting to extract a row from an array returns a vector/column, and not a row.
From array:


N = [1 2;3 4]

2×2 Array{Int64,2}:
 1  2
 3  4

N[1,:]

2-element Array{Int64,1}:
 1
 2

It returns a column instead of a 1x2 array. It doesn’t even show the dimension. Like:

C = [3 8]

1×2 Array{Int64,2}:
 3  8

The 2 element array above (N[1,:] should be 1x2 array, and in row format, not column/vector. Same result in jupyter and the REPL. To post multiply by this result, I have to transpose first to get it back to a row, where it should be a row to begin with.

Any help appreciated, I’m not sure if there’s a reason that it works this way - is it meant to or is something wrong?

The quick way of obtaining what you want is:

julia> N[[1],:]
1×2 Array{Int64,2}:
 1  2

I,e. instead of giving a scalar index, pass a collection of indices for slicing, like when you do N[1:2,:] - although that collection has only one index.

Longer explanation of why is it like that in a recent discussion:

Thank you. I’ll dig deeper into that - it doesn’t make sense yet - I’ll keep looking.

Among the different replies given in the linked discussion, I think that this one by Oscar Smith is the clearest, so perhaps it will make sense after reading it:

1 Like

Without checking I would think that N[1:1, :] is better, since it won’t heap-allocate an array.

3 Likes

Yeah, I agree with DNF that N[1:1, :] is probably more idiomatic.

One way to understand it is that a 1D Vector is not the same as an Nx1 Matrix (i.e. a column), even though they are often interchangeable.

The indexing rules in Julia are agnostic to the dimensionality of the array, i.e. they work the same with 1D, 2D, 3D, etc.

See this post with some more examples of indexing and dimensionality. The general rule is that when you index dimension D, the dimesionality of the result for that dimension is the same as the dimension of the index, so if you index with a zero-dimensional index (a scalar) that dimension is dropped. If you index with a 1-dimensional index (a vector, range, etc.) the dimension is retained. If you index with a 2D index you actually add a dimension.

I think (though I haven’t verified exhaustively) that you can write the general rule as

resultsize(idxs...) = tuple(Iterators.flatten(size.(idxs))...)

i.e. the size of the result is the concatenation of the sizes of the indices.

So 1D and 2D Arrays aren’t treated specially when it comes to indexing.

3 Likes

This does make more sense to me, but it still seems like that should be taken care of internally without having to specify a range. If : only returns 1 value then it should be scalar, otherwise, it should have the dimensions of the piece that you’re looking for. I come from Python, so using : is handled as I’d expect.

C = A[2,:]

2-element Array{Int64,1}:
 3
 8

C = A[2:2,:]

1×2 Array{Int64,2}:
 3  8

There’s often a consistency/convenience trade-off. Opinions definitely differ but I think consistency is usually a better target because what’s convenient for one application is often inconvenient for another.

In this case it’s nice to know what the dimensionality of the output will be when you use :, without having to worry about how many rows or columns the input has. for example, if I have A=rand(3,4) and B=rand(1,4), it would be surprising (to me) if A[:,1] had a different shape than B[:,1].

3 Likes

The reason Julia doesn’t do this is a consequence of a very important idea in Julia which is type stability.
Put simply, since Julia has multiple dispatch, good performance depends on the ability of the compiler to know what types your functions will output based on the types of their input. This allows the compiler to skip dynamic dispatch which is relatively slow.

This means that 2:2 should be of the same type as 2:3 since both construct a range with 2 Int arguments. Then A[2:2,:] should have the same type as A[2:3,:] since in both cases getindex is provided arguments of the same type.

4 Likes

What should you get when you index a 1D array? That is, when you do

a = [11,12,13]

a[2]

Should you get 12 or [12]?

The more I think about this, the more I think that this is a flaw in Julia. If A[:,1] returns an nx1 column (which it does), then A[1:n] should return a 1xn row (which it doesn’t, it returns something that looks like a column, but doesn’t have any dimension attached). It’s a question of consistency.

Maybe I posted this in the wrong place. In this case, the program author now has to write array indices in an odd way - I suspect that this is more an issue of how the author of the Julia module implemented things. It should be easier than this. Typing issues should be sorted out in the background, not by the user.

Actually, it doesn’t. It returns a flat Vector.

I particularly don’t understand what you mean by this. Why should this return a 1xn?

The current way is a bit surprising at first, but it is the most consistent approach.

2 Likes

As DNF has already indicated, your assumption is wrong: A[:,1] does not return a column matrix, but a vector (an array of values with only one dimension).

Maybe you are accustomed to another language that does something different, but that doesn’t mean that such behavior is “a flaw”. And actually it’s the same in languages as popular as Python (with Numpy):…

>>> import numpy as np
>>> A = np.array([[1,2], [3,4]])
>>> A[0,:]
array([1, 2])
>>> A[:,0]
array([1, 3])

… and R:

> A <- matrix(c(1,2,3,4), nrow=2, ncol=2)
> A[1,]
[1] 1 3
> A[,1]
[1] 1 2
2 Likes

I see it now, I didn’t notice, because the flat matrix multiplies a matrix as as though it is a column matrix. Thanks for the feedback.

I’m think I might be thinking of the behaviour of Python lists. MATLAB might be similar (but that is MATrix LABoratory after all).