Getting Started
Scientific data moves between programs, readable input files, and storage formats. A different file layout need not mean different data; a changed Array type or coordinate structure can mean different data even when the numbers look the same.
DataCanon gives structured scientific data a precisely defined identity, independent of its programming language or storage representation. Each DataCanon Instance (DCI) has one unique canonical byte encoding. A fingerprint derived from those bytes lets you identify the data across representations.
This guide constructs a dataset, identifies it, stores and inspects it, then shows how to author scientific input directly.
Installation
Clone the DataCanon code repository, then run these commands from its root:
python3 -m venv DC
source DC/bin/activate
python -m pip install .
The Makefile also provides environment setup, editable installation, and tests.
Construct and identify a DCI
Start with a Python dictionary containing a Tuple key, a List of species, a coordinate Tuple, and a numerical Array:
import numpy as np
from data_canon import as_dc_instance, fingerprint
data = {
("Eg", 0): 2.37,
"species": ["Sr", "Ti", "O"],
"origin": (0.0, 0.0, 0.0),
"lattice": np.eye(3, dtype="float64"),
}
instance = as_dc_instance(data)
identifier = fingerprint(instance)
print(identifier)
Output:
eb8f306c801ca4452e283f052b51a6834a846399cc3aac43720fb7cf48faeca4
The whole dataset is one DCI: a Mapping containing other DCIs. Mapping keys can
be structured values, including the Tuple ("Eg", 0).
Python’s dict, tuple, and NumPy arrays are conventional types used by the
application. Conversion constructs a DCI whose information and equality rules
are defined by DataCanon, independently of those Python types.
The fingerprint is SHA-256 of the DCI’s canonical bytes, written as 64 lowercase hexadecimal characters. You can record it alongside a calculation to identify its input data. It is derived from the data’s defined identity, not from the memory address of a Python object or the layout of a text file.
Type participates in identity. Replacing the origin Tuple with a List changes the DCI even though its coordinates have the same numerical values:
changed_data = dict(data)
changed_data["origin"] = [0.0, 0.0, 0.0]
changed_instance = as_dc_instance(changed_data)
assert changed_instance != instance
assert fingerprint(changed_instance) != identifier
Array type and shape also participate in identity. Equal DCIs have identical canonical bytes and fingerprints; DCI equality itself is defined by the data model, not by comparing fingerprints.
Write a DCI to disk as binary data
Use .dcb to store the canonical bytes directly:
from data_canon import read, write
write(instance, filename="output_data.dcb")
restored = read("output_data.dcb")
assert restored == instance
assert fingerprint(restored) == identifier
The file is binary, not text. Reading reconstructs the same DCI, including its types, Array shape, and element values. DataCanon decoding reconstructs DCIs without invoking application-defined code.
Render a DCI as Shorthand
Shorthand is a human-readable representation of a DCI:
from data_canon import parse_shorthand, render_shorthand
text = render_shorthand(instance)
print(text)
Output:
{
( "Eg", 0 ): 2.37,
"species": [ "Sr", "Ti", "O" ],
"origin": ( 0.0, 0.0, 0.0 ),
"lattice":
array(
[[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0]]
)
}
Parsing the text recovers the same identity:
parsed = parse_shorthand(text)
assert parsed == instance
assert fingerprint(parsed) == identifier
Text layout is presentation. Canonical bytes determine the fingerprint, so spacing does not become part of the data’s identity.
Inspect a DCI via the command line
Installing the package also installs dc. With the environment active, display
the binary file as the Shorthand shown above:
dc output_data.dcb
Inspect particular values without writing a Python script:
dc output_data.dcb --get '("Eg", 0)'
dc output_data.dcb --get species --index 0
Output:
2.37
"Sr"
Print the same fingerprint calculated in Python:
dc output_data.dcb --fingerprint
The CLI guide covers searches, selection, and format transfers in more detail.
Preserve identity through YAML and HDF5
The same DCI can be represented in other formats without changing its identity.
YAML uses native values where faithful and a $ marker for embedded strict
Shorthand, including Tuple keys and Array values:
from data_canon import parse_yaml, render_yaml
yaml_text = render_yaml(instance)
print(yaml_text)
Output:
$ ( "Eg", 0 ): 2.37
species:
- Sr
- Ti
- O
origin: $ ( 0.0, 0.0, 0.0 )
lattice: |-
$
array(
[[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0]]
)
from_yaml = parse_yaml(yaml_text)
assert from_yaml == instance
assert fingerprint(from_yaml) == identifier
For numerical storage, HDF5 represents Arrays as native numerical datasets:
write(instance, filename="output_data.h5")
from_hdf5 = read("output_data.h5")
assert from_hdf5 == instance
assert fingerprint(from_hdf5) == identifier
Use .dcs with the same write() and read() functions to store Shorthand
text. The .dcb, Shorthand, YAML, and HDF5 representations recover equal DCIs
and therefore the same canonical bytes and fingerprint. Hashing the YAML or
HDF5 file bytes directly would instead identify that particular file
representation.
Author scientific data directly
So far, we have constructed data in Python. DataCanon also lets us author structured scientific data directly in Shorthand:
scientific_input = parse_shorthand("""
{
"position": (1/2, 1/4, 0),
"phase": complex(1/2, 1/2 * sqrt(3))
}
""")
The position contains exact Fractions. The Product 1/2 * sqrt(3) exactly
represents an irrational quantity, and the Complex retains the structure of
its real and imaginary components.
DataCanon does not do math. Parsing constructs and preserves Fraction, Power, Product, and Complex DCIs. Applications can convert DCIs to instances of conventional types for computation; DC identity belongs to the stored data structure.
Make scientific input easier to maintain
Optional Shorthand preprocessing lets an input file use named parameters. An excerpt of a crystal-structure input illustrates the idea: a parameter controls several coordinates while exact reference positions remain visible.
source = """
u = 0.305 |
{
"basis_atoms": {
"O": [
(u, u, 0),
(1/2 + u, 1/2 + -u, 1/2)
]
}
}
"""
authored = parse_shorthand(source, preprocess=True)
write(authored, filename="structure.dcb")
Preprocessing substitutes u before strict parsing. The resulting DCI stores
the coordinate values and their numerical structure; the parameter definition
is an input convenience. The same identity and persistence operations now
apply to this authored data.
The CLI enables preprocessing for .dcs input files. The
Shorthand Preprocessing guide
develops substitutions, compact Lists, and explicit eval(...) separately,
and includes a complete crystal-structure example.
Next steps
- Identity and Fingerprinting: identify calculation inputs and associate results with their inputs.
- Persistence and External Resources: store complete datasets and move verified external content between directories and HDF5.
- Conventional Types: convert DCIs to conventional objects and use numerical operations.
- Custom Conversion Rules: connect your own classes to Records without changing the DC type system.
- Command-Line Interface: inspect, search, select, and transfer data from the shell.
- Python API and Specifications: implementation details and the formal contracts.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.