# Why do views of offsetarrays have one-based indexing if bounds are specified?

**URL:** <https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633>\
**Category:** New to Julia\
**Tags:** question\
**Created:** [May 27, 2019, 6:46am UTC](https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633 "2019-05-27T06:46:35Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [May 27, 2019, 6:46am UTC](https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633/1 "2019-05-27T06:46:35Z")

</div>

The index seems to be one-based or otherwise depending on whether I explicitly specify the lower and upper bounds or leave it implicit. Why is this the case?

```julia
julia> a=zeros(-1:1);

julia> b=view(a,-1:1); axes(b)
(Base.OneTo(3),)

julia> b=view(a,:); axes(b)
(Base.IdentityUnitRange(-1:1),)

julia> @which view(a,:)
view(A::AbstractArray, I::Vararg{Any,N}) where N in Base at subarray.jl:153

julia> @which view(a,-1:1)
view(A::AbstractArray, I::Vararg{Any,N}) where N in Base at subarray.jl:153

```

I would have expected the behavior to be identical. Why is there a difference? Is there a way to retain the index information in the view while specifying the bounds?

---

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [May 27, 2019, 10:18am UTC](https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633/2 "2019-05-27T10:18:14Z")

</div>

Evidently this is how OffsetArrays are sliced, as a similar result is obtained without views.

```julia
julia> a=zeros(-1:1)
OffsetArray(::Array{Float64,1}, -1:1) with eltype Float64 with indices -1:1:
 0.0
 0.0
 0.0

julia> axes(a[:])
(Base.IdentityUnitRange(-1:1),)

julia> axes(a[-1:1])
(Base.OneTo(3),)

```

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [May 27, 2019, 10:28am UTC](https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633/3 "2019-05-27T10:28:22Z")

</div>

Indexing in Julia follows† the following rule: if `b = a[idx]` for some vector index `idx`, then `b[j] = a[idx[j]]`. Since `-1:1` has axes of `Base.OneTo(3)`, `b` has the same indexes. This is How It Should Be.

But you can do the following:

```julia
julia> using OffsetArrays

julia> a=zeros(-1:1);

julia> b = a[Base.IdentityUnitRange(-1:1)];

julia> axes(b)
(Base.IdentityUnitRange(-1:1),)

julia> axes(a)
(Base.IdentityUnitRange(-1:1),)

```

* * *

†or _should_ follow, any violations are a bug

---

<div class="post-metadata">

**Author:** ![ryofurue](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ryofurue/32/24531_2.png) [@ryofurue](https://discourse.julialang.org/u/ryofurue)\
**Post date:** [April 22, 2024, 3:53pm UTC](https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633/4 "2024-04-22T15:53:29Z")

</div>

> [@tim.holy](#):
>
> This is How It Should Be.

What was the reasoning behind this decision? Is there a thread to read about it in this forum?

A slice of an OffsetArray loses the offsets, is quite strange (not intuitive). Those who know the `b[j] == a[idx[j]]` rule _can deduce_ the How It Should Be, but you need to invoke the rule because the result is not intuitive. Those who don’t know the rule have no clue.

So, I guess there needs to be the clear discussion of this behavior in the official documentation of OffsetArrays.

Also, using `Base.IdentityUnitRange()` is rather bulky. I wish there were a function, let’s call it `slice()`, to do that: `bb = slice(a, -1:1)` that preserves the indexing.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [April 22, 2024, 5:47pm UTC](https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633/5 "2024-04-22T17:47:00Z")

</div>

> [@ryofurue](#):
>
> I wish there were a function, let’s call it `slice()`, to do that: `bb = slice(a, -1:1)` that preserves the indexing.

That could be an addition to OffsetArrays, I think it is as simple as:

```julia-repl
julia> x = OffsetArray(1:10, -2)
1:10 with indices -1:8

julia> slice(x, range) = OffsetArray(x[range], range)
slice (generic function with 1 method)

julia> y = slice(x, 2:4)
4:6 with indices 2:4

julia> y
4:6 with indices 2:4

```

---

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [April 22, 2024, 5:54pm UTC](https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633/6 "2024-04-22T17:54:24Z")

</div>

This only works with ranges. I had proposed this some years back, but it wasn’t accepted at the time. I’m not sure this is needed in the package, frankly, given that the composition works well. One may define this easily for their own use case.

> <https://github.com/JuliaArrays/OffsetArrays.jl/pull/187>
>
> This PR adds an extra function \`offset\_view\` and macro \`@offset\_view\` that mirro…r \`Base.view\`, except they preserve the indices used for the original indexing. This is achieved by wrapping the view in an \`OffsetArray\`. 
> These functions are complementary \`no\_offset\_view\`
> 
> \`\`\`julia
> julia\> OffsetArrays.@offset\_view (1:10)\[3:4\]
> 3:4 with indices 3:4
> 
> julia\> a = reshape(1:12, 3, 4)
> 3×4 reshape(::UnitRange{Int64}, 3, 4) with eltype Int64:
> 1 4 7 10
> 2 5 8 11
> 3 6 9 12
> 
> julia\> OffsetArrays.@offset\_view a\[:, 3\]
> 3-element OffsetArray(view(reshape(::UnitRange{Int64}, 3, 4), :, 3), 1:3) with eltype Int64 with indices 1:3:
> 7
> 8
> 9
> 
> julia\> OffsetArrays.@offset\_view a\[2:3, 3:4\]
> 2×2 OffsetArray(view(reshape(::UnitRange{Int64}, 3, 4), 2:3, 3:4), 2:3, 3:4) with eltype Int64 with indices 2:3×3:4:
> 8 11
> 9 12
> \`\`\`
> Unfortunately this only works with index types that are compatible with the \`OffsetArray\` constructor, such as \`AbstractUnitRange\`s, \`CartesianIndices\` and such, and doesn't work for \`Vector\`s and other dense index ranges. For example:
> 
> \`\`\`julia
> julia\> OffsetArrays.@offset\_view a\[2:3, \[3,4\]\]
> ERROR: MethodError: Cannot \`convert\` an object of type Array{Int64,1} to an object of type AbstractUnitRange{Int64}
> \`\`\` 
> 
> (The error message may be improved). 
> 
> There is another way to construct such an index-preserving view: by using \`IdOffsetRange\` axes in \`view(A, indices...)\`. Personally I would prefer this, as it returns a \`SubArray\` with axes that are offset, but as #186 show this has approach is still buggy.

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [April 22, 2024, 7:09pm UTC](https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633/7 "2024-04-22T19:09:04Z")

</div>

> [@ryofurue](#):
>
> What was the reasoning behind this decision? Is there a thread to read about it in this forum?

> `b = a[idx]` for some vector index `idx`, then `b[j] = a[idx[j]]`

That’s how regular array indexing works, pre OffsetArrays: if I take `b = a[[3, 5, 11]]`, then `b[1] = a[3]`, right? The reason is that `[3, 5, 11]` has indices of its own, and they are `1:3`. Thus the indices of `b` are `1:3`, i.e., the indices of the index vector become the indices of the result.

So if you want to make arrays-with-offset indices compatible with the overall AbstractArray interface, they have to follow the same rule.

---

<div class="post-metadata">

**Author:** ![ryofurue](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ryofurue/32/24531_2.png) [@ryofurue](https://discourse.julialang.org/u/ryofurue)\
**Post date:** [April 23, 2024, 3:31am UTC](https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633/8 "2024-04-23T03:31:38Z")

</div>

> [@lmiq](#):
>
> `slice(x, range) = OffsetArray(x[range], range)`

But, I don’t know Julia enough to be able to handle “:” and scalar index:

```julia
using OffsetArrays
a = OffsetArray{Float64, 3}(reshape(1:60,3,4,5), -1:1, 0:3, 2:6)
ax1 = axes(a,1)
ax2 = axes(a,2)
ax3 = axes(a,3)
jslice = 2:3
b = a[:, jslice, 4] # Doesn't preserve indexing for jslice.
bb = OffsetArray(a[:, jslice, 4], ax1, jslice) # Re-introduce jslice.
@show typeof(bb)
@show typeof(b)
@show bb[-1,3] # Fine.
@show b[-1,3] # Error

slice(x, ranges...) = OffsetArray(x[ranges...], ranges...)
bbb = slice(a, :, 2:3, 4) # Error

```

What to do when you don’t know the dimensionality of the OffsetArray, when you have “:”, and when one of your index “range” is actually a scalar (`matrix[:,1]` is a 1D vector and so `OffsetArray(matrix[irange,1], irange, 1)` doesn’t work).

So far, my code remains as tedious as the above example.

---

<div class="post-metadata">

**Author:** ![ryofurue](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ryofurue/32/24531_2.png) [@ryofurue](https://discourse.julialang.org/u/ryofurue)\
**Post date:** [April 23, 2024, 3:43am UTC](https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633/9 "2024-04-23T03:43:01Z")

</div>

> [@tim.holy](#):
>
> So if you want to make arrays-with-offset indices compatible with the overall AbstractArray interface, they have to follow the same rule.

Does that mean that a lot of existing code which deals with AbstractArray already relies on the `b[j] == a[idx[j]]` rule? in such a way that if OffsetArrays didn’t obey the rule, significant existing library code would stop working correctly with them?

> [@tim.holy](#):
>
> That’s how regular array indexing works, pre OffsetArrays: if I take `b = a[[3, 5, 11]]`, then `b[1] = a[3]`, right? The reason is that `[3, 5, 11]` has indices of its own, and they are `1:3`. Thus the indices of `b` are `1:3`, i.e., the indices of the index vector become the indices of the result.

Thanks, but I did understand that. I wasn’t asking how the rule makes sense for 1-base arrays. It does makes sense for 1-base arrays. My point is that the rule leads to the unintuitive indexing behavior for OffsetArrays.

All in all, I guess that we should always use `IdentityUnitRange` with OffsetArrays. We don’t do so only because we don’t have a convenient syntax to create an IdentityUnitRange.

---

<div class="post-metadata">

**Author:** ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)\
**Post date:** [April 23, 2024, 6:40am UTC](https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633/10 "2024-04-23T06:40:39Z")

</div>

Considering vectors as functions on finite domains, i.e., mapping indices to values, this is also just function composition:

```julia
b = a ∘ idx
# implies that
b(x) = a(idx(x))

```

Indeed, `keys` and `values` provide the domain and co-domain of a vector:

```julia-repl
julia> using OffsetArrays

julia> a = zeros(-1:1);

julia> showmapping(v) = keys(v) .=> values(v)
showmapping (generic function with 1 method)

julia> showmapping(a)
3-element OffsetArray(::Vector{Pair{Int64, Float64}}, -1:1) with eltype Pair{Int64, Float64} with indices -1:1:
 -1 => 0.0
  0 => 0.0
  1 => 0.0

julia> showmapping(-1:1)
3-element Vector{Pair{Int64, Int64}}:
 1 => -1
 2 => 0
 3 => 1

julia> showmapping(Base.IdentityUnitRange(-1:1))
3-element OffsetArray(::Vector{Pair{Int64, Int64}}, -1:1) with eltype Pair{Int64, Int64} with indices -1:1:
 -1 => -1
  0 => 0
  1 => 1

# and finally the composition
julia> showmapping(a[-1:1])
3-element Vector{Pair{Int64, Float64}}:
 1 => 0.0
 2 => 0.0
 3 => 0.0

```

---

<div class="post-metadata">

**Author:** ![ryofurue](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ryofurue/32/24531_2.png) [@ryofurue](https://discourse.julialang.org/u/ryofurue)\
**Post date:** [April 23, 2024, 7:16am UTC](https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633/11 "2024-04-23T07:16:23Z")

</div>

> [@jishnub](#):
>
> the composition works well

Would you care to elaborate on this? I just want to do the “composition”.

> [@jishnub](#):
>
> One may define this easily for their own use case.

I’d appreciate it if you could show how to define such view function for my cases above? Specifically, how does one define an offset\_view function that can handle

```julia
bb = offset_view(a, :, 2:3, 4)

```

This is my real situation. Since I have’t been able to write such a convenience function myself, my current code is bulky each time I need to take a slice of a multi-dimensional OffestArray.

---

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [April 23, 2024, 8:10am UTC](https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633/12 "2024-04-23T08:10:53Z")

</div>

Depending on the use case, you may use something like

```julia
julia> A = OffsetArray(rand(2,2), 1,1)
2×2 OffsetArray(::Matrix{Float64}, 2:3, 2:3) with eltype Float64 with indices 2:3×2:3:
 0.0829426 0.531739
 0.110745 0.966358

julia> function offset_view(A, inds...)
               view(A, map(to_offset_range, to_indices(A, inds))...)
       end
offset_view (generic function with 1 method)

julia> to_offset_range(r::AbstractUnitRange) = Base.IdentityUnitRange(r)
to_offset_range (generic function with 1 method)

julia> to_offset_range(x) = x
to_offset_range (generic function with 2 methods)

julia> offset_view(A, 2:3, 2:3)
2×2 view(OffsetArray(::Matrix{Float64}, 2:3, 2:3), Base.IdentityUnitRange(2:3), Base.IdentityUnitRange(2:3)) with eltype Float64 with indices 2:3×2:3:
 0.0829426 0.531739
 0.110745 0.966358

julia> offset_view(A, 2:3, 3)
2-element view(OffsetArray(::Matrix{Float64}, 2:3, 2:3), Base.IdentityUnitRange(2:3), 3) with eltype Float64 with indices 2:3:
 0.5317390043544079
 0.9663578290747614

julia> offset_view(A, 2:3, :)
2×2 view(OffsetArray(::Matrix{Float64}, 2:3, 2:3), Base.IdentityUnitRange(2:3), Base.IdentityUnitRange(Base.Slice(OffsetArrays.IdOffsetRange(values=2:3, indices=2:3)))) with eltype Float64 with indices 2:3×Base.Slice(OffsetArrays.IdOffsetRange(values=2:3, indices=2:3)):
 0.0829426 0.531739
 0.110745 0.966358

```

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [April 23, 2024, 8:10am UTC](https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633/13 "2024-04-23T08:10:58Z")

</div>

I don’t think `OffsetArrays` supports `view` directly, but you could just wrap them, as in

```julia
using OffsetArrays

_drop(::Integer, ι...) = (_drop(ι...)...,)
_drop(i1, ι...) = (i1, _drop(ι...)...,)

function offset_view(a::AbstractArray, indices...)
    OffsetArray(view(a, indices...), _drop(indices...)...)
end

```

The `_drop` is required to make sure integer indices are dropped. I did not test this extensively, so there may be horrible corner cases.

---

<div class="post-metadata">

**Author:** ![ryofurue](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ryofurue/32/24531_2.png) [@ryofurue](https://discourse.julialang.org/u/ryofurue)\
**Post date:** [April 24, 2024, 3:31pm UTC](https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633/14 "2024-04-24T15:31:05Z")

</div>

> [@jishnub](#):
>
> Depending on the use case, you may use something like . . .

Thanks! But your code indicates that such a function _should_ be provided by the `OffestArrays` package because your code is too esoteric for an ordinary user to come up with.

So, I disagree with your reasoning that such a function isn’t a useful part of the package because the user can easily write it.

I’m sure that I will _not_ be able to write such a function “Depending on [my] use case”.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [April 24, 2024, 4:52pm UTC](https://discourse.julialang.org/t/why-do-views-of-offsetarrays-have-one-based-indexing-if-bounds-are-specified/24633/15 "2024-04-24T16:52:17Z")

</div>

I mean, it’s just a very different **meaning** of a `view` (and indexing in general).

The “consistent” behavior would mean that `A[2:3]` should return an offset array with indices `2:3`, regardless of the type or indices of `A`. That’s just a different sort of indexing behavior than what Julia has standardized on.
