Back to Blog

Agent workflows

Count Rows or Measured Values? Define Your Data Agent's Denominator

Separate population size, measured values and missing group labels before accepting an agent-generated report. Use a five-record fixture to expose silent denominator changes.

OpenAgentSkillPublished:

The chart says four records. The export contains five.

A reporting agent groups an export by team, counts a score column and produces a tidy summary. The result looks plausible. But did it count people, completed measurements, or only records with known team labels?

Those are different populations. A successful query cannot decide which one the report owner intended.

This guide is for analysts and operators using agents to summarize operational exports. The specific problem is choosing a denominator after the source rows are available, not correcting joins or repairing an incomplete upstream export.

Methodology: separate the row from its measurement

We reviewed PostgreSQL aggregate documentation and pandas grouping documentation on October 18, 2026. We also executed a read-only SQL query over five invented records. It used an inline VALUES fixture, not customer data, and changed no tables. We did not execute a pandas implementation.

Our selection criteria are the business unit being counted, the meaning of a missing measurement and whether records with missing category labels remain visible.

The existing join-cardinality guide covers row multiplication during enrichment. This workflow starts with an unchanged five-row input and asks what the aggregation actually counts.

Work through five records before choosing a function

The fixture contains these records:

RecordTeamScore
ANorth8
BNorthMissing
CSouth0
DSouthMissing
EMissing4

Here, Missing represents a database null, not a literal text label. The population has five records. Three have measurements, including the observed zero. Two lack measurements. One record has no team label but does have a score.

In our executed query, each named team produced two rows, one measured score and one missing score. The null-team group produced one row, one measured score and no missing score. Adding the group results gives five population records and three measured values.

Do not replace missing scores with zero just to make these counts agree. In this fixture, zero is a reported result; missing means no result was provided. A different business definition needs an explicit decision.

Compare counting operations by their question

PostgreSQL's aggregate reference distinguishes COUNT(*) from COUNT(expression): the first counts input rows, while the second counts non-null expression values.

For the fixture, COUNT(*) answers how many records reached aggregation. COUNT(score) answers how many have a score. Their difference reports missing scores in that same input population.

The pandas SQL comparison guide makes a corresponding distinction between group size and column count. GroupBy.size counts group rows; GroupBy.count excludes missing values for each counted column. A multi-column count can therefore return different totals for different fields.

Our recommendation is to name outputs by meaning: population_records, measured_scores and missing_scores. A column called count leaves the denominator ambiguous. Neither operation establishes that each row is a different person; that requires a separate identity and grain check.

Review missing group labels independently

There is another decision before counting: which rows enter the groups?

The DataFrame.groupby reference documents dropna=True as the default. Missing grouping keys are excluded; dropna=False retains them as a group.

For this fixture, excluding the missing team removes record E from grouped reporting even though its score is present. Under that policy, group sizes sum to four and measured scores to two. That is not the same issue as the two missing measurements.

Choose between a visible unassigned group and an explicitly documented exclusion. If the brief says all exported records, a useful default proposal is to preserve unassigned records separately rather than silently hide them. The report owner must approve any alternative.

Keep the raw grouping value distinct from the display label. A real team named Unassigned should not accidentally merge with missing metadata because the agent replaced both with the same text.

Give the agent an acceptance contract

Before implementation, provide a compact specification:

  • One source row represents one submitted assessment.
  • The headline population includes every eligible assessment in the stated period.
  • A zero score is a measurement; a null score is not.
  • Missing team labels remain visible in an unassigned group.
  • Each group reports population, measured and missing counts.
  • Group totals must reconcile to the eligible source population.

For the invented fixture, measurement coverage is three of five records, or 60%. That is arithmetic on the fixture, not a measured product metric. Named-team-only coverage would be two of four, or 50%. Both calculations can be internally correct while answering different questions.

Ask for the population rule beside the percentage. Do not accept a percentage alone, and do not let the agent choose a denominator because it makes the result look better.

Limitations and a useful handoff

A count contract does not validate the score itself, prove source completeness or establish unique identities. Empty strings, sentinel numbers and invalid text may also need normalization before they can be treated as missing. Document that normalization rather than assuming every tool recognizes the same values.

The final report should include its input scope, aggregation expressions, missing-key policy and reconciliation totals. If a population change is intentional, show it as an explicit exclusion with a reason.

Use the OpenAgentSkill directory to explore implementation workflows, but evaluate this small fixture before applying one to a real report. The acceptance question is not whether the agent produced a chart. It is whether a reader can tell exactly who and what each number counts.