SPB Git forge

spb/cancerindex

Public
37commits 1branches 0releases
2.9 MBsize
maindefault branch
10 days agolast push
TypeScript 97.2% SQL 1.5% CSS 0.6% JavaScript 0.5%
19.1 KB · 408 lines typescript
Raw Blame History
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