missingness

This rule identifies the missingness values that a column can take on, and reports the columns that can’t take on any at all.

A column can take on missingness values when the missingnessSet column in the dictionary has a value for it. The allowed values are then the members of that set, as listed in the sets sheet. For example, a column with a missingnessSet of nrNAMissingnessSet can take on NA and nr, while a column with a missingnessSet of genMissingnessSet can take on any of the dictionary’s missingness values.

A value that a column can take on is a statement that the value is missing, rather than a value of the column’s own domain, which is why it is exempt from the other validation rules.

A value that the column can not take on is not a missingness value in that column, and this rule therefore has nothing to say about it. It is left to the other validation rules to decide whether it is valid, the same way as any other value. A column of country codes whose missingness set only holds NULL is a good example: NA is not a missingness value there, it is the country code of Namibia, and the rules that constrain the shape of a value get to judge it.

A primary key is the exception. It identifies an entry and can therefore never be missing, so a column that is the primary key of a table can’t take on any missingness value, and every missingness value in it is reported by this rule. This holds even when the column has a missingness set, and even when the same column is not the primary key of another table.

Consider a sites table where siteID is the primary key, geoLat has a missingnessSet of nrNAMissingnessSet and country has one that only holds nr. The following ODM dataset snippet would fail validation, because the primary key siteID can’t be missing,

                                                Invalid Sites Table                                                
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ siteID                              geoLat                              country                               ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ NA                                 │ 1                                  │ CA                                    │
└────────────────────────────────────┴────────────────────────────────────┴───────────────────────────────────────┘

The following dataset snippet would pass validation. NA and nr are missingness values that geoLat can take on, nr is one that country can take on, and the NA in country is not a missingness value there, but a country code,

                                                 Valid Sites Table                                                 
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ siteID                              geoLat                              country                               ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ 1                                  │ NA                                 │ NA                                    │
│ 2                                  │ nr                                 │ nr                                    │
└────────────────────────────────────┴────────────────────────────────────┴───────────────────────────────────────┘

The same siteID column is a foreign key in the samples table, where it is not the primary key. It can take on the missingness values of its genMissingnessSet, so the following dataset snippet would pass validation as well,

                                                Valid Samples Table                                                
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ siteID                                                                                                          ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ NA                                                                                                              │
│ nan                                                                                                             │
│ nr                                                                                                              │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Error report

The error report will have the following fields

  • errorType: missingness

  • tableName: The name of the table with the invalid missingness value

  • columnName: The name of the column with the invalid missingness value

  • invalidValue: The invalid missingness value

  • rowNumber: The index of the table row with the error

  • row: The dictionary containing the row

  • validationRuleFields: The ODM data dictionary rows used to generate this rule

  • message: Invalid missingness value <invalid_value> found in row <row_index> for column <column_name> in table <table_name>. The allowed missingness values are <allowed_missingness_values>

The error report for the invalid dataset is shown below. The allowed missingness values are reported as none, since a primary key can’t be missing,

{
'errors': [
│   │   {
│   │   │   'errorType': 'missingness',
│   │   │   'tableName': 'sites',
│   │   │   'columnName': 'siteID',
│   │   │   'invalidValue': 'NA',
│   │   │   'rowNumber': 1,
│   │   │   'row': {
│   │   │   │   'siteID': 'NA',
│   │   │   │   'geoLat': '1',
│   │   │   │   'country': 'CA'
│   │   │   },
│   │   │   'validationRuleFields': [
│   │   │   │   {
│   │   │   │   │   'partID': 'siteID',
│   │   │   │   │   'sites': 'pK',
│   │   │   │   │   'sitesRequired': 'mandatory',
│   │   │   │   │   'missingnessSet': 'genMissingnessSet'
│   │   │   │   },
│   │   │   │   {
│   │   │   │   │   'partID': 'NA',
│   │   │   │   │   'partType': 'missingness'
│   │   │   │   },
│   │   │   │   {
│   │   │   │   │   'partID': 'nan',
│   │   │   │   │   'partType': 'missingness'
│   │   │   │   },
│   │   │   │   {
│   │   │   │   │   'partID': 'nr',
│   │   │   │   │   'partType': 'missingness'
│   │   │   │   }
│   │   │   ],
│   │   │   'message': 'missingness rule violated in table sites, column siteID, row(s) 1: Invalid missingness value "NA", the allowed missingness values are none'
│   │   }
],
'warnings': []
}

Relation to the other validation rules

A missingness value is a placeholder for a value rather than a value of the column’s own domain. An allowed missingness value is therefore exempt from the rules that constrain the shape of a value: invalid_type, invalid_category, less_than_min_value, greater_than_max_value, less_than_min_length, greater_than_max_length and duplicate_entries_found. It is also exempt from type coercion. A value of NA in the float column geoLat above is valid and is left as-is, instead of being reported as a type error.

A value that violates this rule is exempt from those rules as well, so that a single invalid missingness value isn’t reported twice.

A value that the column can’t take on is not exempt, since it isn’t a missingness value in that column. It is validated by those rules like any other value: a nan in the country column above is reported by greater_than_max_length, because a country code is at most two characters long, and not by this rule.

A missingness value states why a value is absent, which is a valid way of populating a mandatory column, so missing_values_found is not triggered by one either. That rule only reports a mandatory column that has been left empty. An identifier that is missing is reported by this rule instead, since a primary key has no allowed missingness values.

Rule metadata

The metadata for this rule is contained in both the parts and the sets sheet in the data dictionary. The steps involved are:

  1. Get the table names from the dictionary

  2. Get all the columns for each table

  3. For each column, get the missingness set id from its missingnessSet column. Skip the column if it doesn’t have one and it isn’t the primary key of the table.

  4. Get the missingness values of that set from the sets sheet

    1. Filter the sets to only include those whose setType column is missingnessSets and whose setID column is the missingness set id

    2. The partID column has the missingness value

  5. If the column is the primary key of the table, then it has no allowed missingness values, and the forbidden values are all of the dictionary’s missingness values, which are the parts whose partType column is missingness

  6. Every other column has no forbidden values, since a value outside its missingness set isn’t a missingness value in that column

  7. Add this rule for the column

For example, the ODM snippets below will be used to generate the validation schema for the examples above,

                                                     Parts v2                                                      
┏━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┓
┃ partID   partTy…  status  sites   sitesR…  sampl…  sample…  dataT…  maxLen…  missi…  firstR…  lastU… ┃
┡━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━┩
│ NA      │ missin… │ active │ NA     │ NA      │ NA     │ NA      │ varch… │ NA      │ NA     │ 1.0.0   │ 2.0.0  │
│ nan     │ missin… │ active │ NA     │ NA      │ NA     │ NA      │ varch… │ NA      │ NA     │ 1.0.0   │ 2.0.0  │
│ nr      │ missin… │ active │ NA     │ NA      │ NA     │ NA      │ varch… │ NA      │ NA     │ 1.0.0   │ 2.0.0  │
│ sites   │ tables  │ active │ NA     │ NA      │ NA     │ NA      │ NA     │ NA      │ NA     │ 1.0.0   │ 2.0.0  │
│ samples │ tables  │ active │ NA     │ NA      │ NA     │ NA      │ NA     │ NA      │ NA     │ 1.0.0   │ 2.0.0  │
│ siteID  │ attrib… │ active │ pK     │ mandat… │ fK     │ option… │ varch… │ NA      │ genMi… │ 1.0.0   │ 2.0.0  │
│ geoLat  │ attrib… │ active │ header │ mandat… │ NA     │ NA      │ float  │ NA      │ nrNAM… │ 1.0.0   │ 2.0.0  │
│ geoLong │ attrib… │ active │ header │ option… │ NA     │ NA      │ float  │ NA      │ NA     │ 1.0.0   │ 2.0.0  │
│ country │ attrib… │ active │ header │ option… │ NA     │ NA      │ varch… │ 2       │ nrMis… │ 1.0.0   │ 2.0.0  │
└─────────┴─────────┴────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┴────────┴─────────┴────────┘
                                                      Sets v2                                                      
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓
┃ setID                      setType                partID     status     firstReleased       lastUpdated    ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩
│ genMissingnessSet         │ missingnessSets       │ NA        │ active    │ 1.0.0              │ 2.0.0          │
│ genMissingnessSet         │ missingnessSets       │ nan       │ active    │ 1.0.0              │ 2.0.0          │
│ genMissingnessSet         │ missingnessSets       │ nr        │ active    │ 1.0.0              │ 2.0.0          │
│ nrNAMissingnessSet        │ missingnessSets       │ NA        │ active    │ 1.0.0              │ 2.0.0          │
│ nrNAMissingnessSet        │ missingnessSets       │ nr        │ active    │ 1.0.0              │ 2.0.0          │
│ nrMissingnessSet          │ missingnessSets       │ nr        │ active    │ 1.0.0              │ 2.0.0          │
└───────────────────────────┴───────────────────────┴───────────┴───────────┴────────────────────┴────────────────┘

The rule is added to the siteID, geoLat and country columns, but not to the geoLong column, which neither has a missingness set nor is a primary key.

Cerberus Schema

Cerberus does not have support for this rule. We need to extend the cerberus validator with a custom rule called missingness. Its constraint has two fields, which partition the dictionary’s missingness values into the ones the column accepts and the ones it doesn’t:

  • allowed: The missingness values the column can take on

  • forbidden: The missingness values that trigger this rule, which is everything for a primary key and nothing for the other columns

Both fields are needed, because the validator has to be able to tell a missingness value that is reported apart from one that is exempted. A value in neither field is left alone. The custom rule is evaluated before the other cerberus rules for the column, so that it can exempt the allowed missingness values from them.

The cerberus schema for the ODM dictionary snippets above is shown below,

{
'schemaVersion': '2.0.0',
'schema': {
│   │   'sites': {
│   │   │   'type': 'list',
│   │   │   'schema': {
│   │   │   │   'type': 'dict',
│   │   │   │   'schema': {
│   │   │   │   │   'siteID': {
│   │   │   │   │   │   'missingness': {
│   │   │   │   │   │   │   'allowed': [],
│   │   │   │   │   │   │   'forbidden': [
│   │   │   │   │   │   │   │   'NA',
│   │   │   │   │   │   │   │   'nan',
│   │   │   │   │   │   │   │   'nr'
│   │   │   │   │   │   │   ]
│   │   │   │   │   │   },
│   │   │   │   │   │   'meta': [
│   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   'ruleID': 'missingness',
│   │   │   │   │   │   │   │   'meta': [
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'siteID',
│   │   │   │   │   │   │   │   │   │   'sites': 'pK',
│   │   │   │   │   │   │   │   │   │   'sitesRequired': 'mandatory',
│   │   │   │   │   │   │   │   │   │   'missingnessSet': 'genMissingnessSet'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'NA',
│   │   │   │   │   │   │   │   │   │   'partType': 'missingness'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'nan',
│   │   │   │   │   │   │   │   │   │   'partType': 'missingness'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'nr',
│   │   │   │   │   │   │   │   │   │   'partType': 'missingness'
│   │   │   │   │   │   │   │   │   }
│   │   │   │   │   │   │   │   ]
│   │   │   │   │   │   │   }
│   │   │   │   │   │   ]
│   │   │   │   │   },
│   │   │   │   │   'geoLat': {
│   │   │   │   │   │   'missingness': {
│   │   │   │   │   │   │   'allowed': [
│   │   │   │   │   │   │   │   'NA',
│   │   │   │   │   │   │   │   'nr'
│   │   │   │   │   │   │   ],
│   │   │   │   │   │   │   'forbidden': []
│   │   │   │   │   │   },
│   │   │   │   │   │   'meta': [
│   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   'ruleID': 'missingness',
│   │   │   │   │   │   │   │   'meta': [
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'geoLat',
│   │   │   │   │   │   │   │   │   │   'sites': 'header',
│   │   │   │   │   │   │   │   │   │   'sitesRequired': 'mandatory',
│   │   │   │   │   │   │   │   │   │   'missingnessSet': 'nrNAMissingnessSet'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'NA',
│   │   │   │   │   │   │   │   │   │   'setID': 'nrNAMissingnessSet'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'nr',
│   │   │   │   │   │   │   │   │   │   'setID': 'nrNAMissingnessSet'
│   │   │   │   │   │   │   │   │   }
│   │   │   │   │   │   │   │   ]
│   │   │   │   │   │   │   }
│   │   │   │   │   │   ]
│   │   │   │   │   },
│   │   │   │   │   'country': {
│   │   │   │   │   │   'missingness': {
│   │   │   │   │   │   │   'allowed': [
│   │   │   │   │   │   │   │   'nr'
│   │   │   │   │   │   │   ],
│   │   │   │   │   │   │   'forbidden': []
│   │   │   │   │   │   },
│   │   │   │   │   │   'meta': [
│   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   'ruleID': 'missingness',
│   │   │   │   │   │   │   │   'meta': [
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'country',
│   │   │   │   │   │   │   │   │   │   'sites': 'header',
│   │   │   │   │   │   │   │   │   │   'sitesRequired': 'optional',
│   │   │   │   │   │   │   │   │   │   'missingnessSet': 'nrMissingnessSet'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'nr',
│   │   │   │   │   │   │   │   │   │   'setID': 'nrMissingnessSet'
│   │   │   │   │   │   │   │   │   }
│   │   │   │   │   │   │   │   ]
│   │   │   │   │   │   │   }
│   │   │   │   │   │   ]
│   │   │   │   │   }
│   │   │   │   },
│   │   │   │   'meta': [
│   │   │   │   │   {
│   │   │   │   │   │   'partID': 'sites',
│   │   │   │   │   │   'partType': 'tables'
│   │   │   │   │   }
│   │   │   │   ]
│   │   │   }
│   │   },
│   │   'samples': {
│   │   │   'type': 'list',
│   │   │   'schema': {
│   │   │   │   'type': 'dict',
│   │   │   │   'schema': {
│   │   │   │   │   'siteID': {
│   │   │   │   │   │   'missingness': {
│   │   │   │   │   │   │   'allowed': [
│   │   │   │   │   │   │   │   'NA',
│   │   │   │   │   │   │   │   'nan',
│   │   │   │   │   │   │   │   'nr'
│   │   │   │   │   │   │   ],
│   │   │   │   │   │   │   'forbidden': []
│   │   │   │   │   │   },
│   │   │   │   │   │   'meta': [
│   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   'ruleID': 'missingness',
│   │   │   │   │   │   │   │   'meta': [
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'siteID',
│   │   │   │   │   │   │   │   │   │   'samples': 'fK',
│   │   │   │   │   │   │   │   │   │   'samplesRequired': 'optional',
│   │   │   │   │   │   │   │   │   │   'missingnessSet': 'genMissingnessSet'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'NA',
│   │   │   │   │   │   │   │   │   │   'setID': 'genMissingnessSet'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'nan',
│   │   │   │   │   │   │   │   │   │   'setID': 'genMissingnessSet'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'nr',
│   │   │   │   │   │   │   │   │   │   'setID': 'genMissingnessSet'
│   │   │   │   │   │   │   │   │   }
│   │   │   │   │   │   │   │   ]
│   │   │   │   │   │   │   }
│   │   │   │   │   │   ]
│   │   │   │   │   }
│   │   │   │   },
│   │   │   │   'meta': [
│   │   │   │   │   {
│   │   │   │   │   │   'partID': 'samples',
│   │   │   │   │   │   'partType': 'tables'
│   │   │   │   │   }
│   │   │   │   ]
│   │   │   }
│   │   }
}
}

The meta field for this rule should have multiple entries,

  • the row from the parts sheet for the column. The entry should include the partID, the <table_name>, the <table_name>Required and the missingnessSet fields.

  • and an entry per missingness value in the set. This entry should include the partID and the setID column. For a primary key, whose constraint comes from the missingness parts rather than from a set, the entries are the forbidden missingness parts instead, and they should include the partID and the partType column.

ODM Version 1

For version 1 validation schemas, we add this rule to only those version 2 columns which have a version 1 equivalent part.

For the ODM snippet below,

                                                     Parts v1                                                      
┏━━━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━┳━━━━━━━┳━━━━━━┳━━━━━━━┳━━━━━━┳━━━━━━━┳━━━━━━┳━━━━━━━┳━━━━━━┳━━━━━━━┳━━━━━━┳━━━━━━━┓
┃ part…  part…  stat…  sit…  site…  sam…  samp…  dat…  maxL…  mis…  vers…  ver…  vers…  fir…  last… ┃
┡━━━━━━━╇━━━━━━━╇━━━━━━━╇━━━━━━╇━━━━━━━╇━━━━━━╇━━━━━━━╇━━━━━━╇━━━━━━━╇━━━━━━╇━━━━━━━╇━━━━━━╇━━━━━━━╇━━━━━━╇━━━━━━━┩
│ NA    │ miss… │ acti… │ NA   │ NA    │ NA   │ NA    │ var… │ NA    │ NA   │ NA    │ NA   │ NA    │ 1.0… │ 2.0.0 │
│ nan   │ miss… │ acti… │ NA   │ NA    │ NA   │ NA    │ var… │ NA    │ NA   │ NA    │ NA   │ NA    │ 1.0… │ 2.0.0 │
│ nr    │ miss… │ acti… │ NA   │ NA    │ NA   │ NA    │ var… │ NA    │ NA   │ NA    │ NA   │ NA    │ 1.0… │ 2.0.0 │
│ sites │ tabl… │ acti… │ NA   │ NA    │ NA   │ NA    │ NA   │ NA    │ NA   │ tabl… │ Site │ NA    │ 1.0… │ 2.0.0 │
│ samp… │ tabl… │ acti… │ NA   │ NA    │ NA   │ NA    │ NA   │ NA    │ NA   │ tabl… │ Sam… │ NA    │ 1.0… │ 2.0.0 │
│ site… │ attr… │ acti… │ pK   │ mand… │ fK   │ opti… │ var… │ NA    │ gen… │ vari… │ Sit… │ site… │ 1.0… │ 2.0.0 │
│ geoL… │ attr… │ acti… │ hea… │ mand… │ NA   │ NA    │ flo… │ NA    │ nrN… │ vari… │ Site │ lati… │ 1.0… │ 2.0.0 │
│ geoL… │ attr… │ acti… │ hea… │ opti… │ NA   │ NA    │ flo… │ NA    │ NA   │ vari… │ Site │ long… │ 1.0… │ 2.0.0 │
│ coun… │ attr… │ acti… │ hea… │ opti… │ NA   │ NA    │ var… │ 2     │ nrM… │ vari… │ Site │ coun… │ 1.0… │ 2.0.0 │
└───────┴───────┴───────┴──────┴───────┴──────┴───────┴──────┴───────┴──────┴───────┴──────┴───────┴──────┴───────┘

The corresponding cerberus schema would be,

{
'schemaVersion': '1.0.0',
'schema': {
│   │   'Site': {
│   │   │   'type': 'list',
│   │   │   'schema': {
│   │   │   │   'type': 'dict',
│   │   │   │   'schema': {
│   │   │   │   │   'siteID': {
│   │   │   │   │   │   'missingness': {
│   │   │   │   │   │   │   'allowed': [],
│   │   │   │   │   │   │   'forbidden': [
│   │   │   │   │   │   │   │   'NA',
│   │   │   │   │   │   │   │   'nan',
│   │   │   │   │   │   │   │   'nr'
│   │   │   │   │   │   │   ]
│   │   │   │   │   │   },
│   │   │   │   │   │   'meta': [
│   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   'ruleID': 'missingness',
│   │   │   │   │   │   │   │   'meta': [
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'siteID',
│   │   │   │   │   │   │   │   │   │   'sites': 'pK',
│   │   │   │   │   │   │   │   │   │   'sitesRequired': 'mandatory',
│   │   │   │   │   │   │   │   │   │   'version1Location': 'variables',
│   │   │   │   │   │   │   │   │   │   'version1Table': 'Site;Sample',
│   │   │   │   │   │   │   │   │   │   'version1Variable': 'siteID',
│   │   │   │   │   │   │   │   │   │   'missingnessSet': 'genMissingnessSet'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'NA',
│   │   │   │   │   │   │   │   │   │   'partType': 'missingness'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'nan',
│   │   │   │   │   │   │   │   │   │   'partType': 'missingness'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'nr',
│   │   │   │   │   │   │   │   │   │   'partType': 'missingness'
│   │   │   │   │   │   │   │   │   }
│   │   │   │   │   │   │   │   ]
│   │   │   │   │   │   │   }
│   │   │   │   │   │   ]
│   │   │   │   │   },
│   │   │   │   │   'latitude': {
│   │   │   │   │   │   'missingness': {
│   │   │   │   │   │   │   'allowed': [
│   │   │   │   │   │   │   │   'NA',
│   │   │   │   │   │   │   │   'nr'
│   │   │   │   │   │   │   ],
│   │   │   │   │   │   │   'forbidden': []
│   │   │   │   │   │   },
│   │   │   │   │   │   'meta': [
│   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   'ruleID': 'missingness',
│   │   │   │   │   │   │   │   'meta': [
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'geoLat',
│   │   │   │   │   │   │   │   │   │   'sites': 'header',
│   │   │   │   │   │   │   │   │   │   'sitesRequired': 'mandatory',
│   │   │   │   │   │   │   │   │   │   'version1Location': 'variables',
│   │   │   │   │   │   │   │   │   │   'version1Table': 'Site',
│   │   │   │   │   │   │   │   │   │   'version1Variable': 'latitude',
│   │   │   │   │   │   │   │   │   │   'missingnessSet': 'nrNAMissingnessSet'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'NA',
│   │   │   │   │   │   │   │   │   │   'setID': 'nrNAMissingnessSet'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'nr',
│   │   │   │   │   │   │   │   │   │   'setID': 'nrNAMissingnessSet'
│   │   │   │   │   │   │   │   │   }
│   │   │   │   │   │   │   │   ]
│   │   │   │   │   │   │   }
│   │   │   │   │   │   ]
│   │   │   │   │   },
│   │   │   │   │   'country': {
│   │   │   │   │   │   'missingness': {
│   │   │   │   │   │   │   'allowed': [
│   │   │   │   │   │   │   │   'nr'
│   │   │   │   │   │   │   ],
│   │   │   │   │   │   │   'forbidden': []
│   │   │   │   │   │   },
│   │   │   │   │   │   'meta': [
│   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   'ruleID': 'missingness',
│   │   │   │   │   │   │   │   'meta': [
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'country',
│   │   │   │   │   │   │   │   │   │   'sites': 'header',
│   │   │   │   │   │   │   │   │   │   'sitesRequired': 'optional',
│   │   │   │   │   │   │   │   │   │   'version1Location': 'variables',
│   │   │   │   │   │   │   │   │   │   'version1Table': 'Site',
│   │   │   │   │   │   │   │   │   │   'version1Variable': 'country',
│   │   │   │   │   │   │   │   │   │   'missingnessSet': 'nrMissingnessSet'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'nr',
│   │   │   │   │   │   │   │   │   │   'setID': 'nrMissingnessSet'
│   │   │   │   │   │   │   │   │   }
│   │   │   │   │   │   │   │   ]
│   │   │   │   │   │   │   }
│   │   │   │   │   │   ]
│   │   │   │   │   }
│   │   │   │   },
│   │   │   │   'meta': [
│   │   │   │   │   {
│   │   │   │   │   │   'partID': 'sites',
│   │   │   │   │   │   'partType': 'tables',
│   │   │   │   │   │   'version1Location': 'tables',
│   │   │   │   │   │   'version1Table': 'Site'
│   │   │   │   │   }
│   │   │   │   ]
│   │   │   }
│   │   },
│   │   'Sample': {
│   │   │   'type': 'list',
│   │   │   'schema': {
│   │   │   │   'type': 'dict',
│   │   │   │   'schema': {
│   │   │   │   │   'siteID': {
│   │   │   │   │   │   'missingness': {
│   │   │   │   │   │   │   'allowed': [
│   │   │   │   │   │   │   │   'NA',
│   │   │   │   │   │   │   │   'nan',
│   │   │   │   │   │   │   │   'nr'
│   │   │   │   │   │   │   ],
│   │   │   │   │   │   │   'forbidden': []
│   │   │   │   │   │   },
│   │   │   │   │   │   'meta': [
│   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   'ruleID': 'missingness',
│   │   │   │   │   │   │   │   'meta': [
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'siteID',
│   │   │   │   │   │   │   │   │   │   'samples': 'fK',
│   │   │   │   │   │   │   │   │   │   'samplesRequired': 'optional',
│   │   │   │   │   │   │   │   │   │   'version1Location': 'variables',
│   │   │   │   │   │   │   │   │   │   'version1Table': 'Site;Sample',
│   │   │   │   │   │   │   │   │   │   'version1Variable': 'siteID',
│   │   │   │   │   │   │   │   │   │   'missingnessSet': 'genMissingnessSet'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'NA',
│   │   │   │   │   │   │   │   │   │   'setID': 'genMissingnessSet'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'nan',
│   │   │   │   │   │   │   │   │   │   'setID': 'genMissingnessSet'
│   │   │   │   │   │   │   │   │   },
│   │   │   │   │   │   │   │   │   {
│   │   │   │   │   │   │   │   │   │   'partID': 'nr',
│   │   │   │   │   │   │   │   │   │   'setID': 'genMissingnessSet'
│   │   │   │   │   │   │   │   │   }
│   │   │   │   │   │   │   │   ]
│   │   │   │   │   │   │   }
│   │   │   │   │   │   ]
│   │   │   │   │   }
│   │   │   │   },
│   │   │   │   'meta': [
│   │   │   │   │   {
│   │   │   │   │   │   'partID': 'samples',
│   │   │   │   │   │   'partType': 'tables',
│   │   │   │   │   │   'version1Location': 'tables',
│   │   │   │   │   │   'version1Table': 'Sample'
│   │   │   │   │   }
│   │   │   │   ]
│   │   │   }
│   │   }
}
}

The meta field for version 1 includes everything in version 2 but should also include the following columns for the entry for the column,

  • version1Location

  • version1Table

  • version1Variable

Note that the ODM only defines missingness sets from version 2.0.0 onwards, since the rows of the sets sheet that define the missingness sets are not part of version 1. A version 1 schema generated from a version 2 dictionary therefore only has missingness rules for its primary keys, whose constraint doesn’t come from a set.