Serialization¶
Warning
Serialization is in draft currently. Once at least one implementation is ready, we will remove this warning and release UHI 0.5.
Introduction¶
Histogram serialization has to cover a wide range of formats. As such, we describe a form for serialization that covers the metadata structure as JSON-like, with a provided JSON-schema. The data (bins and/or variable edges) is stored out-of-band in a binary format based on what type of data file you are in. For very small (primarily 1D) histograms, data is allowed inline as well.
The following formats are being targeted:
┌────────┐ ┌────────┐ ┌───────┐
│ ROOT │ │ HDF5 │ │ ZIP │
└────────┘ └────────┘ └───────┘
Other formats can be used as well, assuming they support out-of-band data and text attributes or files for the metadata.
Caveats¶
This structure was based heavily on boost-histogram, but it is intended to be general, and can be expanded in the future as needed. As such, the following limitations are required:
Serialization followed by deserialisation may cause axis changes. Axis types may change to an equivalent but less performant axis, growth status will be lost, etc.
Metadata must be expressible as JSON. It should also be reasonably sized; some formats like HDF5 may limit the size of attributes to 64K.
Floating point errors could be incurred on conversion, as the storage format uses a stable but different representation.
Axis
name
is only part of the metadata, and is not standardized. This is due to lack of support from boost-histogram.
Design¶
The following axes types are supported:
"regular"
: A regularly spaced set of even bins. Boost-histogram’s “integer” axes maps to this axis as well. Hasupper
,lower
,bins
,underflow
,overflow
, andcircular
properties.circular
defaults to False if not present."variable"
: A continuous axis defined by bins+1 edges. Hasedges
, which is either an in-line list of numbers or a string pointing to an out-of-band data source. Also hasunderflow
,overflow
, andcircular
properties.circular
defaults to False if not present."category_int"
: A list of integer bins, non-continuous. Hascategories
, which is an in-line list of integers. Also hasflow
."category_str"
: A list of string bins. Hascategories
, which is an in-line list of strings. Also hasflow
."boolean"
: A true/false axis.
Axes with gaps are currently not supported.
All axes support metadata
, a string-valued dictionary of arbitrary, JSON-like data.
The following storages are supported:
"int"
: A collection of integers. Boost-histogram’s Int64 and AtomicInt64 map to this, and sometimes Unlimited."double"
: A collection of 64-bit floating point values. Boost-histogram’s Double storage maps to this, and sometimes Unlimited."weighted"
: A collection of two arrays of 64-bit floating point values,"value"
and"variance"
. Boost-histogram’s Weight storage maps to this."mean"
: A collection of three arrays of 64-bit floating point values, “count”, “value”, and “variance”. Boost-histogram’s Mean storage maps to this."weighted_mean"
: A collection of four arrays of 64-bit floating point values,"sum_of_weights"
,"sum_of_weights_squared"
,"values"
, and"variances"
. Boost-histogram’s WeighedMean storage maps to this.
CLI/API¶
You can currently test a JSON file against the schema by running:
$ python -m uhi.schema some/file.json
Or with code:
import uhi.schema
uhi.schema.validate("some/file.json")
Eventually this should also be usable for JSON’s inside zip, HDF5 attributes, and maybe more.
Warning
Currently, this spec describes how to prepare the metadata for one of the
targeted backends. It does not yet cover backend specific details, like how to
define and use the binary resource locator strings or how to store the data.
JSON is not a target spec, but just part of the ZIP spec, meaning the files
that currently “pass” the tool above would be valid inside a .zip
file
eventually, but are not valid by themselves.
Rendered schema¶
Histogram¶
https://raw.githubusercontent.com/scikit-hep/uhi/main/src/uhi/resources/histogram.schema.json |
||||||
type |
object |
|||||
patternProperties |
||||||
|
type |
object |
||||
properties |
||||||
|
Arbitrary metadata dictionary. |
|||||
type |
object |
|||||
|
A list of the axes of the histogram. |
|||||
type |
array |
|||||
items |
oneOf |
:ref: |
||||
:ref: |
||||||
:ref: |
||||||
:ref: |
||||||
:ref: |
||||||
|
The storage of the bins of the histogram. |
|||||
oneOf |
:ref: |
|||||
:ref: |
||||||
:ref: |
||||||
:ref: |
||||||
:ref: |
||||||
additionalProperties |
False |
|||||
additionalProperties |
False |
|||||
$defs |
||||||
|
An evenly spaced set of continuous bins. |
|||||
type |
object |
|||||
properties |
||||||
|
type |
string |
||||
const |
regular |
|||||
|
Lower edge of the axis. |
|||||
type |
number |
|||||
|
Upper edge of the axis. |
|||||
type |
number |
|||||
|
Number of bins in the axis. |
|||||
type |
integer |
|||||
minimum |
0 |
|||||
|
True if there is a bin for underflow. |
|||||
type |
boolean |
|||||
|
True if there is a bin for overflow. |
|||||
type |
boolean |
|||||
|
True if the axis wraps around. |
|||||
type |
boolean |
|||||
|
Arbitrary metadata dictionary. |
|||||
type |
object |
|||||
additionalProperties |
False |
|||||
|
A variably spaced set of continuous bins. |
|||||
type |
object |
|||||
properties |
||||||
|
type |
string |
||||
const |
variable |
|||||
|
oneOf |
type |
array |
|||
items |
type |
number |
||||
A path (URI?) to the edges data. |
||||||
type |
string |
|||||
|
type |
boolean |
||||
|
type |
boolean |
||||
|
type |
boolean |
||||
|
Arbitrary metadata dictionary. |
|||||
type |
object |
|||||
additionalProperties |
False |
|||||
|
A set of string categorical bins. |
|||||
type |
object |
|||||
properties |
||||||
|
type |
string |
||||
const |
category_str |
|||||
|
type |
array |
||||
items |
type |
string |
||||
uniqueItems |
True |
|||||
|
True if flow bin (at the overflow position) present. |
|||||
type |
boolean |
|||||
|
Arbitrary metadata dictionary. |
|||||
type |
object |
|||||
additionalProperties |
False |
|||||
|
A set of integer categorical bins in any order. |
|||||
type |
object |
|||||
properties |
||||||
|
type |
string |
||||
const |
category_int |
|||||
|
type |
array |
||||
items |
type |
integer |
||||
uniqueItems |
True |
|||||
|
True if flow bin (at the overflow position) present. |
|||||
type |
boolean |
|||||
|
Arbitrary metadata dictionary. |
|||||
type |
object |
|||||
additionalProperties |
False |
|||||
|
A simple true/false axis with no flow. |
|||||
type |
object |
|||||
properties |
||||||
|
type |
string |
||||
const |
boolean |
|||||
|
Arbitrary metadata dictionary. |
|||||
type |
object |
|||||
additionalProperties |
False |
|||||
|
A storage holding integer counts. |
|||||
type |
object |
|||||
properties |
||||||
|
type |
string |
||||
const |
int |
|||||
|
oneOf |
A path (URI?) to the integer bin data. |
||||
type |
string |
|||||
type |
array |
|||||
items |
type |
integer |
||||
additionalProperties |
False |
|||||
|
A storage holding floating point counts. |
|||||
type |
object |
|||||
properties |
||||||
|
type |
string |
||||
const |
double |
|||||
|
oneOf |
A path (URI?) to the floating point bin data. |
||||
type |
string |
|||||
type |
array |
|||||
items |
type |
number |
||||
additionalProperties |
False |
|||||
|
A storage holding floating point counts and variances. |
|||||
type |
object |
|||||
properties |
||||||
|
type |
string |
||||
const |
int |
|||||
|
oneOf |
A path (URI?) to the floating point bin data; outer dimension is [value, variance] |
||||
type |
string |
|||||
type |
object |
|||||
properties |
||||||
|
type |
array |
||||
items |
type |
number |
||||
|
type |
array |
||||
items |
type |
number |
||||
additionalProperties |
False |
|||||
additionalProperties |
False |
|||||
|
A storage holding ‘profile’-style floating point counts, values, and variances. |
|||||
type |
object |
|||||
properties |
||||||
|
type |
string |
||||
const |
int |
|||||
|
oneOf |
A path (URI?) to the floating point bin data; outer dimension is [counts, value, variance] |
||||
type |
string |
|||||
type |
object |
|||||
properties |
||||||
|
type |
array |
||||
items |
type |
number |
||||
|
type |
array |
||||
items |
type |
number |
||||
|
type |
array |
||||
items |
type |
number |
||||
additionalProperties |
False |
|||||
additionalProperties |
False |
|||||
|
A storage holding ‘profile’-style floating point ∑weights, ∑weights², values, and variances. |
|||||
type |
object |
|||||
properties |
||||||
|
type |
string |
||||
const |
int |
|||||
|
oneOf |
A path (URI?) to the floating point bin data; outer dimension is [∑weights, ∑weights², value, variance] |
||||
type |
string |
|||||
type |
object |
|||||
properties |
||||||
|
type |
array |
||||
items |
type |
number |
||||
|
type |
array |
||||
items |
type |
number |
||||
|
type |
array |
||||
items |
type |
number |
||||
|
type |
array |
||||
items |
type |
number |
||||
additionalProperties |
False |
|||||
additionalProperties |
False |
Full schema¶
The full schema is below:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/scikit-hep/uhi/main/src/uhi/resources/histogram.schema.json",
"title": "Histogram",
"type": "object",
"additionalProperties": false,
"patternProperties": {
".+": {
"type": "object",
"required": ["axes", "storage"],
"additionalProperties": false,
"properties": {
"metadata": {
"type": "object",
"description": "Arbitrary metadata dictionary."
},
"axes": {
"type": "array",
"description": "A list of the axes of the histogram.",
"items": {
"oneOf": [
{ "$ref": "#/$defs/regular_axis" },
{ "$ref": "#/$defs/variable_axis" },
{ "$ref": "#/$defs/category_str_axis" },
{ "$ref": "#/$defs/category_int_axis" },
{ "$ref": "#/$defs/boolean_axis" }
]
}
},
"storage": {
"description": "The storage of the bins of the histogram.",
"oneOf": [
{ "$ref": "#/$defs/int_storage" },
{ "$ref": "#/$defs/double_storage" },
{ "$ref": "#/$defs/weighted_storage" },
{ "$ref": "#/$defs/mean_storage" },
{ "$ref": "#/$defs/weighted_mean_storage" }
]
}
}
}
},
"$defs": {
"regular_axis": {
"type": "object",
"description": "An evenly spaced set of continuous bins.",
"required": ["type", "lower", "upper", "bins", "underflow", "overflow"],
"additionalProperties": false,
"properties": {
"type": { "type": "string", "const": "regular" },
"lower": { "type": "number", "description": "Lower edge of the axis." },
"upper": { "type": "number", "description": "Upper edge of the axis." },
"bins": {
"type": "integer",
"minimum": 0,
"description": "Number of bins in the axis."
},
"underflow": {
"type": "boolean",
"description": "True if there is a bin for underflow."
},
"overflow": {
"type": "boolean",
"description": "True if there is a bin for overflow."
},
"circular": {
"type": "boolean",
"description": "True if the axis wraps around."
},
"metadata": {
"type": "object",
"description": "Arbitrary metadata dictionary."
}
}
},
"variable_axis": {
"type": "object",
"description": "A variably spaced set of continuous bins.",
"required": ["type", "edges", "underflow", "overflow"],
"additionalProperties": false,
"properties": {
"type": { "type": "string", "const": "variable" },
"edges": {
"oneOf": [
{
"type": "array",
"items": { "type": "number", "minItems": 2, "uniqueItems": true }
},
{
"type": "string",
"description": "A path (URI?) to the edges data."
}
]
},
"underflow": { "type": "boolean" },
"overflow": { "type": "boolean" },
"circular": { "type": "boolean" },
"metadata": {
"type": "object",
"description": "Arbitrary metadata dictionary."
}
}
},
"category_str_axis": {
"type": "object",
"description": "A set of string categorical bins.",
"required": ["type", "categories", "flow"],
"additionalProperties": false,
"properties": {
"type": { "type": "string", "const": "category_str" },
"categories": {
"type": "array",
"items": { "type": "string" },
"uniqueItems": true
},
"flow": {
"type": "boolean",
"description": "True if flow bin (at the overflow position) present."
},
"metadata": {
"type": "object",
"description": "Arbitrary metadata dictionary."
}
}
},
"category_int_axis": {
"type": "object",
"description": "A set of integer categorical bins in any order.",
"required": ["type", "categories", "flow"],
"additionalProperties": false,
"properties": {
"type": { "type": "string", "const": "category_int" },
"categories": {
"type": "array",
"items": { "type": "integer" },
"uniqueItems": true
},
"flow": {
"type": "boolean",
"description": "True if flow bin (at the overflow position) present."
},
"metadata": {
"type": "object",
"description": "Arbitrary metadata dictionary."
}
}
},
"boolean_axis": {
"type": "object",
"description": "A simple true/false axis with no flow.",
"required": ["type"],
"additionalProperties": false,
"properties": {
"type": { "type": "string", "const": "boolean" },
"metadata": {
"type": "object",
"description": "Arbitrary metadata dictionary."
}
}
},
"int_storage": {
"type": "object",
"description": "A storage holding integer counts.",
"required": ["type", "data"],
"additionalProperties": false,
"properties": {
"type": { "type": "string", "const": "int" },
"data": {
"oneOf": [
{
"type": "string",
"description": "A path (URI?) to the integer bin data."
},
{ "type": "array", "items": { "type": "integer" } }
]
}
}
},
"double_storage": {
"type": "object",
"description": "A storage holding floating point counts.",
"required": ["type", "data"],
"additionalProperties": false,
"properties": {
"type": { "type": "string", "const": "double" },
"data": {
"oneOf": [
{
"type": "string",
"description": "A path (URI?) to the floating point bin data."
},
{ "type": "array", "items": { "type": "number" } }
]
}
}
},
"weighted_storage": {
"type": "object",
"description": "A storage holding floating point counts and variances.",
"required": ["type", "data"],
"additionalProperties": false,
"properties": {
"type": { "type": "string", "const": "int" },
"data": {
"oneOf": [
{
"type": "string",
"description": "A path (URI?) to the floating point bin data; outer dimension is [value, variance]"
},
{
"type": "object",
"required": ["values", "variances"],
"additionalProperties": false,
"properties": {
"values": { "type": "array", "items": { "type": "number" } },
"variances": { "type": "array", "items": { "type": "number" } }
}
}
]
}
}
},
"mean_storage": {
"type": "object",
"description": "A storage holding 'profile'-style floating point counts, values, and variances.",
"required": ["type", "data"],
"additionalProperties": false,
"properties": {
"type": { "type": "string", "const": "int" },
"data": {
"oneOf": [
{
"type": "string",
"description": "A path (URI?) to the floating point bin data; outer dimension is [counts, value, variance]"
},
{
"type": "object",
"required": ["counts", "values", "variances"],
"additionalProperties": false,
"properties": {
"counts": { "type": "array", "items": { "type": "number" } },
"values": { "type": "array", "items": { "type": "number" } },
"variances": { "type": "array", "items": { "type": "number" } }
}
}
]
}
}
},
"weighted_mean_storage": {
"type": "object",
"description": "A storage holding 'profile'-style floating point ∑weights, ∑weights², values, and variances.",
"required": ["type", "data"],
"additionalProperties": false,
"properties": {
"type": { "type": "string", "const": "int" },
"data": {
"oneOf": [
{
"type": "string",
"description": "A path (URI?) to the floating point bin data; outer dimension is [∑weights, ∑weights², value, variance]"
},
{
"type": "object",
"required": [
"sum_of_weights",
"sum_of_weights_squared",
"values",
"variances"
],
"additionalProperties": false,
"properties": {
"sum_of_weights": {
"type": "array",
"items": { "type": "number" }
},
"sum_of_weights_squared": {
"type": "array",
"items": { "type": "number" }
},
"values": { "type": "array", "items": { "type": "number" } },
"variances": { "type": "array", "items": { "type": "number" } }
}
}
]
}
}
}
}
}