-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
# 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be just ids::Real
, but not a big deal either way.
src/indexing.jl
Outdated
function axisindexes(::Type{Dimensional}, ax::AbstractVector, idx) | ||
idxs = searchsorted(ax, ClosedInterval(idx,idx)) | ||
length(idxs) > 1 && error("more than one datapoint lies on axis value $idx; use an interval to return all values") | ||
idxs[1] | ||
end | ||
|
||
# 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
2
Perhaps 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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/indexing.jl
Outdated
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did anyone bisect this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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:
axisindexes
with a second argument of typeT <: Real
can’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.Idx
very well. Why allowReal
but 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 guessatvalue
as I've defined it will fail if there are small differences in floating point values on the axis from what is requested.