`vcat([], [1 2])` # OK. `vcat([], [1 "2"])` # Not OK

vcat([], [1 2]) # OK
vcat([], [1 "2"]) # Not OK: ERROR: ArgumentError: number of columns of each array must match (got (1, 2))

[[];[1 2]] # OK
[[];[1 "2"]] # Not OK: ERROR: ArgumentError: number of columns of each array must match (got (1, 2))

cat([], cat([], [1 2]; dims=1)) # OK
cat([], cat([], [1 "2"]; dims=1)) # OK
help?> vcat
...

  vcat(A...)

  Concatenate arrays or numbers vertically. Equivalent to cat(A...; dims=1), and to the syntax [a; b; c].

...

It’s not accurate to say vcat(A...) is equivalent to cat(A...; dims=1).

2 Likes

Hm?

julia> vcat([], [1 2])
ERROR: DimensionMismatch: number of columns of each array must match (got (1, 2))

and

julia> [[];[1 2]]
ERROR: DimensionMismatch: number of columns of each array must match (got (1, 2))

How do you keep finding these things? cating empty untyped arrays [] to other stuff seems like a right code smell to me, maybe you should think about changing your approach?

1 Like

Thanks. Maybe it is due to Julia version as the post above. Sorry… I was just looking for an approach to recording some process results in a loop. May I ask for some good advice on the following code?

rec = []
for i = 1:10
    rec = cat(rec, rand(3)'; dims=1)
end
rec

Thanks again. :handshake: :handshake:

Following the docstring, these should agree, no?

julia> A = [[], [1 "2"]]
2-element Vector{Array{Any}}:
 []
 [1 "2"]


julia> cat(A...; dims=1)
1×2 Matrix{Any}:
 1  "2"

julia> vcat(A...)
ERROR: DimensionMismatch: number of columns of each array must match (got (1, 2))
Stacktrace:
     ⋮ internal @ Base
 [3] vcat(::Vector{Any}, ::Matrix{Any})
   @ Base ./abstractarray.jl:1691


2 Likes

I think more context would be helpful. What types are the values in each row? What are you doing with them afterwards?

To concatenate 10 rows of three you can do

julia> mapreduce(vcat, 1:10) do _
           rand(3)'
       end
10×3 Matrix{Float64}:
 0.179634  0.0473286  0.270071
 0.222936  0.761969   0.500733
 0.186272  0.480777   0.0699533
 0.760787  0.509991   0.615817
 0.336471  0.967885   0.307789
 0.939159  0.396305   0.153489
 0.708447  0.370107   0.506113
 0.235929  0.621101   0.791309
 0.549194  0.567354   0.754604
 0.806734  0.507155   0.507556

I’m erroring on vcat([], [1 2]) on v1.10.2, what version is it not erroring on? Also unlike vcat and [[]; [1 2]], cat([], [1 2]; dims=1) seems to ignore [] and does not error. cat([1], [1 2]; dims=1) does throw DimensionMismatch.

julia> versioninfo()
Julia Version 1.10.0
Commit 3120989f39b (2023-12-25 18:01 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 24 × AMD Ryzen 9 3900XT 12-Core Processor
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-15.0.7 (ORCJIT, znver2)
  Threads: 17 on 24 virtual cores
Environment:
  JULIA_NUM_THREADS = 12

Yes, they do.

I just want to write push row vectors to an empty matrix line by line in a loop so that I can analyze it afterwards.

The standard approach to record results in a loop is

rec = []   # Using a correctly typed container is better if performance matters at all.
for i = 1:10
    push!(rec, rand(3))
end

If you need to convert this to a 10×3 matrix for later processing you can do so with

stack(rec, dims = 1)
1 Like

Mine is Julia Version 1.9.0.

Thank you very much. A standard approach in Julia is just what I need.

I can replicate [[], [1,2]], vcat([], [1 2]). and cat([], [1,2]; dims=1) not throwing an error on both v1.9.0 and v1.9.4. [[1]; [1 2]], vcat([1], [1 2]), and cat([1], [1 2]; dims=1) throws an error on both versions. I don’t know a way to automatically test a call across all versions so I won’t do that.

This discrepancy between 1.9 and 1.10 should be an issue if it wasn’t already. Presumably someone had changed vcat and [A...] to not ignore [] anymore. Good catch.

1 Like

Hi, everyone! I am still wondering if there exists more convenient method if I want to construct a matrix row by row from an empty array in a for loop, for example:

# This seems convenient except I have to define the first (two) row outside the loop:
A = rand(2,3)
for i = 1:9
    A = [A; rand(2,3)]
end

# This is desired (isn't it?) but it doesn't work for all.
A = []
for i = 1:10
    A = [A; rand(2,3)]
end

This may be a stopgap for me:

A =[]
for i = 1:10
    push!(A, rand(2,3))
end
A = cat(A...; dims=1) # or A = vcat(A...)

Just use stack unless you need to support Julia versions before 1.9.

But it doesn’t seem to work as I wish:

julia> A = []
Any[]

julia> push!(A, rand(2,3))
1-element Vector{Any}:
 [0.40938348392403623 0.8614872953758306 0.46182688181820797; 0.518159503368555 0.019077942107566304 0.09225241116879213]

julia> push!(A, rand(2,3))
2-element Vector{Any}:
 [0.40938348392403623 0.8614872953758306 0.46182688181820797; 0.518159503368555 0.019077942107566304 0.09225241116879213]
 [0.876546512326307 0.04790814863432025 0.5461966823100315; 0.8829414621366221 0.727703387550281 0.8668757411333791]

julia> stack(A, dims = 1)
2×2×3 Array{Float64, 3}:
[:, :, 1] =
 0.409383  0.51816
 0.876547  0.882941

[:, :, 2] =
 0.861487   0.0190779
 0.0479081  0.727703

[:, :, 3] =
 0.461827  0.0922524
 0.546197  0.866876

How about:

tmpA = Float64[]
for i = 1:10
    append!(tmpA, vec(rand(2,3)))
end
A = reshape(tmpA, :, 3)

This method relegates the memory allocation to the Vector (or Memory in 1.11) code. The data undergoes less copying and should therefore be more efficient. Admittedly, it isn’t the most natural way to express the desired output.

1 Like

reduce(vcat, A) is more efficient than splatting for a variable length container, especially if there are lots of rows.

You could do e.g. A = zeros(0,3) instead of A = [] to start with an empty A that has the correct number of columns (and the correct type).

However, this approach is pretty inefficient because it copies to a new array on every loop iteration — in fact, if you think about it, you should deduce that the cost grows with the square of the number of rows.

If you can compute the number of rows in advance, it’s better to pre-allocate the whole matrix A and then fill it in as desired, e.g.

N = 10
A = Matrix{Float64}(undef, 2N, 3) # pre-allocate
for i = 1:N
    A[2i-1:2i, :] = rand(2,3)
    # or better yet work in-place: @views rand!(A[2i-1:2i, :])
end
1 Like