Loading Skims from Files#
Sharrow can load skim data from several common file formats. This walkthrough
uses the same small autotime skim to demonstrate three layouts:
Zarr stores the three dimensions natively.
OMX stores one matrix per time period, with the period encoded in the matrix name.
Parquet stores one column per time period, with named origin and destination columns identifying the matrix indexes.
The example creates temporary files so that it can be run without downloading any data.
from pathlib import Path
from tempfile import TemporaryDirectory
import numpy as np
import openmatrix
import pandas as pd
import xarray as xr
import sharrow as sh
Example skim data#
The source dataset has native otaz, dtaz, and timeperiod dimensions.
The values are deliberately small and distinct so that the loaded data can be
checked easily.
zones = np.array([101, 102, 103])
timeperiods = ["AM", "PM"]
autotime = np.array(
[
[[1.0, 1.5], [2.0, 2.5], [3.0, 3.5]],
[[4.0, 4.5], [5.0, 5.5], [6.0, 6.5]],
[[7.0, 7.5], [8.0, 8.5], [9.0, 9.5]],
],
dtype=np.float32,
)
source_skims = xr.Dataset(
{"autotime": (("otaz", "dtaz", "timeperiod"), autotime)},
coords={"otaz": zones, "dtaz": zones, "timeperiod": timeperiods},
)
source_skims
<xarray.Dataset> Size: 136B
Dimensions: (otaz: 3, dtaz: 3, timeperiod: 2)
Coordinates:
* otaz (otaz) int64 24B 101 102 103
* dtaz (dtaz) int64 24B 101 102 103
* timeperiod (timeperiod) <U2 16B 'AM' 'PM'
Data variables:
autotime (otaz, dtaz, timeperiod) float32 72B 1.0 1.5 2.0 ... 8.5 9.0 9.5temporary_directory = TemporaryDirectory()
data_directory = Path(temporary_directory.name)
Zarr: native three-dimensional data#
Zarr preserves the dimensions and coordinates in the store. from_zarr reads
the _ARRAY_DIMENSIONS metadata written by xarray, so no dimension mapping is
needed when loading.
zarr_path = data_directory / "skims.zarr"
source_skims.to_zarr(zarr_path, mode="w")
zarr_skims = sh.dataset.from_zarr(zarr_path)
zarr_skims
<xarray.Dataset> Size: 136B
Dimensions: (otaz: 3, dtaz: 3, timeperiod: 2)
Coordinates:
* dtaz (dtaz) int64 24B 101 102 103
* otaz (otaz) int64 24B 101 102 103
* timeperiod (timeperiod) <U2 16B 'AM' 'PM'
Data variables:
autotime (otaz, dtaz, timeperiod) float32 72B dask.array<chunksize=(3, 3, 2), meta=np.ndarray>OMX: time period in matrix names#
The openmatrix (OMX) standard defines a data format that stores two-dimensional matrix data. To represent a third dimension, one approach is to store a matrix for each time period and include the time period after a double underscore in the matrix name, such as autotime__AM.
omx_path = data_directory / "skims.omx"
with openmatrix.open_file(omx_path, mode="w") as omx_file:
for timeperiod in timeperiods:
omx_file.create_carray(
"/data",
f"autotime__{timeperiod}",
obj=source_skims.autotime.sel(timeperiod=timeperiod).values,
)
omx_file.create_carray("/lookup", "taz", obj=zones)
omx_file.root._v_attrs.SHAPE = np.array([len(zones), len(zones)])
Sharrow includes the code needed to read this format of stored data into a three dimension dataset.
omx_skims = sh.dataset.from_omx_3d(
str(omx_path),
index_names=("otaz", "dtaz", "timeperiod"),
time_periods=timeperiods,
)
omx_skims
<xarray.Dataset> Size: 136B
Dimensions: (otaz: 3, dtaz: 3, timeperiod: 2)
Coordinates:
* otaz (otaz) int64 24B 101 102 103
* dtaz (dtaz) int64 24B 101 102 103
* timeperiod (timeperiod) <U2 16B 'AM' 'PM'
Data variables:
autotime (otaz, dtaz, timeperiod) float32 72B dask.array<chunksize=(3, 3, 1), meta=np.ndarray>Parquet: named indexes and time-period columns#
Sometimes, skim data is stored as a table. This is especially convenient when passing
data to tools which prefer tabular data and don’t natively handle multi-dimensional
tensors. The parquet format is widely used for tabular data and can be used to
store skims as a table. Here, origin and destination name the
first two dimensions, while autotime__AM and autotime__PM encode the third
dimension in their column names.
origin, destination = np.meshgrid(zones, zones, indexing="ij")
parquet_data = pd.DataFrame(
{
"origin": origin.ravel(),
"destination": destination.ravel(),
"autotime__AM": source_skims.autotime.sel(timeperiod="AM").values.ravel(),
"autotime__PM": source_skims.autotime.sel(timeperiod="PM").values.ravel(),
}
)
parquet_path = data_directory / "skims.parquet"
parquet_data.to_parquet(parquet_path, index=False)
Sharrow can also read this format of data back into the preferred three dimension dataset in memory.
parquet_skims = sh.dataset.from_parquet_3d(
parquet_path,
index_names=("origin", "destination", "timeperiod"),
time_periods=timeperiods,
)
parquet_skims
<xarray.Dataset> Size: 136B
Dimensions: (origin: 3, destination: 3, timeperiod: 2)
Coordinates:
* origin (origin) int64 24B 101 102 103
* destination (destination) int64 24B 101 102 103
* timeperiod (timeperiod) <U2 16B 'AM' 'PM'
Data variables:
autotime (origin, destination, timeperiod) float32 72B 1.0 1.5 ... 9.5