Identifying Plant Families by Pattern: Turning a Printed Key into Data
Most plant apps answer 'what is this?'. Family-level pattern recognition answers 'how would I know?'. Here is how we encoded the patterns method as a trait ontology, what it can resolve, and where the data runs out.
A photo classifier answers one question: what is this? It does not tell you how it knew, and it leaves you no better at recognising the next plant. The patterns method — learning the handful of structural traits that mark a whole family — answers a different and more durable question: how would I know?
We wanted that second thing in data form. This is what encoding it actually involved, including the parts that did not work.
Why family first
There are roughly 3,900 species in our dataset and 242 families. Identifying to species from scratch is a search over thousands of candidates. Identifying to family is a search over a few hundred, and once you have the family, the remaining question is small and often answerable from one or two extra characters.
The information-theoretic version: distinguishing 242 families needs about log2(242) ≈ 7.9 bits. A question with two roughly balanced answers yields at most 1 bit, so eight or nine good binary questions would be enough in a perfect world. That is the theoretical floor. Real botanical characters are neither balanced nor universally recorded, so the practical number is higher — but it sets the target.
More importantly, family traits are transferable. Learning that Lamiaceae has square stems, opposite leaves and two-lipped flowers lets you place a mint you have never seen. Learning that one photo is basil does not.
The traits have to share one vocabulary
Our first version failed on something dull: the family records and the species records described the same character with different words. Families said flowers were Radial or Bilateral. Species said Actinomorphic or Zygomorphic. Both are correct botanical usage. Neither could be joined to the other.
That is not a display problem to paper over at render time — it means the two datasets cannot be reasoned about together at all. We migrated the stored values so both levels use one vocabulary, then updated every consumer in the same pass: the tree builder, the live key, the species filters, and the enrichment script that would otherwise have quietly reintroduced the old terms on its next run.
Borrowing a model instead of inventing one
Rather than invent a controlled vocabulary, we followed the pattern the Planteome plant-trait-ontology uses: Entity-Quality. A trait is a quality inhering in a structure — ovate (a shape) inheres in leaf (a structure). Qualities come from PATO, structures from PO.
So leafShape: Ovate is not a bare string. It carries PATO:0001891, and the field itself is anchored to the plant-ontology term for a leaf. Around three quarters of our vocabulary terms now resolve to a real PATO or PO identifier; the rest are botanical refinements with no exact ontology equivalent, recorded as such rather than invented.
This matters less for the website than for everything downstream. A trait with an ontology ID can be aligned with other datasets. A trait that is a string in our database cannot.
One term we had simply omitted: Oblong. It is one of the commonest leaf shapes in botanical description, and because it was missing from the vocabulary, every sentence saying "the leaves are oblong" extracted nothing at all. Absences in a controlled vocabulary are invisible — nothing errors, the data is just quietly thinner than it looks.
What the key can actually resolve
The family key asks up to eight questions — leaf venation, flower symmetry, petal count and fusion, sap, stem cross-section, stamen arrangement, ovary position. Built over the 204 families with enough recorded characters, it produces 54 leaves that land on a single family and 41 that end in a tie.
That is a useful and honest result. Roughly half of all paths resolve to one family; the rest narrow to a small group that shares every character we have recorded. A tie is not a failure — going from 242 candidates to three is most of the work, and the remaining three are then separated by something the key does not yet hold.
Within families, the species-level keys resolve 984 of 3,935 species uniquely, leaving 605 tied groups. Fabaceae, with over 300 species, is the hardest: no small set of visible characters separates that many species, and pretending otherwise would be dishonest.
Availability beats information gain
The first version of the key scored each question by how well it split the remaining candidates and asked whichever won. It opened with petal count, because petal count is recorded for 195 families and divides them nicely.
It is also a terrible opening question. Half the year there is no flower. On a tree, the flowers are twenty feet up. Scoring on information gain alone had produced a key that was mathematically efficient and practically unusable.
The fix was to make observability a hard gate rather than a tiebreaker. Every question now declares what you need in front of you — vegetative or floral — and the key opens by asking whether the plant is in flower. Answer no, and it never asks about petals; it works through sap, venation and stem section, and stops honestly when those run out.
That change exposed the real limit. Only three of our eight questions are vegetative, and two of them barely discriminate: 87% of families with recorded sap have Clear, and 98% have round stems. Leaf venation is the one vegetative character that carries real weight, and it is recorded for just 64 of 242 families. The no-flower path is weak, and it is weak because of missing data, not because of the interface.
'Variable' is not an answer
A subtler bug: some families store Variable for a character, meaning the family spans several states. That is a fact about the family, not something anyone can observe on a plant.
It was being offered as a selectable answer, which is merely silly. Worse, it was eliminating families. The matching logic keeps a family when its value is unknown, but Variable is not unknown — it is a value that fails to equal whatever you picked. Answering "5 petals" was ruling out every family recorded as variable in petal count: exactly the families that could have matched. Normalising Variable to unknown at the point of access fixed both.
Where the data runs out
About 28% of our species carry no leaf character at all. We tried to close that gap by extracting traits from Wikipedia with organ-scoped sentence matching, and it worked to a point — around 4,500 facts, each carrying the sentence it came from so a reader can check it.
Then it stopped working, and the reason is worth stating plainly. Of the roughly 1,100 species with no leaf trait, 839 have Wikipedia articles under 800 characters — two sentences of taxonomy and range. There is nothing to extract. These are disproportionately Indian endemics: Atuna travancorica, an endangered Western Ghats tree, has a 203-character article, no GBIF description, and no morphological description in Kew's taxon record either.
Improving the extractor moved that number by seven species. The bottleneck is not parsing, it is that the source does not contain the information. Closing it needs a flora, not a better regex.
What this is for
We are not claiming a trait key replaces a vision model. The realistic architecture is a classifier proposing candidates, a vision-language model reading structure off the image where one is available, and the trait key acting as a re-ranker and an explanation — narrowing, checking, and showing which characters support which answer.
The part that does not come from the model is the part we care about. A classifier that says "Lamiaceae, 94%" teaches nothing. A key that says "square stem, opposite leaves, two-lipped flower — that is the mint family, and here is how the next one will look" leaves you able to do it yourself.
That is the difference between an app you keep needing and one that makes you better at looking.
