Quality Flagging
Variables may have associated data quality variables, known as flags. These
variables should be named <variable_name>_<qcpostfix>
where <qcpostfix>
is consistent within a data product. The data quality variable should also be referenced by name in the ancillary_variables
attribute of the data variable.
Flagging may be automatic, manual, or a combination of the two. Where the flagging
is manual, further information about the reason for flagging may be added to the comment
attribute of the data quality variable.
Data which have been flagged should be treated with caution, and the data provider should be contacted if in doubt about whether data should be used.
Value-based Flags
Value-based flags represent the quality of the corresponding data variable, with a flag value of 0 representing data which are presumed to be of sufficient quality. Larger values of the flag generally correspond to lower quality data, though this isn’t always the case.
For example, consider a variable array with values
1 2 6 5 4 3 6 5 4 3 2 1 4 5 6 7 7 6 5 3 2
and its corresponding flag with values
0 0 0 0 1 1 0 1 1 1 2 2 1 0 0 0 0 0 0 0 0
To understand the meaning of these flag values, we can look at the flag_values
and flag_meanings
attributes of the flag variable, which may look like
flag_meanings
:data_good minor_data_quality_issue major_data_quality_issue
flag_values
:0b 1b 2b
We can see that there are three different flag meanings and three different flag values, and can deduce that a flag value of 0 indicates the data are considered good, a flag value of 1 indicates a minor data quality issue, and a flag value of 2 indicates a major data quality issue. A user may choose, for example, to eliminate data with a major quality issue. To do this, they would simply mask/nan the variable whereever the flag variable has a value of 2, leaving the variable array as
1 2 6 5 4 3 6 5 4 3 - - 4 5 6 7 7 6 5 3 2
.
Value-based flags will have the same dimensions as their associated variable, and the following variable attributes:
_FillValue
: -128bstandard_name
: ‘status_flag’ if the associated variable has no standard name, otherwise ‘<variable_standard_name> status_flag’long_name
: Flag for <variable_name>flag_values
: An array of the values that the flag variable can take. Typically runs from 0 to <length of flag_meanings> - 1.flag_meanings
: A space separated string of the meanings of each of the values in flag_values.
Bitmask flags
While the value-based flags map the values of an array to a single meaning, bitmask flags allow the representation of a boolean array for every flag_meaning
.
This is done by mapping each flag meaning to an increasing power of 2, which allows the representation of every possible state of every meaning using values from 1 to \(2^{\text{num. flags}-1}\).
A value of 0 indicates that no flags are set, and is set as a fill value.
In order to a bitmask flag it must first be unpacked. This adds to the complexity of using the flag, but makes flags much more powerful, so most variables in the FAAM core data product use bitmask flags.
For example, consider a variable array with values
1 2 6 5 4 3 6 5 4 3 2 1 4 5 6 7 7 6 5 3 2
and its corresponding flag with values
1 1 3 3 2 2 4 4 4 4 6 6 6 6 8 8 5 5 3 3 1
To understand the meaning of these flag values, we can look at the flag_masks
and flag_meanings
attributes of the flag variable, which may look like
flag_meanings
:aircraft_on_ground flow_out_of_range temp_out_of_range data_out_of_bounds
flag_masks
:1b 2b 4b 8b
There are four meanings, with each associated with a value of \(2^n\) with \(n\) taking the four values 0, 1, 2, 3. In the FAAM core data, flag values are guaranteed to be increasing powers of 2, thus the flag array can be unpacked simply by progressively right-bitshifting the flag array, and taking the result modulo 2. In python, this can be achieved with the following code:
# Note that we don't need to worry about using the flag_masks attribute, as it
# is guaranteed to be powers of 2 from 1 to 2^(n-1)
unpacked = {}
for i, meaning in enumerate(flag_var.flag_meanings.split()):
unpacked[meaning] = (flag_data >> i) % 2
this would leave us with the following in unpacked
:
{
aircraft_on_ground: array([1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1]),
flow_out_of_range: array([0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0]),
temp_out_of_range: array([0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0]),
data_out_of_bounds: array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0])
}
Bitmask flags will have the same dimensions as their associated variable, and the following variable attributes:
_FillValue
: 0bstandard_name
: ‘status_flag’ if the associated variable has no standard name, otherwise ‘<variable_standard_name> status_flag’long_name
: A descriptive name, indicatingvalid_range
: The valid range of values in the flag variable array. Should be 1b, 2^(<number of flag_meanings>) - 1flag_masks
: An array of the values that the flag variable can take, which will runs from 1 to 2^(<number of flag_meanings> - 1).flag_meanings
: A space separated string of the meanings of each of the values in flag_values.