Julia Package Butler Updates
1 |
struct TimeArrayIterator{T, S} |
|
2 |
source::S |
|
3 |
end
|
|
4 |
|
|
5 |
IteratorInterfaceExtensions.isiterable(x::TimeSeries.TimeArray) = true |
|
6 |
TableTraits.isiterabletable(x::TimeSeries.TimeArray) = true |
|
7 |
|
|
8 |
function IteratorInterfaceExtensions.getiterator(ta::S) where {S<:TimeSeries.TimeArray} |
|
9 |
etype = eltype(TimeSeries.values(ta)) |
|
10 |
|
|
11 |
T = NamedTuple{(:timestamp, Symbol.(TimeSeries.colnames(ta))...), Tuple{S.parameters[3], fill(etype, length(TimeSeries.colnames(ta)))...}} |
|
12 |
|
|
13 |
return TimeArrayIterator{T, S}(ta) |
|
14 |
end
|
|
15 |
|
|
16 |
function Base.length(iter::TimeArrayIterator) |
|
17 |
return length(iter.source) |
|
18 |
end
|
|
19 |
|
|
20 |
function Base.eltype(iter::TimeArrayIterator{T,TS}) where {T,TS} |
|
21 |
return T |
|
22 |
end
|
|
23 |
|
|
24 |
Base.eltype(::Type{TimeArrayIterator{T,TS}}) where {T,TS} = T |
|
25 |
|
|
26 |
@generated function Base.iterate(iter::TimeArrayIterator{T,TS}, state=1) where {T,TS} |
|
27 |
constructor_call = Expr(:call, :($T)) |
|
28 |
|
|
29 |
push!(constructor_call.args, Expr(:tuple)) |
|
30 |
|
|
31 |
# Add timestamp column
|
|
32 |
push!(constructor_call.args[2].args, :(TimeSeries.timestamp(iter.source)[i])) |
|
33 |
|
|
34 |
for i in 1:fieldcount(T)-1 |
|
35 |
push!(constructor_call.args[2].args, :(TimeSeries.values(iter.source)[i,$i])) |
|
36 |
end
|
|
37 |
|
|
38 |
quote
|
|
39 |
if state>length(TimeSeries.timestamp(iter.source)) |
|
40 |
return nothing |
|
41 |
else
|
|
42 |
i = state |
|
43 |
a = $constructor_call |
|
44 |
return a, state+1 |
|
45 |
end
|
|
46 |
end
|
|
47 |
end
|
|
48 |
|
|
49 |
# Sink
|
|
50 |
|
|
51 |
function TimeSeries.TimeArray(x; timestamp_column::Symbol=:timestamp) |
|
52 |
TableTraits.isiterabletable(x) || error("Cannot create a TimeArray from something that is not a table.") |
|
53 |
|
|
54 |
iter = IteratorInterfaceExtensions.getiterator(x) |
|
55 |
|
|
56 |
et = eltype(iter) |
|
57 |
|
|
58 |
if fieldcount(et)<2 |
|
59 |
error("Need at least two columns") |
|
60 |
end
|
|
61 |
|
|
62 |
names = fieldnames(et) |
|
63 |
|
|
64 |
timestep_col_index = findfirst(isequal(timestamp_column), names) |
|
65 |
|
|
66 |
if timestep_col_index===nothing |
|
67 |
error("No timestamp column found.") |
|
68 |
end
|
|
69 |
|
|
70 |
timestep_col_index = something(timestep_col_index) |
|
71 |
|
|
72 |
col_types = [fieldtype(et, i) for i=1:fieldcount(et)] |
|
73 |
|
|
74 |
data_columns = collect(Iterators.filter(i->i[2][1]!=timestamp_column, enumerate(zip(names, col_types)))) |
|
75 |
|
|
76 |
orig_data_type = data_columns[1][2][2] |
|
77 |
|
|
78 |
data_type = orig_data_type <: DataValues.DataValue ? orig_data_type.parameters[1] : orig_data_type |
|
79 |
|
|
80 |
orig_timestep_type = col_types[timestep_col_index] |
|
81 |
|
|
82 |
timestep_type = orig_timestep_type <: DataValues.DataValue ? orig_timestep_type.parameters[1] : orig_timestep_type |
|
83 |
|
|
84 |
if any(i->i[2][2]!=orig_data_type, data_columns) |
|
85 |
error("All data columns need to be of the same type.") |
|
86 |
end
|
|
87 |
|
|
88 |
t_column = Vector{timestep_type}(undef,0) |
|
89 |
d_array = Vector{Vector{data_type}}(undef,0) |
|
90 |
for i in data_columns |
|
91 |
push!(d_array, Vector{data_type}(undef,0)) |
|
92 |
end
|
|
93 |
|
|
94 |
for v in iter |
|
95 |
if orig_timestep_type <: DataValue |
|
96 |
push!(t_column, get(v[timestep_col_index])) |
|
97 |
else
|
|
98 |
push!(t_column, v[timestep_col_index]) |
|
99 |
end
|
|
100 |
|
|
101 |
if orig_data_type <: DataValue |
|
102 |
for (i,c) in enumerate(data_columns) |
|
103 |
push!(d_array[i],get(v[c[1]])) |
|
104 |
end
|
|
105 |
else
|
|
106 |
for (i,c) in enumerate(data_columns) |
|
107 |
push!(d_array[i],v[c[1]]) |
|
108 |
end
|
|
109 |
end
|
|
110 |
end
|
|
111 |
|
|
112 |
d_array = hcat(d_array...) |
|
113 |
|
|
114 |
ta = TimeSeries.TimeArray(t_column,d_array,[i[2][1] for i in data_columns]) |
|
115 |
return ta |
|
116 |
end
|
Read our documentation on viewing source code .