How to create a 2x1 Array?

Reviewing the many solutions in this thread, it is apparent that while there are many ways to create single-column matrices, there is no special syntax for this.

I wonder if this is really a problem. Many types in Base has no short syntax, they are just constructed with a constructor or a function for this purpose. I can imagine three possibilities:

  1. Single-column matrices are not used frequently, eg because vectors are columns vectors in linear algebra, so they are rarely needed. When needed (eg for testing), there are workarounds. If a package uses a lot of these for some reason, they can define a function for it.

  2. They are used frequently enough that a constructor syntax is warranted in base. We can either

    a. co-opt one of the existing syntaxes for this, but this is proving to be difficult,

    b. introduce a special function, eg colmx or something like that.

I am leaning towards 1, but perhaps I just have different use cases.

Tbh I’m still in favour of

[1, 2, 3] -> Vector
[1 2 3]   -> 1x3 Matrix
[1; 2; 3] -> 3x1 Matrix

This seems like the only consistent option for literal syntaxes to me, but I can’t say I really care all that much :slight_smile:

14 Likes

FWIW, I can’t remember ever creating an Nx1 Matrix in the past year or so and I’m almost thinking it could be better that it is hard to create them as they are so rarely useful.

If instead there actually is a need for some syntax, maybe the easiest would be to add a Matrix(v::Vector) method that would create an Nx1 matrix, just in the same way as Matrix(v::RowVector) would create a 1xN Matrix. On 0.6 and master it is a method error, so I guess it wouldn’t be a breaking change.

1 Like

See also https://github.com/JuliaLang/julia/pull/23790.

Part of the rationale for not having an easy way to create an N-by-1 matrix is that such matrices are usually better treated as vectors. However, the return values of expressions like maximum(A, 2) go against this philosophy. The returned array will always have a singleton dimension. It would seem natural to let the maximum function squeeze out this dimension. If this were the case, then the code from the original post would have worked as intended.

Of course it is sometimes useful to preserve the singleton dimension, but one could perhaps distinguish between maximum(A, (2,)) which would squeeze, and maximum(A, [2]) which keep the current behavior.

(Of course this would affect a lot of existing code, and it’s too late for new features for 1.0 anyways.)

1 Like

I found this thread while scratching my head over array creation syntax. Why does Julia 1.1.0 still have two ways to make a vector, one way to make a 1×d array and zero ways to make a d×1 array? Can this still be changed?

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

julia> [1,2]
2-element Array{Int64,1}:
 1
 2

julia> [1;2]
2-element Array{Int64,1}:
 1
 2

This is not correct, please review the solutions above.

Ok, sorry for not being clear on what I meant. I do use single column matrices and what I meant was:

Emphasis mine.

How about a macro?

julia> using MacroTools

julia> macro col(expr)
         @capture expr [x__]
         Expr(:call, :hvcat, Expr(:tuple, [1 for _ in x]...), esc.(x)...)
       end
@col (macro with 1 method)

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

julia> @col [1, 2, 3]
3×1 Array{Int64,2}:
 1
 2
 3
2 Likes

It is very easy to create a one-liner for this purpose (there are multiple suggestions above), but I would just go with

julia> reshape([1, 2], :, 1)
2×1 Array{Int64,2}:
 1
 2

I am not sure I understand why not having dedicated syntax for nx1 matrices is a problem,as you can still create them rather easily. I see this more as a documentation issue.

1 Like

The only reason to prefer directly constructing the array (e.g. using the macro I proposed) is that it avoids allocating a temporary Vector and is therefore a bit faster:

julia> f1(x, y) = reshape([x, y], :, 1)
f1 (generic function with 1 method)

julia> f2(x, y) = @col [x, y]
f2 (generic function with 1 method)

julia> using BenchmarkTools

julia> @btime f1($1, $2)
  53.962 ns (3 allocations: 192 bytes)
2×1 Array{Int64,2}:
 1
 2

julia> @btime f2($1, $2)
  33.171 ns (1 allocation: 96 bytes)
2×1 Array{Int64,2}:
 1
 2

5 Likes

We simply don’t have enough surface syntax available to cover all the possible permutations of dimensionality and concatenation. In fact I’d re-phrase our current setup like this:

No concatenation

  • ,-delimited vectors only

Concatenation:

  • space-delimited does horizontal concatenation (thus is 2+ dimensional)
  • ;- and newline-delimited does vertical concatenation (thus is either 1- or 2+ dimensional)

Since we’re doing concatenation with ;, you can actually get the behavior you want like this:

julia> const matrixifier = Array{Union{}}(undef, 0, 1)
0×1 Array{Union{},2}

julia> [matrixifier; 1; 2; 3]
3×1 Array{Int64,2}:
 1
 2
 3

Ok, that’s mostly a joke. But the distinction between concatenating and not is what makes this more complicated than it might first seem.

6 Likes

It was discussed in the lead up to 1.0 whether [1; 2; 3] should construct a vector or a matrix. But note that there’s nothing special about two dimensions in concatenation syntax in general:

julia> [rand(2); rand(2)]
4-element Array{Float64,1}:
 0.528860561644692
 0.2992626181507161
 0.6659761151342882
 0.423705081212141

julia> [rand(2, 2, 2); rand(2, 2, 2)]
4×2×2 Array{Float64,3}:
[:, :, 1] =
 0.91018   0.0499205
 0.136608  0.128014
 0.621301  0.0150574
 0.58328   0.694085

[:, :, 2] =
 0.533126  0.0534934
 0.974593  0.808837
 0.851397  0.407801
 0.699824  0.928702

julia> [rand(2, 2, 2) rand(2, 2, 2)]
2×4×2 Array{Float64,3}:
[:, :, 1] =
 0.590656    0.106946  0.7484     0.369444
 0.00954534  0.925276  0.0796861  0.467557

[:, :, 2] =
 0.482709  0.0137462  0.241554  0.566803
 0.970356  0.640528   0.737484  0.684

The [... ; ...] syntax calls vcat which does “concatenation along the first dimension”—regardless of how many dimensions the inputs have. If they’re vectors, it produces a vector, if they’re matrices, it produces a matrix, if they’re 3-tensors, it produces a 3-tensor, and so on. There does, however, have to be at least one output dimension since you’re concatenating at least two things vertically, so vcat of scalars does produce a vector. If we’d made [1; 2; 3] produce a 3×1 matrix then that would be arbitrarily treating 2-dimensionally arrays specially, which is a thing that Julia tries very hard to avoid and it would have to lower to something other than just plain vcat (or we’d have to make vcat treat matrices specially as well).

5 Likes

Another way of putting this is that this is the behavior of result = cat(dims=dim, args...) is that it produces a result with the following number of dimensions:

ndims(result) == max(dim, map(ndims, args)...)

Since [... ; ...] is syntax for vcat(...) which is just cat(dims=1, ...) that dictates that ndims([1; 2; 3]) == max(1, map(ndims, (1, 2, 3))...) == max(1, 0, 0, 0) == 1. If we special-cased [1; 2; 3] to produce a matrix then we’d ruin the above consistent rule and make the rule min(2, max(dim, map(ndims, args)...)) instead, which isn’t the worst but makes two dimensions special. It also leaves you without a syntax for vector concatenation which is itself a useful operation. So the choice here is between:

  1. Having syntax for concatenating scalars/vectors into a vector;
  2. Having syntax for concatenating scalars/vectors into an n×1 matrix.

It’s unclear to me that former is less useful or common than the latter.

3 Likes

That said, in practice I wonder if beginner and even somewhat experienced users think of the [x y ...] and [x, y, ...] syntax as concatenation, as opposed to just a vector/matrix constructor.

I am not saying it isn’t nice or consistent, but perhaps concatenation does not need its own special syntax, which could then obviate the need for the above consistency argument, and allow reusing it for “simple” cases like

[1, 2, 3, 4] # a Vector
[1 2;        # 2x2 Matrix
 3 4] 
[1; 2; 3; 4] # 4x1 Matrix 

with line breaks being irrelevant.

Something to ponder for 2.0.

1 Like

But yes, the 1.0 ship has sailed on that one. I would love to get rid of the fiddly space-sensitive array syntax from the core language altogether.

2 Likes

Isn’t there a constructor that can do that ?

julia> Matrix([1,2],2,1)
2×1 Array{Int64,2}:
 1
 2

julia> Matrix([1,2,3,4],2,2)
2×2 Array{Int64,2}:
 1  3
 2  4

Sure, there are zillion ways to create a nx1 matrix, but we are talking about special syntax.

This does’t work for me, on 1.1? But it would be nice if Matrix([1,2]) did the obvious thing, currently an error:

julia> Matrix([1,2]') # not Adjoint! 
1×2 Array{Int64,2}:
 1  2

julia> Matrix([1,2])
ERROR: MethodError: 

I defined my own constructor to do that, I didn’t find one in base.

I don’t think using the constructor falls into the “zillion ways” to create a matrix, there should always be one available in my opinion, even though it might be verbose. Then the special syntax comes on top of that to make your life easier in some cases.