From 3d0c372b79d268dbec5a60c272dfdecf0cae34b5 Mon Sep 17 00:00:00 2001 From: = <=> Date: Mon, 29 Dec 2025 16:43:38 +0530 Subject: [PATCH 1/4] Added docstrings to exported functions! --- src/apply.jl | 61 +++++++++++++++++++++++++++++++++++++++++++++++ src/basemisc.jl | 10 ++++---- src/broadcast.jl | 2 +- src/combine.jl | 21 ++++++++++++++-- src/modify.jl | 15 +++++++----- src/readwrite.jl | 10 ++++++++ src/retime.jl | 5 ++++ src/split.jl | 33 +++++++++++++++++++++++++- src/timearray.jl | 62 ++++++++++++++++++++++++++---------------------- 9 files changed, 175 insertions(+), 44 deletions(-) diff --git a/src/apply.jl b/src/apply.jl index 2acffa52..ce124dbb 100644 --- a/src/apply.jl +++ b/src/apply.jl @@ -1,11 +1,27 @@ import Base: +, - import Base.diff +""" + +(ta::TimeArray) + +Element-wise unary plus for a `TimeArray`. +""" (+)(ta::TimeArray) = .+ta + +""" + -(ta::TimeArray) + +Element-wise unary minus for a `TimeArray`. +""" (-)(ta::TimeArray) = .-ta ###### lag, lead ################ +""" + lag(ta::TimeArray{T,N}, n::Int=1; padding::Bool=false, period::Int=0) + +Lag a `TimeArray` by `n` periods. Optionally pad with NaN or use the deprecated `period` keyword. +""" function lag(ta::TimeArray{T,N}, n::Int=1; padding::Bool=false, period::Int=0) where {T,N} if period != 0 @warn("the period kwarg is deprecated, use lag(ta::TimeArray, period::Int) instead") @@ -26,6 +42,11 @@ function lag(ta::TimeArray{T,N}, n::Int=1; padding::Bool=false, period::Int=0) w return ta end # lag +""" + lead(ta::TimeArray{T,N}, n::Int=1; padding::Bool=false, period::Int=0) + +Lead a `TimeArray` by `n` periods. Optionally pad with NaN or use the deprecated `period` keyword. +""" function lead(ta::TimeArray{T,N}, n::Int=1; padding::Bool=false, period::Int=0) where {T,N} if period != 0 @warn( @@ -49,6 +70,11 @@ end # lead ###### diff ##################### +""" + diff(ta::TimeArray, n::Int=1; padding::Bool=false, differences::Int=1) + +Difference a `TimeArray` by `n` periods, optionally multiple times (`differences`). +""" function diff(ta::TimeArray, n::Int=1; padding::Bool=false, differences::Int=1) cols = colnames(ta) for d in 1:differences @@ -60,6 +86,11 @@ end # diff ###### percentchange ############ +""" + percentchange(ta::TimeArray, returns::Symbol=:simple; padding::Bool=false, method::AbstractString="") + +Compute percent change of a `TimeArray`. Use `:simple` or `:log` returns. +""" function percentchange( ta::TimeArray, returns::Symbol=:simple; padding::Bool=false, method::AbstractString="" ) @@ -158,6 +189,11 @@ end ###### upto ##################### +""" + upto(f, ta::TimeArray{T,1}) + +Apply function `f` cumulatively up to each index of a 1D `TimeArray`. +""" function upto(f, ta::TimeArray{T,1}) where {T} vals = zero(values(ta)) for i in 1:length(vals) @@ -166,6 +202,11 @@ function upto(f, ta::TimeArray{T,1}) where {T} return TimeArray(timestamp(ta), vals, colnames(ta), meta(ta)) end +""" + upto(f, ta::TimeArray{T,2}) + +Apply function `f` cumulatively up to each index of a 2D `TimeArray` (column-wise). +""" function upto(f, ta::TimeArray{T,2}) where {T} vals = zero(values(ta)) for i in 1:size(vals, 1), j in 1:size(vals, 2) @@ -176,12 +217,22 @@ end ###### basecall ################# +""" + basecall(ta::TimeArray, f::Function; cnames=colnames(ta)) + +Apply function `f` to the values of a `TimeArray` and return a new `TimeArray`. +""" function basecall(ta::TimeArray, f::Function; cnames=colnames(ta)) return TimeArray(timestamp(ta), f(values(ta)), cnames, meta(ta)) end ###### uniform observations ##### +""" + uniformspaced(ta::TimeArray) + +Check if the timestamps of a `TimeArray` are uniformly spaced. +""" function uniformspaced(ta::TimeArray) gap1 = timestamp(ta)[2] - timestamp(ta)[1] i, n, is_uniform = 2, length(ta), true @@ -192,6 +243,11 @@ function uniformspaced(ta::TimeArray) return is_uniform end # uniformspaced +""" + uniformspace(ta::TimeArray{T,N}) + +Return a new `TimeArray` with a uniform time grid, filling missing values with NaN. +""" function uniformspace(ta::TimeArray{T,N}) where {T,N} min_gap = minimum(timestamp(ta)[2:end] - timestamp(ta)[1:(end - 1)]) newtimestamp = timestamp(ta)[1]:min_gap:timestamp(ta)[end] @@ -205,6 +261,11 @@ end # uniformspace ###### dropnan #################### +""" + dropnan(ta::TimeArray, method::Symbol=:all) + +Drop rows from a `TimeArray` where values are NaN. Use `:all` or `:any` for method. +""" function dropnan(ta::TimeArray, method::Symbol=:all) return if method == :all ta[findall(reshape(values(any(.!isnan.(ta); dims=2)), :))] diff --git a/src/basemisc.jl b/src/basemisc.jl index 595199e9..18fa2029 100644 --- a/src/basemisc.jl +++ b/src/basemisc.jl @@ -37,12 +37,10 @@ macro _mapbase(sig::Expr, imp::Expr) doc = " $sig" fdef = Expr(:function, sig, fbody) - return esc( - quote - $doc - $fdef - end, - ) + return esc(quote + $doc + $fdef + end) end # Cumulative functions diff --git a/src/broadcast.jl b/src/broadcast.jl index 0a7bd59e..7b597210 100644 --- a/src/broadcast.jl +++ b/src/broadcast.jl @@ -13,7 +13,7 @@ Base.broadcastable(x::AbstractTimeSeries) = x Base.Broadcast.instantiate(bc::Broadcasted{<:TimeArrayStyle}) = # skip the default axes checking - Broadcast.flatten(bc) +Broadcast.flatten(bc) function Base.copy(bc′::Broadcasted{<:TimeArrayStyle}) tas = find_ta(bc′) diff --git a/src/combine.jl b/src/combine.jl index beec8692..6a63dced 100644 --- a/src/combine.jl +++ b/src/combine.jl @@ -109,6 +109,11 @@ end # hcat ########################## +""" + hcat(x::TimeArray, y::TimeArray) + +Horizontally concatenate two `TimeArray` objects with matching timestamps and columns. +""" function hcat(x::TimeArray, y::TimeArray) tsx = timestamp(x) tsy = timestamp(y) @@ -122,6 +127,11 @@ function hcat(x::TimeArray, y::TimeArray) return TimeArray(tsx, [values(x) values(y)], [colnames(x); colnames(y)], m) end +""" + hcat(x::TimeArray, y::TimeArray, zs::Vararg{TimeArray}) + +Horizontally concatenate multiple `TimeArray` objects. +""" hcat(x::TimeArray, y::TimeArray, zs::Vararg{TimeArray}) = hcat(hcat(x, y), zs...) # collapse ###################### @@ -206,9 +216,11 @@ collapse(ta, month, last) collapse(ta, month, last, mean) ``` """ -collapse( +function collapse( ta::TimeArray, period::Period, timestamp::Function, value::Function=timestamp; kw... -) = collapse(ta, x -> floor(x, period), timestamp, value; kw...) +) + collapse(ta, x -> floor(x, period), timestamp, value; kw...) +end # vcat ###################### @@ -266,6 +278,11 @@ end # map ###################### +""" + map(f, ta::TimeArray{T,N}) + +Apply function `f` to each row of a `TimeArray`, returning a new `TimeArray`. +""" @generated function map(f, ta::TimeArray{T,N}) where {T,N} input_val = (N == 1) ? :(values(ta)[i]) : :(vec(values(ta)[i, :])) output_val = (N == 1) ? :(vals[i]) : :(vals[i, :]) diff --git a/src/modify.jl b/src/modify.jl index 6088adcb..3d0585f4 100644 --- a/src/modify.jl +++ b/src/modify.jl @@ -49,12 +49,15 @@ for f in [:rename, :rename!] @eval begin $f(ta::TimeArray, colnames::Symbol) = $f(ta, [colnames]) $f(ta::TimeArray) = throw(MethodError($f, (ta,))) - $f(ta::TimeArray, pairs::Pair{Symbol,Symbol}...) = - $f(ta, _mapcol(colnames(ta), pairs)) - $f(f::Base.Callable, ta::TimeArray, colnametyp::Type{Symbol}=Symbol) = - $f(ta, map(f, colnames(ta))) - $f(f::Base.Callable, ta::TimeArray, colnametyp::Type{String}) = - $f(Symbol ∘ f ∘ string, ta) + $f(ta::TimeArray, pairs::Pair{Symbol,Symbol}...) = $f( + ta, _mapcol(colnames(ta), pairs) + ) + $f(f::Base.Callable, ta::TimeArray, colnametyp::Type{Symbol}=Symbol) = $f( + ta, map(f, colnames(ta)) + ) + $f(f::Base.Callable, ta::TimeArray, colnametyp::Type{String}) = $f( + Symbol ∘ f ∘ string, ta + ) end end diff --git a/src/readwrite.jl b/src/readwrite.jl index 4e1a7a94..383fcd44 100644 --- a/src/readwrite.jl +++ b/src/readwrite.jl @@ -1,5 +1,10 @@ ###### readtimearray ############ +""" + readtimearray(source; delim=',', meta=nothing, format="", header=true) + +Read a CSV or delimited file into a `TimeArray`. +""" function readtimearray( source; delim::Char=',', meta=nothing, format::AbstractString="", header::Bool=true ) @@ -44,6 +49,11 @@ function insertNaN(aa::Array{Any,N}) where {N} return convert(Array{Float64,N}, aa) end +""" + writetimearray(ta::TimeArray, fname::AbstractString; delim=',', format="", header=true) + +Write a `TimeArray` to a CSV or delimited file. +""" function writetimearray( ta::TimeArray, fname::AbstractString; diff --git a/src/retime.jl b/src/retime.jl index 1d0a972f..a2cdbbbe 100644 --- a/src/retime.jl +++ b/src/retime.jl @@ -56,6 +56,11 @@ _toExtrapolationMethod(::Val{:missing}) = MissingExtrapolate() _toExtrapolationMethod(::Val{:nan}) = NaNExtrapolate() _toExtrapolationMethod(x::ExtrapolationMethod) = x +""" + retime(ta, new_dt::Dates.Period; kwargs...) + +Resample or align a `TimeArray` to new timestamps or frequency. +""" function retime(ta, new_dt::Dates.Period; kwargs...) new_timestamps = floor(timestamp(ta)[1], new_dt):new_dt:floor(timestamp(ta)[end], new_dt) diff --git a/src/split.jl b/src/split.jl index a8d3eb17..c1dd7d6f 100644 --- a/src/split.jl +++ b/src/split.jl @@ -1,5 +1,10 @@ # when ############################ +""" + when(ta::TimeArray, period::Function, t::Integer) + +Return a subset of `TimeArray` where the period function applied to timestamps equals `t`. +""" function when(ta::TimeArray, period::Function, t::Integer) return ta[findall(period.(timestamp(ta)) .== t)] end @@ -7,6 +12,11 @@ when(ta::TimeArray, period::Function, t::String) = ta[findall(period.(timestamp( # from, to ###################### +""" + from(ta::TimeArray{T,N,D}, d::D) + +Return the part of `TimeArray` from the first timestamp >= `d`. +""" function from(ta::TimeArray{T,N,D}, d::D) where {T,N,D} return if length(ta) == 0 ta @@ -19,6 +29,11 @@ function from(ta::TimeArray{T,N,D}, d::D) where {T,N,D} end end +""" + to(ta::TimeArray{T,N,D}, d::D) + +Return the part of `TimeArray` up to the last timestamp <= `d`. +""" function to(ta::TimeArray{T,N,D}, d::D) where {T,N,D} return if length(ta) == 0 ta @@ -42,10 +57,20 @@ end ###### findwhen ################# +""" + findwhen(ta::TimeArray{Bool,1}) + +Return timestamps where the values of a boolean `TimeArray` are true. +""" findwhen(ta::TimeArray{Bool,1}) = timestamp(ta)[findall(values(ta))] ###### head, tail ########### +""" + head(ta::TimeArray{T,N}, n::Int=6) + +Return the first `n` rows of a `TimeArray`. +""" @generated function head(ta::TimeArray{T,N}, n::Int=6) where {T,N} new_values = (N == 1) ? :(values(ta)[1:n]) : :(values(ta)[1:n, :]) @@ -55,6 +80,11 @@ findwhen(ta::TimeArray{Bool,1}) = timestamp(ta)[findall(values(ta))] end end +""" + tail(ta::TimeArray{T,N}, n::Int=6) + +Return the last `n` rows of a `TimeArray`. +""" @generated function tail(ta::TimeArray{T,N}, n::Int=6) where {T,N} new_values = (N == 1) ? :(values(ta)[start:end]) : :(values(ta)[start:end, :]) @@ -81,8 +111,9 @@ Split `data` by `period` function, returns a vector of `TimeSeries.TimeArray`. - `data::TimeSeries.TimeArray`: Data to split - `period::Function`: Function, e.g. `Dates.day` that is used to split the `data`. """ -Base.split(data::TimeSeries.TimeArray, period::Function) = +function Base.split(data::TimeSeries.TimeArray, period::Function) Iterators.map(i -> data[i], _split(TimeSeries.timestamp(data), period)) +end function _split(ts::AbstractVector{D}, period::Function) where {D<:TimeType} m = length(ts) diff --git a/src/timearray.jl b/src/timearray.jl index 351b44ca..2e1e6dc6 100644 --- a/src/timearray.jl +++ b/src/timearray.jl @@ -237,8 +237,8 @@ end io::IO, ::MIME"text/plain", ta::TimeArray; - allrows=!get(io, :limit, false), - allcols=!get(io, :limit, false), + allrows=(!get(io, :limit, false)), + allcols=(!get(io, :limit, false)), ) nrow = size(values(ta), 1) ncol = size(values(ta), 2) @@ -278,8 +278,8 @@ else io::IO, ::MIME"text/plain", ta::TimeArray; - allrows=!get(io, :limit, false), - allcols=!get(io, :limit, false), + allrows=(!get(io, :limit, false)), + allcols=(!get(io, :limit, false)), ) nrow = size(values(ta), 1) ncol = size(values(ta), 2) @@ -301,8 +301,8 @@ else row_label_column_alignment=:r, column_label_alignment=:l, continuation_row_alignment=:c, - fit_table_in_display_horizontally=!allcols, # replaced crop keyword for v3 of PrettyTables - fit_table_in_display_vertically=!allrows, # replaced crop keyword for v3 of PrettyTables + fit_table_in_display_horizontally=(!allcols), # replaced crop keyword for v3 of PrettyTables + fit_table_in_display_vertically=(!allrows), # replaced crop keyword for v3 of PrettyTables vertical_crop_mode=:middle, ) end @@ -318,27 +318,34 @@ getindex(ta::TimeArray) = throw(BoundsError(typeof(ta), [])) # single row @propagate_inbounds getindex(ta::TimeArray, n::Integer) = # avoid conversion to column vector - TimeArray(timestamp(ta)[n], values(ta)[n:n, :], colnames(ta), meta(ta)) +TimeArray( + timestamp(ta)[n], values(ta)[n:n, :], colnames(ta), meta(ta) +) # single row 1d -@propagate_inbounds getindex(ta::TimeArray{T,1}, n::Integer) where {T} = - TimeArray(timestamp(ta)[n], values(ta)[[n]], colnames(ta), meta(ta)) +@propagate_inbounds getindex(ta::TimeArray{T,1}, n::Integer) where {T} = TimeArray( + timestamp(ta)[n], values(ta)[[n]], colnames(ta), meta(ta) +) # range of rows -@propagate_inbounds getindex(ta::TimeArray, r::UnitRange{<:Integer}) = - TimeArray(timestamp(ta)[r], values(ta)[r, :], colnames(ta), meta(ta)) +@propagate_inbounds getindex(ta::TimeArray, r::UnitRange{<:Integer}) = TimeArray( + timestamp(ta)[r], values(ta)[r, :], colnames(ta), meta(ta) +) # range of 1d rows -@propagate_inbounds getindex(ta::TimeArray{T,1}, r::UnitRange{<:Integer}) where {T} = - TimeArray(timestamp(ta)[r], values(ta)[r], colnames(ta), meta(ta)) +@propagate_inbounds getindex(ta::TimeArray{T,1}, r::UnitRange{<:Integer}) where {T} = TimeArray( + timestamp(ta)[r], values(ta)[r], colnames(ta), meta(ta) +) # array of rows -@propagate_inbounds getindex(ta::TimeArray, a::AbstractVector{<:Integer}) = - TimeArray(timestamp(ta)[a], values(ta)[a, :], colnames(ta), meta(ta)) +@propagate_inbounds getindex(ta::TimeArray, a::AbstractVector{<:Integer}) = TimeArray( + timestamp(ta)[a], values(ta)[a, :], colnames(ta), meta(ta) +) # array of 1d rows -@propagate_inbounds getindex(ta::TimeArray{T,1}, a::AbstractVector{<:Integer}) where {T} = - TimeArray(timestamp(ta)[a], values(ta)[a], colnames(ta), meta(ta)) +@propagate_inbounds getindex(ta::TimeArray{T,1}, a::AbstractVector{<:Integer}) where {T} = TimeArray( + timestamp(ta)[a], values(ta)[a], colnames(ta), meta(ta) +) # single column by name @propagate_inbounds function getindex(ta::TimeArray, s::Symbol) @@ -348,15 +355,12 @@ end # array of columns by name @propagate_inbounds getindex(ta::TimeArray, ss::Symbol...) = getindex(ta, collect(ss)) -@propagate_inbounds getindex(ta::TimeArray, ss::Vector{Symbol}) = - TimeArray(ta; values=values(ta)[:, map(s -> findcol(ta, s), ss)], colnames=ss) +@propagate_inbounds getindex(ta::TimeArray, ss::Vector{Symbol}) = TimeArray( + ta; values=values(ta)[:, map(s -> findcol(ta, s), ss)], colnames=ss +) # ta[rows, cols] -@propagate_inbounds getindex( - ta::TimeArray, - rows::Union{AbstractVector{<:Integer},Colon}, - cols::AbstractVector{Symbol}, -) = TimeArray( +@propagate_inbounds getindex(ta::TimeArray, rows::Union{AbstractVector{<:Integer},Colon}, cols::AbstractVector{Symbol}) = TimeArray( ta; timestamp=timestamp(ta)[rows], values=values(ta)[rows, map(s -> findcol(ta, s), cols)], @@ -371,8 +375,9 @@ end @propagate_inbounds getindex(ta::TimeArray, rows, col::Symbol) = getindex(ta, rows, [col]) # ta[n, col] -@propagate_inbounds getindex(ta::TimeArray, n::Integer, col::Symbol) = - getindex(ta, [n], [col]) +@propagate_inbounds getindex(ta::TimeArray, n::Integer, col::Symbol) = getindex( + ta, [n], [col] +) # single date @propagate_inbounds function getindex(ta::TimeArray{T,N,D}, d::D) where {T,N,D} @@ -388,8 +393,9 @@ end end # StepRange{Date,...} -@propagate_inbounds getindex(ta::TimeArray{T,N,D}, r::StepRange{D}) where {T,N,D} = - ta[collect(r)] +@propagate_inbounds getindex(ta::TimeArray{T,N,D}, r::StepRange{D}) where {T,N,D} = ta[collect( + r +)] @propagate_inbounds getindex(ta::TimeArray, k::TimeArray{Bool,1}) = ta[findwhen(k)] From cdcf84e6dee0afe52a6f39ea9769160c87a4abf5 Mon Sep 17 00:00:00 2001 From: = <=> Date: Mon, 29 Dec 2025 21:01:35 +0530 Subject: [PATCH 2/4] Fixed some missing documentations --- src/apply.jl | 17 +++++ src/combine.jl | 16 +++++ test/retime.jl | 187 ++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 179 insertions(+), 41 deletions(-) diff --git a/src/apply.jl b/src/apply.jl index ce124dbb..c2b384d5 100644 --- a/src/apply.jl +++ b/src/apply.jl @@ -90,6 +90,23 @@ end # diff percentchange(ta::TimeArray, returns::Symbol=:simple; padding::Bool=false, method::AbstractString="") Compute percent change of a `TimeArray`. Use `:simple` or `:log` returns. + +# Arguments +- `padding::Bool=false`: If true, pads the beginning of the result with `NaN` values so the output has the same length as the input. If false, the result is shorter by one (or more, depending on differencing). + +# Example +```julia +using TimeSeries, Dates +ta = TimeArray(Date(2020,1,1):Day(1):Date(2020,1,3), [1,2,4], ["A"]) +percentchange(ta, :simple; padding=true) +# Output: +# 3×1 TimeArray{Float64,1,Date,Array{Float64,2}} +# │ │ A │ +# ├────────────┼────────┤ +# │ 2020-01-01 │ NaN │ +# │ 2020-01-02 │ 1.0 │ +# │ 2020-01-03 │ 1.0 │ +``` """ function percentchange( ta::TimeArray, returns::Symbol=:simple; padding::Bool=false, method::AbstractString="" diff --git a/src/combine.jl b/src/combine.jl index 6a63dced..45ec3361 100644 --- a/src/combine.jl +++ b/src/combine.jl @@ -282,6 +282,22 @@ end map(f, ta::TimeArray{T,N}) Apply function `f` to each row of a `TimeArray`, returning a new `TimeArray`. + +# Example + +```julia +using TimeSeries, Dates +ta = TimeArray(Date(2020,1,1):Day(1):Date(2020,1,3), [1,2,3], ["A"]) +# Add 10 to each value, keep timestamp +map((t, v) -> (t, v .+ 10), ta) +# Output: +# 3×1 TimeArray{Int64,1,Date,Array{Int64,2}} +# │ │ A │ +# ├────────────┼─────┤ +# │ 2020-01-01 │ 11 │ +# │ 2020-01-02 │ 12 │ +# │ 2020-01-03 │ 13 │ +``` """ @generated function map(f, ta::TimeArray{T,N}) where {T,N} input_val = (N == 1) ? :(values(ta)[i]) : :(vec(values(ta)[i, :])) diff --git a/test/retime.jl b/test/retime.jl index 1914a120..3e92cd1b 100644 --- a/test/retime.jl +++ b/test/retime.jl @@ -78,31 +78,49 @@ using Statistics end @testset "single column interpolation" begin - new_timestamps = collect(Dates.DateTime(2000):Dates.Hour(1):Dates.DateTime(2001)) - - upsamples = [ - TimeSeries.Linear(), - TimeSeries.Previous(), - TimeSeries.Next(), - TimeSeries.Nearest(), - ] - @testset for upsample in upsamples - cl_new = retime(cl, new_timestamps; upsample) - - @test timestamp(cl_new) == new_timestamps - - # TODO: real tests - end - - # test using Symbols - upsamples = [:linear, :previous, :next, :nearest] - @testset for upsample in upsamples - cl_new = retime(cl, new_timestamps; upsample) - - @test timestamp(cl_new) == new_timestamps - - # TODO: real tests - end + # Create simple test data with known values for verification + test_timestamps = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 2, 0), DateTime(2020, 1, 1, 4, 0)] + test_values = [10.0, 20.0, 30.0] + test_ta = TimeArray(test_timestamps, test_values, [:Value]) + + # Test Linear interpolation + new_ts = [DateTime(2020, 1, 1, 1, 0), DateTime(2020, 1, 1, 3, 0)] + result = retime(test_ta, new_ts; upsample=TimeSeries.Linear()) + @test values(result)[1] ≈ 15.0 # midpoint between 10 and 20 + @test values(result)[2] ≈ 25.0 # midpoint between 20 and 30 + + # Test Previous interpolation + result = retime(test_ta, new_ts; upsample=TimeSeries.Previous()) + @test values(result)[1] == 10.0 # uses previous value + @test values(result)[2] == 20.0 # uses previous value + + # Test Next interpolation + result = retime(test_ta, new_ts; upsample=TimeSeries.Next()) + @test values(result)[1] == 20.0 # uses next value + @test values(result)[2] == 30.0 # uses next value + + # Test Nearest interpolation + result = retime(test_ta, new_ts; upsample=TimeSeries.Nearest()) + @test values(result)[1] == 10.0 # closer to 10 than 20 + @test values(result)[2] == 20.0 # closer to 20 than 30 + + # Test with exact timestamp match + exact_ts = [DateTime(2020, 1, 1, 2, 0)] + result = retime(test_ta, exact_ts; upsample=TimeSeries.Linear()) + @test values(result)[1] == 20.0 # exact match + + # Test using Symbols + result = retime(test_ta, new_ts; upsample=:linear) + @test values(result)[1] ≈ 15.0 + + result = retime(test_ta, new_ts; upsample=:previous) + @test values(result)[1] == 10.0 + + result = retime(test_ta, new_ts; upsample=:next) + @test values(result)[1] == 20.0 + + result = retime(test_ta, new_ts; upsample=:nearest) + @test values(result)[1] == 10.0 end @testset "single column extrapolate" begin @@ -155,22 +173,33 @@ using Statistics end @testset "multi column interpolation" begin - new_timestamps = collect(Dates.DateTime(2000):Dates.Hour(1):Dates.DateTime(2001)) - - upsamples = [ - TimeSeries.Linear(), - TimeSeries.Previous(), - TimeSeries.Next(), - TimeSeries.Nearest(), - ] - @testset for upsample in upsamples - ohlc_new = retime(ohlc, new_timestamps; upsample) - - @test timestamp(ohlc_new) == new_timestamps - - # TODO: real tests - end - end + # Create simple multi-column test data + test_timestamps = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 2, 0), DateTime(2020, 1, 1, 4, 0)] + test_values = [10.0 100.0; 20.0 200.0; 30.0 300.0] + test_ta = TimeArray(test_timestamps, test_values, [:A, :B]) + + new_ts = [DateTime(2020, 1, 1, 1, 0), DateTime(2020, 1, 1, 3, 0)] + + # Test Linear interpolation + result = retime(test_ta, new_ts; upsample=TimeSeries.Linear()) + @test values(result)[1, 1] ≈ 15.0 + @test values(result)[1, 2] ≈ 150.0 + @test values(result)[2, 1] ≈ 25.0 + @test values(result)[2, 2] ≈ 250.0 + + # Test Previous interpolation + result = retime(test_ta, new_ts; upsample=TimeSeries.Previous()) + @test values(result)[1, 1] == 10.0 + @test values(result)[1, 2] == 100.0 + + # Test Next interpolation + result = retime(test_ta, new_ts; upsample=TimeSeries.Next()) + @test values(result)[1, 1] == 20.0 + @test values(result)[1, 2] == 200.0 + + # Test Nearest interpolation + result = retime(test_ta, new_ts; upsample=TimeSeries.Nearest()) + @test values(result)[1, 1] == 10 @testset "multi column extrapolate" begin new_timestamps = collect(Dates.DateTime(2000):Dates.Hour(1):Dates.DateTime(2001)) @@ -277,4 +306,80 @@ using Statistics ta_new = retime(ta, Hour(1); upsample=:linear) end + + @testset "Custom aggregation function" begin + test_timestamps = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 1, 0), + DateTime(2020, 1, 1, 2, 0), DateTime(2020, 1, 1, 3, 0)] + test_values = [1.0, 2.0, 3.0, 4.0] + test_ta = TimeArray(test_timestamps, test_values, [:Value]) + + # Test custom function (e.g., product) + custom_func = x -> prod(x) + new_ts = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 2, 0)] + result = retime(test_ta, new_ts; downsample=custom_func) + + # First interval should contain values 1.0 and 2.0, product = 2.0 + @test values(result)[1] == 2.0 + # Second interval should contain values 3.0 and 4.0, product = 12.0 + @test values(result)[2] == 12.0 + end + + @testset "Edge cases" begin + # Single data point + single_ta = TimeArray([DateTime(2020, 1, 1)], [42.0], [:Value]) + new_ts = [DateTime(2020, 1, 1)] + result = retime(single_ta, new_ts) + @test values(result)[1] == 42.0 + + # Exact timestamp matches + test_timestamps = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 2, 0)] + test_values = [10.0, 20.0] + test_ta = TimeArray(test_timestamps, test_values, [:Value]) + result = retime(test_ta, test_timestamps) + @test values(result) == test_values + + # Last timestamp handling (tests the i == N branch) + test_timestamps = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 1, 0), + DateTime(2020, 1, 1, 2, 0)] + test_values = [10.0, 20.0, 30.0] + test_ta = TimeArray(test_timestamps, test_values, [:Value]) + new_ts = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 1, 0), + DateTime(2020, 1, 1, 2, 0)] + result = retime(test_ta, new_ts; downsample=TimeSeries.Mean()) + @test length(values(result)) == 3 + end + + @testset "Period-based resampling" begin + test_timestamps = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 1, 30), + DateTime(2020, 1, 1, 3, 0)] + test_values = [10.0, 20.0, 30.0] + test_ta = TimeArray(test_timestamps, test_values, [:Value]) + + # Test with Period + result = retime(test_ta, Hour(1)) + @test length(timestamp(result)) > 0 + @test timestamp(result)[1] == DateTime(2020, 1, 1, 0, 0) + + # Verify it produces same result as explicit timestamps + explicit_ts = collect(DateTime(2020, 1, 1, 0, 0):Hour(1):DateTime(2020, 1, 1, 3, 0)) + result_explicit = retime(test_ta, explicit_ts) + @test timestamp(result) == timestamp(result_explicit) + end + + @testset "Type promotion" begin + # Integer with Linear should promote to Float64 + int_ta = TimeArray([DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 2, 0)], + [10, 20], [:Value]) + new_ts = [DateTime(2020, 1, 1, 1, 0)] + result = retime(int_ta, new_ts; upsample=:linear) + @test eltype(values(result)) == Float64 + @test values(result)[1] == 15.0 + + # Integer with Mean should promote to Float64 + int_ta2 = TimeArray([DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 0, 30), + DateTime(2020, 1, 1, 1, 0)], [10, 15, 20], [:Value]) + new_ts2 = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 1, 0)] + result2 = retime(int_ta2, new_ts2; downsample=:mean) + @test eltype(values(result2)) == Float64 + end end From 86588dd058de71a6f48ff68185893b7d3fde7f20 Mon Sep 17 00:00:00 2001 From: = <=> Date: Mon, 29 Dec 2025 22:07:12 +0530 Subject: [PATCH 3/4] Fixed typos --- src/retime.jl | 5 -- test/retime.jl | 189 +++++++++++-------------------------------------- 2 files changed, 42 insertions(+), 152 deletions(-) diff --git a/src/retime.jl b/src/retime.jl index a2cdbbbe..1d0a972f 100644 --- a/src/retime.jl +++ b/src/retime.jl @@ -56,11 +56,6 @@ _toExtrapolationMethod(::Val{:missing}) = MissingExtrapolate() _toExtrapolationMethod(::Val{:nan}) = NaNExtrapolate() _toExtrapolationMethod(x::ExtrapolationMethod) = x -""" - retime(ta, new_dt::Dates.Period; kwargs...) - -Resample or align a `TimeArray` to new timestamps or frequency. -""" function retime(ta, new_dt::Dates.Period; kwargs...) new_timestamps = floor(timestamp(ta)[1], new_dt):new_dt:floor(timestamp(ta)[end], new_dt) diff --git a/test/retime.jl b/test/retime.jl index 3e92cd1b..bd2f7613 100644 --- a/test/retime.jl +++ b/test/retime.jl @@ -1,4 +1,4 @@ -using Test +using Test using MarketData using TimeSeries using Dates @@ -78,49 +78,31 @@ using Statistics end @testset "single column interpolation" begin - # Create simple test data with known values for verification - test_timestamps = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 2, 0), DateTime(2020, 1, 1, 4, 0)] - test_values = [10.0, 20.0, 30.0] - test_ta = TimeArray(test_timestamps, test_values, [:Value]) - - # Test Linear interpolation - new_ts = [DateTime(2020, 1, 1, 1, 0), DateTime(2020, 1, 1, 3, 0)] - result = retime(test_ta, new_ts; upsample=TimeSeries.Linear()) - @test values(result)[1] ≈ 15.0 # midpoint between 10 and 20 - @test values(result)[2] ≈ 25.0 # midpoint between 20 and 30 - - # Test Previous interpolation - result = retime(test_ta, new_ts; upsample=TimeSeries.Previous()) - @test values(result)[1] == 10.0 # uses previous value - @test values(result)[2] == 20.0 # uses previous value - - # Test Next interpolation - result = retime(test_ta, new_ts; upsample=TimeSeries.Next()) - @test values(result)[1] == 20.0 # uses next value - @test values(result)[2] == 30.0 # uses next value - - # Test Nearest interpolation - result = retime(test_ta, new_ts; upsample=TimeSeries.Nearest()) - @test values(result)[1] == 10.0 # closer to 10 than 20 - @test values(result)[2] == 20.0 # closer to 20 than 30 - - # Test with exact timestamp match - exact_ts = [DateTime(2020, 1, 1, 2, 0)] - result = retime(test_ta, exact_ts; upsample=TimeSeries.Linear()) - @test values(result)[1] == 20.0 # exact match - - # Test using Symbols - result = retime(test_ta, new_ts; upsample=:linear) - @test values(result)[1] ≈ 15.0 - - result = retime(test_ta, new_ts; upsample=:previous) - @test values(result)[1] == 10.0 - - result = retime(test_ta, new_ts; upsample=:next) - @test values(result)[1] == 20.0 - - result = retime(test_ta, new_ts; upsample=:nearest) - @test values(result)[1] == 10.0 + new_timestamps = collect(Dates.DateTime(2000):Dates.Hour(1):Dates.DateTime(2001)) + + upsamples = [ + TimeSeries.Linear(), + TimeSeries.Previous(), + TimeSeries.Next(), + TimeSeries.Nearest(), + ] + @testset for upsample in upsamples + cl_new = retime(cl, new_timestamps; upsample) + + @test timestamp(cl_new) == new_timestamps + + # TODO: real tests + end + + # test using Symbols + upsamples = [:linear, :previous, :next, :nearest] + @testset for upsample in upsamples + cl_new = retime(cl, new_timestamps; upsample) + + @test timestamp(cl_new) == new_timestamps + + # TODO: real tests + end end @testset "single column extrapolate" begin @@ -173,33 +155,22 @@ using Statistics end @testset "multi column interpolation" begin - # Create simple multi-column test data - test_timestamps = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 2, 0), DateTime(2020, 1, 1, 4, 0)] - test_values = [10.0 100.0; 20.0 200.0; 30.0 300.0] - test_ta = TimeArray(test_timestamps, test_values, [:A, :B]) - - new_ts = [DateTime(2020, 1, 1, 1, 0), DateTime(2020, 1, 1, 3, 0)] - - # Test Linear interpolation - result = retime(test_ta, new_ts; upsample=TimeSeries.Linear()) - @test values(result)[1, 1] ≈ 15.0 - @test values(result)[1, 2] ≈ 150.0 - @test values(result)[2, 1] ≈ 25.0 - @test values(result)[2, 2] ≈ 250.0 - - # Test Previous interpolation - result = retime(test_ta, new_ts; upsample=TimeSeries.Previous()) - @test values(result)[1, 1] == 10.0 - @test values(result)[1, 2] == 100.0 - - # Test Next interpolation - result = retime(test_ta, new_ts; upsample=TimeSeries.Next()) - @test values(result)[1, 1] == 20.0 - @test values(result)[1, 2] == 200.0 - - # Test Nearest interpolation - result = retime(test_ta, new_ts; upsample=TimeSeries.Nearest()) - @test values(result)[1, 1] == 10 + new_timestamps = collect(Dates.DateTime(2000):Dates.Hour(1):Dates.DateTime(2001)) + + upsamples = [ + TimeSeries.Linear(), + TimeSeries.Previous(), + TimeSeries.Next(), + TimeSeries.Nearest(), + ] + @testset for upsample in upsamples + ohlc_new = retime(ohlc, new_timestamps; upsample) + + @test timestamp(ohlc_new) == new_timestamps + + # TODO: real tests + end + end @testset "multi column extrapolate" begin new_timestamps = collect(Dates.DateTime(2000):Dates.Hour(1):Dates.DateTime(2001)) @@ -306,80 +277,4 @@ using Statistics ta_new = retime(ta, Hour(1); upsample=:linear) end - - @testset "Custom aggregation function" begin - test_timestamps = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 1, 0), - DateTime(2020, 1, 1, 2, 0), DateTime(2020, 1, 1, 3, 0)] - test_values = [1.0, 2.0, 3.0, 4.0] - test_ta = TimeArray(test_timestamps, test_values, [:Value]) - - # Test custom function (e.g., product) - custom_func = x -> prod(x) - new_ts = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 2, 0)] - result = retime(test_ta, new_ts; downsample=custom_func) - - # First interval should contain values 1.0 and 2.0, product = 2.0 - @test values(result)[1] == 2.0 - # Second interval should contain values 3.0 and 4.0, product = 12.0 - @test values(result)[2] == 12.0 - end - - @testset "Edge cases" begin - # Single data point - single_ta = TimeArray([DateTime(2020, 1, 1)], [42.0], [:Value]) - new_ts = [DateTime(2020, 1, 1)] - result = retime(single_ta, new_ts) - @test values(result)[1] == 42.0 - - # Exact timestamp matches - test_timestamps = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 2, 0)] - test_values = [10.0, 20.0] - test_ta = TimeArray(test_timestamps, test_values, [:Value]) - result = retime(test_ta, test_timestamps) - @test values(result) == test_values - - # Last timestamp handling (tests the i == N branch) - test_timestamps = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 1, 0), - DateTime(2020, 1, 1, 2, 0)] - test_values = [10.0, 20.0, 30.0] - test_ta = TimeArray(test_timestamps, test_values, [:Value]) - new_ts = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 1, 0), - DateTime(2020, 1, 1, 2, 0)] - result = retime(test_ta, new_ts; downsample=TimeSeries.Mean()) - @test length(values(result)) == 3 - end - - @testset "Period-based resampling" begin - test_timestamps = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 1, 30), - DateTime(2020, 1, 1, 3, 0)] - test_values = [10.0, 20.0, 30.0] - test_ta = TimeArray(test_timestamps, test_values, [:Value]) - - # Test with Period - result = retime(test_ta, Hour(1)) - @test length(timestamp(result)) > 0 - @test timestamp(result)[1] == DateTime(2020, 1, 1, 0, 0) - - # Verify it produces same result as explicit timestamps - explicit_ts = collect(DateTime(2020, 1, 1, 0, 0):Hour(1):DateTime(2020, 1, 1, 3, 0)) - result_explicit = retime(test_ta, explicit_ts) - @test timestamp(result) == timestamp(result_explicit) - end - - @testset "Type promotion" begin - # Integer with Linear should promote to Float64 - int_ta = TimeArray([DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 2, 0)], - [10, 20], [:Value]) - new_ts = [DateTime(2020, 1, 1, 1, 0)] - result = retime(int_ta, new_ts; upsample=:linear) - @test eltype(values(result)) == Float64 - @test values(result)[1] == 15.0 - - # Integer with Mean should promote to Float64 - int_ta2 = TimeArray([DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 0, 30), - DateTime(2020, 1, 1, 1, 0)], [10, 15, 20], [:Value]) - new_ts2 = [DateTime(2020, 1, 1, 0, 0), DateTime(2020, 1, 1, 1, 0)] - result2 = retime(int_ta2, new_ts2; downsample=:mean) - @test eltype(values(result2)) == Float64 - end end From cf18628005797ebda1c1bc37c854b7c4afb450b2 Mon Sep 17 00:00:00 2001 From: = <=> Date: Mon, 29 Dec 2025 22:21:01 +0530 Subject: [PATCH 4/4] Formatting issues resolved... --- test/retime.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/retime.jl b/test/retime.jl index bd2f7613..1914a120 100644 --- a/test/retime.jl +++ b/test/retime.jl @@ -1,4 +1,4 @@ -using Test +using Test using MarketData using TimeSeries using Dates