Why doesn’t no_offset_view remove offsets from a transposed OffsetArray ?
Edit: After I posted my initial message, @adienes kindly pointed out that the offsets are removed: -1:2 → 1:4, 0:4 → 1:5 .
My actual problem is that Makie crashes if given an OffsetArray. I replace the example below :
using GLMakie
using OffsetArrays
const nooffs = OffsetArrays.no_offset_view
is = -1:2
js = 0:4
oa = OffsetArray(rand(length(is), length(js)), is, js)
oa_tn = transpose(nooffs(oa))
oa_nt = nooffs(transpose(oa))
@show axes(oa)
@show axes(oa_tn)
@show axes(oa_nt)
# ^^^(OffsetArrays.IdOffsetRange(values=1:5, indices=1:5), OffsetArrays.IdOffsetRange(values=1:4, indices=1:4))
xs = collect(Float64, is)
ys = collect(Float64, js)
contourf(ys, xs, oa_tn) # -> fine
contourf(ys, xs, oa_nt) # -> stack overflow
So my question is how do you remove the “offset-ness” of the array if you don’t know the type of the array you are given?
Should I always use collect in place of no_offset_view ? I’m reluctant to this solution because copying would entail.
it looks like the offsets are removed though?
julia> oa_nt |> axes
(OffsetArrays.IdOffsetRange(values=1:5, indices=1:5), OffsetArrays.IdOffsetRange(values=1:4, indices=1:4))
Ah, thank you! You’ve made me realize that I didn’t state my problem correctly!
The actual problem which I need to solve is that Makie crashes if given an OffsetArray, a known problem.
I’ll rewrite my initial post for clarity. Sorry for the confusion and thank you for your help.
eldee
April 13, 2026, 5:09pm
4
Assuming you just want to (recursively) replace an OffsetArray with the AbstractArray it wraps, you could use something like
remove_offsets(x) = x
remove_offsets(x::OffsetArray) = remove_offsets(parent(x))
remove_offsets(x::Transpose) = transpose(remove_offsets(parent(x)))
julia> oa
4×5 OffsetArray(::Matrix{Float64}, -1:2, 0:4) with eltype Float64 with indices -1:2×0:4:
0.579148 0.491453 0.925471 0.228824 0.217648
0.586635 0.814061 0.21358 0.996261 0.0567608
0.00234711 0.590042 0.677307 0.0479227 0.492514
0.453501 0.441742 0.852529 0.853108 0.725945
julia> remove_offsets(transpose(oa))
5×4 transpose(::Matrix{Float64}) with eltype Float64:
0.579148 0.586635 0.00234711 0.453501
0.491453 0.814061 0.590042 0.441742
0.925471 0.21358 0.677307 0.852529
0.228824 0.996261 0.0479227 0.853108
0.217648 0.0567608 0.492514 0.725945
If you have more complex wrappers than just Transpose, perhaps
Base.delete_method(only(methods(remove_offsets, Tuple{Transpose})))
# You don't have to do this, but it shows that the solution below also handles the Transpose case automatically
remove_parameters(::Type{T}) where T = Core.typename(T).wrapper
# e.g. Transpose{Float64, OffsetMatrix{Float64, Matrix{Float64}}} ↦ Transpose
remove_offsets(x) = x
remove_offsets(x::Array) = x
remove_offsets(x::OffsetArray) = remove_offsets(parent(x))
remove_offsets(x::A) where {A <: AbstractArray} = remove_parameters(A)((remove_offsets(getfield(x, fn)) for fn in fieldnames(A))...)
might work, as long as there are no interfering inner constructors.
julia> remove_offsets(reshape(transpose(oa), (10, 2)))
10×2 reshape(transpose(::Matrix{Float64}), 10, 2) with eltype Float64:
0.579148 0.00234711
0.491453 0.590042
0.925471 0.677307
0.228824 0.0479227
0.217648 0.492514
0.586635 0.453501
0.814061 0.441742
0.21358 0.852529
0.996261 0.853108
0.0567608 0.72594
It does not feel particularly robust, though.