The OpenD Programming Language

Series

Plain index series data structure.

*.index[i]/*.key[i]/*.time corresponds to *.data[i]/*.value.

Index is assumed to be sorted. sort can be used to normalise a series.

import mir.series;
@fmamath
alias Series = mir_series

Examples

1-dimensional data

auto index = [1, 2, 3, 4];
auto data = [2.1, 3.4, 5.6, 7.8];
auto series = index.series(data);
const cseries = series;

assert(series.contains(2));
assert( ()@trusted{ return (2 in series) is &data[1]; }() );

assert(!series.contains(5));
assert( ()@trusted{ return (5 in series) is null; }() );

assert(series.lowerBound(2) == series[0 .. 1]);
assert(series.upperBound(2) == series[2 .. $]);

assert(cseries.lowerBound(2) == cseries[0 .. 1]);
assert(cseries.upperBound(2) == cseries[2 .. $]);

// slicing type deduction for const / immutable series
static assert(is(typeof(series[]) ==
    Series!(int*, double*)));
static assert(is(typeof(cseries[]) ==
    Series!(const(int)*, const(double)*)));
static assert(is(typeof((cast(immutable) series)[]) ==
    Series!(immutable(int)*, immutable(double)*)));

/// slicing
auto seriesSlice  = series[1 .. $ - 1];
assert(seriesSlice.index == index[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);

2-dimensional data

import mir.date: Date;
import mir.ndslice.topology: canonical, iota;

size_t row_length = 5;

auto index = [
    Date(2017, 01, 01),
    Date(2017, 02, 01),
    Date(2017, 03, 01),
    Date(2017, 04, 01)];

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

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

/// slicing
auto seriesSlice  = series[1 .. $ - 1, 2 .. 4];
assert(seriesSlice.index == index[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!int([row_length], 11)));

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

series.popFront!1;
assert(series.length!1 == 4);

Construct from null

import mir.series;
alias Map = Series!(string*, double*);
Map a = null;
auto b = Map(null);
assert(a.empty);
assert(b.empty);

auto fun(Map a = null)
{

}
import mir.series: series, sort;
auto s = ["b", "a"].series([9, 8]).sort;

import mir.format : text;
assert(s.text == `{ index: [a, b], data: [8, 9] }`);

Meta