Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions src/apply.jl
Original file line number Diff line number Diff line change
@@ -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")
Expand All @@ -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(
Expand All @@ -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
Expand All @@ -60,6 +86,28 @@ 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.

# 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(
Comment thread
anurag-mds marked this conversation as resolved.
ta::TimeArray, returns::Symbol=:simple; padding::Bool=false, method::AbstractString=""
)
Expand Down Expand Up @@ -158,6 +206,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)
Expand All @@ -166,6 +219,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)
Expand All @@ -176,12 +234,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
Expand All @@ -192,6 +260,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]
Expand All @@ -205,6 +278,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)), :))]
Expand Down
10 changes: 4 additions & 6 deletions src/basemisc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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′)
Expand Down
37 changes: 35 additions & 2 deletions src/combine.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 ######################
Expand Down Expand Up @@ -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 ######################

Expand Down Expand Up @@ -266,6 +278,27 @@ end

# map ######################

"""
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 │
```
"""
Comment thread
anurag-mds marked this conversation as resolved.
@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, :])
Expand Down
15 changes: 9 additions & 6 deletions src/modify.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
10 changes: 10 additions & 0 deletions src/readwrite.jl
Original file line number Diff line number Diff line change
@@ -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
)
Expand Down Expand Up @@ -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;
Expand Down
33 changes: 32 additions & 1 deletion src/split.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
# 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
when(ta::TimeArray, period::Function, t::String) = ta[findall(period.(timestamp(ta)) .== t)]

# 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
Expand All @@ -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
Expand All @@ -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, :])

Expand All @@ -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, :])

Expand All @@ -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)
Expand Down
Loading
Loading