|
| 1 | +# SEP-0013 -- A Metadata Class that Supports Axis-aligned Metadata |
| 2 | + |
| 3 | +| SEP | num | |
| 4 | +|---------------|------------------------------------------------------| |
| 5 | +| title | A Metadata Class that Supports Axis-aligned Metadata | |
| 6 | +| author(s) | [Daniel Ryan](https://orcid.org/0000-0001-8661-3825) | |
| 7 | +| contact email | [email protected] | |
| 8 | +| date-creation | 2021-11-21 | |
| 9 | +| type | standard | |
| 10 | +| discussion | link to discussion if available | |
| 11 | +| status | discussion | |
| 12 | + |
| 13 | +# Introduction |
| 14 | +Currently in `ndcube` and `sunpy`, the metadata object attached to data classes |
| 15 | +like `NDCube` and `Map` can be of any `dict`-like type. |
| 16 | +While this level of generality allows users to use the metadata object however |
| 17 | +they want, it prevents the data classes interacting with the metadata object |
| 18 | +because there is no standardized API. |
| 19 | +One important use-case is supporting axis-aligned metadata, i.e. a piece of |
| 20 | +metadata that has a different value for different indices in the data array, |
| 21 | +e.g. exposure time. |
| 22 | +This SEP strives to define a `dict`-like metadata API that is independent of the |
| 23 | +type of data it describes and enables, but does not require, axis-aligned metadata. |
| 24 | + |
| 25 | +The API enables: |
| 26 | +- Access to the metadata values via their name key; |
| 27 | +- Comments associated with specific pieces of metadata; |
| 28 | +- Editing of the metadata values and/or comments; |
| 29 | +- The preservation of a copy of the original metadata values; |
| 30 | +- Addition and removal of metadata; |
| 31 | +- Association of metadata with one or more data axes; |
| 32 | +- Slicing of the metadata object given a slice item valid for the data array the metadata object describes. |
| 33 | + |
| 34 | +It is anticipated that this API will be implemented in `ndcube`. |
| 35 | +The motivating use-case storing exposure time and enabling sunraster data objects |
| 36 | +to normalize their intensity values. |
| 37 | + |
| 38 | +This metadata API is not designed as a standardization of names of specific metadata |
| 39 | +associated with different data types, e.g. images, or instruments. |
| 40 | +Instead it is hoped that such metadata schemes can be implemented on top of this API. |
| 41 | + |
| 42 | +# MetaABC API |
| 43 | +Below is the class definition of an Abstract Base Class (ABC) for a `Meta` object. |
| 44 | + |
| 45 | +```python |
| 46 | +class Meta(dict): |
| 47 | + |
| 48 | + def __init__(self, |
| 49 | + header: dict-like = None, |
| 50 | + comments: dict-like = None, |
| 51 | + axes: dict-like = None, |
| 52 | + data_shape: Iterable[int] = None): |
| 53 | + """ |
| 54 | + A sliceable object for storing metadata. |
| 55 | +
|
| 56 | + Metdata can be linked to a data array axis. This causes it to be sliced when the |
| 57 | + standard Python numeric slicing API is applied to the object. |
| 58 | + Specific pieces of metadata can be obtained using the dict-like str slicing API. |
| 59 | + Metadata associated with an axis/axes must have the same length/shape as those axes. |
| 60 | +
|
| 61 | + Parameters |
| 62 | + ---------- |
| 63 | + header: |
| 64 | + The names and values of metadata. |
| 65 | + comments: |
| 66 | + Comments associated with any of the above pieces of metadata. |
| 67 | + This does not have to include a key for every key in header, |
| 68 | + but every key in comments must also be in header. |
| 69 | + axes: |
| 70 | + The axis/axes associated with the above metadata values. |
| 71 | + Each axis value must be None (for no axis association), `int` |
| 72 | + or an iterable of `int` if the metadata is associated with multiple axes. |
| 73 | + Metadata in header without a corresponding entry here are assumed to not |
| 74 | + be associated with an axis. |
| 75 | + All keys in axes must also be in header. |
| 76 | + data_shape: |
| 77 | + The shape of the data array with which this metadata is associated. |
| 78 | + Must be set if axes input is set. |
| 79 | + """ |
| 80 | + |
| 81 | + @abc.abstractmethod |
| 82 | + @property |
| 83 | + def original_header(self) -> Any: |
| 84 | + """A copy of the original header input.""" |
| 85 | + |
| 86 | + @abc.abstractmethod |
| 87 | + @property |
| 88 | + def comments(self) -> dict: |
| 89 | + """Returns a dict of comments associated with specific pieces of metadata.""" |
| 90 | + |
| 91 | + @abc.abstractmethod |
| 92 | + @property |
| 93 | + def axes(self) -> dict: |
| 94 | + """Returns a dict of array axes with which specific pieces of metadata are associated. |
| 95 | +
|
| 96 | + Axes are returned as tuples of ints. If the metadata is not associated with |
| 97 | + array axis, None is returned. Metadata keys absent from this dict are assumed |
| 98 | + to be associated with no array axes. |
| 99 | + """ |
| 100 | + |
| 101 | + @abc.abstractmethod |
| 102 | + @property |
| 103 | + def shape(self) -> numpy.ndarray: |
| 104 | + """1-D array giving the length of each array axis of the associated data array.""" |
| 105 | + |
| 106 | + @abc.abstractmethod |
| 107 | + def add(self, |
| 108 | + name: str, |
| 109 | + value: Any, |
| 110 | + comment: str = None, |
| 111 | + axis: Union[int, Iterable[int]] = None, |
| 112 | + overwrite: bool = False): |
| 113 | + """Add a new piece of metadata to instance.""" |
| 114 | + |
| 115 | + @abc.abstractmethod |
| 116 | + def remove(self, |
| 117 | + name: str): |
| 118 | + """Remove a new piece of metadata to instance.""" |
| 119 | + |
| 120 | + @abc.abstractmethod |
| 121 | + def __getitem__(self, item: Union[int, slice, Iterable[Union[slice, int]]]) -> "MetaABC": |
| 122 | + """Slice the Meta object. |
| 123 | +
|
| 124 | + The input item must be a valid slice item for an array with a shape given by |
| 125 | + MetaABC.shape. If MetaABC.shape is None, then this method should error. |
| 126 | + This method will alter the following things: |
| 127 | + * The values of axis-aligned metadata; |
| 128 | + * The axes of axis-aligned metadata if any data axes are dropped by the slicing; |
| 129 | + * MetaABC.shape. |
| 130 | + Data axes can be dropped by slicing if an integer is applied to an axis. This means |
| 131 | + that axis indices greater than the dropped axis, will be decremented by 1. |
| 132 | + Metadata, all of whose axes are dropped by the slicing, will have a new axis value of None. |
| 133 | + """ |
| 134 | + |
| 135 | +# Decision Rationale |
| 136 | +This is a great idea because... |
0 commit comments