0.60

ReferenceClient librariesSeries and frames

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

Enterprise
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.

Use a series as an array

Enterprise

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)

Get a sample

Enterprise
import synnax as sy

series = sy.Series([20.0, 20.5, 21.0])
print(series[0])  # 20.0
print(series[-1])  # 21.0

Series properties

Python TypeScript Gives you
len(series) series.length The number of samples.
series.data_type series.dataType The data type, such as float32.
series.time_range series.timeRange The time range of the samples, on a series that a read returns.
np.max(series) series.max The largest sample.
np.min(series) series.min The smallest sample.
series.bounds The smallest and largest samples, as { lower, upper }.

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:

tank_time tank_pressure
2026-10-03 12:00:00.000 20.0
2026-10-03 12:00:00.100 20.5
2026-10-03 12:00:00.200 21.0

These calls return a frame:

  • client.read with more than one channel. With one channel name or key, it returns a MultiSeries.
  • read on a streamer.
  • value on an iterator.

Create a frame

Enterprise
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]}))

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.

Enterprise
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.

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