# KODEBOK – Database over studier av rødt kjøtt og tarmkreft

Versjon 1.0, 30.10.2026. Skrevet av Fable (design og regler); uttrekk ved Opus-agenter; kontroll ved uavhengige Opus-instanser.

## 1. Formål og prinsipper

Databasen samler **hvert publisert estimat** fra kohortstudiene som ligger under WCRF/CUPs vurdering av rødt kjøtt og tykk- og endetarmskreft, pluss oppfølgingene og de nyere kohortene prosjektet har gjennomgått. Formålet er tre ting:

1. **Reprodusere** CUP 2016s samleestimater (kolorektal 1,12; kolon 1,22; rektum 1,13 per 100 g/dag) fra rådata, ikke bare fra CUPs ferdige per-100 g-tall.
2. **Gjøre valgpunktene synlige**: for hver studie finnes flere estimater (modeller, kjønn, delseter, eksponeringsdefinisjoner). Databasen registrerer alle, og markerer hvilket CUP brukte og hvilket CUPs egne regler tilsier.
3. **Multiverse**: regne ut hva samleestimatet blir under alle rimelige kombinasjoner av valg.

Fem prinsipper styrer kodingen:

- **Én rad per estimat, ikke per studie.** Alt som er rapportert med punktestimat og konfidensintervall får sin egen rad, også referansekategorier (RR = 1,00).
- **Tall som publisert.** Ingen avrunding, ingen omregning i `estimates`-tabellen. Omregninger (per 100 g, porsjon → gram) skjer i egne kolonner eller i skript, aldri ved å overskrive det publiserte.
- **Kilde på hver rad.** Tabell/figur og side i artikkelen. Sitater under 15 ord.
- **Kohort ≠ publikasjon.** Samme kohort kan ha flere publikasjoner. Hver samleanalyse må velge én rad per kohort etter en uttalt regel.
- **Det vi ikke vet, kodes som ukjent**, aldri som «nei». Kontrollert vokabular har alltid verdien `unclear`.

## 2. Filstruktur

```
database/
  KODEBOK.md              denne filen
  studies.csv             én rad per publikasjon
  cohorts.csv             én rad per kohort (for dobbelttelling og versjoner)
  estimates.csv           én rad per publisert estimat  ← kjernen
  adjustments.csv         én rad per justeringssett (modell) per studie
  cup_extraction.csv      hva CUP/SLR trakk ut, og avvik fra artikkelen
  dose_conversions.csv    hvordan porsjoner/kvantiler er gjort om til g/dag
  followups.csv           kobling tidlig → senere publikasjon fra samme kohort
  scripts/
    checks.py             automatiske sjekker (kjøres før hver bruk)
    gl_doseresponse.py    Greenland–Longnecker per studie
    pool.py               DerSimonian–Laird og scenarier
  kontroll/
    dobbeltkoding_*.csv   uavhengig andregangs uttrekk
    kontrollrapport.md
```

Alle CSV-er: UTF-8, komma som skilletegn, punktum som desimaltegn, tomme felt for manglende verdier (ikke «NA», ikke 0). Boolske felt: `yes` / `no` / `unclear`.

## 3. Tabellene

### 3.1 `studies.csv` – én rad per publikasjon

| Felt | Type | Innhold |
|---|---|---|
| `study_id` | tekst, nøkkel | `<førsteforfatter><år>` i små bokstaver, f.eks. `norat2005`. Ved to samme år: `larsson2005a`. |
| `cohort_id` | tekst | peker til `cohorts.csv` |
| `first_author`, `year`, `journal`, `doi`, `pmid` | | som publisert |
| `design` | vokabular | `cohort`, `nested_cc` (nested case-control i kohort), `case_cohort`, `pooled_cohorts`, `meta_analysis` |
| `country`, `region` | | `region` ∈ `europe`, `north_america`, `asia`, `oceania`, `multi` |
| `baseline_start`, `baseline_end` | år | rekrutteringsår |
| `followup_years` | tall | gjennomsnitt eller median, slik artikkelen oppgir; `followup_type` ∈ `mean`, `median`, `max`, `unclear` |
| `n_participants` | heltall | analysepopulasjon for kjøttanalysen (ikke hele kohorten) |
| `sex` | | `all`, `men`, `women` |
| `age_range` | tekst | ved baseline |
| `diet_instrument` | vokabular | `ffq`, `diet_history`, `interview`, `24h_recall`, `mixed` |
| `diet_items` | heltall | antall matvarer i instrumentet |
| `diet_repeated` | bool | gjentatte kostmålinger brukt i analysen? |
| `validation_r` | tall | korrelasjon for kjøtt mot referansemetode, hvis oppgitt |
| `outcome_ascertainment` | vokabular | `registry`, `self_report_verified`, `self_report`, `active_followup`, `mixed` |
| `red_meat_definition` | tekst | ordrett liste fra artikkelen (< 15 ord, ellers parafrase) |
| `includes_processed` | bool | inneholder «rødt kjøtt»-variabelen prosesserte produkter? `partial` tillatt |
| `weight_basis` | vokabular | `raw`, `cooked`, `unclear` |
| `fulltext_file` | tekst | sti til .txt/.pdf i Drive |
| `card_file` | tekst | sti til studiekortet |
| `cases_colorectal`, `cases_colon`, `cases_rectum` | heltall | artikkelens totale tilfeller per utfall i kjøttanalysen (brukes av sjekk 5) |
| `in_cup2016` | bool | inngår i CUP 2016 SLR |

Tilleggsverdier tatt i bruk under uttrekket (listet øverst i hvert NOTES.md og samlet i `database/NOTES_all.md`): `diet_instrument` ∈ … + `food_record`; `dose_unit` + `per_20g`, `per_30g`, `per_70g`, `per_50g_per_1000kcal`, `ug_per_1000kcal_day`, `ng_per_1000kcal_day`, `per_100g_week`, `per_50g_week`, `per_200g_week`, `ever_never`, `ratio`; `contrast_type` som før.
| `cup_role` | vokabular | `dose_response`, `hvl_only` (bare høyest-mot-lavest), `excluded`, `post_2016` |
| `notes` | tekst | |

### 3.2 `cohorts.csv`

| Felt | Innhold |
|---|---|
| `cohort_id` | kort navn, f.eks. `epic`, `nhs`, `hpfs`, `mec`, `jphc`, `takayama`, `smc`, `nih_aarp`, `ukbiobank`, `atbc`, `fmches`, `iwhs`, `ahs`, `swhs`, `mccs`, `monica_nl` |
| `cohort_name` | fullt navn |
| `country` | |
| `parent_cohort_id` | hvis delkohort (EPIC-Norfolk → `epic`) |
| `overlaps_with` | semikolonseparert liste over kohorter med overlappende deltakere |
| `notes` | |

Regel: to publikasjoner med samme `cohort_id` (eller der den ene er `parent` av den andre) **kan aldri stå i samme samleanalyse**.

### 3.3 `estimates.csv` – kjernen

| Felt | Type | Innhold |
|---|---|---|
| `estimate_id` | nøkkel | `<study_id>_<løpenummer>`, f.eks. `norat2005_014` |
| `study_id`, `cohort_id` | | |
| `exposure` | vokabular | se 4.1 |
| `exposure_text` | tekst | artikkelens egen betegnelse |
| `exposure_includes_processed` | bool | for akkurat denne variabelen |
| `outcome` | vokabular | `colorectal`, `colon`, `rectum`, `proximal_colon`, `distal_colon`, `distal_colorectum` (distal kolon + rektum), `colon_excl_rectum` |
| `outcome_type` | | `incidence`, `mortality` |
| `sex` | | `all`, `men`, `women` |
| `subgroup` | tekst | tom for hovedanalyse; ellers f.eks. `never_smokers`, `age_lt_60`, `country_italy` |
| `contrast_type` | vokabular | `category` (én kategori mot referanse), `continuous` (per enhet), `hvl` (høyest mot lavest, slik artikkelen rapporterer det), `substitution` |
| `category_set_id` | tekst | binder kategoriene i samme analyse sammen: `<study_id>_<modell>_<outcome>_<sex>_<exposure>` (eksponering alltid med, siden flere variabler deler modell) |
| `category_rank` | heltall | 1 = referanse/laveste, 2, 3 … |
| `category_label` | tekst | som i artikkelen, f.eks. `Q4 (≥ 160 g/d)`, `≥ 5 ganger/uke` |
| `dose_low`, `dose_high` | tall | grensene i artikkelens enhet; tom hvis åpen |
| `dose_point` | tall | median/gjennomsnitt i kategorien hvis artikkelen oppgir det |
| `dose_unit` | vokabular | `g_day`, `g_per_1000kcal_day` (energitetthet, MEC), `servings_week`, `servings_day`, `servings_month`, `times_week`, `times_month`, `g_week`, `per_sd`, `per_10g`, `per_25g`, `per_50g`, `per_100g`, `per_serving`, `per_serving_week` |
| `dose_point_source` | vokabular | `paper_median`, `paper_mean`, `paper_midpoint`, `none` |
| `n_cases` | heltall | i kategorien |
| `n_noncases` / `person_years` / `n_in_category` | | det artikkelen oppgir; feltet `denominator_type` ∈ `person_years`, `n_participants`, `n_controls`, `none` |
| `effect_measure` | | `HR`, `RR`, `OR`, `IRR`, `SIR` |
| `point`, `ci_low`, `ci_high` | tall | som publisert, alle desimaler |
| `ci_level` | | 95 som standard |
| `p_value`, `p_trend` | tekst | som publisert (`0.03`, `<0.001`, `0.32`); `p_trend` bare på raden med høyeste kategori eller på continuous-raden |
| `model_label` | tekst | artikkelens navn: `Model 2`, `multivariable`, `age-adjusted` |
| `adjustment_set_id` | | peker til `adjustments.csv` |
| `is_main_model` | bool | artikkelens mest justerte hovedmodell for dette utfallet (ikke modeller med mediatorer som BMI lagt til i sensitivitet) |
| `is_calibrated` | bool | målefeilkorrigert (EPIC) |
| `is_mutually_adjusted_processed` | bool | justert for prosessert kjøtt |
| `source_table` | tekst | `Table 3`, `Fig 2`, `Suppl Table S4` |
| `source_page` | tekst | sidetall i tidsskriftet; `ms p. 12` hvis bare manus |
| `verbatim` | tekst | ≤ 15 ord som identifiserer raden |
| `extractor` | | `opus-A`, `opus-B`, `fable` |
| `extraction_date` | | ISO |
| `verified_by`, `verified_date`, `verification_status` | | `unverified`, `verified`, `corrected`, `disputed` |
| `notes` | | |

**Hva som skal trekkes ut (R1):** alle estimater for rødt kjøtt (alle definisjoner), prosessert kjøtt, totalt kjøtt, fjørfe og fisk mot tarmkreftutfall, i alle modeller, kjønn og delseter artikkelen rapporterer, inkludert supplement. Fjørfe og fisk er med som **negative kontroller**. Substitusjonsanalyser tas med. Ikke med: andre utfall (CVD, T2D – egne spor senere), adenomer, polypper.

### 3.4 `adjustments.csv`

| Felt | Innhold |
|---|---|
| `adjustment_set_id` | `<study_id>_m<nr>` |
| `study_id`, `model_label` | |
| `covariates_text` | ordrett liste fra artikkelen (fotnote) |
| Boolske flagg: `adj_age`, `adj_sex`, `adj_energy`, `adj_bmi`, `adj_smoking`, `adj_alcohol`, `adj_physical_activity`, `adj_fiber`, `adj_calcium`, `adj_folate`, `adj_fruit_veg`, `adj_processed_meat`, `adj_poultry`, `adj_fish`, `adj_saturated_fat`, `adj_education`, `adj_family_history`, `adj_nsaid`, `adj_hrt`, `adj_screening`, `adj_diabetes`, `adj_stratified_center` | `yes`/`no`/`unclear` |
| `energy_method` | `covariate`, `residual`, `density`, `none`, `unclear` |
| `notes` | |

### 3.5 `cup_extraction.csv`

| Felt | Innhold |
|---|---|
| `cup_row_id` | `cup2016_<study_id>_<outcome>` |
| `cup_source` | `SLR2010`, `SLR2016`, `CUP2017`, `CUP2018rev` |
| `study_id`, `outcome` | |
| `cup_analysis` | `dose_response`, `hvl` |
| `cup_rr`, `cup_ci_low`, `cup_ci_high` | CUPs tall per 100 g/dag (eller HvL) |
| `cup_weight_pct` | fra forest plot |
| `cup_cases` | slik CUP teller |
| `cup_exposure_label` | CUPs betegnelse |
| `cup_contrast` | CUPs oppgitte kontrast (f.eks. «117 vs 20 g/day») |
| `cup_source_page` | SLR side / figur / CSV-rad |
| `estimate_ids_used` | semikolonseparerte `estimate_id` som CUP bygde på (kategoriraden(e) eller continuous-raden) |
| `rule_conform_estimate_ids` | de radene CUPs egne regler (SLR s. 335: uprosessert når tilgjengelig; artikkelens hovedmodell) tilsier |
| `issue_category` | semikolonseparert fra: `includes_processed`, `wrong_dose`, `wrong_case_count`, `observed_vs_calibrated`, `portion_assumption`, `row_choice`, `unadjusted_model_chosen`, `none` |
| `issue_text` | kort beskrivelse |
| `cases_correct` | artikkelens tall der CUP avviker |
| `verified_by` | |

### 3.6 `dose_conversions.csv`

| Felt | Innhold |
|---|---|
| `conversion_id` | |
| `study_id`, `category_set_id` | |
| `original_unit` | |
| `portion_g` | gram per porsjon som er antatt |
| `portion_source` | `paper`, `cup` (SLR/Chan 2011 S1), `us_assumed` |
| `midpoint_rule` | hvordan åpne kategorier er håndtert: `cup_rule` (= bredden til nabokategorien), `paper_median`, `us_rule` |
| `g_day_assigned` | kommaseparerte doser per kategori, i rekkefølgen `category_rank` |
| `cooked_raw_factor` | hvis omregning rå → tilberedt er gjort (standard 1,0; CUP bruker ingen) |
| `notes` | |

### 3.7 `followups.csv`

| Felt | Innhold |
|---|---|
| `cohort_id` | |
| `earlier_study_id`, `later_study_id` | |
| `earlier_cases`, `later_cases` | for hovedutfallet |
| `exposure_comparable` | bool – er «rødt kjøtt» definert likt? |
| `notes` | f.eks. «Islam 2019 isolerer uprosessert; Takachi 2011 gjorde det ikke» |

## 4. Kontrollerte vokabularer

### 4.1 `exposure`

| Verdi | Betyr |
|---|---|
| `red_unprocessed` | rødt kjøtt der prosessert er eksplisitt utelatt |
| `red_total` | rødt kjøtt inkl. prosesserte produkter av rødt kjøtt |
| `red_undefined` | «red meat» uten at artikkelen sier om prosessert er med |
| `red_main_dish` | «rødt kjøtt som hovedrett» (Wei, Bostick) |
| `processed` | prosessert kjøtt |
| `total_meat` | alt kjøtt |
| `beef`, `pork`, `lamb`, `poultry`, `fish`, `organ` | enkeltkjøttslag |
| `nitrate_processed_meat`, `nitrite_processed_meat`, `nitrate_nitrite_processed_meat`, `hca_phip`, `hca_meiqx`, `hca_dimeiqx` | kjøttkomponenter (Cross 2010, Etemadi 2018) – aldri i samleanalyser av kjøtt |
| `white_meat` | fjørfe + fisk, eller slik artikkelen definerer |
| `heme_iron` | beregnet hemjern (bare hvis artikkelen rapporterer det ved siden av kjøtt) |

### 4.2 Regler for `is_main_model`
Gjelder per **eksponering × utfall × kjønn**: artikkelens mest justerte hovedmodell for den kombinasjonen, **uten** (a) modeller som i tillegg justerer for potensielle mediatorer i en sensitivitetsanalyse (f.eks. mettet fett lagt til hos Takachi), og (b) kalibrerte modeller. Hvis artikkelen selv kaller én modell «multivariable» eller «fully adjusted», er det den. Hvis to modeller er likestilte, velges den som justerer for energi. En «sekundær» modell artikkelen selv rangerer lavere (f.eks. Larsson 2005s modell med røyking/HRT/aspirin fra 1997-skjemaet, bare rapportert som Q4 mot Q1) er `no`, og begrunnelsen står i NOTES.

### 4.2b Unntak for kalibrerte hovedrader
Når en artikkel bare publiserer målefeilkorrigerte trendestimater (Papier 2025, MWS), er de korrigerte radene `is_main_model = yes` og `is_calibrated = yes` samtidig. Samleanalyser som vil holde kalibrerte estimater ute, filtrerer på `is_calibrated`, ikke på `is_main_model`. Hamburger-regelen (R6): «uprosessert» som inkluderer hamburger/burger kodes `partial` (Bernstein, Papier, Zouiouich); Mehta følger samme regel for aggregatet.

### 4.3 Regler for dose (`dose_conversions`)
1. Bruk artikkelens medianer per kategori hvis de finnes.
2. Ellers midtpunkt i lukkede kategorier.
3. Åpne topp-/bunnkategorier: CUP-regelen (samme bredde som nabokategorien) **og** artikkelens tall hvis oppgitt; begge registreres.
4. Porsjoner → gram: artikkelens porsjonsstørrelse hvis oppgitt; ellers CUPs (Chan 2011 S1 / SLR); begge registreres, og hver omregning får `portion_source`.
5. Nevnere for Greenland–Longnecker: artikkelens person-år eller deltakere per kategori når de finnes. Når bare tilfeller per kategori er oppgitt, brukes like nevnere for kvantilbaserte kategorier (riktig per konstruksjon) og nevnere ∝ tilfeller/RR for kategorier med faste kuttpunkter (Norat-typen). Begge er tilnærminger og merkes i resultatet.
6. Pilot-erfaring (Norat 2005): GL fra kategoriene med CUP-regel-midtpunkter (toppkategori ≥80 g antatt like bred som nabokategorien, midtpunkt 100) gir 1,22 (0,99–1,52) mot artikkelens egen per-100 g 1,21 (1,02–1,43). Punktestimatet treffer; intervallet blir bredere fordi nevnerne per kategori ikke er oppgitt. (Pilotens første forsøk brukte feilaktig 120 som toppdose og fikk 1,17; feilen ble fanget av Opus-gjennomgangen av de håndkodede radene.) Derfor: når artikkelen selv oppgir et kontinuerlig estimat, er det alltid `estimate_ids_used`, og GL-rekonstruksjonen rapporteres ved siden av som følsomhet.
7. Ingen omregning rå ↔ tilberedt i kodingen. `weight_basis` i `studies.csv` sier hva artikkelen målte. Skript kan bruke faktor 0,75 (rå → tilberedt) som sensitivitet.

## 5. Automatiske sjekker (`checks.py`)

Kjøres før hver analyse, og rapporten legges i `kontroll/`:

1. `ci_low ≤ point ≤ ci_high` for alle rader.
2. Log-symmetri: |ln(ci_high) − ln(point)| og |ln(point) − ln(ci_low)| skal avvike med < 15 %; større avvik flagges som mulig trykkfeil (f.eks. Weis «0.90 (0.92–1.92)»).
3. Referansekategori: `category_rank = 1` skal ha `point = 1.0` og tomme CI-felt.
4. Innen et `category_set_id`: dosene stiger med `category_rank`; antall kategorier ≥ 3 for dose-respons.
5. Sum av `n_cases` over kategoriene i et sett = artikkelens totale tilfeller for utfallet (fra `studies`/`cup_extraction`), ellers flagg.
6. Hver `estimate_id` i `cup_extraction.estimate_ids_used` finnes i `estimates`.
7. Ingen samleanalyse (i `pool.py`) tar inn to rader med samme `cohort_id` eller med forelder/barn-kohort.
8. Alle `adjustment_set_id` i `estimates` finnes i `adjustments`.
9. `verification_status` ≠ `unverified` for alle rader som brukes i en publisert beregning.

### 5.1 Hva sjekkene fant i piloten
Log-symmetri-sjekken flagget to rader i Norat 2005: «1.28 (1.1 to 1.64)» (trykt slik; trolig 1,01) og distal kolon 20–40 g «1.10 (0.65 to 1.63)». Begge er registrert som trykt og merket for kontroll. Dette er sjekkens hensikt: trykkfeil i artikler går ellers rett inn i metaanalyser.

## 6. Kontrollprosedyre

1. **Dobbeltkoding**: en uavhengig Opus-instans, som ikke har sett første uttrekk, koder på nytt **alle** studier i CUPs dose-respons-analyse (13) og et tilfeldig utvalg på 30 % av resten. Avvik løses av Fable mot fulltekst, og løsningen loggføres i `kontroll/rettelogg.md` med rad-ID.
2. **Tall mot fulltekst**: kontrolløren sjekker hvert tall på rader der `is_main_model = yes` eller som inngår i `cup_extraction`, med sidetall.
3. **Reproduksjon**: `pool.py` skal gjenskape CUPs tre samleestimater på to desimaler fra `cup_extraction` (DerSimonian–Laird), og `gl_doseresponse.py` skal gjenskape CUPs per-100 g-tall fra kategoridataene for hver studie innen ± 0,02 i punktestimatet – eller avviket dokumenteres.
4. Først når 1–3 er gjennomført, settes `verification_status = verified`.

## 7. Analyser databasen er bygd for

- **Reproduksjon**: CUP 2016 fra rådata.
- **Regelkonformt CUP**: samme analyse med `rule_conform_estimate_ids` (MEC uprosessert, EPIC kalibrert, Takachi rett dose og tilfeller, Järvinen ut).
- **Kohortversjoner**: Takachi → Islam, Oba → Wada, Norat → Papadimitriou, Cross → Zouiouich.
- **Multiverse**: alle kombinasjoner av (rad-valg) × (versjon) × (porsjonsantakelse) × (med/uten studier som bryter eksponeringsregelen) × (random/fixed).
- **Negative kontroller**: fjørfe og fisk i samme kohorter, samme modeller.
- **Justeringsgradient**: estimat mot antall justeringsfaktorer, og særlig mot `adj_processed_meat` og `adj_energy`.
- **Geografi og tid**: region og publikasjonsår som moderatorer.

## 8. Det databasen ikke kan

Den kan vise hvor avhengig et samleestimat er av uthentingsvalg og av enkeltstudier. Den kan ikke fjerne restkonfundering eller målefeil i kostholdsdata, og den kan ikke gjøre et observasjonsmateriale til et årsaksbevis. Den kan heller ikke rette CUPs tall der CUP brukte upubliserte data (Chan 2011 S1-doser) som ikke ligger i artiklene.
