Changelog¶
All notable changes to sdata are documented here. The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased¶
Changed¶
- Deterministic
DataFramecontent checksums (RFC 0004). The hash basisDataFrame.content_bytes(behindsha256/sha1/md5/sizeandupdate_checksum/verify) is now the canonical CSV form (to_csv(index=False), UTF-8, index excluded) instead ofto_parquet(). Parquet is not byte-stable (pyarrow version, embeddedcreated_by, compression, platform), so Parquet-based checksums were not portable; the canonical CSV depends only on the logical data, so a checksum written on one machine verifies on another and survives pyarrow/pandas upgrades — and needs no pyarrow. Note: checksums produced by older versions differ from the new ones; callupdate_checksum()to refresh stored values.as_blob(fmt)blobs still hash the actual format bytes (asset integrity), which is intentionally separate.
Added¶
pintinterop beyond the curated unit table (RFC 0006). With the optional[units]extra (pint) installed, units outside the curated table (imperial/derived:psi,inch,lbf,degF, …) are mapped through pint onto the same five-axis dimension vector, sodimension_of/convert/convert_factor/UnitSystemaccept them transparently (convert(1, "psi", "MPa"),UnitSystem(["lbf", "inch", "s"])). The curated table always takes precedence; without pint the behaviour is unchanged. Limits: axes outside(L, M, T, Θ, A)(electric current, amount of substance) are not representable, and angles follow pint's dimensionless model — use the curatedrad/deg/gonfor angles.- Angle units
rad/deg/gon/mradon their own dimension axis (RFC 0006). Plane angle is now a distinct base dimension (A) rather than dimensionless, so the angle units interconvert (180 deg == π rad,200 gon == 180 deg) but are kept separate from plain numbers/percent (convert(1, "rad", "%")raises). QUDT-mapped (unit:RAD/unit:DEG/unit:GON); aUnitSystembuilt onrad/degnormalizes angles, a purely mechanical system leaves them untouched. Ambiguousgrad(English gradian vs. German degree) is deliberately not mapped; logarithmic units (dB) stay out of the linear factor model. The internal dimension vector grows from four to five components. - Parquet directory mode (RFC 0007 F5 / RFC 0011).
ParquetWriter(uri, directory=True)writes one<sname>.spqperwriteinto a directory instead of overwriting a single URI, sowrite_groupkeeps each table in its own file;ParquetReader(uri, directory=True)withkeys()+read_groupreads the directory back into aDataFrameGroup(symmetric). Single-file mode is unchanged. - Frequency units
Hz/kHz/MHz/GHz(RFC 0006). Recognized as named units (input, conversion, QUDTunit:HZ), interconvertible with the1/sfamily. The rate/frequency dimension keeps its neutral canonical back-name (1/s/1/ms) — notHz— because the same dimension also denotes strain rate;_CANON_SYMBOLSis now documented as the preferred-symbol table (energy→J, power→W, rate→1/s). - Native per-column HDF5 attributes (RFC 0002).
to_hdfnow attaches each column'sunit/label/description/ontologynatively as HDF5 dataset attributes (readable by any HDF5 tool — h5py/HDFView/h5ls), alongside the_sdatablob;from_hdfmerges them back intocolumn_metadata, even for foreign files without the blob (symmetric to the Arrow field-metadata path). - Persistent target unit system (
DataFrame.unit_system, RFC 0014). The table's target unit system (RFC 0006) now survives serialization: it is stored as a reserved_sdata_unit_systemmetadata field (the base-unit list), so it round-trips through dict, Parquet, HDF5 and JSON-LD (sdata:unitSystem) automatically. A DataFrame loaded from Parquet keeps itsconvert()target;UnitSystemgains__eq__/__hash__.
1.4.0 - 2026-07-02¶
The RFC 0008 improvement program plus the unit-conversion and writer/reader layers:
a symmetric reader interface, explicit format versioning, persistence consolidation and
a PEP 621 packaging modernization. Strictly additive; core dependencies stay
numpy, pandas, suuid.
Added¶
- DataFrame reader interface (
sdata.iolib.reader, RFC 0009). Symmetric to the writer: aDataFrameReaderprotocol +BaseDataFrameReadertemplate with the samerequire_*contract,ParquetReader/StoreReader/SqlReaderand aread_groupbatch helper — round-tripping each backend with its qualifying metadata. - Format versioning (
sdata.format, RFC 0010). A_sdata_format_versionstamped on every object, withread_format_version/ensure_compatible/register_migration, aFormatVersionWarningand anIncompatibleFormatError;Base.from_dictand theDataFramerestore paths check compatibility (opt-instrict=). - Writer interface for DataFrames (
sdata.iolib.writer, RFC 0007). A unifying sink abstraction over the existing serializers: aDataFrameWriterprotocol +BaseDataFrameWritertemplate method with arequire_metadata/require_columns/require_unitscontract, a uniformWriteReceiptreturn, and theensure_sdata/write_with_provenancebridge (metadata truth source staysmetadata/column_metadata, notdf.attrs). Four sinks:ParquetWriter(fsspec URI, embedded_sdata),StoreWriter(object persistence into aJSON1SQLiteStore, findable bysuuid/snamevia a flattening adapter),SqlWriter(relational table + sharedsdata_dataframe_metatable, atomic data+metadata write, allowlisted identifiers) andGraphWriter(RDF/Turtle or JSON-LD, optional named-graph accumulation viasdata[rdf]). Runs on the stdlibsqlite3; ausage/writer.mdguide and RFC 0007 document it. - Unit conversion via dimensional algebra (
sdata.units, RFC 0006). A pure-Python conversion layer:convert/convert_factor/quantity_of/dimension_ofand aUnitSystemthat is solved from its base units — so a consistent system such as["kN", "mm", "ms"]derives all units (stress→GPa, energy→J, velocity→m/s, mass→kg) from the base set via an exactfractions-based solver. Covers length, mass, time, temperature (offset units), force, pressure, energy, power, velocity, area/volume and rate; works on scalars/lists/NumPy arrays/pandas Series; raises a clearUnitConversionErroron incompatible dimensions or inconsistent systems. DataFrame.convert(units=None, inplace=False). Convert a table's columns into a target unit system (a unit list, aUnitSystem, or an explicit{column: unit}mapping) — including derived units — and update the per-columnunitannotations, returning a converted copy by default. Columns without a unit or whose dimension the system does not span are left unchanged.DataFrame.relabel_units(mapping, *, force=False). Fix mislabeled units: set theunitof named columns without rescaling the values; same-dimension relabels are logged, a dimension change requiresforce=True. Returns a report.DataFrame.unit_system. A settable target unit system on the table (aUnitSystemor unit list, also via theunit_system=constructor keyword);convert()with no argument converts into it. Setting only records the target (the data is rescaled byconvert()), and a converted copy carries it over.- Docs. A worked tensile-test example (
force [N]/time [s]/displacement [mm], fully semantically described, converted to[kN, mm, ms]) and a unit-conversion reference inusage/dataframe.md; RFC 0006 v2 (dimensional algebra).
Changed¶
- HDF5 backend uses
h5pyinstead of PyTables (RFC 0002 amendment): each column is a native HDF5 dataset under akeygroup (readable by any HDF5 tool), the sdata metadata rides along as the group's_sdataattribute; thehdfextra now installsh5py. Legacy PyTables kwargs (format/complevel/complib) are accepted and ignored. - Packaging modernized to PEP 621 (RFC 0013). Metadata and extras live in a single
[project]table inpyproject.toml; the version resolves viadynamic = ["version"]fromsdata/__init__.py(single source). Core deps staynumpy/pandas/suuid>=0.2.0. DataFrameGroupmembers are nowDataFrameobjects (RFC 0011) with symmetricwrite_group/read_groupbatch helpers, replacing the ad-hoc raw-pandas layout.Data(deprecated) warns on direct instantiation (RFC 0012);DataFrameis the primary export in__all__/SDATACLS, whileDatastays resolvable for deserialization (subclasses likePudare undisturbed — port is stage 2 / 2.0).
Removed¶
setup.pyandrequirements.txt— folded into the PEP 621[project]table and extras (RFC 0013);setup.cfg(RFC 0008 A5).- Orphaned vendored
contribpackages with no importers:attrdict,semver,simple_graph_db; the legacyiolib/vault.pyandnode.py(RFC 0011); and the deadFlatHDFDataStore(iolib/hdf.py, superseded byDataFrame.to_hdf/h5py).
Fixed¶
logging.basicConfigremoved from library modules — a library must not configure the root logger; aNullHandleris attached at the package root instead (RFC 0008 A1).Metadata.from_jsonwith no source raises a clearValueErrorinstead ofUnboundLocalError(A2); a German topology-class value that failed to resolve now uses its canonical English term (A6).
Docs¶
- RFCs 0009 (reader), 0010 (format versioning), 0011 (persistence consolidation),
0012 (
Datadeprecation), 0013 (packaging), an open-points backlog and an RFC 0002 h5py amendment. - Contributor
conventions.md(English code/docs, typing, lenient/stricterror policy); an honest README design-goal status table; license set to MIT.
[1.3.0] - 2026-06-29 (unreleased — shipped as part of 1.4.0)¶
A large, strictly additive increment: a content/integrity foundation under all
data containers (Blob), a much broader DataFrame serialization portfolio, and
native, format-agnostic metadata embedding for images. Core dependencies remain
numpy, pandas, suuid; every new backend stays optional with a pure-Python path.
Added¶
- New attribute dtypes
date,time,duration,decimal,complex,floatlistandlangstring(pure stdlib):date/time(xsd:date/xsd:time),durationas adatetime.timedeltaparsed from ISO 8601 (xsd:duration),decimalasdecimal.Decimalfor exact numerics (xsd:decimal),complexnumbers andfloatlist(typedlist[float], also from numpy arrays) — the latter two use the custom datatype CURIEssdata:complex/sdata:floatlist, andlangstring(rdf:langString,"Hallo@de") renders via JSON-LD@language— all with a lossless round-trip. Lenient/strict=coercion as for the existing dtypes. - Native image metadata (RFC 0005). New pure-Python, Pillow-free module
sdata.imagemetaembeds/reads sdata metadata natively into six containers with one API (detect_format/embed/extract/supported_formats): PNG (iTXt), JPEG (APP1), JPEG 2000 (uuidbox), GIF (comment extension), WebP (sdATchunk) and TIFF (private IFD tag, original bytes untouched).Imagegains a uniformsave/from_fileflow,embedded_metadata(), and a lossless<file>.meta.jsonsidecar fallback for formats without a native carrier (e.g. BMP), controllable viasave(sidecar=True|False|None). Blobas the content/integrity/provenance foundation (RFC 0003). HardenedBlobwithsha256/sha1/md5,size,verify()/update_checksum(), a lazycontent_bytescache,exists(),write(uri)/open()(fsspec), standard-vocabulary provenance metadata (dcat:mediaType,dcterms:*,schema:sha256) and mime/creation-date autofill.FileReferenceandImagenow build onBlob.- Shared integrity mixin (RFC 0004, Option B).
sdata.sclass.content.ContentIntegrityMixinprovides the hash/verify/sizelayer to bothBlobandDataFramevia acontent_byteshook (no inheritance between them). DataFrame.as_blob(fmt)(RFC 0004, Option C). Render a table as a standaloneBlobin a chosen format (parquet/csv/arrow/feather) — composition that grants hash/verify/size/write/openwithout changing the base class.DataFrameserialization portfolio. Native per-column field metadata in Arrow/Feather (to_arrow/from_arrow/to_feather/from_feather), a Frictionless Data Package bundle (to_datapackage/from_datapackage,.zip), and HDF5 I/O (to_hdf/from_hdf, optionalsdata[hdf], RFC 0002).- RFCs. 0002 (HDF5), 0003 (Blob foundation), 0004 (DataFrame vs. Blob), 0005 (native image metadata); MkDocs nav, API reference and usage guides extended.
Changed¶
DataFrame.content_byteshashes the data only (plain Parquet), so storing the checksum in the metadata does not change the hash (no self-reference).- Documentation reorganized around the full DataFrame serialization portfolio and the
image-metadata workflow (new
usage/image-metadata.md).
Notes¶
- 100 % line coverage maintained;
mkdocs build --strictgreen.sdata.imagemetais measured (100 %) via synthetic, Pillow-free tests, so coverage holds even without Pillow installed.
1.2.0 - 2026-06-26¶
- Machine-readable metadata backbone. Typed dtype registry, a registered JSON-LD
@context(vocab/units/BFO),to_jsonld/to_rdf/to_turtlewith<sname>.meta.jsonldsidecars, declarativeMetadataSchema/TableSchemavalidation, an interactive Jupyter layer (_repr_html_, attribute autocomplete) and signed metadata as W3C Verifiable Credentials over the pure-Python EdDSA stack (sdata.did). - Self-describing
DataFramecontainer with per-column metadata and Parquet/CSV/ dict/JSON-LD serialization, superseding the deprecatedDataclass. - Docs & packaging. MkDocs Material + mkdocstrings documentation site; core
dependencies reduced to
numpy/pandas/suuid(stdlibzoneinfo); warning-free test suite.