Multivariate Time Series

When you have multiple signals that share the same timestamps — like wind, solar, and hydro power — use TimeSeriesTable (also aliased as MultivariateTimeSeries). It stores per-column metadata and a 2D numpy array of values.

[1]:
from datetime import datetime, timedelta, timezone

import numpy as np

import timedatamodel as tdm

base = datetime(2024, 1, 15, tzinfo=timezone.utc)
timestamps = [base + timedelta(hours=i) for i in range(24)]
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[1], line 5
      1 from datetime import datetime, timedelta, timezone
      3 import numpy as np
----> 5 import timedatamodel as tdm
      7 base = datetime(2024, 1, 15, tzinfo=timezone.utc)
      8 timestamps = [base + timedelta(hours=i) for i in range(24)]

ModuleNotFoundError: No module named 'timedatamodel'

Creating a TimeSeriesTable

Pass a 2D array (rows = timestamps, columns = variables) along with per-column metadata.

[2]:
rng = np.random.default_rng(42)

wind = 80 + 40 * np.sin(np.linspace(0, 2 * np.pi, 24)) + rng.normal(0, 5, 24)
solar = np.clip(60 * np.sin(np.linspace(-0.5, np.pi + 0.5, 24)), 0, None) + rng.normal(0, 2, 24)
solar = np.clip(solar, 0, None)
hydro = 50 + rng.normal(0, 3, 24)

values = np.column_stack([wind, solar, hydro])

table = tdm.TimeSeriesTable(
    tdm.Frequency.PT1H,
    timezone="UTC",
    timestamps=timestamps,
    values=values,
    names=["wind", "solar", "hydro"],
    units=["MW", "MW", "MW"],
    data_types=[tdm.DataType.MEASUREMENT, tdm.DataType.MEASUREMENT, tdm.DataType.MEASUREMENT],
)
table
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[2], line 10
      6 hydro = 50 + rng.normal(0, 3, 24)
      8 values = np.column_stack([wind, solar, hydro])
---> 10 table = tdm.TimeSeriesTable(
     11     tdm.Frequency.PT1H,
     12     timezone="UTC",
     13     timestamps=timestamps,
     14     values=values,
     15     names=["wind", "solar", "hydro"],
     16     units=["MW", "MW", "MW"],
     17     data_types=[tdm.DataType.MEASUREMENT, tdm.DataType.MEASUREMENT, tdm.DataType.MEASUREMENT],
     18 )
     19 table

NameError: name 'tdm' is not defined

Inspecting table properties

[3]:
print(f"Columns:     {table.column_names}")
print(f"Shape:       ({len(table)}, {table.n_columns})")
print(f"Begin:       {table.begin}")
print(f"End:         {table.end}")
print(f"Has missing: {table.has_missing}")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[3], line 1
----> 1 print(f"Columns:     {table.column_names}")
      2 print(f"Shape:       ({len(table)}, {table.n_columns})")
      3 print(f"Begin:       {table.begin}")

NameError: name 'table' is not defined

Selecting a single column

select_column() extracts one column as a univariate TimeSeries, carrying over its metadata.

[4]:
ts_wind = table.select_column("wind")
ts_wind
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[4], line 1
----> 1 ts_wind = table.select_column("wind")
      2 ts_wind

NameError: name 'table' is not defined
[5]:
ts_solar = table.select_column("solar")
print(f"Name: {ts_solar.name}, Unit: {ts_solar.unit}, Data type: {ts_solar.data_type}")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[5], line 1
----> 1 ts_solar = table.select_column("solar")
      2 print(f"Name: {ts_solar.name}, Unit: {ts_solar.unit}, Data type: {ts_solar.data_type}")

NameError: name 'table' is not defined

Merging univariate series into a table

TimeSeries.merge() is the reverse operation — combine several univariate series that share the same timestamps.

[6]:
ts_a = tdm.TimeSeries(
    tdm.Frequency.PT1H,
    timestamps=timestamps,
    values=wind.tolist(),
    name="wind",
    unit="MW",
)
ts_b = tdm.TimeSeries(
    tdm.Frequency.PT1H,
    timestamps=timestamps,
    values=solar.tolist(),
    name="solar",
    unit="MW",
)
ts_c = tdm.TimeSeries(
    tdm.Frequency.PT1H,
    timestamps=timestamps,
    values=hydro.tolist(),
    name="hydro",
    unit="MW",
)

merged = tdm.TimeSeries.merge([ts_a, ts_b, ts_c])
merged
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[6], line 1
----> 1 ts_a = tdm.TimeSeries(
      2     tdm.Frequency.PT1H,
      3     timestamps=timestamps,
      4     values=wind.tolist(),
      5     name="wind",
      6     unit="MW",
      7 )
      8 ts_b = tdm.TimeSeries(
      9     tdm.Frequency.PT1H,
     10     timestamps=timestamps,
   (...)     13     unit="MW",
     14 )
     15 ts_c = tdm.TimeSeries(
     16     tdm.Frequency.PT1H,
     17     timestamps=timestamps,
   (...)     20     unit="MW",
     21 )

NameError: name 'tdm' is not defined

Decomposing a table back to univariate series

[7]:
univariate_list = merged.to_univariate_list()
for ts in univariate_list:
    print(f"{ts.name}: {len(ts)} points, unit={ts.unit}")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[7], line 1
----> 1 univariate_list = merged.to_univariate_list()
      2 for ts in univariate_list:
      3     print(f"{ts.name}: {len(ts)} points, unit={ts.unit}")

NameError: name 'merged' is not defined

Table arithmetic

Scalar arithmetic works on all columns simultaneously.

[8]:
table_gw = table * 0.001
table_gw.head(5)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[8], line 1
----> 1 table_gw = table * 0.001
      2 table_gw.head(5)

NameError: name 'table' is not defined

Iteration and indexing

Each row is a (timestamp, [values...]) tuple.

[9]:
ts_val, row_vals = table[0]
print(f"Timestamp: {ts_val}")
print(f"Values:    {row_vals}")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[9], line 1
----> 1 ts_val, row_vals = table[0]
      2 print(f"Timestamp: {ts_val}")
      3 print(f"Values:    {row_vals}")

NameError: name 'table' is not defined
[10]:
for ts_val, row_vals in table.head(4):
    wind_mw, solar_mw, hydro_mw = row_vals
    print(f"{ts_val:%Y-%m-%d %H:%M}  wind={wind_mw:6.1f}  solar={solar_mw:5.1f}  hydro={hydro_mw:5.1f}")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[10], line 1
----> 1 for ts_val, row_vals in table.head(4):
      2     wind_mw, solar_mw, hydro_mw = row_vals
      3     print(f"{ts_val:%Y-%m-%d %H:%M}  wind={wind_mw:6.1f}  solar={solar_mw:5.1f}  hydro={hydro_mw:5.1f}")

NameError: name 'table' is not defined

Conversion to pandas DataFrame

[11]:
df = table.to_pandas_dataframe()
df.head()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[11], line 1
----> 1 df = table.to_pandas_dataframe()
      2 df.head()

NameError: name 'table' is not defined

Summary

  • TimeSeriesTable stores multiple aligned columns with per-column metadata

  • select_column() extracts a single column as a TimeSeries

  • TimeSeries.merge() combines univariate series into a table

  • to_univariate_list() decomposes a table back to individual series

  • Scalar arithmetic applies across all columns

Next up: nb_06 covers TimeSeriesCube and TimeSeriesCollection for higher-dimensional data.