Skip to content

Commit

Permalink
Tables.jl integration (#382)
Browse files Browse the repository at this point in the history
  • Loading branch information
iblislin authored Jul 3, 2019
1 parent 099dbd9 commit 1223b3e
Show file tree
Hide file tree
Showing 13 changed files with 351 additions and 3 deletions.
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
at index []
```

* `Tables.jl` interface integration. (#382)

* 2D `getindex` supports. (#423)

```julia
Expand Down
5 changes: 4 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,19 @@ DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab"
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"

[compat]
RecipesBase = "≥ 0.2.3"
Reexport = "≥ 0.2.0"
julia = "≥ 0.7.0"

[extras]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
MarketData = "945b72a4-3b13-509d-9b46-1525bb5c06de"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["MarketData", "Test", "Random"]
test = ["MarketData", "Test", "DataFrames", "CSV", "Random"]
2 changes: 2 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[deps]
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
MarketData = "945b72a4-3b13-509d-9b46-1525bb5c06de"
TimeSeries = "9e3dc215-6440-5c97-bce1-76c03772f85e"

[compat]
Documenter = "~0.20"
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ makedocs(
"apply.md",
"combine.md",
"readwrite.md",
"tables.md",
"dotfile.md",
"plotting.md",
]
Expand Down
1 change: 0 additions & 1 deletion docs/src/apply.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ The value of the `cl` object on Jan 3, 2000 is 111.94. On Jan 4, 2000 it
is 102.50 and on Jan 5, 2000 it's 104.0:

```@repl lag
using TimeSeries
using MarketData
cl[1:3]
```
Expand Down
1 change: 1 addition & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Pages = [
"apply.md",
"combine.md",
"readwrite.md",
"tables.md",
"dotfile.md",
"plotting.md",
]
Expand Down
49 changes: 49 additions & 0 deletions docs/src/tables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Tables.jl Interface Integration

Quoted from the home page of Tables.jl:
> The [Table.jl](https://github.com/JuliaData/Tables.jl) package provides simple,
> yet powerful interface functions for working with all kinds tabular data through
> predictable access patterns.
The integration provides handy constructor to convert a table between several
types.
The time index of a `TimeArray` is considered as a normal data column named
`timestamp`.

Here this doc shows some example usages of this integration.
Converting table between `DataFrame`s or `CSV` are quite common cases.


## `TimeArray` to `DataFrame`

```@repl df-ta
using MarketData, DataFrames
df = DataFrame(ohlc)
```

## `DataFrame` to `TimeArray`

In this case, user needs to point out the column of time index via the
`timestamp` keyword argument.

```@repl df-ta
df′ = DataFrames.rename(df, :timestamp => :A);
df′ |> first
TimeArray(df′, timestamp = :A)
```


## Save a `TimeArray` via `CSV.jl`

```julia
using CSV
CSV.write(filename, ta)
```


## Load a `TimeArray` from csv file via `CSV.jl`

```julia
using CSV
TimeArray(CSV.File(filename), timestamp = :timestamp)
```
2 changes: 2 additions & 0 deletions src/TimeSeries.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module TimeSeries
using Dates
using DelimitedFiles
using Statistics
using Tables

using RecipesBase
using Reexport
Expand All @@ -23,6 +24,7 @@ export TimeArray, AbstractTimeSeries,
include(".timeseriesrc.jl")
include("timearray.jl")
include("utilities.jl")
include("tables.jl")
include("split.jl")
include("apply.jl")
include("broadcast.jl")
Expand Down
73 changes: 73 additions & 0 deletions src/tables.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# JuliaData/Tables.jl integration

# const TimeArrayColIter = Tables.EachColumn{<:TimeArray}
module TablesIntegration

using ..TimeSeries

using Tables


# S: the column names, including the extra column name for time index
# N: number of rows
# M: number of columns
struct TableIter{T<:AbstractTimeSeries,S,N,M}
x::T
end

function TableIter(ta::T) where {T<:TimeArray}
N, M = size(ta, 1), size(ta, 2) + 1
S = Tuple(TimeSeries.replace_dupes!([:timestamp; colnames(ta)]))
TableIter{T,S,N,M}(ta)
end

data(i::TableIter) = getfield(i, :x)

TimeSeries.timestamp(i::TableIter) = timestamp(data(i))
TimeSeries.colnames(i::TableIter) = colnames(data(i))

Base.getindex(x::TableIter{<:TimeArray}, i::Integer) =
i == 1 ? timestamp(x) : values(data(x)[colnames(x)[i - 1]])
Base.getindex(x::TableIter{<:TimeArray}, j::Integer, i::Integer) =
i == 1 ? timestamp(x)[j] : values(data(x)[colnames(x)[i - 1]])[j]

Base.length(::TableIter{<:TimeArray,S,N,M}) where {S,N,M} = M
Base.lastindex(::TableIter{<:TimeArray,S,N,M}) where {S,N,M} = M
Base.lastindex(::TableIter{<:TimeArray,S,N,M}, d::Integer) where {S,N,M} =
ifelse(d == 1, N, ifelse(d == 2, M, 1))
Base.size(::TableIter{<:TimeArray,S,N,M}) where {S,N,M} = (N, M)

Base.propertynames(x::TableIter{<:TimeArray,S}) where {S} = S
Base.getproperty(x::TableIter{<:TimeArray,S}, c::Symbol) where {S} =
c == S[1] ? timestamp(x) : values(getproperty(data(x), c))

function Base.iterate(x::TableIter, i::Integer = 1)
i > length(x) && return nothing
x[i], i + 1
end

Tables.istable(::Type{<:AbstractTimeSeries}) = true
Tables.rowaccess(::Type{<:TimeArray}) = true
Tables.rows(ta::TimeArray) = Tables.rows(Tables.columntable(ta))
Tables.columnaccess(::Type{<:TimeArray}) = true
Tables.columns(ta::TimeArray) = TableIter(ta)
Tables.eachcolumn(i::TableIter) = i
Tables.schema(ta::AbstractTimeSeries{T,N,D}) where {T,N,D} = Tables.schema(TableIter(ta))
Tables.schema(i::TableIter{T,S}) where {T,S} = Tables.Schema(S, coltypes(data(i)))

coltypes(x::AbstractTimeSeries{T,N,D}) where {T,N,D} = (D, (T for _ 1:size(x, 2))...)

function TimeSeries.TimeArray(x; timestamp::Symbol)
Tables.istable(x) || throw(ArgumentError("TimeArray requires a table input"))

sch = Tables.schema(x)
names = sch.names
(timestamp names) && throw(ArgumentError("time index `$timestamp` not found"))
names′ = filter(!isequal(timestamp), collect(sch.names))

cols = Tables.columns(x)
val = mapreduce(n -> collect(getproperty(cols, n)), hcat, names′)
TimeArray(collect(getproperty(cols, timestamp)), val, names′, x)
end

end # TablesIntegration
4 changes: 4 additions & 0 deletions src/timearray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ abstract type AbstractTimeSeries{T,N,D} end
TimeArray(timestamp, values[, colnames, meta = nothing])
TimeArray(ta::TimeArray; timestamp, values, colnames, meta)
TimeArray(data::NamedTuple, timestamp = :datetime, meta)
TimeArray(table; timestamp::Symbol)
The second constructor will yields a new TimeArray with the new given fields.
Note that the unchanged fields will be shared, there aren't any copy for the
Expand All @@ -28,6 +29,9 @@ The third constructor builds a `TimeArray` from a `NamedTuple`.
- `timestamp::AbstractVector{<:TimeType}`: a vector of sorted timestamps,
Each element in this vector should be unique.
- `timestamp::Symbol`: the column name of the time index from the source table.
The constructor is used for the Tables.jl package integration.
- `values::AbstractArray`: a data vector or matrix. Its number of rows
should match the length of `timestamp`.
Expand Down
1 change: 0 additions & 1 deletion test/REQUIRE

This file was deleted.

1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ tests = [
"readwrite",
"timeseriesrc",
"basemisc",
"tables",
]


Expand Down
Loading

0 comments on commit 1223b3e

Please sign in to comment.