Help implementing `copyto!` for broadcasting

I have a type DataRow. It’s goal is to act like a row of a DataFrame, but standing on its own.

  1. It’s one dimensional
  2. It’s indexable
  3. It iterates through values
  4. It stores values in a Vector{Any} internally

I’m trying to extend in-place broadcasting, via this link.

If r is a DataRow with two elements, I would like

r .= [3, 4]

to update the values of r. I’ve implemented


Base.BroadcastStyle(::Type{<:DataRow}) = Broadcast.Style{DataRow}()

function copyto!(dest::DataRow, bc::Broadcasted{Broadcast.DefaultArrayStyle{1}})
	println("success")
	nothing
end

This feels like the method to implement, but I haven’t yet been able to hit this method

julia> r .= [1, 2]
ERROR: MethodError: no method matching copyto!(::DataRow, ::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1},Tuple{Base.OneTo{Int64}},typeof(identity),Tuple{Array{Int64,1}}})

Overall I’m pretty lost about how to implement broadcasting. Any help is appreciated, particularly if someone could point me to a good implementation in some package that I can copy

1 Like

You know we have implemented it in DataFrames.jl :smiley:. Here is an implementation: https://github.com/JuliaData/DataFrames.jl/blob/master/src/other/broadcasting.jl#L216 (it is more complex than you need but hopefully will be useful as a template). If you have specific questions please let me know.

1 Like

A quick fix that might help is that you should use Base.copyto! unless you explicitly import copyto!.

I think this is correct?

Base.BroadcastStyle(::Type{<:DataRow}) = Broadcast.Style{DataRow}()

function Base.copyto!(r::DataRow, bc::Broadcasted)
	bcf = Broadcast.flatten(bc)
	bcf2 = Base.Broadcast.preprocess(r, bcf)

	for i in 1:length(r)
		r[i] = bcf2[i]
	end
	r
end

EDIT : I have no idea what these preprocess steps are. Can someone help me understand tem?

I am not sure you need to flatten (you are not using any internal structure of bc yet in your code), preprocess is probably needed (to do unaliasing). Now for 1:length(r) is probably safe, but a normal thing would be to use axes(bcf2, 1) (but I think it should be the same in this case as you are not accepting non standard indexing in bcf2 anyway - right?).

2 Likes