Time series API

This page provides some examples of how to use the time series API. Please check the time series functional documentation to learn about the time series modeling in PowSyBl.

Handling the index type

Create time series with irregular index

The following example shows how to create an irregular time series index with 3 instants, and how to iterate over all instants of the time index.

Instant t1 = Instant.parse("2018-01-01T00:00:00Z");
Instant t2 = t1.plusSeconds(1);
Instant t3 = t2.plusSeconds(2);
Instant t4 = t3.plusSeconds(3);
TimeSeriesIndex irregularIndex = IrregularTimeSeriesIndex.create(t1, t2, t3, t4);

To iterate over all instants of the time index:

irregularIndex.stream().forEach(System.out::println);

Output:

2018-01-01T00:00:00Z
2018-01-01T00:00:01Z
2018-01-01T00:00:03Z
2018-01-01T00:00:06Z

Create time series with regular index

The following example shows how to create a regular time series with 3 instants in steps of 1 second, and how to iterate over all instants:

Instant start = Instant.parse("2018-01-01T00:00:00Z");
Instant end = start.plusSeconds(3);
Duration spacing = Duration.ofSeconds(1);
TimeSeriesIndex regularIndex = RegularTimeSeriesIndex.create(start, end, spacing);

As with irregular time indexes, we can iterate over all instants:

regularIndex.stream().forEach(System.out::println);

Output:

2018-01-01T00:00:00Z
2018-01-01T00:00:01Z
2018-01-01T00:00:02Z
2018-01-01T00:00:03Z

Create time series with infinite index

An infinite time series is a time series with only two points: one at instant 0 and another at instant Long.MAX_VALUE. To get an infinite time series:

TimeSeriesIndex infiniteIndex = InfiniteTimeSeriesIndex.INSTANCE;

Handling the data type

Create double type time series

To create a double data time series based on time index regularIndex:

StoredDoubleTimeSeries dts = TimeSeries.createDouble("dts", regularIndex);

We now have a time series with 3 instants but without any data. By default the time series is filled with NaN values which means absent values.

double[] values = dts.toArray();
System.out.println(Arrays.toString(values));

Output:

[NaN, NaN, NaN, NaN]

Create string type time series

Similarly to double time series, to create a string data time series based on time index regularIndex:

StringTimeSeries sts = TimeSeries.createString("sts", regularIndex);

For string time series null values or empty strings are used to model absent values.

String[] values = sts.toArray();
System.out.println(Arrays.toString(values));

Output:

[null, null, null, null]

Handle data chunks

In order to add data to a time series, we need to create data chunks: DoubleDataChunks for double time series and StringDataChunks for string time series.

Double data chunk

The following example shows how to create an uncompressed data chunk and print its JSON representation:

DoubleDataChunk chunk = DataChunk.create(1d, 1d, 1d, 3d);
System.out.println(chunk.toJson());

Output:

{
  "offset" : 0,
  "values" : [ 1.0, 1.0, 1.0, 3.0 ]
}

The following example shows how to compress the chunk using the RLE compression algorithm:

DoubleDataChunk compressedChunk = chunk.tryToCompress();
System.out.println(compressedChunk.toJson());

Output:

{
  "offset" : 0,
  "uncompressedLength" : 4,
  "stepValues" : [ 1.0, 3.0 ],
  "stepLengths" : [ 3, 1 ]
}

The chunk.tryToCompress() method computes a compression factor by estimating the uncompressed and compressed data sizes of the data chunk. If the compression factor is greater or equals to one, it returns the chunk itself otherwise it returns a compressed data chunk.

The compression factor can be accessed using the getCompressionFactor() method:

System.out.println(compressedChunk.getCompressionFactor());

Output:

0.75

In this example, the compressed data chunk’s size is 25% smaller than the uncompressed one.

To add a compressed or uncompressed data chunk to a time series, use the addChunk method:

dts.addChunk(chunk); 

The time series now contains data. The following example shows how to print contained values:

System.out.println(Arrays.toString(dts.toArray()));

Output:

[1.0, 1.0, 1.0, 3.0]

String data chunk

The following example shows how to create a StringDataChunk instance, and the JSON representation of both the compressed and uncompressed version of this data chunk:

StringDataChunk chunk2 = DataChunk.create("hello", "bye", "bye", "bye");
System.out.println(chunk2.toJson());
System.out.println(chunk2.tryToCompress().toJson());

Output:

{
  "offset" : 0,
  "values" : [ "hello", "bye", "bye", "bye" ]
}
{
  "offset" : 0,
  "uncompressedLength" : 4,
  "stepValues" : [ "hello", "bye" ],
  "stepLengths" : [ 1, 3 ]
}

As with double time series, string data chunks can be added to a string time series:

sts.addChunk(chunk2); 

Calculated time series

The following example creates a calculated time series from an existing time series:

TimeSeriesIndex index = RegularTimeSeriesIndex.create(Interval.parse("2015-01-01T00:00:00Z/2015-07-20T00:00:00Z"), Duration.ofDays(200));
DoubleTimeSeries dts = TimeSeries.createDouble("dts", index, 1d, 2d);

List<DoubleTimeSeries> result = DoubleTimeSeries.fromTimeSeries(dts)
                                                .build("ts['a'] = ts['dts'] + 1",
                                                       "ts['b'] = ts['a'] * 2");
System.out.println(TimeSeries.toJson(result));

Output:

[ {
  "name" : "a",
  "expr" : {
    "binaryOp" : {
      "op" : "PLUS",
      "timeSeriesName" : "dts",
      "integer" : 1
    }
  }
}, {
  "name" : "b",
  "expr" : {
    "binaryOp" : {
      "op" : "MULTIPLY",
      "binaryOp" : {
        "op" : "PLUS",
        "timeSeriesName" : "dts",
        "integer" : 1
      },
      "integer" : 2
    }
  }
} ]

Calculated time series are evaluated on the fly during array conversion or iteration (through iterators or streams). Only the arithmetic expression is stored.

System.out.println(Arrays.toString(result.get(0).toArray()));
System.out.println(Arrays.toString(result.get(1).toArray()));

Output:

[2.0, 2.0, 2.0, 4.0]
[4.0, 4.0, 4.0, 8.0]

Check the functional documentation for the list of supported vector operations.

Handling CSV data

Time series can be imported from CSV:

String csv = String.join(System.lineSeparator(),
        "Time;Version;ts1;ts2",
        "1970-01-01T01:00:00.000+01:00;1;1.0;",
        "1970-01-01T02:00:00.000+01:00;1;;a",
        "1970-01-01T03:00:00.000+01:00;1;3.0;b") + System.lineSeparator();
Map<Integer, List<TimeSeries>> timeSeriesPerVersion = TimeSeries.parseCsv(csv, ';');

This CSV contains 2 time series, a double time series ts1 and a string time series ts2. The following example shows how to print instants and values of these 2 time series:

DoubleTimeSeries ts1 = (DoubleTimeSeries) timeSeriesPerVersion.get(1).get(0);
StringTimeSeries ts2 = (StringTimeSeries) timeSeriesPerVersion.get(1).get(1);
System.out.println("ts1 instants: " + Arrays.toString(ts1.getMetadata().getIndex().stream().toArray()));
System.out.println("ts1 values: " + Arrays.toString(ts1.toArray()));
System.out.println("ts2 instants: " + Arrays.toString(ts2.getMetadata().getIndex().stream().toArray()));
System.out.println("ts2 values: " + Arrays.toString(ts2.toArray()));

Output:

ts1 instants: [1970-01-01T00:00:00Z, 1970-01-01T01:00:00Z, 1970-01-01T02:00:00Z]
ts1 values: [1.0, NaN, 3.0]
ts2 instants: [1970-01-01T00:00:00Z, 1970-01-01T01:00:00Z, 1970-01-01T02:00:00Z]
ts2 values: [null, a, b]