# Method

## What was joined to what

Two public company lists, each taken whole:

- Fortune 500 2026, 500 domains, from the installmap universe
  file `fortune500/companies.csv`.
- Inc 5000 2026, 4,969 domains, from
  `inc5000_2026/companies.csv`, which also carries rank, industry, state, city,
  employee band, founding year and years on the list.

Domains are lowercased, `www.` and any scheme stripped, and deduplicated inside
each list. The join key is the person row's `company_domain` field against that
domain, exact string match. No fuzzy matching, no subsidiary rollup: a person
filed under a subsidiary domain is not counted for the parent.

The person table is `blitz_people` in the `data` database, about 137 million
person rows, indexed on `company_domain`. Every query is an `$in` batch of 50 domains
against that index. No full collection scan was run. Two queries per batch: an
aggregation grouping by domain to get the total person rows, and a find with the
broad title regex below applied server side.

## The broad server side filter

```
chief\s+(revenue|operating|operations|people|human|information security|data|product|sustainability|diversity|inclusion|customer|growth|marketing|technolog|technical|financial)|\bCHRO\b|\bCISO\b|(vice president|vp|svp|evp|head)\s+(of\s+)?(global\s+)?(diversity|dei|d&i|inclusion)
```

Only rows matching it leave the database. Every published title regex is a subset
of it, so nothing a published regex would match is lost at this stage.

## Published title regexes

Each is matched case insensitively against the person's `title` field only, never
against the headline. A person can match more than one title.

| Title | Regex |
|---|---|
| Chief Revenue Officer | `\bchief\s+(\w+\s+){0,3}?revenue\s+(\w+\s+){0,3}?officer\b` |
| Chief Operating Officer | `\bchief\s+(\w+\s+){0,3}?operat(ing|ions)\s+(\w+\s+){0,3}?officer\b` |
| Chief People Officer / CHRO | `\bchief\s+(\w+\s+){0,3}?(people|human\s+resources?|human\s+capital)\s+(\w+\s+){0,3}?officer\b|\bCHRO\b` |
| Chief Information Security Officer | `\bchief\s+information\s+security\s+officer\b|\bCISO\b` |
| Chief Data Officer | `\bchief\s+data\s+(\w+\s+){0,3}?officer\b|\bchief\s+data\s*&\s*analytics\s+officer\b` |
| Chief Product Officer | `\bchief\s+(\w+\s+){0,3}?product\s+(\w+\s+){0,3}?officer\b` |
| Chief Sustainability Officer | `\bchief\s+(\w+\s+){0,3}?sustainability\s+(\w+\s+){0,3}?officer\b` |
| Chief Diversity Officer | `\bchief\s+(\w+\s+|\w+,\s+){0,4}?(diversity|inclusion)(\s|,|&|and|\w)*?\s+officer\b` |
| Wider DEI lead (chief / VP / head of diversity, DEI, inclusion) | `\b(chief|vice\s+president|vp|svp|evp|head|global\s+head)\b[^|]{0,40}?\b(diversity|dei|d&i|inclusion)\b` |
| Chief Customer Officer | `\bchief\s+(\w+\s+){0,3}?customer\s+(\w+\s+){0,3}?officer\b` |
| Chief Growth Officer | `\bchief\s+(\w+\s+){0,3}?growth\s+(\w+\s+){0,3}?officer\b` |
| Chief Marketing Officer | `\bchief\s+(\w+\s+){0,3}?marketing\s+(\w+\s+){0,3}?officer\b` |
| Chief Technology Officer | `\bchief\s+(\w+\s+){0,3}?technolog(y|ies)\s+(\w+\s+){0,3}?officer\b|\bchief\s+technical\s+officer\b` |
| Chief Financial Officer | `\bchief\s+(\w+\s+){0,3}?financial\s+(\w+\s+){0,3}?officer\b` |

The wider DEI regex is counted separately from Chief Diversity Officer and is the
looser of the two: it takes a chief, VP, SVP, EVP or head whose title mentions
diversity, DEI, D&I or inclusion. It exists because the figure in circulation for
diversity leadership counts roles that are not chiefs.

## Exclusions

A row whose title matches this is dropped before any title is counted:

```
\b(assistant|assistants|asst|deputy|former|ex|aspiring|future|retired|outgoing|interim\s+assistant)\b|\boffice\s+of\s+the\b|\bto\s+the\b|\bto\s+(chief|the\s+chief|ciso|chro|cro|coo|cfo|cto|cmo)\b|\bchief\s+of\s+staff\b|\bexecutive\s+(assistant|business\s+partner)\b|\breports?\s+to\b|\bsearching\s+for\b|\bopen\s+to\b|\blooking\s+for\b
```

That removes assistants and executive assistants, deputies, chiefs of staff,
"former", "ex", "aspiring", "retired" and "outgoing" holders, "office of the"
constructions, and "to the" and "to chief" constructions such as "Executive
Assistant to the Chief Revenue Officer". Divisional titles are NOT excluded: a
divisional chief is counted as a match, and the title text is published in the
roster file so a reader can see which ones are divisional.

## Depth bands, and why they exist

The median Fortune 500 company in this data has 10,123 person rows.
The median Inc 5000 company has 37. A company observed through
37 people can lack a matched title purely because the holder is
not one of those 37 rows. Raw shares across the two lists
therefore measure observation depth as much as they measure title adoption, and
`title_shares_raw.csv` is published only so the reader can see that trap.

Three bands restrict both sides to companies with comparable observation depth:

- 500 to 5,000 rows: Fortune 500 n=117, Inc 5000 n=208
- 1,000 or more rows: Fortune 500 n=429, Inc 5000 n=100
- 100 to 500 rows: Fortune 500 n=16, Inc 5000 n=924

Every band is a half-open interval on the person row count: "500 to 5,000 rows"
is [500, 5,000), "1,000 or more rows" is [1,000, inf) and "100 to 500 rows" is
[100, 500). The n values above are computed on those intervals.

The first band is the one with the most companies on both sides and is the band
the report leads with. Bands overlap by construction: "1,000 or more rows" shares
companies with "500 to 5,000 rows".

## The percentile convention

p10 and p90 in `coverage.csv` use the nearest-rank convention on the sorted list
of per company row counts, zero indexed: index = min(len - 1, int(p * len)).
For the Fortune 500, with 483 covered companies, p10 is the value at index 48.

## The coverage denominator

A share is always companies with the title over companies in that list with any
person rows, never over the nominal 500 or 4,969. Companies with no rows at all
are not counted as companies without the title.

## Funnel

| | Fortune 500 | Inc 5000 |
|---|---|---|
| Domains in list | 500 | 4,969 |
| Companies with person rows | 483 | 4,469 |
| Person rows scanned | 8,554,225 | 661,586 |
| Rows matching the broad regex | 7,727 | 3,446 |
| Dropped by the exclusion regex | 638 | 56 |
| Classified into a published title | 5,829 | 3,337 |
| Matched the broad regex, no published title | 1,258 | 53 |

## Hand review

Every Chief Sustainability Officer and Chief Diversity Officer match on both lists
was read by hand, because both cells are under 100 rows. The full reviewed list,
including the rows judged false positives and the reason for each, is in the
repository at `data/reports/list_titles_reviewed.csv`. Five Chief Revenue Officer
matches per list were read the same way as examples; the rest of the Chief Revenue
Officer cell is regex output that was not individually verified.

## Small cells

A count of 1 to 4 prints as `<5`. Zero prints as zero, because an absence is the
result. Named rosters are exempt: a roster of holders names them.

## Limitations

1. Titles are self reported on a professional network profile. This is not a
   company filing, a proxy statement or an org chart.
2. The person rows carry a `fetched_at` stamp for when the row was collected, not
   an "as of" date for the title. A person who left is still in the data and a
   recent hire may be missing. Every figure is "as observed in the September 2026
   snapshot" and no time series can be built from it.
3. Coverage is not 500 and not 4,969. It is
   483 and 4,469, and that is the
   denominator everywhere.
4. Observation depth differs by a factor of roughly
   273 at the median between the
   two lists. Cross list shares are only meaningful inside a band.
5. An absence is an absence in this data, not in the world. Zero Inc 5000
   companies with a Chief Sustainability Officer means no person in these rows
   holds that title at those companies.
6. The Inc 5000 selects companies on three year revenue growth, which is a
   selection on exactly the trait that produces a revenue chief. The two lists
   also differ in sector mix, so `cro_by_industry.csv` is published beside the
   headline.
7. Regex output beyond the hand reviewed cells is not individually verified.
   Divisional titles inflate counts and are visible in the roster file.
8. One person can match several titles, so title columns do not sum to the number
   of people.
