invenia / intervals.jl Goto Github PK
View Code? Open in Web Editor NEWNon-iterable ranges
License: MIT License
Non-iterable ranges
License: MIT License
It would be very nice to have parse(::Type{Interval{T}}, ::AbstractString)
which could take strings of the form "[1,2)"
. I expect we'll need to provide the element type so that we can determine how we should parse the endpoint values.
One tricky part of this will be to determine where the delimiter is as ,
or ..
could be used in the endpoint value. Possibly in scenarios where it is ambiguous we should require quotes around the value.
As brought up by @rofinn in #51: we may want to store the other endpoint of an anchored interval to avoid having to re-compute the value when calling first
or last
. One issue with pre-computing this value is that currently we can support AnchoredInterval
s where the other-endpoint is not computable. For example:
julia> using Intervals, TimeZones, Base.Dates
julia> x = AnchoredInterval{Day(-1)}(ZonedDateTime(2015,3,9,2,tz"America/Winnipeg"))
Intervals.AnchoredInterval{-1 day,TimeZones.ZonedDateTime}(2015-03-09T02:00:00-05:00, Inclusivity(false, true))
julia> last(x)
2015-03-09T02:00:00-05:00
julia> first(x)
ERROR: NonExistentTimeError: Local DateTime 2015-03-08T02:00:00 does not exist within America/Winnipeg
Stacktrace:
[1] #ZonedDateTime#8(::Bool, ::Type{T} where T, ::DateTime, ::TimeZones.VariableTimeZone) at /Users/omus/.julia/v0.6/TimeZones/src/types.jl:172
[2] TimeZones.ZonedDateTime(::DateTime, ::TimeZones.VariableTimeZone) at /Users/omus/.julia/v0.6/TimeZones/src/types.jl:166
[3] first(::Intervals.AnchoredInterval{-1 day,TimeZones.ZonedDateTime}) at /Users/omus/.julia/v0.6/Intervals/src/anchoredinterval.jl:126
Possibly we can still do some optimizations by lazily computing the other endpoint.
In #82 (review)
we inserted NaN
in between the segments to cause the line to disconnect.
This is a trick @mkborregaard showed me
This causes issues if the the values are not numbers.
E.g. DateTime
s
However, this is not too much a problem as the normal way to plot is with values on y
, and intervals of datetimes on x
Because in
is the wrong function to use in this case.
Related to #1.
Change introduced in: JuliaLang/julia#30200
Originally brought up by @iamed2 in #78 (comment). We should define traits for AbstractInterval
s including: OrderStyle
, ArithmeticStyle
, and RangeStepStyle
(see https://github.com/JuliaLang/julia/blob/master/base/traits.jl).
I believe that OrderStyle
should be defined as:
Base.OrderStyle(::Type{<:AbstractInterval}) = Base.Ordered()
For the other styles I'm less sure and I think we may need to fall back to using the element type.
The method definition here:
Intervals.jl/src/anchoredinterval.jl
Lines 179 to 197 in c37cbb7
is quite bad for latency because it invalidates the convert(::Type{Any}, x)
method in Base which a large number of methods are inferred to use. This is a similar issue to JuliaData/CategoricalArrays.jl#177. The invalidations can be seen via:
julia> using SnoopCompileCore
julia> invalidations = @snoopr using Intervals
julia> using SnoopCompile
julia> trees = invalidation_trees(invalidations);
julia> trees[end]
....
inserting convert(::Type{T}, interval::AnchoredInterval{P, T, L, R} where R<:Intervals.Bounded where L<:Intervals.Bounded) where {P, T} in Intervals at /home/kc/.julia/packages/Intervals/T6hJb/src/anchoredinterval.jl:177 invalidated:
....
358: signature Tuple{typeof(convert), Type{Symbol}, Any} triggered MethodInstance for Base.ImmutableDict{Symbol, Any}(::Base.ImmutableDict{Symbol, Any}, ::Any, ::Any) (101 children)
359: signature Tuple{typeof(convert), Type{Symbol}, Any} triggered MethodInstance for merge(::NamedTuple{(), Tuple{}}, ::Base.Iterators.Pairs) (394 children)
360: signature Tuple{typeof(convert), Type{Union{Dict{String, Any}, Base.TOML.ParserError}}, Any} triggered MethodInstance for recurse_dict!(::Base.TOML.Parser, ::Dict{String, Any}, ::SubArray{String, 1, Vector{String}, Tuple{UnitRange{Int64}}, true}, ::Bool) (536 children)
backedges: 1: superseding convert(::Type{Any}, x) in Base at essentials.jl:204 with MethodInstance for convert(::Type{Any}, ::Any) (1242 children)
These two are also pretty bad:
Lines 148 to 149 in c37cbb7
julia> using Intervals, Dates
julia> a = HE(DateTime(2020,5,27,12))
AnchoredInterval{-1 hour,DateTime}(2020-05-27T12:00:00, Inclusivity(false, true))
julia> b = Interval(DateTime(2020,5,12,11), DateTime(2020,5,12,12), false, true)
Interval{DateTime}(2020-05-12T11:00:00, 2020-05-12T12:00:00, Inclusivity(false, true))
julia> hash(a) == hash(b)
false
Since hashing in Julia is value based and these are representing the same thing they should hash the same.
julia> using Dates, TimeZones, Intervals
julia> st = HE(ZonedDateTime(2014,1,1, tz"America/Winnipeg"))
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2014, 1, 1, tz"America/Winnipeg"), Inclusivity(false, true))
julia> r = st:Hour(1):(st + Day(1))
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2014, 1, 1, tz"America/Winnipeg"), Inclusivity(false, true)):1 hour:AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2014, 1, 2, tz"America/Winnipeg"), Inclusivity(false, true))
julia> collect(r)[1:2]
2-element Array{AnchoredInterval{-1 hour,ZonedDateTime},1}:
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2014, 1, 1, tz"America/Winnipeg"), Inclusivity(false, true))
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2014, 1, 1, 1, tz"America/Winnipeg"), Inclusivity(false, true))
julia> r[1:2]
Error showing value of type StepRangeLen{AnchoredInterval{-1 hour,ZonedDateTime},AnchoredInterval{-1 hour,ZonedDateTime},Hour}:
ERROR: MethodError: Cannot `convert` an object of type Hour to an object of type ZonedDateTime
Closest candidates are:
convert(::Type{T}, ::Interval{T}) where T at /Users/ericdavies/.julia/packages/Intervals/52k6l/src/interval.jl:124
convert(::Type{T}, ::AnchoredInterval{P,T}) where {P, T} at /Users/ericdavies/.julia/packages/Intervals/52k6l/src/anchoredinterval.jl:174
convert(::Type{T}, ::T) where T at essentials.jl:167
...
Stacktrace:
[1] AnchoredInterval{-1 hour,ZonedDateTime}(::Hour) at /Users/ericdavies/.julia/packages/Intervals/52k6l/src/anchoredinterval.jl:79
[2] step(::StepRangeLen{AnchoredInterval{-1 hour,ZonedDateTime},AnchoredInterval{-1 hour,ZonedDateTime},Hour}) at ./range.jl:501
[3] show(::IOContext{REPL.Terminals.TTYTerminal}, ::StepRangeLen{AnchoredInterval{-1 hour,ZonedDateTime},AnchoredInterval{-1 hour,ZonedDateTime},Hour}) at ./range.jl:712
[4] show(::IOContext{REPL.Terminals.TTYTerminal}, ::MIME{Symbol("text/plain")}, ::StepRangeLen{AnchoredInterval{-1 hour,ZonedDateTime},AnchoredInterval{-1 hour,ZonedDateTime},Hour}) at ./show.jl:7
[5] display(::REPL.REPLDisplay{REPL.LineEditREPL}, ::MIME{Symbol("text/plain")}, ::StepRangeLen{AnchoredInterval{-1 hour,ZonedDateTime},AnchoredInterval{-1 hour,ZonedDateTime},Hour}) at /Users/ericdavies/.julia/packages/OhMyREPL/GFHgr/src/output_prompt_overwrite.jl:6
[6] display(::REPL.REPLDisplay, ::Any) at /Users/ericdavies/repos/julia1p2/usr/share/julia/stdlib/v1.2/REPL/src/REPL.jl:136
[7] display(::Any) at ./multimedia.jl:323
Range indexing appears to be taking a StepRange
and making a StepRangeLen
, which we don't handle effectively.
I think this might be a bug in step(::StepRangeLen)
.
Using Intervals
julia> NaN == NaN
false
julia> isequal(NaN, NaN)
true
julia> Interval(NaN, NaN) == Interval(NaN, NaN)
false # This seems like the correct behaviour
julia> isequal(Interval(NaN, NaN), Interval(NaN, NaN))
false # This should return true according to how `==` vs `isequal` work.
We currently define -(::AnchoredInterval, ::AnchoredInterval)
which I'm not sure is implemented correctly:
julia> AnchoredInterval{-1}(6) - AnchoredInterval{1}(6) # No difference between these intervals
0
julia> AnchoredInterval{-1}(6) - AnchoredInterval{-1}(5) # Delta of 1 between the anchors but between the lower/upper bounds the delta is 0. Is this correct?
1
julia> AnchoredInterval{-1}(6) - AnchoredInterval{1}(5) # Same interval but one is in anchor ending and the other is anchor beginning. Seems like this should definitely be 0
1
a = 0.0..0.2
3.0 in a # works
3 in a # fails with ERROR: MethodError: no method matching iterate(::Interval{Float64})
We should implement floor
, ceil
, and round
for intervals. Unfortunately there are some open questions around how to support rounding on intervals:
Let's look at some examples of possible implementations. Let's start with rounding both endpoints:
julia> round(Interval(0.6, 1.6)) # Rounds both endpoints up, span remains the same
Interval{Float64,Closed,Closed}(1.0, 2.0)
julia> round(Interval(0.4, 1.4)) # Rounds both endpoints down, span remains the same
Interval{Float64,Closed,Closed}(0.0, 1.0)
julia> round(Interval(0.6, 1.4)) # Rounds left up and right down, span differs
Interval{Float64,Closed,Closed}(1.0, 1.0)
julia> round(Interval(0.5, 1.5)) # Note: Behaviour due to default of `RoundNearest`
Interval{Float64,Closed,Closed}(0.0, 2.0)
julia> round(Interval(1.5, 2.5))
Interval{Float64,Closed,Closed}(2.0, 2.0)
From looking at these examples rounding one endpoint up and one down definitely doesn't seem right.
Now let's take a look at some other where we floor both endpoints:
julia> floor(Interval(0.6, 1.4)) # Span was 0.8, after flooring is 1.0
Interval{Float64,Closed,Closed}(0.0, 1.0)
julia> floor(Interval(0.1, 1.9)) # Span was 1.8, after flooring is 1.0
Interval{Float64,Closed,Closed}(0.0, 1.0)
After seeing flooring both endpoints in practice I think that the span should probably remain the same after the operation.
This would mean that we should probably only perform round, floor, and ceil on a specified endpoint. Effectively, this just shifts the interval and the span always remains the same. Such an implementation also seems to work well for AnchoredInterval
however we may want to have the option to round based on the anchor (which may be the left or right endpoint).
julia> using Intervals
[ Info: Precompiling Intervals [d8418881-c3e1-53bb-8760-2df7ec849ed5]
ERROR: LoadError: LoadError: LoadError: Cannot read stream serialized with a newer version of Julia.
Got data version 12 > current version 10
Stacktrace:
[1] error(::String) at ./error.jl:33
[2] readheader(::Serialization.Serializer{IOStream}) at /Users/solver/Projects/julia-1.5/usr/share/julia/stdlib/v1.5/Serialization/src/Serialization.jl:715
[3] handle_deserialize(::Serialization.Serializer{IOStream}, ::Int32) at /Users/solver/Projects/julia-1.5/usr/share/julia/stdlib/v1.5/Serialization/src/Serialization.jl:878
[4] deserialize(::Serialization.Serializer{IOStream}) at /Users/solver/Projects/julia-1.5/usr/share/julia/stdlib/v1.5/Serialization/src/Serialization.jl:773
[5] deserialize at /Users/solver/Projects/julia-1.5/usr/share/julia/stdlib/v1.5/Serialization/src/Serialization.jl:760 [inlined]
[6] open(::typeof(Serialization.deserialize), ::String, ::Vararg{String,N} where N; kwargs::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}) at ./io.jl:325
[7] open at ./io.jl:323 [inlined]
[8] (::TimeZones.var"#3#4"{String})() at /Users/solver/.julia/packages/TimeZones/tx0tt/src/types/timezone.jl:50
[9] get!(::TimeZones.var"#3#4"{String}, ::Dict{String,Tuple{Dates.TimeZone,TimeZones.Class}}, ::String) at ./dict.jl:450
[10] Dates.TimeZone(::String, ::TimeZones.Class) at /Users/solver/.julia/packages/TimeZones/tx0tt/src/types/timezone.jl:46 (repeats 2 times)
[11] @tz_str(::LineNumberNode, ::Module, ::Any) at /Users/solver/.julia/packages/TimeZones/tx0tt/src/types/timezone.jl:86
[12] include(::Function, ::Module, ::String) at ./Base.jl:380
[13] include at ./Base.jl:368 [inlined]
[14] include(::String) at /Users/solver/.julia/packages/Intervals/ua9cq/src/Intervals.jl:1
[15] top-level scope at /Users/solver/.julia/packages/Intervals/ua9cq/src/Intervals.jl:29
[16] include(::Function, ::Module, ::String) at ./Base.jl:380
[17] include(::Module, ::String) at ./Base.jl:368
[18] top-level scope at none:2
[19] eval at ./boot.jl:331 [inlined]
[20] eval(::Expr) at ./client.jl:467
[21] top-level scope at ./none:3
in expression starting at /Users/solver/.julia/packages/Intervals/ua9cq/src/interval.jl:153
in expression starting at /Users/solver/.julia/packages/Intervals/ua9cq/src/interval.jl:152
in expression starting at /Users/solver/.julia/packages/Intervals/ua9cq/src/Intervals.jl:29
ERROR: Failed to precompile Intervals [d8418881-c3e1-53bb-8760-2df7ec849ed5] to /Users/solver/.julia/compiled/v1.5/Intervals/ihXRn_qcO3F.ji.
Stacktrace:
[1] error(::String) at ./error.jl:33
[2] compilecache(::Base.PkgId, ::String) at ./loading.jl:1305
[3] _require(::Base.PkgId) at ./loading.jl:1030
[4] require(::Base.PkgId) at ./loading.jl:928
[5] require(::Module, ::Symbol) at ./loading.jl:923
julia> 10 > Intervals.LeftEndpoint(Interval(10, 20, false, false))
true
julia> Intervals.LeftEndpoint(Interval(10, 20, false, false)) > 10 # Should be false
true
julia> 10 > Intervals.LeftEndpoint(Interval(10, 20, false, false)) # Should be false
true
julia> Intervals.LeftEndpoint(Interval(10, 20, false, false)) > 10
true
Interval parsing support was added in #128 using regular expressions. Usually we can get better performance and reduced allocations if we write a lower-level parser instead of using general regular expressions. The current performance is:
julia> using Intervals, BenchmarkTools
julia> @btime parse(Interval{Int}, "[1,2)")
734.102 ns (13 allocations: 656 bytes)
Interval{Int64,Closed,Open}(1, 2)
Currently when running extrema
the following behaviour occurs:
julia> x = [Interval(1,3), Interval(2,6), Interval(3,5)]
3-element Array{Intervals.Interval{Int64},1}:
[1 .. 3]
[2 .. 6]
[3 .. 5]
julia> extrema(x)
(Interval{Int64}(1, 3, Inclusivity(true, true)), Interval{Int64}(3, 5, Inclusivity(true, true)))
I would have expected the answer to have the interval with the smallest left-endpoint and the interval with the largest-right endpoint.
They're pretty mysterious:
I personally find reading code that uses HE
very confusing, especially if HourEnding
is also used.
To be fair, i'm not sure what unsuprising behaviour would be from things named HE
and HB
.
If rounding to the nearest hour needs a nicer interface, i think we can do better than HE
and HB
.
p.s. As a historical note, I've been confused by HE
and HB
, and their description as "pseudoconstructors", since my very first month at Invenia.
It would be nice to be able to get the first and last item within the bounds an Interval to some precision.
Ex.
julia> function min(i::Interval{T, Open}; precision=eps(T)) where T
return first(i) + precision
end
julia> function min(i::Interval{T, Closed}; precision=eps(T)) where T
return first(i)
end
julia> min(Interval{Open, Open}(1.0, 2.0); precision=0.1)
1.1
julia> min(Interval{Closed, Closed}(1.0, 2.0); precision=0.1)
1.0
It would be nice to extract the period from an AnchoredInterval like so:
t = AnchoredInterval{Hour(-1)}(DateTime(2016, 8, 11, 12))
t.anchor.instant.period
> -1 hour
This is an idea I've had for a while on but haven't investigated. Currently Inclusivity
is defined as:
struct Inclusivity
first::Bool
last::Bool
end
This structure uses 2-bytes to store these two booleans but we can theoretically reduce the size down to 2-bits. Unfortunately Julia only allows structures as small as 8-bits/1-byte but this is still an improvement. Potentially this could save a bunch of memory when it comes to storing arrays of Interval
s.
For intervals using discrete units like integers the intervals [1,2]
and [3,4]
should be contiguous (no values can exist between these intervals). Most likely implementing this would require an additional trait so we can increase a type by a single discrete value. For Integer
this would be one
but for DateTime
it would be milliseconds while Date
would be days.
It'd be nice if we could disable first
and last
order checking of arguments for performance reasons (maybe just don't have an inner constructor?).
Something I noticed:
julia> zdt = ZonedDateTime(2018,10,23,14,tz"America/Winnipeg")
2018-10-23T14:00:00-05:00
julia> interval = HourEnding(zdt)
Intervals.AnchoredInterval{-1 hour,TimeZones.ZonedDateTime}(2018-10-23T14:00:00-05:00, Inclusivity(false, true))
julia> zdt <= Intervals.RightEndpoint(interval)
true
julia> zdt == Intervals.RightEndpoint(interval)
false
julia> zdt < Intervals.RightEndpoint(interval)
false
It is strange that ==
isn't working here (edit: fixed by #56). Additionally, these would also be nice:
Endpoint{Intervals.Left}(::AbstractInterval)
constructorconst First = Left; const Last = Right
as left and right may not be the most sensible and these match the first
and last
functions.As discussed in another PR the covert
methods using AnchoredInterval{Ending}
and Anchored{Beginning}
should be dropped as:
Ending
/ Beginning
is not a periodAnchoredInterval{Ending}
or AnchoredInterval{Beginning}
convert(T, x)::T
should always hold in every convert methodWith that said, these convert methods have been very useful for generic testing in test/comparisons.jl
so we should find an alternate way of providing this functionality.
...unless you specify lt=<
.
https://github.com/JuliaMath/IntervalSets.jl
IntervalSets.jl does allow time types but doesn't explicitly handle them. It's unclear whether IntervalSets.jl supports unbounded intervals which we are working to support here (#89).
I would expect that Interval(0, -1)
should either throw an error, or better yet, give an empty interval, but instead it automatically re-orders to Interval(-1, 0)
. This seems dangerous, because if you have something that's computing the right end, and it happens to be less than the left end, you probably either want an error or an empty interval. Would you consider changing this?
Note that this would be analogous to range iterators, for example 0:(-1)
which is an iterator with 0 elements.
Intervals enforces that the first endpoint always precedes the last endpoint:
julia> Interval(1, 2)
Interval{Int64}(1, 2, Inclusivity(true, true))
julia> Interval(2, 1)
Interval{Int64}(1, 2, Inclusivity(true, true))
This behaviour could be confusing when supplying inclusiveness:
julia> x, y = 2, 1;
julia> Interval(x, y, false, true) # Should `false` associated with `x` or the first endpoint?
Interval{Int64}(1, 2, Inclusivity(true, false))
I would suggest we should deprecate this behaviour as it's already a corner case.
The PR which added support for unbounded intervals did not add support for plotting them.
It was briefly looked into and the main issue is that an unbounded interval should print for +/- infinity or typemax
/ typemin
but from some initial experimentation it appears that lines created this way are too large and are just not plotted at all.
The other less pressing issue is that we need a different marker to display an unbounded endpoint. My initial thoughts were to use no marker for unbounded and use custom markers to create shapes that look like square bracket and parenthesis to represent closed/open intervals.
In consulting with @omus and @iamed2, I've determined that we should have implemented <
instead of isless
. The existing implementation of isless
also assumes that an interval is only less than another interval if they are disjoint (which makes it difficult if you want to, say, sort things or do simple min
and max
).
So:
isless
should be stricken<
will be defined such that it doesn't require the intervals to be disjoint≪
(muchless
? ismuchless
? disjoint_less_than
?) will be defined as a < b && isempty(a ∩ b)
(≫
will also have to be defined)julia> interval = AnchoredInterval{Day(1)}(floor(now(tz"America/Winnipeg"), Day(1)))
AnchoredInterval{1 day, TimeZones.ZonedDateTime}(2018-05-01T00:00:00-05:00, Inclusivity(true, false))
julia> string(interval)
"[DB 2018-05-01-05:00)"
This means "day beginning 2018-05-01
in time zone -05:00
", but because the time (00:00
) is not displayed, it's easy to misinterpret the time zone as a time. Probably best to just display the whole thing (rather than selectively leaving off the time) when using ZonedDateTime
s.
Would be nice to have docstrings for these types along with some example documentation.
It can be rather annoying to read code with a Vector{Interval}
as you can end up with things like: first(first(v))
which would be more readable as lower(first(v))
Right now we allow comparing scalars and intervals. For some comparisons this makes sense:
julia> 1 < Interval(2,3) # 1 is below the entire interval
true
julia> 2 < Interval(2,3) # 2 is definitely not below the interval
false
julia> 2 == Interval(2,3) # 2 is definitely not equal to the interval
false
julia> 2 <= Interval(2,3) # a user may expect this to pass
false
For the last comparison there is no reason to do <=
instead of <
as a scalar can never be equal to an interval. Most likely the user meant to do an operation such as:
julia> 2 <= Intervals.RightEndpoint(Interval(2,3))
true
julia> 2 <= Intervals.LeftEndpoint(Interval(2,3))
false
We may want to throw an exception to indicate the problem.
julia> using Intervals # 1.3.2
julia> f(::Type{StepRange}) = AnchoredInterval{-1}(0):AnchoredInterval{-1}(5)
f (generic function with 1 method)
julia> f(::Type{Array}) = [AnchoredInterval{-1}(6)]
f (generic function with 2 methods)
julia> typeof(f(StepRange))
StepRange{AnchoredInterval{-1,Int64,L,R} where R<:Intervals.Bounded where L<:Intervals.Bounded,Int64}
julia> typeof(f(Array))
Array{AnchoredInterval{-1,Int64,Open,Closed},1}
julia> r = broadcast(f, [StepRange, Array])
2-element Array{AbstractArray{T,1} where T,1}:
AnchoredInterval{-1,Int64,Open,Closed}(0):1:AnchoredInterval{-1,Int64,Open,Closed}(5)
AnchoredInterval{-1,Int64,Open,Closed}[AnchoredInterval{-1,Int64,Open,Closed}(6)]
julia> r isa AbstractArray{<:AbstractInterval}
false
With StepRange{Int}
it works as expected:
julia> intersect(1:1:5, 2:1:7)
2:1:5
With StepRange{<:AnchoredInterval}
it does not work:
julia> dt = DateTime(2018, 1, 1);
julia> a = HE(dt):Hour(1):HE(dt + Hour(5));
julia> b = HE(dt + Hour(2)):Hour(1):HE(dt + Hour(7));
julia> intersect(a, b)
ERROR: MethodError: no method matching rem(::Millisecond, ::Hour)
Closest candidates are:
rem(::Any, ::Any, ::RoundingMode{:ToZero}) at div.jl:67
rem(::Any, ::Any, ::RoundingMode{:Down}) at div.jl:68
rem(::Any, ::Any, ::RoundingMode{:Up}) at div.jl:69
...
Stacktrace:
[1] intersect(::StepRange{AnchoredInterval{Hour(-1),DateTime,Open,Closed},Hour}, ::StepRange{AnchoredInterval{Hour(-1),DateTime,Open,Closed},Hour}) at ./range.jl:852
[2] top-level scope at REPL[56]:1
The following works but returns a Vector
rather than a StepRange
:
julia> intersect(collect(a), b)
4-element Array{AnchoredInterval{Hour(-1),DateTime,Open,Closed},1}:
AnchoredInterval{Hour(-1),DateTime,Open,Closed}(DateTime("2018-01-01T02:00:00"))
AnchoredInterval{Hour(-1),DateTime,Open,Closed}(DateTime("2018-01-01T03:00:00"))
AnchoredInterval{Hour(-1),DateTime,Open,Closed}(DateTime("2018-01-01T04:00:00"))
AnchoredInterval{Hour(-1),DateTime,Open,Closed}(DateTime("2018-01-01T05:00:00"))
This is with julia 1.5.2 and Intervals 1.5.0
julia> i1 = Interval{Float64, Open, Closed}(1, 2)
Interval{Float64,Open,Closed}(1.0, 2.0)
julia> i2 = Interval{Float64, Open, Closed}(1, 2 + 1e-10)
Interval{Float64,Open,Closed}(1.0, 2.00001)
julia> i1 ≈ i2
ERROR: MethodError: no method matching isapprox(::Interval{Float64,Open,Closed}, ::Interval{Float64,Open,Closed})
How would this behaviour be defined?
The lower and upper bounds would have to be ≈ equal separately.
The closed/open distinction would be ignored.
If the underlying types don't have ≈ defined an error is thrown?
To me it seems like a natural assumption that
HourEnding
would have inclusivity of false, true
,
and HourBeginning
would have inclusivity of true, false
but I am not sure if I am typical or not.
Arithmetic involving an AnchoredInterval{P, T}
when P isa Period
runs afoul of the rounding code in the constructor, producing counterintuitive results:
julia> he = HourEnding(now())
HourEnding{DateTime}(2018-02-28T18:00:00, Inclusivity(false, true))
julia> he - Minute(30)
HourEnding{DateTime}(2018-02-28T18:00:00, Inclusivity(false, true))
julia> he + Minute(30)
HourEnding{DateTime}(2018-02-28T19:00:00, Inclusivity(false, true))
Rounding will have to be removed from the AnchoredInterval{P, T}
constructor to resolve this issue, but it is nice being able to call HourEnding(now())
without having to explicitly round now()
to the nearest hour.
Of the many possible solutions @omus and I have considered, two seem most promising:
HourEnding{T}
remains a type alias for AnchoredInterval{Hour(-1), T}
, but only the HourEnding
constructor will perform the rounding operation
HourEnding{DateTime}(now())
will not be rounded (as it's just an alias for AnchoredInterval{Hour(-1), DateTime}
), which may lead to confusionHourEnding
and AnchoredInterval
both become subtypes of a new AbstractAnchoredInterval
(Curt has suggested SpanInterval
as a replacement name)
Either way, I'll have to fix the way string(::AnchoredInterval{P, T})
works, because currently it assumes that everything will be rounded when T <: TimeType
.
It would be nice to support missing
s as endpoints in intervals. Right now
if I want to represent a block of data where the endpoint is unknown, I'm stuck using typemax
/typemin
for the missing endpoint.
Most numbers are okay:
julia> println(10.2 .. 12.9)
[10.2..12.9]
But it can get pretty rough:
julia> using TimeZones
julia> println(now(tz"America/Winnipeg") .. now(tz"America/Winnipeg") + Base.Dates.Hour(1))
[2018-04-25T10:32:55.066-05:00..2018-04-25T11:32:55.155-05:00]
Can be solved by putting spaces on either side of the ..
.
This is type treason, as Jeff puts it, and makes Bad Things Happen on 0.7. See JuliaLang/julia#24195. If we simply do away with those methods, the printing is a little less concise, but still reasonable (IMO):
julia> HourBeginning
AnchoredInterval{1 hour,T} where T<:Dates.TimeType
This is in line with how Base prints aliases, e.g.
julia> AbstractVecOrMat
Union{AbstractArray{T,1}, AbstractArray{T,2}} where T
Thoughts?
Recall in #82 i said: noone ever wants vertical intervals, it is always time intervals on the x-asis, and values over those times on the y-axis? I was of-course wrong
People have started using the interval plotting for showing confidance intervals accordings to some statistical tests.
It seems only a mater of time before they want to do so vertically.
In Plotting section of the documentation, there is supposed to be an image here instead of the word plot
:
Thanks @omus for a quick response on the previous issue. Also, in the API section (Intervals.span — Function) I have two doubts:
isbounded
and isunbounded
functions are not exported. Is that the expected behaviour? If so, it will be helpful to mention that in the docs.Feel free to close the issue if this is expected.
Probably a problem specific to arrays of AnchoredIntervals
julia> target
10-element Array{AnchoredInterval{-1 hour,ZonedDateTime},1}:
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2013, 2, 12, 7, tz"UTC"), Inclusivity(false, true))
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2013, 2, 12, 7, tz"UTC"), Inclusivity(false, true))
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2013, 2, 13, 7, tz"UTC"), Inclusivity(false, true))
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2013, 2, 13, 7, tz"UTC"), Inclusivity(false, true))
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2013, 2, 14, 7, tz"UTC"), Inclusivity(false, true))
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2013, 2, 14, 7, tz"UTC"), Inclusivity(false, true))
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2013, 2, 12, 8, tz"UTC"), Inclusivity(false, true))
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2013, 2, 12, 8, tz"UTC"), Inclusivity(false, true))
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2013, 2, 13, 8, tz"UTC"), Inclusivity(false, true))
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2013, 2, 13, 8, tz"UTC"), Inclusivity(false, true))
julia> a = union(target)
3-element Array{AbstractInterval,1}:
Interval{ZonedDateTime}(ZonedDateTime(2013, 2, 12, 6, tz"UTC"), ZonedDateTime(2013, 2, 12, 8, tz"UTC"), Inclusivity(false, true))
Interval{ZonedDateTime}(ZonedDateTime(2013, 2, 13, 6, tz"UTC"), ZonedDateTime(2013, 2, 13, 8, tz"UTC"), Inclusivity(false, true))
Interval{ZonedDateTime}(ZonedDateTime(2013, 2, 14, 6, tz"UTC"), ZonedDateTime(2013, 2, 14, 7, tz"UTC"), Inclusivity(false, true))
julia> b = union!(target)
5-element Array{AnchoredInterval{-1 hour,ZonedDateTime},1}:
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2013, 2, 12, 7, tz"UTC"), Inclusivity(false, true))
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2013, 2, 13, 7, tz"UTC"), Inclusivity(false, true))
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2013, 2, 14, 7, tz"UTC"), Inclusivity(false, true))
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2013, 2, 12, 8, tz"UTC"), Inclusivity(false, true))
AnchoredInterval{-1 hour,ZonedDateTime}(ZonedDateTime(2013, 2, 13, 8, tz"UTC"), Inclusivity(false, true))
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.