Broadcasting and arrays of arrays

I was hoping that this would work for centering some data that is e.g. Array{SVector}

d = [rand(2) for i=1:10]
d - mean(d)

or even the broadcasting version

d .- mean(d)

But they don’t work. I understand why they don’t work, but I’m wondering if it would be possible to have consistent broadcasting rules that would make it work.

For now, I know I can do

d .- [mean(d)]

It is just that with scalar elements, one can write

d = rand(10)
d - mean(d)

I think the way you typically “scalarize” an object is by enclosing it in a Ref so d .- Ref(mean(d))

Thanks, that is interesting. It doesn’t appear to do the right thing, however:

julia> vs = [[i, i+1] for i=1:10];

julia> mean(vs .- Ref(mean(vs)))
2-element Array{Float64,1}:
 0.0
 1.0

julia> mean(vs .- [mean(vs)])
2-element Array{Float64,1}:
 0.0
 0.0

julia> [zeros(2)] .+ Ref(mean(vs))
1-element Array{Array{Float64,1},1}:
 [5.5, 5.5]

julia> [zeros(2)] .+ [mean(vs)]
1-element Array{Array{Float64,1},1}:
 [5.5, 6.5]

I’m honestly not sure how Ref is supposed to work but I think the reason it doesn’t work here boils down to

julia> x = Ref([1, 2])
Base.RefArray{Int64,Array{Int64,1},Void}([1, 2], 1, nothing)

julia> x[]
1

It does work to instead use Base.RefValue but it’s unexported and undocumented, probably for a reason. Some other options, with or without broadcasting:

[x - mean(d) for x in d]
map(x -> x - mean(d), d)
(x -> x - mean(d)).(d)

Your other options all work, but are going to re-compute mean(d) for every element.

I’m also super surprised by the behavior of Ref in this case, but I guess RefArrays behave differently in broadcast? In particular, it looks like a RefArray behaves like a scalar but only using the first element of the contained array. A 1-element tuple has the behavior I would expect, as does wrapping the array in a 1-element array.

julia> [1, 2] .- ([1, 2],)
2-element Array{Array{Int64,1},1}:
 [0, -1]
 [1, 0] 

julia> [1, 2] .- Ref([1, 2])  # behaves like [1, 2] .- [1]
2-element Array{Int64,1}:
 0
 1

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

this is surprising enough that I might consider it a broadcast bug.

Thanks for the replies. I agree that it’s surprising and can’t think of any reason why it should be like that. We’ll see: https://github.com/JuliaLang/julia/issues/24880

I still hope there is a way to make any expression
A - a or A .- a work if eltype(A) == typeof(a), even if typeof(a) is an array.

1 Like

Also d .- (mean(d),) works.

I just noticed StaticArrays has Scalar, which seems to do the right thing even for non-StaticArray types:

so yet another option, in the spirit of Ref is: d .- Scalar(mean(d))

3 Likes

Interesting. Having Scalar in Base would make sense.

1 Like