Skip to content
Closed
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
42 changes: 42 additions & 0 deletions src/TimeSeries.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,45 @@
"""
TimeSeries

A Julia package for working with time series data.

The `TimeSeries` package provides the `TimeArray` type and associated methods for handling
time-indexed data. It supports operations like filtering, transforming, combining, and
resampling time series data.

# Main Types

- `TimeArray`: The primary time series container
- `AbstractTimeSeries`: Abstract supertype for time series types

# Key Functions

- Indexing and filtering: `when`, `from`, `to`, `findwhen`, `head`, `tail`
- Transformations: `lag`, `lead`, `diff`, `percentchange`, `moving`, `upto`
- Combining: `merge`, `collapse`, `vcat`, `hcat`
- Resampling: `retime` with various interpolation and aggregation methods
- I/O: `readtimearray`, `writetimearray`
- Utilities: `timestamp`, `values`, `colnames`, `meta`

# Examples

```julia
using TimeSeries, Dates

# Create a TimeArray
dates = Date(2020,1,1):Day(1):Date(2020,1,10)
ta = TimeArray(dates, rand(10), [:Value])

# Filter by date range
ta_subset = from(ta, Date(2020,1,5))

# Calculate moving average
ta_ma = moving(mean, ta, 3)

# Resample to weekly
ta_weekly = retime(ta, Week(1))
```
"""
module TimeSeries

# stdlib
Expand Down
176 changes: 170 additions & 6 deletions src/apply.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,46 @@
import Base: +, -
import Base.diff

"""
+(ta::TimeArray)

Unary plus operator for `TimeArray`.

Returns a broadcasted copy with the unary plus operator applied elementwise.
"""
(+)(ta::TimeArray) = .+ta

"""
-(ta::TimeArray)

Unary minus operator for `TimeArray`.

Returns a broadcasted copy with elementwise negation applied to all values.
"""
(-)(ta::TimeArray) = .-ta

###### lag, lead ################

"""
lag(ta::TimeArray, n::Int=1; padding::Bool=false)

Shift values backward by `n` periods, removing the first `n` rows.

# Arguments

- `n::Int`: Number of periods to lag (default: 1)
- `padding::Bool`: If `true`, pad with `NaN` to maintain length (default: `false`)

Note: The deprecated `period` keyword argument is also accepted but should not be used.

# Examples

```julia
lag(ta) # Lag by 1 period
lag(ta, 5) # Lag by 5 periods
lag(ta, padding=true) # Lag with NaN padding
```
"""
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 +61,26 @@ function lag(ta::TimeArray{T,N}, n::Int=1; padding::Bool=false, period::Int=0) w
return ta
end # lag

"""
lead(ta::TimeArray, n::Int=1; padding::Bool=false)

Shift values forward by `n` periods, removing the last `n` rows.

# Arguments

- `n::Int`: Number of periods to lead (default: 1)
- `padding::Bool`: If `true`, pad with `NaN` to maintain length (default: `false`)

Note: The deprecated `period` keyword argument is also accepted but should not be used.

# Examples

```julia
lead(ta) # Lead by 1 period
lead(ta, 5) # Lead by 5 periods
lead(ta, padding=true) # Lead with NaN padding
```
"""
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 +104,27 @@ end # lead

###### diff #####################

"""
diff(ta::TimeArray, n::Int=1; padding::Bool=false, differences::Int=1)

Calculate the `n`-period difference of a `TimeArray`.

This is a `TimeArray` specialization of `Base.diff`.

# Arguments

- `n::Int`: Number of periods for differencing (default: 1)
- `padding::Bool`: If `true`, pad with `NaN` to maintain length (default: `false`)
- `differences::Int`: Number of times to apply differencing (default: 1)

# Examples

```julia
diff(ta) # First difference
diff(ta, 2) # 2-period difference
diff(ta, differences=2) # Second-order difference
```
"""
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 +136,24 @@ end # diff

###### percentchange ############

"""
percentchange(ta::TimeArray, returns::Symbol=:simple; padding::Bool=false)

Calculate percentage change between consecutive periods.

# Arguments

- `returns::Symbol`: Type of returns calculation - `:simple` or `:log` (default: `:simple`)
- `padding::Bool`: If `true`, pad with `NaN` to maintain length (default: `false`)

# Examples

```julia
percentchange(ta) # Simple returns
percentchange(ta, :log) # Log returns
percentchange(ta, padding=true) # With NaN padding
```
"""
function percentchange(
ta::TimeArray, returns::Symbol=:simple; padding::Bool=false, method::AbstractString=""
)
Expand Down Expand Up @@ -108,15 +202,26 @@ end
"""
moving(f, ta::TimeArray{T,2}, w::Integer; padding = false, dims = 1, colnames = [...])

## Example
Apply user-defined function `f` to a 2D `TimeArray` with window size `w`.

In case of `dims = 2`, the user-defined function `f` will get a 2D `Array` as input.
# Arguments

- `f`: Function to apply to each window
- `ta::TimeArray{T,2}`: 2D time array
- `w::Integer`: Window size
- `padding::Bool`: If `true`, pad with `NaN` to maintain length (default: `false`)
- `dims::Integer`: Dimension to apply function - `1` for column-wise, `2` for row-wise (default: 1)
- `colnames::Vector{Symbol}`: Column names for result (default: original column names)

# Examples

```julia
moving(ohlc, 10; dims=2, colnames=[:A, ...]) do
# given that `ohlc` is a 500x4 `TimeArray`,
# size(A) is (10, 4)
...
moving(mean, ta, 10) # 10-period moving average

# For dims=2, function receives a 2D window
moving(ta, 10; dims=2) do window
# window is a 10×ncol matrix
sum(window)
end
```
"""
Expand Down Expand Up @@ -158,6 +263,18 @@ end

###### upto #####################

"""
upto(f, ta::TimeArray)

Apply function `f` cumulatively from the start up to each row.

# Examples

```julia
upto(sum, ta) # Cumulative sum
upto(mean, ta) # Expanding mean
```
"""
function upto(f, ta::TimeArray{T,1}) where {T}
vals = zero(values(ta))
for i in 1:length(vals)
Expand All @@ -176,12 +293,36 @@ end

###### basecall #################

"""
basecall(ta::TimeArray, f::Function; cnames=colnames(ta))

Apply a function `f` to the values array and return a new `TimeArray`.

# Arguments

- `f::Function`: Function to apply to the values array
- `cnames`: Column names for the result (default: original column names)

# Examples

```julia
basecall(ta, transpose) # Transpose the values
basecall(ta, sort; cnames=[:Sorted]) # Sort values
```
"""
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 timestamps in a `TimeArray` are uniformly spaced.

Returns `true` if all consecutive timestamp differences are equal, `false` otherwise.
"""
function uniformspaced(ta::TimeArray)
gap1 = timestamp(ta)[2] - timestamp(ta)[1]
i, n, is_uniform = 2, length(ta), true
Expand All @@ -192,6 +333,13 @@ function uniformspaced(ta::TimeArray)
return is_uniform
end # uniformspaced

"""
uniformspace(ta::TimeArray)

Convert a `TimeArray` to uniform spacing using the minimum gap between timestamps.

Missing timestamps are filled with rows containing zeros.
"""
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 +353,22 @@ end # uniformspace

###### dropnan ####################

"""
dropnan(ta::TimeArray, method::Symbol=:all)

Remove rows containing `NaN` values from a `TimeArray`.

# Arguments

- `method::Symbol`: Removal strategy - `:all` removes rows where all values are `NaN`, `:any` removes rows with any `NaN` (default: `:all`)

# Examples

```julia
dropnan(ta) # Remove rows where all values are NaN
dropnan(ta, :any) # Remove rows with any NaN
```
"""
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
6 changes: 4 additions & 2 deletions src/combine.jl
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,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
8 changes: 8 additions & 0 deletions src/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@
# 0.20.0
###############################################################################

"""
update(ta::TimeArray, tstamp, val)

!!! warning "Deprecated"
This function is deprecated. Use `vcat(ta, TimeArray(tstamp, val))` instead.
"""
function update end

export update

@deprecate(
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
Loading
Loading