Report a bug
If you spot a problem with this page, click here to create a Github issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

mir.timeseries

Time-series

The module contains Series data structure with special iteration and indexing methods. It is aimed to construct time-series using Mir and Phobos algorithms.

Public imports: mir.ndslice.slice.

Authors:
Ilya Yaroshenko
Examples:
import std.datetime: Date;
import std.algorithm.setops: nWayUnion;
import std.algorithm.iteration: uniq;
import std.array: array;
import mir.ndslice.slice: sliced;
import mir.ndslice.allocation: slice;

auto time0 = [
    Date(2017, 01, 01),
    Date(2017, 03, 01),
    Date(2017, 04, 01)].sliced;

auto data0 = [1.0, 3, 4].sliced;
auto series0 = time0.series(data0);

auto time1 = [
    Date(2017, 01, 01),
    Date(2017, 02, 01),
    Date(2017, 05, 01)].sliced;

auto data1 = [10.0, 20, 50].sliced;
auto series1 = time1.series(data1);

auto time = [time0, time1].nWayUnion.uniq.array.sliced;
auto data = slice!double([time.length, 2], 0); // initialized to 0 value
auto series = time.series(data);

series[0 .. $, 0][] = series0; // fill first column
series[0 .. $, 1][] = series1; // fill second column

assert(data == [
    [1, 10],
    [0, 20],
    [3,  0],
    [4,  0],
    [0, 50]]);
struct Observation(Time, Data);
Plain time observation data structure. Observation are used as return tuple for for indexing Series.
Time time;
Date, date-time, time, or integer.
Data data;
Value or ndslice.
auto observation(Time, Data)(Time time, Data data);
Convenient function for Observation construction.
struct Series(TimeIterator, SliceKind kind, size_t[] packs, Iterator);
Plain time series data structure.
*.time[i] corresponds to *.data[i].
Examples:
1-dimensional data
auto time = [1, 2, 3, 4].sliced;
auto data = [2.1, 3.4, 5.6, 7.8].sliced;
auto series = time.series(data);

/// slicing
auto seriesSlice  = series[1 .. $ - 1];
assert(seriesSlice.time == time[1 .. $ - 1]);
assert(seriesSlice.data == data[1 .. $ - 1]);
static assert(is(typeof(series) == typeof(seriesSlice)));

/// indexing
assert(series[1] == observation(2, 3.4));

/// range primitives
assert(series.length == 4);
assert(series.front == observation(1, 2.1));

series.popFront;
assert(series.front == observation(2, 3.4));

series.popBackN(10);
assert(series.empty);
Examples:
2-dimensional data
import std.datetime: Date;
import mir.ndslice.topology: canonical, iota;

size_t row_length = 5;

auto time = [
    Date(2017, 01, 01),
    Date(2017, 02, 01),
    Date(2017, 03, 01),
    Date(2017, 04, 01)].sliced;

//  1,  2,  3,  4,  5
//  6,  7,  8,  9, 10
// 11, 12, 13, 14, 15
// 16, 17, 18, 19, 20
auto data = iota([time.length, row_length], 1);

// canonical and universal ndslices are more flexible then contiguous
auto series = time.series(data.canonical);

/// slicing
auto seriesSlice  = series[1 .. $ - 1, 2 .. 4];
assert(seriesSlice.time == time[1 .. $ - 1]);
assert(seriesSlice.data == data[1 .. $ - 1, 2 .. 4]);

static if (kindOf!(typeof(series.data)) != Contiguous)
    static assert(is(typeof(series) == typeof(seriesSlice)));

/// indexing
assert(series[1, 4] == observation(Date(2017, 02, 01), 10));
assert(series[2] == observation(Date(2017, 03, 01), iota([row_length], 11)));

/// range primitives
assert(series.length == 4);
assert(series.length!1 == 5);

series.popFront!1;
assert(series.length!1 == 4);
Slice!(Contiguous, [1], TimeIterator) time;
Time series is assumed to be sorted.
TimeIterator is an iterator on top of date, date-time, time, or integer types. For example, Date*, DateTime*, immutable(long)*, mir.ndslice.iterator.IotaIterator.
Slice!(kind, packs, Iterator) data;
Data is any ndslice with only one constraints, data and time lengths should be equal.
void opIndexAssign(TimeIterator_, SliceKind kind_, size_t[] packs_, Iterator_)(Series!(TimeIterator_, kind_, packs_, Iterator_) r);
Special [] = index-assign operator for time-series. Assigns data from r with time intersection. If a time index in r is not in the time index for this series, then no op-assign will take place. This and r series are assumed to be sorted.
Parameters:
Series!(TimeIterator_, kind_, packs_, Iterator_) r rvalue time-series
Examples:
auto time = [1, 2, 3, 4].sliced;
auto data = [10.0, 10, 10, 10].sliced;
auto series = time.series(data);

auto rtime = [0, 2, 4, 5].sliced;
auto rdata = [1.0, 2, 3, 4].sliced;
auto rseries = rtime.series(rdata);

series[] = rseries;
assert(series.data == [10, 2, 10, 3]);
void opIndexOpAssign(string op, TimeIterator_, SliceKind kind_, size_t[] packs_, Iterator_)(Series!(TimeIterator_, kind_, packs_, Iterator_) r);
Special [] op= index-op-assign operator for time-series. Op-assigns data from r with time intersection. If a time index in r is not in the time index for this series, then no op-assign will take place. This and r series are assumed to be sorted.
Parameters:
Series!(TimeIterator_, kind_, packs_, Iterator_) r rvalue time-series
Examples:
auto time = [1, 2, 3, 4].sliced;
auto data = [10.0, 10, 10, 10].sliced;
auto series = time.series(data);

auto rtime = [0, 2, 4, 5].sliced;
auto rdata = [1.0, 2, 3, 4].sliced;
auto rseries = rtime.series(rdata);

series[] += rseries;
assert(series.data == [10, 12, 10, 13]);
const @property bool empty(size_t dimension = 0)()
if (dimension < packs[0]);

const @property size_t length(size_t dimension = 0)()
if (dimension < packs[0]);

@property auto front(size_t dimension = 0)()
if (dimension < packs[0]);

@property auto back(size_t dimension = 0)()
if (dimension < packs[0]);

void popFront(size_t dimension = 0)()
if (dimension < packs[0]);

void popBack(size_t dimension = 0)()
if (dimension < packs[0]);

void popFrontExactly(size_t dimension = 0)(size_t n)
if (dimension < packs[0]);

void popBackExactly(size_t dimension = 0)(size_t n)
if (dimension < packs[0]);

void popFrontN(size_t dimension = 0)(size_t n)
if (dimension < packs[0]);

void popBackN(size_t dimension = 0)(size_t n)
if (dimension < packs[0]);

const _Slice!() opSlice(size_t dimension)(size_t i, size_t j)
if (dimension < packs[0]);

const size_t opDollar(size_t dimension = 0)();

auto opIndex(Slices...)(Slices slices)
if (allSatisfy!(templateOr!(is_Slice, isIndex), Slices));

@property auto save()();
ndslice-like primitives
auto series(TimeIterator, SliceKind kind, size_t[] packs, Iterator)(Slice!(Contiguous, [1], TimeIterator) time, Slice!(kind, packs, Iterator) data);
Convenient function for Series construction.
enum auto isSeries(U : Series!(TimeIterator, kind, packs, Iterator), TimeIterator, SliceKind kind, size_t[] packs, Iterator);

enum auto isSeries(U);
Returns:
packs if U is a Series type or null otherwise;
template sort(alias less = "a < b")
Sorts time-series according to the less predicate applied to time observations.
The function works only for 1-dimensional time-series data.
Examples:
1D data
auto time = [1, 2, 4, 3].sliced;
auto data = [2.1, 3.4, 5.6, 7.8].sliced;
auto series = time.series(data);
series.sort;
assert(series.time == [1, 2, 3, 4]);
assert(series.data == [2.1, 3.4, 7.8, 5.6]);
/// initial time and data are the same
assert(time.iterator is series.time.iterator);
assert(data.iterator is series.data.iterator);
Examples:
2D data
import mir.timeseries;
import mir.ndslice.allocation: uninitSlice;

auto time = [4, 2, 3, 1].sliced;
auto data =
    [2.1, 3.4, 
     5.6, 7.8,
     3.9, 9.0,
     4.0, 2.0].sliced(4, 2);
auto series = time.series(data);

series.sort(
    uninitSlice!size_t(series.length), // index buffer
    uninitSlice!double(series.length), // data buffer
    );

assert(series.time == [1, 2, 3, 4]);
assert(series.data ==
    [[4.0, 2.0],
     [5.6, 7.8],
     [3.9, 9.0],
     [2.1, 3.4]]);
/// initial time and data are the same
assert(time.iterator is series.time.iterator);
assert(data.iterator is series.data.iterator);
Series!(TimeIterator, kind, packs, Iterator) sort(TimeIterator, SliceKind kind, size_t[] packs, Iterator)(Series!(TimeIterator, kind, packs, Iterator) series)
if (packs == [1]);
One dimensional case.
Series!(TimeIterator, kind, packs, Iterator) sort(TimeIterator, SliceKind kind, size_t[] packs, Iterator, SliceKind indexKind, IndexIterator, SliceKind dataKind, DataIterator)(Series!(TimeIterator, kind, packs, Iterator) series, Slice!(indexKind, [1], IndexIterator) indexBuffer, Slice!(dataKind, [1], DataIterator) dataBuffer)
if (packs.length == 1);
N-dimensional case. Requires index and data buffers.