Implement indexing by value with atvalue#52
Conversation
|
This is great, thanks for diving in! You're totally right that the error message there is currently unreachable. But I think I'd prefer to keep it, since: I wouldn't read too much into the current definition of About |
|
I've tightened up the definition of One thing to keep in mind is that |
timholy
left a comment
There was a problem hiding this comment.
LGTM, see minor comments.
I do agree that the question of ulp-level accuracy might be important. Perhaps we should consider a ValueTol type as well. We could use searchsorted to find the element; if the returned range is empty then we could check the previous and next one to see if it's within the specified tol.
People might specify the value at quite low precision, e.g., using the values displayed in the compact printing of an array.
src/indexing.jl
Outdated
| # Dimensional axes may be indexed directly by their elements if Non-Real and unique | ||
| # Maybe extend error message to all <: Numbers if Base allows it? | ||
| axisindexes{T<:Real}(::Type{Dimensional}, ax::AbstractVector{T}, idx::T) = error("indexing by axis value is not supported for axes with $(eltype(ax)) elements; use an ClosedInterval instead") | ||
| axisindexes{T<:Real}(::Type{Dimensional}, ax::AbstractVector, idx::T) = |
There was a problem hiding this comment.
This could be just ids::Real, but not a big deal either way.
src/indexing.jl
Outdated
|
|
||
| # Dimensional axes may always be indexed by value if in a Value type wrapper. | ||
| axisindexes(::Type{Dimensional}, ax::AbstractVector, idx::Value) = | ||
| findfirst(ax.==idx.val) |
There was a problem hiding this comment.
findfirst(ax, idx.val) would be more efficient: ax.==idx.val creates a temporary vector the length of ax, whereas findfirst(ax, idx.val) does not allocate.
There was a problem hiding this comment.
This implementation really could be the same as the method on line 136. I'm not sure why I spelled it that way, but I think it had something to do with some types not implementing the comparisons that I needed.
Maybe we spell this as |
af9d6ab to
6e99a30
Compare
|
After a long delay, I have rebased and addressed the comments. I added both |
src/indexing.jl
Outdated
| else # it's zero | ||
| last(idxs) > 0 && abs(ax[last(idxs)] - idx.val) < idx.tol && return last(idxs) | ||
| first(idxs) <= length(ax) && abs(ax[first(idxs)] - idx.val) < idx.tol && return first(idxs) | ||
| return 0 |
There was a problem hiding this comment.
I can defeat this with the following:
julia> using AxisArrays, OffsetArrays
julia> A = AxisArray(OffsetArray([1 2; 3 4], 0:1, 1:2), Axis{:x}([1.0,4.0]), Axis{:y}([2.0,6.1]))
2-dimensional AxisArray{Int64,2,...} with axes:
:x, [1.0, 4.0]
:y, [2.0, 6.1]
And data, a OffsetArrays.OffsetArray{Int64,2,Array{Int64,2}} with indices 0:1×1:2:
1 2
3 4
julia> A[atvalue(5.0)]
1-dimensional AxisArray{Int64,1,...} with axes:
:y, [2.0, 6.1]
And data, a OffsetArrays.OffsetArray{Int64,1,Array{Int64,1}} with indices 1:2:
1
2Perhaps you can kill two birds with one stone (see your comment below about throwing a more informative BoundsError) and throw the error from here?
There was a problem hiding this comment.
I fixed the error you identified and added the ability to throw a BoundsError with a value index. The issue isn't entirely resolved though, as indicated by a @test_broken in the tests.
I've found a few parts of AxisArrays that I haven't touched that don't work as expected when using OffsetArrays, so I'm not sure how much I should attempt to resolve in this PR. Consider the following:
julia> using AxisArrays,OffsetArrays
julia> B = AxisArray(OffsetArray([1,2,3], 2:4), Axis{:junk}([:a, :b, :c]))
1-dimensional AxisArray{Int64,1,...} with axes:
:junk, Symbol[:a, :b, :c]
And data, a OffsetArrays.OffsetArray{Int64,1,Array{Int64,1}} with indices 2:4:
1
2
3
julia> B[:a]
ERROR: BoundsError: attempt to access OffsetArrays.OffsetArray{Int64,1,Array{Int64,1}} with indices 2:4 at index [1]
Stacktrace:
[1] throw_boundserror(::OffsetArrays.OffsetArray{Int64,1,Array{Int64,1}}, ::Tuple{Int64}) at ./abstractarray.jl:433
[2] checkbounds at ./abstractarray.jl:362 [inlined]
[3] getindex at /Users/ajkeller/.julia/v0.6/OffsetArrays/src/OffsetArrays.jl:94 [inlined]
[4] getindex at /Users/ajkeller/.julia/v0.6/AxisArrays/src/indexing.jl:26 [inlined]
[5] getindex(::AxisArrays.AxisArray{Int64,1,OffsetArrays.OffsetArray{Int64,1,Array{Int64,1}},Tuple{AxisArrays.Axis{:junk,Array{Symbol,1}}}}, ::Symbol) at /Users/ajkeller/.julia/v0.6/AxisArrays/src/indexing.jl:111
Perhaps there should be a separate PR that tries to fix these issues, perhaps via translating from standard indices to offset indices using Base.indices? I'm not terribly familiar with the non-standard indexing schemes in Julia so perhaps there's a better way to go.
There was a problem hiding this comment.
Yeah, don't worry too much about that case yet, I suspect #90 is relevant.
|
LGTM, though is the Travis failure on 0.5 due to these changes or something different? |
|
I think the single test failure on Julia 0.5 is because of the following indexing regression in Base on 0.5.2: Julia 0.5.2: That last error should also be an ArgumentError, not a BoundsError. Note that this did not happen in Julia 0.5.1, and appears to be fixed in Julia 0.6.0, although I couldn't find anything on the issue tracker related to this. Let me know how you'd like me to proceed (mark as a broken test for |
|
Very interesting. Yes, not really your problem, but if you can add that broken mark then this seems good to go. |
|
Please hold off on merging until I implement (edit: specifically, |
|
Okay, provided this passes CI, I feel this is good to go. I noticed that there are a few things which are out of date in the README when I was checking the In a future PR I think it would be good to: 1) use a manually seeded random number generator so the examples are exactly reproducible (permitting doc tests in |
|
Thanks extra for the doc updates! |
|
Thanks! |
| @test_throws ArgumentError A[:,6.1] | ||
| if VERSION == v"0.5.2" | ||
| # Cannot compose @test_broken with @test_throws (Julia #21098) | ||
| # A[:,6.1] incorrectly throws a BoundsError instead of an ArgumentError on Julia 0.5.2 |
There was a problem hiding this comment.
I didn't bisect it, but I'm quite certain that it was JuliaLang/julia#19730. We used to check bounds with < before converting indices. Now we convert indices first. This is expected.
There was a problem hiding this comment.
huh? how, this is a problem only on 0.5.2 apparently?
|
Thanks so much @ajkeller34 and @timholy! This is great. :) |
| @test_throws ArgumentError A[1.0f0] | ||
| if VERSION == v"0.5.2" | ||
| # Cannot compose @test_broken with @test_throws (Julia #21098) | ||
| # A[:,6.1] incorrectly throws a BoundsError instead of an ArgumentError on Julia 0.5.2 |
There was a problem hiding this comment.
this would show up as a package regression if I make an 0.5.3, so I'd like to figure out what caused this and whether it was something that should not have been backported
Some comments:
axisindexeswith a second argument of typeT <: Realcan’t be called from withinto_index, sinceReal <: Idx. I have removed the error message. For reals, I think it is more consistent to just let indexing fail than to try and guess if the user was attempting to index by value and throw an error accordingly.Idxvery well. Why allowRealbut onlyAbstractArray{Int}? I’m guessing it’s just complicated to try and getAbstractArray{T<:Real}, or would it be unusual to index that way?atvalue, one could imagine functions likeatvalueapprox,atvaluenearest, etc. I haven't felt a need for them, but it may be worth considering. I guessatvalueas I've defined it will fail if there are small differences in floating point values on the axis from what is requested.