Skip to content

Binned quantitative classes have overlapping boundaries: 41 boundaries across 10 series admit two classes for one value #608

Description

@realmarcin

In METPO's binned quantitative classes, consecutive bins share their boundary value: the lower bin declares maxInclusive N and the next declares minInclusive N. A measurement of exactly N therefore satisfies both class definitions.

Observed in metpo.owl, versionIRI https://w3id.org/metpo/releases/2026-06-12/metpo.owl.

Scope: this is systematic, not isolated

Every binned series is affected — 41 overlapping boundaries across 10 series:

series bins overlapping boundaries
temperature optimum 7 6
temperature range 7 6
pH range 6 5
pH delta 6 5
temperature delta 5 4
GC 4 3
NaCl optimum 4 3
NaCl range 4 3
NaCl delta 4 3
pH optimum 4 3

Concretely, for temperature optimum:

METPO:1000441  temperature optimum very low   value <= 10
METPO:1000442  temperature optimum low        value >= 10  and <= 22
METPO:1000443  temperature optimum mid1       value >= 22  and <= 27
METPO:1000444  temperature optimum mid2       value >= 27  and <= 30
METPO:1000445  temperature optimum mid3       value >= 30  and <= 34
METPO:1000446  temperature optimum mid4       value >= 34  and <= 40
METPO:1000447  temperature optimum high       value >= 40

10 satisfies both 1000441 and 1000442; 22 satisfies both 1000442 and 1000443; and so on. The same shape appears in GC (<=42.65 / >=42.65), NaCl (<=1 / >=1) and pH (<=6 / >=6).

Why it matters

These classes read as a partition — "very low / low / mid1 / … / high" — but they are not disjoint. Consequences:

  • A reasoner will infer a boundary-valued individual into two sibling classes. If the siblings are ever asserted owl:disjointWith, that becomes an inconsistency rather than a modelling wrinkle.
  • Any consumer binning a measurement must invent a tie-break, and different consumers will invent different ones, so the same input yields different METPO terms in different pipelines. That silently defeats the point of using a shared ontology for the bins.
  • Boundary values are not rare in this domain. Growth temperatures cluster hard on round numbers — 10, 22, 30, 34, 37, 40 — so the ambiguous values are exactly the commonly reported ones, not tail cases.

The rule we adopted downstream

In kg-microbe-projects we bin BacDive growth temperatures to these terms. Our rule:

Evaluate bins in ascending order and take the first match.

which makes every bin after the first behave as lower-exclusive:

very low  value <= 10
low       10 < value <= 22
mid1      22 < value <= 27
mid2      27 < value <= 30
mid3      30 < value <= 34
mid4      34 < value <= 40
high      40 < value

So a reading of exactly 10 is very low and exactly 22 is low — the lower bin wins. This is documented in our code and pinned by boundary tests at 10, 22, 30, 34 and 40. We are not proposing it as the right semantics, only stating what we do so our terms are interpretable; any consumer choosing "upper bin wins" would produce different terms from the same data.

Suggested fix

Make the intervals half-open in the ontology, so the partition is genuine and no tie-break is needed:

very low  maxExclusive 10
low       minInclusive 10   maxExclusive 22
mid1      minInclusive 22   maxExclusive 27
...
high      minInclusive 40

That is the usual convention for binned ranges and would make boundary values unambiguous without changing which bin any non-boundary value falls into. Note it assigns boundary values to the upper bin, the opposite of our current downstream rule — we would rather follow the ontology than keep our own convention, so we would change to match.

If the current inclusive-both-ends encoding is deliberate (e.g. the bins are meant as overlapping guidance rather than a partition), it would help to say so in the class definitions, since the labels strongly imply a partition.

Reproducing

The overlap set above was extracted by walking owl:Restriction elements in metpo.owl and comparing each series' consecutive minInclusive / maxInclusive facets. Happy to contribute the extraction script or a PR applying the half-open bounds if that is useful.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions