Giter Site home page Giter Site logo

intervals.jl's People

Contributors

ararslan avatar cmcaine avatar ericphanson avatar expandingman avatar fchorney avatar glennmoy avatar haberdashpi avatar iamed2 avatar juliatagbot avatar kristofferc avatar mjram0s avatar mzgubic avatar nickrobinson251 avatar nicoleepp avatar omus avatar oxinabox avatar pabloferz avatar rofinn avatar spurll avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

intervals.jl's Issues

Support parsing intervals from strings

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.

Store other endpoint in `AnchoredInterval`

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 AnchoredIntervals 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.

Adding a convert(::Type{T}, ...) where {T} is a source of a large number of invalidations

The method definition here:

# Allows an interval to be converted to a scalar when the set contained by the interval only
# contains a single element.
function Base.convert(::Type{T}, interval::AnchoredInterval{P,T}) where {P,T}
if isclosed(interval) && (sign(P) == 0 || first(interval) == last(interval))
return first(interval)
else
# Remove deprecation in version 2.0.0
depwarn(
"`convert(T, interval::AnchoredInterval{P,T})` is deprecated for " *
"intervals which are not closed with coinciding endpoints. " *
"Use `anchor(interval)` instead.",
:convert,
)
return anchor(interval)
# TODO: For when deprecation is removed
# throw(DomainError(interval, "The interval is not closed with coinciding endpoints"))
end
end

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:

Base.:(==)(a, b::Endpoint) = a == b.endpoint && isclosed(b)
Base.:(==)(a::Endpoint, b) = b == a

Hashing differs between Intervals and AnchoredIntervals

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.

Range indexing fails

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).

Define `isequal`

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.

Delta between `AnchoredInterval`s

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

failure with different types

a = 0.0..0.2
3.0 in a # works
3 in a # fails with ERROR: MethodError: no method matching iterate(::Interval{Float64})

Implement rounding for intervals

We should implement floor, ceil, and round for intervals. Unfortunately there are some open questions around how to support rounding on intervals:

  1. Which endpoint should be rounded? Left, right, both?
  2. Should the span between the endpoints remain consistent?

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).

ERROR: LoadError: LoadError: LoadError: Cannot read stream serialized with a newer version of Julia. Got data version 12 > current version 10

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

Comparisons between `T` and `Endpoint{T}` where `included=false` are wrong

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

Improve Interval parsing performance

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)

Define extrema behaviour

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.

Remove (or rename) `HE` and `HB`

They're pretty mysterious:

  • The methods have surprising behaviour
  • The names are not descriptive names
  • The names don't follow any usual naming/style conventions

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.

Create min/max functions that take bounds into account

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

Extracting Period from Interval

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

Reduce interval storage size

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 Intervals.

Contiguous detection with discrete values

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.

Option to not do bounds checking?

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?).

Generalize Endpoint

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) constructor
  • const First = Left; const Last = Right as left and right may not be the most sensible and these match the first and last functions.

Deprecate `convert(AnchoredInterval{Ending}, ...)` and `convert(AnchoredInterval{Beginning}, ...)`

As discussed in another PR the covert methods using AnchoredInterval{Ending} and Anchored{Beginning} should be dropped as:

  1. Ending / Beginning is not a period
  2. You can't have an instance of AnchoredInterval{Ending} or AnchoredInterval{Beginning}
  3. The invariant convert(T, x)::T should always hold in every convert method

With 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.

Plotting recipe: markers are broken

using Intervals
using Plots
intervals = [Interval{Closed, Closed}(float(x), float(x + 0.5)) for x in 1:11]
plot(intervals, 1:11)

gives the following
image

I suspect what happened is that the markers used to be filtered by NaN positions, and are not anymore. Will give it a go.

behavior if right end is less than left end

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.

Fail instead of re-ordering interval endpoints

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.

Add plotting support for unbounded intervals

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.

Use `<` (and `≪`) instead of `isless`

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)

`print` for `AnchoredInterval` is not ideal for some `ZonedDateTime` cases

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 ZonedDateTimes.

Should we error on `<=` between intervals and non-intervals?

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.

Broadcast on StepRange and Array results in Any eltype

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

intersect does not work with StepRange{<:AnchoredInterval}

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

Define ≈ operator for Intervals

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?

Addition/subtraction for some `AnchoredInterval`s produces unexpected results

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:

  1. HourEnding{T} remains a type alias for AnchoredInterval{Hour(-1), T}, but only the HourEnding constructor will perform the rounding operation
    • This means that HourEnding{DateTime}(now()) will not be rounded (as it's just an alias for AnchoredInterval{Hour(-1), DateTime}), which may lead to confusion
  2. HourEnding and AnchoredInterval both become subtypes of a new AbstractAnchoredInterval (Curt has suggested SpanInterval as a replacement name)
    • This doesn't sit particularly well with me because, aside from the rounding that happens in the constructor, the functionality will be identical

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.

Support missings in Intervals

It would be nice to support missings 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.

`print(::Interval)` can be hard to read

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 ...

Don't overload show(io, ::Type)

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?

Plotting Vertical Intervals

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.

isbounded() and isunbounded() are not exported

Thanks @omus for a quick response on the previous issue. Also, in the API section (Intervals.span — Function) I have two doubts:

  1. The isbounded and isunbounded functions are not exported. Is that the expected behaviour? If so, it will be helpful to mention that in the docs.
  2. Is that trailing ( ` ) intentional in the second snippet?
    Screenshot from 2020-06-29 22-53-41

Feel free to close the issue if this is expected.

`Intervals.union` and `Intervals.union!` don’t give the same output

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))

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.