--- title: Calc With Metadata marimo-version: 0.24.0 --- # Calculations with metadata Scope ----- This guide illustrates how the role of metadata in calculations extends beyond simple filtering. ## Prerequisites ``` {note} The guide assumes that the SSB Timeseries library is installed and that a working configuration is active. See [the quickstart guide](quickstart) for instructions to that. ``` The presented functionality relies on `dataset.Dataset` and `meta.taxonomy.Taxonomy`. Other imports like`types.SeriesType` and external libraries are used only for generating the sample data. ```python {.marimo} from ssb_timeseries.dataset import Dataset from ssb_timeseries.meta.taxonomy import Taxonomy ``` Generate sample data -------------------- ```python {.marimo} from ssb_timeseries.types import SeriesType from ssb_timeseries.sample_data import create_df from itertools import product from datetime import date ``` Generate some test data ```python {.marimo} def create_some_example_data( set_name: str, series_tags: dict[str,list[str]], ): """Generate and save some sample data.""" set_tags = { "Country": "Norway" } df = create_df( *[value for value in series_tags.values()], temporality= 'FROM_TO', start_date="2024-01-01", end_date="2026-12-01", freq="MS", ) Dataset( name=set_name, data_type=SeriesType('NONE', 'FROM_TO'), data=df, tags = set_tags, attributes = series_tags.keys(), ).save() ``` We will generate random data for all permutations of some descriptive metadata tags. This time we include a real classification that we will simply name "taxonomy", and use completely out of context. (It just happens to have a suitable shape and size.) ```python {.marimo} taxonomy = Taxonomy(klass_id=157) taxonomy.print_tree() ```
╙── 0
    ├─╼ 1
    │   ├─╼ 1.1
    │   │   ├─╼ 1.1.1
    │   │   ├─╼ 1.1.2
    │   │   └─╼ 1.1.3
    │   └─╼ 1.2
    ├─╼ 11
    │   ├─╼ 11.1
    │   └─╼ 11.2
    ├─╼ 12
    │   ├─╼ 12.1
    │   │   ├─╼ 12.1.1
    │   │   ├─╼ 12.1.10
    │   │   ├─╼ 12.1.11
    │   │   ├─╼ 12.1.12
    │   │   ├─╼ 12.1.13
    │   │   ├─╼ 12.1.2
    │   │   ├─╼ 12.1.3
    │   │   ├─╼ 12.1.4
    │   │   ├─╼ 12.1.5
    │   │   ├─╼ 12.1.6
    │   │   ├─╼ 12.1.7
    │   │   ├─╼ 12.1.8
    │   │   └─╼ 12.1.9
    │   ├─╼ 12.2
    │   │   ├─╼ 12.2.1
    │   │   ├─╼ 12.2.2
    │   │   ├─╼ 12.2.3
    │   │   ├─╼ 12.2.4
    │   │   └─╼ 12.2.5
    │   └─╼ 12.3
    │       ├─╼ 12.3.1
    │       ├─╼ 12.3.2
    │       ├─╼ 12.3.3
    │       └─╼ 12.3.4
    ├─╼ 13
    ├─╼ 14
    ├─╼ 15
    ├─╼ 2
    ├─╼ 3
    ├─╼ 4
    │   ├─╼ 4.1
    │   └─╼ 4.2
    ├─╼ 5
    ├─╼ 6
    ├─╼ 7
    │   ├─╼ 7.1
    │   ├─╼ 7.2
    │   ├─╼ 7.3
    │   ├─╼ 7.4
    │   ├─╼ 7.5
    │   └─╼ 7.6
    ├─╼ 8
    │   ├─╼ 8.1
    │   ├─╼ 8.2
    │   ├─╼ 8.3
    │   ├─╼ 8.4
    │   ├─╼ 8.5
    │   ├─╼ 8.6
    │   ├─╼ 8.7
    │   ├─╼ 8.8
    │   └─╼ 8.9
    └─╼ 9
```python {.marimo} create_some_example_data( set_name="More Prices and Volumes", series_tags = { "variable": ["price", "volume"], "product": ["milk", "eggs", "bread", "juice", "ham", "cheese"], "category": taxonomy.leaf_nodes, } ) ``` Here we use the 53 `taxonomy.leaf_nodes` to populate a `category` attribute. Filtering datasets by tags -------------------------- The most typical use of descriptive metadata, aka `Dataset.tags`, is to extract subsets of datasets for specific purposes. A simple "example with Prices and Volumes" extracts *prices* and *volumes* for a number of *products* into separate variables and calculate revenues by multiplying them: ```python {.marimo} prices_and_volumes = Dataset(name="More Prices and Volumes") prices = prices_and_volumes[{'variable': 'price'}] volumes = prices_and_volumes[{'variable': 'volume'}] revenue = prices * volumes ``` The name and tags of the returned dataset need to be updated to make sense: ```python {.marimo} revenue.rename("More Revenues", ('price', 'revenue')) revenue.replace_tags(({'variable':'price'}, {'variable': 'revenue'})) ``` So from 53 taxonomy entities times 6 products we get 318 revenue series. Group by behaviour ------------------ Group by can be configured to run in "auto" mode: using metadata attributes to select whether to calculate sums or averages. (The functionality was hard coded for the PoC phase. It is now disabled, but a functionality skeleton is still there. The missing link for working properly is configuration interaction.) ```python {.marimo} volumes.data = volumes.pd # a workaround for BUG # q = volumes.groupby('Q', 'auto') # --> another bug! ``` ## Aggregates Since we now happen to have a properly tagged dataset containing all the leaf nodes in such a tree, we can calculate the aggregates for the rest of the taxonomy structure, that is for the "parent" nodes of the hierarchy: ```python {.marimo} taxonomy.parent_nodes ```
['1', '0', '1.1', '11', '12', '12.1', '12.2', '12.3', '4', '7', '8']
```python {.marimo} list_of_functions = ['sum'] # there are more options --> see the reference aggregated_revenue = revenue.aggregate( attributes=["category"], # lengths must match ↓ taxonomies=[taxonomy], # lengths must match ↑ functions=list_of_functions ) aggregated_revenue.pl.schema.to_python() ```
{'sum(0)': "<class 'float'>",
 'sum(1)': "<class 'float'>",
 'sum(1.1)': "<class 'float'>",
 'sum(11)': "<class 'float'>",
 'sum(12)': "<class 'float'>",
 'sum(12.1)': "<class 'float'>",
 'sum(12.2)': "<class 'float'>",
 'sum(12.3)': "<class 'float'>",
 'sum(4)': "<class 'float'>",
 'sum(7)': "<class 'float'>",
 'sum(8)': "<class 'float'>",
 'valid_from': "<class 'datetime.datetime'>",
 'valid_to': "<class 'datetime.datetime'>"}
```python {.marimo} # aggregated_revenue.tags ``` Here we can observe a bug: "input" lists data, not series names. Including input data could be OK elsewhere, but it inflates `.tags` in a way that is not scalable. (Tags are included in parquet metadata fields, so including data may limit the maximum size of datasets / cause other problems.) ```python {.marimo} ``` Planned: Canonic datasets ----------------------- Calculations with canonic datasets are sets where a few specific datasets play a key role. The datasets can reside in local repositories or accessed through API. Examples: - Currency conversion. - Inflation adjustment. For such features to work, canonic sets and critical attributes must be specified. Hard coded would work, but configurations would be better. Planned: Unit conversion ---------------------- Automatic unit conversions based on tags require configurations to identify the name of the unit attribute.