spb/cancerindex
Public
TypeScript 97.2%
SQL 1.5%
CSS 0.6%
JavaScript 0.5%
1/**2 * Metric catalog (CLAUDE.md §250-252, §352). Every derived number shown on the site references3 * one of these definitions by slug; the formula text and version are displayed on /methodology.4 *5 * Composite scores (CI-IMPACT etc.) are intentionally absent from Phase 1 (§353).6 */7export interface MetricSeed {8 slug: string;9 name: string;10 description: string;11 formula: string;12 formulaVersion: string;13 unit: string;14 higherIsWorse: boolean | null;15 aggregation: string | null;16 validDimensions: string[];17 sourceSlugs: string[];18 category: string;19 eligibility: Record<string, unknown>;20 experimental: boolean;21}2223export const METRIC_CATALOG: MetricSeed[] = [24 // --- Burden (epidemiology observations) ---25 {26 slug: 'incidence_count',27 name: 'Annual new cases',28 description: 'Number of new cases diagnosed in the reference year for the population scope. Registry counts are observed; national/global figures may be estimates and are labeled as such.',29 formula: 'epidemiology_observations.value WHERE metric = incidence_count',30 formulaVersion: 'ci-incidence-count-v1',31 unit: 'count',32 higherIsWorse: true,33 aggregation: 'none',34 validDimensions: ['geography', 'sex', 'age_group', 'year'],35 sourceSlugs: ['cdc-wonder', 'seer', 'iarc-globocan'],36 category: 'burden',37 eligibility: { requires: ['incidence_count'], entityLevel: 'top' },38 experimental: false,39 },40 {41 slug: 'mortality_count',42 name: 'Annual deaths',43 description: 'Number of deaths with this cancer as underlying cause in the reference year for the population scope.',44 formula: 'epidemiology_observations.value WHERE metric = mortality_count',45 formulaVersion: 'ci-mortality-count-v1',46 unit: 'count',47 higherIsWorse: true,48 aggregation: 'none',49 validDimensions: ['geography', 'sex', 'age_group', 'year'],50 sourceSlugs: ['cdc-wonder', 'seer', 'iarc-globocan'],51 category: 'burden',52 eligibility: { requires: ['mortality_count'], entityLevel: 'top' },53 experimental: false,54 },55 {56 slug: 'as_incidence_rate',57 name: 'Age-standardized incidence rate',58 description: 'Incidence per 100,000 person-years, age-standardized to the standard population declared by the source. Rates standardized to different standard populations are never compared in one ranking.',59 formula: 'epidemiology_observations.value WHERE metric = as_incidence_rate (standard_population fixed per scope)',60 formulaVersion: 'ci-asir-v1',61 unit: 'per_100k',62 higherIsWorse: true,63 aggregation: 'none',64 validDimensions: ['geography', 'sex', 'year'],65 sourceSlugs: ['cdc-wonder', 'seer', 'iarc-globocan'],66 category: 'burden',67 eligibility: { requires: ['as_incidence_rate'], entityLevel: 'top' },68 experimental: false,69 },70 {71 slug: 'as_mortality_rate',72 name: 'Age-standardized mortality rate',73 description: 'Deaths per 100,000 person-years, age-standardized to the standard population declared by the source.',74 formula: 'epidemiology_observations.value WHERE metric = as_mortality_rate (standard_population fixed per scope)',75 formulaVersion: 'ci-asmr-v1',76 unit: 'per_100k',77 higherIsWorse: true,78 aggregation: 'none',79 validDimensions: ['geography', 'sex', 'year'],80 sourceSlugs: ['cdc-wonder', 'seer', 'iarc-globocan'],81 category: 'burden',82 eligibility: { requires: ['as_mortality_rate'], entityLevel: 'top' },83 experimental: false,84 },85 // --- Lethality ---86 {87 slug: 'mortality_incidence_ratio',88 name: 'Mortality-to-incidence ratio',89 description: 'Deaths divided by new cases in the same population, year and sex. A crude proxy of lethality; it is not a survival probability and is affected by incidence trends and registration completeness.',90 formula: 'mortality_count / incidence_count (same geography, year, sex, source family)',91 formulaVersion: 'ci-mir-v1',92 unit: 'ratio',93 higherIsWorse: true,94 aggregation: 'none',95 validDimensions: ['geography', 'sex', 'year'],96 sourceSlugs: ['cdc-wonder', 'seer', 'iarc-globocan'],97 category: 'lethality',98 eligibility: { requires: ['incidence_count', 'mortality_count'], minIncidence: 100, entityLevel: 'top' },99 experimental: false,100 },101 {102 slug: 'five_year_survival',103 name: '5-year relative survival',104 description: '5-year relative survival for the diagnosis period and population declared by the source. Population survival statistics do not predict an individual outcome.',105 formula: 'survival_observations.probability WHERE survival_type = relative AND duration_months = 60 AND stage IS NULL',106 formulaVersion: 'ci-5ys-v1',107 unit: 'probability',108 higherIsWorse: false,109 aggregation: 'none',110 validDimensions: ['geography', 'sex', 'diagnosis_period'],111 sourceSlugs: ['seer'],112 category: 'lethality',113 eligibility: { requires: ['five_year_relative_survival'], minCohort: 50, entityLevel: 'top' },114 experimental: false,115 },116 // --- Clinical research (ClinicalTrials.gov) ---117 {118 slug: 'active_trials',119 name: 'Active clinical trials',120 description: 'Interventional studies registered on ClinicalTrials.gov whose overall status is RECRUITING, NOT_YET_RECRUITING, ENROLLING_BY_INVITATION or ACTIVE_NOT_RECRUITING and whose conditions map to this cancer or one of its descendants in the NCIt hierarchy.',121 formula: 'COUNT(DISTINCT trial) FROM trial_conditions JOIN clinical_trials WHERE cancer_id IN descendants(cancer) AND study_type = INTERVENTIONAL AND overall_status IN (RECRUITING, NOT_YET_RECRUITING, ENROLLING_BY_INVITATION, ACTIVE_NOT_RECRUITING)',122 formulaVersion: 'ci-active-trials-v1',123 unit: 'count',124 higherIsWorse: false,125 aggregation: 'descendants',126 validDimensions: ['entity_level'],127 sourceSlugs: ['clinicaltrials'],128 category: 'trials',129 eligibility: { entityLevel: 'any' },130 experimental: false,131 },132 {133 slug: 'recruiting_trials',134 name: 'Recruiting clinical trials',135 description: 'Interventional studies currently RECRUITING whose conditions map to this cancer or its descendants.',136 formula: 'COUNT(DISTINCT trial) … overall_status = RECRUITING AND study_type = INTERVENTIONAL',137 formulaVersion: 'ci-recruiting-trials-v1',138 unit: 'count',139 higherIsWorse: false,140 aggregation: 'descendants',141 validDimensions: ['entity_level'],142 sourceSlugs: ['clinicaltrials'],143 category: 'trials',144 eligibility: { entityLevel: 'any' },145 experimental: false,146 },147 {148 slug: 'phase3_trials',149 name: 'Active Phase III trials',150 description: 'Active interventional studies with PHASE3 among their phases, mapped to this cancer or its descendants.',151 formula: 'COUNT(DISTINCT trial) … active statuses AND PHASE3 = ANY(phases)',152 formulaVersion: 'ci-phase3-trials-v1',153 unit: 'count',154 higherIsWorse: false,155 aggregation: 'descendants',156 validDimensions: ['entity_level'],157 sourceSlugs: ['clinicaltrials'],158 category: 'trials',159 eligibility: { entityLevel: 'any' },160 experimental: false,161 },162 // --- Research activity (PubMed) ---163 {164 slug: 'publications_5y',165 name: 'Publications, last 5 years',166 description: 'PubMed records matching the cancer’s MeSH-anchored query (stored verbatim with the count) with a publication date in the last 5 full years plus the current year.',167 formula: 'literature_counts.count WHERE window_key = 5y (query stored per row)',168 formulaVersion: 'ci-pubs-5y-v1',169 unit: 'count',170 higherIsWorse: false,171 aggregation: 'none',172 validDimensions: ['entity_level'],173 sourceSlugs: ['pubmed'],174 category: 'research',175 eligibility: { entityLevel: 'any', requiresQuery: true },176 experimental: false,177 },178 {179 slug: 'publications_12m',180 name: 'Publications, last 12 months',181 description: 'PubMed records matching the cancer’s query with a publication date in the last 12 months.',182 formula: 'literature_counts.count WHERE window_key = 12m',183 formulaVersion: 'ci-pubs-12m-v1',184 unit: 'count',185 higherIsWorse: false,186 aggregation: 'none',187 validDimensions: ['entity_level'],188 sourceSlugs: ['pubmed'],189 category: 'research',190 eligibility: { entityLevel: 'any', requiresQuery: true },191 experimental: false,192 },193 {194 slug: 'publication_growth',195 name: 'Publication growth',196 description: 'Ratio of publications in the last 12 months to the average annual publications over the preceding 5-year window. Values above 1 indicate accelerating literature.',197 formula: 'publications_12m / (publications_5y_prior / 5)',198 formulaVersion: 'ci-pub-growth-v1',199 unit: 'ratio',200 higherIsWorse: null,201 aggregation: 'none',202 validDimensions: ['entity_level'],203 sourceSlugs: ['pubmed'],204 category: 'trend',205 eligibility: { entityLevel: 'any', minPublications5y: 50 },206 experimental: false,207 },208 // --- Molecular knowledge ---209 {210 slug: 'curated_evidence_items',211 name: 'Curated clinical evidence items',212 description: 'Accepted CIViC evidence items whose disease maps to this cancer or its descendants.',213 formula: 'COUNT(civic_evidence_items) WHERE status = ACCEPTED AND cancer_id IN descendants(cancer)',214 formulaVersion: 'ci-civic-evidence-v1',215 unit: 'count',216 higherIsWorse: false,217 aggregation: 'descendants',218 validDimensions: ['entity_level'],219 sourceSlugs: ['civic'],220 category: 'molecular',221 eligibility: { entityLevel: 'any' },222 experimental: false,223 },224 {225 slug: 'associated_genes',226 name: 'Genes with curated or cohort evidence',227 description: 'Distinct genes linked to this cancer (or descendants) by an accepted CIViC evidence item or by a GDC cohort frequency of at least 5% with ≥ 20 affected cases.',228 formula: 'COUNT(DISTINCT gene) FROM (civic accepted evidence ∪ gdc frequency ≥ 0.05 AND cases_affected ≥ 20)',229 formulaVersion: 'ci-genes-v1',230 unit: 'count',231 higherIsWorse: false,232 aggregation: 'descendants',233 validDimensions: ['entity_level'],234 sourceSlugs: ['civic', 'gdc'],235 category: 'molecular',236 eligibility: { entityLevel: 'any' },237 experimental: false,238 },239 {240 slug: 'genomic_cohorts',241 name: 'Public genomic cohorts',242 description: 'Open-access genomic studies (GDC projects) whose disease maps to this cancer or its descendants.',243 formula: 'COUNT(genomic_cohorts) WHERE cancer_id IN descendants(cancer)',244 formulaVersion: 'ci-cohorts-v1',245 unit: 'count',246 higherIsWorse: false,247 aggregation: 'descendants',248 validDimensions: ['entity_level'],249 sourceSlugs: ['gdc'],250 category: 'molecular',251 eligibility: { entityLevel: 'any' },252 experimental: false,253 },254 // --- Gap indexes (derived; require burden) ---255 {256 slug: 'trial_gap',257 name: 'Trial Gap Index',258 description: 'Burden percentile minus active-trial percentile within the same scope. Positive values flag cancers with high mortality burden but comparatively few active trials. A quantitative signal, not an accusation (CLAUDE.md §324). Caveat (Phase 1): trial counts aggregate a cancer and its NCIt descendants only; registrations phrased at a broader level (e.g. "colorectal cancer" for colon and rectal cancer) are attributed to the broader entity, which can overstate the gap of narrower top-level sites.',259 formula: 'percentile(mortality_count) − percentile(active_trials)',260 formulaVersion: 'ci-trial-gap-v1',261 unit: 'percentile_points',262 higherIsWorse: true,263 aggregation: 'none',264 validDimensions: ['geography', 'year'],265 sourceSlugs: ['clinicaltrials', 'cdc-wonder', 'seer', 'iarc-globocan'],266 category: 'unmet_need',267 eligibility: { requires: ['mortality_count', 'active_trials'], entityLevel: 'top' },268 experimental: false,269 },270 {271 slug: 'research_gap',272 name: 'Research Gap Index',273 description: 'Burden percentile minus research-activity percentile (publications, last 5 years) within the same scope. Positive values flag cancers with high mortality burden but comparatively little literature. Caveat (Phase 1): literature counts are query-based per entity (query stored on each row); they are not aggregated over descendants.',274 formula: 'percentile(mortality_count) − percentile(publications_5y)',275 formulaVersion: 'ci-research-gap-v1',276 unit: 'percentile_points',277 higherIsWorse: true,278 aggregation: 'none',279 validDimensions: ['geography', 'year'],280 sourceSlugs: ['pubmed', 'cdc-wonder', 'seer', 'iarc-globocan'],281 category: 'unmet_need',282 eligibility: { requires: ['mortality_count', 'publications_5y'], entityLevel: 'top' },283 experimental: false,284 },285286 // --- Clinical-trial intelligence (SPEC §10; computed in packages/ranking/src/trial-intelligence.ts) ---287 {288 slug: 'phase3_recruiting_trials',289 name: 'Recruiting Phase III trials',290 description: 'Interventional studies with PHASE3 among their phases and overall status RECRUITING, mapped to this cancer or its descendants.',291 formula: 'COUNT(DISTINCT trial) … overall_status = RECRUITING AND PHASE3 = ANY(phases) AND study_type = INTERVENTIONAL',292 formulaVersion: 'ci-phase3-recruiting-v1',293 unit: 'count',294 higherIsWorse: false,295 aggregation: 'descendants',296 validDimensions: ['entity_level'],297 sourceSlugs: ['clinicaltrials'],298 category: 'trials',299 eligibility: { entityLevel: 'any' },300 experimental: false,301 },302 {303 slug: 'trial_growth_yoy',304 name: 'Trial registration growth (year over year)',305 description: 'Relative change in the number of interventional studies first posted in the last 12 months versus the preceding 12 months, over the cancer and its descendants. Requires at least 20 studies in the prior window so small denominators do not produce spurious growth.',306 formula: '(new_trials_12m − new_trials_prior_12m) / new_trials_prior_12m (first_posted_date windows; interventional; descendants)',307 formulaVersion: 'ci-trial-growth-v1',308 unit: 'ratio',309 higherIsWorse: null,310 aggregation: 'descendants',311 validDimensions: ['entity_level'],312 sourceSlugs: ['clinicaltrials'],313 category: 'trend',314 eligibility: { entityLevel: 'any', minPriorTrials: 20 },315 experimental: false,316 },317 {318 slug: 'trial_termination_share',319 name: 'Trial termination share',320 description: 'Share of interventional studies first posted since 2010 that ended TERMINATED or WITHDRAWN among those that reached a terminal status (COMPLETED, TERMINATED, WITHDRAWN). Registrant-reported statuses; reasons are shown as posted and never inferred. Requires ≥ 30 terminal studies.',321 formula: '(terminated + withdrawn) / (completed + terminated + withdrawn) WHERE first_posted_date ≥ 2010-01-01 AND study_type = INTERVENTIONAL',322 formulaVersion: 'ci-trial-termination-v1',323 unit: 'ratio',324 higherIsWorse: true,325 aggregation: 'descendants',326 validDimensions: ['entity_level'],327 sourceSlugs: ['clinicaltrials'],328 category: 'trials',329 eligibility: { entityLevel: 'any', minTerminalTrials: 30 },330 experimental: false,331 },332 {333 slug: 'sponsor_concentration',334 name: 'Sponsor concentration (HHI)',335 description: 'Herfindahl–Hirschman index of lead sponsors over active interventional studies (sum of squared sponsor shares; 1 = a single sponsor). Requires ≥ 10 active studies.',336 formula: 'Σ (active_trials_by_lead_sponsor / active_trials)² over the cancer and its descendants',337 formulaVersion: 'ci-sponsor-hhi-v1',338 unit: 'index',339 higherIsWorse: null,340 aggregation: 'descendants',341 validDimensions: ['entity_level'],342 sourceSlugs: ['clinicaltrials'],343 category: 'trials',344 eligibility: { entityLevel: 'any', minActiveTrials: 10 },345 experimental: false,346 },347 {348 slug: 'trials_per_1000_deaths',349 name: 'Active trials per 1,000 annual deaths',350 description: 'Active interventional studies divided by annual deaths (thousands) in the same population scope. A burden-normalized measure of clinical-research intensity; it depends on the epidemiology source of the scope and on how registrants phrase conditions.',351 formula: 'active_trials / (mortality_count / 1000) (same geography, year, sex, source family; deaths ≥ 100)',352 formulaVersion: 'ci-trials-per-deaths-v1',353 unit: 'per_1000_deaths',354 higherIsWorse: false,355 aggregation: 'none',356 validDimensions: ['geography', 'year'],357 sourceSlugs: ['clinicaltrials', 'cdc-wonder', 'cdc-uscs', 'seer', 'iarc-globocan'],358 category: 'unmet_need',359 eligibility: { requires: ['mortality_count', 'active_trials'], minDeaths: 100, entityLevel: 'top' },360 experimental: false,361 },362 {363 slug: 'publications_per_1000_deaths',364 name: 'Publications (5 y) per 1,000 annual deaths',365 description: 'PubMed records over the last 5 years divided by annual deaths (thousands) in the same population scope. A burden-normalized measure of research output.',366 formula: 'publications_5y / (mortality_count / 1000) (same geography, year, sex, source family; deaths ≥ 100)',367 formulaVersion: 'ci-pubs-per-deaths-v1',368 unit: 'per_1000_deaths',369 higherIsWorse: false,370 aggregation: 'none',371 validDimensions: ['geography', 'year'],372 sourceSlugs: ['pubmed', 'cdc-wonder', 'cdc-uscs', 'seer', 'iarc-globocan'],373 category: 'unmet_need',374 eligibility: { requires: ['mortality_count', 'publications_5y'], minDeaths: 100, entityLevel: 'top' },375 experimental: false,376 },377 {378 slug: 'trial_gap_ratio',379 name: 'Trial Gap Ratio',380 description: 'log₂ of a cancer’s share of deaths divided by its share of active interventional trials, both computed over the eligible top-level cancers of the scope. 0 = trial share matches death share; +1 = twice as many deaths as trials would suggest; −1 = half. Scale-free, so it can be compared across scopes, unlike the percentile-based Trial Gap Index.',381 formula: 'log2( (deaths / Σ deaths) / (active_trials / Σ active_trials) ) over eligible top-level cancers (deaths ≥ 100, active_trials ≥ 1)',382 formulaVersion: 'ci-trial-gap-ratio-v1',383 unit: 'log2_ratio',384 higherIsWorse: true,385 aggregation: 'none',386 validDimensions: ['geography', 'year'],387 sourceSlugs: ['clinicaltrials', 'cdc-wonder', 'cdc-uscs', 'seer', 'iarc-globocan'],388 category: 'unmet_need',389 eligibility: { requires: ['mortality_count', 'active_trials'], minDeaths: 100, entityLevel: 'top' },390 experimental: false,391 },392 {393 slug: 'research_gap_ratio',394 name: 'Research Gap Ratio',395 description: 'log₂ of a cancer’s share of deaths divided by its share of publications (last 5 years), both over the eligible top-level cancers of the scope. 0 = literature share matches death share; positive values flag comparatively under-published cancers. Caveat: literature counts are query-based per entity and not aggregated over descendants.',396 formula: 'log2( (deaths / Σ deaths) / (publications_5y / Σ publications_5y) ) over eligible top-level cancers (deaths ≥ 100, publications_5y ≥ 1)',397 formulaVersion: 'ci-research-gap-ratio-v1',398 unit: 'log2_ratio',399 higherIsWorse: true,400 aggregation: 'none',401 validDimensions: ['geography', 'year'],402 sourceSlugs: ['pubmed', 'cdc-wonder', 'cdc-uscs', 'seer', 'iarc-globocan'],403 category: 'unmet_need',404 eligibility: { requires: ['mortality_count', 'publications_5y'], minDeaths: 100, entityLevel: 'top' },405 experimental: false,406 },407];408