How to create a 2x1 Array?

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

https://github.com/JuliaLang/julia/issues/7128

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.

;; is for matrix creation, so you can do something like this:

julia> [11;;]
1×1 Matrix{Int64}:
11

julia> [11; 22;;]
2×1 Matrix{Int64}:
11
22

julia> m = Matrix{Int}(undef, 2, 1)
2×1 Matrix{Int64}:
140260574423008
140260574423008

5 Likes