datatools DC Brasil — admin console
LIVE 20260729T191406Z-16b1 base, jonatas, felipe, victor, bernardo, kauan · Jul 29
← Explore

The Data Commons data model

Why the curated output looks the way it does: what a variable actually is, what the constraint list means, and why one variable ends up split across several observation files. For definitions of individual terms, see the glossary.

Everything is a node in one graph

Data Commons has no tables. There are nodes and typed edges between them. Three kinds of node matter when reading a run:

Schema nodes Class · Property · Enumeration

The vocabulary itself. A Class names a kind of thing (Person, FarmInventory); a Property names a relation (count, gender); an Enumeration fixes the legal values of a property. Nothing here holds data — it declares what may be said.

Variable nodes StatisticalVariable

A recipe describing what is measured — population, measured property, statistic type, constraints. Still no numbers: one variable node stands behind millions of data points.

Observation nodes StatVarObservation

The facts. Each one binds a variable to an entity, a date and a value. These are the rows in the generated CSVs.

A variable is a recipe, not a number

The detail panel shows four core fields. Together they read as a sentence: take the population of things of class FarmInventory, apply the property count, report the raw measured value, in units of Head.

FieldRoleValue is…Example
populationTypeWhat kind of thing is measureda ClassFarmInventory
measuredPropertyWhich aspect of ita Propertycount
statTypeHow the numbers were reducedan enum valuemeasuredValue
unitThe measurement unita UnitOfMeasure nodeHead

Every one of those values is itself a node. That is the whole trick: because count is the same property node everywhere, a Brazilian count and a Japanese count are comparable without anyone reconciling spreadsheets.

What a property value may be

“Is every value a custom enumeration?” — only when the property says so. Each property declares its own range, and that decides the shape of the value:

Enumeration value
The property declares an Enumeration as its range, so the value must be one of a fixed set of member nodes — gender accepts Male or Female, race accepts WhiteAlone, AsianAlone and so on. An arbitrary string is rejected, and a member that does not exist upstream cannot be invented locally.
Class
populationType ranges over Class, so its value is a class node rather than an enum member. This is why the option list depends on which classes exist, not on a hand-written list.
Quantity / range
Numeric dimensions become their own small nodes — age = [25 34 Years] is a QuantityRange, not a label. That is what makes age brackets comparable across sources.
Unit node
unit points at a UnitOfMeasure node (Head, BrazilianReal, Percent), so units are shared vocabulary rather than free text.
Literal
A few properties — name, description — take plain text. These are labels for humans and never carry meaning the graph relies on.

New classes, properties and enum members can be defined locally — that is what the br/ namespace is for. What cannot be done is putting a value of the wrong shape into an existing property: the validator rejects it, because the shape is fixed by whoever declared the property upstream.

Constraints are ordinary properties with a job

Constraints are not a separate mechanism. They are properties on the same variable node; what makes them constraints is their role — they narrow the population.

populationType      = FarmInventory       ← all farm inventory, everything
farmInventoryType   = CattleAndCalves     ← now only cattle and calves
Scoped to the population
A constraint is only legal for populations it applies to: farmInventoryType belongs to FarmInventory and could not be attached to Person. This is why the editor offers constraint options per populationType rather than one global list.
Constraints distinguish siblings
Count_Person, Count_Person_Female and Count_Person_Female_Urban are the same four core fields with progressively more constraints. Adding a constraint creates a genuinely different variable — never widen an existing one to absorb a new dimension.
The DCID follows the constraints
The identifier is a deterministic serialisation of the core fields plus every constraint. If a DCID names a dimension the node does not carry as a constraint, two unrelated series can collide onto one identity — worth checking whenever a name and its constraint list disagree.

Observations: where, when, how much

Each row of a generated CSV becomes one observation node. The variable holds the meaning, so the observation only has to carry the specifics:

variableMeasured
The variable node — the recipe above.
observationAbout
The entity the fact is about — a place node such as country/BRA or br/geoId/3550308.
observationDate
The period the value covers, e.g. "2023" or "2023-01-01".
value
The number itself.
unit · measurementMethod · observationPeriod
Optional qualifiers carried per observation, so one variable can hold series that differ in method or cadence.

Why one variable has several observation files

The obs list shows a type in front of each file — Country, AdministrativeArea2, and so on. That is not a type of the observation: every observation is a StatVarObservation. It is the type of the entities the file is about, i.e. the geographic level of its rows.

Entity typeExample entitiesRows scale with
Country country/BRA1 place
BrazilianRegion localNorte, Nordeste, …5 places
AdministrativeArea1 states27 places
AdministrativeArea2 municípios~5,570 places
br/IpeaMetropolitanArea localIPEA metro areasa few dozen

So a file with ~142k rows is not a different variable — it is the municipal level, ~5,570 places across ~50 years. Same recipe, finer resolution. The namespace is the tell for which levels are ours: Country and AdministrativeArea1/2 are standard Data Commons classes, while BrazilianRegion and br/IpeaMetropolitanArea are defined locally.

End to end

“Median income of women aged 25–34 in São Paulo, 2020, in BRL” — first the recipe:

Node:              dcid:Median_Income_Person_Female_Years25To34
typeOf:            StatisticalVariable
populationType:    Person                  ← Class
measuredProperty:  income                  ← Property
statType:          medianValue             ← enum value
unit:              BrazilianReal           ← UnitOfMeasure node
gender:            Female                  ← constraint, enum value
age:               [25 34 Years]           ← constraint, quantity range

…then one of its many observations:

typeOf:            StatVarObservation
variableMeasured:  Median_Income_Person_Female_Years25To34
observationAbout:  br/geoId/3550308        ← entity, typeOf AdministrativeArea2
observationDate:   "2020"
value:             2400

The separation is the point: the variable says what is measured, the observation says where, when and how much, and the level (AdministrativeArea2) is a fact about the place node — not about the observation.