Skip to content

Data types

zarr-matlab supports every Zarr v3 core data type plus the numpy extension types written by zarr-python.

Zarr data type MATLAB type Notes
bool logical
int8 … int64, uint8 … uint64 same-named integer
float32 / float64 single / double
float16 single converted losslessly on read; rounds to-nearest-even on write
complex64 / complex128 single / double complex
string string array variable-length UTF-8 (vlen-utf8 codec)
variable_length_bytes cell of uint8 row vectors vlen-bytes codec; create with "bytes"
numpy.datetime64 / numpy.timedelta64 int64 ticks see below
struct (legacy name structured) struct array records of named fixed-size fields; see below
fixed_length_utf32 string array fixed-size UTF-32 text, mostly as a struct field

zarr.create accepts either MATLAB class names ("double", "logical") or Zarr names ("float64", "bool").

store = zarr.stores.MemoryStore();

zc = zarr.create(store, 4, "complex128", Path="c");
zc(:) = [1+2i; 3-4i; complex(NaN, Inf); 0];
v = zc(:);
assert(v(2) == 3-4i && isnan(real(v(3))))

zh = zarr.create(store, 3, "float16", Path="h");
zh(:) = single([1.5; -2; 0.099976]);
assert(isa(zh(:), 'single'))

Strings

string arrays map to the Zarr string dtype with the vlen-utf8 codec — fully compatible with zarr-python string arrays, including empty strings and non-ASCII text. Compression codecs stack on top as usual.

labels = zarr.create(store, [2 2], "string", Path="labels", ...
    ChunkShape=[2 2], Codecs={zarr.codecs.GzipCodec(5)}, FillValue="?");
labels(1, :) = ["alpha" "beta"];
out = labels(:, :);
assert(out(1, 2) == "beta" && out(2, 1) == "?")

Raw byte strings use the "bytes" dtype and cell arrays of uint8:

raw = zarr.create(store, 3, "bytes", Path="raw");
raw(:) = {uint8([1 2 3]); uint8.empty(1, 0); uint8(255)};
out = raw(:);
assert(isequal(out{1}, uint8([1 2 3])) && isempty(out{2}))

Structured records

The struct data type stores records of named, fixed-size fields, which HDF5 and NWB call a compound type. MATLAB holds such an array as a struct array with one field per record field. The data type is defined by its list of fields, so zarr.create takes it as a struct with name and configuration, in the same shape it has in zarr.json. A text field is fixed_length_utf32, which spends four bytes per character.

labelType = struct('name', "fixed_length_utf32", ...
    'configuration', struct('length_bytes', 32));        % up to 8 characters
fields = struct('name', {'id'; 'label'}, 'data_type', {'int32'; labelType});
recordType = struct('name', "struct", 'configuration', struct('fields', fields));

records = zarr.create(store, 3, recordType, Path="records");
records.write(struct('id', {int32(1); int32(2)}, 'label', {"alpha"; "beta"}), 1);
out = records.read();
assert(out(2).label == "beta")
assert(out(3).id == 0 && out(3).label == "")              % default fill record

A FillValue for a structured array is a scalar struct with every field. Arrays that zarr-python wrote before version 3.3 use the legacy name structured; zarr-matlab reads them and writes them back under that name.

Datetimes: exact by design

zarr-python (and pandas/xarray) commonly write numpy.datetime64[ns]. MATLAB's datetime stores milliseconds as a double, which cannot represent nanosecond timestamps exactly — a silent-corruption trap. zarr-matlab therefore exposes these types as exact int64 ticks, with the unit available in the metadata:

t = zarr.create(store, 4, "datetime64[ns]", Path="t");   % fill = NaT (intmin)
t(1) = int64(0);                                         % ns since epoch
t(2) = int64(1700000000123456789);                       % exact (scalar literal)
t(3) = int64(-1);
v = t(:);
assert(v(2) == int64(1700000000123456789))               % exact
assert(v(4) == intmin('int64'))                          % NaT
disp(t.meta.dataTypeConfig.unit)                         % "ns"

Convert to MATLAB datetime when (sub-microsecond) precision loss is acceptable:

dt = datetime(1970, 1, 1) + seconds(double(v(2)) * 1e-9);
assert(year(dt) == 2023)

timedelta64 works identically ("timedelta64[ms]" etc.); NaT is intmin('int64') in both.