Series and frames
Create series and frames, get values out of them, and use them with NumPy, pandas, and typed arrays.
A series holds the samples of one channel as a typed array. A frame holds the series of one or more channels, keyed by channel name or key. Reads and streamers return series and frames, and writers accept them.
Series
All samples in a series have one data type, such as float32 or timestamp.
Create a series
Python
TypeScript
import numpy as np
import synnax as sy
# The first value sets the data type: float64.
series = sy.Series([20.0, 20.5, 21.0])
# Set the data type yourself.
series = sy.Series([20.0, 20.5, 21.0], data_type=sy.DataType.FLOAT32)
# A NumPy array keeps its dtype: int16.
series = sy.Series(np.array([1, 2, 3], dtype=np.int16))
# Strings make a string series, and dictionaries make a json series.
series = sy.Series(["open", "closed"])
series = sy.Series([{"valve": "open"}, {"valve": "closed"}])The first value of a list sets the data type. sy.Series([20, 20.5]) is an int64
series that holds [20, 20]. Write 20.0, or set data_type. A list that starts with
a number and holds a string raises a ValueError.
import { Series } from "@synnaxlabs/client";
// Numbers make a float64 series.
const pressures = new Series([20.0, 20.5, 21.0]);
// Set the data type yourself.
const float32s = new Series({ data: [20.0, 20.5, 21.0], dataType: "float32" });
// A typed array keeps its type. This is the fastest way to build a large series.
const int16s = new Series(new Int16Array([1, 2, 3]));
// Strings make a string series, and objects make a json series.
const states = new Series(["open", "closed"]);
const events = new Series([{ valve: "open" }, { valve: "closed" }]);All values must have the same type. The constructor does not check: a string in a number
series becomes NaN.
Use a series as an array
Python
TypeScript
A series works with NumPy, so you can pass it to NumPy, pandas, or Matplotlib with no conversion:
import matplotlib.pyplot as plt
import numpy as np
import synnax as sy
series = sy.Series([20.0, 20.5, 21.0])
print(np.mean(series)) # 20.5
array = series.to_numpy()
plt.plot(series)A series is iterable. The data property returns the samples of a numeric series as a
typed array, such as a Float64Array:
const series = new Series([20.0, 20.5, 21.0]);
console.log([...series]); // [ 20, 20.5, 21 ]
console.log(series.data); // Float64Array(3) [ 20, 20.5, 21 ]Get a sample
Python
TypeScript
import synnax as sy
series = sy.Series([20.0, 20.5, 21.0])
print(series[0]) # 20.0
print(series[-1]) # 21.0const series = new Series([20.0, 20.5, 21.0]);
console.log(series.at(0)); // 20
console.log(series.at(-1)); // 21
console.log(series.at(5)); // undefined
series.at(5, true); // throws, because the index is out of rangeat returns undefined when the index is outside the series. Pass true as the second
argument to throw instead.
Series properties
The time range of a series starts at its first sample and ends just after its last sample.
Frames
A frame holds the series of one or more channels. Each channel can have more than one series. Here, a frame holds the series of an index channel and a data channel:
These calls return a frame:
client.readwith more than one channel. With one channel name or key, it returns aMultiSeries.readon a streamer.valueon an iterator.
Create a frame
Python
TypeScript
import pandas as pd
import synnax as sy
# Key the series by channel name, or by channel key.
frame = sy.Frame({
"tank_pressure": sy.Series([20.0, 20.5, 21.0]),
"tank_temperature": sy.Series([290.0, 290.2, 290.4]),
})
# One sample for each channel.
frame = sy.Frame({"tank_pressure": 20.0, "tank_temperature": 290.0})
# Many series for one channel: a list of channels and a list of series.
frame = sy.Frame(
["tank_pressure", "tank_pressure"],
[sy.Series([20.0, 20.5]), sy.Series([21.0, 21.5])],
)
# Each column of a DataFrame becomes a series.
frame = sy.Frame(pd.DataFrame({"tank_pressure": [20.0, 20.5, 21.0]}))import { Frame, Series } from "@synnaxlabs/client";
// Key the series by channel name, or by channel key.
const frame = new Frame({
tank_pressure: new Series([20.0, 20.5, 21.0]),
tank_temperature: new Series([290.0, 290.2, 290.4]),
});
// Many series for one channel.
const multi = new Frame({
tank_pressure: [new Series([20.0, 20.5]), new Series([21.0, 21.5])],
});The series in a frame can have different lengths. A write has more rules: see the rules of writes.
Get data from a frame
Get the data of a channel by its name or key. The result is a MultiSeries: all the
series of the channel, used like one series.
Python
TypeScript
frame = sy.Frame(
["tank_pressure", "tank_pressure"],
[sy.Series([20.0, 20.5]), sy.Series([21.0, 21.5])],
)
pressure = frame["tank_pressure"]
print(pressure.to_numpy()) # [20. 20.5 21. 21.5]
print(pressure[0]) # 20.0
print(len(pressure.series)) # 2
# One column for each channel.
df = frame.to_df()to_df gives one column for each channel. When the frame holds the index channel of
each channel, as a frame that you read with its index channels does, the DataFrame
index is the UTC timestamps of the samples, and the index channels are not columns. The
index has the name of the index channel, or time when the frame holds more than one
index channel. Channels with different index channels are joined on time, and a cell is
empty when a channel has no sample at that time. Integer columns with empty cells use
pandas nullable types, such as Int64, to keep large values exact. A min_max read is
not indexed by time, because its samples do not keep their times. In other frames, the
index is the sample number, and all channels must have the same number of samples.
frame[mask] selects the rows of to_df where a boolean mask is true. The mask has one
value for each row.
When the frame has no series for the channel, frame["name"] returns an empty
MultiSeries, and frame.get("name") returns None or the default that you give. A
frame that you read by key also finds a channel by its name. When two channels in the
frame have the same name, frame["name"] raises a MultipleFoundError.
const frame = new Frame({
tank_pressure: [new Series([20.0, 20.5]), new Series([21.0, 21.5])],
tank_temperature: new Series([290.0, 290.2, 290.4, 290.6]),
});
const pressure = frame.get("tank_pressure");
console.log([...pressure]); // [ 20, 20.5, 21, 21.5 ]
console.log(pressure.at(0)); // 20
console.log(pressure.series.length); // 2
// One sample for each channel, at an index.
console.log(frame.at(1)); // { tank_pressure: 20.5, tank_temperature: 290.2 }frame.at returns undefined for a channel that has no sample at the index. Pass
true as the second argument to throw instead.
Typed samples in TypeScript
at returns a TelemValue: a number, bigint, string, boolean, or TimeStamp. When you
know the type of a series, as returns a Series of that type:
const series = new Series([20.0, 20.5, 21.0]);
const numbers: Series<number> = series.as("number");
const first: number = numbers.at(0, true);as takes "number", "bigint", "string", or "boolean". For "number",
"bigint", and "boolean", it throws when the data type does not match:
cannot convert series of type string to number